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