]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/pidref.c
cgroup-util: make cg_pidref_get_path() PidRef parameter const
[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
109void pidref_done(PidRef *pidref) {
110 assert(pidref);
111
112 *pidref = (PidRef) {
113 .fd = safe_close(pidref->fd),
114 };
115}
116
83765982
LP
117PidRef *pidref_free(PidRef *pidref) {
118 /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
119 if (!pidref)
120 return NULL;
121
122 pidref_done(pidref);
123 return mfree(pidref);
124}
125
126int pidref_dup(const PidRef *pidref, PidRef **ret) {
127 _cleanup_close_ int dup_fd = -EBADF;
128 pid_t dup_pid = 0;
129
130 assert(ret);
131
132 /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
133 * acquire a pidfd if we don't have one yet!
134 *
135 * If NULL is passed we'll generate a PidRef that refers to no process. This makes it easy to copy
136 * pidref fields that might or might not reference a process yet. */
137
138 if (pidref) {
139 if (pidref->fd >= 0) {
140 dup_fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
141 if (dup_fd < 0) {
142 if (!ERRNO_IS_RESOURCE(errno))
143 return -errno;
144
145 dup_fd = -EBADF;
146 }
147 }
148
149 if (pidref->pid > 0)
150 dup_pid = pidref->pid;
151 }
152
153 PidRef *dup_pidref = new(PidRef, 1);
154 if (!dup_pidref)
155 return -ENOMEM;
156
157 *dup_pidref = (PidRef) {
158 .fd = TAKE_FD(dup_fd),
159 .pid = dup_pid,
160 };
161
162 *ret = TAKE_PTR(dup_pidref);
163 return 0;
164}
165
166int pidref_new_from_pid(pid_t pid, PidRef **ret) {
167 _cleanup_(pidref_freep) PidRef *n = 0;
168 int r;
169
170 assert(ret);
171
172 if (pid < 0)
173 return -ESRCH;
174
175 n = new(PidRef, 1);
176 if (!n)
177 return -ENOMEM;
178
179 *n = PIDREF_NULL;
180
181 r = pidref_set_pid(n, pid);
182 if (r < 0)
183 return r;
184
185 *ret = TAKE_PTR(n);
186 return 0;
187}
188
44c55e5a 189int pidref_kill(const PidRef *pidref, int sig) {
3bda3f17
LP
190
191 if (!pidref)
192 return -ESRCH;
193
194 if (pidref->fd >= 0)
195 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
196
197 if (pidref->pid > 0)
198 return RET_NERRNO(kill(pidref->pid, sig));
199
200 return -ESRCH;
201}
202
44c55e5a 203int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
3bda3f17
LP
204 int r;
205
206 r = pidref_kill(pidref, sig);
207 if (r < 0)
208 return r;
209
210 if (!IN_SET(sig, SIGCONT, SIGKILL))
211 (void) pidref_kill(pidref, SIGCONT);
212
213 return 0;
214}
a0d1659c 215
44c55e5a 216int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
a0d1659c
LP
217
218 if (!pidref)
219 return -ESRCH;
220
221 if (pidref->fd >= 0) {
222 siginfo_t si;
223
224 /* We can't use structured initialization here, since the structure contains various unions
225 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
226 * allow assignments to */
227 zero(si);
228 si.si_signo = sig;
229 si.si_code = SI_QUEUE;
230 si.si_pid = getpid_cached();
231 si.si_uid = getuid();
232 si.si_value.sival_int = value;
233
234 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
235 }
236
237 if (pidref->pid > 0)
238 return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
239
240 return -ESRCH;
241}
9cb7e49f 242
bd389293 243int pidref_verify(const PidRef *pidref) {
ec8dc835
LP
244 int r;
245
246 /* This is a helper that is supposed to be called after reading information from procfs via a
247 * PidRef. It ensures that the PID we track still matches the PIDFD we pin. If this value differs
248 * after a procfs read, we might have read the data from a recycled PID. */
249
250 if (!pidref_is_set(pidref))
251 return -ESRCH;
252
253 if (pidref->fd < 0)
254 return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
255
256 r = pidfd_verify_pid(pidref->fd, pidref->pid);
257 if (r < 0)
258 return r;
259
260 return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
261}
262
9cb7e49f
LP
263static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
264 siphash24_compress(&pidref->pid, sizeof(pidref->pid), state);
265}
266
267static int pidref_compare_func(const PidRef *a, const PidRef *b) {
268 return CMP(a->pid, b->pid);
269}
270
271DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
272 pidref_hash_ops,
273 PidRef,
274 pidref_hash_func,
275 pidref_compare_func,
276 pidref_free);