1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #define _GNU_SOURCE |
4 | #include <err.h> |
5 | #include <errno.h> |
6 | #include <fcntl.h> |
7 | #include <inttypes.h> |
8 | #include <limits.h> |
9 | #include <sched.h> |
10 | #include <signal.h> |
11 | #include <stdio.h> |
12 | #include <stdlib.h> |
13 | #include <string.h> |
14 | #include <sys/stat.h> |
15 | #include <sys/syscall.h> |
16 | #include <sys/types.h> |
17 | #include <sys/wait.h> |
18 | #include <unistd.h> |
19 | |
20 | #ifndef CLONE_PIDFD |
21 | #define CLONE_PIDFD 0x00001000 |
22 | #endif |
23 | |
24 | #ifndef __NR_pidfd_send_signal |
25 | #define __NR_pidfd_send_signal -1 |
26 | #endif |
27 | |
28 | static int do_child(void *args) |
29 | { |
30 | printf(format: "%d\n" , getpid()); |
31 | _exit(EXIT_SUCCESS); |
32 | } |
33 | |
34 | static pid_t pidfd_clone(int flags, int *pidfd) |
35 | { |
36 | size_t stack_size = 1024; |
37 | char *stack[1024] = { 0 }; |
38 | |
39 | #ifdef __ia64__ |
40 | return __clone2(do_child, stack, stack_size, flags | SIGCHLD, NULL, pidfd); |
41 | #else |
42 | return clone(fn: do_child, child_stack: stack + stack_size, flags: flags | SIGCHLD, NULL, pidfd); |
43 | #endif |
44 | } |
45 | |
46 | static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, |
47 | unsigned int flags) |
48 | { |
49 | return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags); |
50 | } |
51 | |
52 | static int pidfd_metadata_fd(pid_t pid, int pidfd) |
53 | { |
54 | int procfd, ret; |
55 | char path[100]; |
56 | |
57 | snprintf(s: path, maxlen: sizeof(path), format: "/proc/%d" , pid); |
58 | procfd = open(file: path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); |
59 | if (procfd < 0) { |
60 | warn(format: "Failed to open %s\n" , path); |
61 | return -1; |
62 | } |
63 | |
64 | /* |
65 | * Verify that the pid has not been recycled and our /proc/<pid> handle |
66 | * is still valid. |
67 | */ |
68 | ret = sys_pidfd_send_signal(pidfd, sig: 0, NULL, flags: 0); |
69 | if (ret < 0) { |
70 | switch (errno) { |
71 | case EPERM: |
72 | /* Process exists, just not allowed to signal it. */ |
73 | break; |
74 | default: |
75 | warn(format: "Failed to signal process\n" ); |
76 | close(fd: procfd); |
77 | procfd = -1; |
78 | } |
79 | } |
80 | |
81 | return procfd; |
82 | } |
83 | |
84 | int main(int argc, char *argv[]) |
85 | { |
86 | int pidfd = -1, ret = EXIT_FAILURE; |
87 | char buf[4096] = { 0 }; |
88 | pid_t pid; |
89 | int procfd, statusfd; |
90 | ssize_t bytes; |
91 | |
92 | pid = pidfd_clone(CLONE_PIDFD, pidfd: &pidfd); |
93 | if (pid < 0) |
94 | err(status: ret, format: "CLONE_PIDFD" ); |
95 | if (pidfd == -1) { |
96 | warnx(format: "CLONE_PIDFD is not supported by the kernel" ); |
97 | goto out; |
98 | } |
99 | |
100 | procfd = pidfd_metadata_fd(pid, pidfd); |
101 | close(fd: pidfd); |
102 | if (procfd < 0) |
103 | goto out; |
104 | |
105 | statusfd = openat(fd: procfd, file: "status" , O_RDONLY | O_CLOEXEC); |
106 | close(fd: procfd); |
107 | if (statusfd < 0) |
108 | goto out; |
109 | |
110 | bytes = read(fd: statusfd, buf: buf, nbytes: sizeof(buf)); |
111 | if (bytes > 0) |
112 | bytes = write(STDOUT_FILENO, buf: buf, n: bytes); |
113 | close(fd: statusfd); |
114 | ret = EXIT_SUCCESS; |
115 | |
116 | out: |
117 | (void)wait(NULL); |
118 | |
119 | exit(status: ret); |
120 | } |
121 | |