| // SPDX-License-Identifier: GPL-2.0 | |
| #define _GNU_SOURCE | |
| #include <err.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <inttypes.h> | |
| #include <limits.h> | |
| #include <sched.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/stat.h> | |
| #include <sys/syscall.h> | |
| #include <sys/types.h> | |
| #include <sys/wait.h> | |
| #include <unistd.h> | |
| #ifndef CLONE_PIDFD | |
| #define CLONE_PIDFD 0x00001000 | |
| #endif | |
| #ifndef __NR_pidfd_send_signal | |
| #define __NR_pidfd_send_signal -1 | |
| #endif | |
| static int do_child(void *args) | |
| { | |
| printf("%d\n", getpid()); | |
| _exit(EXIT_SUCCESS); | |
| } | |
| static pid_t pidfd_clone(int flags, int *pidfd) | |
| { | |
| size_t stack_size = 1024; | |
| char *stack[1024] = { 0 }; | |
| #ifdef __ia64__ | |
| return __clone2(do_child, stack, stack_size, flags | SIGCHLD, NULL, pidfd); | |
| #else | |
| return clone(do_child, stack + stack_size, flags | SIGCHLD, NULL, pidfd); | |
| #endif | |
| } | |
| static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, | |
| unsigned int flags) | |
| { | |
| return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags); | |
| } | |
| static int pidfd_metadata_fd(pid_t pid, int pidfd) | |
| { | |
| int procfd, ret; | |
| char path[100]; | |
| snprintf(path, sizeof(path), "/proc/%d", pid); | |
| procfd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); | |
| if (procfd < 0) { | |
| warn("Failed to open %s\n", path); | |
| return -1; | |
| } | |
| /* | |
| * Verify that the pid has not been recycled and our /proc/<pid> handle | |
| * is still valid. | |
| */ | |
| ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0); | |
| if (ret < 0) { | |
| switch (errno) { | |
| case EPERM: | |
| /* Process exists, just not allowed to signal it. */ | |
| break; | |
| default: | |
| warn("Failed to signal process\n"); | |
| close(procfd); | |
| procfd = -1; | |
| } | |
| } | |
| return procfd; | |
| } | |
| int main(int argc, char *argv[]) | |
| { | |
| int pidfd = -1, ret = EXIT_FAILURE; | |
| char buf[4096] = { 0 }; | |
| pid_t pid; | |
| int procfd, statusfd; | |
| ssize_t bytes; | |
| pid = pidfd_clone(CLONE_PIDFD, &pidfd); | |
| if (pid < 0) | |
| err(ret, "CLONE_PIDFD"); | |
| if (pidfd == -1) { | |
| warnx("CLONE_PIDFD is not supported by the kernel"); | |
| goto out; | |
| } | |
| procfd = pidfd_metadata_fd(pid, pidfd); | |
| close(pidfd); | |
| if (procfd < 0) | |
| goto out; | |
| statusfd = openat(procfd, "status", O_RDONLY | O_CLOEXEC); | |
| close(procfd); | |
| if (statusfd < 0) | |
| goto out; | |
| bytes = read(statusfd, buf, sizeof(buf)); | |
| if (bytes > 0) | |
| bytes = write(STDOUT_FILENO, buf, bytes); | |
| close(statusfd); | |
| ret = EXIT_SUCCESS; | |
| out: | |
| (void)wait(NULL); | |
| exit(ret); | |
| } |