Bug Summary

File:build-scan/../src/basic/process-util.c
Warning:line 89, column 25
Potential leak of memory pointed to by 'escaped'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name process-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/process-util.c

../src/basic/process-util.c

1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <ctype.h>
4#include <errno(*__errno_location ()).h>
5#include <limits.h>
6#include <linux1/oom.h>
7#include <signal.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdio_ext.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/mman.h>
14#include <sys/mount.h>
15#include <sys/personality.h>
16#include <sys/prctl.h>
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <syslog.h>
20#include <unistd.h>
21#if HAVE_VALGRIND_VALGRIND_H1
22#include <valgrind/valgrind.h>
23#endif
24
25#include "alloc-util.h"
26#include "architecture.h"
27#include "escape.h"
28#include "fd-util.h"
29#include "fileio.h"
30#include "fs-util.h"
31#include "ioprio.h"
32#include "log.h"
33#include "macro.h"
34#include "missing.h"
35#include "process-util.h"
36#include "raw-clone.h"
37#include "signal-util.h"
38#include "stat-util.h"
39#include "string-table.h"
40#include "string-util.h"
41#include "terminal-util.h"
42#include "user-util.h"
43#include "util.h"
44
45int get_process_state(pid_t pid) {
46 const char *p;
47 char state;
48 int r;
49 _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0);
50
51 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 51, __PRETTY_FUNCTION__); } while (0)
;
52
53 p = procfs_file_alloca(pid, "stat")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "stat"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("stat")); sprintf((char*) _r_, "/proc/""%" "i""/" "stat"
, _pid_); } _r_; })
;
54
55 r = read_one_line_file(p, &line);
56 if (r == -ENOENT2)
57 return -ESRCH3;
58 if (r < 0)
59 return r;
60
61 p = strrchr(line, ')');
62 if (!p)
63 return -EIO5;
64
65 p++;
66
67 if (sscanf(p, " %c", &state) != 1)
68 return -EIO5;
69
70 return (unsigned char) state;
71}
72
73int get_process_comm(pid_t pid, char **ret) {
74 _cleanup_free___attribute__((cleanup(freep))) char *escaped = NULL((void*)0), *comm = NULL((void*)0);
75 const char *p;
76 int r;
77
78 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/process-util.c", 78
, __PRETTY_FUNCTION__); } while (0)
;
24
Taking false branch
25
Loop condition is false. Exiting loop
79 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 79, __PRETTY_FUNCTION__); } while (0)
;
26
Taking false branch
27
Loop condition is false. Exiting loop
80
81 escaped = new(char, TASK_COMM_LEN)((char*) malloc_multiply(sizeof(char), (16)));
28
Calling 'malloc_multiply'
31
Returned allocated memory
82 if (!escaped)
32
Assuming 'escaped' is non-null
33
Taking false branch
83 return -ENOMEM12;
84
85 p = procfs_file_alloca(pid, "comm")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "comm"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("comm")); sprintf((char*) _r_, "/proc/""%" "i""/" "comm"
, _pid_); } _r_; })
;
34
Taking true branch
86
87 r = read_one_line_file(p, &comm);
88 if (r == -ENOENT2)
35
Assuming the condition is true
36
Taking true branch
89 return -ESRCH3;
37
Potential leak of memory pointed to by 'escaped'
90 if (r < 0)
91 return r;
92
93 /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
94 cellescape(escaped, TASK_COMM_LEN16, comm);
95
96 *ret = TAKE_PTR(escaped)({ typeof(escaped) _ptr_ = (escaped); (escaped) = ((void*)0);
_ptr_; })
;
97 return 0;
98}
99
100int get_process_cmdline(pid_t pid, size_t max_length, bool_Bool comm_fallback, char **line) {
101 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
102 bool_Bool space = false0;
103 char *k;
104 _cleanup_free___attribute__((cleanup(freep))) char *ans = NULL((void*)0);
105 const char *p;
106 int c;
107
108 assert(line)do { if ((__builtin_expect(!!(!(line)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("line"), "../src/basic/process-util.c", 108
, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming 'line' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
109 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 109, __PRETTY_FUNCTION__); } while (0)
;
4
Assuming 'pid' is >= 0
5
Taking false branch
6
Loop condition is false. Exiting loop
110
111 /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
112 * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
113 * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
114 * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
115 * command line that resolves to the empty string will return the "comm" name of the process instead.
116 *
117 * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
118 * comm_fallback is false). Returns 0 and sets *line otherwise. */
119
120 p = procfs_file_alloca(pid, "cmdline")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "cmdline"); } else { _r_ = __builtin_alloca
((sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 :
sizeof(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof
(pid_t) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)])
)) + 1 + sizeof("cmdline")); sprintf((char*) _r_, "/proc/""%"
"i""/" "cmdline", _pid_); } _r_; })
;
7
Assuming '_pid_' is equal to 0
8
Taking true branch
121
122 f = fopen(p, "re");
123 if (!f) {
9
Assuming 'f' is non-null
10
Taking false branch
124 if (errno(*__errno_location ()) == ENOENT2)
125 return -ESRCH3;
126 return -errno(*__errno_location ());
127 }
128
129 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
130
131 if (max_length == 0) {
11
Assuming 'max_length' is not equal to 0
12
Taking false branch
132 /* This is supposed to be a safety guard against runaway command lines. */
133 long l = sysconf(_SC_ARG_MAX_SC_ARG_MAX);
134 assert(l > 0)do { if ((__builtin_expect(!!(!(l > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("l > 0"), "../src/basic/process-util.c"
, 134, __PRETTY_FUNCTION__); } while (0)
;
135 max_length = l;
136 }
137
138 if (max_length == 1) {
13
Assuming 'max_length' is not equal to 1
14
Taking false branch
139
140 /* If there's only room for one byte, return the empty string */
141 ans = new0(char, 1)((char*) calloc((1), sizeof(char)));
142 if (!ans)
143 return -ENOMEM12;
144
145 *line = TAKE_PTR(ans)({ typeof(ans) _ptr_ = (ans); (ans) = ((void*)0); _ptr_; });
146 return 0;
147
148 } else {
149 bool_Bool dotdotdot = false0;
150 size_t left;
151
152 ans = new(char, max_length)((char*) malloc_multiply(sizeof(char), (max_length)));
153 if (!ans)
15
Assuming 'ans' is non-null
16
Taking false branch
154 return -ENOMEM12;
155
156 k = ans;
157 left = max_length;
158 while ((c = getc(f)) != EOF(-1)) {
17
Assuming the condition is false
18
Loop condition is false. Execution continues on line 184
159
160 if (isprint(c)((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int) _ISprint
)
) {
161
162 if (space) {
163 if (left <= 2) {
164 dotdotdot = true1;
165 break;
166 }
167
168 *(k++) = ' ';
169 left--;
170 space = false0;
171 }
172
173 if (left <= 1) {
174 dotdotdot = true1;
175 break;
176 }
177
178 *(k++) = (char) c;
179 left--;
180 } else if (k > ans)
181 space = true1;
182 }
183
184 if (dotdotdot
18.1
'dotdotdot' is false
18.1
'dotdotdot' is false
) {
19
Taking false branch
185 if (max_length <= 4) {
186 k = ans;
187 left = max_length;
188 } else {
189 k = ans + max_length - 4;
190 left = 4;
191
192 /* Eat up final spaces */
193 while (k > ans && isspace(k[-1])((*__ctype_b_loc ())[(int) ((k[-1]))] & (unsigned short int
) _ISspace)
) {
194 k--;
195 left++;
196 }
197 }
198
199 strncpy(k, "...", left-1);
200 k[left-1] = 0;
201 } else
202 *k = 0;
203 }
204
205 /* Kernel threads have no argv[] */
206 if (isempty(ans)) {
20
Taking true branch
207 _cleanup_free___attribute__((cleanup(freep))) char *t = NULL((void*)0);
208 int h;
209
210 ans = mfree(ans);
211
212 if (!comm_fallback)
21
Assuming 'comm_fallback' is true
22
Taking false branch
213 return -ENOENT2;
214
215 h = get_process_comm(pid, &t);
23
Calling 'get_process_comm'
216 if (h < 0)
217 return h;
218
219 size_t l = strlen(t);
220
221 if (l + 3 <= max_length) {
222 ans = strjoin("[", t, "]")strjoin_real(("["), t, "]", ((void*)0));
223 if (!ans)
224 return -ENOMEM12;
225
226 } else if (max_length <= 6) {
227 ans = new(char, max_length)((char*) malloc_multiply(sizeof(char), (max_length)));
228 if (!ans)
229 return -ENOMEM12;
230
231 memcpy(ans, "[...]", max_length-1);
232 ans[max_length-1] = 0;
233 } else {
234 t[max_length - 6] = 0;
235
236 /* Chop off final spaces */
237 delete_trailing_chars(t, WHITESPACE" \t\n\r");
238
239 ans = strjoin("[", t, "...]")strjoin_real(("["), t, "...]", ((void*)0));
240 if (!ans)
241 return -ENOMEM12;
242 }
243
244 *line = TAKE_PTR(ans)({ typeof(ans) _ptr_ = (ans); (ans) = ((void*)0); _ptr_; });
245 return 0;
246 }
247
248 k = realloc(ans, strlen(ans) + 1);
249 if (!k)
250 return -ENOMEM12;
251
252 ans = NULL((void*)0);
253 *line = k;
254
255 return 0;
256}
257
258int rename_process(const char name[]) {
259 static size_t mm_size = 0;
260 static char *mm = NULL((void*)0);
261 bool_Bool truncated = false0;
262 size_t l;
263
264 /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
265 * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
266 * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
267 * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
268 * truncated.
269 *
270 * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
271
272 if (isempty(name))
273 return -EINVAL22; /* let's not confuse users unnecessarily with an empty name */
274
275 if (!is_main_thread())
276 return -EPERM1; /* Let's not allow setting the process name from other threads than the main one, as we
277 * cache things without locking, and we make assumptions that PR_SET_NAME sets the
278 * process name that isn't correct on any other threads */
279
280 l = strlen(name);
281
282 /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
283 * can use PR_SET_NAME, which sets the thread name for the calling thread. */
284 if (prctl(PR_SET_NAME15, name) < 0)
285 log_debug_errno(errno, "PR_SET_NAME failed: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 285, __func__
, "PR_SET_NAME failed: %m") : -abs(_e); })
;
286 if (l >= TASK_COMM_LEN16) /* Linux process names can be 15 chars at max */
287 truncated = true1;
288
289 /* Second step, change glibc's ID of the process name. */
290 if (program_invocation_name) {
291 size_t k;
292
293 k = strlen(program_invocation_name);
294 strncpy(program_invocation_name, name, k);
295 if (l > k)
296 truncated = true1;
297 }
298
299 /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
300 * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
301 * the end. This is the best option for changing /proc/self/cmdline. */
302
303 /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
304 * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
305 * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if
306 * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
307 * mmap() is not. */
308 if (geteuid() != 0)
309 log_debug("Skipping PR_SET_MM, as we don't have privileges.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 309, __func__, "Skipping PR_SET_MM, as we don't have privileges."
) : -abs(_e); })
;
310 else if (mm_size < l+1) {
311 size_t nn_size;
312 char *nn;
313
314 nn_size = PAGE_ALIGN(l+1)ALIGN_TO((l+1), page_size());
315 nn = mmap(NULL((void*)0), nn_size, PROT_READ0x1|PROT_WRITE0x2, MAP_PRIVATE0x02|MAP_ANONYMOUS0x20, -1, 0);
316 if (nn == MAP_FAILED((void *) -1)) {
317 log_debug_errno(errno, "mmap() failed: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 317, __func__
, "mmap() failed: %m") : -abs(_e); })
;
318 goto use_saved_argv;
319 }
320
321 strncpy(nn, name, nn_size);
322
323 /* Now, let's tell the kernel about this new memory */
324 if (prctl(PR_SET_MM35, PR_SET_MM_ARG_START8, (unsigned long) nn, 0, 0) < 0) {
325 log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 325, __func__
, "PR_SET_MM_ARG_START failed, proceeding without: %m") : -abs
(_e); })
;
326 (void) munmap(nn, nn_size);
327 goto use_saved_argv;
328 }
329
330 /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's
331 * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */
332 if (prctl(PR_SET_MM35, PR_SET_MM_ARG_END9, (unsigned long) nn + l + 1, 0, 0) < 0)
333 log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 333, __func__
, "PR_SET_MM_ARG_END failed, proceeding without: %m") : -abs(
_e); })
;
334
335 if (mm)
336 (void) munmap(mm, mm_size);
337
338 mm = nn;
339 mm_size = nn_size;
340 } else {
341 strncpy(mm, name, mm_size);
342
343 /* Update the end pointer, continuing regardless of any failure. */
344 if (prctl(PR_SET_MM35, PR_SET_MM_ARG_END9, (unsigned long) mm + l + 1, 0, 0) < 0)
345 log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 345, __func__
, "PR_SET_MM_ARG_END failed, proceeding without: %m") : -abs(
_e); })
;
346 }
347
348use_saved_argv:
349 /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
350 * it still looks here */
351
352 if (saved_argc > 0) {
353 int i;
354
355 if (saved_argv[0]) {
356 size_t k;
357
358 k = strlen(saved_argv[0]);
359 strncpy(saved_argv[0], name, k);
360 if (l > k)
361 truncated = true1;
362 }
363
364 for (i = 1; i < saved_argc; i++) {
365 if (!saved_argv[i])
366 break;
367
368 memzero(saved_argv[i], strlen(saved_argv[i]))({ size_t _l_ = (strlen(saved_argv[i])); void *_x_ = (saved_argv
[i]); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); })
;
369 }
370 }
371
372 return !truncated;
373}
374
375int is_kernel_thread(pid_t pid) {
376 _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0);
377 unsigned long long flags;
378 size_t l, i;
379 const char *p;
380 char *q;
381 int r;
382
383 if (IN_SET(pid, 0, 1)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, 1})/sizeof(int)]; switch(pid) { case 0
: case 1: _found = 1; break; default: break; } _found; })
|| pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
384 return 0;
385 if (!pid_is_valid(pid))
386 return -EINVAL22;
387
388 p = procfs_file_alloca(pid, "stat")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "stat"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("stat")); sprintf((char*) _r_, "/proc/""%" "i""/" "stat"
, _pid_); } _r_; })
;
389 r = read_one_line_file(p, &line);
390 if (r == -ENOENT2)
391 return -ESRCH3;
392 if (r < 0)
393 return r;
394
395 /* Skip past the comm field */
396 q = strrchr(line, ')');
397 if (!q)
398 return -EINVAL22;
399 q++;
400
401 /* Skip 6 fields to reach the flags field */
402 for (i = 0; i < 6; i++) {
403 l = strspn(q, WHITESPACE" \t\n\r");
404 if (l < 1)
405 return -EINVAL22;
406 q += l;
407
408 l = strcspn(q, WHITESPACE" \t\n\r");
409 if (l < 1)
410 return -EINVAL22;
411 q += l;
412 }
413
414 /* Skip preceeding whitespace */
415 l = strspn(q, WHITESPACE" \t\n\r");
416 if (l < 1)
417 return -EINVAL22;
418 q += l;
419
420 /* Truncate the rest */
421 l = strcspn(q, WHITESPACE" \t\n\r");
422 if (l < 1)
423 return -EINVAL22;
424 q[l] = 0;
425
426 r = safe_atollu(q, &flags);
427 if (r < 0)
428 return r;
429
430 return !!(flags & PF_KTHREAD0x00200000);
431}
432
433int get_process_capeff(pid_t pid, char **capeff) {
434 const char *p;
435 int r;
436
437 assert(capeff)do { if ((__builtin_expect(!!(!(capeff)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("capeff"), "../src/basic/process-util.c"
, 437, __PRETTY_FUNCTION__); } while (0)
;
438 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 438, __PRETTY_FUNCTION__); } while (0)
;
439
440 p = procfs_file_alloca(pid, "status")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "status"); } else { _r_ = __builtin_alloca (
(sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("status")); sprintf((char*) _r_, "/proc/""%" "i""/"
"status", _pid_); } _r_; })
;
441
442 r = get_proc_field(p, "CapEff", WHITESPACE" \t\n\r", capeff);
443 if (r == -ENOENT2)
444 return -ESRCH3;
445
446 return r;
447}
448
449static int get_process_link_contents(const char *proc_file, char **name) {
450 int r;
451
452 assert(proc_file)do { if ((__builtin_expect(!!(!(proc_file)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("proc_file"), "../src/basic/process-util.c"
, 452, __PRETTY_FUNCTION__); } while (0)
;
453 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/basic/process-util.c", 453
, __PRETTY_FUNCTION__); } while (0)
;
454
455 r = readlink_malloc(proc_file, name);
456 if (r == -ENOENT2)
457 return -ESRCH3;
458 if (r < 0)
459 return r;
460
461 return 0;
462}
463
464int get_process_exe(pid_t pid, char **name) {
465 const char *p;
466 char *d;
467 int r;
468
469 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 469, __PRETTY_FUNCTION__); } while (0)
;
470
471 p = procfs_file_alloca(pid, "exe")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "exe"); } else { _r_ = __builtin_alloca ((sizeof
("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof(
pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("exe")); sprintf((char*) _r_, "/proc/""%" "i""/" "exe"
, _pid_); } _r_; })
;
472 r = get_process_link_contents(p, name);
473 if (r < 0)
474 return r;
475
476 d = endswith(*name, " (deleted)");
477 if (d)
478 *d = '\0';
479
480 return 0;
481}
482
483static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
484 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
485 char line[LINE_MAX2048];
486 const char *p;
487
488 assert(field)do { if ((__builtin_expect(!!(!(field)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("field"), "../src/basic/process-util.c",
488, __PRETTY_FUNCTION__); } while (0)
;
489 assert(uid)do { if ((__builtin_expect(!!(!(uid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("uid"), "../src/basic/process-util.c", 489
, __PRETTY_FUNCTION__); } while (0)
;
490
491 if (pid < 0)
492 return -EINVAL22;
493
494 p = procfs_file_alloca(pid, "status")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "status"); } else { _r_ = __builtin_alloca (
(sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("status")); sprintf((char*) _r_, "/proc/""%" "i""/"
"status", _pid_); } _r_; })
;
495 f = fopen(p, "re");
496 if (!f) {
497 if (errno(*__errno_location ()) == ENOENT2)
498 return -ESRCH3;
499 return -errno(*__errno_location ());
500 }
501
502 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
503
504 FOREACH_LINE(line, f, return -errno)for (;;) if (!fgets(line, sizeof(line), f)) { if (ferror(f)) {
return -(*__errno_location ()); } break; } else
{
505 char *l;
506
507 l = strstrip(line);
508
509 if (startswith(l, field)) {
510 l += strlen(field);
511 l += strspn(l, WHITESPACE" \t\n\r");
512
513 l[strcspn(l, WHITESPACE" \t\n\r")] = 0;
514
515 return parse_uid(l, uid);
516 }
517 }
518
519 return -EIO5;
520}
521
522int get_process_uid(pid_t pid, uid_t *uid) {
523
524 if (pid == 0 || pid == getpid_cached()) {
525 *uid = getuid();
526 return 0;
527 }
528
529 return get_process_id(pid, "Uid:", uid);
530}
531
532int get_process_gid(pid_t pid, gid_t *gid) {
533
534 if (pid == 0 || pid == getpid_cached()) {
535 *gid = getgid();
536 return 0;
537 }
538
539 assert_cc(sizeof(uid_t) == sizeof(gid_t))GCC diagnostic push ; GCC diagnostic ignored "-Wdeclaration-after-statement"
; struct _assert_struct_10 { char x[(sizeof(uid_t) == sizeof
(gid_t)) ? 0 : -1]; }; GCC diagnostic pop
;
540 return get_process_id(pid, "Gid:", gid);
541}
542
543int get_process_cwd(pid_t pid, char **cwd) {
544 const char *p;
545
546 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 546, __PRETTY_FUNCTION__); } while (0)
;
547
548 p = procfs_file_alloca(pid, "cwd")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "cwd"); } else { _r_ = __builtin_alloca ((sizeof
("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof(
pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("cwd")); sprintf((char*) _r_, "/proc/""%" "i""/" "cwd"
, _pid_); } _r_; })
;
549
550 return get_process_link_contents(p, cwd);
551}
552
553int get_process_root(pid_t pid, char **root) {
554 const char *p;
555
556 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 556, __PRETTY_FUNCTION__); } while (0)
;
557
558 p = procfs_file_alloca(pid, "root")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "root"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("root")); sprintf((char*) _r_, "/proc/""%" "i""/" "root"
, _pid_); } _r_; })
;
559
560 return get_process_link_contents(p, root);
561}
562
563int get_process_environ(pid_t pid, char **env) {
564 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
565 _cleanup_free___attribute__((cleanup(freep))) char *outcome = NULL((void*)0);
566 int c;
567 const char *p;
568 size_t allocated = 0, sz = 0;
569
570 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 570, __PRETTY_FUNCTION__); } while (0)
;
571 assert(env)do { if ((__builtin_expect(!!(!(env)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("env"), "../src/basic/process-util.c", 571
, __PRETTY_FUNCTION__); } while (0)
;
572
573 p = procfs_file_alloca(pid, "environ")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "environ"); } else { _r_ = __builtin_alloca
((sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 :
sizeof(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof
(pid_t) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)])
)) + 1 + sizeof("environ")); sprintf((char*) _r_, "/proc/""%"
"i""/" "environ", _pid_); } _r_; })
;
574
575 f = fopen(p, "re");
576 if (!f) {
577 if (errno(*__errno_location ()) == ENOENT2)
578 return -ESRCH3;
579 return -errno(*__errno_location ());
580 }
581
582 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
583
584 while ((c = fgetc(f)) != EOF(-1)) {
585 if (!GREEDY_REALLOC(outcome, allocated, sz + 5)greedy_realloc((void**) &(outcome), &(allocated), (sz
+ 5), sizeof((outcome)[0]))
)
586 return -ENOMEM12;
587
588 if (c == '\0')
589 outcome[sz++] = '\n';
590 else
591 sz += cescape_char(c, outcome + sz);
592 }
593
594 if (!outcome) {
595 outcome = strdup("");
596 if (!outcome)
597 return -ENOMEM12;
598 } else
599 outcome[sz] = '\0';
600
601 *env = TAKE_PTR(outcome)({ typeof(outcome) _ptr_ = (outcome); (outcome) = ((void*)0);
_ptr_; })
;
602
603 return 0;
604}
605
606int get_process_ppid(pid_t pid, pid_t *_ppid) {
607 int r;
608 _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0);
609 long unsigned ppid;
610 const char *p;
611
612 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 612, __PRETTY_FUNCTION__); } while (0)
;
613 assert(_ppid)do { if ((__builtin_expect(!!(!(_ppid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_ppid"), "../src/basic/process-util.c",
613, __PRETTY_FUNCTION__); } while (0)
;
614
615 if (pid == 0 || pid == getpid_cached()) {
616 *_ppid = getppid();
617 return 0;
618 }
619
620 p = procfs_file_alloca(pid, "stat")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "stat"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("stat")); sprintf((char*) _r_, "/proc/""%" "i""/" "stat"
, _pid_); } _r_; })
;
621 r = read_one_line_file(p, &line);
622 if (r == -ENOENT2)
623 return -ESRCH3;
624 if (r < 0)
625 return r;
626
627 /* Let's skip the pid and comm fields. The latter is enclosed
628 * in () but does not escape any () in its value, so let's
629 * skip over it manually */
630
631 p = strrchr(line, ')');
632 if (!p)
633 return -EIO5;
634
635 p++;
636
637 if (sscanf(p, " "
638 "%*c " /* state */
639 "%lu ", /* ppid */
640 &ppid) != 1)
641 return -EIO5;
642
643 if ((long unsigned) (pid_t) ppid != ppid)
644 return -ERANGE34;
645
646 *_ppid = (pid_t) ppid;
647
648 return 0;
649}
650
651int wait_for_terminate(pid_t pid, siginfo_t *status) {
652 siginfo_t dummy;
653
654 assert(pid >= 1)do { if ((__builtin_expect(!!(!(pid >= 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 1"), "../src/basic/process-util.c"
, 654, __PRETTY_FUNCTION__); } while (0)
;
655
656 if (!status)
657 status = &dummy;
658
659 for (;;) {
660 zero(*status)(({ size_t _l_ = (sizeof(*status)); void *_x_ = (&(*status
)); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }))
;
661
662 if (waitid(P_PID, pid, status, WEXITED4) < 0) {
663
664 if (errno(*__errno_location ()) == EINTR4)
665 continue;
666
667 return negative_errno();
668 }
669
670 return 0;
671 }
672}
673
674/*
675 * Return values:
676 * < 0 : wait_for_terminate() failed to get the state of the
677 * process, the process was terminated by a signal, or
678 * failed for an unknown reason.
679 * >=0 : The process terminated normally, and its exit code is
680 * returned.
681 *
682 * That is, success is indicated by a return value of zero, and an
683 * error is indicated by a non-zero value.
684 *
685 * A warning is emitted if the process terminates abnormally,
686 * and also if it returns non-zero unless check_exit_code is true.
687 */
688int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
689 _cleanup_free___attribute__((cleanup(freep))) char *buffer = NULL((void*)0);
690 siginfo_t status;
691 int r, prio;
692
693 assert(pid > 1)do { if ((__builtin_expect(!!(!(pid > 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid > 1"), "../src/basic/process-util.c"
, 693, __PRETTY_FUNCTION__); } while (0)
;
694
695 if (!name) {
696 r = get_process_comm(pid, &buffer);
697 if (r < 0)
698 log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 698, __func__, "Failed to acquire process name of "
"%" "i" ", ignoring: %m", pid) : -abs(_e); })
;
699 else
700 name = buffer;
701 }
702
703 prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR3 : LOG_DEBUG7;
704
705 r = wait_for_terminate(pid, &status);
706 if (r < 0)
707 return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name))({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 707, __func__, "Failed to wait for %s: %m"
, strna(name)) : -abs(_e); })
;
708
709 if (status.si_code == CLD_EXITEDCLD_EXITED) {
710 if (status.si_status_sifields._sigchld.si_status != EXIT_SUCCESS0)
711 log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,({ int _level = (((flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ?
3 : 7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/basic/process-util.c"
, 712, __func__, "%s failed with exit status %i.", strna(name
), status._sifields._sigchld.si_status) : -abs(_e); })
712 "%s failed with exit status %i.", strna(name), status.si_status)({ int _level = (((flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ?
3 : 7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/basic/process-util.c"
, 712, __func__, "%s failed with exit status %i.", strna(name
), status._sifields._sigchld.si_status) : -abs(_e); })
;
713 else
714 log_debug("%s succeeded.", name)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 714, __func__, "%s succeeded."
, name) : -abs(_e); })
;
715
716 return status.si_status_sifields._sigchld.si_status;
717
718 } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){CLD_KILLED, CLD_DUMPED})/sizeof(int)]; switch
(status.si_code) { case CLD_KILLED: case CLD_DUMPED: _found =
1; break; default: break; } _found; })
) {
719
720 log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status))({ int _level = (((prio))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 720, __func__, "%s terminated by signal %s."
, strna(name), signal_to_string(status._sifields._sigchld.si_status
)) : -abs(_e); })
;
721 return -EPROTO71;
722 }
723
724 log_full(prio, "%s failed due to unknown reason.", strna(name))({ int _level = (((prio))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 724, __func__, "%s failed due to unknown reason."
, strna(name)) : -abs(_e); })
;
725 return -EPROTO71;
726}
727
728/*
729 * Return values:
730 *
731 * < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process
732 * was terminated by a signal, or failed for an unknown reason.
733 *
734 * >=0 : The process terminated normally with no failures.
735 *
736 * Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure
737 * states are indicated by error is indicated by a non-zero value.
738 *
739 * This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off
740 * to remain entirely race-free.
741 */
742int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
743 sigset_t mask;
744 int r;
745 usec_t until;
746
747 assert_se(sigemptyset(&mask) == 0)do { if ((__builtin_expect(!!(!(sigemptyset(&mask) == 0))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("sigemptyset(&mask) == 0"
), "../src/basic/process-util.c", 747, __PRETTY_FUNCTION__); }
while (0)
;
748 assert_se(sigaddset(&mask, SIGCHLD) == 0)do { if ((__builtin_expect(!!(!(sigaddset(&mask, 17) == 0
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("sigaddset(&mask, SIGCHLD) == 0"
), "../src/basic/process-util.c", 748, __PRETTY_FUNCTION__); }
while (0)
;
749
750 /* Drop into a sigtimewait-based timeout. Waiting for the
751 * pid to exit. */
752 until = now(CLOCK_MONOTONIC1) + timeout;
753 for (;;) {
754 usec_t n;
755 siginfo_t status = {};
756 struct timespec ts;
757
758 n = now(CLOCK_MONOTONIC1);
759 if (n >= until)
760 break;
761
762 r = sigtimedwait(&mask, NULL((void*)0), timespec_store(&ts, until - n)) < 0 ? -errno(*__errno_location ()) : 0;
763 /* Assuming we woke due to the child exiting. */
764 if (waitid(P_PID, pid, &status, WEXITED4|WNOHANG1) == 0) {
765 if (status.si_pid_sifields._kill.si_pid == pid) {
766 /* This is the correct child.*/
767 if (status.si_code == CLD_EXITEDCLD_EXITED)
768 return (status.si_status_sifields._sigchld.si_status == 0) ? 0 : -EPROTO71;
769 else
770 return -EPROTO71;
771 }
772 }
773 /* Not the child, check for errors and proceed appropriately */
774 if (r < 0) {
775 switch (r) {
776 case -EAGAIN11:
777 /* Timed out, child is likely hung. */
778 return -ETIMEDOUT110;
779 case -EINTR4:
780 /* Received a different signal and should retry */
781 continue;
782 default:
783 /* Return any unexpected errors */
784 return r;
785 }
786 }
787 }
788
789 return -EPROTO71;
790}
791
792void sigkill_wait(pid_t pid) {
793 assert(pid > 1)do { if ((__builtin_expect(!!(!(pid > 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid > 1"), "../src/basic/process-util.c"
, 793, __PRETTY_FUNCTION__); } while (0)
;
794
795 if (kill(pid, SIGKILL9) > 0)
796 (void) wait_for_terminate(pid, NULL((void*)0));
797}
798
799void sigkill_waitp(pid_t *pid) {
800 PROTECT_ERRNO__attribute__((cleanup(_reset_errno_))) __attribute__((unused
)) int _saved_errno_ = (*__errno_location ())
;
801
802 if (!pid)
803 return;
804 if (*pid <= 1)
805 return;
806
807 sigkill_wait(*pid);
808}
809
810void sigterm_wait(pid_t pid) {
811 assert(pid > 1)do { if ((__builtin_expect(!!(!(pid > 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid > 1"), "../src/basic/process-util.c"
, 811, __PRETTY_FUNCTION__); } while (0)
;
812
813 if (kill_and_sigcont(pid, SIGTERM15) > 0)
814 (void) wait_for_terminate(pid, NULL((void*)0));
815}
816
817int kill_and_sigcont(pid_t pid, int sig) {
818 int r;
819
820 r = kill(pid, sig) < 0 ? -errno(*__errno_location ()) : 0;
821
822 /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
823 * affected by a process being suspended anyway. */
824 if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){18, 9})/sizeof(int)]; switch(sig) { case
18: case 9: _found = 1; break; default: break; } _found; })
)
825 (void) kill(pid, SIGCONT18);
826
827 return r;
828}
829
830int getenv_for_pid(pid_t pid, const char *field, char **ret) {
831 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
832 char *value = NULL((void*)0);
833 bool_Bool done = false0;
834 const char *path;
835 size_t l;
836
837 assert(pid >= 0)do { if ((__builtin_expect(!!(!(pid >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pid >= 0"), "../src/basic/process-util.c"
, 837, __PRETTY_FUNCTION__); } while (0)
;
838 assert(field)do { if ((__builtin_expect(!!(!(field)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("field"), "../src/basic/process-util.c",
838, __PRETTY_FUNCTION__); } while (0)
;
839 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/process-util.c", 839
, __PRETTY_FUNCTION__); } while (0)
;
840
841 if (pid == 0 || pid == getpid_cached()) {
842 const char *e;
843
844 e = getenv(field);
845 if (!e) {
846 *ret = NULL((void*)0);
847 return 0;
848 }
849
850 value = strdup(e);
851 if (!value)
852 return -ENOMEM12;
853
854 *ret = value;
855 return 1;
856 }
857
858 path = procfs_file_alloca(pid, "environ")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "environ"); } else { _r_ = __builtin_alloca
((sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 :
sizeof(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof
(pid_t) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)])
)) + 1 + sizeof("environ")); sprintf((char*) _r_, "/proc/""%"
"i""/" "environ", _pid_); } _r_; })
;
859
860 f = fopen(path, "re");
861 if (!f) {
862 if (errno(*__errno_location ()) == ENOENT2)
863 return -ESRCH3;
864
865 return -errno(*__errno_location ());
866 }
867
868 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
869
870 l = strlen(field);
871
872 do {
873 char line[LINE_MAX2048];
874 size_t i;
875
876 for (i = 0; i < sizeof(line)-1; i++) {
877 int c;
878
879 c = getc(f);
880 if (_unlikely_(c == EOF)(__builtin_expect(!!(c == (-1)),0))) {
881 done = true1;
882 break;
883 } else if (c == 0)
884 break;
885
886 line[i] = c;
887 }
888 line[i] = 0;
889
890 if (strneq(line, field, l)(strncmp((line), (field), (l)) == 0) && line[l] == '=') {
891 value = strdup(line + l + 1);
892 if (!value)
893 return -ENOMEM12;
894
895 *ret = value;
896 return 1;
897 }
898
899 } while (!done);
900
901 *ret = NULL((void*)0);
902 return 0;
903}
904
905int pid_is_my_child(pid_t pid) {
906 pid_t ppid;
907 int r;
908
909 if (pid <= 1)
910 return false0;
911
912 r = get_process_ppid(pid, &ppid);
913 if (r < 0)
914 return r;
915
916 return ppid == getpid_cached();
917}
918
919bool_Bool pid_is_unwaited(pid_t pid) {
920 /* Checks whether a PID is still valid at all, including a zombie */
921
922 if (pid < 0)
923 return false0;
924
925 if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
926 return true1;
927
928 if (pid == getpid_cached())
929 return true1;
930
931 if (kill(pid, 0) >= 0)
932 return true1;
933
934 return errno(*__errno_location ()) != ESRCH3;
935}
936
937bool_Bool pid_is_alive(pid_t pid) {
938 int r;
939
940 /* Checks whether a PID is still valid and not a zombie */
941
942 if (pid < 0)
943 return false0;
944
945 if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
946 return true1;
947
948 if (pid == getpid_cached())
949 return true1;
950
951 r = get_process_state(pid);
952 if (IN_SET(r, -ESRCH, 'Z')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){-3, 'Z'})/sizeof(int)]; switch(r) { case
-3: case 'Z': _found = 1; break; default: break; } _found; }
)
)
953 return false0;
954
955 return true1;
956}
957
958int pid_from_same_root_fs(pid_t pid) {
959 const char *root;
960
961 if (pid < 0)
962 return false0;
963
964 if (pid == 0 || pid == getpid_cached())
965 return true1;
966
967 root = procfs_file_alloca(pid, "root")({ pid_t _pid_ = (pid); const char *_r_; if (_pid_ == 0) { _r_
= ("/proc/self/" "root"); } else { _r_ = __builtin_alloca ((
sizeof("""/proc/""") - 1) + (2+(sizeof(pid_t) <= 1 ? 3 : sizeof
(pid_t) <= 2 ? 5 : sizeof(pid_t) <= 4 ? 10 : sizeof(pid_t
) <= 8 ? 20 : sizeof(int[-2*(sizeof(pid_t) > 8)]))) + 1
+ sizeof("root")); sprintf((char*) _r_, "/proc/""%" "i""/" "root"
, _pid_); } _r_; })
;
968
969 return files_same(root, "/proc/1/root", 0);
970}
971
972bool_Bool is_main_thread(void) {
973 static thread_local__thread int cached = 0;
974
975 if (_unlikely_(cached == 0)(__builtin_expect(!!(cached == 0),0)))
976 cached = getpid_cached() == gettid() ? 1 : -1;
977
978 return cached > 0;
979}
980
981_noreturn___attribute__((noreturn)) void freeze(void) {
982
983 log_close();
984
985 /* Make sure nobody waits for us on a socket anymore */
986 close_all_fds(NULL((void*)0), 0);
987
988 sync();
989
990 /* Let's not freeze right away, but keep reaping zombies. */
991 for (;;) {
992 int r;
993 siginfo_t si = {};
994
995 r = waitid(P_ALL, 0, &si, WEXITED4);
996 if (r < 0 && errno(*__errno_location ()) != EINTR4)
997 break;
998 }
999
1000 /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
1001 for (;;)
1002 pause();
1003}
1004
1005bool_Bool oom_score_adjust_is_valid(int oa) {
1006 return oa >= OOM_SCORE_ADJ_MIN(-1000) && oa <= OOM_SCORE_ADJ_MAX1000;
1007}
1008
1009unsigned long personality_from_string(const char *p) {
1010 int architecture;
1011
1012 if (!p)
1013 return PERSONALITY_INVALID0xffffffffLU;
1014
1015 /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just
1016 * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for
1017 * the same register size. */
1018
1019 architecture = architecture_from_string(p);
1020 if (architecture < 0)
1021 return PERSONALITY_INVALID0xffffffffLU;
1022
1023 if (architecture == native_architecture()ARCHITECTURE_X86_64)
1024 return PER_LINUX;
1025#ifdef SECONDARY_ARCHITECTUREARCHITECTURE_X86
1026 if (architecture == SECONDARY_ARCHITECTUREARCHITECTURE_X86)
1027 return PER_LINUX32;
1028#endif
1029
1030 return PERSONALITY_INVALID0xffffffffLU;
1031}
1032
1033const char* personality_to_string(unsigned long p) {
1034 int architecture = _ARCHITECTURE_INVALID;
1035
1036 if (p == PER_LINUX)
1037 architecture = native_architecture()ARCHITECTURE_X86_64;
1038#ifdef SECONDARY_ARCHITECTUREARCHITECTURE_X86
1039 else if (p == PER_LINUX32)
1040 architecture = SECONDARY_ARCHITECTUREARCHITECTURE_X86;
1041#endif
1042
1043 if (architecture < 0)
1044 return NULL((void*)0);
1045
1046 return architecture_to_string(architecture);
1047}
1048
1049int safe_personality(unsigned long p) {
1050 int ret;
1051
1052 /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno,
1053 * and in others as negative return value containing an errno-like value. Let's work around this: this is a
1054 * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and
1055 * the return value indicating the same issue, so that we are definitely on the safe side.
1056 *
1057 * See https://github.com/systemd/systemd/issues/6737 */
1058
1059 errno(*__errno_location ()) = 0;
1060 ret = personality(p);
1061 if (ret < 0) {
1062 if (errno(*__errno_location ()) != 0)
1063 return -errno(*__errno_location ());
1064
1065 errno(*__errno_location ()) = -ret;
1066 }
1067
1068 return ret;
1069}
1070
1071int opinionated_personality(unsigned long *ret) {
1072 int current;
1073
1074 /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit
1075 * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the
1076 * two most relevant personalities: PER_LINUX and PER_LINUX32. */
1077
1078 current = safe_personality(PERSONALITY_INVALID0xffffffffLU);
1079 if (current < 0)
1080 return current;
1081
1082 if (((unsigned long) current & 0xffff) == PER_LINUX32)
1083 *ret = PER_LINUX32;
1084 else
1085 *ret = PER_LINUX;
1086
1087 return 0;
1088}
1089
1090void valgrind_summary_hack(void) {
1091#if HAVE_VALGRIND_VALGRIND_H1
1092 if (getpid_cached() == 1 && RUNNING_ON_VALGRIND(unsigned)__extension__ ({ volatile unsigned long int _zzq_args
[6]; volatile unsigned long int _zzq_result; _zzq_args[0] = (
unsigned long int)(VG_USERREQ__RUNNING_ON_VALGRIND); _zzq_args
[1] = (unsigned long int)(0); _zzq_args[2] = (unsigned long int
)(0); _zzq_args[3] = (unsigned long int)(0); _zzq_args[4] = (
unsigned long int)(0); _zzq_args[5] = (unsigned long int)(0);
__asm__ volatile("rolq $3, %%rdi ; rolq $13, %%rdi\n\t" "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
"xchgq %%rbx,%%rbx" : "=d" (_zzq_result) : "a" (&_zzq_args
[0]), "0" (0) : "cc", "memory" ); _zzq_result; })
) {
1093 pid_t pid;
1094 pid = raw_clone(SIGCHLD17);
1095 if (pid < 0)
1096 log_emergency_errno(errno, "Failed to fork off valgrind helper: %m")({ int _level = ((log_emergency_level())), _e = (((*__errno_location
()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/basic/process-util.c"
, 1096, __func__, "Failed to fork off valgrind helper: %m") :
-abs(_e); })
;
1097 else if (pid == 0)
1098 exit(EXIT_SUCCESS0);
1099 else {
1100 log_info("Spawned valgrind helper as PID "PID_FMT".", pid)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1100, __func__, "Spawned valgrind helper as PID "
"%" "i"".", pid) : -abs(_e); })
;
1101 (void) wait_for_terminate(pid, NULL((void*)0));
1102 }
1103 }
1104#endif
1105}
1106
1107int pid_compare_func(const void *a, const void *b) {
1108 const pid_t *p = a, *q = b;
1109
1110 /* Suitable for usage in qsort() */
1111
1112 if (*p < *q)
1113 return -1;
1114 if (*p > *q)
1115 return 1;
1116 return 0;
1117}
1118
1119int ioprio_parse_priority(const char *s, int *ret) {
1120 int i, r;
1121
1122 assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("s"), "../src/basic/process-util.c", 1122
, __PRETTY_FUNCTION__); } while (0)
;
1123 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/process-util.c", 1123
, __PRETTY_FUNCTION__); } while (0)
;
1124
1125 r = safe_atoi(s, &i);
1126 if (r < 0)
1127 return r;
1128
1129 if (!ioprio_priority_is_valid(i))
1130 return -EINVAL22;
1131
1132 *ret = i;
1133 return 0;
1134}
1135
1136/* The cached PID, possible values:
1137 *
1138 * == UNSET [0] → cache not initialized yet
1139 * == BUSY [-1] → some thread is initializing it at the moment
1140 * any other → the cached PID
1141 */
1142
1143#define CACHED_PID_UNSET((pid_t) 0) ((pid_t) 0)
1144#define CACHED_PID_BUSY((pid_t) -1) ((pid_t) -1)
1145
1146static pid_t cached_pid = CACHED_PID_UNSET((pid_t) 0);
1147
1148void reset_cached_pid(void) {
1149 /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
1150 cached_pid = CACHED_PID_UNSET((pid_t) 0);
1151}
1152
1153/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc
1154 * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
1155 * libpthread, as it is part of glibc anyway. */
1156extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle);
1157extern void* __dso_handle __attribute__ ((__weak__));
1158
1159pid_t getpid_cached(void) {
1160 static bool_Bool installed = false0;
1161 pid_t current_value;
1162
1163 /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
1164 * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
1165 * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when
1166 * objects were used across fork()s. With this caching the old behaviour is somewhat restored.
1167 *
1168 * https://bugzilla.redhat.com/show_bug.cgi?id=1443976
1169 * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
1170 */
1171
1172 current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET((pid_t) 0), CACHED_PID_BUSY((pid_t) -1));
1173
1174 switch (current_value) {
1175
1176 case CACHED_PID_UNSET((pid_t) 0): { /* Not initialized yet, then do so now */
1177 pid_t new_pid;
1178
1179 new_pid = raw_getpid();
1180
1181 if (!installed) {
1182 /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
1183 * only half-documented (glibc doesn't document it but LSB does — though only superficially)
1184 * we'll check for errors only in the most generic fashion possible. */
1185
1186 if (__register_atfork(NULL((void*)0), NULL((void*)0), reset_cached_pid, __dso_handle) != 0) {
1187 /* OOM? Let's try again later */
1188 cached_pid = CACHED_PID_UNSET((pid_t) 0);
1189 return new_pid;
1190 }
1191
1192 installed = true1;
1193 }
1194
1195 cached_pid = new_pid;
1196 return new_pid;
1197 }
1198
1199 case CACHED_PID_BUSY((pid_t) -1): /* Somebody else is currently initializing */
1200 return raw_getpid();
1201
1202 default: /* Properly initialized */
1203 return current_value;
1204 }
1205}
1206
1207int must_be_root(void) {
1208
1209 if (geteuid() == 0)
1210 return 0;
1211
1212 log_error("Need to be root.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1212, __func__, "Need to be root."
) : -abs(_e); })
;
1213 return -EPERM1;
1214}
1215
1216int safe_fork_full(
1217 const char *name,
1218 const int except_fds[],
1219 size_t n_except_fds,
1220 ForkFlags flags,
1221 pid_t *ret_pid) {
1222
1223 pid_t original_pid, pid;
1224 sigset_t saved_ss, ss;
1225 bool_Bool block_signals = false0;
1226 int prio, r;
1227
1228 /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
1229 * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
1230
1231 prio = flags & FORK_LOG ? LOG_ERR3 : LOG_DEBUG7;
1232
1233 original_pid = getpid_cached();
1234
1235 if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
1236
1237 /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
1238 * be sure that SIGTERMs are not lost we might send to the child. */
1239
1240 if (sigfillset(&ss) < 0)
1241 return log_full_errno(prio, errno, "Failed to reset signal set: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1241, __func__
, "Failed to reset signal set: %m") : -abs(_e); })
;
1242
1243 block_signals = true1;
1244
1245 } else if (flags & FORK_WAIT) {
1246
1247 /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
1248
1249 if (sigemptyset(&ss) < 0)
1250 return log_full_errno(prio, errno, "Failed to clear signal set: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1250, __func__
, "Failed to clear signal set: %m") : -abs(_e); })
;
1251
1252 if (sigaddset(&ss, SIGCHLD17) < 0)
1253 return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1253, __func__
, "Failed to add SIGCHLD to signal set: %m") : -abs(_e); })
;
1254
1255 block_signals = true1;
1256 }
1257
1258 if (block_signals)
1259 if (sigprocmask(SIG_SETMASK2, &ss, &saved_ss) < 0)
1260 return log_full_errno(prio, errno, "Failed to set signal mask: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1260, __func__
, "Failed to set signal mask: %m") : -abs(_e); })
;
1261
1262 if (flags & FORK_NEW_MOUNTNS)
1263 pid = raw_clone(SIGCHLD17|CLONE_NEWNS0x00020000);
1264 else
1265 pid = fork();
1266 if (pid < 0) {
1267 r = -errno(*__errno_location ());
1268
1269 if (block_signals) /* undo what we did above */
1270 (void) sigprocmask(SIG_SETMASK2, &saved_ss, NULL((void*)0));
1271
1272 return log_full_errno(prio, r, "Failed to fork: %m")({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1272, __func__, "Failed to fork: %m"
) : -abs(_e); })
;
1273 }
1274 if (pid > 0) {
1275 /* We are in the parent process */
1276
1277 log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1277, __func__, "Successfully forked off '%s' as PID "
"%" "i" ".", strna(name), pid) : -abs(_e); })
;
1278
1279 if (flags & FORK_WAIT) {
1280 r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
1281 if (r < 0)
1282 return r;
1283 if (r != EXIT_SUCCESS0) /* exit status > 0 should be treated as failure, too */
1284 return -EPROTO71;
1285 }
1286
1287 if (block_signals) /* undo what we did above */
1288 (void) sigprocmask(SIG_SETMASK2, &saved_ss, NULL((void*)0));
1289
1290 if (ret_pid)
1291 *ret_pid = pid;
1292
1293 return 1;
1294 }
1295
1296 /* We are in the child process */
1297
1298 if (flags & FORK_REOPEN_LOG) {
1299 /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
1300 log_close();
1301 log_set_open_when_needed(true1);
1302 }
1303
1304 if (name) {
1305 r = rename_process(name);
1306 if (r < 0)
1307 log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,({ int _level = ((flags & FORK_LOG ? 4 : 7)), _e = ((r)),
_realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm
) >= ((_level) & 0x07)) ? log_internal_realm(((_realm)
<< 10 | (_level)), _e, "../src/basic/process-util.c", 1308
, __func__, "Failed to rename process, ignoring: %m") : -abs(
_e); })
1308 r, "Failed to rename process, ignoring: %m")({ int _level = ((flags & FORK_LOG ? 4 : 7)), _e = ((r)),
_realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm
) >= ((_level) & 0x07)) ? log_internal_realm(((_realm)
<< 10 | (_level)), _e, "../src/basic/process-util.c", 1308
, __func__, "Failed to rename process, ignoring: %m") : -abs(
_e); })
;
1309 }
1310
1311 if (flags & FORK_DEATHSIG)
1312 if (prctl(PR_SET_PDEATHSIG1, SIGTERM15) < 0) {
1313 log_full_errno(prio, errno, "Failed to set death signal: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1313, __func__
, "Failed to set death signal: %m") : -abs(_e); })
;
1314 _exit(EXIT_FAILURE1);
1315 }
1316
1317 if (flags & FORK_RESET_SIGNALS) {
1318 r = reset_all_signal_handlers();
1319 if (r < 0) {
1320 log_full_errno(prio, r, "Failed to reset signal handlers: %m")({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1320, __func__, "Failed to reset signal handlers: %m"
) : -abs(_e); })
;
1321 _exit(EXIT_FAILURE1);
1322 }
1323
1324 /* This implicitly undoes the signal mask stuff we did before the fork()ing above */
1325 r = reset_signal_mask();
1326 if (r < 0) {
1327 log_full_errno(prio, r, "Failed to reset signal mask: %m")({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1327, __func__, "Failed to reset signal mask: %m"
) : -abs(_e); })
;
1328 _exit(EXIT_FAILURE1);
1329 }
1330 } else if (block_signals) { /* undo what we did above */
1331 if (sigprocmask(SIG_SETMASK2, &saved_ss, NULL((void*)0)) < 0) {
1332 log_full_errno(prio, errno, "Failed to restore signal mask: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1332, __func__
, "Failed to restore signal mask: %m") : -abs(_e); })
;
1333 _exit(EXIT_FAILURE1);
1334 }
1335 }
1336
1337 if (flags & FORK_DEATHSIG) {
1338 pid_t ppid;
1339 /* Let's see if the parent PID is still the one we started from? If not, then the parent
1340 * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
1341
1342 ppid = getppid();
1343 if (ppid == 0)
1344 /* Parent is in a differn't PID namespace. */;
1345 else if (ppid != original_pid) {
1346 log_debug("Parent died early, raising SIGTERM.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1346, __func__, "Parent died early, raising SIGTERM."
) : -abs(_e); })
;
1347 (void) raise(SIGTERM15);
1348 _exit(EXIT_FAILURE1);
1349 }
1350 }
1351
1352 if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)(((flags) & (FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) == (
FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE))
) {
1353
1354 /* Optionally, make sure we never propagate mounts to the host. */
1355
1356 if (mount(NULL((void*)0), "/", NULL((void*)0), MS_SLAVEMS_SLAVE | MS_RECMS_REC, NULL((void*)0)) < 0) {
1357 log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m")({ int _level = ((prio)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1357, __func__
, "Failed to remount root directory as MS_SLAVE: %m") : -abs(
_e); })
;
1358 _exit(EXIT_FAILURE1);
1359 }
1360 }
1361
1362 if (flags & FORK_CLOSE_ALL_FDS) {
1363 /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
1364 log_close();
1365
1366 r = close_all_fds(except_fds, n_except_fds);
1367 if (r < 0) {
1368 log_full_errno(prio, r, "Failed to close all file descriptors: %m")({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1368, __func__, "Failed to close all file descriptors: %m"
) : -abs(_e); })
;
1369 _exit(EXIT_FAILURE1);
1370 }
1371 }
1372
1373 /* When we were asked to reopen the logs, do so again now */
1374 if (flags & FORK_REOPEN_LOG) {
1375 log_open();
1376 log_set_open_when_needed(false0);
1377 }
1378
1379 if (flags & FORK_NULL_STDIO) {
1380 r = make_null_stdio();
1381 if (r < 0) {
1382 log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m")({ int _level = ((prio)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/basic/process-util.c", 1382, __func__, "Failed to connect stdin/stdout to /dev/null: %m"
) : -abs(_e); })
;
1383 _exit(EXIT_FAILURE1);
1384 }
1385 }
1386
1387 if (ret_pid)
1388 *ret_pid = getpid_cached();
1389
1390 return 0;
1391}
1392
1393int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
1394 bool_Bool stdout_is_tty, stderr_is_tty;
1395 size_t n, i;
1396 va_list ap;
1397 char **l;
1398 int r;
1399
1400 assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("path"), "../src/basic/process-util.c", 1400
, __PRETTY_FUNCTION__); } while (0)
;
1401
1402 /* Spawns a temporary TTY agent, making sure it goes away when we go away */
1403
1404 r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
1405 if (r < 0)
1406 return r;
1407 if (r > 0)
1408 return 0;
1409
1410 /* In the child: */
1411
1412 stdout_is_tty = isatty(STDOUT_FILENO1);
1413 stderr_is_tty = isatty(STDERR_FILENO2);
1414
1415 if (!stdout_is_tty || !stderr_is_tty) {
1416 int fd;
1417
1418 /* Detach from stdout/stderr. and reopen
1419 * /dev/tty for them. This is important to
1420 * ensure that when systemctl is started via
1421 * popen() or a similar call that expects to
1422 * read EOF we actually do generate EOF and
1423 * not delay this indefinitely by because we
1424 * keep an unused copy of stdin around. */
1425 fd = open("/dev/tty", O_WRONLY01);
1426 if (fd < 0) {
1427 log_error_errno(errno, "Failed to open /dev/tty: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1427, __func__
, "Failed to open /dev/tty: %m") : -abs(_e); })
;
1428 _exit(EXIT_FAILURE1);
1429 }
1430
1431 if (!stdout_is_tty && dup2(fd, STDOUT_FILENO1) < 0) {
1432 log_error_errno(errno, "Failed to dup2 /dev/tty: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1432, __func__
, "Failed to dup2 /dev/tty: %m") : -abs(_e); })
;
1433 _exit(EXIT_FAILURE1);
1434 }
1435
1436 if (!stderr_is_tty && dup2(fd, STDERR_FILENO2) < 0) {
1437 log_error_errno(errno, "Failed to dup2 /dev/tty: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/basic/process-util.c", 1437, __func__
, "Failed to dup2 /dev/tty: %m") : -abs(_e); })
;
1438 _exit(EXIT_FAILURE1);
1439 }
1440
1441 safe_close_above_stdio(fd);
1442 }
1443
1444 /* Count arguments */
1445 va_start(ap, path)__builtin_va_start(ap, path);
1446 for (n = 0; va_arg(ap, char*)__builtin_va_arg(ap, char*); n++)
1447 ;
1448 va_end(ap)__builtin_va_end(ap);
1449
1450 /* Allocate strv */
1451 l = newa(char*, n + 1)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(char*), n + 1))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(char*), n + 1)"), "../src/basic/process-util.c"
, 1451, __PRETTY_FUNCTION__); } while (0); (char**) __builtin_alloca
(sizeof(char*)*(n + 1)); })
;
1452
1453 /* Fill in arguments */
1454 va_start(ap, path)__builtin_va_start(ap, path);
1455 for (i = 0; i <= n; i++)
1456 l[i] = va_arg(ap, char*)__builtin_va_arg(ap, char*);
1457 va_end(ap)__builtin_va_end(ap);
1458
1459 execv(path, l);
1460 _exit(EXIT_FAILURE1);
1461}
1462
1463int set_oom_score_adjust(int value) {
1464 char t[DECIMAL_STR_MAX(int)(2+(sizeof(int) <= 1 ? 3 : sizeof(int) <= 2 ? 5 : sizeof
(int) <= 4 ? 10 : sizeof(int) <= 8 ? 20 : sizeof(int[-2
*(sizeof(int) > 8)])))
];
1465
1466 sprintf(t, "%i", value);
1467
1468 return write_string_file("/proc/self/oom_score_adj", t,
1469 WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
1470}
1471
1472static const char *const ioprio_class_table[] = {
1473 [IOPRIO_CLASS_NONE] = "none",
1474 [IOPRIO_CLASS_RT] = "realtime",
1475 [IOPRIO_CLASS_BE] = "best-effort",
1476 [IOPRIO_CLASS_IDLE] = "idle",
1477};
1478
1479DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES)int ioprio_class_to_string_alloc(int i, char **str) { char *s
; if (i < 0 || i > 8) return -34; if (i < (int) __extension__
(__builtin_choose_expr( !__builtin_types_compatible_p(typeof
(ioprio_class_table), typeof(&*(ioprio_class_table))), sizeof
(ioprio_class_table)/sizeof((ioprio_class_table)[0]), ((void)
0)))) { s = strdup(ioprio_class_table[i]); if (!s) return -12
; } else { if (asprintf(&s, "%i", i) < 0) return -12; }
*str = s; return 0; } int ioprio_class_from_string(const char
*s) { int i; unsigned u = 0; if (!s) return (int) -1; for (i
= 0; i < (int) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(ioprio_class_table), typeof(&*(ioprio_class_table
))), sizeof(ioprio_class_table)/sizeof((ioprio_class_table)[0
]), ((void)0))); i++) if (streq_ptr(ioprio_class_table[i], s)
) return i; if (safe_atou(s, &u) >= 0 && u <=
8) return (int) u; return (int) -1; }
;
1480
1481static const char *const sigchld_code_table[] = {
1482 [CLD_EXITEDCLD_EXITED] = "exited",
1483 [CLD_KILLEDCLD_KILLED] = "killed",
1484 [CLD_DUMPEDCLD_DUMPED] = "dumped",
1485 [CLD_TRAPPEDCLD_TRAPPED] = "trapped",
1486 [CLD_STOPPEDCLD_STOPPED] = "stopped",
1487 [CLD_CONTINUEDCLD_CONTINUED] = "continued",
1488};
1489
1490DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int)const char *sigchld_code_to_string(int i) { if (i < 0 || i
>= (int) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(sigchld_code_table), typeof(&*(sigchld_code_table
))), sizeof(sigchld_code_table)/sizeof((sigchld_code_table)[0
]), ((void)0)))) return ((void*)0); return sigchld_code_table
[i]; } int sigchld_code_from_string(const char *s) { return (
int) string_table_lookup(sigchld_code_table, __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(sigchld_code_table), typeof
(&*(sigchld_code_table))), sizeof(sigchld_code_table)/sizeof
((sigchld_code_table)[0]), ((void)0))), s); }
;
1491
1492static const char* const sched_policy_table[] = {
1493 [SCHED_OTHER0] = "other",
1494 [SCHED_BATCH3] = "batch",
1495 [SCHED_IDLE5] = "idle",
1496 [SCHED_FIFO1] = "fifo",
1497 [SCHED_RR2] = "rr",
1498};
1499
1500DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX)int sched_policy_to_string_alloc(int i, char **str) { char *s
; if (i < 0 || i > 2147483647) return -34; if (i < (
int) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(sched_policy_table), typeof(&*(sched_policy_table
))), sizeof(sched_policy_table)/sizeof((sched_policy_table)[0
]), ((void)0)))) { s = strdup(sched_policy_table[i]); if (!s)
return -12; } else { if (asprintf(&s, "%i", i) < 0) return
-12; } *str = s; return 0; } int sched_policy_from_string(const
char *s) { int i; unsigned u = 0; if (!s) return (int) -1; for
(i = 0; i < (int) __extension__ (__builtin_choose_expr( !
__builtin_types_compatible_p(typeof(sched_policy_table), typeof
(&*(sched_policy_table))), sizeof(sched_policy_table)/sizeof
((sched_policy_table)[0]), ((void)0))); i++) if (streq_ptr(sched_policy_table
[i], s)) return i; if (safe_atou(s, &u) >= 0 &&
u <= 2147483647) return (int) u; return (int) -1; }
;

