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