chiark / gitweb /
util: no need for in_initrd() cache to be thread-local
[elogind.git] / src / shared / util.c
index 2419a76d9887eb71be3578b66eadc754babbcf33..80dbf73c947d4f501503b2cc3626f28c8afff289 100644 (file)
@@ -41,7 +41,6 @@
 #include <stdarg.h>
 #include <sys/inotify.h>
 #include <sys/poll.h>
-#include <libgen.h>
 #include <ctype.h>
 #include <sys/prctl.h>
 #include <sys/utsname.h>
@@ -60,6 +59,7 @@
 #include <langinfo.h>
 #include <locale.h>
 #include <libgen.h>
+#undef basename
 
 #include "macro.h"
 #include "util.h"
@@ -77,6 +77,7 @@
 #include "utf8.h"
 #include "gunicode.h"
 #include "virt.h"
+#include "def.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -85,7 +86,7 @@ static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
 
 size_t page_size(void) {
-        static __thread size_t pgsz = 0;
+        static thread_local size_t pgsz = 0;
         long r;
 
         if (_likely_(pgsz > 0))
@@ -373,17 +374,21 @@ char *split(const char *c, size_t *l, const char *separator, char **state) {
 /* Split a string into words, but consider strings enclosed in '' and
  * "" as words even if they include spaces. */
 char *split_quoted(const char *c, size_t *l, char **state) {
-        char *current, *e;
+        const char *current, *e;
         bool escaped = false;
 
-        current = *state ? *state : (char*) c;
+        assert(c);
+        assert(l);
+        assert(state);
 
-        if (!*current || *c == 0)
-                return NULL;
+        current = *state ? *state : c;
 
         current += strspn(current, WHITESPACE);
 
-        if (*current == '\'') {
+        if (*current == 0)
+                return NULL;
+
+        else if (*current == '\'') {
                 current ++;
 
                 for (e = current; *e; e++) {
@@ -396,7 +401,8 @@ char *split_quoted(const char *c, size_t *l, char **state) {
                 }
 
                 *l = e-current;
-                *state = *e == 0 ? e : e+1;
+                *state = (char*) (*e == 0 ? e : e+1);
+
         } else if (*current == '\"') {
                 current ++;
 
@@ -410,7 +416,8 @@ char *split_quoted(const char *c, size_t *l, char **state) {
                 }
 
                 *l = e-current;
-                *state = *e == 0 ? e : e+1;
+                *state = (char*) (*e == 0 ? e : e+1);
+
         } else {
                 for (e = current; *e; e++) {
                         if (escaped)
@@ -421,7 +428,7 @@ char *split_quoted(const char *c, size_t *l, char **state) {
                                 break;
                 }
                 *l = e-current;
-                *state = e;
+                *state = (char*) e;
         }
 
         return (char*) current;
@@ -491,7 +498,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
 
         f = fopen(p, "re");
         if (!f)
-                return -errno;
+                return errno == ENOENT ? -ESRCH : -errno;
 
         if (!fgets(line, sizeof(line), f)) {
                 if (ferror(f))
@@ -557,6 +564,7 @@ char *truncate_nl(char *s) {
 
 int get_process_comm(pid_t pid, char **name) {
         const char *p;
+        int r;
 
         assert(name);
         assert(pid >= 0);
@@ -566,7 +574,11 @@ int get_process_comm(pid_t pid, char **name) {
         else
                 p = procfs_file_alloca(pid, "comm");
 
-        return read_one_line_file(p, name);
+        r = read_one_line_file(p, name);
+        if (r == -ENOENT)
+                return -ESRCH;
+
+        return r;
 }
 
 int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
@@ -723,7 +735,7 @@ int get_process_exe(pid_t pid, char **name) {
 
         r = readlink_malloc(p, name);
         if (r < 0)
-                return r;
+                return r == -ENOENT ? -ESRCH : r;
 
         d = endswith(*name, " (deleted)");
         if (d)
@@ -1356,78 +1368,6 @@ char *xescape(const char *s, const char *bad) {
         return r;
 }
 
-char *bus_path_escape(const char *s) {
-        char *r, *t;
-        const char *f;
-
-        assert(s);
-
-        /* Escapes all chars that D-Bus' object path cannot deal
-         * with. Can be reversed with bus_path_unescape(). We special
-         * case the empty string. */
-
-        if (*s == 0)
-                return strdup("_");
-
-        r = new(char, strlen(s)*3 + 1);
-        if (!r)
-                return NULL;
-
-        for (f = s, t = r; *f; f++) {
-
-                /* Escape everything that is not a-zA-Z0-9. We also
-                 * escape 0-9 if it's the first character */
-
-                if (!(*f >= 'A' && *f <= 'Z') &&
-                    !(*f >= 'a' && *f <= 'z') &&
-                    !(f > s && *f >= '0' && *f <= '9')) {
-                        *(t++) = '_';
-                        *(t++) = hexchar(*f >> 4);
-                        *(t++) = hexchar(*f);
-                } else
-                        *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return r;
-}
-
-char *bus_path_unescape(const char *f) {
-        char *r, *t;
-
-        assert(f);
-
-        /* Special case for the empty string */
-        if (streq(f, "_"))
-                return strdup("");
-
-        r = new(char, strlen(f) + 1);
-        if (!r)
-                return NULL;
-
-        for (t = r; *f; f++) {
-
-                if (*f == '_') {
-                        int a, b;
-
-                        if ((a = unhexchar(f[1])) < 0 ||
-                            (b = unhexchar(f[2])) < 0) {
-                                /* Invalid escape code, let's take it literal then */
-                                *(t++) = '_';
-                        } else {
-                                *(t++) = (char) ((a << 4) | b);
-                                f += 2;
-                        }
-                } else
-                        *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return r;
-}
-
 char *ascii_strlower(char *t) {
         char *p;
 
@@ -2562,7 +2502,7 @@ int getttyname_malloc(int fd, char **r) {
         assert(r);
 
         k = ttyname_r(fd, path, sizeof(path));
-        if (k != 0)
+        if (k > 0)
                 return -k;
 
         char_array_0(path);
@@ -2597,10 +2537,8 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
         char line[LINE_MAX], *p;
         unsigned long ttynr;
         const char *fn;
-        int k;
 
         assert(pid >= 0);
-        assert(d);
 
         if (pid == 0)
                 fn = "/proc/self/stat";
@@ -2611,10 +2549,8 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
         if (!f)
                 return -errno;
 
-        if (!fgets(line, sizeof(line), f)) {
-                k = feof(f) ? -EIO : -errno;
-                return k;
-        }
+        if (!fgets(line, sizeof(line), f))
+                return feof(f) ? -EIO : -errno;
 
         p = strrchr(line, ')');
         if (!p)
@@ -2634,7 +2570,9 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
         if (major(ttynr) == 0 && minor(ttynr) == 0)
                 return -ENOENT;
 
-        *d = (dev_t) ttynr;
+        if (d)
+                *d = (dev_t) ttynr;
+
         return 0;
 }
 
@@ -2799,9 +2737,9 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
 
 _pure_ static int is_temporary_fs(struct statfs *s) {
         assert(s);
-        return
-                F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
-                F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
+
+        return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+               F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
 }
 
 int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
@@ -2924,11 +2862,13 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
          * first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
-        if (fchmod(fd, mode) < 0)
-                return -errno;
+        if (mode != (mode_t) -1)
+                if (fchmod(fd, mode) < 0)
+                        return -errno;
 
-        if (fchown(fd, uid, gid) < 0)
-                return -errno;
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (fchown(fd, uid, gid) < 0)
+                        return -errno;
 
         return 0;
 }
@@ -3040,13 +2980,14 @@ int status_printf(const char *status, bool ellipse, bool ephemeral, const char *
 }
 
 int status_welcome(void) {
-        int r;
         _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
+        int r;
 
         r = parse_env_file("/etc/os-release", NEWLINE,
                            "PRETTY_NAME", &pretty_name,
                            "ANSI_COLOR", &ansi_color,
                            NULL);
+
         if (r < 0 && r != -ENOENT)
                 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
 
@@ -3696,12 +3637,21 @@ char *resolve_dev_console(char **active) {
         else
                 tty = *active;
 
+        if (streq(tty, "tty0")) {
+                char *tmp;
+
+                /* Get the active VC (e.g. tty1) */
+                if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+                        free(*active);
+                        tty = *active = tmp;
+                }
+        }
+
         return tty;
 }
 
 bool tty_is_vc_resolve(const char *tty) {
-        char *active = NULL;
-        bool b;
+        _cleanup_free_ char *active = NULL;
 
         assert(tty);
 
@@ -3714,10 +3664,7 @@ bool tty_is_vc_resolve(const char *tty) {
                         return false;
         }
 
-        b = tty_is_vc(tty);
-        free(active);
-
-        return b;
+        return tty_is_vc(tty);
 }
 
 const char *default_term_for_tty(const char *tty) {
@@ -4015,8 +3962,8 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         if (!t)
                 return -ENOMEM;
 
-        fn = path_get_file_name(path);
-        k = fn-path;
+        fn = basename(path);
+        k = fn - path;
         memcpy(t, path, k);
         t[k] = '.';
         stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
@@ -4201,7 +4148,7 @@ int symlink_atomic(const char *from, const char *to) {
         if (!t)
                 return -ENOMEM;
 
-        fn = path_get_file_name(to);
+        fn = basename(to);
         k = fn-to;
         memcpy(t, to, k);
         t[k] = '.';
@@ -4633,7 +4580,7 @@ char *strjoin(const char *x, ...) {
 }
 
 bool is_main_thread(void) {
-        static __thread int cached = 0;
+        static thread_local int cached = 0;
 
         if (_unlikely_(cached == 0))
                 cached = getpid() == gettid() ? 1 : -1;
@@ -4851,7 +4798,7 @@ static const char *const __signal_table[] = {
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
 
 const char *signal_to_string(int signo) {
-        static __thread char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+        static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
         const char *name;
 
         name = __signal_to_string(signo);
@@ -4989,15 +4936,15 @@ int fd_inc_sndbuf(int fd, size_t n) {
         socklen_t l = sizeof(value);
 
         r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
-        if (r >= 0 &&
-            l == sizeof(value) &&
-            (size_t) value >= n*2)
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
                 return 0;
 
+        /* If we have the privileges we will ignore the kernel limit. */
+
         value = (int) n;
-        r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
-        if (r < 0)
-                return -errno;
+        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+                        return -errno;
 
         return 1;
 }
@@ -5007,16 +4954,15 @@ int fd_inc_rcvbuf(int fd, size_t n) {
         socklen_t l = sizeof(value);
 
         r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
-        if (r >= 0 &&
-            l == sizeof(value) &&
-            (size_t) value >= n*2)
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
                 return 0;
 
-        value = (int) n;
-        r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
-        if (r < 0)
-                return -errno;
+        /* If we have the privileges we will ignore the kernel limit. */
 
+        value = (int) n;
+        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+                        return -errno;
         return 1;
 }
 
@@ -5208,7 +5154,7 @@ bool is_valid_documentation_url(const char *url) {
 }
 
 bool in_initrd(void) {
-        static __thread int saved = -1;
+        static int saved = -1;
         struct statfs s;
 
         if (saved >= 0)
@@ -5273,10 +5219,10 @@ int make_console_stdio(void) {
 }
 
 int get_home_dir(char **_h) {
-        char *h;
+        struct passwd *p;
         const char *e;
+        char *h;
         uid_t u;
-        struct passwd *p;
 
         assert(_h);
 
@@ -5319,6 +5265,53 @@ int get_home_dir(char **_h) {
         return 0;
 }
 
+int get_shell(char **_s) {
+        struct passwd *p;
+        const char *e;
+        char *s;
+        uid_t u;
+
+        assert(_s);
+
+        /* Take the user specified one */
+        e = getenv("SHELL");
+        if (e) {
+                s = strdup(e);
+                if (!s)
+                        return -ENOMEM;
+
+                *_s = s;
+                return 0;
+        }
+
+        /* Hardcode home directory for root to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                s = strdup("/bin/sh");
+                if (!s)
+                        return -ENOMEM;
+
+                *_s = s;
+                return 0;
+        }
+
+        /* Check the database... */
+        errno = 0;
+        p = getpwuid(u);
+        if (!p)
+                return errno > 0 ? -errno : -ESRCH;
+
+        if (!path_is_absolute(p->pw_shell))
+                return -EINVAL;
+
+        s = strdup(p->pw_shell);
+        if (!s)
+                return -ENOMEM;
+
+        *_s = s;
+        return 0;
+}
+
 bool filename_is_safe(const char *p) {
 
         if (isempty(p))
@@ -5433,7 +5426,7 @@ bool is_locale_utf8(void) {
                 goto out;
         }
 
-        if(streq(set, "UTF-8")) {
+        if (streq(set, "UTF-8")) {
                 cached_answer = true;
                 goto out;
         }
@@ -5466,6 +5459,7 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TREE_RIGHT]         = "\342\224\224\342\224\200", /* └─ */
                         [DRAW_TREE_SPACE]         = "  ",                       /*    */
                         [DRAW_TRIANGULAR_BULLET]  = "\342\200\243 ",            /* ‣  */
+                        [DRAW_BLACK_CIRCLE]       = "\342\227\217 ",            /* ●  */
                 },
                 /* ASCII fallback */ {
                         [DRAW_TREE_VERT]          = "| ",
@@ -5473,6 +5467,7 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TREE_RIGHT]         = "`-",
                         [DRAW_TREE_SPACE]         = "  ",
                         [DRAW_TRIANGULAR_BULLET]  = "> ",
+                        [DRAW_BLACK_CIRCLE]       = "* ",
                 }
         };
 
@@ -5768,56 +5763,6 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *sear
         return search_and_fopen_internal(path, mode, s, _f);
 }
 
-int create_tmp_dir(char template[], char** dir_name) {
-        int r = 0;
-        char *d = NULL, *dt;
-
-        assert(dir_name);
-
-        RUN_WITH_UMASK(0077) {
-                d = mkdtemp(template);
-        }
-        if (!d) {
-                log_error("Can't create directory %s: %m", template);
-                return -errno;
-        }
-
-        dt = strjoin(d, "/tmp", NULL);
-        if (!dt) {
-                r = log_oom();
-                goto fail3;
-        }
-
-        RUN_WITH_UMASK(0000) {
-                r = mkdir(dt, 0777);
-        }
-        if (r < 0) {
-                log_error("Can't create directory %s: %m", dt);
-                r = -errno;
-                goto fail2;
-        }
-        log_debug("Created temporary directory %s", dt);
-
-        r = chmod(dt, 0777 | S_ISVTX);
-        if (r < 0) {
-                log_error("Failed to chmod %s: %m", dt);
-                r = -errno;
-                goto fail1;
-        }
-        log_debug("Set sticky bit on %s", dt);
-
-        *dir_name = dt;
-
-        return 0;
-fail1:
-        rmdir(dt);
-fail2:
-        free(dt);
-fail3:
-        rmdir(template);
-        return r;
-}
-
 char *strextend(char **x, ...) {
         va_list ap;
         size_t f, l;
@@ -5893,10 +5838,18 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
         size_t a;
         void *q;
 
+        assert(p);
+        assert(allocated);
+
         if (*allocated >= need)
                 return *p;
 
         a = MAX(64u, need * 2);
+
+        /* check for overflows */
+        if (a < need)
+                return NULL;
+
         q = realloc(*p, a);
         if (!q)
                 return NULL;
@@ -5906,6 +5859,25 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
         return q;
 }
 
+void* greedy_realloc0(void **p, size_t *allocated, size_t need) {
+        size_t prev;
+        uint8_t *q;
+
+        assert(p);
+        assert(allocated);
+
+        prev = *allocated;
+
+        q = greedy_realloc(p, allocated, need);
+        if (!q)
+                return NULL;
+
+        if (*allocated > prev)
+                memset(&q[prev], 0, *allocated - prev);
+
+        return q;
+}
+
 bool id128_is_valid(const char *s) {
         size_t i, l;
 
@@ -5992,24 +5964,127 @@ int split_pair(const char *s, const char *sep, char **l, char **r) {
         return 0;
 }
 
-bool restore_state(void) {
+int shall_restore_state(void) {
         _cleanup_free_ char *line;
         char *w, *state;
-        int r;
         size_t l;
+        int r;
 
-        if (detect_container(NULL) > 0)
-                return true;
+        r = proc_cmdline(&line);
+        if (r < 0)
+                return r;
+        if (r == 0) /* Container ... */
+                return 1;
 
-        r = read_one_line_file("/proc/cmdline", &line);
-        if (r < 0) {
-                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+        FOREACH_WORD_QUOTED(w, l, line, state)
+                if (l == 23 && strneq(w, "systemd.restore_state=0", 23))
+                        return 0;
+
+        return 1;
+}
+
+int proc_cmdline(char **ret) {
+        int r;
+
+        if (detect_container(NULL) > 0) {
+                *ret = NULL;
                 return 0;
         }
 
-        FOREACH_WORD_QUOTED(w, l, line, state)
-                if (strneq(w, "systemd.restore_state=0", l))
-                        return false;
+        r = read_one_line_file("/proc/cmdline", ret);
+        if (r < 0)
+                return r;
 
-        return true;
+        return 1;
+}
+
+int container_get_leader(const char *machine, pid_t *pid) {
+        _cleanup_free_ char *s = NULL, *class = NULL;
+        const char *p;
+        pid_t leader;
+        int r;
+
+        assert(machine);
+        assert(pid);
+
+        p = strappenda("/run/systemd/machines/", machine);
+        r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
+        if (r == -ENOENT)
+                return -EHOSTDOWN;
+        if (r < 0)
+                return r;
+        if (!s)
+                return -EIO;
+
+        if (!streq_ptr(class, "container"))
+                return -EIO;
+
+        r = parse_pid(s, &leader);
+        if (r < 0)
+                return r;
+        if (leader <= 1)
+                return -EIO;
+
+        *pid = leader;
+        return 0;
+}
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *root_fd) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1;
+        const char *pidns, *mntns, *root;
+        int rfd;
+
+        assert(pid >= 0);
+        assert(pidns_fd);
+        assert(mntns_fd);
+        assert(root_fd);
+
+        mntns = procfs_file_alloca(pid, "ns/mnt");
+        mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        if (mntnsfd < 0)
+                return -errno;
+
+        pidns = procfs_file_alloca(pid, "ns/pid");
+        pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        if (pidnsfd < 0)
+                return -errno;
+
+        root = procfs_file_alloca(pid, "root");
+        rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        if (rfd < 0)
+                return -errno;
+
+        *pidns_fd = pidnsfd;
+        *mntns_fd = mntnsfd;
+        *root_fd = rfd;
+        pidnsfd = -1;
+        mntnsfd = -1;
+
+        return 0;
+}
+
+int namespace_enter(int pidns_fd, int mntns_fd, int root_fd) {
+        assert(pidns_fd >= 0);
+        assert(mntns_fd >= 0);
+        assert(root_fd >= 0);
+
+        if (setns(pidns_fd, CLONE_NEWPID) < 0)
+                return -errno;
+
+        if (setns(mntns_fd, CLONE_NEWNS) < 0)
+                return -errno;
+
+        if (fchdir(root_fd) < 0)
+                return -errno;
+
+        if (chroot(".") < 0)
+                return -errno;
+
+        if (setresgid(0, 0, 0) < 0)
+                return -errno;
+
+        if (setresuid(0, 0, 0) < 0)
+                return -errno;
+
+        return 0;
 }