summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/process-util.c102
-rw-r--r--src/basic/process-util.h2
2 files changed, 90 insertions, 14 deletions
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 1f4c2e4e43..d5e7edb589 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/types.h>
@@ -274,27 +275,100 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
return 0;
}
-void rename_process(const char name[8]) {
- assert(name);
+int rename_process(const char name[]) {
+ static size_t mm_size = 0;
+ static char *mm = NULL;
+ bool truncated = false;
+ size_t l;
+
+ /* 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 */
- /* 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 usually one of 10 (i.e. length
- * of "/sbin/init"), to the third one one of 7 (i.e. length of
- * "systemd"). If you pass a longer string it will be
- * truncated */
+ l = strlen(name);
+ /* First step, change the comm field. */
(void) prctl(PR_SET_NAME, name);
+ if (l > 15) /* Linux 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.*/
+ if (mm_size < l+1) {
+ size_t nn_size;
+ char *nn;
+
+ /* Let's not bother with this if we don't have euid == 0. Strictly speaking if people do weird stuff
+ * with capabilities this could work even for euid != 0, but our own code generally doesn't do that,
+ * hence let's use this as quick bypass check, to avoid calling mmap() if PR_SET_MM_ARG_START fails
+ * with EPERM later on anyway. After all geteuid() is dead cheap to call, but mmap() is not. */
+ if (geteuid() != 0) {
+ log_debug("Skipping PR_SET_MM_ARG_START, as we don't have privileges.");
+ goto use_saved_argv;
+ }
+
+ 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) {
+ log_debug_errno(errno, "mmap() failed: %m");
+ goto use_saved_argv;
+ }
+
+ 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) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m");
+ (void) munmap(nn, nn_size);
+ goto use_saved_argv;
+ }
+
+ /* 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 (program_invocation_name)
- strncpy(program_invocation_name, name, strlen(program_invocation_name));
+ if (mm)
+ (void) munmap(mm, mm_size);
+
+ mm = nn;
+ mm_size = nn_size;
+ } else
+ strncpy(mm, name, mm_size);
+
+use_saved_argv:
+ /* 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) {
int i;
- if (saved_argv[0])
- strncpy(saved_argv[0], name, strlen(saved_argv[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 (i = 1; i < saved_argc; i++) {
if (!saved_argv[i])
@@ -303,6 +377,8 @@ void rename_process(const char name[8]) {
memzero(saved_argv[i], strlen(saved_argv[i]));
}
}
+
+ return !truncated;
}
int is_kernel_thread(pid_t pid) {
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 89dfeb4d6a..d378901399 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -64,7 +64,7 @@ void sigkill_waitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
-void rename_process(const char name[8]);
+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);