1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
6 Copyright 2013 Thomas H.P. Andersen
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.
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.
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/>.
23 #include <sys/mount.h>
24 #include <sys/personality.h>
25 #include <sys/prctl.h>
27 #include <sys/types.h>
30 #if HAVE_VALGRIND_VALGRIND_H
31 #include <valgrind/valgrind.h>
34 #include "alloc-util.h"
35 //#include "architecture.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"
49 static void test_get_process_comm(pid_t pid) {
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
62 xsprintf(path, "/proc/"PID_FMT"/comm", pid);
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);
68 log_warning("%s not exist.", path);
70 assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
71 log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
73 assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
74 log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, 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);
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);
86 assert_se(is_kernel_thread(pid) == 0 || pid != 1);
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));
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);
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);
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);
106 if (!detect_container())
107 assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
109 getenv_for_pid(pid, "PATH", &i);
110 log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
113 static void test_pid_is_unwaited(void) {
123 waitpid(pid, &status, 0);
124 assert_se(!pid_is_unwaited(pid));
126 assert_se(pid_is_unwaited(getpid_cached()));
127 assert_se(!pid_is_unwaited(-1));
130 static void test_pid_is_alive(void) {
140 waitpid(pid, &status, 0);
141 assert_se(!pid_is_alive(pid));
143 assert_se(pid_is_alive(getpid_cached()));
144 assert_se(!pid_is_alive(-1));
147 #if 0 /// UNNEEDED by elogind
148 static void test_personality(void) {
150 assert_se(personality_to_string(PER_LINUX));
151 assert_se(!personality_to_string(PERSONALITY_INVALID));
153 assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
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);
159 assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
160 assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
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);
167 assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32);
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;
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)
193 (void) wait_for_terminate(pid, &si);
195 assert_se(si.si_code == CLD_EXITED);
196 assert_se(si.si_status == 0);
202 assert_se(unshare(CLONE_NEWNS) >= 0);
204 fd = mkostemp(path, O_CLOEXEC);
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);
214 assert_se(unlink(path) >= 0);
216 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
218 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
220 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
221 assert_se(streq(line, "[testa]"));
224 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
225 assert_se(streq(line, ""));
228 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
229 assert_se(streq(line, "["));
232 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
233 assert_se(streq(line, "[."));
236 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
237 assert_se(streq(line, "[.."));
240 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
241 assert_se(streq(line, "[..."));
244 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
245 assert_se(streq(line, "[...]"));
248 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
249 assert_se(streq(line, "[t...]"));
252 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
253 assert_se(streq(line, "[testa]"));
256 assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
258 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
260 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
261 assert_se(streq(line, "[testa]"));
264 assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
266 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
267 assert_se(streq(line, "foo bar"));
270 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
271 assert_se(streq(line, "foo bar"));
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"));
279 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
280 assert_se(streq(line, "foo bar quux"));
283 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
284 assert_se(streq(line, ""));
287 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
288 assert_se(streq(line, "."));
291 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
292 assert_se(streq(line, ".."));
295 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
296 assert_se(streq(line, "..."));
299 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
300 assert_se(streq(line, "f..."));
303 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
304 assert_se(streq(line, "fo..."));
307 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
308 assert_se(streq(line, "foo..."));
311 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
312 assert_se(streq(line, "foo..."));
315 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
316 assert_se(streq(line, "foo b..."));
319 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
320 assert_se(streq(line, "foo ba..."));
323 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
324 assert_se(streq(line, "foo bar..."));
327 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
328 assert_se(streq(line, "foo bar..."));
331 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
332 assert_se(streq(line, "foo bar quux"));
335 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
336 assert_se(streq(line, "foo bar quux"));
339 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
340 assert_se(streq(line, "foo bar quux"));
343 assert_se(ftruncate(fd, 0) >= 0);
344 assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
346 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
348 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
349 assert_se(streq(line, "[aaaa bbbb cccc]"));
352 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
353 assert_se(streq(line, "[aaaa...]"));
356 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
357 assert_se(streq(line, "[aaaa...]"));
360 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
361 assert_se(streq(line, "[aaaa b...]"));
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;
373 r = rename_process(p);
374 assert_se(r == ret ||
375 (ret == 0 && r >= 0) ||
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)
387 assert_se(get_process_comm(0, &comm) >= 0);
388 log_info("comm = <%s>", comm);
389 assert_se(strneq(comm, p, 15));
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));
398 log_info("cmdline = <%s> (not verified)", cmdline);
401 static void test_rename_process_one(const char *p, int ret) {
410 test_rename_process_now(p, ret);
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);
419 static void test_rename_process_multi(void) {
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);
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);
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 */
456 static void test_getpid_cached(void) {
458 pid_t a, b, c, d, e, f, child;
464 assert_se(a == b && a == c);
467 assert_se(child >= 0);
475 assert_se(a == b && a == c);
483 assert_se(a == d && a == e && a == f);
485 assert_se(wait_for_terminate(child, &si) >= 0);
486 assert_se(si.si_status == 0);
487 assert_se(si.si_code == CLD_EXITED);
490 #define MEASURE_ITERATIONS (10000000LLU)
492 static void test_getpid_measure(void) {
493 unsigned long long i;
496 t = now(CLOCK_MONOTONIC);
497 for (i = 0; i < MEASURE_ITERATIONS; i++)
499 q = now(CLOCK_MONOTONIC) - t;
501 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
503 t = now(CLOCK_MONOTONIC);
504 for (i = 0; i < MEASURE_ITERATIONS; i++)
505 (void) getpid_cached();
506 q = now(CLOCK_MONOTONIC) - t;
508 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
511 static void test_safe_fork(void) {
516 BLOCK_SIGNALS(SIGCHLD);
518 r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
523 usleep(100 * USEC_PER_MSEC);
528 assert_se(wait_for_terminate(pid, &status) >= 0);
529 assert_se(status.si_code == CLD_EXITED);
530 assert_se(status.si_status == 88);
533 static void test_pid_to_ptr(void) {
535 assert_se(PTR_TO_PID(NULL) == 0);
536 assert_se(PID_TO_PTR(0) == NULL);
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);
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);
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);
552 int main(int argc, char *argv[]) {
554 log_set_max_level(LOG_DEBUG);
555 log_parse_environment();
564 (void) parse_pid(argv[1], &pid);
565 test_get_process_comm(pid);
567 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
568 test_get_process_comm(getpid());
571 test_pid_is_unwaited();
573 #if 0 /// UNNEEDED by elogind
576 test_get_process_cmdline_harder();
577 #if 0 /// UNNEEDED by elogind
578 test_rename_process();
580 test_getpid_cached();
581 test_getpid_measure();