]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-process-util.c
Merge pull request #7675 from shawnl/unaligned
[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 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 fd = mkostemp(path, O_CLOEXEC);
197 assert_se(fd >= 0);
198
199 if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
200 /* This happens under selinux… Abort the test in this case. */
201 log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
202 assert(errno == EACCES);
203 return;
204 }
205
206 assert_se(unlink(path) >= 0);
207
208 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
209
210 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
211
212 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
213 assert_se(streq(line, "[testa]"));
214 line = mfree(line);
215
216 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
217 assert_se(streq(line, ""));
218 line = mfree(line);
219
220 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
221 assert_se(streq(line, "["));
222 line = mfree(line);
223
224 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
225 assert_se(streq(line, "[."));
226 line = mfree(line);
227
228 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
229 assert_se(streq(line, "[.."));
230 line = mfree(line);
231
232 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
233 assert_se(streq(line, "[..."));
234 line = mfree(line);
235
236 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
237 assert_se(streq(line, "[...]"));
238 line = mfree(line);
239
240 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
241 assert_se(streq(line, "[t...]"));
242 line = mfree(line);
243
244 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
245 assert_se(streq(line, "[testa]"));
246 line = mfree(line);
247
248 assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
249
250 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
251
252 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
253 assert_se(streq(line, "[testa]"));
254 line = mfree(line);
255
256 assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
257
258 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
259 assert_se(streq(line, "foo bar"));
260 line = mfree(line);
261
262 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
263 assert_se(streq(line, "foo bar"));
264 line = mfree(line);
265
266 assert_se(write(fd, "quux", 4) == 4);
267 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
268 assert_se(streq(line, "foo bar quux"));
269 line = mfree(line);
270
271 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
272 assert_se(streq(line, "foo bar quux"));
273 line = mfree(line);
274
275 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
276 assert_se(streq(line, ""));
277 line = mfree(line);
278
279 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
280 assert_se(streq(line, "."));
281 line = mfree(line);
282
283 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
284 assert_se(streq(line, ".."));
285 line = mfree(line);
286
287 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
288 assert_se(streq(line, "..."));
289 line = mfree(line);
290
291 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
292 assert_se(streq(line, "f..."));
293 line = mfree(line);
294
295 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
296 assert_se(streq(line, "fo..."));
297 line = mfree(line);
298
299 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
300 assert_se(streq(line, "foo..."));
301 line = mfree(line);
302
303 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
304 assert_se(streq(line, "foo..."));
305 line = mfree(line);
306
307 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
308 assert_se(streq(line, "foo b..."));
309 line = mfree(line);
310
311 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
312 assert_se(streq(line, "foo ba..."));
313 line = mfree(line);
314
315 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
316 assert_se(streq(line, "foo bar..."));
317 line = mfree(line);
318
319 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
320 assert_se(streq(line, "foo bar..."));
321 line = mfree(line);
322
323 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
324 assert_se(streq(line, "foo bar quux"));
325 line = mfree(line);
326
327 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
328 assert_se(streq(line, "foo bar quux"));
329 line = mfree(line);
330
331 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
332 assert_se(streq(line, "foo bar quux"));
333 line = mfree(line);
334
335 assert_se(ftruncate(fd, 0) >= 0);
336 assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
337
338 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
339
340 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
341 assert_se(streq(line, "[aaaa bbbb cccc]"));
342 line = mfree(line);
343
344 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
345 assert_se(streq(line, "[aaaa...]"));
346 line = mfree(line);
347
348 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
349 assert_se(streq(line, "[aaaa...]"));
350 line = mfree(line);
351
352 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
353 assert_se(streq(line, "[aaaa b...]"));
354 line = mfree(line);
355
356 safe_close(fd);
357 _exit(EXIT_SUCCESS);
358 }
359
360 static void test_rename_process_now(const char *p, int ret) {
361 _cleanup_free_ char *comm = NULL, *cmdline = NULL;
362 int r;
363
364 r = rename_process(p);
365 assert_se(r == ret ||
366 (ret == 0 && r >= 0) ||
367 (ret > 0 && r > 0));
368
369 if (r < 0)
370 return;
371
372 #if HAVE_VALGRIND_VALGRIND_H
373 /* see above, valgrind is weird, we can't verify what we are doing here */
374 if (RUNNING_ON_VALGRIND)
375 return;
376 #endif
377
378 assert_se(get_process_comm(0, &comm) >= 0);
379 log_info("comm = <%s>", comm);
380 assert_se(strneq(comm, p, 15));
381
382 assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
383 /* we cannot expect cmdline to be renamed properly without privileges */
384 if (geteuid() == 0) {
385 log_info("cmdline = <%s>", cmdline);
386 assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
387 assert_se(startswith(p, cmdline));
388 } else
389 log_info("cmdline = <%s> (not verified)", cmdline);
390 }
391
392 static void test_rename_process_one(const char *p, int ret) {
393 siginfo_t si;
394 pid_t pid;
395
396 pid = fork();
397 assert_se(pid >= 0);
398
399 if (pid == 0) {
400 /* child */
401 test_rename_process_now(p, ret);
402 _exit(EXIT_SUCCESS);
403 }
404
405 assert_se(wait_for_terminate(pid, &si) >= 0);
406 assert_se(si.si_code == CLD_EXITED);
407 assert_se(si.si_status == EXIT_SUCCESS);
408 }
409
410 static void test_rename_process_multi(void) {
411 pid_t pid;
412
413 pid = fork();
414 assert_se(pid >= 0);
415
416 if (pid > 0) {
417 siginfo_t si;
418
419 assert_se(wait_for_terminate(pid, &si) >= 0);
420 assert_se(si.si_code == CLD_EXITED);
421 assert_se(si.si_status == EXIT_SUCCESS);
422
423 return;
424 }
425
426 /* child */
427 test_rename_process_now("one", 1);
428 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
429 (void) setresuid(99, 99, 99); /* change uid when running privileged */
430 test_rename_process_now("time!", 0);
431 test_rename_process_now("0", 1); /* shorter than "one", should fit */
432 test_rename_process_one("", -EINVAL);
433 test_rename_process_one(NULL, -EINVAL);
434 _exit(EXIT_SUCCESS);
435 }
436
437 static void test_rename_process(void) {
438 test_rename_process_one(NULL, -EINVAL);
439 test_rename_process_one("", -EINVAL);
440 test_rename_process_one("foo", 1); /* should always fit */
441 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
442 test_rename_process_one("1234567", 1); /* should always fit */
443 test_rename_process_multi(); /* multiple invocations and dropped privileges */
444 }
445
446 static void test_getpid_cached(void) {
447 siginfo_t si;
448 pid_t a, b, c, d, e, f, child;
449
450 a = raw_getpid();
451 b = getpid_cached();
452 c = getpid();
453
454 assert_se(a == b && a == c);
455
456 child = fork();
457 assert_se(child >= 0);
458
459 if (child == 0) {
460 /* In child */
461 a = raw_getpid();
462 b = getpid_cached();
463 c = getpid();
464
465 assert_se(a == b && a == c);
466 _exit(EXIT_SUCCESS);
467 }
468
469 d = raw_getpid();
470 e = getpid_cached();
471 f = getpid();
472
473 assert_se(a == d && a == e && a == f);
474
475 assert_se(wait_for_terminate(child, &si) >= 0);
476 assert_se(si.si_status == 0);
477 assert_se(si.si_code == CLD_EXITED);
478 }
479
480 #define MEASURE_ITERATIONS (10000000LLU)
481
482 static void test_getpid_measure(void) {
483 unsigned long long i;
484 usec_t t, q;
485
486 t = now(CLOCK_MONOTONIC);
487 for (i = 0; i < MEASURE_ITERATIONS; i++)
488 (void) getpid();
489 q = now(CLOCK_MONOTONIC) - t;
490
491 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
492
493 t = now(CLOCK_MONOTONIC);
494 for (i = 0; i < MEASURE_ITERATIONS; i++)
495 (void) getpid_cached();
496 q = now(CLOCK_MONOTONIC) - t;
497
498 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
499 }
500
501 static void test_safe_fork(void) {
502 siginfo_t status;
503 pid_t pid;
504 int r;
505
506 BLOCK_SIGNALS(SIGCHLD);
507
508 r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
509 assert_se(r >= 0);
510
511 if (r == 0) {
512 /* child */
513 usleep(100 * USEC_PER_MSEC);
514
515 _exit(88);
516 }
517
518 assert_se(wait_for_terminate(pid, &status) >= 0);
519 assert_se(status.si_code == CLD_EXITED);
520 assert_se(status.si_status == 88);
521 }
522
523 int main(int argc, char *argv[]) {
524
525 log_set_max_level(LOG_DEBUG);
526 log_parse_environment();
527 log_open();
528
529 saved_argc = argc;
530 saved_argv = argv;
531
532 if (argc > 1) {
533 pid_t pid = 0;
534
535 (void) parse_pid(argv[1], &pid);
536 test_get_process_comm(pid);
537 } else {
538 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
539 test_get_process_comm(getpid());
540 }
541
542 test_pid_is_unwaited();
543 test_pid_is_alive();
544 test_personality();
545 test_get_process_cmdline_harder();
546 test_rename_process();
547 test_getpid_cached();
548 test_getpid_measure();
549 test_safe_fork();
550
551 return 0;
552 }