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