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 (void) 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 assert_se(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) >= 0);
206 fd = mkostemp(path, O_CLOEXEC);
209 if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
210 /* This happens under selinux… Abort the test in this case. */
211 log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
212 assert(errno == EACCES);
216 assert_se(unlink(path) >= 0);
218 assert_se(prctl(PR_SET_NAME, "testa") >= 0);
220 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
222 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
223 assert_se(streq(line, "[testa]"));
226 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
227 assert_se(streq(line, ""));
230 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
231 assert_se(streq(line, "["));
234 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
235 assert_se(streq(line, "[."));
238 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
239 assert_se(streq(line, "[.."));
242 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
243 assert_se(streq(line, "[..."));
246 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
247 assert_se(streq(line, "[...]"));
250 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
251 assert_se(streq(line, "[t...]"));
254 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
255 assert_se(streq(line, "[testa]"));
258 assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
260 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
262 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
263 assert_se(streq(line, "[testa]"));
266 assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
268 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
269 assert_se(streq(line, "foo bar"));
272 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
273 assert_se(streq(line, "foo bar"));
276 assert_se(write(fd, "quux", 4) == 4);
277 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
278 assert_se(streq(line, "foo bar quux"));
281 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
282 assert_se(streq(line, "foo bar quux"));
285 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
286 assert_se(streq(line, ""));
289 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
290 assert_se(streq(line, "."));
293 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
294 assert_se(streq(line, ".."));
297 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
298 assert_se(streq(line, "..."));
301 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
302 assert_se(streq(line, "f..."));
305 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
306 assert_se(streq(line, "fo..."));
309 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
310 assert_se(streq(line, "foo..."));
313 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
314 assert_se(streq(line, "foo..."));
317 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
318 assert_se(streq(line, "foo b..."));
321 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
322 assert_se(streq(line, "foo ba..."));
325 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
326 assert_se(streq(line, "foo bar..."));
329 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
330 assert_se(streq(line, "foo bar..."));
333 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
334 assert_se(streq(line, "foo bar quux"));
337 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
338 assert_se(streq(line, "foo bar quux"));
341 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
342 assert_se(streq(line, "foo bar quux"));
345 assert_se(ftruncate(fd, 0) >= 0);
346 assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
348 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
350 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
351 assert_se(streq(line, "[aaaa bbbb cccc]"));
354 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
355 assert_se(streq(line, "[aaaa...]"));
358 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
359 assert_se(streq(line, "[aaaa...]"));
362 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
363 assert_se(streq(line, "[aaaa b...]"));
370 #if 0 /// UNNEEDED by elogind
371 static void test_rename_process_now(const char *p, int ret) {
372 _cleanup_free_ char *comm = NULL, *cmdline = NULL;
375 r = rename_process(p);
376 assert_se(r == ret ||
377 (ret == 0 && r >= 0) ||
383 #if HAVE_VALGRIND_VALGRIND_H
384 /* see above, valgrind is weird, we can't verify what we are doing here */
385 if (RUNNING_ON_VALGRIND)
389 assert_se(get_process_comm(0, &comm) >= 0);
390 log_info("comm = <%s>", comm);
391 assert_se(strneq(comm, p, 15));
393 assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
394 /* we cannot expect cmdline to be renamed properly without privileges */
395 if (geteuid() == 0) {
396 log_info("cmdline = <%s>", cmdline);
397 assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
398 assert_se(startswith(p, cmdline));
400 log_info("cmdline = <%s> (not verified)", cmdline);
403 static void test_rename_process_one(const char *p, int ret) {
412 test_rename_process_now(p, ret);
416 assert_se(wait_for_terminate(pid, &si) >= 0);
417 assert_se(si.si_code == CLD_EXITED);
418 assert_se(si.si_status == EXIT_SUCCESS);
421 static void test_rename_process_multi(void) {
430 assert_se(wait_for_terminate(pid, &si) >= 0);
431 assert_se(si.si_code == CLD_EXITED);
432 assert_se(si.si_status == EXIT_SUCCESS);
438 test_rename_process_now("one", 1);
439 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
440 (void) setresuid(99, 99, 99); /* change uid when running privileged */
441 test_rename_process_now("time!", 0);
442 test_rename_process_now("0", 1); /* shorter than "one", should fit */
443 test_rename_process_one("", -EINVAL);
444 test_rename_process_one(NULL, -EINVAL);
448 static void test_rename_process(void) {
449 test_rename_process_one(NULL, -EINVAL);
450 test_rename_process_one("", -EINVAL);
451 test_rename_process_one("foo", 1); /* should always fit */
452 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
453 test_rename_process_one("1234567", 1); /* should always fit */
454 test_rename_process_multi(); /* multiple invocations and dropped privileges */
458 static void test_getpid_cached(void) {
460 pid_t a, b, c, d, e, f, child;
466 assert_se(a == b && a == c);
469 assert_se(child >= 0);
477 assert_se(a == b && a == c);
485 assert_se(a == d && a == e && a == f);
487 assert_se(wait_for_terminate(child, &si) >= 0);
488 assert_se(si.si_status == 0);
489 assert_se(si.si_code == CLD_EXITED);
492 #define MEASURE_ITERATIONS (10000000LLU)
494 static void test_getpid_measure(void) {
495 unsigned long long i;
498 t = now(CLOCK_MONOTONIC);
499 for (i = 0; i < MEASURE_ITERATIONS; i++)
501 q = now(CLOCK_MONOTONIC) - t;
503 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
505 t = now(CLOCK_MONOTONIC);
506 for (i = 0; i < MEASURE_ITERATIONS; i++)
507 (void) getpid_cached();
508 q = now(CLOCK_MONOTONIC) - t;
510 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
513 static void test_safe_fork(void) {
518 BLOCK_SIGNALS(SIGCHLD);
520 r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
525 usleep(100 * USEC_PER_MSEC);
530 assert_se(wait_for_terminate(pid, &status) >= 0);
531 assert_se(status.si_code == CLD_EXITED);
532 assert_se(status.si_status == 88);
535 static void test_pid_to_ptr(void) {
537 assert_se(PTR_TO_PID(NULL) == 0);
538 assert_se(PID_TO_PTR(0) == NULL);
540 assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
541 assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
542 assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1);
543 assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2);
545 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
546 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
548 #if SIZEOF_PID_T >= 4
549 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
550 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
554 int main(int argc, char *argv[]) {
556 log_set_max_level(LOG_DEBUG);
557 log_parse_environment();
566 (void) parse_pid(argv[1], &pid);
567 test_get_process_comm(pid);
569 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
570 test_get_process_comm(getpid());
573 test_pid_is_unwaited();
575 #if 0 /// UNNEEDED by elogind
578 test_get_process_cmdline_harder();
579 #if 0 /// UNNEEDED by elogind
580 test_rename_process();
582 test_getpid_cached();
583 test_getpid_measure();