/******************************************************************** * Description: _shm.c * C implementation of rcslib shared memory API * * Derived from a work by Fred Proctor & Will Shackleford * * Author: * License: LGPL Version 2 * System: Linux * * Copyright (c) 2004 All rights reserved. * * Last change: ********************************************************************/ #include "_shm.h" #include "rcs_print.hh" #include /* NULL */ #include #include #include #include #include #include #include #include #if defined(qnx) && !defined(USE_POSIX_SHAREDMEM) #define USE_POSIX_SHAREDMEM 1 #endif #ifdef USE_POSIX_SHAREDMEM #include #include #else #include #endif #include static int shmems_created_list[100]; static int shmems_created_list_initialized = 0; shm_t *rcs_shm_open(key_t key, size_t size, int oflag, /* int mode */ ...) { va_list ap; int mode; int shmflg = 0; shm_t *shm; #ifdef USE_POSIX_SHAREDMEM int existed_before = 0; #if HAVE_FSTAT struct stat statbuf; #endif #else struct shmid_ds shared_mem_info; int pid; int i; #endif va_start(ap, oflag); if (oflag) { mode = va_arg(ap, int); shmflg |= mode; } va_end(ap); #ifdef USE_POSIX_SHAREDMEM rcs_print_debug(PRINT_SHARED_MEMORY_ACTIVITY, "rcs_shm_open(key=%d(0x%X),size=%d(0x%X),oflag=%d)\n", key, key, size, size, oflag); if (key == 0) { rcs_print_error("rcs_shm_open(%d(0x%X), %d(0x%X), %d(0x%X)): error\n", key, key, size, size, oflag, oflag); rcs_print_error("RCS Shared Memory key may not be zero.\n"); return NULL; } shm = (shm_t *) calloc(sizeof(shm_t), 1); if (NULL == shm) { rcs_print_error("rcs_shm_open: calloc failed\n"); return NULL; } shm->create_errno = 0; shm->addr = NULL; shm->key = key; shm->size = size; #ifdef POSIX_SHMEM_NAME_PREFIX strncpy(shm->name, POSIX_SHMEM_NAME_PREFIX, 64); sprintf(shm->name + strlen(shm->name), "/rcs_shm%d", key); #else sprintf(shm->name, "/rcs_shm%d", key); #endif shm->id = 0; errno = 0; if (oflag) { oflag = O_CREAT; shm->id = shm_open(shm->name, O_RDWR, 0777); } /* Create a new memory object */ if (shm->id <= 0) { shm->id = shm_open(shm->name, oflag | O_RDWR, 0777); if (shm->id == -1) { rcs_print_error("shm_open(%s,%d(0x%X),%d(0x%X)) failed:%s %d\n", shm->name, oflag | O_RDWR, oflag | O_RDWR, mode, mode, strerror(errno), errno); shm->create_errno = errno; return shm; } existed_before = (oflag == 0); } else { existed_before = 1; } shm->created = (oflag != 0); /* Set the memory object's size */ if (!existed_before) { if (ftruncate(shm->id, size + 16) == -1) { rcs_print_error("ftruncate(%d,%d): %s %d\n", shm->id, (size + 16), strerror(errno), errno); shm->create_errno = errno; return shm; } } #if HAVE_FSTAT else { if (-1 == fstat(shm->id, &statbuf)) { rcs_print_error("fstat failed. (errno=%d) %s\n", errno, strerror(errno)); shm->create_errno = errno; return shm; } if (statbuf.st_size != size + 16) { rcs_print_error ("Shared memory buffer %s already exists but has the wrong size of % d instead of the expected size of % d. \n ", shm->name, statbuf.st_size, size + 16); shm->create_errno = -1; return shm; } } #endif /* Map the memory object */ shm->addr = mmap(0, size + 16, PROT_READ | PROT_WRITE, MAP_SHARED, shm->id, 0); if (shm->addr == MAP_FAILED) { rcs_print_error ("mmap(0,%d,PROT_READ | PROT_WRITE, MAP_SHARED,%d,0) failed: %s %d\n", shm->id, size, strerror(errno), errno); shm->create_errno = errno; } shm->size = size; if (oflag & O_CREAT && !existed_before) { *((int *) ((char *) shm->addr + size)) = 0; } else { (*((int *) ((char *) shm->addr + size)))++; } return (shm); #else rcs_print_debug (PRINT_SHARED_MEMORY_ACTIVITY, "rcs_shm_open(key=%d(0x%X),size=%zu(0x%zX),oflag=%d)\n", key, key, size, size, oflag); if (key == 0) { rcs_print_error("rcs_shm_open(%d(0x%X), %zu(0x%zX), %d(0x%X)): error\n", key, key, size, size, oflag, oflag); rcs_print_error("RCS Shared Memory key may not be zero.\n"); return NULL; } #if defined(O_CREAT) #if O_CREAT != IPC_CREAT if ((oflag & O_CREAT) && !(oflag & IPC_CREAT)) { oflag &= ~(O_CREAT); oflag |= IPC_CREAT; } #endif #endif if (oflag) { shmflg |= IPC_CREAT; } shm = (shm_t *) calloc(sizeof(shm_t), 1); if (NULL == shm) { rcs_print_error("rcs_shm_open: calloc failed\n"); return NULL; } shm->create_errno = 0; shm->addr = NULL; shm->key = key; errno = 0; shm->size = size; if ((shm->id = shmget(key, (int) size, shmflg)) == -1) { shm->create_errno = errno; rcs_print_error("shmget(%d(0x%X),%zd,%d) failed: (errno = %d): %s\n", key, key, size, shmflg, errno, strerror(errno)); switch (errno) { case EEXIST: rcs_print_error ("A shared memory buffer for this key already exists.\n"); break; case EINVAL: rcs_print_error ("Either the size is too big or the shared memory buffer already exists but is of the wrong size.\n"); break; case ENOSPC: rcs_print_error ("The system imposed limit on the maximum number of shared memory segments has been exceeded.\n"); break; case ENOENT: rcs_print_error ("No shared memory buffer exists for this key and the IPC_CREAT was not given.\n"); break; } return (shm); } /* map shmem area into local address space */ shmflg = 0; shmflg &= ~SHM_RDONLY; if ((shm->addr = (void *) shmat(shm->id, 0, shmflg)) == (void *) -1) { shm->create_errno = errno; rcs_print_error("shmat(%d,0,%d) failed:(errno = %d): %s\n", shm->id, shmflg, errno, strerror(errno)); rcs_print_error("key = %d (0x%X)\n", key, key); shm->addr = NULL; return (shm); } /* Check to see if I am the creator of this shared memory buffer. */ if (shmctl(shm->id, IPC_STAT, &shared_mem_info) < 0) { rcs_print_error("shmctl error: %d %s\n", errno, strerror(errno)); return shm; } /* If oflag was not set this process couldn't be the creator. */ if (!oflag) { return shm; } if (!shmems_created_list_initialized) { memset(shmems_created_list, 0, 100 * sizeof(int)); shmems_created_list_initialized = 1; } else { for (i = 0; i < 100; i++) { if (shmems_created_list[i] == key) { return shm; } } } pid = (int) getpid(); if (pid <= 0) { rcs_print_error("getpid error: %d %s\n", errno, strerror(errno)); return shm; } shm->created = (shared_mem_info.shm_cpid == pid); #ifdef linux_2_4_0 shm->created = 1; #endif if (shm->created) { for (i = 0; i < 100; i++) { if (shmems_created_list[i] <= 0) { shmems_created_list[i] = shm->key; break; } } } return shm; #endif } int rcs_shm_close(shm_t * shm) { #ifdef USE_POSIX_SHAREDMEM int nattch; if (shm == 0) { return -1; } if (shm->addr > 0) { nattch = rcs_shm_nattch(shm); (*((int *) ((char *) shm->addr + shm->size)))--; if (munmap(shm->addr, shm->size + 16) == -1) { rcs_print_error("munmap(%p,%d) failed. %s %d\n", shm->addr, shm->size, strerror(errno), errno); return -1; } shm->addr = NULL; if (shm->id > 0) { if (close(shm->id) == -1) { rcs_print_error("close(%d) failed. %s %d\n", shm->id, strerror(errno), errno); } } if (nattch <= 1) { shm_unlink(shm->name); } shm->id = 0; } #else struct shmid_ds shared_mem_info; int i; /* check for invalid ptr */ if (shm == NULL) return -1; rcs_print_debug(PRINT_SHARED_MEMORY_ACTIVITY, "rcs_shm_close(shm->key=%d(0x%X),shm->size=%zu(0x%zX),shm->addr=%p)\n", shm->key, shm->key, shm->size, shm->size, shm->addr); /* detach from shmem */ shmdt((char *) shm->addr); /* remove OS shmem if there are no attached processes */ if (rcs_shm_nattch(shm) == 0) { shmctl(shm->id, IPC_RMID, &shared_mem_info); } if (shm->created && shmems_created_list_initialized) { for (i = 0; i < 100; i++) { if (shmems_created_list[i] == shm->key) { shmems_created_list[i] = 0; break; } } } #endif free(shm); return 0; } int rcs_shm_delete(shm_t * shm) { #ifdef USE_POSIX_SHAREDMEM if (shm == 0) { return -1; } if (shm->addr > 0) { (*((int *) ((char *) shm->addr + shm->size)))--; if (munmap(shm->addr, shm->size + 16) == -1) { rcs_print_error("munmap(%p,%d) failed. %s %d\n", shm->addr, shm->size, strerror(errno), errno); return -1; } shm->addr = NULL; if (shm->id > 0) { if (close(shm->id) == -1) { rcs_print_error("close(%d) failed. %s %d\n", shm->id, strerror(errno), errno); } } shm->id = 0; } shm_unlink(shm->name); #else struct shmid_ds shared_mem_info; /* check for invalid ptr */ if (shm == NULL) return -1; /* detach from shmem */ shmdt((char *) shm->addr); /* remove OS shmem regardless of whether there are attached processes */ shmctl(shm->id, IPC_RMID, &shared_mem_info); #endif free(shm); return 0; } int rcs_shm_nattch(shm_t * shm) { #ifdef USE_POSIX_SHAREDMEM if (shm == 0) { return -1; } if (shm->addr == 0) { return -1; } return *((int *) (((char *) shm->addr) + shm->size)) + 1; #else struct shmid_ds shared_mem_info; int err; /* check for invalid ptr */ if (shm == NULL) return -1; /* get the status of shared memory */ err = shmctl(shm->id, IPC_STAT, &shared_mem_info); if(err == -1) { rcs_print_error("rcs_shm_nattch: shmctl failed: %s\n", strerror(errno)); return 0; } return shared_mem_info.shm_nattch; #endif }