chiark / gitweb /
Prep v231: Apply missing fixes from upstream (1/6) src/basic
[elogind.git] / src / basic / process-util.c
index 5825944c90414a954f14a3fb4c05de9ff60b55cc..cd9c0f7e5d5155abf9ea63fed1d6b7b292112ec4 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <linux/oom.h>
+#include <sched.h>
+#include <signal.h>
 #include <stdbool.h>
 #include <stdbool.h>
-#include <sys/types.h>
-#include <string.h>
 #include <stdio.h>
 #include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
-#include <signal.h>
-#include <ctype.h>
-
+#include <syslog.h>
+#include <unistd.h>
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+
+#include "alloc-util.h"
+//#include "architecture.h"
+#include "escape.h"
+#include "fd-util.h"
 #include "fileio.h"
 #include "fileio.h"
-#include "util.h"
+#include "fs-util.h"
+//#include "ioprio.h"
 #include "log.h"
 #include "log.h"
-#include "signal-util.h"
+#include "macro.h"
+#include "missing.h"
 #include "process-util.h"
 #include "process-util.h"
+#include "signal-util.h"
+//#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
 
 int get_process_state(pid_t pid) {
         const char *p;
 
 int get_process_state(pid_t pid) {
         const char *p;
@@ -80,6 +101,7 @@ int get_process_comm(pid_t pid, char **name) {
 
 int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
 
 int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
+        bool space = false;
         char *r = NULL, *k;
         const char *p;
         int c;
         char *r = NULL, *k;
         const char *p;
         int c;
@@ -87,6 +109,15 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         assert(line);
         assert(pid >= 0);
 
         assert(line);
         assert(pid >= 0);
 
+        /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
+         * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
+         * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
+         * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
+         * command line that resolves to the empty string will return the "comm" name of the process instead.
+         *
+         * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
+         * comm_fallback is false). */
+
         p = procfs_file_alloca(pid, "cmdline");
 
         f = fopen(p, "re");
         p = procfs_file_alloca(pid, "cmdline");
 
         f = fopen(p, "re");
@@ -96,24 +127,44 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 return -errno;
         }
 
                 return -errno;
         }
 
-        if (max_length == 0) {
+        if (max_length == 1) {
+
+                /* If there's only room for one byte, return the empty string */
+                r = new0(char, 1);
+                if (!r)
+                        return -ENOMEM;
+
+                *line = r;
+                return 0;
+
+        } else if (max_length == 0) {
                 size_t len = 0, allocated = 0;
 
                 while ((c = getc(f)) != EOF) {
 
                 size_t len = 0, allocated = 0;
 
                 while ((c = getc(f)) != EOF) {
 
-                        if (!GREEDY_REALLOC(r, allocated, len+2)) {
+                        if (!GREEDY_REALLOC(r, allocated, len+3)) {
                                 free(r);
                                 return -ENOMEM;
                         }
 
                                 free(r);
                                 return -ENOMEM;
                         }
 
-                        r[len++] = isprint(c) ? c : ' ';
-                }
+                        if (isprint(c)) {
+                                if (space) {
+                                        r[len++] = ' ';
+                                        space = false;
+                                }
+
+                                r[len++] = c;
+                        } else if (len > 0)
+                                space = true;
+               }
 
                 if (len > 0)
 
                 if (len > 0)
-                        r[len-1] = 0;
+                        r[len] = 0;
+                else
+                        r = mfree(r);
 
         } else {
 
         } else {
-                bool space = false;
+                bool dotdotdot = false;
                 size_t left;
 
                 r = new(char, max_length);
                 size_t left;
 
                 r = new(char, max_length);
@@ -125,28 +176,46 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 while ((c = getc(f)) != EOF) {
 
                         if (isprint(c)) {
                 while ((c = getc(f)) != EOF) {
 
                         if (isprint(c)) {
+
                                 if (space) {
                                 if (space) {
-                                        if (left <= 4)
+                                        if (left <= 2) {
+                                                dotdotdot = true;
                                                 break;
                                                 break;
+                                        }
 
                                         *(k++) = ' ';
                                         left--;
                                         space = false;
                                 }
 
 
                                         *(k++) = ' ';
                                         left--;
                                         space = false;
                                 }
 
-                                if (left <= 4)
+                                if (left <= 1) {
+                                        dotdotdot = true;
                                         break;
                                         break;
+                                }
 
                                 *(k++) = (char) c;
                                 left--;
 
                                 *(k++) = (char) c;
                                 left--;
-                        }  else
+                        } else if (k > r)
                                 space = true;
                 }
 
                                 space = true;
                 }
 
-                if (left <= 4) {
-                        size_t n = MIN(left-1, 3U);
-                        memcpy(k, "...", n);
-                        k[n] = 0;
+                if (dotdotdot) {
+                        if (max_length <= 4) {
+                                k = r;
+                                left = max_length;
+                        } else {
+                                k = r + max_length - 4;
+                                left = 4;
+
+                                /* Eat up final spaces */
+                                while (k > r && isspace(k[-1])) {
+                                        k--;
+                                        left++;
+                                }
+                        }
+
+                        strncpy(k, "...", left-1);
+                        k[left-1] = 0;
                 } else
                         *k = 0;
         }
                 } else
                         *k = 0;
         }
