Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ name: Build and test with CMake
on:
push:
branches:
- master
- main
pull_request:
branches:
- master
- main

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ include(CheckSymbolExists)
check_symbol_exists(__GLIBC__ features.h _GNU_SOURCE)
configure_file(config.h.in config.h)

add_executable(pwait pwait.c ptrace.c netlink.c poll.c capabilities.c)
add_executable(pwait pwait.c ptrace.c netlink.c poll.c pidfd.c capabilities.c)
target_link_libraries(pwait cap)

install(TARGETS pwait RUNTIME DESTINATION bin)
Expand Down
71 changes: 71 additions & 0 deletions src/pidfd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* Implements waiting for a process using the `pidfd_open` syscall
*
* This implementation is similar to the one in
* [the man page for `pidfd_open`](https://man7.org/linux/man-pages/man2/pidfd_open.2.html)
* but that's effectively forced by the task; there isn't a lot of variety in
* how you can write this.
*/

#define _GNU_SOURCE

#include "pwait.h"
#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/syscall.h>


/**
* Open a process file descriptor.
*
* This is a thin wrapper around the pidfd_open() syscall.
*
* @return The file descriptor opened, or -1 if opening failed.
*/
static int pidfd_open(pid_t pid) {
long result = syscall(SYS_pidfd_open, pid, 0);
int pidfd = (int)result;
assert((long)pidfd == result);
return pidfd;
}


int wait_using_pidfd(pid_t pid) {
int fd = pidfd_open(pid);
if (fd < 0) {
// ESRCH indicates that no process with that ID was found
return errno == ESRCH ? -1 : EX_OSERR;
}

struct pollfd pfd = {
.fd = fd,
.events = POLLIN,
};

int pfd_status;
while ((pfd_status = poll(&pfd, 1, -1)) >= 0) {
if (pfd_status < 0) {
if (errno == EINTR) {
// poll() was interrupted by a signal, so we can just keep going
continue;
}
else {
// Some more serious error occurred
return EX_OSERR;
}
}
else if (pfd_status == 0) {
// This shouldn't happen because we set timeout to -1, i.e. infinite
return -1;
}
else {
assert(pfd_status == 1);
if (pfd.revents & POLLIN) {
break;
}
}
}
return 0;
}
5 changes: 4 additions & 1 deletion src/pwait.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static void help(const char* name) {
printf(" -d, --delay=SECONDS set the polling frequency when --method=poll\n");
printf(" -h, --help print this help message and exit\n");
printf(" -m, --method=METHOD use METHOD to wait for the process\n");
printf(" METHOD is one of 'netlink' (default), 'ptrace', or 'poll'\n");
printf(" METHOD is one of 'netlink' (default), 'ptrace', 'poll', or 'pidfd'\n");
printf(" -v, --verbose print diagnostic output to stderr\n");
#else
printf(" -h print this help message and exit\n");
Expand Down Expand Up @@ -101,6 +101,9 @@ int main(const int argc, char* const* argv) {
else if (strncmp(optarg, "poll", 5) == 0) {
wait_function = wait_using_polling;
}
else if (strncmp(optarg, "pidfd", 6) == 0) {
wait_function = wait_using_pidfd;
}
else {
wait_function = NULL;
}
Expand Down
1 change: 1 addition & 0 deletions src/pwait.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ int acquire_capabilities(size_t n, const cap_value_t* capabilities_to_acquire);
int wait_using_ptrace(pid_t pid);
int wait_using_netlink(pid_t pid);
int wait_using_polling(pid_t pid);
int wait_using_pidfd(pid_t pid);
void set_delay(unsigned int delay);
8 changes: 7 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ add_test(
NAME test-pwait-poll
COMMAND "${CMAKE_CURRENT_LIST_DIR}/test_pwait.sh"
)
set_tests_properties(test-pwait-poll PROPERTIES ENVIRONMENT "PWAIT=$<TARGET_FILE:pwait>;PWAIT_METHOD=poll")
set_tests_properties(test-pwait-poll PROPERTIES ENVIRONMENT "PWAIT=$<TARGET_FILE:pwait>;PWAIT_METHOD=poll;PWAIT_SKIP_EXIT_CODE_TESTS=1")

add_test(
NAME test-pwait-pidfd
COMMAND "${CMAKE_CURRENT_LIST_DIR}/test_pwait.sh"
)
set_tests_properties(test-pwait-pidfd PROPERTIES ENVIRONMENT "PWAIT=$<TARGET_FILE:pwait>;PWAIT_METHOD=pidfd;PWAIT_SKIP_EXIT_CODE_TESTS=1")
28 changes: 12 additions & 16 deletions test/test_pwait.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ test_pwait_and_target_exit_times() {


run_all_tests() {
test_pwait_exit_code 0
test_pwait_exit_code 1
test_pwait_exit_code 128
if [[ -z "${PWAIT_SKIP_EXIT_CODE_TESTS:-}" ]]; then
test_pwait_exit_code 0
test_pwait_exit_code 1
test_pwait_exit_code 128
fi
test_target_does_not_exist_after_pwait_exit
test_pwait_and_target_exit_times 0.1s
test_pwait_and_target_exit_times 1s
Expand All @@ -138,23 +140,17 @@ run_all_tests() {
}


run_poll_tests() {
pwait_options=("--method=poll")
test_target_does_not_exist_after_pwait_exit
run_poll_delay_tests() {
for delay in 1 2 5; do
pwait_options=("--method=poll" "--delay=$delay")
test_pwait_and_target_exit_times 10s "${delay}"
done
}


pwait_options=()
case "${PWAIT_METHOD:-}" in
poll)
run_poll_tests
;;
*)
pwait_options=("--method=${PWAIT_METHOD}")
run_all_tests
;;
esac
pwait_options=("--method=${PWAIT_METHOD}")
run_all_tests

if [[ "${PWAIT_METHOD:-}" == "poll" ]]; then
run_poll_delay_tests
fi
Loading