]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-process-util.c
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 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 fd
= mkostemp(path
, O_CLOEXEC
);
199 if (mount(path
, "/proc/self/cmdline", "bind", MS_BIND
, NULL
) < 0) {
200 /* This happens under selinux… Abort the test in this case. */
201 log_warning_errno(errno
, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
202 assert(errno
== EACCES
);
206 assert_se(unlink(path
) >= 0);
208 assert_se(prctl(PR_SET_NAME
, "testa") >= 0);
210 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
212 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
213 assert_se(streq(line
, "[testa]"));
216 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line
) >= 0);
217 assert_se(streq(line
, ""));
220 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line
) >= 0);
221 assert_se(streq(line
, "["));
224 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line
) >= 0);
225 assert_se(streq(line
, "[."));
228 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line
) >= 0);
229 assert_se(streq(line
, "[.."));
232 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line
) >= 0);
233 assert_se(streq(line
, "[..."));
236 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line
) >= 0);
237 assert_se(streq(line
, "[...]"));
240 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line
) >= 0);
241 assert_se(streq(line
, "[t...]"));
244 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line
) >= 0);
245 assert_se(streq(line
, "[testa]"));
248 assert_se(write(fd
, "\0\0\0\0\0\0\0\0\0", 10) == 10);
250 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
252 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
253 assert_se(streq(line
, "[testa]"));
256 assert_se(write(fd
, "foo\0bar\0\0\0\0\0", 10) == 10);
258 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) >= 0);
259 assert_se(streq(line
, "foo bar"));
262 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
263 assert_se(streq(line
, "foo bar"));
266 assert_se(write(fd
, "quux", 4) == 4);
267 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) >= 0);
268 assert_se(streq(line
, "foo bar quux"));
271 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
272 assert_se(streq(line
, "foo bar quux"));
275 assert_se(get_process_cmdline(getpid_cached(), 1, true, &line
) >= 0);
276 assert_se(streq(line
, ""));
279 assert_se(get_process_cmdline(getpid_cached(), 2, true, &line
) >= 0);
280 assert_se(streq(line
, "."));
283 assert_se(get_process_cmdline(getpid_cached(), 3, true, &line
) >= 0);
284 assert_se(streq(line
, ".."));
287 assert_se(get_process_cmdline(getpid_cached(), 4, true, &line
) >= 0);
288 assert_se(streq(line
, "..."));
291 assert_se(get_process_cmdline(getpid_cached(), 5, true, &line
) >= 0);
292 assert_se(streq(line
, "f..."));
295 assert_se(get_process_cmdline(getpid_cached(), 6, true, &line
) >= 0);
296 assert_se(streq(line
, "fo..."));
299 assert_se(get_process_cmdline(getpid_cached(), 7, true, &line
) >= 0);
300 assert_se(streq(line
, "foo..."));
303 assert_se(get_process_cmdline(getpid_cached(), 8, true, &line
) >= 0);
304 assert_se(streq(line
, "foo..."));
307 assert_se(get_process_cmdline(getpid_cached(), 9, true, &line
) >= 0);
308 assert_se(streq(line
, "foo b..."));
311 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line
) >= 0);
312 assert_se(streq(line
, "foo ba..."));
315 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line
) >= 0);
316 assert_se(streq(line
, "foo bar..."));
319 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line
) >= 0);
320 assert_se(streq(line
, "foo bar..."));
323 assert_se(get_process_cmdline(getpid_cached(), 13, true, &line
) >= 0);
324 assert_se(streq(line
, "foo bar quux"));
327 assert_se(get_process_cmdline(getpid_cached(), 14, true, &line
) >= 0);
328 assert_se(streq(line
, "foo bar quux"));
331 assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line
) >= 0);
332 assert_se(streq(line
, "foo bar quux"));
335 assert_se(ftruncate(fd
, 0) >= 0);
336 assert_se(prctl(PR_SET_NAME
, "aaaa bbbb cccc") >= 0);
338 assert_se(get_process_cmdline(getpid_cached(), 0, false, &line
) == -ENOENT
);
340 assert_se(get_process_cmdline(getpid_cached(), 0, true, &line
) >= 0);
341 assert_se(streq(line
, "[aaaa bbbb cccc]"));
344 assert_se(get_process_cmdline(getpid_cached(), 10, true, &line
) >= 0);
345 assert_se(streq(line
, "[aaaa...]"));
348 assert_se(get_process_cmdline(getpid_cached(), 11, true, &line
) >= 0);
349 assert_se(streq(line
, "[aaaa...]"));
352 assert_se(get_process_cmdline(getpid_cached(), 12, true, &line
) >= 0);
353 assert_se(streq(line
, "[aaaa b...]"));
360 static void test_rename_process_now(const char *p
, int ret
) {
361 _cleanup_free_
char *comm
= NULL
, *cmdline
= NULL
;
364 r
= rename_process(p
);
365 assert_se(r
== ret
||
366 (ret
== 0 && r
>= 0) ||
372 #if HAVE_VALGRIND_VALGRIND_H
373 /* see above, valgrind is weird, we can't verify what we are doing here */
374 if (RUNNING_ON_VALGRIND
)
378 assert_se(get_process_comm(0, &comm
) >= 0);
379 log_info("comm = <%s>", comm
);
380 assert_se(strneq(comm
, p
, 15));
382 assert_se(get_process_cmdline(0, 0, false, &cmdline
) >= 0);
383 /* we cannot expect cmdline to be renamed properly without privileges */
384 if (geteuid() == 0) {
385 log_info("cmdline = <%s>", cmdline
);
386 assert_se(strneq(p
, cmdline
, STRLEN("test-process-util")));
387 assert_se(startswith(p
, cmdline
));
389 log_info("cmdline = <%s> (not verified)", cmdline
);
392 static void test_rename_process_one(const char *p
, int ret
) {
401 test_rename_process_now(p
, ret
);
405 assert_se(wait_for_terminate(pid
, &si
) >= 0);
406 assert_se(si
.si_code
== CLD_EXITED
);
407 assert_se(si
.si_status
== EXIT_SUCCESS
);
410 static void test_rename_process_multi(void) {
419 assert_se(wait_for_terminate(pid
, &si
) >= 0);
420 assert_se(si
.si_code
== CLD_EXITED
);
421 assert_se(si
.si_status
== EXIT_SUCCESS
);
427 test_rename_process_now("one", 1);
428 test_rename_process_now("more", 0); /* longer than "one", hence truncated */
429 (void) setresuid(99, 99, 99); /* change uid when running privileged */
430 test_rename_process_now("time!", 0);
431 test_rename_process_now("0", 1); /* shorter than "one", should fit */
432 test_rename_process_one("", -EINVAL
);
433 test_rename_process_one(NULL
, -EINVAL
);
437 static void test_rename_process(void) {
438 test_rename_process_one(NULL
, -EINVAL
);
439 test_rename_process_one("", -EINVAL
);
440 test_rename_process_one("foo", 1); /* should always fit */
441 test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
442 test_rename_process_one("1234567", 1); /* should always fit */
443 test_rename_process_multi(); /* multiple invocations and dropped privileges */
446 static void test_getpid_cached(void) {
448 pid_t a
, b
, c
, d
, e
, f
, child
;
454 assert_se(a
== b
&& a
== c
);
457 assert_se(child
>= 0);
465 assert_se(a
== b
&& a
== c
);
473 assert_se(a
== d
&& a
== e
&& a
== f
);
475 assert_se(wait_for_terminate(child
, &si
) >= 0);
476 assert_se(si
.si_status
== 0);
477 assert_se(si
.si_code
== CLD_EXITED
);
480 #define MEASURE_ITERATIONS (10000000LLU)
482 static void test_getpid_measure(void) {
483 unsigned long long i
;
486 t
= now(CLOCK_MONOTONIC
);
487 for (i
= 0; i
< MEASURE_ITERATIONS
; i
++)
489 q
= now(CLOCK_MONOTONIC
) - t
;
491 log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS
*USEC_PER_SEC
/q
));
493 t
= now(CLOCK_MONOTONIC
);
494 for (i
= 0; i
< MEASURE_ITERATIONS
; i
++)
495 (void) getpid_cached();
496 q
= now(CLOCK_MONOTONIC
) - t
;
498 log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS
*USEC_PER_SEC
/q
));
501 static void test_safe_fork(void) {
506 BLOCK_SIGNALS(SIGCHLD
);
508 r
= safe_fork("(test-child)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_NULL_STDIO
|FORK_REOPEN_LOG
, &pid
);
513 usleep(100 * USEC_PER_MSEC
);
518 assert_se(wait_for_terminate(pid
, &status
) >= 0);
519 assert_se(status
.si_code
== CLD_EXITED
);
520 assert_se(status
.si_status
== 88);
523 int main(int argc
, char *argv
[]) {
525 log_set_max_level(LOG_DEBUG
);
526 log_parse_environment();
535 (void) parse_pid(argv
[1], &pid
);
536 test_get_process_comm(pid
);
538 TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
539 test_get_process_comm(getpid());
542 test_pid_is_unwaited();
545 test_get_process_cmdline_harder();
546 test_rename_process();
547 test_getpid_cached();
548 test_getpid_measure();