]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-process-util.c
f540eeb3b0991d0d9bbd81282bb2f2f6a4266be0
[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 "signal-util.h"
42 #include "stdio-util.h"
43 #include "string-util.h"
44 #include "terminal-util.h"
45 #include "test-helper.h"
46 #include "util.h"
47 #include "virt.h"
48
49 static void test_get_process_comm(pid_t pid) {
50 struct stat st;
51 _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
52 _cleanup_free_ char *env = NULL;
53 char path[STRLEN("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
54 pid_t e;
55 uid_t u;
56 gid_t g;
57 dev_t h;
58 int r;
59
60 xsprintf(path, "/proc/"PID_FMT"/comm", pid);
61
62 if (stat(path, &st) == 0) {
63 assert_se(get_process_comm(pid, &a) >= 0);
64 log_info("PID"PID_FMT" comm: '%s'", pid, a);
65 } else
66 log_warning("%s not exist.", path);
67
68 assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
69 log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
70
71 assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
72 log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
73
74 free(d);
75 assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
76 log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
77
78 assert_se(get_process_ppid(pid, &e) >= 0);
79 log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
80 assert_se(pid == 1 ? e == 0 : e > 0);
81
82 assert_se(is_kernel_thread(pid) == 0 || pid != 1);
83
84 r = get_process_exe(pid, &f);
85 assert_se(r >= 0 || r == -EACCES);
86 log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
87
88 assert_se(get_process_uid(pid, &u) == 0);
89 log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
90 assert_se(u == 0 || pid != 1);
91
92 assert_se(get_process_gid(pid, &g) == 0);
93 log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
94 assert_se(g == 0 || pid != 1);
95
96 r = get_process_environ(pid, &env);
97 assert_se(r >= 0 || r == -EACCES);
98 log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
99
100 if (!detect_container())
101 assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
102
103 (void) getenv_for_pid(pid, "PATH", &i);
104 log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
105 }
106
107 static void test_pid_is_unwaited(void) {
108 pid_t pid;
109
110 pid = fork();
111 assert_se(pid >= 0);
112 if (pid == 0) {
113 _exit(EXIT_SUCCESS);
114 } else {
115 int status;
116
117 waitpid(pid, &status, 0);
118 assert_se(!pid_is_unwaited(pid));
119 }
120 assert_se(pid_is_unwaited(getpid_cached()));
121 assert_se(!pid_is_unwaited(-1));
122 }
123
124 static void test_pid_is_alive(void) {
125 pid_t pid;
126
127 pid = fork();
128 assert_se(pid >= 0);
129 if (pid == 0) {
130 _exit(EXIT_SUCCESS);
131 } else {
132 int status;
133
134 waitpid(pid, &status, 0);
135 assert_se(!pid_is_alive(pid));
136 }
137 assert_se(pid_is_alive(getpid_cached()));
138 assert_se(!pid_is_alive(-1));
139 }
140
141 static void test_personality(void) {
142
143 assert_se(personality_to_string(PER_LINUX));
144 assert_se(!personality_to_string(PERSONALITY_INVALID));
145
146 assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
147
148 assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
149 assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
150
151 #ifdef __x86_64__
152 assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
153 assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
154
155 assert_se(personality_from_string("x86-64") == PER_LINUX);
156 assert_se(personality_from_string("x86") == PER_LINUX32);
157 assert_se(personality_from_string("ia64") == PERSONALITY_INVALID);
158 assert_se(personality_from_string(NULL) == PERSONALITY_INVALID);
159
160 assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32);
161 #endif
162 }
163
164 static void test_get_process_cmdline_harder(void) {
165 char path[] = "/tmp/test-cmdlineXXXXXX";
166 _cleanup_close_ int fd = -1;
167 _cleanup_free_ char *line = NULL;
168 pid_t pid;
169
170 if (geteuid() != 0)
171 return;
172
173 #if HAVE_VALGRIND_VALGRIND_H
174 /* valgrind patches open(/proc//cmdline)
175 * so, test_get_process_cmdline_harder fails always
176 * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
177 if (RUNNING_ON_VALGRIND)
178 return;
179 #endif
180
181 pid = fork();
182 if (pid > 0) {
183 siginfo_t si;
184
185 (void) wait_for_terminate(pid, &si);
186
187 assert_se(si.si_code == CLD_EXITED);
188 assert_se(si.si_status == 0);
189
190 return;
191 }
192
193 assert_se(pid == 0);
194 assert_se(unshare(CLONE_NEWNS) >= 0);
195
196 assert_se(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) >= 0);
197
198 fd = mkostemp(path, O_CLOEXEC);
199 assert_se(fd >= 0);
200
201 if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
202 /* This happens under selinux… Abort the test in this case. */
203 log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
204 assert(errno == EACCES);
205 return;
206 }
207
208 assert_se(unlink(path) >= 0);
209
210 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
211
212 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
213
214 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
215 assert_se(streq(line, "[testa]"));
216 line = mfree(line);
217
218 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
219 assert_se(streq(line, ""));
220 line = mfree(line);
221
222 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
223 assert_se(streq(line, "["));
224 line = mfree(line);
225
226 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
227 assert_se(streq(line, "[."));
228 line = mfree(line);
229
230 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
231 assert_se(streq(line, "[.."));
232 line = mfree(line);
233
234 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
235 assert_se(streq(line, "[..."));
236 line = mfree(line);
237
238 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
239 assert_se(streq(line, "[...]"));
240 line = mfree(line);
241
242 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
243 assert_se(streq(line, "[t...]"));
244 line = mfree(line);
245
246 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
247 assert_se(streq(line, "[testa]"));
248 line = mfree(line);
249
250 assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
251
252 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
253
254 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
255 assert_se(streq(line, "[testa]"));
256 line = mfree(line);
257
258 assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
259
260 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
261 assert_se(streq(line, "foo bar"));
262 line = mfree(line);
263
264 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
265 assert_se(streq(line, "foo bar"));
266 line = mfree(line);
267
268 assert_se(write(fd, "quux", 4) == 4);
269 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
270 assert_se(streq(line, "foo bar quux"));
271 line = mfree(line);
272
273 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
274 assert_se(streq(line, "foo bar quux"));
275 line = mfree(line);
276
277 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
278 assert_se(streq(line, ""));
279 line = mfree(line);
280
281 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
282 assert_se(streq(line, "."));
283 line = mfree(line);
284
285 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
286 assert_se(streq(line, ".."));
287 line = mfree(line);
288
289 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
290 assert_se(streq(line, "..."));
291 line = mfree(line);
292
293 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
294 assert_se(streq(line, "f..."));
295 line = mfree(line);
296
297 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
298 assert_se(streq(line, "fo..."));
299 line = mfree(line);
300
301 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
302 assert_se(streq(line, "foo..."));
303 line = mfree(line);
304
305 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
306 assert_se(streq(line, "foo..."));
307 line = mfree(line);
308
309 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
310 assert_se(streq(line, "foo b..."));
311 line = mfree(line);
312
313 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
314 assert_se(streq(line, "foo ba..."));
315 line = mfree(line);
316
317 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
318 assert_se(streq(line, "foo bar..."));
319 line = mfree(line);
320
321 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
322 assert_se(streq(line, "foo bar..."));
323 line = mfree(line);
324
325 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
326 assert_se(streq(line, "foo bar quux"));
327 line = mfree(line);
328
329 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
330 assert_se(streq(line, "foo bar quux"));
331 line = mfree(line);
332
333 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
334 assert_se(streq(line, "foo bar quux"));
335 line = mfree(line);
336
337 assert_se(ftruncate(fd, 0) >= 0);
338 assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
339
340 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
341
342 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
343 assert_se(streq(line, "[aaaa bbbb cccc]"));
344 line = mfree(line);
345
346 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
347 assert_se(streq(line, "[aaaa...]"));
348 line = mfree(line);
349
350 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
351 assert_se(streq(line, "[aaaa...]"));
352 line = mfree(line);
353
354 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
355 assert_se(streq(line, "[aaaa b...]"));
356 line = mfree(line);
357
358 safe_close(fd);
359 _exit(EXIT_SUCCESS);
360 }
361
362 static void test_rename_process_now(const char *p, int ret) {
363 _cleanup_free_ char *comm = NULL, *cmdline = NULL;
364 int r;
365
366 r = rename_process(p);
367 assert_se(r == ret ||
368 (ret == 0 && r >= 0) ||
369 (ret > 0 && r > 0));
370
371 if (r < 0)
372 return;
373
374 #if HAVE_VALGRIND_VALGRIND_H
375 /* see above, valgrind is weird, we can't verify what we are doing here */
376 if (RUNNING_ON_VALGRIND)
377 return;
378 #endif
379
380 assert_se(get_process_comm(0, &comm) >= 0);
381 log_info("comm = <%s>", comm);
382 assert_se(strneq(comm, p, 15));
383
384 assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
385 /* we cannot expect cmdline to be renamed properly without privileges */
386 if (geteuid() == 0) {
387 log_info("cmdline = <%s>", cmdline);
388 assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
389 assert_se(startswith(p, cmdline));
390 } else
391 log_info("cmdline = <%s> (not verified)", cmdline);
392 }
393
394 static void test_rename_process_one(const char *p, int ret) {
395 siginfo_t si;
396 pid_t pid;
397
398 pid = fork();
399 assert_se(pid >= 0);
400
401 if (pid == 0) {
402 /* child */
403 test_rename_process_now(p, ret);
404 _exit(EXIT_SUCCESS);
405 }
406
407 assert_se(wait_for_terminate(pid, &si) >= 0);
408 assert_se(si.si_code == CLD_EXITED);
409 assert_se(si.si_status == EXIT_SUCCESS);
410 }
411
412 static void test_rename_process_multi(void) {
413 pid_t pid;
414
415 pid = fork();
416 assert_se(pid >= 0);
417
418 if (pid > 0) {
419 siginfo_t si;
420
421 assert_se(wait_for_terminate(pid, &si) >= 0);
422 assert_se(si.si_code == CLD_EXITED);
423 assert_se(si.si_status == EXIT_SUCCESS);
424
425 return;
426 }
427
428 /* child */
429 test_rename_process_now("one", 1);
430 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
431 (void) setresuid(99, 99, 99); /* change uid when running privileged */
432 test_rename_process_now("time!", 0);
433 test_rename_process_now("0", 1); /* shorter than "one", should fit */
434 test_rename_process_one("", -EINVAL);
435 test_rename_process_one(NULL, -EINVAL);
436 _exit(EXIT_SUCCESS);
437 }
438
439 static void test_rename_process(void) {
440 test_rename_process_one(NULL, -EINVAL);
441 test_rename_process_one("", -EINVAL);
442 test_rename_process_one("foo", 1); /* should always fit */
443 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
444 test_rename_process_one("1234567", 1); /* should always fit */
445 test_rename_process_multi(); /* multiple invocations and dropped privileges */
446 }
447
448 static void test_getpid_cached(void) {
449 siginfo_t si;
450 pid_t a, b, c, d, e, f, child;
451
452 a = raw_getpid();
453 b = getpid_cached();
454 c = getpid();
455
456 assert_se(a == b && a == c);
457
458 child = fork();
459 assert_se(child >= 0);
460
461 if (child == 0) {
462 /* In child */
463 a = raw_getpid();
464 b = getpid_cached();
465 c = getpid();
466
467 assert_se(a == b && a == c);
468 _exit(EXIT_SUCCESS);
469 }
470
471 d = raw_getpid();
472 e = getpid_cached();
473 f = getpid();
474
475 assert_se(a == d && a == e && a == f);
476
477 assert_se(wait_for_terminate(child, &si) >= 0);
478 assert_se(si.si_status == 0);
479 assert_se(si.si_code == CLD_EXITED);
480 }
481
482 #define MEASURE_ITERATIONS (10000000LLU)
483
484 static void test_getpid_measure(void) {
485 unsigned long long i;
486 usec_t t, q;
487
488 t = now(CLOCK_MONOTONIC);
489 for (i = 0; i < MEASURE_ITERATIONS; i++)
490 (void) getpid();
491 q = now(CLOCK_MONOTONIC) - t;
492
493 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
494
495 t = now(CLOCK_MONOTONIC);
496 for (i = 0; i < MEASURE_ITERATIONS; i++)
497 (void) getpid_cached();
498 q = now(CLOCK_MONOTONIC) - t;
499
500 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
501 }
502
503 static void test_safe_fork(void) {
504 siginfo_t status;
505 pid_t pid;
506 int r;
507
508 BLOCK_SIGNALS(SIGCHLD);
509
510 r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
511 assert_se(r >= 0);
512
513 if (r == 0) {
514 /* child */
515 usleep(100 * USEC_PER_MSEC);
516
517 _exit(88);
518 }
519
520 assert_se(wait_for_terminate(pid, &status) >= 0);
521 assert_se(status.si_code == CLD_EXITED);
522 assert_se(status.si_status == 88);
523 }
524
525 static void test_pid_to_ptr(void) {
526
527 assert_se(PTR_TO_PID(NULL) == 0);
528 assert_se(PID_TO_PTR(0) == NULL);
529
530 assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
531 assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
532 assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1);
533 assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2);
534
535 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
536 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
537
538 #if SIZEOF_PID_T >= 4
539 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
540 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
541 #endif
542 }
543
544 static void test_ioprio_class_from_to_string_one(const char *val, int expected) {
545 assert_se(ioprio_class_from_string(val) == expected);
546 if (expected >= 0) {
547 _cleanup_free_ char *s = NULL;
548 unsigned ret;
549
550 assert_se(ioprio_class_to_string_alloc(expected, &s) == 0);
551 /* We sometimes get a class number and sometimes a number back */
552 assert_se(streq(s, val) ||
553 safe_atou(val, &ret) == 0);
554 }
555 }
556
557 static void test_ioprio_class_from_to_string(void) {
558 test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE);
559 test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT);
560 test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE);
561 test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE);
562 test_ioprio_class_from_to_string_one("0", 0);
563 test_ioprio_class_from_to_string_one("1", 1);
564 test_ioprio_class_from_to_string_one("7", 7);
565 test_ioprio_class_from_to_string_one("8", 8);
566 test_ioprio_class_from_to_string_one("9", -1);
567 test_ioprio_class_from_to_string_one("-1", -1);
568 }
569
570 int main(int argc, char *argv[]) {
571 log_set_max_level(LOG_DEBUG);
572 log_parse_environment();
573 log_open();
574
575 saved_argc = argc;
576 saved_argv = argv;
577
578 if (argc > 1) {
579 pid_t pid = 0;
580
581 (void) parse_pid(argv[1], &pid);
582 test_get_process_comm(pid);
583 } else {
584 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
585 test_get_process_comm(getpid());
586 }
587
588 test_pid_is_unwaited();
589 test_pid_is_alive();
590 test_personality();
591 test_get_process_cmdline_harder();
592 test_rename_process();
593 test_getpid_cached();
594 test_getpid_measure();
595 test_safe_fork();
596 test_pid_to_ptr();
597 test_ioprio_class_from_to_string();
598
599 return 0;
600 }