mtimeout.1: Use correct dash for number ranges.
[misc] / sema.c
CommitLineData
300a556d
MW
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? */
56union semun {
57 int val;
58 struct semid_ds *buf;
59 unsigned short *array;
60};
61
62/*----- Random utilities --------------------------------------------------*/
63
64static 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
82enum { NEVER, UNTIL, FOREVER };
83
84struct timeout {
85 int wait;
86 struct timeval tv;
87};
88
89static struct timeval now;
90
91static void update_now(void) { int rc = gettimeofday(&now, 0); assert(!rc); }
92
93static void f2tv(double t, struct timeval *tv)
94 { tv->tv_sec = t; tv->tv_usec = 1e6*(t - tv->tv_sec); }
95
96static 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
134enum { NTY_PATH };
135struct nameinfo {
136 unsigned ty;
137 union {
138 const char *path;
139 } u;
140};
141
142static 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
156static 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
169static 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
188static 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 */
255await:
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. */
283fail:
284 die(254, "failed to open semaphore set: %s", strerror(errno));
285
286notready:
287 die(252, "semaphore set not ready");
288}
289
290static 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
296static 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
305static 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
314static 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
325static struct sembuf *sigsembuf;
326static int sigflag;
327
328static void wait_alarm(int hunoz)
329 { sigflag = 1; if (sigsembuf) sigsembuf->sem_flg |= IPC_NOWAIT; }
330
331static 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
518static struct timeout timeout = { FOREVER };
519
520static 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
562static 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
612static 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
646static 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
682static 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
713static 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
746static 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 }
771done_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
793static 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
808static void version(FILE *fp)
809 { pquis(fp, "$, version " VERSION "\n"); }
810
811static 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
820static void help(FILE *fp)
821{
822 version(fp); putc('\n', fp);
823 usage(fp);
824 fprintf(fp, "\n\
825Top-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
832int 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]);
866found:
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 -------------------------------------------------*/