]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-process-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-process-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6 Copyright 2013 Thomas H.P. Andersen
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sched.h>
23 #include <sys/mount.h>
24 #include <sys/personality.h>
25 #include <sys/prctl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #if HAVE_VALGRIND_VALGRIND_H
31 #include <valgrind/valgrind.h>
32 #endif
33
34 #include "alloc-util.h"
35 #include "architecture.h"
36 #include "fd-util.h"
37 #include "log.h"
38 #include "macro.h"
39 #include "parse-util.h"
40 #include "process-util.h"
41 #include "stdio-util.h"
42 #include "string-util.h"
43 #include "terminal-util.h"
44 #include "test-helper.h"
45 #include "util.h"
46 #include "virt.h"
47
48 static void test_get_process_comm(pid_t pid) {
49 struct stat st;
50 _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
51 _cleanup_free_ char *env = NULL;
52 char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
53 pid_t e;
54 uid_t u;
55 gid_t g;
56 dev_t h;
57 int r;
58
59 xsprintf(path, "/proc/"PID_FMT"/comm", pid);
60
61 if (stat(path, &st) == 0) {
62 assert_se(get_process_comm(pid, &a) >= 0);
63 log_info("PID"PID_FMT" comm: '%s'", pid, a);
64 } else
65 log_warning("%s not exist.", path);
66
67 assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
68 log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
69
70 assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
71 log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
72
73 free(d);
74 assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
75 log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
76
77 assert_se(get_process_ppid(pid, &e) >= 0);
78 log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
79 assert_se(pid == 1 ? e == 0 : e > 0);
80
81 assert_se(is_kernel_thread(pid) == 0 || pid != 1);
82
83 r = get_process_exe(pid, &f);
84 assert_se(r >= 0 || r == -EACCES);
85 log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
86
87 assert_se(get_process_uid(pid, &u) == 0);
88 log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
89 assert_se(u == 0 || pid != 1);
90
91 assert_se(get_process_gid(pid, &g) == 0);
92 log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
93 assert_se(g == 0 || pid != 1);
94
95 r = get_process_environ(pid, &env);
96 assert_se(r >= 0 || r == -EACCES);
97 log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
98
99 if (!detect_container())
100 assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
101
102 getenv_for_pid(pid, "PATH", &i);
103 log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
104 }
105
106 static void test_pid_is_unwaited(void) {
107 pid_t pid;
108
109 pid = fork();
110 assert_se(pid >= 0);
111 if (pid == 0) {
112 _exit(EXIT_SUCCESS);
113 } else {
114 int status;
115
116 waitpid(pid, &status, 0);
117 assert_se(!pid_is_unwaited(pid));
118 }
119 assert_se(pid_is_unwaited(getpid_cached()));
120 assert_se(!pid_is_unwaited(-1));
121 }
122
123 static void test_pid_is_alive(void) {
124 pid_t pid;
125
126 pid = fork();
127 assert_se(pid >= 0);
128 if (pid == 0) {
129 _exit(EXIT_SUCCESS);
130 } else {
131 int status;
132
133 waitpid(pid, &status, 0);
134 assert_se(!pid_is_alive(pid));
135 }
136 assert_se(pid_is_alive(getpid_cached()));
137 assert_se(!pid_is_alive(-1));
138 }
139
140 static void test_personality(void) {
141
142 assert_se(personality_to_string(PER_LINUX));
143 assert_se(!personality_to_string(PERSONALITY_INVALID));
144
145 assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
146
147 assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
148 assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
149
150 #ifdef __x86_64__
151 assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
152 assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
153
154 assert_se(personality_from_string("x86-64") == PER_LINUX);
155 assert_se(personality_from_string("x86") == PER_LINUX32);
156 assert_se(personality_from_string("ia64") == PERSONALITY_INVALID);
157 assert_se(personality_from_string(NULL) == PERSONALITY_INVALID);
158
159 assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32);
160 #endif
161 }
162
163 static void test_get_process_cmdline_harder(void) {
164 char path[] = "/tmp/test-cmdlineXXXXXX";
165 _cleanup_close_ int fd = -1;
166 _cleanup_free_ char *line = NULL;
167 pid_t pid;
168
169 if (geteuid() != 0)
170 return;
171
172 #if HAVE_VALGRIND_VALGRIND_H
173 /* valgrind patches open(/proc//cmdline)
174 * so, test_get_process_cmdline_harder fails always
175 * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
176 if (RUNNING_ON_VALGRIND)
177 return;
178 #endif
179
180 pid = fork();
181 if (pid > 0) {
182 siginfo_t si;
183
184 (void) wait_for_terminate(pid, &si);
185
186 assert_se(si.si_code == CLD_EXITED);
187 assert_se(si.si_status == 0);
188
189 return;
190 }
191
192 assert_se(pid == 0);
193 assert_se(unshare(CLONE_NEWNS) >= 0);
194
195 fd = mkostemp(path, O_CLOEXEC);
196 assert_se(fd >= 0);
197
198 if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
199 /* This happens under selinux… Abort the test in this case. */
200 log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
201 assert(errno == EACCES);
202 return;
203 }
204
205 assert_se(unlink(path) >= 0);
206
207 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
208
209 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
210
211 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
212 assert_se(streq(line, "[testa]"));
213 line = mfree(line);
214
215 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
216 assert_se(streq(line, ""));
217 line = mfree(line);
218
219 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
220 assert_se(streq(line, "["));
221 line = mfree(line);
222
223 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
224 assert_se(streq(line, "[."));
225 line = mfree(line);
226
227 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
228 assert_se(streq(line, "[.."));
229 line = mfree(line);
230
231 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
232 assert_se(streq(line, "[..."));
233 line = mfree(line);
234
235 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
236 assert_se(streq(line, "[...]"));
237 line = mfree(line);
238
239 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
240 assert_se(streq(line, "[t...]"));
241 line = mfree(line);
242
243 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
244 assert_se(streq(line, "[testa]"));
245 line = mfree(line);
246
247 assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
248
249 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
250
251 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
252 assert_se(streq(line, "[testa]"));
253 line = mfree(line);
254
255 assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
256
257 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
258 assert_se(streq(line, "foo bar"));
259 line = mfree(line);
260
261 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
262 assert_se(streq(line, "foo bar"));
263 line = mfree(line);
264
265 assert_se(write(fd, "quux", 4) == 4);
266 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
267 assert_se(streq(line, "foo bar quux"));
268 line = mfree(line);
269
270 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
271 assert_se(streq(line, "foo bar quux"));
272 line = mfree(line);
273
274 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
275 assert_se(streq(line, ""));
276 line = mfree(line);
277
278 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
279 assert_se(streq(line, "."));
280 line = mfree(line);
281
282 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
283 assert_se(streq(line, ".."));
284 line = mfree(line);
285
286 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
287 assert_se(streq(line, "..."));
288 line = mfree(line);
289
290 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
291 assert_se(streq(line, "f..."));
292 line = mfree(line);
293
294 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
295 assert_se(streq(line, "fo..."));
296 line = mfree(line);
297
298 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
299 assert_se(streq(line, "foo..."));
300 line = mfree(line);
301
302 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
303 assert_se(streq(line, "foo..."));
304 line = mfree(line);
305
306 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
307 assert_se(streq(line, "foo b..."));
308 line = mfree(line);
309
310 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
311 assert_se(streq(line, "foo ba..."));
312 line = mfree(line);
313
314 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
315 assert_se(streq(line, "foo bar..."));
316 line = mfree(line);
317
318 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
319 assert_se(streq(line, "foo bar..."));
320 line = mfree(line);
321
322 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
323 assert_se(streq(line, "foo bar quux"));
324 line = mfree(line);
325
326 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
327 assert_se(streq(line, "foo bar quux"));
328 line = mfree(line);
329
330 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
331 assert_se(streq(line, "foo bar quux"));
332 line = mfree(line);
333
334 assert_se(ftruncate(fd, 0) >= 0);
335 assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
336
337 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
338
339 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
340 assert_se(streq(line, "[aaaa bbbb cccc]"));
341 line = mfree(line);
342
343 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
344 assert_se(streq(line, "[aaaa...]"));
345 line = mfree(line);
346
347 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
348 assert_se(streq(line, "[aaaa...]"));
349 line = mfree(line);
350
351 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
352 assert_se(streq(line, "[aaaa b...]"));
353 line = mfree(line);
354
355 safe_close(fd);
356 _exit(0);
357 }
358
359 static void test_rename_process_now(const char *p, int ret) {
360 _cleanup_free_ char *comm = NULL, *cmdline = NULL;
361 int r;
362
363 r = rename_process(p);
364 assert_se(r == ret ||
365 (ret == 0 && r >= 0) ||
366 (ret > 0 && r > 0));
367
368 if (r < 0)
369 return;
370
371 #if HAVE_VALGRIND_VALGRIND_H
372 /* see above, valgrind is weird, we can't verify what we are doing here */
373 if (RUNNING_ON_VALGRIND)
374 return;
375 #endif
376
377 assert_se(get_process_comm(0, &comm) >= 0);
378 log_info("comm = <%s>", comm);
379 assert_se(strneq(comm, p, 15));
380
381 assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
382 /* we cannot expect cmdline to be renamed properly without privileges */
383 if (geteuid() == 0) {
384 log_info("cmdline = <%s>", cmdline);
385 assert_se(strneq(p, cmdline, strlen("test-process-util")));
386 assert_se(startswith(p, cmdline));
387 } else
388 log_info("cmdline = <%s> (not verified)", cmdline);
389 }
390
391 static void test_rename_process_one(const char *p, int ret) {
392 siginfo_t si;
393 pid_t pid;
394
395 pid = fork();
396 assert_se(pid >= 0);
397
398 if (pid == 0) {
399 /* child */
400 test_rename_process_now(p, ret);
401 _exit(EXIT_SUCCESS);
402 }
403
404 assert_se(wait_for_terminate(pid, &si) >= 0);
405 assert_se(si.si_code == CLD_EXITED);
406 assert_se(si.si_status == EXIT_SUCCESS);
407 }
408
409 static void test_rename_process_multi(void) {
410 pid_t pid;
411
412 pid = fork();
413 assert_se(pid >= 0);
414
415 if (pid > 0) {
416 siginfo_t si;
417
418 assert_se(wait_for_terminate(pid, &si) >= 0);
419 assert_se(si.si_code == CLD_EXITED);
420 assert_se(si.si_status == EXIT_SUCCESS);
421
422 return;
423 }
424
425 /* child */
426 test_rename_process_now("one", 1);
427 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
428 setresuid(99, 99, 99);
429 test_rename_process_now("time!", 0);
430 test_rename_process_now("0", 1); /* shorter than "one", should fit */
431 test_rename_process_one("", -EINVAL);
432 test_rename_process_one(NULL, -EINVAL);
433 _exit(EXIT_SUCCESS);
434 }
435
436 static void test_rename_process(void) {
437 test_rename_process_one(NULL, -EINVAL);
438 test_rename_process_one("", -EINVAL);
439 test_rename_process_one("foo", 1); /* should always fit */
440 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
441 test_rename_process_one("1234567", 1); /* should always fit */
442 test_rename_process_multi(); /* multiple invocations and dropped privileges */
443 }
444
445 static void test_getpid_cached(void) {
446 siginfo_t si;
447 pid_t a, b, c, d, e, f, child;
448
449 a = raw_getpid();
450 b = getpid_cached();
451 c = getpid();
452
453 assert_se(a == b && a == c);
454
455 child = fork();
456 assert_se(child >= 0);
457
458 if (child == 0) {
459 /* In child */
460 a = raw_getpid();
461 b = getpid_cached();
462 c = getpid();
463
464 assert_se(a == b && a == c);
465 _exit(0);
466 }
467
468 d = raw_getpid();
469 e = getpid_cached();
470 f = getpid();
471
472 assert_se(a == d && a == e && a == f);
473
474 assert_se(wait_for_terminate(child, &si) >= 0);
475 assert_se(si.si_status == 0);
476 assert_se(si.si_code == CLD_EXITED);
477 }
478
479 #define MEASURE_ITERATIONS (10000000LLU)
480
481 static void test_getpid_measure(void) {
482 unsigned long long i;
483 usec_t t, q;
484
485 t = now(CLOCK_MONOTONIC);
486 for (i = 0; i < MEASURE_ITERATIONS; i++)
487 (void) getpid();
488 q = now(CLOCK_MONOTONIC) - t;
489
490 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
491
492 t = now(CLOCK_MONOTONIC);
493 for (i = 0; i < MEASURE_ITERATIONS; i++)
494 (void) getpid_cached();
495 q = now(CLOCK_MONOTONIC) - t;
496
497 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
498 }
499
500 int main(int argc, char *argv[]) {
501
502 log_set_max_level(LOG_DEBUG);
503 log_parse_environment();
504 log_open();
505
506 saved_argc = argc;
507 saved_argv = argv;
508
509 if (argc > 1) {
510 pid_t pid = 0;
511
512 (void) parse_pid(argv[1], &pid);
513 test_get_process_comm(pid);
514 } else {
515 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
516 test_get_process_comm(getpid());
517 }
518
519 test_pid_is_unwaited();
520 test_pid_is_alive();
521 test_personality();
522 test_get_process_cmdline_harder();
523 test_rename_process();
524 test_getpid_cached();
525 test_getpid_measure();
526
527 return 0;
528 }