]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
waitpid: add timeout support
authorThomas Weißschuh <thomas@t-8ch.de>
Tue, 3 Jan 2023 23:57:53 +0000 (23:57 +0000)
committerThomas Weißschuh <thomas@t-8ch.de>
Fri, 6 Jan 2023 14:37:55 +0000 (14:37 +0000)
misc-utils/waitpid.1.adoc
misc-utils/waitpid.c
tests/expected/misc/waitpid-normal [moved from tests/expected/misc/waitpid with 100% similarity]
tests/expected/misc/waitpid-timeout [new file with mode: 0644]
tests/ts/misc/waitpid

index 864610fd36be88fc922a90f1fc18e96e82ae5a92..4b566dc8805e4f73f0b8ff21288f8025d542de17 100644 (file)
@@ -12,7 +12,7 @@ waitpid - utility to wait for arbitrary processes
 
 == SYNOPSIS
 
-*waitpid* [-v] pid...
+*waitpid* [-v] [*--timeout*|*-t* _seconds_] pid...
 
 == DESCRIPTION
 
@@ -26,6 +26,9 @@ exited.
 *-v*, *--verbose*::
 Be more verbose.
 
+*-t*, *--timeout* _seconds_::
+Maximum wait time.
+
 include::man-common/help-version.adoc[]
 
 == EXIT STATUS
@@ -38,6 +41,8 @@ success
 unspecified failure
 *2*::
 system does not provide necessary functionality
+*3*::
+timeout expired
 
 == AUTHORS
 
index f9bd62634d0585938eac4d91d148d43293c17a72..64f01373cc2f43f220b801613791639b446280e0 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <sys/epoll.h>
+#include <sys/timerfd.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include "xalloc.h"
 #include "strutils.h"
 #include "exitcodes.h"
+#include "timeutils.h"
+
+#define EXIT_TIMEOUT_EXPIRED 3
+
+#define TIMEOUT_SOCKET_IDX UINT64_MAX
 
 #define err_nosys(exitcode, ...) \
        err(errno == ENOSYS ? EXIT_NOTSUPP : exitcode, __VA_ARGS__)
 
 static bool verbose = false;
+static struct timespec timeout;
 
 static pid_t *parse_pids(size_t n_strings, char * const *strings)
 {
@@ -62,19 +69,48 @@ static int *open_pidfds(size_t n_pids, pid_t *pids)
        return pidfds;
 }
 
-static void add_listeners(int epll, size_t n_pids, int * const pidfds)
+static int open_timeoutfd(void)
+{
+       int fd;
+       struct itimerspec timer = {};
+
+       if (!timeout.tv_sec && !timeout.tv_nsec)
+               return -1;
+       
+       timer.it_value = timeout;
+
+       fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+       if (fd == -1)
+               err_nosys(EXIT_FAILURE, _("could not create timerfd"));
+
+       if (timerfd_settime(fd, 0, &timer, NULL))
+               err_nosys(EXIT_FAILURE, _("could not set timer"));
+
+       return fd;
+
+}
+
+static void add_listeners(int epll, size_t n_pids, int * const pidfds, int timeoutfd)
 {
+       struct epoll_event evt = {
+               .events = EPOLLIN,
+       };
+
+       if (timeoutfd != -1) {
+               evt.data.u64 = TIMEOUT_SOCKET_IDX;
+               if (epoll_ctl(epll, EPOLL_CTL_ADD, timeoutfd, &evt))
+                       err_nosys(EXIT_FAILURE, _("could not add timerfd"));
+       }
+
        for (size_t i = 0; i < n_pids; i++) {
-               struct epoll_event evt = {
-                       .events = EPOLLIN,
-                       .data = { .u64 = i },
-               };
+               evt.data.u64 = i;
                if (epoll_ctl(epll, EPOLL_CTL_ADD, pidfds[i], &evt))
                        err_nosys(EXIT_FAILURE, _("could not add listener"));
        }
 }
 
