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