@@ -165,7 +234,37 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 if (h < 0)
                         return h;
 
                 if (h < 0)
                         return h;
 
-                r = strjoin("[", t, "]", NULL);
+                if (max_length == 0)
+                        r = strjoin("[", t, "]", NULL);
+                else {
+                        size_t l;
+
+                        l = strlen(t);
+
+                        if (l + 3 <= max_length)
+                                r = strjoin("[", t, "]", NULL);
+                        else if (max_length <= 6) {
+
+                                r = new(char, max_length);
+                                if (!r)
+                                        return -ENOMEM;
+
+                                memcpy(r, "[...]", max_length-1);
+                                r[max_length-1] = 0;
+                        } else {
+                                char *e;
+
+                                t[max_length - 6] = 0;
+
+                                /* Chop off final spaces */
+                                e = strchr(t, 0);
+                                while (e > t && isspace(e[-1]))
+                                        e--;
+                                *e = 0;
+
+                                r = strjoin("[", t, "...]", NULL);
+                        }
+                }
                 if (!r)
                         return -ENOMEM;
         }
                 if (!r)
                         return -ENOMEM;
         }
@@ -174,6 +273,39 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         return 0;
 }
 
         return 0;
 }
 
+#if 0 /// UNNEEDED by elogind
+void rename_process(const char name[8]) {
+        assert(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 */
+
+        (void) prctl(PR_SET_NAME, name);
+
+        if (program_invocation_name)
+                strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+        if (saved_argc > 0) {
+                int i;
+
+                if (saved_argv[0])
+                        strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+                for (i = 1; i < saved_argc; i++) {
+                        if (!saved_argv[i])
+                                break;
+
+                        memzero(saved_argv[i], strlen(saved_argv[i]));
+                }
+        }
+}
+#endif // 0
+
 int is_kernel_thread(pid_t pid) {
         const char *p;
         size_t count;
 int is_kernel_thread(pid_t pid) {
         const char *p;
         size_t count;
@@ -206,8 +338,7 @@ int is_kernel_thread(pid_t pid) {
         return 0;
 }
 
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
 int get_process_capeff(pid_t pid, char **capeff) {
         const char *p;
         int r;
 int get_process_capeff(pid_t pid, char **capeff) {
         const char *p;
         int r;
@@ -259,8 +390,7 @@ int get_process_exe(pid_t pid, char **name) {
         return 0;
 }
 
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
 static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
 static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
@@ -269,9 +399,6 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
         assert(field);
         assert(uid);
 
         assert(field);
         assert(uid);
 
-        if (pid == 0)
-                return getuid();
-
         p = procfs_file_alloca(pid, "status");
         f = fopen(p, "re");
         if (!f) {
         p = procfs_file_alloca(pid, "status");
         f = fopen(p, "re");
         if (!f) {
@@ -361,7 +488,7 @@ int get_process_environ(pid_t pid, char **env) {
                 if (!outcome)
                         return -ENOMEM;
         } else
                 if (!outcome)
                         return -ENOMEM;
         } else
-        outcome[sz] = '\0';
+                outcome[sz] = '\0';
 
         *env = outcome;
         outcome = NULL;
 
         *env = outcome;
         outcome = NULL;
@@ -369,7 +496,7 @@ int get_process_environ(pid_t pid, char **env) {
         return 0;
 }
 
         return 0;
 }
 
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+int get_process_ppid(pid_t pid, pid_t *_ppid) {
         int r;
         _cleanup_free_ char *line = NULL;
         long unsigned ppid;
         int r;
         _cleanup_free_ char *line = NULL;
         long unsigned ppid;
@@ -431,7 +558,7 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
                         if (errno == EINTR)
                                 continue;
 
                         if (errno == EINTR)
                                 continue;
 
-                        return -errno;
+                        return negative_errno();
                 }
 
                 return 0;
                 }
 
                 return 0;
@@ -482,15 +609,32 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod
         return -EPROTO;
 }
 
         return -EPROTO;
 }
 
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
+void sigkill_wait(pid_t pid) {
+        assert(pid > 1);
+
+        if (kill(pid, SIGKILL) > 0)
+                (void) wait_for_terminate(pid, NULL);
+}
+
+void sigkill_waitp(pid_t *pid) {
+        if (!pid)
+                return;
+        if (*pid <= 1)
+                return;
+
+        sigkill_wait(*pid);
+}
+
 int kill_and_sigcont(pid_t pid, int sig) {
         int r;
 
         r = kill(pid, sig) < 0 ? -errno : 0;
 
 int kill_and_sigcont(pid_t pid, int sig) {
         int r;
 
         r = kill(pid, sig) < 0 ? -errno : 0;
 
-        if (r >= 0)
-                kill(pid, SIGCONT);
+        /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
+         * affected by a process being suspended anyway. */
+        if (r >= 0 && !IN_SET(SIGCONT, SIGKILL))
+                (void) kill(pid, SIGCONT);
 
         return r;
 }
 
         return r;
 }
@@ -585,3 +729,143 @@ bool pid_is_alive(pid_t pid) {
 
         return true;
 }
 
         return true;
 }
+
+#if 0 /// UNNEEDED by elogind
+int pid_from_same_root_fs(pid_t pid) {
+        const char *root;
+
+        if (pid < 0)
+                return 0;
+
+        root = procfs_file_alloca(pid, "root");
+
+        return files_same(root, "/proc/1/root");
+}
+#endif // 0
+
+bool is_main_thread(void) {
+        static thread_local int cached = 0;
+
+        if (_unlikely_(cached == 0))
+                cached = getpid() == gettid() ? 1 : -1;
+
+        return cached > 0;
+}
+
+#if 0 /// UNNEEDED by elogind
+noreturn void freeze(void) {
+
+        log_close();
+
+        /* Make sure nobody waits for us on a socket anymore */
+        close_all_fds(NULL, 0);
+
+        sync();
+
+        for (;;)
+                pause();
+}
+
+bool oom_score_adjust_is_valid(int oa) {
+        return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
+}
+
+unsigned long personality_from_string(const char *p) {
+        int architecture;
+
+        if (!p)
+                return PERSONALITY_INVALID;
+
+        /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just
+         * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for
+         * the same register size. */
+
+        architecture = architecture_from_string(p);
+        if (architecture < 0)
+                return PERSONALITY_INVALID;
+
+        if (architecture == native_architecture())
+                return PER_LINUX;
+#ifdef SECONDARY_ARCHITECTURE
+        if (architecture == SECONDARY_ARCHITECTURE)
+                return PER_LINUX32;
+#endif
+
+        return PERSONALITY_INVALID;
+}
+
+const char* personality_to_string(unsigned long p) {
+        int architecture = _ARCHITECTURE_INVALID;
+
+        if (p == PER_LINUX)
+                architecture = native_architecture();
+#ifdef SECONDARY_ARCHITECTURE
+        else if (p == PER_LINUX32)
+                architecture = SECONDARY_ARCHITECTURE;
+#endif
+
+        if (architecture < 0)
+                return NULL;
+
+        return architecture_to_string(architecture);
+}
+
+void valgrind_summary_hack(void) {
+#ifdef HAVE_VALGRIND_VALGRIND_H
+        if (getpid() == 1 && RUNNING_ON_VALGRIND) {
+                pid_t pid;
+                pid = raw_clone(SIGCHLD);
+                if (pid < 0)
+                        log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+                else if (pid == 0)
+                        exit(EXIT_SUCCESS);
+                else {
+                        log_info("Spawned valgrind helper as PID "PID_FMT".", pid);
+                        (void) wait_for_terminate(pid, NULL);
+                }
+        }
+#endif
+}
+
+int pid_compare_func(const void *a, const void *b) {
+        const pid_t *p = a, *q = b;
+
+        /* Suitable for usage in qsort() */
+
+        if (*p < *q)
+                return -1;
+        if (*p > *q)
+                return 1;
+        return 0;
+}
+
+static const char *const ioprio_class_table[] = {
+        [IOPRIO_CLASS_NONE] = "none",
+        [IOPRIO_CLASS_RT] = "realtime",
+        [IOPRIO_CLASS_BE] = "best-effort",
+        [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
+
+static const char *const sigchld_code_table[] = {
+        [CLD_EXITED] = "exited",
+        [CLD_KILLED] = "killed",
+        [CLD_DUMPED] = "dumped",
+        [CLD_TRAPPED] = "trapped",
+        [CLD_STOPPED] = "stopped",
+        [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char* const sched_policy_table[] = {
+        [SCHED_OTHER] = "other",
+        [SCHED_BATCH] = "batch",
+        [SCHED_IDLE] = "idle",
+        [SCHED_FIFO] = "fifo",
+        [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
+#endif // 0