../src/basic/alloc-util.h

1/* SPDX-License-Identifier: LGPL-2.1+ */
2#pragma once
3
4#include <alloca.h>
5#include <stddef.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "macro.h"
10
11#define new(t, n)((t*) malloc_multiply(sizeof(t), (n))) ((t*) malloc_multiply(sizeof(t), (n)))
12
13#define new0(t, n)((t*) calloc((n), sizeof(t))) ((t*) calloc((n), sizeof(t)))
14
15#define newa(t, n)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 15, __PRETTY_FUNCTION__); } while
(0); (t*) __builtin_alloca (sizeof(t)*(n)); })
\
16 ({ \
17 assert(!size_multiply_overflow(sizeof(t), n))do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 17, __PRETTY_FUNCTION__); } while
(0)
; \
18 (t*) alloca(sizeof(t)*(n))__builtin_alloca (sizeof(t)*(n)); \
19 })
20
21#define newa0(t, n)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 21, __PRETTY_FUNCTION__); } while
(0); (t*) ({ char *_new_; size_t _len_ = sizeof(t)*(n); _new_
= __builtin_alloca (_len_); (void *) memset(_new_, 0, _len_)
; }); })
\
22 ({ \
23 assert(!size_multiply_overflow(sizeof(t), n))do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 23, __PRETTY_FUNCTION__); } while
(0)
; \
24 (t*) alloca0(sizeof(t)*(n))({ char *_new_; size_t _len_ = sizeof(t)*(n); _new_ = __builtin_alloca
(_len_); (void *) memset(_new_, 0, _len_); })
; \
25 })
26
27#define newdup(t, p, n)((t*) memdup_multiply(p, sizeof(t), (n))) ((t*) memdup_multiply(p, sizeof(t), (n)))
28
29#define newdup_suffix0(t, p, n)((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
30
31#define malloc0(n)(calloc(1, (n))) (calloc(1, (n)))
32
33static inline void *mfree(void *memory) {
34 free(memory);
35 return NULL((void*)0);
36}
37
38#define free_and_replace(a, b)({ free(a); (a) = (b); (b) = ((void*)0); 0; }) \
39 ({ \
40 free(a); \
41 (a) = (b); \
42 (b) = NULL((void*)0); \
43 0; \
44 })
45
46void* memdup(const void *p, size_t l) _alloc_(2);
47void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
48
49static inline void freep(void *p) {
50 free(*(void**) p);
51}
52
53#define _cleanup_free___attribute__((cleanup(freep))) _cleanup_(freep)__attribute__((cleanup(freep)))
54
55static inline bool_Bool size_multiply_overflow(size_t size, size_t need) {
56 return _unlikely_(need != 0 && size > (SIZE_MAX / need))(__builtin_expect(!!(need != 0 && size > ((18446744073709551615UL
) / need)),0))
;
57}
58
59_malloc___attribute__ ((malloc)) _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
60 if (size_multiply_overflow(size, need))
29
Taking false branch
61 return NULL((void*)0);
62
63 return malloc(size * need);
30
Memory is allocated
64}
65
66#if !HAVE_REALLOCARRAY1
67_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) {
68 if (size_multiply_overflow(size, need))
69 return NULL((void*)0);
70
71 return realloc(p, size * need);
72}
73#endif
74
75_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
76 if (size_multiply_overflow(size, need))
77 return NULL((void*)0);
78
79 return memdup(p, size * need);
80}
81
82_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
83 if (size_multiply_overflow(size, need))
84 return NULL((void*)0);
85
86 return memdup_suffix0(p, size * need);
87}
88
89void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
90void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
91
92#define GREEDY_REALLOC(array, allocated, need)greedy_realloc((void**) &(array), &(allocated), (need
), sizeof((array)[0]))
\
93 greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
94
95#define GREEDY_REALLOC0(array, allocated, need)greedy_realloc0((void**) &(array), &(allocated), (need
), sizeof((array)[0]))
\
96 greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
97
98#define alloca0(n)({ char *_new_; size_t _len_ = n; _new_ = __builtin_alloca (_len_
); (void *) memset(_new_, 0, _len_); })
\
99 ({ \
100 char *_new_; \
101 size_t _len_ = n; \
102 _new_ = alloca(_len_)__builtin_alloca (_len_); \
103 (void *) memset(_new_, 0, _len_); \
104 })
105
106/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
107#define alloca_align(size, align)({ void *_ptr_; size_t _mask_ = (align) - 1; _ptr_ = __builtin_alloca
((size) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) &
~_mask_); })
\
108 ({ \
109 void *_ptr_; \
110 size_t _mask_ = (align) - 1; \
111 _ptr_ = alloca((size) + _mask_)__builtin_alloca ((size) + _mask_); \
112 (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
113 })
114
115#define alloca0_align(size, align)({ void *_new_; size_t _size_ = (size); _new_ = ({ void *_ptr_
; size_t _mask_ = ((align)) - 1; _ptr_ = __builtin_alloca ((_size_
) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_
); }); (void*)memset(_new_, 0, _size_); })
\
116 ({ \
117 void *_new_; \
118 size_t _size_ = (size); \
119 _new_ = alloca_align(_size_, (align))({ void *_ptr_; size_t _mask_ = ((align)) - 1; _ptr_ = __builtin_alloca
((_size_) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) &
~_mask_); })
; \
120 (void*)memset(_new_, 0, _size_); \
121 })
122
123/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to
124 * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
125#define TAKE_PTR(ptr)({ typeof(ptr) _ptr_ = (ptr); (ptr) = ((void*)0); _ptr_; }) \
126 ({ \
127 typeof(ptr) _ptr_ = (ptr); \
128 (ptr) = NULL((void*)0); \
129 _ptr_; \
130 })