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