chiark / gitweb /
Release 1.4.2.1.
[misc] / sema.c
1 /* -*-c-*-
2  *
3  * Operations on SysV semaphores
4  *
5  * (c) 2016 Mark Wooding
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 /*----- Header files ------------------------------------------------------*/
26
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <float.h>
31 #include <limits.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37
38 #include <sys/types.h>
39 #include <sys/ipc.h>
40 #include <sys/sem.h>
41
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <sys/stat.h>
45 #include <sys/wait.h>
46
47 #include <mLib/mdwopt.h>
48 #include <mLib/quis.h>
49 #include <mLib/report.h>
50 #include <mLib/tv.h>
51
52 #include "fence.h"
53 #include "timemax.h"
54
55 /* Oh, for pity's sake why did nobody do this for us? */
56 union semun {
57   int val;
58   struct semid_ds *buf;
59   unsigned short *array;
60 };
61
62 /*----- Random utilities --------------------------------------------------*/
63
64 static int parse_int(const char *what, const char *p,
65                      int radix, int min, int max)
66 {
67   char *q;
68   int oerr;
69   long i = -1;
70
71   oerr = errno;
72   if (isspace((unsigned char)*p)) errno = EINVAL;
73   else i = strtol(p, &q, radix);
74   if (*q || errno || !(min <= i && i <= max))
75     die(253, "invalid %s `%s'", what, p);
76   errno = oerr;
77   return ((int)i);
78 }
79
80 /*----- Timeout handling --------------------------------------------------*/
81
82 enum { NEVER, UNTIL, FOREVER };
83
84 struct timeout {
85   int wait;
86   struct timeval tv;
87 };
88
89 static struct timeval now;
90
91 static void update_now(void) { int rc = gettimeofday(&now, 0); assert(!rc); }
92
93 static void f2tv(double t, struct timeval *tv)
94   { tv->tv_sec = t; tv->tv_usec = 1e6*(t - tv->tv_sec); }
95
96 static void parse_timeout(const char *p, struct timeout *t)
97 {
98   double x = 0;
99   char *q;
100   int oerr;
101   time_t t_max = timemax();
102   struct timeval tv;
103
104   if (strcmp(p, "forever") == 0)
105     t->wait = FOREVER;
106   else if (strcmp(p, "none") == 0 || strcmp(p, "never") == 0)
107     t->wait = NEVER;
108   else {
109     oerr = errno; errno = 0;
110     if (isspace((unsigned char)*p)) errno = EINVAL;
111     else x = strtod(p, &q);
112     if (x < 0) errno = ERANGE;
113     if (!errno) {
114       switch (tolower((unsigned char )*q)) {
115         case 'd': x *= 24;
116         case 'h': x *= 60;
117         case 'm': x *= 60;
118         case 's': q++; break;
119       }
120       if (*q) errno = EINVAL;
121     }
122     if (errno) die(253, "invalid timeout specification `%s'", p);
123     if (x >= t_max - now.tv_sec) t->wait = FOREVER;
124     else if (!x) t->wait = NEVER;
125     else { t->wait = UNTIL; f2tv(x, &tv); TV_ADD(&t->tv, &now, &tv); }
126     errno = oerr;
127   }
128 }
129
130 /*----- Semaphore operations ----------------------------------------------*/
131
132 #define SEMA_PROJ 0xd1 /* fair die roll */
133
134 enum { NTY_PATH };
135 struct nameinfo {
136   unsigned ty;
137   union {
138     const char *path;
139   } u;
140 };
141
142 static void parse_name(const char *name, struct nameinfo *ni)
143 {
144   size_t plen = strcspn(name, ":/");
145   switch (name[plen]) {
146     case 0: case '/': ni->ty = NTY_PATH; ni->u.path = name; break;
147     case ':':
148       if (strncmp(name, "file:", 5) == 0)
149         { ni->ty = NTY_PATH; ni->u.path = name + plen + 1; }
150       else
151         die(253, "unknown name type `%.*s'", (int)plen, name);
152       break;
153   }
154 }
155
156 static int sema_initialized_p(int semid)
157 {
158   union semun u;
159   struct semid_ds sem;
160
161   u.buf = &sem;
162   if (semctl(semid, 0, IPC_STAT, u) < 0) {
163     die(254, "failed to check that semaphore set is initialized: %s",
164         strerror(errno));
165   }
166   return (!!sem.sem_otime);
167 }
168
169 static void make_sema_file(const char *name, int of, mode_t mode)
170 {
171   int fd;
172   struct nameinfo ni;
173
174   parse_name(name, &ni);
175   if (ni.ty != NTY_PATH)
176     die(253, "semaphore name `%s' doesn't designate a path", name);
177
178   if ((fd = open(ni.u.path, O_CREAT | of, mode)) < 0) {
179     die(254, "failed to create semaphore file `%s': %s",
180         ni.u.path, strerror(errno));
181   }
182   close(fd);
183 }
184
185 #define OF_PROBE 1u
186 #define OF_UNINIT 2u
187
188 static int open_sema(const char *name, unsigned f, int of,
189                      mode_t mode, int ival,
190                      const struct timeout *t)
191 {
192   int ff = mode & 0777, rc, nrace = 5;
193   int semid;
194   union semun u;
195   struct sembuf buf[1];
196   double w, ww;
197   struct nameinfo ni;
198   struct timeval tv, ttv;
199   key_t k;
200
201   /* Turn the name into an IPC key. */
202   parse_name(name, &ni);
203   assert(ni.ty == NTY_PATH);
204   if ((k = ftok(ni.u.path, SEMA_PROJ)) == (key_t)-1) {
205     die(254, "failed to get key from semaphore file `%s': %s",
206         ni.u.path, strerror(errno));
207   }
208
209   for (;;) {
210     /* Oh, horrors.  A newly created semaphore set is uninitialized.  But if
211      * we set IPC_CREAT without IPC_EXCL then we don't have any idea whether
212      * the semaphore set was created or not.  So we have this little dance to
213      * do.
214      */
215     if ((of & (O_CREAT | O_EXCL)) != (O_CREAT | O_EXCL)) {
216       semid = semget(k, 1, ff);
217       if (semid >= 0) goto await;
218       else if (errno != ENOENT) goto fail;
219       else if (ff & O_CREAT) /* try to create -- below */;
220       else if (f & OF_PROBE) return (-1);
221       else goto fail;
222     }
223
224     /* So, here, we have O_CREAT set, and either the semaphore set doesn't
225      * seem to exist yet, or O_EXCL is set and we didn't bother checking yet.
226      * So now we try to create the set.  If that fails with something other
227      * than EEXIST, or we were trying with O_EXCL, then we're done.
228      * Otherwise it's just appeared out of nowhere, so let's briefly try
229      * racing with whoever else it is, but give up if it doesn't look like
230      * we're likely to win.
231      */
232     if ((semid = semget(k, 1, ff | IPC_CREAT | IPC_EXCL)) >= 0) break;
233     else if ((ff & O_EXCL) || errno != EEXIST || nrace--) goto fail;
234   }
235
236   /* Right, we just created the semaphore set.  Now we have to initialize it,
237    * because nobody did that for us.  Set the initial value through semop to
238    * set the sem_otime stamp as an indicator that the set is properly
239    * initialized.
240    */
241   u.val = ival ? 0 : 1;
242   buf[0].sem_num = 0;
243   buf[0].sem_op = ival ? ival : -1;
244   buf[0].sem_flg = IPC_NOWAIT;
245   if (semctl(semid, 0, SETVAL, u) < 0 ||
246       semop(semid, buf, 1) < 0)
247     die(254, "failed to initialize semaphore set: %s", strerror(errno));
248
249   /* Whew. */
250   return (semid);
251
252   /* Make sure that the semaphore set is actually initialized before we
253    * continue.
254    */
255 await:
256   if ((f & OF_UNINIT) || sema_initialized_p(semid)) return (semid);
257   if (t->wait == NEVER) goto notready;
258   w = 1e-4;
259   update_now();
260   for (;;) {
261     ww = w/2 + w*rand()/RAND_MAX; f2tv(ww, &tv); TV_ADD(&tv, &tv, &now);
262     if (t->wait == UNTIL) {
263       if (TV_CMP(&now, >=, &t->tv)) goto notready;
264       if (TV_CMP(&tv, >, &t->tv)) tv = t->tv;
265     }
266     for (;;) {
267       TV_SUB(&ttv, &tv, &now);
268       rc = select(0, 0, 0, 0, &ttv);
269       update_now();
270       if (!rc) break;
271       else if (errno != EINTR)
272         die(254, "unexpected error from select: %s", strerror(errno));
273     }
274     if (sema_initialized_p(semid)) return (0);
275     w *= 2;
276     if (w >= 10) w = 10;
277   }
278
279   /* Done. */
280   return (semid);
281
282   /* Report a get failure. */
283 fail:
284   die(254, "failed to open semaphore set: %s", strerror(errno));
285
286 notready:
287   die(252, "semaphore set not ready");
288 }
289
290 static void rm_sema(int semid)
291 {
292   if (semctl(semid, 0, IPC_RMID))
293     die(254, "failed to remove semaphore set: %s", strerror(errno));
294 }
295
296 static unsigned read_sema(int semid)
297 {
298   int val;
299
300   if ((val = semctl(semid, 0, GETVAL)) < 0)
301     die(254, "failed to read semaphore value: %s", strerror(errno));
302   return (val);
303 }
304
305 static void set_sema(int semid, unsigned val)
306 {
307   union semun u;
308
309   u.val = val;
310   if (semctl(semid, 0, SETVAL, u) < 0)
311     die(254, "failed to set semaphore value: %s", strerror(errno));
312 }
313
314 static void post_sema(int semid, int by)
315 {
316   struct sembuf buf[1];
317
318   buf[0].sem_num = 0;
319   buf[0].sem_op = by;
320   buf[0].sem_flg = 0;
321   if (semop(semid, buf, 1))
322     die(254, "failed to post semaphore: %s", strerror(errno));
323 }
324
325 static struct sembuf *sigsembuf;
326 static int sigflag;
327
328 static void wait_alarm(int hunoz)
329   { sigflag = 1; if (sigsembuf) sigsembuf->sem_flg |= IPC_NOWAIT; }
330
331 static int wait_sema(int semid, int by, int flg, const struct timeout *t)
332 {
333   struct sembuf buf[1];
334   struct sigaction sa, osa;
335   sigset_t ss, oss, ps;
336   struct timeval tv;
337   struct itimerval it, oit;
338   int rc, sig;
339   unsigned f = 0;
340 #define f_alarm 1u
341
342   /* Set up the command buffer. */
343   buf[0].sem_num = 0;
344   buf[0].sem_op = -by;
345   buf[0].sem_flg = flg;
346
347   /* Modify it according to the timeout settings. */
348   switch (t->wait) {
349
350     case NEVER:
351       /* Don't wait. */
352       buf[0].sem_flg |= IPC_NOWAIT;
353       break;
354
355     case FOREVER:
356       /* Wait until it's ready. */
357       break;
358
359     case UNTIL:
360       /* Wait until the specified time.  This is fiddly, and will likely
361        * require setting an alarm.
362        */
363
364       /* If the timeout is already in the past, then don't bother with the
365        * alarm, and simply don't wait.
366        */
367       if (TV_CMP(&t->tv, <=, &now)) {
368         buf[0].sem_flg |= IPC_NOWAIT;
369         break;
370       }
371
372       /* Find the current alarm setting. */
373       if (getitimer(ITIMER_REAL, &oit)) {
374         die(254, "failed to read current alarm setting: %s",
375             strerror(errno));
376       }
377
378       /* If that's not set, or it's going to go off after our timeout, then
379        * we should override it temporarily.  Otherwise, just let it do its
380        * (likely lethal) thing.
381        */
382       if (!oit.it_value.tv_sec && !oit.it_value.tv_usec)
383         f |= f_alarm;
384       else {
385         TV_ADD(&oit.it_value, &oit.it_value, &now);
386         if (TV_CMP(&t->tv, <, &oit.it_value)) f |= f_alarm;
387       }
388
389       /* If we are setting an alarm, then now is the time to mess about with
390        * signals and timers.
391        */
392       if (f & f_alarm) {
393
394         /* Mask out the signal while we fiddle with the settings. */
395         sigemptyset(&ss);
396         sigaddset(&ss, SIGALRM);
397         if (sigprocmask(SIG_BLOCK, &ss, &oss))
398           die(254, "failed to block alarm signal: %s", strerror(errno));
399
400         /* Establish our new signal handler. */
401         sigsembuf = &buf[0];
402         sigflag = 0;
403         sa.sa_handler = wait_alarm;
404         sa.sa_mask = ss;
405         sa.sa_flags = 0;
406         if (sigaction(SIGALRM, &sa, &osa))
407           die(254, "failed to set alarm handler: %s", strerror(errno));
408
409         /* Set up the timer. */
410         TV_SUB(&it.it_value, &t->tv, &now);
411         it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0;
412         if (setitimer(ITIMER_REAL, &it, 0))
413           die(254, "failed to set alarm: %s", strerror(errno));
414
415         /* This bit's quite scummy.  There isn't a POSIX-standard way to wait
416          * for a finite time before giving up, so we use alarms.  But there
417          * also isn't a standard way to avoid a race between the alarm going
418          * off and us waiting for the semaphore, because semop doesn't have a
419          * signal set argument.  So instead we have the signal handler frob
420          * the semop parameter block as well as setting a flag.  Assuming
421          * that semop is actually a system call, then one of the following
422          * will happen: the signal will happen before the call, in which case
423          * it will set IPC_NOWAIT and semop will either succeed or fail
424          * immediately; the signal will happen during the call, while we're
425          * waiting for the semaphore, so semop will return with EINTR; or the
426          * signal will happen afterwards, by which point it's too late for us
427          * to care.
428          */
429         compiler_fence(buf, &sigflag, FENCE_END);
430         if (sigprocmask(SIG_UNBLOCK, &ss, 0))
431           die(254, "failed to unblock alarm signal: %s", strerror(errno));
432       }
433       break;
434   }
435
436   /* Wait for the semaphore. */
437   if ((rc = semop(semid, buf, 1)) < 0 &&
438       errno != EAGAIN &&
439       !(errno == EINTR && sigflag))
440     die(254, "failed to post semaphore: %s", strerror(errno));
441
442   /* Now we clean up again afterwards. */
443   update_now();
444
445   /* If we set an alarm, we must dismantle it and restore the previous
446    * situation.
447    */
448   if (f & f_alarm) {
449
450     /* We're messing with the alarm again, so mask it out temporarily. */
451     if (sigprocmask(SIG_BLOCK, &ss, &oss))
452       die(254, "failed to block alarm signal: %s", strerror(errno));
453
454     /* Turn off the alarm timer.  We don't need it any more. */
455     it.it_value.tv_sec = 0; it.it_value.tv_usec = 0;
456     if (setitimer(ITIMER_REAL, &it, 0))
457       die(254, "failed to disarm alarm timer: %s", strerror(errno));
458
459     /* At this point, if there's an alarm signal pending, it must be from the
460      * timer we set, and it's too late to do any good.  So just clear it and
461      * move on.  After this point, alarms will be from the previously
462      * established timer, and we should let them happen.
463      */
464     if (sigpending(&ps))
465       die(254, "failed to read pending signals: %s", strerror(errno));
466     if (sigismember(&ps, SIGALRM) && sigwait(&ss, &sig))
467       die(254, "failed to clear pending alarm: %s", strerror(errno));
468
469     /* Figure out how to restore the old timer. */
470     if (oit.it_value.tv_sec || oit.it_value.tv_usec) {
471       if (TV_CMP(&oit.it_value, >, &now))
472         TV_SUB(&oit.it_value, &oit.it_value, &now);
473       else {
474         /* We should have had an alarm by now.  Schedule one for when we
475          * unblock the signal again.
476          */
477         raise(SIGALRM);
478
479         /* Sort out the timer again. */
480         if (!oit.it_interval.tv_sec && !oit.it_interval.tv_usec)
481           oit.it_value.tv_sec = 0, oit.it_value.tv_usec = 0;
482         else {
483           TV_SUB(&tv, &now, &oit.it_value);
484           if (TV_CMP(&tv, <, &oit.it_interval))
485             TV_SUB(&oit.it_value, &oit.it_interval, &tv);
486           else {
487             /* We've overshot the previous deadline, and missed at least one
488              * repetition.  If this is bad, then we've already failed quite
489              * miserably.
490              */
491             oit.it_value = oit.it_interval;
492           }
493         }
494       }
495     }
496
497     /* We've now recovered the right settings, even if that's just to turn
498      * the alarm off, so go do that.
499      */
500     if ((oit.it_value.tv_sec || oit.it_value.tv_usec) &&
501         setitimer(ITIMER_REAL, &oit, 0))
502       die(254, "failed to restore old alarm settings: %s", strerror(errno));
503
504     /* Restore the old signal handler. */
505     if (sigaction(SIGALRM, &osa, 0))
506       die(254, "failed to restore old alarm handler: %s", strerror(errno));
507
508     /* And finally we can restore the previous signal mask. */
509     if (sigprocmask(SIG_SETMASK, &oss, 00))
510       die(254, "failed to restore old signal mask: %s", strerror(errno));
511   }
512
513   return (rc ? -1 : 0);
514 }
515
516 /*----- Main code ---------------------------------------------------------*/
517
518 static struct timeout timeout = { FOREVER };
519
520 static int cmd_mkfile(int argc, char *argv[])
521 {
522   mode_t mode = 0666, mask = 0;
523   const char *name = 0;
524   int of = 0;
525   unsigned f = 0;
526 #define f_bogus 1u
527 #define f_mode 2u
528
529   for (;;) {
530     static const struct option opts[] = {
531       { "mode",                 OPTF_ARGREQ,    0,      'm' },
532       { "exclusive",            0,              0,      'x' },
533       { 0,                      0,              0,      0 }
534     };
535     int o = mdwopt(argc, argv, "m:x", opts, 0, 0, OPTF_NOPROGNAME);
536
537     if (o < 0) break;
538     switch (o) {
539       case 'm':
540         mode = parse_int("mode", optarg, 8, 0, 07777);
541         f |= f_mode;
542         break;
543       case 'x': of |= O_EXCL; break;
544       default: f |= f_bogus; break;
545     }
546   }
547   argc -= optind; argv += optind;
548   if (argc < 1) f |= f_bogus;
549   else { name = argv[0]; argc--; argv++; }
550   if (argc) f |= f_bogus;
551   if (f & f_bogus) return (-1);
552
553   if (f & f_mode) mask = umask(0);
554   make_sema_file(name, of, mode);
555   if (f & f_mode) umask(mask);
556   return (0);
557
558 #undef f_bogus
559 #undef f_mode
560 }
561
562 static int cmd_new(int argc, char *argv[])
563 {
564   mode_t mode = 0, mask;
565   const char *name = 0;
566   unsigned init = 0;
567   int of = O_CREAT;
568   unsigned f = 0;
569 #define f_bogus 1u
570 #define f_mode 2u
571
572   for (;;) {
573     static const struct option opts[] = {
574       { "mode",                 OPTF_ARGREQ,    0,      'm' },
575       { "exclusive",            0,              0,      'x' },
576       { 0,                      0,              0,      0 }
577     };
578     int o = mdwopt(argc, argv, "m:x", opts, 0, 0, OPTF_NOPROGNAME);
579
580     if (o < 0) break;
581     switch (o) {
582       case 'm':
583         mode = parse_int("mode", optarg, 8, 0, 0777);
584         f |= f_mode;
585         break;
586       case 'x': of |= O_EXCL; break;
587       default: f |= f_bogus; break;
588     }
589   }
590   argc -= optind; argv += optind;
591   if (argc < 2) f |= f_bogus;
592   else {
593     name = argv[0];
594     init = parse_int("initial semaphore value", argv[1],
595                      0, 0, SEM_VALUE_MAX);
596     argc -= 2; argv += 2;
597   }
598   if (argc) f |= f_bogus;
599   if (f & f_bogus) return (-1);
600
601   if (!(f & f_mode)) {
602     mask = umask(0); umask(mask);
603     mode = ~mask & 0777;
604   }
605   open_sema(name, 0, of, mode, init, &timeout);
606   return (0);
607
608 #undef f_bogus
609 #undef f_mode
610 }
611
612 static int cmd_rm(int argc, char *argv[])
613 {
614   const char *name = 0;
615   unsigned f = 0, ff = 0;
616   int semid;
617 #define f_bogus 1u
618
619   for (;;) {
620     static const struct option opts[] = {
621       { "force",                0,              0,      'f' },
622       { 0,                      0,              0,      0 }
623     };
624     int o = mdwopt(argc, argv, "f", opts, 0, 0, OPTF_NOPROGNAME);
625
626     if (o < 0) break;
627     switch (o) {
628       case 'f': ff |= OF_PROBE; break;
629       default: f |= f_bogus; break;
630     }
631   }
632   argc -= optind; argv += optind;
633   if (!argc) f |= f_bogus;
634   else { name = argv[0]; argc--; argv++; }
635   if (argc) f |= f_bogus;
636   if (f & f_bogus) return (-1);
637
638   semid = open_sema(name, ff | OF_UNINIT, 0, 0, 0, &timeout);
639   if (semid >= 0) rm_sema(semid);
640
641   return (0);
642
643 #undef f_bogus
644 }
645
646 static int cmd_set(int argc, char *argv[])
647 {
648   const char *name = 0;
649   int semid;
650   unsigned n = 0;
651   unsigned f = 0;
652 #define f_bogus 1u
653
654   for (;;) {
655     static const struct option opts[] = {
656       { 0,                      0,              0,      0 }
657     };
658     int o = mdwopt(argc, argv, "", opts, 0, 0, OPTF_NOPROGNAME);
659
660     if (o < 0) break;
661     switch (o) {
662       default: f |= f_bogus; break;
663     }
664   }
665   argc -= optind; argv += optind;
666   if (argc < 2) f |= f_bogus;
667   else {
668     name = argv[0];
669     n = parse_int("count", argv[1], 0, 0, SEM_VALUE_MAX);
670     argc -= 2; argv += 2;
671   }
672   if (argc) f |= f_bogus;
673   if (f & f_bogus) return (-1);
674
675   semid = open_sema(name, 0, 0, 0, 0, &timeout);
676   set_sema(semid, n);
677   return (0);
678
679 #undef f_bogus
680 }
681
682 static int cmd_get(int argc, char *argv[])
683 {
684   const char *name = 0;
685   int semid;
686   unsigned f = 0;
687 #define f_bogus 1u
688
689   for (;;) {
690     static const struct option opts[] = {
691       { 0,                      0,              0,      0 }
692     };
693     int o = mdwopt(argc, argv, "", opts, 0, 0, OPTF_NOPROGNAME);
694
695     if (o < 0) break;
696     switch (o) {
697       default: f |= f_bogus; break;
698     }
699   }
700   argc -= optind; argv += optind;
701   if (!argc) f |= f_bogus;
702   else { name = argv[0]; argc--; argv++; }
703   if (argc) f |= f_bogus;
704   if (f & f_bogus) return (-1);
705
706   semid = open_sema(name, 0, 0, 0, 0, &timeout);
707   printf("%u\n", read_sema(semid));
708   return (0);
709
710 #undef f_bogus
711 }
712
713 static int cmd_post(int argc, char *argv[])
714 {
715   const char *name = 0;
716   int semid, n = 1;
717   unsigned f = 0;
718 #define f_bogus 1u
719
720   for (;;) {
721     static const struct option opts[] = {
722       { "count",                OPTF_ARGREQ,    0,      'n' },
723       { 0,                      0,              0,      0 }
724     };
725     int o = mdwopt(argc, argv, "n:", opts, 0, 0, OPTF_NOPROGNAME);
726
727     if (o < 0) break;
728     switch (o) {
729       case 'n': n = parse_int("count", optarg, 0, 1, SEM_VALUE_MAX); break;
730       default: f |= f_bogus; break;
731     }
732   }
733   argc -= optind; argv += optind;
734   if (!argc) f |= f_bogus;
735   else { name = argv[0]; argc--; argv++; }
736   if (argc) f |= f_bogus;
737   if (f & f_bogus) return (-1);
738
739   semid = open_sema(name, 0, 0, 0, 0, &timeout);
740   post_sema(semid, n);
741   return (0);
742
743 #undef f_bogus
744 }
745
746 static int cmd_wait(int argc, char *argv[])
747 {
748   const char *name = 0;
749   int semid, n = 1;
750   int flg = 0;
751   unsigned f = 0;
752 #define f_bogus 1u
753
754   for (;;) {
755     static const struct option opts[] = {
756       { "count",                OPTF_ARGREQ,    0,      'n' },
757       { 0,                      0,              0,      0 }
758     };
759     int o = mdwopt(argc, argv, "-n:", opts, 0, 0, OPTF_NOPROGNAME);
760
761     if (o < 0) break;
762     switch (o) {
763       case 'n': n = parse_int("count", optarg, 0, 1, SEM_VALUE_MAX); break;
764       case 0:
765         if (!name) name = optarg;
766         else { optind--; goto done_opts; }
767         break;
768       default: f |= f_bogus; break;
769     }
770   }
771 done_opts:
772   argc -= optind; argv += optind;
773   if (!name) {
774     if (!argc) f |= f_bogus;
775     else { name = argv[0]; argc--; argv++; }
776   }
777   if (argc) flg |= SEM_UNDO;
778   if (f & f_bogus) return (-1);
779
780   semid = open_sema(name, 0, 0, 0, 0, &timeout);
781   if (wait_sema(semid, n, flg, &timeout))
782     die(251, "semaphore busy");
783
784   if (argc) {
785     execvp(argv[0], argv);
786     die(254, "failed to exec `%s': %s", argv[0], strerror(errno));
787   }
788   return (0);
789
790 #undef f_bogus
791 }
792
793 static const struct subcmd {
794   const char *name;
795   int (*func)(int, char *[]);
796   const char *usage;
797 } subcmds[] = {
798   { "mkfile", cmd_mkfile,       "[-x] [-m MODE] NAME" },
799   { "new", cmd_new,             "[-x] [-m MODE] NAME VALUE" },
800   { "rm", cmd_rm,               "[-f] NAME" },
801   { "get", cmd_get,             "NAME" },
802   { "set", cmd_set,             "NAME VALUE" },
803   { "post", cmd_post,           "[-n COUNT] NAME" },
804   { "wait", cmd_wait,           "[-n COUNT] NAME [COMMAND ARGUMENTS ...]" },
805   { 0, 0,               0 }
806 };
807
808 static void version(FILE *fp)
809   { pquis(fp, "$, version " VERSION "\n"); }
810
811 static void usage(FILE *fp)
812 {
813   const struct subcmd *c;
814
815   fprintf(fp, "usage:\n");
816   for (c = subcmds; c->name; c++)
817     fprintf(fp, "\t%s [-OPTIONS] %s %s\n", QUIS, c->name, c->usage);
818 }
819
820 static void help(FILE *fp)
821 {
822   version(fp); putc('\n', fp);
823   usage(fp);
824   fprintf(fp, "\n\
825 Top-level options:\n\
826 \n\
827   -w, --wait=WHEN               How long to wait (`forever', `never', or\n\
828                                   nonnegative number followed by `s', `m',\n\
829                                   `h', `d')\n");
830 }
831
832 int main(int argc, char *argv[])
833 {
834   const struct subcmd *c;
835   int rc;
836   unsigned f = 0;
837 #define f_bogus 1u
838
839   ego(argv[0]);
840   update_now();
841   for (;;) {
842     static const struct option opts[] = {
843       { "help",                 0,              0,      'h' },
844       { "version",              0,              0,      'v' },
845       { "wait",                 OPTF_ARGREQ,    0,      'w' },
846       { 0,                      0,              0,      0 }
847     };
848     int o = mdwopt(argc, argv, "+hvw:", opts, 0, 0, 0);
849
850     if (o < 0) break;
851     switch (o) {
852       case 'h': help(stdout); exit(0); break;
853       case 'v': version(stdout); exit(0); break;
854       case 'w': parse_timeout(optarg, &timeout); break;
855       default: f |= f_bogus; break;
856     }
857   }
858   argc -= optind; argv += optind;
859   if (!argc) f |= f_bogus;
860   if (f & f_bogus) { usage(stderr); exit(253); }
861
862   optind = 0;
863   for (c = subcmds; c->name; c++)
864     if (strcmp(argv[0], c->name) == 0) goto found;
865   die(253, "unknown command `%s'", argv[0]);
866 found:
867   argc--; argv++;
868   rc = c->func(argc, argv);
869   if (rc < 0) {
870     fprintf(stderr, "usage: %s %s %s\n", QUIS, c->name, c->usage);
871     exit(253);
872   }
873   return (rc);
874
875 #undef f_bogus
876 }
877
878 /*----- That's all, folks -------------------------------------------------*/