_shm.c
1 /******************************************************************** 2 * Description: _shm.c 3 * C implementation of rcslib shared memory API 4 * 5 * Derived from a work by Fred Proctor & Will Shackleford 6 * 7 * Author: 8 * License: LGPL Version 2 9 * System: Linux 10 * 11 * Copyright (c) 2004 All rights reserved. 12 * 13 * Last change: 14 ********************************************************************/ 15 16 #include "_shm.h" 17 #include "rcs_print.hh" 18 #include <stdio.h> /* NULL */ 19 #include <stdlib.h> 20 #include <stdarg.h> 21 #include <errno.h> 22 #include <stddef.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 #include <sys/ipc.h> 26 27 #if defined(qnx) && !defined(USE_POSIX_SHAREDMEM) 28 #define USE_POSIX_SHAREDMEM 1 29 #endif 30 31 #ifdef USE_POSIX_SHAREDMEM 32 #include <fcntl.h> 33 #include <sys/mman.h> 34 #else 35 #include <sys/shm.h> 36 #endif 37 38 #include <string.h> 39 40 static int shmems_created_list[100]; 41 static int shmems_created_list_initialized = 0; 42 43 shm_t *rcs_shm_open(key_t key, size_t size, int oflag, /* int mode */ ...) 44 { 45 va_list ap; 46 int mode; 47 int shmflg = 0; 48 shm_t *shm; 49 #ifdef USE_POSIX_SHAREDMEM 50 int existed_before = 0; 51 #if HAVE_FSTAT 52 struct stat statbuf; 53 #endif 54 #else 55 struct shmid_ds shared_mem_info; 56 57 int pid; 58 int i; 59 #endif 60 61 va_start(ap, oflag); 62 if (oflag) { 63 mode = va_arg(ap, int); 64 shmflg |= mode; 65 } 66 va_end(ap); 67 68 #ifdef USE_POSIX_SHAREDMEM 69 rcs_print_debug(PRINT_SHARED_MEMORY_ACTIVITY, 70 "rcs_shm_open(key=%d(0x%X),size=%d(0x%X),oflag=%d)\n", 71 key, key, size, size, oflag); 72 73 if (key == 0) { 74 rcs_print_error("rcs_shm_open(%d(0x%X), %d(0x%X), %d(0x%X)): error\n", 75 key, key, size, size, oflag, oflag); 76 rcs_print_error("RCS Shared Memory key may not be zero.\n"); 77 return NULL; 78 } 79 80 shm = (shm_t *) calloc(sizeof(shm_t), 1); 81 if (NULL == shm) { 82 rcs_print_error("rcs_shm_open: calloc failed\n"); 83 return NULL; 84 } 85 shm->create_errno = 0; 86 shm->addr = NULL; 87 shm->key = key; 88 shm->size = size; 89 #ifdef POSIX_SHMEM_NAME_PREFIX 90 strncpy(shm->name, POSIX_SHMEM_NAME_PREFIX, 64); 91 sprintf(shm->name + strlen(shm->name), "/rcs_shm%d", key); 92 #else 93 sprintf(shm->name, "/rcs_shm%d", key); 94 #endif 95 96 shm->id = 0; 97 errno = 0; 98 99 if (oflag) { 100 oflag = O_CREAT; 101 shm->id = shm_open(shm->name, O_RDWR, 0700); 102 } 103 104 /* Create a new memory object */ 105 if (shm->id <= 0) { 106 shm->id = shm_open(shm->name, oflag | O_RDWR, 0700); 107 if (shm->id == -1) { 108 rcs_print_error("shm_open(%s,%d(0x%X),%d(0x%X)) failed:%s %d\n", 109 shm->name, oflag | O_RDWR, oflag | O_RDWR, 110 mode, mode, strerror(errno), errno); 111 shm->create_errno = errno; 112 return shm; 113 } 114 existed_before = (oflag == 0); 115 } else { 116 existed_before = 1; 117 } 118 119 shm->created = (oflag != 0); 120 121 /* Set the memory object's size */ 122 if (!existed_before) { 123 if (ftruncate(shm->id, size + 16) == -1) { 124 rcs_print_error("ftruncate(%d,%d): %s %d\n", 125 shm->id, (size + 16), strerror(errno), errno); 126 shm->create_errno = errno; 127 return shm; 128 } 129 } 130 #if HAVE_FSTAT 131 else { 132 if (-1 == fstat(shm->id, &statbuf)) { 133 rcs_print_error("fstat failed. (errno=%d) %s\n", 134 errno, strerror(errno)); 135 shm->create_errno = errno; 136 return shm; 137 } 138 if (statbuf.st_size != size + 16) { 139 rcs_print_error 140 ("Shared memory buffer %s already exists but has the wrong size of % d instead of the expected size of % d. \n ", 141 shm->name, statbuf.st_size, size + 16); 142 shm->create_errno = -1; 143 return shm; 144 } 145 } 146 #endif 147 148 /* Map the memory object */ 149 shm->addr = mmap(0, size + 16, 150 PROT_READ | PROT_WRITE, MAP_SHARED, shm->id, 0); 151 if (shm->addr == MAP_FAILED) { 152 rcs_print_error 153 ("mmap(0,%d,PROT_READ | PROT_WRITE, MAP_SHARED,%d,0) failed: %s %d\n", 154 shm->id, size, strerror(errno), errno); 155 shm->create_errno = errno; 156 } 157 shm->size = size; 158 if (oflag & O_CREAT && !existed_before) { 159 *((int *) ((char *) shm->addr + size)) = 0; 160 } else { 161 (*((int *) ((char *) shm->addr + size)))++; 162 } 163 164 return (shm); 165 #else 166 167 rcs_print_debug 168 (PRINT_SHARED_MEMORY_ACTIVITY, 169 "rcs_shm_open(key=%d(0x%X),size=%zu(0x%zX),oflag=%d)\n", 170 key, key, size, size, oflag); 171 172 if (key == 0) { 173 rcs_print_error("rcs_shm_open(%d(0x%X), %zu(0x%zX), %d(0x%X)): error\n", 174 key, key, size, size, oflag, oflag); 175 rcs_print_error("RCS Shared Memory key may not be zero.\n"); 176 return NULL; 177 } 178 #if defined(O_CREAT) 179 #if O_CREAT != IPC_CREAT 180 if ((oflag & O_CREAT) && !(oflag & IPC_CREAT)) { 181 oflag &= ~(O_CREAT); 182 oflag |= IPC_CREAT; 183 } 184 #endif 185 #endif 186 187 if (oflag) { 188 shmflg |= IPC_CREAT; 189 } 190 191 shm = (shm_t *) calloc(sizeof(shm_t), 1); 192 if (NULL == shm) { 193 rcs_print_error("rcs_shm_open: calloc failed\n"); 194 return NULL; 195 } 196 shm->create_errno = 0; 197 shm->addr = NULL; 198 shm->key = key; 199 errno = 0; 200 201 shm->size = size; 202 203 if ((shm->id = shmget(key, (int) size, shmflg)) == -1) { 204 shm->create_errno = errno; 205 rcs_print_error("shmget(%d(0x%X),%zd,%d) failed: (errno = %d): %s\n", 206 key, key, size, shmflg, errno, strerror(errno)); 207 switch (errno) { 208 case EEXIST: 209 rcs_print_error 210 ("A shared memory buffer for this key already exists.\n"); 211 break; 212 213 case EINVAL: 214 rcs_print_error 215 ("Either the size is too big or the shared memory buffer already exists but is of the wrong size.\n"); 216 break; 217 218 case ENOSPC: 219 rcs_print_error 220 ("The system imposed limit on the maximum number of shared memory segments has been exceeded.\n"); 221 break; 222 223 case ENOENT: 224 rcs_print_error 225 ("No shared memory buffer exists for this key and the IPC_CREAT was not given.\n"); 226 break; 227 } 228 return (shm); 229 } 230 231 /* map shmem area into local address space */ 232 shmflg = 0; 233 shmflg &= ~SHM_RDONLY; 234 if ((shm->addr = (void *) shmat(shm->id, 0, shmflg)) == (void *) -1) { 235 shm->create_errno = errno; 236 rcs_print_error("shmat(%d,0,%d) failed:(errno = %d): %s\n", shm->id, 237 shmflg, errno, strerror(errno)); 238 rcs_print_error("key = %d (0x%X)\n", key, key); 239 shm->addr = NULL; 240 return (shm); 241 } 242 243 /* Check to see if I am the creator of this shared memory buffer. */ 244 if (shmctl(shm->id, IPC_STAT, &shared_mem_info) < 0) { 245 rcs_print_error("shmctl error: %d %s\n", errno, strerror(errno)); 246 return shm; 247 } 248 249 /* If oflag was not set this process couldn't be the creator. */ 250 if (!oflag) { 251 return shm; 252 } 253 254 if (!shmems_created_list_initialized) { 255 memset(shmems_created_list, 0, 100 * sizeof(int)); 256 shmems_created_list_initialized = 1; 257 } else { 258 for (i = 0; i < 100; i++) { 259 if (shmems_created_list[i] == key) { 260 return shm; 261 } 262 } 263 } 264 265 pid = (int) getpid(); 266 if (pid <= 0) { 267 rcs_print_error("getpid error: %d %s\n", errno, strerror(errno)); 268 return shm; 269 } 270 shm->created = (shared_mem_info.shm_cpid == pid); 271 #ifdef linux_2_4_0 272 shm->created = 1; 273 #endif 274 if (shm->created) { 275 for (i = 0; i < 100; i++) { 276 if (shmems_created_list[i] <= 0) { 277 shmems_created_list[i] = shm->key; 278 break; 279 } 280 } 281 } 282 return shm; 283 #endif 284 } 285 286 int rcs_shm_close(shm_t * shm) 287 { 288 #ifdef USE_POSIX_SHAREDMEM 289 int nattch; 290 if (shm == 0) { 291 return -1; 292 } 293 if (shm->addr > 0) { 294 nattch = rcs_shm_nattch(shm); 295 (*((int *) ((char *) shm->addr + shm->size)))--; 296 if (munmap(shm->addr, shm->size + 16) == -1) { 297 rcs_print_error("munmap(%p,%d) failed. %s %d\n", 298 shm->addr, shm->size, strerror(errno), errno); 299 return -1; 300 } 301 shm->addr = NULL; 302 if (shm->id > 0) { 303 if (close(shm->id) == -1) { 304 rcs_print_error("close(%d) failed. %s %d\n", 305 shm->id, strerror(errno), errno); 306 } 307 } 308 if (nattch <= 1) { 309 shm_unlink(shm->name); 310 } 311 shm->id = 0; 312 } 313 #else 314 struct shmid_ds shared_mem_info; 315 316 int i; 317 318 /* check for invalid ptr */ 319 if (shm == NULL) 320 return -1; 321 322 rcs_print_debug(PRINT_SHARED_MEMORY_ACTIVITY, 323 "rcs_shm_close(shm->key=%d(0x%X),shm->size=%zu(0x%zX),shm->addr=%p)\n", 324 shm->key, shm->key, shm->size, shm->size, shm->addr); 325 326 /* detach from shmem */ 327 shmdt((char *) shm->addr); 328 329 /* remove OS shmem if there are no attached processes */ 330 if (rcs_shm_nattch(shm) == 0) { 331 shmctl(shm->id, IPC_RMID, &shared_mem_info); 332 } 333 334 if (shm->created && shmems_created_list_initialized) { 335 for (i = 0; i < 100; i++) { 336 if (shmems_created_list[i] == shm->key) { 337 shmems_created_list[i] = 0; 338 break; 339 } 340 } 341 } 342 #endif 343 344 free(shm); 345 346 return 0; 347 } 348 349 int rcs_shm_delete(shm_t * shm) 350 { 351 #ifdef USE_POSIX_SHAREDMEM 352 if (shm == 0) { 353 return -1; 354 } 355 if (shm->addr > 0) { 356 (*((int *) ((char *) shm->addr + shm->size)))--; 357 if (munmap(shm->addr, shm->size + 16) == -1) { 358 rcs_print_error("munmap(%p,%d) failed. %s %d\n", 359 shm->addr, shm->size, strerror(errno), errno); 360 return -1; 361 } 362 shm->addr = NULL; 363 if (shm->id > 0) { 364 if (close(shm->id) == -1) { 365 rcs_print_error("close(%d) failed. %s %d\n", 366 shm->id, strerror(errno), errno); 367 } 368 } 369 shm->id = 0; 370 } 371 shm_unlink(shm->name); 372 #else 373 struct shmid_ds shared_mem_info; 374 375 /* check for invalid ptr */ 376 if (shm == NULL) 377 return -1; 378 379 /* detach from shmem */ 380 shmdt((char *) shm->addr); 381 382 /* remove OS shmem regardless of whether there are attached processes */ 383 shmctl(shm->id, IPC_RMID, &shared_mem_info); 384 #endif 385 386 free(shm); 387 388 return 0; 389 } 390 391 int rcs_shm_nattch(shm_t * shm) 392 { 393 #ifdef USE_POSIX_SHAREDMEM 394 if (shm == 0) { 395 return -1; 396 } 397 if (shm->addr == 0) { 398 return -1; 399 } 400 return *((int *) (((char *) shm->addr) + shm->size)) + 1; 401 #else 402 struct shmid_ds shared_mem_info; 403 int err; 404 /* check for invalid ptr */ 405 if (shm == NULL) 406 return -1; 407 408 /* get the status of shared memory */ 409 err = shmctl(shm->id, IPC_STAT, &shared_mem_info); 410 411 if(err == -1) { 412 rcs_print_error("rcs_shm_nattch: shmctl failed: %s\n", 413 strerror(errno)); 414 return 0; 415 } 416 417 return shared_mem_info.shm_nattch; 418 #endif 419 420 }