chiark / gitweb /
unit-name: fix escaping logic in unit_name_mangle_with_suffix()
[elogind.git] / src / shared / util.c
index 7d94a28302f7d49d6e54d0c66a06e84cb793e2aa..e987abc423bec6a13df0a624dedb5c7e6ec98e31 100644 (file)
@@ -174,6 +174,69 @@ char* first_word(const char *s, const char *word) {
         return (char*) p;
 }
 
+static size_t cescape_char(char c, char *buf) {
+        char * buf_old = buf;
+
+        switch (c) {
+
+                case '\a':
+                        *(buf++) = '\\';
+                        *(buf++) = 'a';
+                        break;
+                case '\b':
+                        *(buf++) = '\\';
+                        *(buf++) = 'b';
+                        break;
+                case '\f':
+                        *(buf++) = '\\';
+                        *(buf++) = 'f';
+                        break;
+                case '\n':
+                        *(buf++) = '\\';
+                        *(buf++) = 'n';
+                        break;
+                case '\r':
+                        *(buf++) = '\\';
+                        *(buf++) = 'r';
+                        break;
+                case '\t':
+                        *(buf++) = '\\';
+                        *(buf++) = 't';
+                        break;
+                case '\v':
+                        *(buf++) = '\\';
+                        *(buf++) = 'v';
+                        break;
+                case '\\':
+                        *(buf++) = '\\';
+                        *(buf++) = '\\';
+                        break;
+                case '"':
+                        *(buf++) = '\\';
+                        *(buf++) = '"';
+                        break;
+                case '\'':
+                        *(buf++) = '\\';
+                        *(buf++) = '\'';
+                        break;
+
+                default:
+                        /* For special chars we prefer octal over
+                         * hexadecimal encoding, simply because glib's
+                         * g_strescape() does the same */
+                        if ((c < ' ') || (c >= 127)) {
+                                *(buf++) = '\\';
+                                *(buf++) = octchar((unsigned char) c >> 6);
+                                *(buf++) = octchar((unsigned char) c >> 3);
+                                *(buf++) = octchar((unsigned char) c);
+                        } else
+                                *(buf++) = c;
+                        break;
+        }
+
+        return buf - buf_old;
+}
+
 int close_nointr(int fd) {
         assert(fd >= 0);
 
@@ -363,6 +426,46 @@ int safe_atou8(const char *s, uint8_t *ret) {
         return 0;
 }
 
+int safe_atou16(const char *s, uint16_t *ret) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((unsigned long) (uint16_t) l != l)
+                return -ERANGE;
+
+        *ret = (uint16_t) l;
+        return 0;
+}
+
+int safe_atoi16(const char *s, int16_t *ret) {
+        char *x = NULL;
+        long l;
+
+        assert(s);
+        assert(ret);
+
+        errno = 0;
+        l = strtol(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((long) (int16_t) l != l)
+                return -ERANGE;
+
+        *ret = (int16_t) l;
+        return 0;
+}
+
 int safe_atollu(const char *s, long long unsigned *ret_llu) {
         char *x = NULL;
         unsigned long long l;
@@ -695,7 +798,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         }
 
         /* Kernel threads have no argv[] */
-        if (r == NULL || r[0] == 0) {
+        if (isempty(r)) {
                 _cleanup_free_ char *t = NULL;
                 int h;
 
@@ -757,19 +860,30 @@ int get_process_capeff(pid_t pid, char **capeff) {
         return get_status_field(p, "\nCapEff:", capeff);
 }
 
+static int get_process_link_contents(const char *proc_file, char **name) {
+        int r;
+
+        assert(proc_file);
+        assert(name);
+
+        r = readlink_malloc(proc_file, name);
+        if (r < 0)
+                return r == -ENOENT ? -ESRCH : r;
+
+        return 0;
+}
+
 int get_process_exe(pid_t pid, char **name) {
         const char *p;
         char *d;
         int r;
 
         assert(pid >= 0);
-        assert(name);
 
         p = procfs_file_alloca(pid, "exe");
-
-        r = readlink_malloc(p, name);
+        r = get_process_link_contents(p, name);
         if (r < 0)
-                return r == -ENOENT ? -ESRCH : r;
+                return r;
 
         d = endswith(*name, " (deleted)");
         if (d)
@@ -821,6 +935,59 @@ int get_process_gid(pid_t pid, gid_t *gid) {
         return get_process_id(pid, "Gid:", gid);
 }
 
+int get_process_cwd(pid_t pid, char **cwd) {
+        const char *p;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "cwd");
+
+        return get_process_link_contents(p, cwd);
+}
+
+int get_process_root(pid_t pid, char **root) {
+        const char *p;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "root");
+
+        return get_process_link_contents(p, root);
+}
+
+int get_process_environ(pid_t pid, char **environ) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *outcome = NULL;
+        int c;
+        const char *p;
+        size_t allocated = 0, sz = 0;
+
+        assert(pid >= 0);
+        assert(environ);
+
+        p = procfs_file_alloca(pid, "environ");
+
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        while ((c = fgetc(f)) != EOF) {
+                if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
+                        return -ENOMEM;
+
+                if (c == '\0')
+                        outcome[sz++] = '\n';
+                else
+                        sz += cescape_char(c, outcome + sz);
+        }
+
+        outcome[sz] = '\0';
+        *environ = outcome;
+        outcome = NULL;
+
+        return 0;
+}
+
 char *strnappend(const char *s, const char *suffix, size_t b) {
         size_t a;
         char *r;
@@ -893,6 +1060,28 @@ int readlink_malloc(const char *p, char **ret) {
         return readlinkat_malloc(AT_FDCWD, p, ret);
 }
 
+int readlink_value(const char *p, char **ret) {
+        _cleanup_free_ char *link = NULL;
+        char *value;
+        int r;
+
+        r = readlink_malloc(p, &link);
+        if (r < 0)
+                return r;
+
+        value = basename(link);
+        if (!value)
+                return -ENOENT;
+
+        value = strdup(value);
+        if (!value)
+                return -ENOMEM;
+
+        *ret = value;
+
+        return 0;
+}
+
 int readlink_and_make_absolute(const char *p, char **r) {
         _cleanup_free_ char *target = NULL;
         char *k;
@@ -1178,63 +1367,7 @@ char *cescape(const char *s) {
                 return NULL;
 
         for (f = s, t = r; *f; f++)
-
-                switch (*f) {
-
-                case '\a':
-                        *(t++) = '\\';
-                        *(t++) = 'a';
-                        break;
-                case '\b':
-                        *(t++) = '\\';
-                        *(t++) = 'b';
-                        break;
-                case '\f':
-                        *(t++) = '\\';
-                        *(t++) = 'f';
-                        break;
-                case '\n':
-                        *(t++) = '\\';
-                        *(t++) = 'n';
-                        break;
-                case '\r':
-                        *(t++) = '\\';
-                        *(t++) = 'r';
-                        break;
-                case '\t':
-                        *(t++) = '\\';
-                        *(t++) = 't';
-                        break;
-                case '\v':
-                        *(t++) = '\\';
-                        *(t++) = 'v';
-                        break;
-                case '\\':
-                        *(t++) = '\\';
-                        *(t++) = '\\';
-                        break;
-                case '"':
-                        *(t++) = '\\';
-                        *(t++) = '"';
-                        break;
-                case '\'':
-                        *(t++) = '\\';
-                        *(t++) = '\'';
-                        break;
-
-                default:
-                        /* For special chars we prefer octal over
-                         * hexadecimal encoding, simply because glib's
-                         * g_strescape() does the same */
-                        if ((*f < ' ') || (*f >= 127)) {
-                                *(t++) = '\\';
-                                *(t++) = octchar((unsigned char) *f >> 6);
-                                *(t++) = octchar((unsigned char) *f >> 3);
-                                *(t++) = octchar((unsigned char) *f);
-                        } else
-                                *(t++) = *f;
-                        break;
-                }
+                t += cescape_char(*f, t);
 
         *t = 0;
 
@@ -2466,14 +2599,53 @@ char* dirname_malloc(const char *path) {
 }
 
 int dev_urandom(void *p, size_t n) {
-        _cleanup_close_ int fd;
+        static int have_syscall = -1;
+        int r, fd;
         ssize_t k;
 
+        /* Gathers some randomness from the kernel. This call will
+         * never block, and will always return some data from the
+         * kernel, regardless if the random pool is fully initialized
+         * or not. It thus makes no guarantee for the quality of the
+         * returned entropy, but is good enough for or usual usecases
+         * of seeding the hash functions for hashtable */
+
+        /* Use the getrandom() syscall unless we know we don't have
+         * it, or when the requested size is too large for it. */
+        if (have_syscall != 0 || (size_t) (int) n != n) {
+                r = getrandom(p, n, GRND_NONBLOCK);
+                if (r == (int) n) {
+                        have_syscall = true;
+                        return 0;
+                }
+
+                if (r < 0) {
+                        if (errno == ENOSYS)
+                                /* we lack the syscall, continue with
+                                 * reading from /dev/urandom */
+                                have_syscall = false;
+                        else if (errno == EAGAIN)
+                                /* not enough entropy for now. Let's
+                                 * remember to use the syscall the
+                                 * next time, again, but also read
+                                 * from /dev/urandom for now, which
+                                 * doesn't care about the current
+                                 * amount of entropy.  */
+                                have_syscall = true;
+                        else
+                                return -errno;
+                } else
+                        /* too short read? */
+                        return -EIO;
+        }
+
         fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
         if (fd < 0)
                 return errno == ENOENT ? -ENOSYS : -errno;
 
         k = loop_read(fd, p, n, true);
+        safe_close(fd);
+
         if (k < 0)
                 return (int) k;
         if ((size_t) k != n)
@@ -2482,8 +2654,36 @@ int dev_urandom(void *p, size_t n) {
         return 0;
 }
 
-void random_bytes(void *p, size_t n) {
+void initialize_srand(void) {
         static bool srand_called = false;
+        unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+        void *auxv;
+#endif
+
+        if (srand_called)
+                return;
+
+        x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+        /* The kernel provides us with a bit of entropy in auxv, so
+         * let's try to make use of that to seed the pseudo-random
+         * generator. It's better than nothing... */
+
+        auxv = (void*) getauxval(AT_RANDOM);
+        if (auxv)
+                x ^= *(unsigned*) auxv;
+#endif
+
+        x ^= (unsigned) now(CLOCK_REALTIME);
+        x ^= (unsigned) gettid();
+
+        srand(x);
+        srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
         uint8_t *q;
         int r;
 
@@ -2494,28 +2694,7 @@ void random_bytes(void *p, size_t n) {
         /* If some idiot made /dev/urandom unavailable to us, he'll
          * get a PRNG instead. */
 
-        if (!srand_called) {
-                unsigned x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
-                /* The kernel provides us with a bit of entropy in
-                 * auxv, so let's try to make use of that to seed the
-                 * pseudo-random generator. It's better than
-                 * nothing... */
-
-                void *auxv;
-
-                auxv = (void*) getauxval(AT_RANDOM);
-                if (auxv)
-                        x ^= *(unsigned*) auxv;
-#endif
-
-                x ^= (unsigned) now(CLOCK_REALTIME);
-                x ^= (unsigned) gettid();
-
-                srand(x);
-                srand_called = true;
-        }
+        initialize_srand();
 
         for (q = p; q < (uint8_t*) p + n; q ++)
                 *q = rand();
@@ -2737,7 +2916,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         if (k < 0)
                 return k;
 
-        snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
+        sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
 
         k = readlink_malloc(fn, &s);
         if (k < 0) {
@@ -2895,6 +3074,19 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
         return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
 }
 
+static int file_is_priv_sticky(const char *p) {
+        struct stat st;
+
+        assert(p);
+
+        if (lstat(p, &st) < 0)
+                return -errno;
+
+        return
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                (st.st_mode & S_ISVTX);
+}
+
 static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
         int fd, r;
         struct statfs s;
@@ -3133,7 +3325,8 @@ char *replace_env(const char *format, char **env) {
 
                 case CURLY:
                         if (*e == '{') {
-                                if (!(k = strnappend(r, word, e-word-1)))
+                                k = strnappend(r, word, e-word-1);
+                                if (!k)
                                         goto fail;
 
                                 free(r);
@@ -3143,7 +3336,8 @@ char *replace_env(const char *format, char **env) {
                                 state = VARIABLE;
 
                         } else if (*e == '$') {
-                                if (!(k = strnappend(r, word, e-word)))
+                                k = strnappend(r, word, e-word);
+                                if (!k)
                                         goto fail;
 
                                 free(r);
@@ -3175,7 +3369,8 @@ char *replace_env(const char *format, char **env) {
                 }
         }
 
-        if (!(k = strnappend(r, word, e-word)))
+        k = strnappend(r, word, e-word);
+        if (!k)
                 goto fail;
 
         free(r);
@@ -3208,7 +3403,7 @@ char **replace_env_argv(char **argv, char **env) {
                         if (e) {
                                 int r;
 
-                                r = strv_split_quoted(&m, e);
+                                r = strv_split_quoted(&m, e, true);
                                 if (r < 0) {
                                         ret[k] = NULL;
                                         strv_free(ret);
@@ -3529,41 +3724,33 @@ char *unquote(const char *s, const char* quotes) {
 }
 
 char *normalize_env_assignment(const char *s) {
-        _cleanup_free_ char *name = NULL, *value = NULL, *p = NULL;
-        char *eq, *r;
+        _cleanup_free_ char *value = NULL;
+        const char *eq;
+        char *p, *name;
 
         eq = strchr(s, '=');
         if (!eq) {
-                char *t;
+                char *r, *t;
 
                 r = strdup(s);
                 if (!r)
                         return NULL;
 
                 t = strstrip(r);
-                if (t == r)
-                        return r;
+                if (t != r)
+                        memmove(r, t, strlen(t) + 1);
 
-                memmove(r, t, strlen(t) + 1);
                 return r;
         }
 
-        name = strndup(s, eq - s);
-        if (!name)
-                return NULL;
-
-        p = strdup(eq + 1);
-        if (!p)
-                return NULL;
+        name = strndupa(s, eq - s);
+        p = strdupa(eq + 1);
 
         value = unquote(strstrip(p), QUOTES);
         if (!value)
                 return NULL;
 
-        if (asprintf(&r, "%s=%s", strstrip(name), value) < 0)
-                r = NULL;
-
-        return r;
+        return strjoin(strstrip(name), "=", value, NULL);
 }
 
 int wait_for_terminate(pid_t pid, siginfo_t *status) {
@@ -4780,19 +4967,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
         return -ENOENT;
 }
 
-int file_is_priv_sticky(const char *p) {
-        struct stat st;
-
-        assert(p);
-
-        if (lstat(p, &st) < 0)
-                return -errno;
-
-        return
-                (st.st_uid == 0 || st.st_uid == getuid()) &&
-                (st.st_mode & S_ISVTX);
-}
-
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
@@ -6097,27 +6271,28 @@ int split_pair(const char *s, const char *sep, char **l, char **r) {
 
 int shall_restore_state(void) {
         _cleanup_free_ char *line = NULL;
-        const char *word, *state;
-        size_t l;
+        const char *p;
         int r;
 
         r = proc_cmdline(&line);
         if (r < 0)
                 return r;
-        if (r == 0) /* Container ... */
-                return 1;
 
         r = 1;
+        p = line;
 
-        FOREACH_WORD_QUOTED(word, l, line, state) {
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
                 const char *e;
-                char n[l+1];
                 int k;
 
-                memcpy(n, word, l);
-                n[l] = 0;
+                k = unquote_first_word(&p, &word, true);
+                if (k < 0)
+                        return k;
+                if (k == 0)
+                        break;
 
-                e = startswith(n, "systemd.restore_state=");
+                e = startswith(word, "systemd.restore_state=");
                 if (!e)
                         continue;
 
@@ -6130,51 +6305,35 @@ int shall_restore_state(void) {
 }
 
 int proc_cmdline(char **ret) {
-        int r;
-
-        if (detect_container(NULL) > 0) {
-                char *buf = NULL, *p;
-                size_t sz = 0;
-
-                r = read_full_file("/proc/1/cmdline", &buf, &sz);
-                if (r < 0)
-                        return r;
-
-                for (p = buf; p + 1 < buf + sz; p++)
-                        if (*p == 0)
-                                *p = ' ';
-
-                *p = 0;
-                *ret = buf;
-                return 1;
-        }
-
-        r = read_one_line_file("/proc/cmdline", ret);
-        if (r < 0)
-                return r;
+        assert(ret);
 
-        return 1;
+        if (detect_container(NULL) > 0)
+                return get_process_cmdline(1, 0, false, ret);
+        else
+                return read_one_line_file("/proc/cmdline", ret);
 }
 
 int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
         _cleanup_free_ char *line = NULL;
-        const char *w, *state;
-        size_t l;
+        const char *p;
         int r;
 
         assert(parse_item);
 
         r = proc_cmdline(&line);
         if (r < 0)
-                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
-        if (r <= 0)
-                return 0;
+                return r;
 
-        FOREACH_WORD_QUOTED(w, l, line, state) {
-                char word[l+1], *value;
+        p = line;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                char *value = NULL;
 
-                memcpy(word, w, l);
-                word[l] = 0;
+                r = unquote_first_word(&p, &word, true);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
                 /* Filter out arguments that are intended only for the
                  * initrd */
@@ -6363,6 +6522,10 @@ int getpeercred(int fd, struct ucred *ucred) {
          * to namespacing issues */
         if (u.pid <= 0)
                 return -ENODATA;
+        if (u.uid == (uid_t) -1)
+                return -ENODATA;
+        if (u.gid == (gid_t) -1)
+                return -ENODATA;
 
         *ucred = u;
         return 0;
@@ -6938,19 +7101,19 @@ int is_symlink(const char *path) {
 
 int is_dir(const char* path, bool follow) {
         struct stat st;
+        int r;
 
-        if (follow) {
-                if (stat(path, &st) < 0)
-                        return -errno;
-        } else {
-                if (lstat(path, &st) < 0)
-                        return -errno;
-        }
+        if (follow)
+                r = stat(path, &st);
+        else
+                r = lstat(path, &st);
+        if (r < 0)
+                return -errno;
 
         return !!S_ISDIR(st.st_mode);
 }
 
-int unquote_first_word(const char **p, char **ret) {
+int unquote_first_word(const char **p, char **ret, bool relax) {
         _cleanup_free_ char *s = NULL;
         size_t allocated = 0, sz = 0;
 
@@ -7009,8 +7172,11 @@ int unquote_first_word(const char **p, char **ret) {
                         break;
 
                 case VALUE_ESCAPE:
-                        if (c == 0)
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
                                 return -EINVAL;
+                        }
 
                         if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
@@ -7021,9 +7187,11 @@ int unquote_first_word(const char **p, char **ret) {
                         break;
 
                 case SINGLE_QUOTE:
-                        if (c == 0)
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
                                 return -EINVAL;
-                        else if (c == '\'')
+                        else if (c == '\'')
                                 state = VALUE;
                         else if (c == '\\')
                                 state = SINGLE_QUOTE_ESCAPE;
@@ -7037,8 +7205,11 @@ int unquote_first_word(const char **p, char **ret) {
                         break;
 
                 case SINGLE_QUOTE_ESCAPE:
-                        if (c == 0)
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
                                 return -EINVAL;
+                        }
 
                         if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
@@ -7064,8 +7235,11 @@ int unquote_first_word(const char **p, char **ret) {
                         break;
 
                 case DOUBLE_QUOTE_ESCAPE:
-                        if (c == 0)
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
                                 return -EINVAL;
+                        }
 
                         if (!GREEDY_REALLOC(s, allocated, sz+2))
                                 return -ENOMEM;
@@ -7125,7 +7299,7 @@ int unquote_many_words(const char **p, ...) {
         l = newa0(char*, n);
         for (c = 0; c < n; c++) {
 
-                r = unquote_first_word(p, &l[c]);
+                r = unquote_first_word(p, &l[c], false);
                 if (r < 0) {
                         int j;
 
@@ -7189,7 +7363,7 @@ int sethostname_idempotent(const char *s) {
         if (streq(buf, s))
                 return 0;
 
-        r = sethostname(buf, strlen(buf));
+        r = sethostname(s, strlen(s));
         if (r < 0)
                 return -errno;