]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/pidref.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / basic / pidref.c
CommitLineData
3bda3f17
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "errno-util.h"
4#include "fd-util.h"
5#include "missing_syscall.h"
a3f32436 6#include "missing_wait.h"
3bda3f17
LP
7#include "parse-util.h"
8#include "pidref.h"
9#include "process-util.h"
a0d1659c 10#include "signal-util.h"
3bda3f17
LP
11
12int pidref_set_pid(PidRef *pidref, pid_t pid) {
13 int fd;
14
15 assert(pidref);
16
17 if (pid < 0)
18 return -ESRCH;
19 if (pid == 0)
20 pid = getpid_cached();
21
22 fd = pidfd_open(pid, 0);
23 if (fd < 0) {
24 /* Graceful fallback in case the kernel doesn't support pidfds or is out of fds */
25 if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && !ERRNO_IS_RESOURCE(errno))
26 return -errno;
27
28 fd = -EBADF;
29 }
30
31 *pidref = (PidRef) {
32 .fd = fd,
33 .pid = pid,
34 };
35
36 return 0;
37}
38
39int pidref_set_pidstr(PidRef *pidref, const char *pid) {
40 pid_t nr;
41 int r;
42
43 assert(pidref);
44
45 r = parse_pid(pid, &nr);
46 if (r < 0)
47 return r;
48
49 return pidref_set_pid(pidref, nr);
50}
51
52int pidref_set_pidfd(PidRef *pidref, int fd) {
53 int r;
54
55 assert(pidref);
56
57 if (fd < 0)
58 return -EBADF;
59
60 int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
61 if (fd_copy < 0) {
62 pid_t pid;
63
64 if (!ERRNO_IS_RESOURCE(errno))
65 return -errno;
66
67 /* Graceful fallback if we are out of fds */
68 r = pidfd_get_pid(fd, &pid);
69 if (r < 0)
70 return r;
71
dcfcea6d 72 *pidref = PIDREF_MAKE_FROM_PID(pid);
3bda3f17
LP
73 return 0;
74 }
75
76 return pidref_set_pidfd_consume(pidref, fd_copy);
77}
78
79int pidref_set_pidfd_take(PidRef *pidref, int fd) {
80 pid_t pid;
81 int r;
82
83 assert(pidref);
84
85 if (fd < 0)
86 return -EBADF;
87
88 r = pidfd_get_pid(fd, &pid);
89 if (r < 0)
90 return r;
91
92 *pidref = (PidRef) {
93 .fd = fd,
94 .pid = pid,
95 };
96
97 return 0;
98}
99
100int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
101 int r;
102
103 r = pidref_set_pidfd_take(pidref, fd);
104 if (r < 0)
105 safe_close(fd);
106
107 return r;
108}
109
a1796e9b
LP
110int pidref_set_parent(PidRef *ret) {
111 _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
112 pid_t ppid;
113 int r;
114
115 assert(ret);
116
117 /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
118 * we get reparented to other processes, with our old parent's PID already being recycled. */
119
120 ppid = getppid();
121 for (;;) {
122 r = pidref_set_pid(&parent, ppid);
123 if (r < 0)
124 return r;
125
126 if (parent.fd < 0) /* If pidfds are not available, then we are done */
127 break;
128
129 pid_t now_ppid = getppid();
130 if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
131 break;
132
133 /* Otherwise let's try again with the new ppid */
134 ppid = now_ppid;
135 pidref_done(&parent);
136 }
137
138 *ret = TAKE_PIDREF(parent);
139 return 0;
140}
141
3bda3f17
LP
142void pidref_done(PidRef *pidref) {
143 assert(pidref);
144
145 *pidref = (PidRef) {
146 .fd = safe_close(pidref->fd),
147 };
148}
149
83765982
LP
150PidRef *pidref_free(PidRef *pidref) {
151 /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
152 if (!pidref)
153 return NULL;
154
155 pidref_done(pidref);
156 return mfree(pidref);
157}
158
159int pidref_dup(const PidRef *pidref, PidRef **ret) {
160 _cleanup_close_ int dup_fd = -EBADF;
161 pid_t dup_pid = 0;
162
163 assert(ret);
164
165 /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
166 * acquire a pidfd if we don't have one yet!
167 *
168 * If NULL is passed we'll generate a PidRef that refers to no process. This makes it easy to copy
169 * pidref fields that might or might not reference a process yet. */
170
171 if (pidref) {
172 if (pidref->fd >= 0) {
173 dup_fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
174 if (dup_fd < 0) {
175 if (!ERRNO_IS_RESOURCE(errno))
176 return -errno;
177
178 dup_fd = -EBADF;
179 }
180 }
181
182 if (pidref->pid > 0)
183 dup_pid = pidref->pid;
184 }
185
186 PidRef *dup_pidref = new(PidRef, 1);
187 if (!dup_pidref)
188 return -ENOMEM;
189
190 *dup_pidref = (PidRef) {
191 .fd = TAKE_FD(dup_fd),
192 .pid = dup_pid,
193 };
194
195 *ret = TAKE_PTR(dup_pidref);
196 return 0;
197}
198
199int pidref_new_from_pid(pid_t pid, PidRef **ret) {
200 _cleanup_(pidref_freep) PidRef *n = 0;
201 int r;
202
203 assert(ret);
204
205 if (pid < 0)
206 return -ESRCH;
207
208 n = new(PidRef, 1);
209 if (!n)
210 return -ENOMEM;
211
212 *n = PIDREF_NULL;
213
214 r = pidref_set_pid(n, pid);
215 if (r < 0)
216 return r;
217
218 *ret = TAKE_PTR(n);
219 return 0;
220}
221
44c55e5a 222int pidref_kill(const PidRef *pidref, int sig) {
3bda3f17
LP
223
224 if (!pidref)
225 return -ESRCH;
226
227 if (pidref->fd >= 0)
228 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
229
230 if (pidref->pid > 0)
231 return RET_NERRNO(kill(pidref->pid, sig));
232
233 return -ESRCH;
234}
235
44c55e5a 236int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
3bda3f17
LP
237 int r;
238
239 r = pidref_kill(pidref, sig);
240 if (r < 0)
241 return r;
242
243 if (!IN_SET(sig, SIGCONT, SIGKILL))
244 (void) pidref_kill(pidref, SIGCONT);
245
246 return 0;
247}
a0d1659c 248
44c55e5a 249int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
a0d1659c
LP
250
251 if (!pidref)
252 return -ESRCH;
253
254 if (pidref->fd >= 0) {
255 siginfo_t si;
256
257 /* We can't use structured initialization here, since the structure contains various unions
258 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
259 * allow assignments to */
260 zero(si);
261 si.si_signo = sig;
262 si.si_code = SI_QUEUE;
263 si.si_pid = getpid_cached();
264 si.si_uid = getuid();
265 si.si_value.sival_int = value;
266
267 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
268 }
269
270 if (pidref->pid > 0)
271 return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
272
273 return -ESRCH;
274}
9cb7e49f 275
bd389293 276int pidref_verify(const PidRef *pidref) {
ec8dc835
LP
277 int r;
278
279 /* This is a helper that is supposed to be called after reading information from procfs via a
280 * PidRef. It ensures that the PID we track still matches the PIDFD we pin. If this value differs
281 * after a procfs read, we might have read the data from a recycled PID. */
282
283 if (!pidref_is_set(pidref))
284 return -ESRCH;
285
3d7ba61a
LP
286 if (pidref->pid == 1)
287 return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
288
ec8dc835
LP
289 if (pidref->fd < 0)
290 return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
291
292 r = pidfd_verify_pid(pidref->fd, pidref->pid);
293 if (r < 0)
294 return r;
295
296 return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
297}
298
a7a87769
LP
299bool pidref_is_self(const PidRef *pidref) {
300 if (!pidref)
301 return false;
302
303 return pidref->pid == getpid_cached();
304}
305
a3f32436
LP
306int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
307 int r;
308
309 if (!pidref_is_set(pidref))
310 return -ESRCH;
311
312 if (pidref->pid == 1 || pidref->pid == getpid_cached())
313 return -ECHILD;
314
315 siginfo_t si = {};
316
317 if (pidref->fd >= 0) {
318 r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
319 if (r >= 0) {
320 if (ret)
321 *ret = si;
322 return r;
323 }
324 if (r != -EINVAL) /* P_PIDFD was added in kernel 5.4 only */
325 return r;
326 }
327
328 r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
329 if (r >= 0 && ret)
330 *ret = si;
331 return r;
332}
333
334int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) {
335 int r;
336
337 for (;;) {
338 r = pidref_wait(pidref, ret, WEXITED);
339 if (r != -EINTR)
340 return r;
341 }
342}
343
9cb7e49f 344static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
c01a5c05 345 siphash24_compress_typesafe(pidref->pid, state);
9cb7e49f
LP
346}
347
348static int pidref_compare_func(const PidRef *a, const PidRef *b) {
349 return CMP(a->pid, b->pid);
350}
351
4c8d5f02
MY
352DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
353
354DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
355 PidRef, pidref_hash_func, pidref_compare_func,
356 pidref_free);