Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F164631957
D58385.id182621.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D58385.id182621.diff
View Options
diff --git a/bin/pwait/tests/Makefile b/bin/pwait/tests/Makefile
--- a/bin/pwait/tests/Makefile
+++ b/bin/pwait/tests/Makefile
@@ -1,3 +1,4 @@
+ATF_TESTS_C= pwait_reap
ATF_TESTS_SH= pwait_test
.include <bsd.test.mk>
diff --git a/bin/pwait/tests/pwait_reap.c b/bin/pwait/tests/pwait_reap.c
new file mode 100644
--- /dev/null
+++ b/bin/pwait/tests/pwait_reap.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2026 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * Wait for a process to terminate, optionally reap it, and verify that it
+ * exited cleanly.
+ */
+static void
+wait_zero(pid_t pid, bool reap)
+{
+ int wstatus;
+ pid_t ret;
+
+ do {
+ ret = waitpid(pid, &wstatus, reap ? 0 : WNOWAIT);
+ ATF_REQUIRE(ret == pid || (ret < 0 && errno == EINTR));
+ } while (ret != pid);
+ ATF_CHECK(WIFEXITED(wstatus));
+ ATF_CHECK_EQ(WEXITSTATUS(wstatus), 0);
+}
+
+/*
+ * Verify that pwait is still waiting for the target process.
+ */
+static void
+pwait_is_waiting(pid_t pwpid, pid_t tpid, int pwout, int pwerr)
+{
+ struct pollfd pfds[2];
+ char line[64] = "", *eol, *rest;
+ ssize_t ret;
+
+ pfds[0].fd = pwout;
+ pfds[0].events = POLLIN;
+ pfds[1].fd = pwerr;
+ pfds[1].events = POLLIN;
+
+ /* loop until we have some output */
+ do {
+ /* send SIGINFO so pwait prints the target PID on stderr */
+ ATF_REQUIRE(kill(pwpid, SIGINFO) == 0);
+ } while ((ret = poll(pfds, 2, 100)) == 0);
+ ATF_REQUIRE_EQ(ret, 1);
+ ATF_REQUIRE_EQ(pfds[1].revents, POLLIN);
+
+ /* read line from stderr */
+ ATF_REQUIRE((ret = read(pwerr, line, sizeof(line) - 1)) > 0);
+
+ /* verify the line */
+ ATF_REQUIRE((eol = strchr(line, '\n')) != NULL);
+ *eol = '\0';
+ ATF_REQUIRE_EQ(strtol(line, &rest, 10), tpid);
+ ATF_REQUIRE_STREQ(rest, "");
+}
+
+/*
+ * Verify that pwait has reported that the target process has terminated,
+ * then reap it.
+ */
+static void
+pwait_is_done(pid_t pwpid, pid_t tpid, int pwout, int pwerr)
+{
+ struct pollfd pfds[2];
+ char line[64] = "", *eol, *rest;
+ ssize_t ret;
+
+ pfds[0].fd = pwout;
+ pfds[0].events = POLLIN;
+ pfds[1].fd = pwerr;
+ pfds[1].events = POLLIN;
+
+ /* loop until we have some output */
+ do {
+ /* nothing */
+ } while ((ret = poll(pfds, 2, 100)) == 0);
+ ATF_REQUIRE_EQ(ret, 1);
+ ATF_REQUIRE_EQ(pfds[0].revents, POLLIN);
+
+ /* read line from stdout */
+ ATF_REQUIRE((ret = read(pwout, line, sizeof(line) - 1)) > 0);
+
+ /* verify the line */
+ ATF_REQUIRE((eol = strchr(line, '\n')) != NULL);
+ *eol = '\0';
+ ATF_REQUIRE_EQ(strtol(line, &rest, 10), tpid);
+ ATF_REQUIRE_STREQ(rest, ": exited with status 0.");
+
+ /* reap pwait */
+ wait_zero(pwpid, true);
+}
+
+static void
+pwait_reap_test(bool reap)
+{
+ char pidstr[16];
+ pid_t tpid, pwpid;
+ int tpipe[2], pwout[2], pwerr[2];
+
+ /* fork target process */
+ ATF_REQUIRE(pipe2(tpipe, O_CLOEXEC) == 0);
+ ATF_REQUIRE((tpid = fork()) >= 0);
+ if (tpid == 0) {
+ (void)close(tpipe[1]);
+ /* block until parent closes its end of the pipe */
+ (void)read(tpipe[0], pidstr, 1);
+ _exit(0);
+ }
+ (void)close(tpipe[0]);
+
+ /* fork pwait */
+ snprintf(pidstr, sizeof(pidstr), "%ld", (long)tpid);
+ ATF_REQUIRE(pipe2(pwout, O_CLOEXEC) == 0);
+ ATF_REQUIRE(pipe2(pwerr, O_CLOEXEC) == 0);
+ ATF_REQUIRE((pwpid = fork()) >= 0);
+ if (pwpid == 0) {
+ (void)dup2(pwout[0], STDOUT_FILENO);
+ (void)dup2(pwerr[0], STDERR_FILENO);
+ execlp("pwait", "pwait", reap ? "-rv" : "-v", pidstr, NULL);
+ _exit(99);
+ }
+
+ /* wait for pwait to become ready */
+ pwait_is_waiting(pwpid, tpid, pwout[1], pwerr[1]);
+
+ /* unblock the target and wait for it to terminate */
+ (void)close(tpipe[1]);
+ wait_zero(tpid, false);
+
+ if (reap) {
+ /* check that pwait is still waiting */
+ pwait_is_waiting(pwpid, tpid, pwout[1], pwerr[1]);
+ } else {
+ /* check that pwait has reported completion */
+ pwait_is_done(pwpid, tpid, pwout[1], pwerr[1]);
+ }
+
+ /* reap the target */
+ wait_zero(tpid, true);
+
+ if (reap) {
+ /* check that pwait has reported completion */
+ pwait_is_done(pwpid, tpid, pwout[1], pwerr[1]);
+ }
+}
+
+ATF_TC(pwait_normal);
+ATF_TC_HEAD(pwait_normal, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test that pwait returns before the "
+ "target process has been reaped");
+}
+ATF_TC_BODY(pwait_normal, tc)
+{
+ pwait_reap_test(true);
+}
+
+ATF_TC(pwait_reap);
+ATF_TC_HEAD(pwait_reap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test that pwait -r does not return "
+ "until the target process has been reaped");
+}
+ATF_TC_BODY(pwait_reap, tc)
+{
+ pwait_reap_test(true);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pwait_normal);
+ ATF_TP_ADD_TC(tp, pwait_reap);
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Aug 3, 5:23 PM (4 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35832798
Default Alt Text
D58385.id182621.diff (4 KB)
Attached To
Mode
D58385: pwait: Test the new -r option
Attached
Detach File
Event Timeline
Log In to Comment