mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
pwait: Test the new -r option
Test that pwait without -r reports a process as soon as it terminates, while pwait with -r does not report it until it has been reaped. MFC after: 1 week Sponsored by: Klara, Inc. Sponsored by: NetApp, Inc. Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D58385
This commit is contained in:
1 parent
eddd8aa99c
commit
e115066370
2 files changed
+189
No files matched your search
@@ -1,3 +1,4 @@
|
||||
ATF_TESTS_C= pwait_reap
|
||||
ATF_TESTS_SH= pwait_test
|
||||
|
||||
.include <bsd.test.mk>
|
||||
@@ -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());
|
||||
}
|
||||
Reference in new issue
Block a user