3 * Operations on SysV semaphores
5 * (c) 2016 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
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.
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.
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.
25 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
47 #include <mLib/mdwopt.h>
48 #include <mLib/quis.h>
49 #include <mLib/report.h>
55 /* Oh, for pity's sake why did nobody do this for us? */
59 unsigned short *array
;
62 /*----- Random utilities --------------------------------------------------*/
64 static int parse_int(const char *what
, const char *p
,
65 int radix
, int min
, int max
)
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
);
80 /*----- Timeout handling --------------------------------------------------*/
82 enum { NEVER
, UNTIL
, FOREVER
};
89 static struct timeval now
;
91 static void update_now(void) { int rc
= gettimeofday(&now
, 0); assert(!rc
); }
93 static void f2tv(double t
, struct timeval
*tv
)
94 { tv
->tv_sec
= t
; tv
->tv_usec
= 1e6
*(t
- tv
->tv_sec
); }
96 static void parse_timeout(const char *p
, struct timeout
*t
)
101 time_t t_max
= timemax();
104 if (strcmp(p
, "forever") == 0)
106 else if (strcmp(p
, "none") == 0 || strcmp(p
, "never") == 0)
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
;
114 switch (tolower((unsigned char )*q
)) {
118 case 's': q
++; break;
120 if (*q
) errno
= EINVAL
;
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
); }
130 /*----- Semaphore operations ----------------------------------------------*/
132 #define SEMA_PROJ 0xd1 /* fair die roll */
142 static void parse_name(const char *name
, struct nameinfo
*ni
)
144 size_t plen
= strcspn(name
, ":/");
145 switch (name
[plen
]) {
146 case 0: case '/': ni
->ty
= NTY_PATH
; ni
->u
.path
= name
; break;
148 if (strncmp(name
, "file:", 5) == 0)
149 { ni
->ty
= NTY_PATH
; ni
->u
.path
= name
+ plen
+ 1; }
151 die(253, "unknown name type `%.*s'", (int)plen
, name
);
156 static int sema_initialized_p(int semid
)
162 if (semctl(semid
, 0, IPC_STAT
, u
) < 0) {
163 die(254, "failed to check that semaphore set is initialized: %s",
166 return (!!sem
.sem_otime
);
169 static void make_sema_file(const char *name
, int of
, mode_t mode
)
174 parse_name(name
, &ni
);
175 if (ni
.ty
!= NTY_PATH
)
176 die(253, "semaphore name `%s' doesn't designate a path", name
);
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
));
188 static int open_sema(const char *name
, unsigned f
, int of
,
189 mode_t mode
, int ival
,
190 const struct timeout
*t
)
192 int ff
= mode
& 0777, rc
, nrace
= 5;
195 struct sembuf buf
[1];
198 struct timeval tv
, ttv
;
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
));
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
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);
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.
232 if ((semid
= semget(k
, 1, ff
| IPC_CREAT
| IPC_EXCL
)) >= 0) break;
233 else if ((ff
& O_EXCL
) || errno
!= EEXIST
|| nrace
--) goto fail
;
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
241 u
.val
= ival ?
0 : 1;
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
));
252 /* Make sure that the semaphore set is actually initialized before we
256 if ((f
& OF_UNINIT
) || sema_initialized_p(semid
)) return (semid
);
257 if (t
->wait
== NEVER
) goto notready
;
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
;
267 TV_SUB(&ttv
, &tv
, &now
);
268 rc
= select(0, 0, 0, 0, &ttv
);
271 else if (errno
!= EINTR
)
272 die(254, "unexpected error from select: %s", strerror(errno
));
274 if (sema_initialized_p(semid
)) return (0);
282 /* Report a get failure. */
284 die(254, "failed to open semaphore set: %s", strerror(errno
));
287 die(252, "semaphore set not ready");
290 static void rm_sema(int semid
)
292 if (semctl(semid
, 0, IPC_RMID
))
293 die(254, "failed to remove semaphore set: %s", strerror(errno
));
296 static unsigned read_sema(int semid
)
300 if ((val
= semctl(semid
, 0, GETVAL
)) < 0)
301 die(254, "failed to read semaphore value: %s", strerror(errno
));
305 static void set_sema(int semid
, unsigned val
)
310 if (semctl(semid
, 0, SETVAL
, u
) < 0)
311 die(254, "failed to set semaphore value: %s", strerror(errno
));
314 static void post_sema(int semid
, int by
)
316 struct sembuf buf
[1];
321 if (semop(semid
, buf
, 1))
322 die(254, "failed to post semaphore: %s", strerror(errno
));
325 static struct sembuf
*sigsembuf
;
328 static void wait_alarm(int hunoz
)
329 { sigflag
= 1; if (sigsembuf
) sigsembuf
->sem_flg
|= IPC_NOWAIT
; }
331 static int wait_sema(int semid
, int by
, int flg
, const struct timeout
*t
)
333 struct sembuf buf
[1];
334 struct sigaction sa
, osa
;
335 sigset_t ss
, oss
, ps
;
337 struct itimerval it
, oit
;
342 /* Set up the command buffer. */
345 buf
[0].sem_flg
= flg
;
347 /* Modify it according to the timeout settings. */
352 buf
[0].sem_flg
|= IPC_NOWAIT
;
356 /* Wait until it's ready. */
360 /* Wait until the specified time. This is fiddly, and will likely
361 * require setting an alarm.
364 /* If the timeout is already in the past, then don't bother with the
365 * alarm, and simply don't wait.
367 if (TV_CMP(&t
->tv
, <=, &now
)) {
368 buf
[0].sem_flg
|= IPC_NOWAIT
;
372 /* Find the current alarm setting. */
373 if (getitimer(ITIMER_REAL
, &oit
)) {
374 die(254, "failed to read current alarm setting: %s",
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.
382 if (!oit
.it_value
.tv_sec
&& !oit
.it_value
.tv_usec
)
385 TV_ADD(&oit
.it_value
, &oit
.it_value
, &now
);
386 if (TV_CMP(&t
->tv
, <, &oit
.it_value
)) f
|= f_alarm
;
389 /* If we are setting an alarm, then now is the time to mess about with
390 * signals and timers.
394 /* Mask out the signal while we fiddle with the settings. */
396 sigaddset(&ss
, SIGALRM
);
397 if (sigprocmask(SIG_BLOCK
, &ss
, &oss
))
398 die(254, "failed to block alarm signal: %s", strerror(errno
));
400 /* Establish our new signal handler. */
403 sa
.sa_handler
= wait_alarm
;
406 if (sigaction(SIGALRM
, &sa
, &osa
))
407 die(254, "failed to set alarm handler: %s", strerror(errno
));
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
));
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
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
));
436 /* Wait for the semaphore. */
437 if ((rc
= semop(semid
, buf
, 1)) < 0 &&
439 !(errno
== EINTR
&& sigflag
))
440 die(254, "failed to post semaphore: %s", strerror(errno
));
442 /* Now we clean up again afterwards. */
445 /* If we set an alarm, we must dismantle it and restore the previous
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
));
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
));
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.
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
));
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
);
474 /* We should have had an alarm by now. Schedule one for when we
475 * unblock the signal again.
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;
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
);
487 /* We've overshot the previous deadline, and missed at least one
488 * repetition. If this is bad, then we've already failed quite
491 oit
.it_value
= oit
.it_interval
;
497 /* We've now recovered the right settings, even if that's just to turn
498 * the alarm off, so go do that.
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
));
504 /* Restore the old signal handler. */
505 if (sigaction(SIGALRM
, &osa
, 0))
506 die(254, "failed to restore old alarm handler: %s", strerror(errno
));
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
));
513 return (rc ?
-1 : 0);
516 /*----- Main code ---------------------------------------------------------*/
518 static struct timeout timeout
= { FOREVER
};
520 static int cmd_mkfile(int argc
, char *argv
[])
522 mode_t mode
= 0666, mask
= 0;
523 const char *name
= 0;
530 static const struct option opts
[] = {
531 { "mode", OPTF_ARGREQ
, 0, 'm' },
532 { "exclusive", 0, 0, 'x' },
535 int o
= mdwopt(argc
, argv
, "m:x", opts
, 0, 0, OPTF_NOPROGNAME
);
540 mode
= parse_int("mode", optarg
, 8, 0, 07777);
543 case 'x': of
|= O_EXCL
; break;
544 default: f
|= f_bogus
; break;
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);
553 if (f
& f_mode
) mask
= umask(0);
554 make_sema_file(name
, of
, mode
);
555 if (f
& f_mode
) umask(mask
);
562 static int cmd_new(int argc
, char *argv
[])
564 mode_t mode
= 0, mask
;
565 const char *name
= 0;
573 static const struct option opts
[] = {
574 { "mode", OPTF_ARGREQ
, 0, 'm' },
575 { "exclusive", 0, 0, 'x' },
578 int o
= mdwopt(argc
, argv
, "m:x", opts
, 0, 0, OPTF_NOPROGNAME
);
583 mode
= parse_int("mode", optarg
, 8, 0, 0777);
586 case 'x': of
|= O_EXCL
; break;
587 default: f
|= f_bogus
; break;
590 argc
-= optind
; argv
+= optind
;
591 if (argc
< 2) f
|= f_bogus
;
594 init
= parse_int("initial semaphore value", argv
[1],
595 0, 0, SEM_VALUE_MAX
);
596 argc
-= 2; argv
+= 2;
598 if (argc
) f
|= f_bogus
;
599 if (f
& f_bogus
) return (-1);
602 mask
= umask(0); umask(mask
);
605 open_sema(name
, 0, of
, mode
, init
, &timeout
);
612 static int cmd_rm(int argc
, char *argv
[])
614 const char *name
= 0;
615 unsigned f
= 0, ff
= 0;
620 static const struct option opts
[] = {
621 { "force", 0, 0, 'f' },
624 int o
= mdwopt(argc
, argv
, "f", opts
, 0, 0, OPTF_NOPROGNAME
);
628 case 'f': ff
|= OF_PROBE
; break;
629 default: f
|= f_bogus
; break;
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);
638 semid
= open_sema(name
, ff
| OF_UNINIT
, 0, 0, 0, &timeout
);
639 if (semid
>= 0) rm_sema(semid
);
646 static int cmd_set(int argc
, char *argv
[])
648 const char *name
= 0;
655 static const struct option opts
[] = {
658 int o
= mdwopt(argc
, argv
, "", opts
, 0, 0, OPTF_NOPROGNAME
);
662 default: f
|= f_bogus
; break;
665 argc
-= optind
; argv
+= optind
;
666 if (argc
< 2) f
|= f_bogus
;
669 n
= parse_int("count", argv
[1], 0, 0, SEM_VALUE_MAX
);
670 argc
-= 2; argv
+= 2;
672 if (argc
) f
|= f_bogus
;
673 if (f
& f_bogus
) return (-1);
675 semid
= open_sema(name
, 0, 0, 0, 0, &timeout
);
682 static int cmd_get(int argc
, char *argv
[])
684 const char *name
= 0;
690 static const struct option opts
[] = {
693 int o
= mdwopt(argc
, argv
, "", opts
, 0, 0, OPTF_NOPROGNAME
);
697 default: f
|= f_bogus
; break;
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);
706 semid
= open_sema(name
, 0, 0, 0, 0, &timeout
);
707 printf("%u\n", read_sema(semid
));
713 static int cmd_post(int argc
, char *argv
[])
715 const char *name
= 0;
721 static const struct option opts
[] = {
722 { "count", OPTF_ARGREQ
, 0, 'n' },
725 int o
= mdwopt(argc
, argv
, "n:", opts
, 0, 0, OPTF_NOPROGNAME
);
729 case 'n': n
= parse_int("count", optarg
, 0, 1, SEM_VALUE_MAX
); break;
730 default: f
|= f_bogus
; break;
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);
739 semid
= open_sema(name
, 0, 0, 0, 0, &timeout
);
746 static int cmd_wait(int argc
, char *argv
[])
748 const char *name
= 0;
755 static const struct option opts
[] = {
756 { "count", OPTF_ARGREQ
, 0, 'n' },
759 int o
= mdwopt(argc
, argv
, "-n:", opts
, 0, 0, OPTF_NOPROGNAME
);
763 case 'n': n
= parse_int("count", optarg
, 0, 1, SEM_VALUE_MAX
); break;
765 if (!name
) name
= optarg
;
766 else { optind
--; goto done_opts
; }
768 default: f
|= f_bogus
; break;
772 argc
-= optind
; argv
+= optind
;
774 if (!argc
) f
|= f_bogus
;
775 else { name
= argv
[0]; argc
--; argv
++; }
777 if (argc
) flg
|= SEM_UNDO
;
778 if (f
& f_bogus
) return (-1);
780 semid
= open_sema(name
, 0, 0, 0, 0, &timeout
);
781 if (wait_sema(semid
, n
, flg
, &timeout
))
782 die(251, "semaphore busy");
785 execvp(argv
[0], argv
);
786 die(254, "failed to exec `%s': %s", argv
[0], strerror(errno
));
793 static const struct subcmd
{
795 int (*func
)(int, char *[]);
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 ...]" },
808 static void version(FILE *fp
)
809 { pquis(fp
, "$, version " VERSION
"\n"); }
811 static void usage(FILE *fp
)
813 const struct subcmd
*c
;
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
);
820 static void help(FILE *fp
)
822 version(fp
); putc('\n', fp
);
825 Top-level options:\n\
827 -w, --wait=WHEN How long to wait (`forever', `never', or\n\
828 nonnegative number followed by `s', `m',\n\
832 int main(int argc
, char *argv
[])
834 const struct subcmd
*c
;
842 static const struct option opts
[] = {
843 { "help", 0, 0, 'h' },
844 { "version", 0, 0, 'v' },
845 { "wait", OPTF_ARGREQ
, 0, 'w' },
848 int o
= mdwopt(argc
, argv
, "+hvw:", opts
, 0, 0, 0);
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;
858 argc
-= optind
; argv
+= optind
;
859 if (!argc
) f
|= f_bogus
;
860 if (f
& f_bogus
) { usage(stderr
); exit(253); }
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]);
868 rc
= c
->func(argc
, argv
);
870 fprintf(stderr
, "usage: %s %s %s\n", QUIS
, c
->name
, c
->usage
);
878 /*----- That's all, folks -------------------------------------------------*/