1 /* S H A R E D M E M O R Y D R I V E R
2 =======================================
3
4 by Jerzy.Borkowski@obs.unige.ch
5
6 09-Mar-98 : initial version 1.0 released
7 23-Mar-98 : shared_malloc now accepts new handle as an argument
8 23-Mar-98 : shmem://0, shmem://1, etc changed to shmem://h0, etc due to bug
9 in url parser.
10 10-Apr-98 : code cleanup
11 13-May-99 : delayed initialization added, global table deleted on exit when
12 no shmem segments remain, and last process terminates
13 */
14
15 #ifdef HAVE_SHMEM_SERVICES
16 #include "fitsio2.h" /* drvrsmem.h is included by it */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25
26 #if defined(unix) || defined(__unix__) || defined(__unix) || defined(HAVE_UNISTD_H)
27 #include <unistd.h>
28 #endif
29
30
31 static int shared_kbase = 0; /* base for shared memory handles */
32 static int shared_maxseg = 0; /* max number of shared memory blocks */
33 static int shared_range = 0; /* max number of tried entries */
34 static int shared_fd = SHARED_INVALID; /* handle of global access lock file */
35 static int shared_gt_h = SHARED_INVALID; /* handle of global table segment */
36 static SHARED_LTAB *shared_lt = NULL; /* local table pointer */
37 static SHARED_GTAB *shared_gt = NULL; /* global table pointer */
38 static int shared_create_mode = 0666; /* permission flags for created objects */
39 static int shared_debug = 1; /* simple debugging tool, set to 0 to disable messages */
40 static int shared_init_called = 0; /* flag whether shared_init() has been called, used for delayed init */
41
42 /* static support routines prototypes */
43
44 static int shared_clear_entry(int idx); /* unconditionally clear entry */
45 static int shared_destroy_entry(int idx); /* unconditionally destroy sema & shseg and clear entry */
46 static int shared_mux(int idx, int mode); /* obtain exclusive access to specified segment */
47 static int shared_demux(int idx, int mode); /* free exclusive access to specified segment */
48
49 static int shared_process_count(int sem); /* valid only for time of invocation */
50 static int shared_delta_process(int sem, int delta); /* change number of processes hanging on segment */
51 static int shared_attach_process(int sem);
52 static int shared_detach_process(int sem);
53 static int shared_get_free_entry(int newhandle); /* get free entry in shared_key, or -1, entry is set rw locked */
54 static int shared_get_hash(long size, int idx);/* return hash value for malloc */
55 static long shared_adjust_size(long size); /* size must be >= 0 !!! */
56 static int shared_check_locked_index(int idx); /* verify that given idx is valid */
57 static int shared_map(int idx); /* map all tables for given idx, check for validity */
58 static int shared_validate(int idx, int mode); /* use intrnally inside crit.sect !!! */
59
60 /* support routines - initialization */
61
62
shared_clear_entry(int idx)63 static int shared_clear_entry(int idx) /* unconditionally clear entry */
64 { if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
65 shared_gt[idx].key = SHARED_INVALID; /* clear entries in global table */
66 shared_gt[idx].handle = SHARED_INVALID;
67 shared_gt[idx].sem = SHARED_INVALID;
68 shared_gt[idx].semkey = SHARED_INVALID;
69 shared_gt[idx].nprocdebug = 0;
70 shared_gt[idx].size = 0;
71 shared_gt[idx].attr = 0;
72
73 return(SHARED_OK);
74 }
75
shared_destroy_entry(int idx)76 static int shared_destroy_entry(int idx) /* unconditionally destroy sema & shseg and clear entry */
77 { int r, r2;
78 union semun filler;
79
80 if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
81 r2 = r = SHARED_OK;
82 filler.val = 0; /* this is to make cc happy (warning otherwise) */
83 if (SHARED_INVALID != shared_gt[idx].sem) r = semctl(shared_gt[idx].sem, 0, IPC_RMID, filler); /* destroy semaphore */
84 if (SHARED_INVALID != shared_gt[idx].handle) r2 = shmctl(shared_gt[idx].handle, IPC_RMID, 0); /* destroy shared memory segment */
85 if (SHARED_OK == r) r = r2; /* accumulate error code in r, free r2 */
86 r2 = shared_clear_entry(idx);
87 return((SHARED_OK == r) ? r2 : r);
88 }
89
shared_cleanup(void)90 void shared_cleanup(void) /* this must (should) be called during exit/abort */
91 { int i, j, r, oktodelete, filelocked, segmentspresent;
92 flock_t flk;
93 struct shmid_ds ds;
94
95 if (shared_debug) printf("shared_cleanup:");
96 if (NULL != shared_lt)
97 { if (shared_debug) printf(" deleting segments:");
98 for (i=0; i<shared_maxseg; i++)
99 { if (0 == shared_lt[i].tcnt) continue; /* we're not using this segment, skip this ... */
100 if (-1 != shared_lt[i].lkcnt) continue; /* seg not R/W locked by us, skip this ... */
101
102 r = shared_destroy_entry(i); /* destroy unconditionally sema & segment */
103 if (shared_debug)
104 { if (SHARED_OK == r) printf(" [%d]", i);
105 else printf(" [error on %d !!!!]", i);
106
107 }
108 }
109 free((void *)shared_lt); /* free local table */
110 shared_lt = NULL;
111 }
112 if (NULL != shared_gt) /* detach global index table */
113 { oktodelete = 0;
114 filelocked = 0;
115 if (shared_debug) printf(" detaching globalsharedtable");
116 if (SHARED_INVALID != shared_fd)
117
118 flk.l_type = F_WRLCK; /* lock whole lock file */
119 flk.l_whence = 0;
120 flk.l_start = 0;
121 flk.l_len = shared_maxseg;
122 if (-1 != fcntl(shared_fd, F_SETLK, &flk))
123 { filelocked = 1; /* success, scan global table, to see if there are any segs */
124 segmentspresent = 0; /* assume, there are no segs in the system */
125 for (j=0; j<shared_maxseg; j++)
126 { if (SHARED_INVALID != shared_gt[j].key)
127 { segmentspresent = 1; /* yes, there is at least one */
128 break;
129 }
130 }
131 if (0 == segmentspresent) /* if there are no segs ... */
132 if (0 == shmctl(shared_gt_h, IPC_STAT, &ds)) /* get number of processes attached to table */
133 { if (ds.shm_nattch <= 1) oktodelete = 1; /* if only one (we), then it is safe (but see text 4 lines later) to unlink */
134 }
135 }
136 shmdt((char *)shared_gt); /* detach global table */
137 if (oktodelete) /* delete global table from system, if no shm seg present */
138 { shmctl(shared_gt_h, IPC_RMID, 0); /* there is a race condition here - time window between shmdt and shmctl */
139 shared_gt_h = SHARED_INVALID;
140 }
141 shared_gt = NULL;
142 if (filelocked) /* if we locked, we need to unlock */
143 { flk.l_type = F_UNLCK;
144 flk.l_whence = 0;
145 flk.l_start = 0;
146 flk.l_len = shared_maxseg;
147 fcntl(shared_fd, F_SETLK, &flk);
148 }
149 }
150 shared_gt_h = SHARED_INVALID;
151
152 if (SHARED_INVALID != shared_fd) /* close lock file */
153 { if (shared_debug) printf(" closing lockfile");
154 close(shared_fd);
155 shared_fd = SHARED_INVALID;
156 }
157
158
159 shared_kbase = 0;
160 shared_maxseg = 0;
161 shared_range = 0;
162 shared_init_called = 0;
163
164 if (shared_debug) printf(" <<done>>\n");
165 return;
166 }
167
168
shared_init(int debug_msgs)169 int shared_init(int debug_msgs) /* initialize shared memory stuff, you have to call this routine once */
170 { int i;
171 char buf[1000], *p;
172 mode_t oldumask;
173
174 shared_init_called = 1; /* tell everybody no need to call us for the 2nd time */
175 shared_debug = debug_msgs; /* set required debug mode */
176
177 if (shared_debug) printf("shared_init:");
178
179 shared_kbase = 0; /* adapt to current env. settings */
180 if (NULL != (p = getenv(SHARED_ENV_KEYBASE))) shared_kbase = atoi(p);
181 if (0 == shared_kbase) shared_kbase = SHARED_KEYBASE;
182 if (shared_debug) printf(" keybase=%d", shared_kbase);
183
184 shared_maxseg = 0;
185 if (NULL != (p = getenv(SHARED_ENV_MAXSEG))) shared_maxseg = atoi(p);
186 if (0 == shared_maxseg) shared_maxseg = SHARED_MAXSEG;
187 if (shared_debug) printf(" maxseg=%d", shared_maxseg);
188
189 shared_range = 3 * shared_maxseg;
190
191 if (SHARED_INVALID == shared_fd) /* create rw locking file (this file is never deleted) */
192 { if (shared_debug) printf(" lockfileinit=");
193 snprintf(buf, 1000,"%s.%d.%d", SHARED_FDNAME, shared_kbase, shared_maxseg);
194 oldumask = umask(0);
195
196 shared_fd = open(buf, O_TRUNC | O_EXCL | O_CREAT | O_RDWR, shared_create_mode);
197 umask(oldumask);
198 if (SHARED_INVALID == shared_fd) /* or just open rw locking file, in case it already exists */
199 { shared_fd = open(buf, O_TRUNC | O_RDWR, shared_create_mode);
200 if (SHARED_INVALID == shared_fd) return(SHARED_NOFILE);
201 if (shared_debug) printf("slave");
202
203 }
204 else
205 { if (shared_debug) printf("master");
206 }
207 }
208
209 if (SHARED_INVALID == shared_gt_h) /* global table not attached, try to create it in shared memory */
210 { if (shared_debug) printf(" globalsharedtableinit=");
211 shared_gt_h = shmget(shared_kbase, shared_maxseg * sizeof(SHARED_GTAB), IPC_CREAT | IPC_EXCL | shared_create_mode); /* try open as a master */
212 if (SHARED_INVALID == shared_gt_h) /* if failed, try to open as a slave */
213 { shared_gt_h = shmget(shared_kbase, shared_maxseg * sizeof(SHARED_GTAB), shared_create_mode);
214 if (SHARED_INVALID == shared_gt_h) return(SHARED_IPCERR); /* means deleted ID residing in system, shared mem unusable ... */
215 shared_gt = (SHARED_GTAB *)shmat(shared_gt_h, 0, 0); /* attach segment */
216 if (((SHARED_GTAB *)SHARED_INVALID) == shared_gt) return(SHARED_IPCERR);
217 if (shared_debug) printf("slave");
218 }
219 else
220 { shared_gt = (SHARED_GTAB *)shmat(shared_gt_h, 0, 0); /* attach segment */
221 if (((SHARED_GTAB *)SHARED_INVALID) == shared_gt) return(SHARED_IPCERR);
222 for (i=0; i<shared_maxseg; i++) shared_clear_entry(i); /* since we are master, init data */
223 if (shared_debug) printf("master");
224 }
225 }
226
227 if (NULL == shared_lt) /* initialize local table */
228 { if (shared_debug) printf(" localtableinit=");
229 if (NULL == (shared_lt = (SHARED_LTAB *)malloc(shared_maxseg * sizeof(SHARED_LTAB)))) return(SHARED_NOMEM);
230 for (i=0; i<shared_maxseg; i++)
231 { shared_lt[i].p = NULL; /* not mapped */
232 shared_lt[i].tcnt = 0; /* unused (or zero threads using this seg) */
233 shared_lt[i].lkcnt = 0; /* segment is unlocked */
234 shared_lt[i].seekpos = 0L; /* r/w pointer at the beginning of file */
235 }
236 if (shared_debug) printf("ok");
237 }
238
239 atexit(shared_cleanup); /* we want shared_cleanup to be called at exit or abort */
240
241 if (shared_debug) printf(" <<done>>\n");
242 return(SHARED_OK);
243 }
244
245
shared_recover(int id)246 int shared_recover(int id) /* try to recover dormant segments after applic crash */
247 { int i, r, r2;
248
249 if (NULL == shared_gt) return(SHARED_NOTINIT); /* not initialized */
250 if (NULL == shared_lt) return(SHARED_NOTINIT); /* not initialized */
251 r = SHARED_OK;
252 for (i=0; i<shared_maxseg; i++)
253 { if (-1 != id) if (i != id) continue;
254 if (shared_lt[i].tcnt) continue; /* somebody (we) is using it */
255 if (SHARED_INVALID == shared_gt[i].key) continue; /* unused slot */
256 if (shared_mux(i, SHARED_NOWAIT | SHARED_RDWRITE)) continue; /* acquire exclusive access to segment, but do not wait */
257 r2 = shared_process_count(shared_gt[i].sem);
258 if ((shared_gt[i].nprocdebug > r2) || (0 == r2))
259 { if (shared_debug) printf("Bogus handle=%d nproc=%d sema=%d:", i, shared_gt[i].nprocdebug, r2);
260 r = shared_destroy_entry(i);
261 if (shared_debug)
262 { printf("%s", r ? "error couldn't clear handle" : "handle cleared");
263 }
264 }
265 shared_demux(i, SHARED_RDWRITE);
266 }
267 return(r); /* table full */
268 }
269
270 /* API routines - mutexes and locking */
271
shared_mux(int idx,int mode)272 static int shared_mux(int idx, int mode) /* obtain exclusive access to specified segment */
273 { flock_t flk;
274
275 int r;
276
277 if (0 == shared_init_called) /* delayed initialization */
278 { if (SHARED_OK != (r = shared_init(0))) return(r);
279
280 }
281 if (SHARED_INVALID == shared_fd) return(SHARED_NOTINIT);
282 if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
283 flk.l_type = ((mode & SHARED_RDWRITE) ? F_WRLCK : F_RDLCK);
284 flk.l_whence = 0;
285 flk.l_start = idx;
286 flk.l_len = 1;
287 if (shared_debug) printf(" [mux (%d): ", idx);
288 if (-1 == fcntl(shared_fd, ((mode & SHARED_NOWAIT) ? F_SETLK : F_SETLKW), &flk))
289 { switch (errno)
290 { case EAGAIN: ;
291
292 case EACCES: if (shared_debug) printf("again]");
293 return(SHARED_AGAIN);
294 default: if (shared_debug) printf("err]");
295 return(SHARED_IPCERR);
296 }
297 }
298 if (shared_debug) printf("ok]");
299 return(SHARED_OK);
300 }
301
302
303
shared_demux(int idx,int mode)304 static int shared_demux(int idx, int mode) /* free exclusive access to specified segment */
305 { flock_t flk;
306
307 if (SHARED_INVALID == shared_fd) return(SHARED_NOTINIT);
308 if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
309 flk.l_type = F_UNLCK;
310 flk.l_whence = 0;
311 flk.l_start = idx;
312 flk.l_len = 1;
313 if (shared_debug) printf(" [demux (%d): ", idx);
314 if (-1 == fcntl(shared_fd, F_SETLKW, &flk))
315 { switch (errno)
316 { case EAGAIN: ;
317 case EACCES: if (shared_debug) printf("again]");
318 return(SHARED_AGAIN);
319 default: if (shared_debug) printf("err]");
320 return(SHARED_IPCERR);
321 }
322
323 }
324 if (shared_debug) printf("mode=%d ok]", mode);
325 return(SHARED_OK);
326 }
327
328
329
shared_process_count(int sem)330 static int shared_process_count(int sem) /* valid only for time of invocation */
331 { union semun su;
332
333 su.val = 0; /* to force compiler not to give warning messages */
334 return(semctl(sem, 0, GETVAL, su)); /* su is unused here */
335 }
336
337
shared_delta_process(int sem,int delta)338 static int shared_delta_process(int sem, int delta) /* change number of processes hanging on segment */
339 { struct sembuf sb;
340
341 if (SHARED_INVALID == sem) return(SHARED_BADARG); /* semaphore not attached */
342 sb.sem_num = 0;
343 sb.sem_op = delta;
344 sb.sem_flg = SEM_UNDO;
345 return((-1 == semop(sem, &sb, 1)) ? SHARED_IPCERR : SHARED_OK);
346 }
347
348
shared_attach_process(int sem)349 static int shared_attach_process(int sem)
350 { if (shared_debug) printf(" [attach process]");
351 return(shared_delta_process(sem, 1));
352 }
353
354
shared_detach_process(int sem)355 static int shared_detach_process(int sem)
356 { if (shared_debug) printf(" [detach process]");
357 return(shared_delta_process(sem, -1));
358 }
359
360 /* API routines - hashing and searching */
361
362
shared_get_free_entry(int newhandle)363 static int shared_get_free_entry(int newhandle) /* get newhandle, or -1, entry is set rw locked */
364 {
365 if (NULL == shared_gt) return(-1); /* not initialized */
366 if (NULL == shared_lt) return(-1); /* not initialized */
367 if (newhandle < 0) return(-1);
368 if (newhandle >= shared_maxseg) return(-1);
369 if (shared_lt[newhandle].tcnt) return(-1); /* somebody (we) is using it */
370 if (shared_mux(newhandle, SHARED_NOWAIT | SHARED_RDWRITE)) return(-1); /* used by others */
371 if (SHARED_INVALID == shared_gt[newhandle].key) return(newhandle); /* we have found free slot, lock it and return index */
372 shared_demux(newhandle, SHARED_RDWRITE);
373 if (shared_debug) printf("[free_entry - ERROR - entry unusable]");
374 return(-1); /* table full */
375 }
376
377
shared_get_hash(long size,int idx)378 static int shared_get_hash(long size, int idx) /* return hash value for malloc */
379 { static int counter = 0;
380 int hash;
381
382 hash = (counter + size * idx) % shared_range;
383 counter = (counter + 1) % shared_range;
384 return(hash);
385 }
386
387
shared_adjust_size(long size)388 static long shared_adjust_size(long size) /* size must be >= 0 !!! */
389 { return(((size + sizeof(BLKHEAD) + SHARED_GRANUL - 1) / SHARED_GRANUL) * SHARED_GRANUL); }
390
391
392 /* API routines - core : malloc/realloc/free/attach/detach/lock/unlock */
393
shared_malloc(long size,int mode,int newhandle)394 int shared_malloc(long size, int mode, int newhandle) /* return idx or SHARED_INVALID */
395 { int h, i, r, idx, key;
396 union semun filler;
397 BLKHEAD *bp;
398
399 if (0 == shared_init_called) /* delayed initialization */
400 { if (SHARED_OK != (r = shared_init(0))) return(r);
401 }
402 if (shared_debug) printf("malloc (size = %ld, mode = %d):", size, mode);
403 if (size < 0) return(SHARED_INVALID);
404 if (-1 == (idx = shared_get_free_entry(newhandle))) return(SHARED_INVALID);
405 if (shared_debug) printf(" idx=%d", idx);
406 for (i = 0; ; i++)
407 { if (i >= shared_range) /* table full, signal error & exit */
408 { shared_demux(idx, SHARED_RDWRITE);
409 return(SHARED_INVALID);
410 }
411 key = shared_kbase + ((i + shared_get_hash(size, idx)) % shared_range);
412 if (shared_debug) printf(" key=%d", key);
413 h = shmget(key, shared_adjust_size(size), IPC_CREAT | IPC_EXCL | shared_create_mode);
414 if (shared_debug) printf(" handle=%d", h);
415 if (SHARED_INVALID == h) continue; /* segment already accupied */
416 bp = (BLKHEAD *)shmat(h, 0, 0); /* try attach */
417 if (shared_debug) printf(" p=%p", bp);
418 if (((BLKHEAD *)SHARED_INVALID) == bp) /* cannot attach, delete segment, try with another key */
419 { shmctl(h, IPC_RMID, 0);
420 continue;
421 } /* now create semaphor counting number of processes attached */
422 if (SHARED_INVALID == (shared_gt[idx].sem = semget(key, 1, IPC_CREAT | IPC_EXCL | shared_create_mode)))
423 { shmdt((void *)bp); /* cannot create segment, delete everything */
424 shmctl(h, IPC_RMID, 0);
425 continue; /* try with another key */
426 }
427 if (shared_debug) printf(" sem=%d", shared_gt[idx].sem);
428 if (shared_attach_process(shared_gt[idx].sem)) /* try attach process */
429 { semctl(shared_gt[idx].sem, 0, IPC_RMID, filler); /* destroy semaphore */
430 shmdt((char *)bp); /* detach shared mem segment */
431 shmctl(h, IPC_RMID, 0); /* destroy shared mem segment */
432 continue; /* try with another key */
433 }
434 bp->s.tflag = BLOCK_SHARED; /* fill in data in segment's header (this is really not necessary) */
435 bp->s.ID[0] = SHARED_ID_0;
436 bp->s.ID[1] = SHARED_ID_1;
437 bp->s.handle = idx; /* used in yorick */
438 if (mode & SHARED_RESIZE)
439 { if (shmdt((char *)bp)) r = SHARED_IPCERR; /* if segment is resizable, then detach segment */
440 shared_lt[idx].p = NULL;
441 }
442 else { shared_lt[idx].p = bp; }
443 shared_lt[idx].tcnt = 1; /* one thread using segment */
444 shared_lt[idx].lkcnt = 0; /* no locks at the moment */
445 shared_lt[idx].seekpos = 0L; /* r/w pointer positioned at beg of block */
446 shared_gt[idx].handle = h; /* fill in data in global table */
447 shared_gt[idx].size = size;
448 shared_gt[idx].attr = mode;
449 shared_gt[idx].semkey = key;
450 shared_gt[idx].key = key;
451 shared_gt[idx].nprocdebug = 0;
452
453 break;
454 }
455 shared_demux(idx, SHARED_RDWRITE); /* hope this will not fail */
456 return(idx);
457 }
458
459
shared_attach(int idx)460 int shared_attach(int idx)
461 { int r, r2;
462
463 if (SHARED_OK != (r = shared_mux(idx, SHARED_RDWRITE | SHARED_WAIT))) return(r);
464 if (SHARED_OK != (r = shared_map(idx)))
465 { shared_demux(idx, SHARED_RDWRITE);
466 return(r);
467 }
468 if (shared_attach_process(shared_gt[idx].sem)) /* try attach process */
469 { shmdt((char *)(shared_lt[idx].p)); /* cannot attach process, detach everything */
470 shared_lt[idx].p = NULL;
471 shared_demux(idx, SHARED_RDWRITE);
472 return(SHARED_BADARG);
473 }
474 shared_lt[idx].tcnt++; /* one more thread is using segment */
475 if (shared_gt[idx].attr & SHARED_RESIZE) /* if resizeable, detach and return special pointer */
476 { if (shmdt((char *)(shared_lt[idx].p))) r = SHARED_IPCERR; /* if segment is resizable, then detach segment */
477 shared_lt[idx].p = NULL;
478 }
479 shared_lt[idx].seekpos = 0L; /* r/w pointer positioned at beg of block */
480 r2 = shared_demux(idx, SHARED_RDWRITE);
481 return(r ? r : r2);
482 }
483
484
485
shared_check_locked_index(int idx)486 static int shared_check_locked_index(int idx) /* verify that given idx is valid */
487 { int r;
488
489 if (0 == shared_init_called) /* delayed initialization */
490 { if (SHARED_OK != (r = shared_init(0))) return(r);
491
492 }
493 if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
494 if (NULL == shared_lt[idx].p) return(SHARED_BADARG); /* NULL pointer, not attached ?? */
495 if (0 == shared_lt[idx].lkcnt) return(SHARED_BADARG); /* not locked ?? */
496 if ((SHARED_ID_0 != (shared_lt[idx].p)->s.ID[0]) || (SHARED_ID_1 != (shared_lt[idx].p)->s.ID[1]) ||
497 (BLOCK_SHARED != (shared_lt[idx].p)->s.tflag)) /* invalid data in segment */
498 return(SHARED_BADARG);
499 return(SHARED_OK);
500 }
501
502
503
shared_map(int idx)504 static int shared_map(int idx) /* map all tables for given idx, check for validity */
505 { int h; /* have to obtain excl. access before calling shared_map */
506 BLKHEAD *bp;
507
508 if ((idx < 0) || (idx >= shared_maxseg)) return(SHARED_BADARG);
509 if (SHARED_INVALID == shared_gt[idx].key) return(SHARED_BADARG);
510 if (SHARED_INVALID == (h = shmget(shared_gt[idx].key, 1, shared_create_mode))) return(SHARED_BADARG);
511 if (((BLKHEAD *)SHARED_INVALID) == (bp = (BLKHEAD *)shmat(h, 0, 0))) return(SHARED_BADARG);
512 if ((SHARED_ID_0 != bp->s.ID[0]) || (SHARED_ID_1 != bp->s.ID[1]) || (BLOCK_SHARED != bp->s.tflag) || (h != shared_gt[idx].handle))
513 { shmdt((char *)bp); /* invalid segment, detach everything */
514 return(SHARED_BADARG);
515
516 }
517 if (shared_gt[idx].sem != semget(shared_gt[idx].semkey, 1, shared_create_mode)) /* check if sema is still there */
518 { shmdt((char *)bp); /* cannot attach semaphore, detach everything */
519 return(SHARED_BADARG);
520 }
521 shared_lt[idx].p = bp; /* store pointer to shmem data */
522 return(SHARED_OK);
523 }
524
525
shared_validate(int idx,int mode)526 static int shared_validate(int idx, int mode) /* use intrnally inside crit.sect !!! */
527 { int r;
528
529 if (SHARED_OK != (r = shared_mux(idx, mode))) return(r); /* idx checked by shared_mux */
530 if (NULL == shared_lt[idx].p)
531 if (SHARED_OK != (r = shared_map(idx)))
532 { shared_demux(idx, mode);
533 return(r);
534 }
535 if ((SHARED_ID_0 != (shared_lt[idx].p)->s.ID[0]) || (SHARED_ID_1 != (shared_lt[idx].p)->s.ID[1]) || (BLOCK_SHARED != (shared_lt[idx].p)->s.tflag))
536 { shared_demux(idx, mode);
537 return(r);
538 }
539 return(SHARED_OK);
540 }
541
542
shared_realloc(int idx,long newsize)543 SHARED_P shared_realloc(int idx, long newsize) /* realloc shared memory segment */
544 { int h, key, i, r;
545 BLKHEAD *bp;
546 long transfersize;
547
548 r = SHARED_OK;
549 if (newsize < 0) return(NULL);
550 if (shared_check_locked_index(idx)) return(NULL);
551 if (0 == (shared_gt[idx].attr & SHARED_RESIZE)) return(NULL);
552 if (-1 != shared_lt[idx].lkcnt) return(NULL); /* check for RW lock */
553 if (shared_adjust_size(shared_gt[idx].size) == shared_adjust_size(newsize))
554 { shared_gt[idx].size = newsize;
555
556 return((SHARED_P)((shared_lt[idx].p) + 1));
557 }
558 for (i = 0; ; i++)
559 { if (i >= shared_range) return(NULL); /* table full, signal error & exit */
560 key = shared_kbase + ((i + shared_get_hash(newsize, idx)) % shared_range);
561 h = shmget(key, shared_adjust_size(newsize), IPC_CREAT | IPC_EXCL | shared_create_mode);
562 if (SHARED_INVALID == h) continue; /* segment already accupied */
563 bp = (BLKHEAD *)shmat(h, 0, 0); /* try attach */
564 if (((BLKHEAD *)SHARED_INVALID) == bp) /* cannot attach, delete segment, try with another key */
565 { shmctl(h, IPC_RMID, 0);
566 continue;
567 }
568 *bp = *(shared_lt[idx].p); /* copy header, then data */
569 transfersize = ((newsize < shared_gt[idx].size) ? newsize : shared_gt[idx].size);
570 if (transfersize > 0)
571 memcpy((void *)(bp + 1), (void *)((shared_lt[idx].p) + 1), transfersize);
572 if (shmdt((char *)(shared_lt[idx].p))) r = SHARED_IPCERR; /* try to detach old segment */
573 if (shmctl(shared_gt[idx].handle, IPC_RMID, 0)) if (SHARED_OK == r) r = SHARED_IPCERR; /* destroy old shared memory segment */
574 shared_gt[idx].size = newsize; /* signal new size */
575 shared_gt[idx].handle = h; /* signal new handle */
576 shared_gt[idx].key = key; /* signal new key */
577 shared_lt[idx].p = bp;
578 break;
579 }
580 return((SHARED_P)(bp + 1));
581 }
582
583
shared_free(int idx)584 int shared_free(int idx) /* detach segment, if last process & !PERSIST, destroy segment */
585 { int cnt, r, r2;
586
587 if (SHARED_OK != (r = shared_validate(idx, SHARED_RDWRITE | SHARED_WAIT))) return(r);
588 if (SHARED_OK != (r = shared_detach_process(shared_gt[idx].sem))) /* update number of processes using segment */
589 { shared_demux(idx, SHARED_RDWRITE);
590 return(r);
591 }
592 shared_lt[idx].tcnt--; /* update number of threads using segment */
593 if (shared_lt[idx].tcnt > 0) return(shared_demux(idx, SHARED_RDWRITE)); /* if more threads are using segment we are done */
594 if (shmdt((char *)(shared_lt[idx].p))) /* if, we are the last thread, try to detach segment */
595 { shared_demux(idx, SHARED_RDWRITE);
596 return(SHARED_IPCERR);
597 }
598 shared_lt[idx].p = NULL; /* clear entry in local table */
599 shared_lt[idx].seekpos = 0L; /* r/w pointer positioned at beg of block */
600 if (-1 == (cnt = shared_process_count(shared_gt[idx].sem))) /* get number of processes hanging on segment */
601 { shared_demux(idx, SHARED_RDWRITE);
602 return(SHARED_IPCERR);
603 }
604 if ((0 == cnt) && (0 == (shared_gt[idx].attr & SHARED_PERSIST))) r = shared_destroy_entry(idx); /* no procs on seg, destroy it */
605 r2 = shared_demux(idx, SHARED_RDWRITE);
606 return(r ? r : r2);
607 }
608
609
shared_lock(int idx,int mode)610 SHARED_P shared_lock(int idx, int mode) /* lock given segment for exclusive access */
611 { int r;
612
613 if (shared_mux(idx, mode)) return(NULL); /* idx checked by shared_mux */
614 if (0 != shared_lt[idx].lkcnt) /* are we already locked ?? */
615 if (SHARED_OK != (r = shared_map(idx)))
616 { shared_demux(idx, mode);
617 return(NULL);
618 }
619 if (NULL == shared_lt[idx].p) /* stupid pointer ?? */
620 if (SHARED_OK != (r = shared_map(idx)))
621 { shared_demux(idx, mode);
622 return(NULL);
623 }
624 if ((SHARED_ID_0 != (shared_lt[idx].p)->s.ID[0]) || (SHARED_ID_1 != (shared_lt[idx].p)->s.ID[1]) || (BLOCK_SHARED != (shared_lt[idx].p)->s.tflag))
625 { shared_demux(idx, mode);
626 return(NULL);
627 }
628 if (mode & SHARED_RDWRITE)
629 { shared_lt[idx].lkcnt = -1;
630
631 shared_gt[idx].nprocdebug++;
632 }
633
634 else shared_lt[idx].lkcnt++;
635 shared_lt[idx].seekpos = 0L; /* r/w pointer positioned at beg of block */
636 return((SHARED_P)((shared_lt[idx].p) + 1));
637 }
638
639
shared_unlock(int idx)640 int shared_unlock(int idx) /* unlock given segment, assumes seg is locked !! */
641 { int r, r2, mode;
642
643 if (SHARED_OK != (r = shared_check_locked_index(idx))) return(r);
644 if (shared_lt[idx].lkcnt > 0)
645 { shared_lt[idx].lkcnt--; /* unlock read lock */
646 mode = SHARED_RDONLY;
647 }
648 else
649 { shared_lt[idx].lkcnt = 0; /* unlock write lock */
650 shared_gt[idx].nprocdebug--;
651 mode = SHARED_RDWRITE;
652 }
653 if (0 == shared_lt[idx].lkcnt) if (shared_gt[idx].attr & SHARED_RESIZE)
654 { if (shmdt((char *)(shared_lt[idx].p))) r = SHARED_IPCERR; /* segment is resizable, then detach segment */
655 shared_lt[idx].p = NULL; /* signal detachment in local table */
656 }
657 r2 = shared_demux(idx, mode); /* unlock segment, rest is only parameter checking */
658 return(r ? r : r2);
659 }
660
661 /* API routines - support and info routines */
662
663
shared_attr(int idx)664 int shared_attr(int idx) /* get the attributes of the shared memory segment */
665 { int r;
666
667 if (shared_check_locked_index(idx)) return(SHARED_INVALID);
668 r = shared_gt[idx].attr;
669 return(r);
670 }
671
672
shared_set_attr(int idx,int newattr)673 int shared_set_attr(int idx, int newattr) /* get the attributes of the shared memory segment */
674 { int r;
675
676 if (shared_check_locked_index(idx)) return(SHARED_INVALID);
677 if (-1 != shared_lt[idx].lkcnt) return(SHARED_INVALID); /* ADDED - check for RW lock */
678 r = shared_gt[idx].attr;
679 shared_gt[idx].attr = newattr;
680 return(r);
681
682 }
683
684
shared_set_debug(int mode)685 int shared_set_debug(int mode) /* set/reset debug mode */
686 { int r = shared_debug;
687
688 shared_debug = mode;
689 return(r);
690 }
691
692
shared_set_createmode(int mode)693 int shared_set_createmode(int mode) /* set/reset debug mode */
694 { int r = shared_create_mode;
695
696 shared_create_mode = mode;
697 return(r);
698 }
699
700
701
702
shared_list(int id)703 int shared_list(int id)
704 { int i, r;
705
706 if (NULL == shared_gt) return(SHARED_NOTINIT); /* not initialized */
707 if (NULL == shared_lt) return(SHARED_NOTINIT); /* not initialized */
708 if (shared_debug) printf("shared_list:");
709 r = SHARED_OK;
710 printf(" Idx Key Nproc Size Flags\n");
711 printf("==============================================\n");
712 for (i=0; i<shared_maxseg; i++)
713 { if (-1 != id) if (i != id) continue;
714 if (SHARED_INVALID == shared_gt[i].key) continue; /* unused slot */
715 switch (shared_mux(i, SHARED_NOWAIT | SHARED_RDONLY)) /* acquire exclusive access to segment, but do not wait */
716
717 { case SHARED_AGAIN:
718 printf("!%3d %08lx %4d %8d", i, (unsigned long int)shared_gt[i].key,
719 shared_gt[i].nprocdebug, shared_gt[i].size);
720 if (SHARED_RESIZE & shared_gt[i].attr) printf(" RESIZABLE");
721 if (SHARED_PERSIST & shared_gt[i].attr) printf(" PERSIST");
722 printf("\n");
723 break;
724 case SHARED_OK:
725 printf(" %3d %08lx %4d %8d", i, (unsigned long int)shared_gt[i].key,
726
727 shared_gt[i].nprocdebug, shared_gt[i].size);
728 if (SHARED_RESIZE & shared_gt[i].attr) printf(" RESIZABLE");
729 if (SHARED_PERSIST & shared_gt[i].attr) printf(" PERSIST");
730 printf("\n");
731 shared_demux(i, SHARED_RDONLY);
732 break;
733 default:
734 continue;
735 }
736 }
737 if (shared_debug) printf(" done\n");
738 return(r); /* table full */
739 }
740
shared_getaddr(int id,char ** address)741 int shared_getaddr(int id, char **address)
742 { int i;
743 char segname[10];
744
745 if (NULL == shared_gt) return(SHARED_NOTINIT); /* not initialized */
746 if (NULL == shared_lt) return(SHARED_NOTINIT); /* not initialized */
747
748 strcpy(segname,"h");
749 snprintf(segname+1,9,"%d", id);
750
751 if (smem_open(segname,0,&i)) return(SHARED_BADARG);
752
753 *address = ((char *)(((DAL_SHM_SEGHEAD *)(shared_lt[i].p + 1)) + 1));
754 /* smem_close(i); */
755 return(SHARED_OK);
756 }
757
758
shared_uncond_delete(int id)759 int shared_uncond_delete(int id)
760 { int i, r;
761
762 if (NULL == shared_gt) return(SHARED_NOTINIT); /* not initialized */
763 if (NULL == shared_lt) return(SHARED_NOTINIT); /* not initialized */
764 if (shared_debug) printf("shared_uncond_delete:");
765 r = SHARED_OK;
766 for (i=0; i<shared_maxseg; i++)
767 { if (-1 != id) if (i != id) continue;
768 if (shared_attach(i))
769 { if (-1 != id) printf("no such handle\n");
770 continue;
771 }
772 printf("handle %d:", i);
773 if (NULL == shared_lock(i, SHARED_RDWRITE | SHARED_NOWAIT))
774 { printf(" cannot lock in RW mode, not deleted\n");
775 continue;
776 }
777 if (shared_set_attr(i, SHARED_RESIZE) >= SHARED_ERRBASE)
778 { printf(" cannot clear PERSIST attribute");
779 }
780 if (shared_free(i))
781 { printf(" delete failed\n");
782 }
783 else
784 { printf(" deleted\n");
785 }
786 }
787 if (shared_debug) printf(" done\n");
788 return(r); /* table full */
789 }
790
791
792 /************************* CFITSIO DRIVER FUNCTIONS ***************************/
793
smem_init(void)794 int smem_init(void)
795 { return(0);
796 }
797
smem_shutdown(void)798 int smem_shutdown(void)
799
800 { if (shared_init_called) shared_cleanup();
801 return(0);
802 }
803
smem_setoptions(int option)804 int smem_setoptions(int option)
805 { option = 0;
806 return(0);
807 }
808
809
smem_getoptions(int * options)810 int smem_getoptions(int *options)
811 { if (NULL == options) return(SHARED_NULPTR);
812 *options = 0;
813 return(0);
814 }
815
smem_getversion(int * version)816 int smem_getversion(int *version)
817 { if (NULL == version) return(SHARED_NULPTR);
818 *version = 10;
819 return(0);
820 }
821
822
smem_open(char * filename,int rwmode,int * driverhandle)823 int smem_open(char *filename, int rwmode, int *driverhandle)
824 { int h, nitems, r;
825 DAL_SHM_SEGHEAD *sp;
826
827
828 if (NULL == filename) return(SHARED_NULPTR);
829 if (NULL == driverhandle) return(SHARED_NULPTR);
830 nitems = sscanf(filename, "h%d", &h);
831 if (1 != nitems) return(SHARED_BADARG);
832
833 if (SHARED_OK != (r = shared_attach(h))) return(r);
834
835 if (NULL == (sp = (DAL_SHM_SEGHEAD *)shared_lock(h,
836 ((READWRITE == rwmode) ? SHARED_RDWRITE : SHARED_RDONLY))))
837 { shared_free(h);
838 return(SHARED_BADARG);
839 }
840
841 if ((h != sp->h) || (DAL_SHM_SEGHEAD_ID != sp->ID))
842 { shared_unlock(h);
843 shared_free(h);
844
845 return(SHARED_BADARG);
846 }
847
848 *driverhandle = h;
849 return(0);
850 }
851
852
smem_create(char * filename,int * driverhandle)853 int smem_create(char *filename, int *driverhandle)
854 { DAL_SHM_SEGHEAD *sp;
855 int h, sz, nitems;
856
857 if (NULL == filename) return(SHARED_NULPTR); /* currently ignored */
858 if (NULL == driverhandle) return(SHARED_NULPTR);
859 nitems = sscanf(filename, "h%d", &h);
860 if (1 != nitems) return(SHARED_BADARG);
861
862 if (SHARED_INVALID == (h = shared_malloc(sz = 2880 + sizeof(DAL_SHM_SEGHEAD),
863 SHARED_RESIZE | SHARED_PERSIST, h)))
864 return(SHARED_NOMEM);
865
866 if (NULL == (sp = (DAL_SHM_SEGHEAD *)shared_lock(h, SHARED_RDWRITE)))
867 { shared_free(h);
868 return(SHARED_BADARG);
869 }
870
871 sp->ID = DAL_SHM_SEGHEAD_ID;
872 sp->h = h;
873 sp->size = sz;
874 sp->nodeidx = -1;
875
876 *driverhandle = h;
877
878 return(0);
879 }
880
881
smem_close(int driverhandle)882 int smem_close(int driverhandle)
883 { int r;
884
885 if (SHARED_OK != (r = shared_unlock(driverhandle))) return(r);
886 return(shared_free(driverhandle));
887 }
888
smem_remove(char * filename)889 int smem_remove(char *filename)
890 { int nitems, h, r;
891
892 if (NULL == filename) return(SHARED_NULPTR);
893 nitems = sscanf(filename, "h%d", &h);
894 if (1 != nitems) return(SHARED_BADARG);
895
896 if (0 == shared_check_locked_index(h)) /* are we locked ? */
897
898 { if (-1 != shared_lt[h].lkcnt) /* are we locked RO ? */
899 { if (SHARED_OK != (r = shared_unlock(h))) return(r); /* yes, so relock in RW */
900 if (NULL == shared_lock(h, SHARED_RDWRITE)) return(SHARED_BADARG);
901 }
902
903 }
904 else /* not locked */
905 { if (SHARED_OK != (r = smem_open(filename, READWRITE, &h)))
906 return(r); /* so open in RW mode */
907 }
908
909 shared_set_attr(h, SHARED_RESIZE); /* delete PERSIST attribute */
910 return(smem_close(h)); /* detach segment (this will delete it) */
911 }
912
smem_size(int driverhandle,LONGLONG * size)913 int smem_size(int driverhandle, LONGLONG *size)
914 {
915 if (NULL == size) return(SHARED_NULPTR);
916 if (shared_check_locked_index(driverhandle)) return(SHARED_INVALID);
917 *size = (LONGLONG) (shared_gt[driverhandle].size - sizeof(DAL_SHM_SEGHEAD));
918 return(0);
919 }
920
smem_flush(int driverhandle)921 int smem_flush(int driverhandle)
922 {
923 if (shared_check_locked_index(driverhandle)) return(SHARED_INVALID);
924 return(0);
925 }
926
smem_seek(int driverhandle,LONGLONG offset)927 int smem_seek(int driverhandle, LONGLONG offset)
928 {
929 if (offset < 0) return(SHARED_BADARG);
930 if (shared_check_locked_index(driverhandle)) return(SHARED_INVALID);
931 shared_lt[driverhandle].seekpos = offset;
932 return(0);
933 }
934
smem_read(int driverhandle,void * buffer,long nbytes)935 int smem_read(int driverhandle, void *buffer, long nbytes)
936 {
937 if (NULL == buffer) return(SHARED_NULPTR);
938 if (shared_check_locked_index(driverhandle)) return(SHARED_INVALID);
939 if (nbytes < 0) return(SHARED_BADARG);
940 if ((shared_lt[driverhandle].seekpos + nbytes) > shared_gt[driverhandle].size)
941 return(SHARED_BADARG); /* read beyond EOF */
942
943 memcpy(buffer,
944 ((char *)(((DAL_SHM_SEGHEAD *)(shared_lt[driverhandle].p + 1)) + 1)) +
945 shared_lt[driverhandle].seekpos,
946 nbytes);
947
948 shared_lt[driverhandle].seekpos += nbytes;
949 return(0);
950 }
951
smem_write(int driverhandle,void * buffer,long nbytes)952 int smem_write(int driverhandle, void *buffer, long nbytes)
953 {
954 if (NULL == buffer) return(SHARED_NULPTR);
955 if (shared_check_locked_index(driverhandle)) return(SHARED_INVALID);
956 if (-1 != shared_lt[driverhandle].lkcnt) return(SHARED_INVALID); /* are we locked RW ? */
957
958 if (nbytes < 0) return(SHARED_BADARG);
959 if ((unsigned long)(shared_lt[driverhandle].seekpos + nbytes) > (unsigned long)(shared_gt[driverhandle].size - sizeof(DAL_SHM_SEGHEAD)))
960 { /* need to realloc shmem */
961 if (NULL == shared_realloc(driverhandle, shared_lt[driverhandle].seekpos + nbytes + sizeof(DAL_SHM_SEGHEAD)))
962 return(SHARED_NOMEM);
963 }
964
965 memcpy(((char *)(((DAL_SHM_SEGHEAD *)(shared_lt[driverhandle].p + 1)) + 1)) +
966 shared_lt[driverhandle].seekpos,
967 buffer,
968 nbytes);
969
970 shared_lt[driverhandle].seekpos += nbytes;
971 return(0);
972 }
973 #endif
974