--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sched.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "argv-util.h"
+#include "errno-util.h"
+#include "missing_sched.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+bool invoked_as(char *argv[], const char *token) {
+ if (!argv || isempty(argv[0]))
+ return false;
+
+ if (isempty(token))
+ return false;
+
+ return strstr(last_path_component(argv[0]), token);
+}
+
+bool invoked_by_systemd(void) {
+ int r;
+
+ /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
+ * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
+ const char *e = getenv("SYSTEMD_EXEC_PID");
+ if (!e)
+ return false;
+
+ if (streq(e, "*"))
+ /* For testing. */
+ return true;
+
+ pid_t p;
+ r = parse_pid(e, &p);
+ if (r < 0) {
+ /* We know that systemd sets the variable correctly. Something else must have set it. */
+ log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
+ return false;
+ }
+
+ return getpid_cached() == p;
+}
+
+bool argv_looks_like_help(int argc, char **argv) {
+ char **l;
+
+ /* Scans the command line for indications the user asks for help. This is supposed to be called by
+ * tools that do not implement getopt() style command line parsing because they are not primarily
+ * user-facing. Detects four ways of asking for help:
+ *
+ * 1. Passing zero arguments
+ * 2. Passing "help" as first argument
+ * 3. Passing --help as any argument
+ * 4. Passing -h as any argument
+ */
+
+ if (argc <= 1)
+ return true;
+
+ if (streq_ptr(argv[1], "help"))
+ return true;
+
+ l = strv_skip(argv, 1);
+
+ return strv_contains(l, "--help") ||
+ strv_contains(l, "-h");
+}
+
+static int update_argv(const char name[], size_t l) {
+ static int can_do = -1;
+
+ if (can_do == 0)
+ return 0;
+ can_do = false; /* We'll set it to true only if the whole process works */
+
+ /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
+ * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
+ * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if
+ * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
+ * mmap() is not. */
+ if (geteuid() != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+ "Skipping PR_SET_MM, as we don't have privileges.");
+
+ static size_t mm_size = 0;
+ static char *mm = NULL;
+ int r;
+
+ if (mm_size < l+1) {
+ size_t nn_size;
+ char *nn;
+
+ nn_size = PAGE_ALIGN(l+1);
+ nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (nn == MAP_FAILED)
+ return log_debug_errno(errno, "mmap() failed: %m");
+
+ strncpy(nn, name, nn_size);
+
+ /* Now, let's tell the kernel about this new memory */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+ if (ERRNO_IS_PRIVILEGE(errno))
+ return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
+
+ /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
+ * below the desired start address, in which case the kernel may have kicked this back due
+ * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
+ * action). The proper solution would be to have a prctl() API that could set both start+end
+ * simultaneously, or at least let us query the existing address to anticipate this condition
+ * and respond accordingly. For now, we can only guess at the cause of this failure and try
+ * a workaround--which will briefly expand the arg space to something potentially huge before
+ * resizing it to what we want. */
+ log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
+
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
+ r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
+ (void) munmap(nn, nn_size);
+ return r;
+ }
+
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
+ return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
+ } else {
+ /* And update the end pointer to the new end, too. If this fails, we don't really know what
+ * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
+ * and continue. */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
+ log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ }
+
+ if (mm)
+ (void) munmap(mm, mm_size);
+
+ mm = nn;
+ mm_size = nn_size;
+ } else {
+ strncpy(mm, name, mm_size);
+
+ /* Update the end pointer, continuing regardless of any failure. */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
+ log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ }
+
+ can_do = true;
+ return 0;
+}
+
+int rename_process(const char name[]) {
+ bool truncated = false;
+
+ /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
+ * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
+ * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
+ * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
+ * truncated.
+ *
+ * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
+
+ if (isempty(name))
+ return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
+
+ if (!is_main_thread())
+ return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
+ * cache things without locking, and we make assumptions that PR_SET_NAME sets the
+ * process name that isn't correct on any other threads */
+
+ size_t l = strlen(name);
+
+ /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
+ * can use PR_SET_NAME, which sets the thread name for the calling thread. */
+ if (prctl(PR_SET_NAME, name) < 0)
+ log_debug_errno(errno, "PR_SET_NAME failed: %m");
+ if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
+ truncated = true;
+
+ /* Second step, change glibc's ID of the process name. */
+ if (program_invocation_name) {
+ size_t k;
+
+ k = strlen(program_invocation_name);
+ strncpy(program_invocation_name, name, k);
+ if (l > k)
+ truncated = true;
+ }
+
+ /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
+ * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
+ * the end. This is the best option for changing /proc/self/cmdline. */
+ (void) update_argv(name, l);
+
+ /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
+ * it still looks here */
+ if (saved_argc > 0) {
+ if (saved_argv[0]) {
+ size_t k;
+
+ k = strlen(saved_argv[0]);
+ strncpy(saved_argv[0], name, k);
+ if (l > k)
+ truncated = true;
+ }
+
+ for (int i = 1; i < saved_argc; i++) {
+ if (!saved_argv[i])
+ break;
+
+ memzero(saved_argv[i], strlen(saved_argv[i]));
+ }
+ }
+
+ return !truncated;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+extern int saved_argc;
+extern char **saved_argv;
+
+static inline void save_argc_argv(int argc, char **argv) {
+ /* Protect against CVE-2021-4034 style attacks */
+ assert_se(argc > 0);
+ assert_se(argv);
+ assert_se(argv[0]);
+
+ saved_argc = argc;
+ saved_argv = argv;
+}
+
+bool invoked_as(char *argv[], const char *token);
+bool invoked_by_systemd(void);
+bool argv_looks_like_help(int argc, char **argv);
+
+int rename_process(const char name[]);
#include "sd-messages.h"
#include "alloc-util.h"
+#include "argv-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-util.h"
'alloc-util.h',
'architecture.c',
'architecture.h',
+ 'argv-util.c',
+ 'argv-util.h',
'arphrd-util.c',
'arphrd-util.h',
'async.c',
'user-util.h',
'utf8.c',
'utf8.h',
- 'util.c',
'util.h',
'virt.c',
'virt.h',
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include "alloc-util.h"
#include "architecture.h"
+#include "argv-util.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-util.h"
return 0;
}
-static int update_argv(const char name[], size_t l) {
- static int can_do = -1;
-
- if (can_do == 0)
- return 0;
- can_do = false; /* We'll set it to true only if the whole process works */
-
- /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
- * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
- * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if
- * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
- * mmap() is not. */
- if (geteuid() != 0)
- return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
- "Skipping PR_SET_MM, as we don't have privileges.");
-
- static size_t mm_size = 0;
- static char *mm = NULL;
- int r;
-
- if (mm_size < l+1) {
- size_t nn_size;
- char *nn;
-
- nn_size = PAGE_ALIGN(l+1);
- nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- if (nn == MAP_FAILED)
- return log_debug_errno(errno, "mmap() failed: %m");
-
- strncpy(nn, name, nn_size);
-
- /* Now, let's tell the kernel about this new memory */
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
- if (ERRNO_IS_PRIVILEGE(errno))
- return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
-
- /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
- * below the desired start address, in which case the kernel may have kicked this back due
- * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
- * action). The proper solution would be to have a prctl() API that could set both start+end
- * simultaneously, or at least let us query the existing address to anticipate this condition
- * and respond accordingly. For now, we can only guess at the cause of this failure and try
- * a workaround--which will briefly expand the arg space to something potentially huge before
- * resizing it to what we want. */
- log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
-
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
- r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
- (void) munmap(nn, nn_size);
- return r;
- }
-
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
- return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
- } else {
- /* And update the end pointer to the new end, too. If this fails, we don't really know what
- * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
- * and continue. */
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
- log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
- }
-
- if (mm)
- (void) munmap(mm, mm_size);
-
- mm = nn;
- mm_size = nn_size;
- } else {
- strncpy(mm, name, mm_size);
-
- /* Update the end pointer, continuing regardless of any failure. */
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
- log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
- }
-
- can_do = true;
- return 0;
-}
-
-int rename_process(const char name[]) {
- bool truncated = false;
-
- /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
- * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
- * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
- * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
- * truncated.
- *
- * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
-
- if (isempty(name))
- return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
-
- if (!is_main_thread())
- return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
- * cache things without locking, and we make assumptions that PR_SET_NAME sets the
- * process name that isn't correct on any other threads */
-
- size_t l = strlen(name);
-
- /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
- * can use PR_SET_NAME, which sets the thread name for the calling thread. */
- if (prctl(PR_SET_NAME, name) < 0)
- log_debug_errno(errno, "PR_SET_NAME failed: %m");
- if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
- truncated = true;
-
- /* Second step, change glibc's ID of the process name. */
- if (program_invocation_name) {
- size_t k;
-
- k = strlen(program_invocation_name);
- strncpy(program_invocation_name, name, k);
- if (l > k)
- truncated = true;
- }
-
- /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
- * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
- * the end. This is the best option for changing /proc/self/cmdline. */
- (void) update_argv(name, l);
-
- /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
- * it still looks here */
- if (saved_argc > 0) {
- if (saved_argv[0]) {
- size_t k;
-
- k = strlen(saved_argv[0]);
- strncpy(saved_argv[0], name, k);
- if (l > k)
- truncated = true;
- }
-
- for (int i = 1; i < saved_argc; i++) {
- if (!saved_argv[i])
- break;
-
- memzero(saved_argv[i], strlen(saved_argv[i]));
- }
- }
-
- return !truncated;
-}
-
int is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL;
unsigned long long flags;
return 0;
}
-bool invoked_as(char *argv[], const char *token) {
- if (!argv || isempty(argv[0]))
- return false;
-
- if (isempty(token))
- return false;
-
- return strstr(last_path_component(argv[0]), token);
-}
-
-bool invoked_by_systemd(void) {
- int r;
-
- /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
- * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
- const char *e = getenv("SYSTEMD_EXEC_PID");
- if (!e)
- return false;
-
- if (streq(e, "*"))
- /* For testing. */
- return true;
-
- pid_t p;
- r = parse_pid(e, &p);
- if (r < 0) {
- /* We know that systemd sets the variable correctly. Something else must have set it. */
- log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
- return false;
- }
-
- return getpid_cached() == p;
-}
-
_noreturn_ void freeze(void) {
log_close();
pause();
}
-bool argv_looks_like_help(int argc, char **argv) {
- char **l;
-
- /* Scans the command line for indications the user asks for help. This is supposed to be called by
- * tools that do not implement getopt() style command line parsing because they are not primarily
- * user-facing. Detects four ways of asking for help:
- *
- * 1. Passing zero arguments
- * 2. Passing "help" as first argument
- * 3. Passing --help as any argument
- * 4. Passing -h as any argument
- */
-
- if (argc <= 1)
- return true;
-
- if (streq_ptr(argv[1], "help"))
- return true;
-
- l = strv_skip(argv, 1);
-
- return strv_contains(l, "--help") ||
- strv_contains(l, "-h");
-}
-
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
[CLD_KILLED] = "killed",
int kill_and_sigcont(pid_t pid, int sig);
-int rename_process(const char name[]);
int is_kernel_thread(pid_t pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
int setpriority_closest(int priority);
-bool invoked_as(char *argv[], const char *token);
-
-bool invoked_by_systemd(void);
-
_noreturn_ void freeze(void);
-
-bool argv_looks_like_help(int argc, char **argv);
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "alloc-util.h"
-#include "build.h"
-#include "env-file.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "hostname-util.h"
-#include "log.h"
-#include "macro.h"
-#include "parse-util.h"
-#include "stat-util.h"
-#include "string-util.h"
-#include "util.h"
-#include "virt.h"
-
-int saved_argc = 0;
-char **saved_argv = NULL;
#include "macro.h"
-extern int saved_argc;
-extern char **saved_argv;
-
-static inline void save_argc_argv(int argc, char **argv) {
-
- /* Protect against CVE-2021-4034 style attacks */
- assert_se(argc > 0);
- assert_se(argv);
- assert_se(argv[0]);
-
- saved_argc = argc;
- saved_argv = argv;
-}
-
/* Note: log2(0) == log2(1) == 0 here and below. */
#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
#if HAVE_APPARMOR
#include "apparmor-util.h"
#endif
+#include "argv-util.h"
#include "async.h"
#include "barrier.h"
#include "bpf-lsm.h"
#include "alloc-util.h"
#include "apparmor-setup.h"
#include "architecture.h"
+#include "argv-util.h"
#if HAVE_LIBBPF
#include "bpf-lsm.h"
#endif
#include <sys/prctl.h>
#include <unistd.h>
+#include "argv-util.h"
#include "def.h"
#include "exit-status.h"
#include "fd-util.h"
#include "sd-daemon.h"
+#include "argv-util.h"
#include "pager.h"
#include "selinux-util.h"
#include "spawn-ask-password-agent.h"
#include "spawn-polkit-agent.h"
#include "static-destruct.h"
-#include "util.h"
#define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \
int main(int argc, char *argv[]) { \
#include "sd-daemon.h"
+#include "argv-util.h"
#include "macro.h"
#include "static-destruct.h"
-#include "util.h"
static inline bool manager_errno_skip_test(int r) {
return IN_SET(abs(r),
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "argv-util.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "parse-util.h"
############################################################
tests += [
+ [files('test-argv-util.c')],
+
[files('test-device-nodes.c')],
[files('test-ether-addr-util.c')],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sched.h>
+#include <unistd.h>
+
+#if HAVE_VALGRIND_VALGRIND_H
+# include <valgrind/valgrind.h>
+#endif
+
+#include "argv-util.h"
+#include "missing_sched.h"
+#include "process-util.h"
+#include "tests.h"
+#include "virt.h"
+
+static void test_rename_process_now(const char *p, int ret) {
+ _cleanup_free_ char *comm = NULL, *cmdline = NULL;
+ int r;
+
+ log_info("/* %s(%s) */", __func__, p);
+
+ r = rename_process(p);
+ assert_se(r == ret ||
+ (ret == 0 && r >= 0) ||
+ (ret > 0 && r > 0));
+
+ log_debug_errno(r, "rename_process(%s): %m", p);
+
+ if (r < 0)
+ return;
+
+#if HAVE_VALGRIND_VALGRIND_H
+ /* see above, valgrind is weird, we can't verify what we are doing here */
+ if (RUNNING_ON_VALGRIND)
+ return;
+#endif
+
+ assert_se(get_process_comm(0, &comm) >= 0);
+ log_debug("comm = <%s>", comm);
+ assert_se(strneq(comm, p, TASK_COMM_LEN-1));
+ /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
+ * future. We'd only check the initial part, at least until we recompile, but this will still pass. */
+
+ r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline);
+ assert_se(r >= 0);
+ /* we cannot expect cmdline to be renamed properly without privileges */
+ if (geteuid() == 0) {
+ if (r == 0 && detect_container() > 0)
+ log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline);
+ else {
+ log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p);
+
+ bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */
+
+ assert_se(strneq(cmdline + skip, p, strlen("test-process-util")));
+ assert_se(startswith(cmdline + skip, p));
+ }
+ } 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;
+
+ log_info("/* %s(%s) */", __func__, p);
+
+ 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);
+}
+
+TEST(rename_process_invalid) {
+ assert_se(rename_process(NULL) == -EINVAL);
+ assert_se(rename_process("") == -EINVAL);
+}
+
+TEST(rename_process_multi) {
+ 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;
+ }
+
+ /* 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 */
+ _exit(EXIT_SUCCESS);
+}
+
+TEST(rename_process) {
+ 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 */
+}
+
+static int intro(void) {
+ log_show_color(true);
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
#include "macro.h"
#include "module-util.h"
#include "tests.h"
-#include "util.h"
static int load_module(const char *mod_name) {
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
_exit(EXIT_SUCCESS);
}
-static void test_rename_process_now(const char *p, int ret) {
- _cleanup_free_ char *comm = NULL, *cmdline = NULL;
- int r;
-
- log_info("/* %s(%s) */", __func__, p);
-
- r = rename_process(p);
- assert_se(r == ret ||
- (ret == 0 && r >= 0) ||
- (ret > 0 && r > 0));
-
- log_debug_errno(r, "rename_process(%s): %m", p);
-
- if (r < 0)
- return;
-
-#if HAVE_VALGRIND_VALGRIND_H
- /* see above, valgrind is weird, we can't verify what we are doing here */
- if (RUNNING_ON_VALGRIND)
- return;
-#endif
-
- assert_se(get_process_comm(0, &comm) >= 0);
- log_debug("comm = <%s>", comm);
- assert_se(strneq(comm, p, TASK_COMM_LEN-1));
- /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
- * future. We'd only check the initial part, at least until we recompile, but this will still pass. */
-
- r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline);
- assert_se(r >= 0);
- /* we cannot expect cmdline to be renamed properly without privileges */
- if (geteuid() == 0) {
- if (r == 0 && detect_container() > 0)
- log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline);
- else {
- log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p);
-
- bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */
-
- assert_se(strneq(cmdline + skip, p, strlen("test-process-util")));
- assert_se(startswith(cmdline + skip, p));
- }
- } 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;
-
- log_info("/* %s(%s) */", __func__, p);
-
- 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);
-}
-
-TEST(rename_process_invalid) {
- assert_se(rename_process(NULL) == -EINVAL);
- assert_se(rename_process("") == -EINVAL);
-}
-
-TEST(rename_process_multi) {
- 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;
- }
-
- /* 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 */
- _exit(EXIT_SUCCESS);
-}
-
-TEST(rename_process) {
- 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(getpid_cached) {
siginfo_t si;
pid_t a, b, c, d, e, f, child;