summaryrefslogtreecommitdiff
path: root/corebinutils/pwait/pwait.c
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:28:04 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:28:04 +0300
commit681e61b5ab23760db91aea6b30cd38ef3526e308 (patch)
tree5227011b83263515d7f292cbd872bacb01e58c7c /corebinutils/pwait/pwait.c
parent76166d4f612e1318ffe99493f9d8b578887b1278 (diff)
parent2dea7718929906912270d2d02cc19fbbf2848579 (diff)
downloadProject-Tick-681e61b5ab23760db91aea6b30cd38ef3526e308.tar.gz
Project-Tick-681e61b5ab23760db91aea6b30cd38ef3526e308.zip
Add 'corebinutils/pwait/' from commit '2dea7718929906912270d2d02cc19fbbf2848579'
git-subtree-dir: corebinutils/pwait git-subtree-mainline: 76166d4f612e1318ffe99493f9d8b578887b1278 git-subtree-split: 2dea7718929906912270d2d02cc19fbbf2848579
Diffstat (limited to 'corebinutils/pwait/pwait.c')
-rw-r--r--corebinutils/pwait/pwait.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/corebinutils/pwait/pwait.c b/corebinutils/pwait/pwait.c
new file mode 100644
index 0000000000..323a798e86
--- /dev/null
+++ b/corebinutils/pwait/pwait.c
@@ -0,0 +1,332 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004-2009, Jilles Tjoelker
+ * Copyright (c) 2026 Project Tick.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/timerfd.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdint.h>
+
+static int
+x_pidfd_open(pid_t pid)
+{
+#if defined(__NR_pidfd_open)
+ return syscall(__NR_pidfd_open, pid, 0);
+#elif defined(SYS_pidfd_open)
+ return syscall(SYS_pidfd_open, pid, 0);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+// We need to keep track of pids, whether they are done, and their open pidfd
+struct target_pid {
+ long pid;
+ int fd;
+ bool done;
+};
+
+static int
+pidcmp(const void *a, const void *b)
+{
+ const struct target_pid *pa = a;
+ const struct target_pid *pb = b;
+ return (pa->pid > pb->pid ? 1 : pa->pid < pb->pid ? -1 : 0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * pwait - wait for processes to terminate
+ */
+int
+main(int argc, char *argv[])
+{
+ struct target_pid *targets = NULL;
+ char *end, *s;
+ double timeout = 0.0;
+ long pid;
+ pid_t mypid;
+ int ntargets = 0, nallocated = 0;
+ int ndone = 0, nleft = 0, opt, ret;
+ bool oflag = false, pflag = false, tflag = false, verbose = false;
+
+ while ((opt = getopt(argc, argv, "opt:v")) != -1) {
+ switch (opt) {
+ case 'o':
+ oflag = true;
+ break;
+ case 'p':
+ pflag = true;
+ break;
+ case 't':
+ tflag = true;
+ errno = 0;
+ timeout = strtod(optarg, &end);
+ if (end == optarg || errno == ERANGE || timeout < 0) {
+ errx(EX_DATAERR, "timeout value");
+ }
+ switch (*end) {
+ case '\0':
+ break;
+ case 's':
+ end++;
+ break;
+ case 'h':
+ timeout *= 60;
+ /* FALLTHROUGH */
+ case 'm':
+ timeout *= 60;
+ end++;
+ break;
+ default:
+ errx(EX_DATAERR, "timeout unit");
+ }
+ if (*end != '\0') {
+ errx(EX_DATAERR, "timeout unit");
+ }
+ if (timeout > 100000000L) {
+ errx(EX_DATAERR, "timeout value");
+ }
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ usage();
+ }
+
+ mypid = getpid();
+ long pid_max = 4194304;
+ FILE *f = fopen("/proc/sys/kernel/pid_max", "r");
+ if (f != NULL) {
+ if (fscanf(f, "%ld", &pid_max) != 1) {
+ pid_max = 4194304;
+ }
+ fclose(f);
+ }
+
+ for (int n = 0; n < argc; n++) {
+ s = argv[n];
+ /* Undocumented Solaris compat */
+ if (strncmp(s, "/proc/", 6) == 0) {
+ s += 6;
+ }
+ errno = 0;
+ pid = strtol(s, &end, 10);
+ if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) {
+ warnx("%s: bad process id", s);
+ continue;
+ }
+ if (pid == mypid) {
+ warnx("%s: skipping my own pid", s);
+ continue;
+ }
+
+ /* Check for duplicates */
+ bool dup = false;
+ for (int i = 0; i < ntargets; i++) {
+ if (targets[i].pid == pid) {
+ dup = true;
+ break;
+ }
+ }
+ if (dup) {
+ continue;
+ }
+
+ if (ntargets >= nallocated) {
+ nallocated = nallocated == 0 ? 16 : nallocated * 2;
+ targets = realloc(targets, nallocated * sizeof(struct target_pid));
+ if (targets == NULL) {
+ err(EX_OSERR, "realloc");
+ }
+ }
+
+ targets[ntargets].pid = pid;
+ targets[ntargets].done = false;
+ int fd = x_pidfd_open((pid_t)pid);
+ if (fd == -1) {
+ /* Not found or invalid */
+ warn("%ld", pid);
+ targets[ntargets].fd = -1;
+ targets[ntargets].done = true;
+ ndone++;
+ } else {
+ targets[ntargets].fd = fd;
+ nleft++;
+ }
+ ntargets++;
+ }
+
+ /* Sort targets ascending by PID to match BSD behavior */
+ qsort(targets, ntargets, sizeof(struct target_pid), pidcmp);
+
+ int timer_fd = -1;
+ if ((ndone == 0 || !oflag) && nleft > 0 && tflag) {
+ timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (timer_fd == -1) {
+ err(EX_OSERR, "timerfd_create");
+ }
+ struct itimerspec its = {};
+ its.it_value.tv_sec = (time_t)timeout;
+ its.it_value.tv_nsec = (long)((timeout - its.it_value.tv_sec) * 1000000000UL);
+ if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) {
+ /* timerfd needs non-zero value to arm */
+ its.it_value.tv_nsec = 1;
+ }
+ if (timerfd_settime(timer_fd, 0, &its, NULL) == -1) {
+ err(EX_OSERR, "timerfd_settime");
+ }
+ }
+
+ struct pollfd *pfds = calloc(ntargets + 1, sizeof(struct pollfd));
+ if (pfds == NULL) {
+ err(EX_OSERR, "calloc");
+ }
+
+ ret = EX_OK;
+ while ((ndone == 0 || !oflag) && ret == EX_OK && nleft > 0) {
+ int npoll = 0;
+ if (timer_fd != -1) {
+ pfds[npoll].fd = timer_fd;
+ pfds[npoll].events = POLLIN;
+ pfds[npoll].revents = 0;
+ npoll++;
+ }
+
+ for (int i = 0; i < ntargets; i++) {
+ if (!targets[i].done) {
+ pfds[npoll].fd = targets[i].fd;
+ pfds[npoll].events = POLLIN | POLLHUP | POLLERR;
+ pfds[npoll].revents = 0;
+ npoll++;
+ }
+ }
+
+ int n = poll(pfds, npoll, -1);
+ if (n == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ err(EX_OSERR, "poll");
+ }
+
+ if (timer_fd != -1 && (pfds[0].revents & POLLIN)) {
+ uint64_t expirations;
+ (void)read(timer_fd, &expirations, sizeof(expirations));
+ if (verbose) {
+ printf("timeout\n");
+ }
+ ret = 124;
+ }
+
+ int pfds_idx = timer_fd != -1 ? 1 : 0;
+ for (int i = 0; i < ntargets; i++) {
+ if (targets[i].done) {
+ continue;
+ }
+ if (pfds[pfds_idx].revents & (POLLIN | POLLHUP | POLLERR)) {
+ pid = targets[i].pid;
+ if (verbose) {
+ siginfo_t info = {};
+ int wret = waitid(P_PIDFD, targets[i].fd, &info, WEXITED | WNOHANG);
+ if (wret == 0 && info.si_pid != 0) {
+ if (info.si_code == CLD_EXITED) {
+ printf("%ld: exited with status %d.\n",
+ pid, info.si_status);
+ } else if (info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED) {
+ printf("%ld: killed by signal %d.\n",
+ pid, info.si_status);
+ } else {
+ printf("%ld: terminated.\n", pid);
+ }
+ } else {
+ printf("%ld: terminated.\n", pid);
+ }
+ }
+ close(targets[i].fd);
+ targets[i].fd = -1;
+ targets[i].done = true;
+ ndone++;
+ nleft--;
+ }
+ pfds_idx++;
+ }
+ }
+
+ if (timer_fd != -1) {
+ close(timer_fd);
+ }
+ free(pfds);
+
+ if (pflag) {
+ for (int i = 0; i < ntargets; i++) {
+ if (!targets[i].done) {
+ printf("%ld\n", targets[i].pid);
+ }
+ }
+ }
+
+ free(targets);
+ exit(ret);
+}