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