diff options
Diffstat (limited to 'src/test/test-process-util.c')
-rw-r--r-- | src/test/test-process-util.c | 355 |
1 files changed, 267 insertions, 88 deletions
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 07c0704ef..f19875dae 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -1,22 +1,4 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Thomas H.P. Andersen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ +/* SPDX-License-Identifier: LGPL-2.1+ */ #include <sched.h> #include <sys/mount.h> @@ -26,7 +8,7 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> -#ifdef HAVE_VALGRIND_VALGRIND_H +#if HAVE_VALGRIND_VALGRIND_H #include <valgrind/valgrind.h> #endif @@ -37,6 +19,7 @@ #include "macro.h" #include "parse-util.h" #include "process-util.h" +#include "signal-util.h" #include "stdio-util.h" #include "string-util.h" #include "terminal-util.h" @@ -48,7 +31,7 @@ static void test_get_process_comm(pid_t pid) { struct stat st; _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL; _cleanup_free_ char *env = NULL; - char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)]; + char path[STRLEN("/proc//comm") + DECIMAL_STR_MAX(pid_t)]; #if 0 /// UNNEEDED by elogind pid_t e; uid_t u; @@ -104,10 +87,42 @@ static void test_get_process_comm(pid_t pid) { if (!detect_container()) assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1); - getenv_for_pid(pid, "PATH", &i); + (void) getenv_for_pid(pid, "PATH", &i); log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i)); } +static void test_get_process_comm_escape_one(const char *input, const char *output) { + _cleanup_free_ char *n = NULL; + + log_info("input: <%s> — output: <%s>", input, output); + + assert_se(prctl(PR_SET_NAME, input) >= 0); + assert_se(get_process_comm(0, &n) >= 0); + + log_info("got: <%s>", n); + + assert_se(streq_ptr(n, output)); +} + +static void test_get_process_comm_escape(void) { + _cleanup_free_ char *saved = NULL; + + assert_se(get_process_comm(0, &saved) >= 0); + + test_get_process_comm_escape_one("", ""); + test_get_process_comm_escape_one("foo", "foo"); + test_get_process_comm_escape_one("012345678901234", "012345678901234"); + test_get_process_comm_escape_one("0123456789012345", "012345678901234"); + test_get_process_comm_escape_one("äöüß", "\\303\\244\\303…"); + test_get_process_comm_escape_one("xäöüß", "x\\303\\244…"); + test_get_process_comm_escape_one("xxäöüß", "xx\\303\\244…"); + test_get_process_comm_escape_one("xxxäöüß", "xxx\\303\\244…"); + test_get_process_comm_escape_one("xxxxäöüß", "xxxx\\303\\244…"); + test_get_process_comm_escape_one("xxxxxäöüß", "xxxxx\\303…"); + + assert_se(prctl(PR_SET_NAME, saved) >= 0); +} + static void test_pid_is_unwaited(void) { pid_t pid; @@ -121,7 +136,7 @@ static void test_pid_is_unwaited(void) { waitpid(pid, &status, 0); assert_se(!pid_is_unwaited(pid)); } - assert_se(pid_is_unwaited(getpid())); + assert_se(pid_is_unwaited(getpid_cached())); assert_se(!pid_is_unwaited(-1)); } @@ -138,7 +153,7 @@ static void test_pid_is_alive(void) { waitpid(pid, &status, 0); assert_se(!pid_is_alive(pid)); } - assert_se(pid_is_alive(getpid())); + assert_se(pid_is_alive(getpid_cached())); assert_se(!pid_is_alive(-1)); } @@ -176,7 +191,7 @@ static void test_get_process_cmdline_harder(void) { if (geteuid() != 0) return; -#ifdef HAVE_VALGRIND_VALGRIND_H +#if HAVE_VALGRIND_VALGRIND_H /* valgrind patches open(/proc//cmdline) * so, test_get_process_cmdline_harder fails always * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */ @@ -199,6 +214,8 @@ static void test_get_process_cmdline_harder(void) { assert_se(pid == 0); assert_se(unshare(CLONE_NEWNS) >= 0); + assert_se(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) >= 0); + fd = mkostemp(path, O_CLOEXEC); assert_se(fd >= 0); @@ -213,201 +230,231 @@ static void test_get_process_cmdline_harder(void) { assert_se(prctl(PR_SET_NAME, "testa") >= 0); - assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT); + assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT); - assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0); assert_se(streq(line, "[testa]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0); assert_se(streq(line, "")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0); assert_se(streq(line, "[")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0); assert_se(streq(line, "[.")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0); assert_se(streq(line, "[..")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0); assert_se(streq(line, "[...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0); assert_se(streq(line, "[...]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0); assert_se(streq(line, "[t...]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0); assert_se(streq(line, "[testa]")); line = mfree(line); assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10); - assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT); + assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT); - assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0); assert_se(streq(line, "[testa]")); line = mfree(line); assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10); - assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0); assert_se(streq(line, "foo bar")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0); assert_se(streq(line, "foo bar")); line = mfree(line); assert_se(write(fd, "quux", 4) == 4); - assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0); assert_se(streq(line, "foo bar quux")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0); assert_se(streq(line, "foo bar quux")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0); assert_se(streq(line, "")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0); assert_se(streq(line, ".")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0); assert_se(streq(line, "..")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0); assert_se(streq(line, "...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0); assert_se(streq(line, "f...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0); assert_se(streq(line, "fo...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0); assert_se(streq(line, "foo...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0); assert_se(streq(line, "foo...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 9, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0); assert_se(streq(line, "foo b...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0); assert_se(streq(line, "foo ba...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0); assert_se(streq(line, "foo bar...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0); assert_se(streq(line, "foo bar...")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 13, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0); assert_se(streq(line, "foo bar quux")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 14, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0); assert_se(streq(line, "foo bar quux")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 1000, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0); assert_se(streq(line, "foo bar quux")); line = mfree(line); assert_se(ftruncate(fd, 0) >= 0); assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0); - assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT); + assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT); - assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0); assert_se(streq(line, "[aaaa bbbb cccc]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0); assert_se(streq(line, "[aaaa...]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0); assert_se(streq(line, "[aaaa...]")); line = mfree(line); - assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0); + assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0); assert_se(streq(line, "[aaaa b...]")); line = mfree(line); safe_close(fd); - _exit(0); + _exit(EXIT_SUCCESS); } #if 0 /// UNNEEDED by elogind -static void test_rename_process_one(const char *p, int ret) { +static void test_rename_process_now(const char *p, int ret) { _cleanup_free_ char *comm = NULL, *cmdline = NULL; - pid_t pid; int r; - pid = fork(); - assert_se(pid >= 0); - - if (pid > 0) { - siginfo_t si; - - assert_se(wait_for_terminate(pid, &si) >= 0); - assert_se(si.si_code == CLD_EXITED); - assert_se(si.si_status == EXIT_SUCCESS); - - return; - } - - /* child */ r = rename_process(p); - assert_se(r == ret || (ret == 0 && r >= 0) || (ret > 0 && r > 0)); if (r < 0) - goto finish; + return; -#ifdef HAVE_VALGRIND_VALGRIND_H +#if HAVE_VALGRIND_VALGRIND_H /* see above, valgrind is weird, we can't verify what we are doing here */ if (RUNNING_ON_VALGRIND) - goto finish; + return; #endif assert_se(get_process_comm(0, &comm) >= 0); log_info("comm = <%s>", comm); - assert_se(strneq(comm, p, 15)); + assert_se(strneq(comm, p, TASK_COMM_LEN-1)); assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0); - log_info("cmdline = <%s>", cmdline); - assert_se(strneq(p, cmdline, strlen("test-process-util"))); - assert_se(startswith(p, cmdline)); + /* we cannot expect cmdline to be renamed properly without privileges */ + if (geteuid() == 0) { + log_info("cmdline = <%s>", cmdline); + assert_se(strneq(p, cmdline, STRLEN("test-process-util"))); + assert_se(startswith(p, cmdline)); + } else + log_info("cmdline = <%s> (not verified)", cmdline); +} + +static void test_rename_process_one(const char *p, int ret) { + siginfo_t si; + pid_t pid; + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + /* child */ + test_rename_process_now(p, ret); + _exit(EXIT_SUCCESS); + } + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); +} + +static void test_rename_process_multi(void) { + pid_t pid; + + pid = fork(); + assert_se(pid >= 0); + + if (pid > 0) { + siginfo_t si; + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); + + return; + } -finish: + /* child */ + test_rename_process_now("one", 1); + test_rename_process_now("more", 0); /* longer than "one", hence truncated */ + (void) setresuid(99, 99, 99); /* change uid when running privileged */ + test_rename_process_now("time!", 0); + test_rename_process_now("0", 1); /* shorter than "one", should fit */ + test_rename_process_one("", -EINVAL); + test_rename_process_one(NULL, -EINVAL); _exit(EXIT_SUCCESS); } @@ -417,11 +464,135 @@ static void test_rename_process(void) { test_rename_process_one("foo", 1); /* should always fit */ test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */ test_rename_process_one("1234567", 1); /* should always fit */ + test_rename_process_multi(); /* multiple invocations and dropped privileges */ } #endif // 0 -int main(int argc, char *argv[]) { +static void test_getpid_cached(void) { + siginfo_t si; + pid_t a, b, c, d, e, f, child; + + a = raw_getpid(); + b = getpid_cached(); + c = getpid(); + + assert_se(a == b && a == c); + + child = fork(); + assert_se(child >= 0); + if (child == 0) { + /* In child */ + a = raw_getpid(); + b = getpid_cached(); + c = getpid(); + + assert_se(a == b && a == c); + _exit(EXIT_SUCCESS); + } + + d = raw_getpid(); + e = getpid_cached(); + f = getpid(); + + assert_se(a == d && a == e && a == f); + + assert_se(wait_for_terminate(child, &si) >= 0); + assert_se(si.si_status == 0); + assert_se(si.si_code == CLD_EXITED); +} + +#define MEASURE_ITERATIONS (10000000LLU) + +static void test_getpid_measure(void) { + unsigned long long i; + usec_t t, q; + + t = now(CLOCK_MONOTONIC); + for (i = 0; i < MEASURE_ITERATIONS; i++) + (void) getpid(); + q = now(CLOCK_MONOTONIC) - t; + + log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q)); + + t = now(CLOCK_MONOTONIC); + for (i = 0; i < MEASURE_ITERATIONS; i++) + (void) getpid_cached(); + q = now(CLOCK_MONOTONIC) - t; + + log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q)); +} + +static void test_safe_fork(void) { + siginfo_t status; + pid_t pid; + int r; + + BLOCK_SIGNALS(SIGCHLD); + + r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid); + assert_se(r >= 0); + + if (r == 0) { + /* child */ + usleep(100 * USEC_PER_MSEC); + + _exit(88); + } + + assert_se(wait_for_terminate(pid, &status) >= 0); + assert_se(status.si_code == CLD_EXITED); + assert_se(status.si_status == 88); +} + +static void test_pid_to_ptr(void) { + + assert_se(PTR_TO_PID(NULL) == 0); + assert_se(PID_TO_PTR(0) == NULL); + + assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1); + assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2); + assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1); + assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2); + + assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX); + assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN); + +#if SIZEOF_PID_T >= 4 + assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX); + assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN); +#endif +} + +#if 0 /// UNNEEDED by elogind +static void test_ioprio_class_from_to_string_one(const char *val, int expected) { + assert_se(ioprio_class_from_string(val) == expected); + if (expected >= 0) { + _cleanup_free_ char *s = NULL; + unsigned ret; + + assert_se(ioprio_class_to_string_alloc(expected, &s) == 0); + /* We sometimes get a class number and sometimes a number back */ + assert_se(streq(s, val) || + safe_atou(val, &ret) == 0); + } +} + +static void test_ioprio_class_from_to_string(void) { + test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE); + test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT); + test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE); + test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE); + test_ioprio_class_from_to_string_one("0", 0); + test_ioprio_class_from_to_string_one("1", 1); + test_ioprio_class_from_to_string_one("7", 7); + test_ioprio_class_from_to_string_one("8", 8); + test_ioprio_class_from_to_string_one("9", -1); + test_ioprio_class_from_to_string_one("-1", -1); +} +#endif // 0 + +int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); @@ -439,6 +610,7 @@ int main(int argc, char *argv[]) { test_get_process_comm(getpid()); } + test_get_process_comm_escape(); test_pid_is_unwaited(); test_pid_is_alive(); #if 0 /// UNNEEDED by elogind @@ -448,6 +620,13 @@ int main(int argc, char *argv[]) { #if 0 /// UNNEEDED by elogind test_rename_process(); #endif // 0 + test_getpid_cached(); + test_getpid_measure(); + test_safe_fork(); + test_pid_to_ptr(); +#if 0 /// UNNEEDED by elogind + test_ioprio_class_from_to_string(); +#endif // 0 return 0; } |