Bug Summary

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

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)
;
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)
;
80
81 escaped = new(char, TASK_COMM_LEN)((char*) malloc_multiply(sizeof(char), (16)));
82 if (!escaped)
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_; })
;
86
87 r = read_one_line_file(p, &comm);
88 if (r == -ENOENT2)
89 return -ESRCH3;
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)));
15
Calling 'malloc_multiply'
18
Returned allocated memory
153 if (!ans)
19
Assuming 'ans' is non-null
20
Taking false branch
154 return -ENOMEM12;
155
156 k = ans;
157 left = max_length;
158 while ((c = getc(f)) != EOF(-1)) {
21
Assuming the condition is true
22
Loop condition is true. Entering loop body
27
Assuming the condition is false
28
Loop condition is false. Execution continues on line 184
159
160 if (isprint(c)((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int) _ISprint
)
) {
23
Assuming the condition is true
24
Taking true branch
161
162 if (space
24.1
'space' is false
24.1
'space' is false
) {
25
Taking false branch
163 if (left <= 2) {
164 dotdotdot = true1;
165 break;
166 }
167
168 *(k++) = ' ';
169 left--;
170 space = false0;
171 }
172
173 if (left
25.1
'left' is > 1
25.1
'left' is > 1
<= 1) {
26
Taking false branch
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
28.1
'dotdotdot' is false
28.1
'dotdotdot' is false
) {
29
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)) {
30
Taking false 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)
213 return -ENOENT2;
214
215 h = get_process_comm(pid, &t);
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);
31
Attempt to reallocate memory
249 if (!k)
32
Assuming 'k' is null
33
Reallocation failed
34
Taking true branch
250 return -ENOMEM12;
35
Potential leak of memory pointed to by 'k'
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_12 { 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))
16
Taking false branch
61 return NULL((void*)0);
62
63 return malloc(size * need);
17
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 })