diff options
Diffstat (limited to 'shared/systemd/src/basic/process-util.c')
| -rw-r--r-- | shared/systemd/src/basic/process-util.c | 151 |
1 files changed, 89 insertions, 62 deletions
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 2de3541c..6dcb26fb 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -212,50 +212,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } -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 */ - - 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 */ - - 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; +static int update_argv(const char name[], size_t l) { + static int can_do = -1; - /* 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 (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 @@ -263,22 +225,29 @@ int rename_process(const char name[]) { * 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) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { + 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) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } + 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 @@ -290,15 +259,13 @@ int rename_process(const char name[]) { 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) { - log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); (void) munmap(nn, nn_size); - goto use_saved_argv; + return r; } - 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 still failed, proceeding without: %m"); - goto use_saved_argv; - } + 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, @@ -320,13 +287,56 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } -use_saved_argv: + 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) { - int i; - if (saved_argv[0]) { size_t k; @@ -336,7 +346,7 @@ use_saved_argv: truncated = true; } - for (i = 1; i < saved_argc; i++) { + for (int i = 1; i < saved_argc; i++) { if (!saved_argv[i]) break; @@ -634,6 +644,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) { return 0; } +int get_process_umask(pid_t pid, mode_t *umask) { + _cleanup_free_ char *m = NULL; + const char *p; + int r; + + assert(umask); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Umask", WHITESPACE, &m); + if (r == -ENOENT) + return -ESRCH; + + return parse_mode(m, umask); +} + int wait_for_terminate(pid_t pid, siginfo_t *status) { siginfo_t dummy; @@ -1284,8 +1311,8 @@ int safe_fork_full( r, "Failed to rename process, ignoring: %m"); } - if (flags & FORK_DEATHSIG) - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT)) + if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) { log_full_errno(prio, errno, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } |