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