-static void wait_for_exits(int epll, size_t n_pids, pid_t * const pids, int * const pidfds)
+static void wait_for_exits(int epll, size_t n_pids, pid_t * const pids,
+                          int * const pidfds)
 {
        while (n_pids) {
                struct epoll_event evt;
@@ -87,6 +123,11 @@ static void wait_for_exits(int epll, size_t n_pids, pid_t * const pids, int * co
                        else
                                err_nosys(EXIT_FAILURE, _("failure during wait"));
                }
+               if (evt.data.u64 == TIMEOUT_SOCKET_IDX) {
+                       if (verbose)
+                               printf(_("Timeout expired\n"));
+                       exit(EXIT_TIMEOUT_EXPIRED);
+               }
                if (verbose)
                        printf(_("PID %d finished\n"), pids[evt.data.u64]);
                assert((size_t) ret <= n_pids);
@@ -104,7 +145,8 @@ static void __attribute__((__noreturn__)) usage(void)
        fprintf(out, _(" %s [options] pid...\n"), program_invocation_short_name);
 
        fputs(USAGE_OPTIONS, out);
-       fputs(_(" -v, --verbose         be more verbose\n"), out);
+       fputs(_(" -v, --verbose           be more verbose\n"), out);
+       fputs(_(" -t, --timeout=<timeout> wait at most timeout seconds\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fprintf(out, USAGE_HELP_OPTIONS(23));
@@ -118,17 +160,22 @@ static int parse_options(int argc, char **argv)
 {
        int c;
        static const struct option longopts[] = {
-               { "verbose", no_argument, NULL, 'v' },
-               { "version", no_argument, NULL, 'V' },
-               { "help",    no_argument, NULL, 'h' },
+               { "verbose", no_argument,       NULL, 'v' },
+               { "timeout", required_argument, NULL, 't' },
+               { "version", no_argument,       NULL, 'V' },
+               { "help",    no_argument,       NULL, 'h' },
                { NULL, 0, NULL, 0 },
        };
 
-       while ((c = getopt_long (argc, argv, "vVh", longopts, NULL)) != -1) {
+       while ((c = getopt_long (argc, argv, "vVht:", longopts, NULL)) != -1) {
                switch (c) {
                case 'v':
                        verbose = true;
                        break;
+               case 't':
+                       strtotimespec_or_err(optarg, &timeout,
+                                            _("Could not parse timeout"));
+                       break;
                case 'V':
                        print_version(EXIT_SUCCESS);
                case 'h':
@@ -145,7 +192,7 @@ int main(int argc, char **argv)
 {
        int pid_idx, epoll;
        size_t n_pids;
-       int *pidfds;
+       int *pidfds, timeoutfd;
 
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
@@ -159,10 +206,11 @@ int main(int argc, char **argv)
        pid_t *pids = parse_pids(argc - pid_idx, argv + pid_idx);
 
        pidfds = open_pidfds(n_pids, pids);
+       timeoutfd = open_timeoutfd();
        epoll = epoll_create(n_pids);
        if (epoll == -1)
                err_nosys(EXIT_FAILURE, _("could not create epoll"));
 
-       add_listeners(epoll, n_pids, pidfds);
+       add_listeners(epoll, n_pids, pidfds, timeoutfd);
        wait_for_exits(epoll, n_pids, pids, pidfds);
 }
diff --git a/tests/expected/misc/waitpid-timeout b/tests/expected/misc/waitpid-timeout
new file mode 100644 (file)
index 0000000..9b003c7
--- /dev/null
@@ -0,0 +1,2 @@
+Timeout expired
+3
index 033bd40719a4e3af9205261062fb601e8e2cbcdb..effdd2fe064079a5895b26fecf8570c74debb465 100755 (executable)
@@ -22,6 +22,8 @@ ts_init "$*"
 
 ts_check_test_command "$TS_CMD_WAITPID"
 
+ts_init_subtest normal
+
 (sleep 0.2; echo 1 >> "$TS_OUTPUT") &
 BG1="$!"
 
@@ -34,4 +36,11 @@ ts_skip_exitcode_not_supported
 
 echo 4 >> "$TS_OUTPUT"
 
+ts_finalize_subtest
+
+ts_init_subtest timeout
+"$TS_CMD_WAITPID" -v -t 0.1 1 >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+echo $? >> "$TS_OUTPUT"
+ts_finalize_subtest
+
 ts_finalize