From: Thomas Weißschuh Date: Tue, 3 Jan 2023 23:57:53 +0000 (+0000) Subject: waitpid: add timeout support X-Git-Tag: v2.39-rc1~190^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8528bd29aa63799edbf5f9e9c815151d731d2e80;p=thirdparty%2Futil-linux.git waitpid: add timeout support --- diff --git a/misc-utils/waitpid.1.adoc b/misc-utils/waitpid.1.adoc index 864610fd36..4b566dc880 100644 --- a/misc-utils/waitpid.1.adoc +++ b/misc-utils/waitpid.1.adoc @@ -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 diff --git a/misc-utils/waitpid.c b/misc-utils/waitpid.c index f9bd62634d..64f01373cc 100644 --- a/misc-utils/waitpid.c +++ b/misc-utils/waitpid.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -33,11 +34,17 @@ #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= 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 b/tests/expected/misc/waitpid-normal similarity index 100% rename from tests/expected/misc/waitpid rename to tests/expected/misc/waitpid-normal diff --git a/tests/expected/misc/waitpid-timeout b/tests/expected/misc/waitpid-timeout new file mode 100644 index 0000000000..9b003c7156 --- /dev/null +++ b/tests/expected/misc/waitpid-timeout @@ -0,0 +1,2 @@ +Timeout expired +3 diff --git a/tests/ts/misc/waitpid b/tests/ts/misc/waitpid index 033bd40719..effdd2fe06 100755 --- a/tests/ts/misc/waitpid +++ b/tests/ts/misc/waitpid @@ -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