/ src / libnml / os_intf / _shm.c
_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  }