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