]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-process-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-process-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
0b452006
RC
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
69281c49
LP
22#include <sched.h>
23#include <sys/mount.h>
26fbedd7 24#include <sys/personality.h>
69281c49 25#include <sys/prctl.h>
0b452006 26#include <sys/stat.h>
07630cea 27#include <sys/types.h>
0b452006
RC
28#include <sys/wait.h>
29#include <unistd.h>
349cc4a5 30#if HAVE_VALGRIND_VALGRIND_H
b3d69149
EV
31#include <valgrind/valgrind.h>
32#endif
0b452006 33
b5efdb8a 34#include "alloc-util.h"
26fbedd7 35#include "architecture.h"
69281c49 36#include "fd-util.h"
0b452006 37#include "log.h"
0b452006 38#include "macro.h"
18dade5a 39#include "parse-util.h"
07630cea 40#include "process-util.h"
9a140c35 41#include "stdio-util.h"
07630cea 42#include "string-util.h"
288a74cc 43#include "terminal-util.h"
76c19e9f 44#include "test-helper.h"
07630cea
LP
45#include "util.h"
46#include "virt.h"
0b452006 47
9a140c35 48static void test_get_process_comm(pid_t pid) {
0b452006 49 struct stat st;
ba19c6e1 50 _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
0b452006 51 _cleanup_free_ char *env = NULL;
9a140c35 52 char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
0b452006
RC
53 pid_t e;
54 uid_t u;
55 gid_t g;
56 dev_t h;
57 int r;
0b452006 58
9a140c35
ZJS
59 xsprintf(path, "/proc/"PID_FMT"/comm", pid);
60
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);
cfeaa44a 64 } else
9a140c35 65 log_warning("%s not exist.", path);
0b452006 66
9a140c35
ZJS
67 assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
68 log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
0b452006 69
9a140c35 70 assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
69281c49
LP
71 log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
72
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);
0b452006 76
9a140c35
ZJS
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);
0b452006 80
9a140c35 81 assert_se(is_kernel_thread(pid) == 0 || pid != 1);
0b452006 82
9a140c35 83 r = get_process_exe(pid, &f);
0b452006 84 assert_se(r >= 0 || r == -EACCES);
9a140c35 85 log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
0b452006 86
9a140c35
ZJS
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);
0b452006 90
9a140c35
ZJS
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);
0b452006 94
9a140c35 95 r = get_process_environ(pid, &env);
0b452006 96 assert_se(r >= 0 || r == -EACCES);
9a140c35 97 log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
0b452006 98
75f86906 99 if (!detect_container())
9a140c35 100 assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
0b452006 101
9a140c35
ZJS
102 getenv_for_pid(pid, "PATH", &i);
103 log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
0b452006
RC
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 }
df0ff127 119 assert_se(pid_is_unwaited(getpid_cached()));
0b452006
RC
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 }
df0ff127 136 assert_se(pid_is_alive(getpid_cached()));
0b452006
RC
137 assert_se(!pid_is_alive(-1));
138}
139
26fbedd7
LP
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
69281c49
LP
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
349cc4a5 172#if HAVE_VALGRIND_VALGRIND_H
b3d69149
EV
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
69281c49
LP
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);
347ebd02
ZJS
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
69281c49
LP
205 assert_se(unlink(path) >= 0);
206
207 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
208
df0ff127 209 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
69281c49 210
df0ff127 211 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
69281c49
LP
212 assert_se(streq(line, "[testa]"));
213 line = mfree(line);
214
df0ff127 215 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
69281c49
LP
216 assert_se(streq(line, ""));
217 line = mfree(line);
218
df0ff127 219 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
69281c49
LP
220 assert_se(streq(line, "["));
221 line = mfree(line);
222
df0ff127 223 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
69281c49
LP
224 assert_se(streq(line, "[."));
225 line = mfree(line);
226
df0ff127 227 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
69281c49
LP
228 assert_se(streq(line, "[.."));
229 line = mfree(line);
230
df0ff127 231 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
69281c49
LP
232 assert_se(streq(line, "[..."));
233 line = mfree(line);
234
df0ff127 235 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
69281c49
LP
236 assert_se(streq(line, "[...]"));
237 line = mfree(line);
238
df0ff127 239 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
69281c49
LP
240 assert_se(streq(line, "[t...]"));
241 line = mfree(line);
242
df0ff127 243 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
69281c49
LP
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
df0ff127 249 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
69281c49 250
df0ff127 251 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
69281c49
LP
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
df0ff127 257 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
69281c49
LP
258 assert_se(streq(line, "foo bar"));
259 line = mfree(line);
260
df0ff127 261 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
69281c49
LP
262 assert_se(streq(line, "foo bar"));
263 line = mfree(line);
264
265 assert_se(write(fd, "quux", 4) == 4);
df0ff127 266 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
69281c49
LP
267 assert_se(streq(line, "foo bar quux"));
268 line = mfree(line);
269
df0ff127 270 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
69281c49
LP
271 assert_se(streq(line, "foo bar quux"));
272 line = mfree(line);
273
df0ff127 274 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
69281c49
LP
275 assert_se(streq(line, ""));
276 line = mfree(line);
277
df0ff127 278 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
69281c49
LP
279 assert_se(streq(line, "."));
280 line = mfree(line);
281
df0ff127 282 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
69281c49
LP
283 assert_se(streq(line, ".."));
284 line = mfree(line);
285
df0ff127 286 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
69281c49
LP
287 assert_se(streq(line, "..."));
288 line = mfree(line);
289
df0ff127 290 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
69281c49
LP
291 assert_se(streq(line, "f..."));
292 line = mfree(line);
293
df0ff127 294 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
69281c49
LP
295 assert_se(streq(line, "fo..."));
296 line = mfree(line);
297
df0ff127 298 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
69281c49
LP
299 assert_se(streq(line, "foo..."));
300 line = mfree(line);
301
df0ff127 302 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
69281c49
LP
303 assert_se(streq(line, "foo..."));
304 line = mfree(line);
305
df0ff127 306 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
69281c49
LP
307 assert_se(streq(line, "foo b..."));
308 line = mfree(line);
309
df0ff127 310 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
69281c49
LP
311 assert_se(streq(line, "foo ba..."));
312 line = mfree(line);
313
df0ff127 314 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
69281c49
LP
315 assert_se(streq(line, "foo bar..."));
316 line = mfree(line);
317
df0ff127 318 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
69281c49
LP
319 assert_se(streq(line, "foo bar..."));
320 line = mfree(line);
321
df0ff127 322 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
69281c49
LP
323 assert_se(streq(line, "foo bar quux"));
324 line = mfree(line);
325
df0ff127 326 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
69281c49
LP
327 assert_se(streq(line, "foo bar quux"));
328 line = mfree(line);
329
df0ff127 330 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
69281c49
LP
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
df0ff127 337 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
69281c49 338
df0ff127 339 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
69281c49
LP
340 assert_se(streq(line, "[aaaa bbbb cccc]"));
341 line = mfree(line);
342
df0ff127 343 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
69281c49
LP
344 assert_se(streq(line, "[aaaa...]"));
345 line = mfree(line);
346
df0ff127 347 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
69281c49
LP
348 assert_se(streq(line, "[aaaa...]"));
349 line = mfree(line);
350
df0ff127 351 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
69281c49
LP
352 assert_se(streq(line, "[aaaa b...]"));
353 line = mfree(line);
354
355 safe_close(fd);
356 _exit(0);
357}
358
049c884a 359static void test_rename_process_now(const char *p, int ret) {
9bfaffd5 360 _cleanup_free_ char *comm = NULL, *cmdline = NULL;
9bfaffd5
LP
361 int r;
362
9bfaffd5 363 r = rename_process(p);
9bfaffd5
LP
364 assert_se(r == ret ||
365 (ret == 0 && r >= 0) ||
366 (ret > 0 && r > 0));
367
368 if (r < 0)
049c884a 369 return;
9bfaffd5 370
349cc4a5 371#if HAVE_VALGRIND_VALGRIND_H
9bfaffd5
LP
372 /* see above, valgrind is weird, we can't verify what we are doing here */
373 if (RUNNING_ON_VALGRIND)
049c884a 374 return;
9bfaffd5
LP
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);
049c884a
JW
382 /* we cannot expect cmdline to be renamed properly without privileges */
383 if (geteuid() == 0) {
384 log_info("cmdline = <%s>", cmdline);
385 assert_se(strneq(p, cmdline, strlen("test-process-util")));
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;
411
412 pid = fork();
413 assert_se(pid >= 0);
9bfaffd5 414
049c884a
JW
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 */
428 setresuid(99, 99, 99);
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);
9bfaffd5
LP
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 */
049c884a 442 test_rename_process_multi(); /* multiple invocations and dropped privileges */
9bfaffd5
LP
443}
444
5c30a6d2
LP
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));
498}
499
0b452006 500int main(int argc, char *argv[]) {
9bfaffd5
LP
501
502 log_set_max_level(LOG_DEBUG);
0b452006
RC
503 log_parse_environment();
504 log_open();
505
9bfaffd5
LP
506 saved_argc = argc;
507 saved_argv = argv;
508
18dade5a
ZJS
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 {
76c19e9f 515 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
18dade5a
ZJS
516 test_get_process_comm(getpid());
517 }
518
0b452006
RC
519 test_pid_is_unwaited();
520 test_pid_is_alive();
26fbedd7 521 test_personality();
69281c49 522 test_get_process_cmdline_harder();
9bfaffd5 523 test_rename_process();
5c30a6d2
LP
524 test_getpid_cached();
525 test_getpid_measure();
0b452006
RC
526
527 return 0;
528}