]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-process-util.c
f540eeb3b0991d0d9bbd81282bb2f2f6a4266be0
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
)];
60 xsprintf(path
, "/proc/"PID_FMT
"/comm", pid
);
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
);
66 log_warning("%s not exist.", path
);
68 assert_se(get_process_cmdline(pid
, 0, true, &c
) >= 0);
69 log_info("PID"PID_FMT
" cmdline: '%s'", pid
, c
);
71 assert_se(get_process_cmdline(pid
, 8, false, &d
) >= 0);
72 log_info("PID"PID_FMT
" cmdline truncated to 8: '%s'", pid
, 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
);
78 assert_se(get_process_ppid(pid
, &e
) >= 0);
79 log_info("PID"PID_FMT
" PPID: "PID_FMT
, pid
, e
);
80 assert_se(pid
== 1 ? e
== 0 : e
> 0);
82 assert_se(is_kernel_thread(pid
) == 0 || pid
!= 1);
84 r
= get_process_exe(pid
, &f
);
85 assert_se(r
>= 0 || r
== -EACCES
);
86 log_info("PID"PID_FMT
" exe: '%s'", pid
, strna(f
));
88 assert_se(get_process_uid(pid
, &u
) == 0);
89 log_info("PID"PID_FMT
" UID: "UID_FMT
, pid
, u
);
90 assert_se(u
== 0 || pid
!= 1);
92 assert_se(get_process_gid(pid
, &g
) == 0);
93 log_info("PID"PID_FMT
" GID: "GID_FMT
, pid
, g
);
94 assert_se(g
== 0 || pid
!= 1);
96 r
= get_process_environ(pid
, &env
);
97 assert_se(r
>= 0 || r
== -EACCES
);
98 log_info("PID"PID_FMT
" strlen(environ): %zi", pid
, env
? (ssize_t
)strlen(env
) : (ssize_t
)-errno
);
100 if (!detect_container())
101 assert_se(get_ctty_devnr(pid
, &h
) == -ENXIO
|| pid
!= 1);
103 (void) getenv_for_pid(pid
, "PATH", &i
);
104 log_info("PID"PID_FMT
" $PATH: '%s'", pid
, strna(i
));
107 static void test_pid_is_unwaited(void) {
117 waitpid(pid
, &status
, 0);
118 assert_se(!pid_is_unwaited(pid
));
120 assert_se(pid_is_unwaited(getpid_cached()));
121 assert_se(!pid_is_unwaited(-1));
124 static void test_pid_is_alive(void) {
134 waitpid(pid
, &status
, 0);
135 assert_se(!pid_is_alive(pid
));
137 assert_se(pid_is_alive(getpid_cached()));
138 assert_se(!pid_is_alive(-1));
141 static void test_personality(void) {
143 assert_se(personality_to_string(PER_LINUX
));
144 assert_se(!personality_to_string(PERSONALITY_INVALID
));
146 assert_se(streq(personality_to_string(PER_LINUX
), architecture_to_string(native_architecture())));
148 assert_se(personality_from_string(personality_to_string(PER_LINUX
)) == PER_LINUX
);
149 assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX
);
152 assert_se(streq_ptr(personality_to_string(PER_LINUX
), "x86-64"));
153 assert_se(streq_ptr(personality_to_string(PER_LINUX32
), "x86"));
155 assert_se(personality_from_string("x86-64") == PER_LINUX
);
156 assert_se(personality_from_string("x86") == PER_LINUX32
);
157 assert_se(personality_from_string("ia64") == PERSONALITY_INVALID
);
158 assert_se(personality_from_string(NULL
) == PERSONALITY_INVALID
);
160 assert_se(personality_from_string(personality_to_string(PER_LINUX32
)) == PER_LINUX32
);
164 static void test_get_process_cmdline_harder(void) {
165 char path
[] = "/tmp/test-cmdlineXXXXXX";
166 _cleanup_close_
int fd
= -1;
167 _cleanup_free_
char *line
= NULL
;
173 #if HAVE_VALGRIND_VALGRIND_H
174 /* valgrind patches open(/proc//cmdline)
175 * so, test_get_process_cmdline_harder fails always
176 * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
177 if (RUNNING_ON_VALGRIND
)
185 (void) wait_for_terminate(pid
, &si
);
187 assert_se(si
.si_code
== CLD_EXITED
);
188 assert_se(si
.si_status
== 0);
194 assert_se(unshare(CLONE_NEWNS
) >= 0);
196 assert_se(mount(NULL
, "/", NULL
, MS_PRIVATE
|MS_REC
, NULL
) >= 0);
198 fd
= mkostemp(path
, O_CLOEXEC
);
201 if (mount(path
, "/proc/self/cmdline", "bind", MS_BIND
, NULL
) < 0) {
202 /* This happens under selinux… Abort the test in this case. */
203 log_warning_errno(errno
, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
204 assert(errno
== EACCES
);
208 assert_se(unlink(path
) >= 0);
210 assert_se(prctl(PR_SET_NAME
, "testa") >= 0);
212 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
214 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
215 assert_se(streq(line
, "[testa]"));
218 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line
) >= 0);
219 assert_se(streq(line
, ""));
222 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line
) >= 0);
223 assert_se(streq(line
, "["));
226 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line
) >= 0);
227 assert_se(streq(line
, "[."));
230 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line
) >= 0);
231 assert_se(streq(line
, "[.."));
234 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line
) >= 0);
235 assert_se(streq(line
, "[..."));
238 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line
) >= 0);
239 assert_se(streq(line
, "[...]"));
242 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line
) >= 0);
243 assert_se(streq(line
, "[t...]"));
246 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line
) >= 0);
247 assert_se(streq(line
, "[testa]"));
250 assert_se(write(fd
, "\0\0\0\0\0\0\0\0\0", 10) == 10);
252 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
254 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
255 assert_se(streq(line
, "[testa]"));
258 assert_se(write(fd
, "foo\0bar\0\0\0\0\0", 10) == 10);
260 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) >= 0);
261 assert_se(streq(line
, "foo bar"));
264 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
265 assert_se(streq(line
, "foo bar"));
268 assert_se(write(fd
, "quux", 4) == 4);
269 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) >= 0);
270 assert_se(streq(line
, "foo bar quux"));
273 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
274 assert_se(streq(line
, "foo bar quux"));
277 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line
) >= 0);
278 assert_se(streq(line
, ""));
281 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line
) >= 0);
282 assert_se(streq(line
, "."));
285 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line
) >= 0);
286 assert_se(streq(line
, ".."));
289 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line
) >= 0);
290 assert_se(streq(line
, "..."));
293 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line
) >= 0);
294 assert_se(streq(line
, "f..."));
297 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line
) >= 0);
298 assert_se(streq(line
, "fo..."));
301 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line
) >= 0);
302 assert_se(streq(line
, "foo..."));
305 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line
) >= 0);
306 assert_se(streq(line
, "foo..."));
309 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line
) >= 0);
310 assert_se(streq(line
, "foo b..."));
313 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line
) >= 0);
314 assert_se(streq(line
, "foo ba..."));
317 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line
) >= 0);
318 assert_se(streq(line
, "foo bar..."));
321 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line
) >= 0);
322 assert_se(streq(line
, "foo bar..."));
325 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line
) >= 0);
326 assert_se(streq(line
, "foo bar quux"));
329 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line
) >= 0);
330 assert_se(streq(line
, "foo bar quux"));
333 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line
) >= 0);
334 assert_se(streq(line
, "foo bar quux"));
337 assert_se(ftruncate(fd
, 0) >= 0);
338 assert_se(prctl(PR_SET_NAME
, "aaaa bbbb cccc") >= 0);
340 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
342 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
343 assert_se(streq(line
, "[aaaa bbbb cccc]"));
346 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line
) >= 0);
347 assert_se(streq(line
, "[aaaa...]"));
350 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line
) >= 0);
351 assert_se(streq(line
, "[aaaa...]"));
354 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line
) >= 0);
355 assert_se(streq(line
, "[aaaa b...]"));
362 static void test_rename_process_now(const char *p
, int ret
) {
363 _cleanup_free_
char *comm
= NULL
, *cmdline
= NULL
;
366 r
= rename_process(p
);
367 assert_se(r
== ret
||
368 (ret
== 0 && r
>= 0) ||
374 #if HAVE_VALGRIND_VALGRIND_H
375 /* see above, valgrind is weird, we can't verify what we are doing here */
376 if (RUNNING_ON_VALGRIND
)
380 assert_se(get_process_comm(0, &comm
) >= 0);
381 log_info("comm = <%s>", comm
);
382 assert_se(strneq(comm
, p
, 15));
384 assert_se(get_process_cmdline(0, 0, false, &cmdline
) >= 0);
385 /* we cannot expect cmdline to be renamed properly without privileges */
386 if (geteuid() == 0) {
387 log_info("cmdline = <%s>", cmdline
);
388 assert_se(strneq(p
, cmdline
, STRLEN("test-process-util")));
389 assert_se(startswith(p
, cmdline
));
391 log_info("cmdline = <%s> (not verified)", cmdline
);
394 static void test_rename_process_one(const char *p
, int ret
) {
403 test_rename_process_now(p
, ret
);
407 assert_se(wait_for_terminate(pid
, &si
) >= 0);
408 assert_se(si
.si_code
== CLD_EXITED
);
409 assert_se(si
.si_status
== EXIT_SUCCESS
);
412 static void test_rename_process_multi(void) {
421 assert_se(wait_for_terminate(pid
, &si
) >= 0);
422 assert_se(si
.si_code
== CLD_EXITED
);
423 assert_se(si
.si_status
== EXIT_SUCCESS
);
429 test_rename_process_now("one", 1);
430 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
431 (void) setresuid(99, 99, 99); /* change uid when running privileged */
432 test_rename_process_now("time!", 0);
433 test_rename_process_now("0", 1); /* shorter than "one", should fit */
434 test_rename_process_one("", -EINVAL
);
435 test_rename_process_one(NULL
, -EINVAL
);
439 static void test_rename_process(void) {
440 test_rename_process_one(NULL
, -EINVAL
);
441 test_rename_process_one("", -EINVAL
);
442 test_rename_process_one("foo", 1); /* should always fit */
443 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
444 test_rename_process_one("1234567", 1); /* should always fit */
445 test_rename_process_multi(); /* multiple invocations and dropped privileges */
448 static void test_getpid_cached(void) {
450 pid_t a
, b
, c
, d
, e
, f
, child
;
456 assert_se(a
== b
&& a
== c
);
459 assert_se(child
>= 0);
467 assert_se(a
== b
&& a
== c
);
475 assert_se(a
== d
&& a
== e
&& a
== f
);
477 assert_se(wait_for_terminate(child
, &si
) >= 0);
478 assert_se(si
.si_status
== 0);
479 assert_se(si
.si_code
== CLD_EXITED
);
482 #define MEASURE_ITERATIONS (10000000LLU)
484 static void test_getpid_measure(void) {
485 unsigned long long i
;
488 t
= now(CLOCK_MONOTONIC
);
489 for (i
= 0; i
< MEASURE_ITERATIONS
; i
++)
491 q
= now(CLOCK_MONOTONIC
) - t
;
493 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS
*USEC_PER_SEC
/q
));
495 t
= now(CLOCK_MONOTONIC
);
496 for (i
= 0; i
< MEASURE_ITERATIONS
; i
++)
497 (void) getpid_cached();
498 q
= now(CLOCK_MONOTONIC
) - t
;
500 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS
*USEC_PER_SEC
/q
));
503 static void test_safe_fork(void) {
508 BLOCK_SIGNALS(SIGCHLD
);
510 r
= safe_fork("(test-child)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_NULL_STDIO
|FORK_REOPEN_LOG
, &pid
);
515 usleep(100 * USEC_PER_MSEC
);
520 assert_se(wait_for_terminate(pid
, &status
) >= 0);
521 assert_se(status
.si_code
== CLD_EXITED
);
522 assert_se(status
.si_status
== 88);
525 static void test_pid_to_ptr(void) {
527 assert_se(PTR_TO_PID(NULL
) == 0);
528 assert_se(PID_TO_PTR(0) == NULL
);
530 assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
531 assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
532 assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1);
533 assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2);
535 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX
)) == INT16_MAX
);
536 assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN
)) == INT16_MIN
);
538 #if SIZEOF_PID_T >= 4
539 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX
)) == INT32_MAX
);
540 assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN
)) == INT32_MIN
);
544 static void test_ioprio_class_from_to_string_one(const char *val
, int expected
) {
545 assert_se(ioprio_class_from_string(val
) == expected
);
547 _cleanup_free_
char *s
= NULL
;
550 assert_se(ioprio_class_to_string_alloc(expected
, &s
) == 0);
551 /* We sometimes get a class number and sometimes a number back */
552 assert_se(streq(s
, val
) ||
553 safe_atou(val
, &ret
) == 0);
557 static void test_ioprio_class_from_to_string(void) {
558 test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE
);
559 test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT
);
560 test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE
);
561 test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE
);
562 test_ioprio_class_from_to_string_one("0", 0);
563 test_ioprio_class_from_to_string_one("1", 1);
564 test_ioprio_class_from_to_string_one("7", 7);
565 test_ioprio_class_from_to_string_one("8", 8);
566 test_ioprio_class_from_to_string_one("9", -1);
567 test_ioprio_class_from_to_string_one("-1", -1);
570 int main(int argc
, char *argv
[]) {
571 log_set_max_level(LOG_DEBUG
);
572 log_parse_environment();
581 (void) parse_pid(argv
[1], &pid
);
582 test_get_process_comm(pid
);
584 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
585 test_get_process_comm(getpid());
588 test_pid_is_unwaited();
591 test_get_process_cmdline_harder();
592 test_rename_process();
593 test_getpid_cached();
594 test_getpid_measure();
597 test_ioprio_class_from_to_string();