| // 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); |
| } |