about summary refs log tree commit diff
path: root/shared/systemd/src/basic/process-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/systemd/src/basic/process-util.c')
-rw-r--r--shared/systemd/src/basic/process-util.c151
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);
                 }