chiark / gitweb /
macro: rework how we define cleanup macros
[elogind.git] / src / shared / util.c
index 969ef2bb90a759409178ff109c29bc2c11a740fa..53caa7f9e504ca8afaab61d96e1cf31f6af46070 100644 (file)
@@ -59,6 +59,7 @@
 #include <limits.h>
 #include <langinfo.h>
 #include <locale.h>
+#include <libgen.h>
 
 #include "macro.h"
 #include "util.h"
@@ -70,6 +71,8 @@
 #include "path-util.h"
 #include "exit-status.h"
 #include "hashmap.h"
+#include "env-util.h"
+#include "fileio.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -77,6 +80,16 @@ char **saved_argv = NULL;
 static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
 
+#define PROCFS_PATH_LEN (sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t))
+
+#define FORMAT_PROCFS_PATH(buffer, path, pid)                                                           \
+        do {                                                                                            \
+                assert_cc(sizeof(buffer) == (PROCFS_PATH_LEN + 1 + sizeof(path)));                      \
+                snprintf(buffer, sizeof(buffer) - 1, "/proc/%lu/%s", (unsigned long) pid, path);        \
+                char_array_0(buffer);                                                                   \
+        } while(0)
+
+
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -182,44 +195,62 @@ bool first_word(const char *s, const char *word) {
 }
 
 int close_nointr(int fd) {
-        assert(fd >= 0);
-
-        for (;;) {
-                int r;
+        int r;
 
-                r = close(fd);
-                if (r >= 0)
-                        return r;
+        assert(fd >= 0);
+        r = close(fd);
 
-                if (errno != EINTR)
-                        return -errno;
-        }
+        /* Just ignore EINTR; a retry loop is the wrong
+         * thing to do on Linux.
+         *
+         * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+         * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+         * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+         * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+         */
+        if (_unlikely_(r < 0 && errno == EINTR))
+                return 0;
+        else if (r >= 0)
+                return r;
+        else
+                return -errno;
 }
 
 void close_nointr_nofail(int fd) {
-        int saved_errno = errno;
+        PROTECT_ERRNO;
 
         /* like close_nointr() but cannot fail, and guarantees errno
          * is unchanged */
 
         assert_se(close_nointr(fd) == 0);
-
-        errno = saved_errno;
 }
 
 void close_many(const int fds[], unsigned n_fd) {
         unsigned i;
 
+        assert(fds || n_fd <= 0);
+
         for (i = 0; i < n_fd; i++)
                 close_nointr_nofail(fds[i]);
 }
 
+int unlink_noerrno(const char *path) {
+        PROTECT_ERRNO;
+        int r;
+
+        r = unlink(path);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
 int parse_boolean(const char *v) {
         assert(v);
 
-        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || strcaseeq(v, "on"))
                 return 1;
-        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || strcaseeq(v, "off"))
                 return 0;
 
         return -EINVAL;
@@ -281,7 +312,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
         l = strtoul(s, &x, 0);
 
         if (!x || x == s || *x || errno)
-                return errno ? -errno : -EINVAL;
+                return errno > 0 ? -errno : -EINVAL;
 
         if ((unsigned long) (unsigned) l != l)
                 return -ERANGE;
@@ -301,7 +332,7 @@ int safe_atoi(const char *s, int *ret_i) {
         l = strtol(s, &x, 0);
 
         if (!x || x == s || *x || errno)
-                return errno ? -errno : -EINVAL;
+                return errno > 0 ? -errno : -EINVAL;
 
         if ((long) (int) l != l)
                 return -ERANGE;
@@ -344,6 +375,23 @@ int safe_atolli(const char *s, long long int *ret_lli) {
         return 0;
 }
 
+int safe_atod(const char *s, double *ret_d) {
+        char *x = NULL;
+        double d;
+
+        assert(s);
+        assert(ret_d);
+
+        errno = 0;
+        d = strtod(s, &x);
+
+        if (!x || x == s || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_d = (double) d;
+        return 0;
+}
+
 /* Split a string into words. */
 char *split(const char *c, size_t *l, const char *separator, char **state) {
         char *current;
@@ -420,14 +468,13 @@ char *split_quoted(const char *c, size_t *l, char **state) {
 int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
         int r;
         _cleanup_fclose_ FILE *f = NULL;
-        char fn[PATH_MAX], line[LINE_MAX], *p;
+        char fn[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/stat")], line[LINE_MAX], *p;
         long unsigned ppid;
 
         assert(pid > 0);
         assert(_ppid);
 
         assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
-        char_array_0(fn);
 
         f = fopen(fn, "re");
         if (!f)
@@ -464,13 +511,12 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
 
 int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
         _cleanup_fclose_ FILE *f = NULL;
-        char fn[PATH_MAX], line[LINE_MAX], *p;
+        char fn[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/stat")], line[LINE_MAX], *p;
 
         assert(pid > 0);
         assert(st);
 
         assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
-        char_array_0(fn);
 
         f = fopen(fn, "re");
         if (!f)
@@ -520,31 +566,6 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
         return 0;
 }
 
-int write_one_line_file(const char *fn, const char *line) {
-        _cleanup_fclose_ FILE *f = NULL;
-
-        assert(fn);
-        assert(line);
-
-        f = fopen(fn, "we");
-        if (!f)
-                return -errno;
-
-        errno = 0;
-        if (fputs(line, f) < 0)
-                return errno ? -errno : -EIO;
-
-        if (!endswith(line, "\n"))
-                fputc('\n', f);
-
-        fflush(f);
-
-        if (ferror(f))
-                return errno ? -errno : -EIO;
-
-        return 0;
-}
-
 int fchmod_umask(int fd, mode_t m) {
         mode_t u;
         int r;
@@ -556,334 +577,6 @@ int fchmod_umask(int fd, mode_t m) {
         return r;
 }
 
-int write_one_line_file_atomic(const char *fn, const char *line) {
-        _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        assert(fn);
-        assert(line);
-
-        r = fopen_temporary(fn, &f, &p);
-        if (r < 0)
-                return r;
-
-        fchmod_umask(fileno(f), 0644);
-
-        errno = 0;
-        if (fputs(line, f) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (!endswith(line, "\n"))
-                fputc('\n', f);
-
-        fflush(f);
-
-        if (ferror(f))
-                r = errno ? -errno : -EIO;
-        else {
-                if (rename(p, fn) < 0)
-                        r = -errno;
-                else
-                        r = 0;
-        }
-
-finish:
-        if (r < 0)
-                unlink(p);
-
-        return r;
-}
-
-int read_one_line_file(const char *fn, char **line) {
-        _cleanup_fclose_ FILE *f = NULL;
-        char t[LINE_MAX], *c;
-
-        assert(fn);
-        assert(line);
-
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        if (!fgets(t, sizeof(t), f)) {
-
-                if (ferror(f))
-                        return errno ? -errno : -EIO;
-
-                t[0] = 0;
-        }
-
-        c = strdup(t);
-        if (!c)
-                return -ENOMEM;
-        truncate_nl(c);
-
-        *line = c;
-        return 0;
-}
-
-int read_full_file(const char *fn, char **contents, size_t *size) {
-        _cleanup_fclose_ FILE *f = NULL;
-        size_t n, l;
-        _cleanup_free_ char *buf = NULL;
-        struct stat st;
-
-        assert(fn);
-        assert(contents);
-
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        if (fstat(fileno(f), &st) < 0)
-                return -errno;
-
-        /* Safety check */
-        if (st.st_size > 4*1024*1024)
-                return -E2BIG;
-
-        n = st.st_size > 0 ? st.st_size : LINE_MAX;
-        l = 0;
-
-        for (;;) {
-                char *t;
-                size_t k;
-
-                t = realloc(buf, n+1);
-                if (!t)
-                        return -ENOMEM;
-
-                buf = t;
-                k = fread(buf + l, 1, n - l, f);
-
-                if (k <= 0) {
-                        if (ferror(f))
-                                return -errno;
-
-                        break;
-                }
-
-                l += k;
-                n *= 2;
-
-                /* Safety check */
-                if (n > 4*1024*1024)
-                        return -E2BIG;
-        }
-
-        buf[l] = 0;
-        *contents = buf;
-        buf = NULL;
-
-        if (size)
-                *size = l;
-
-        return 0;
-}
-
-int parse_env_file(
-                const char *fname,
-                const char *separator, ...) {
-
-        int r = 0;
-        char *contents = NULL, *p;
-
-        assert(fname);
-        assert(separator);
-
-        if ((r = read_full_file(fname, &contents, NULL)) < 0)
-                return r;
-
-        p = contents;
-        for (;;) {
-                const char *key = NULL;
-
-                p += strspn(p, separator);
-                p += strspn(p, WHITESPACE);
-
-                if (!*p)
-                        break;
-
-                if (!strchr(COMMENTS, *p)) {
-                        va_list ap;
-                        char **value;
-
-                        va_start(ap, separator);
-                        while ((key = va_arg(ap, char *))) {
-                                size_t n;
-                                char *v;
-
-                                value = va_arg(ap, char **);
-
-                                n = strlen(key);
-                                if (strncmp(p, key, n) != 0 ||
-                                    p[n] != '=')
-                                        continue;
-
-                                p += n + 1;
-                                n = strcspn(p, separator);
-
-                                if (n >= 2 &&
-                                    strchr(QUOTES, p[0]) &&
-                                    p[n-1] == p[0])
-                                        v = strndup(p+1, n-2);
-                                else
-                                        v = strndup(p, n);
-
-                                if (!v) {
-                                        r = -ENOMEM;
-                                        va_end(ap);
-                                        goto fail;
-                                }
-
-                                if (v[0] == '\0') {
-                                        /* return empty value strings as NULL */
-                                        free(v);
-                                        v = NULL;
-                                }
-
-                                free(*value);
-                                *value = v;
-
-                                p += n;
-
-                                r ++;
-                                break;
-                        }
-                        va_end(ap);
-                }
-
-                if (!key)
-                        p += strcspn(p, separator);
-        }
-
-fail:
-        free(contents);
-        return r;
-}
-
-int load_env_file(const char *fname,
-                  char ***rl) {
-
-        FILE _cleanup_fclose_ *f;
-        char *b;
-        char _cleanup_free_ *c = NULL;
-        char _cleanup_strv_free_ **m = NULL;
-
-        assert(fname);
-        assert(rl);
-
-        f = fopen(fname, "re");
-        if (!f)
-                return -errno;
-
-        while (!feof(f)) {
-                char l[LINE_MAX], *p, *u, *cs;
-                char **t;
-
-                if (!fgets(l, sizeof(l), f)) {
-                        if (!feof(f))
-                                return -errno;
-                        else if (!c)
-                                break;
-                }
-
-                cs = endswith(l, "\\\n");
-                if (cs) {
-                        *cs = '\0';
-                        b = strappend(c, l);
-                        if (!b)
-                                return log_oom();
-
-                        free(c);
-                        c = b;
-                        *l = '\0';
-                        continue;
-                }
-
-                if (c) {
-                        b = strappend(c, l);
-                        if (!b)
-                                return log_oom();
-
-                        free(c);
-                        c = b;
-                }
-
-                p = strstrip(c ? c : l);
-
-                if (!*p)
-                        continue;
-
-                if (strchr(COMMENTS, *p))
-                        continue;
-
-                u = normalize_env_assignment(p);
-                if (!u)
-                        return log_oom();
-
-                free(c);
-                c = NULL;
-
-                t = strv_append(m, u);
-                free(u);
-
-                if (!t)
-                        return log_oom();
-
-                strv_free(m);
-                m = t;
-        }
-
-        *rl = m;
-        m = NULL;
-
-        return 0;
-}
-
-int write_env_file(const char *fname, char **l) {
-        char **i, *p;
-        FILE *f;
-        int r;
-
-        r = fopen_temporary(fname, &f, &p);
-        if (r < 0)
-                return r;
-
-        fchmod_umask(fileno(f), 0644);
-
-        errno = 0;
-        STRV_FOREACH(i, l) {
-                fputs(*i, f);
-                fputc('\n', f);
-        }
-
-        fflush(f);
-
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else {
-                if (rename(p, fname) < 0)
-                        r = -errno;
-                else
-                        r = 0;
-        }
-
-        if (r < 0)
-                unlink(p);
-
-        fclose(f);
-        free(p);
-
-        return r;
-}
-
 char *truncate_nl(char *s) {
         assert(s);
 
@@ -899,12 +592,9 @@ int get_process_comm(pid_t pid, char **name) {
         if (pid == 0)
                 r = read_one_line_file("/proc/self/comm", name);
         else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = read_one_line_file(p, name);
-                free(p);
+                char path[PROCFS_PATH_LEN + sizeof("/comm")];
+                FORMAT_PROCFS_PATH(path, "comm", pid);
+                r = read_one_line_file(path, name);
         }
 
         return r;
@@ -920,12 +610,9 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         if (pid == 0)
                 f = fopen("/proc/self/cmdline", "re");
         else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                f = fopen(p, "re");
-                free(p);
+                char path[PROCFS_PATH_LEN + sizeof("/cmdline")];
+                FORMAT_PROCFS_PATH(path, "cmdline", pid);
+                f = fopen(path, "re");
         }
 
         if (!f)
@@ -1012,7 +699,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
 }
 
 int is_kernel_thread(pid_t pid) {
-        char *p;
+        char path[PROCFS_PATH_LEN + sizeof("/cmdline")];
         size_t count;
         char c;
         bool eof;
@@ -1021,11 +708,8 @@ int is_kernel_thread(pid_t pid) {
         if (pid == 0)
                 return 0;
 
-        if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
-                return -ENOMEM;
-
-        f = fopen(p, "re");
-        free(p);
+        FORMAT_PROCFS_PATH(path, "cmdline", pid);
+        f = fopen(path, "re");
 
         if (!f)
                 return -errno;
@@ -1050,46 +734,32 @@ int get_process_exe(pid_t pid, char **name) {
         if (pid == 0)
                 r = readlink_malloc("/proc/self/exe", name);
         else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = readlink_malloc(p, name);
-                free(p);
+                char path[PROCFS_PATH_LEN + sizeof("/exe")];
+                FORMAT_PROCFS_PATH(path, "exe", pid);
+                r = readlink_malloc(path, name);
         }
 
         return r;
 }
 
 static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
-        char *p;
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
+        char path[PROCFS_PATH_LEN + sizeof("/status")];
+        char line[LINE_MAX];
 
+        assert(field);
         assert(uid);
 
         if (pid == 0)
                 return getuid();
 
-        if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
-                return -ENOMEM;
-
-        f = fopen(p, "re");
-        free(p);
-
+        FORMAT_PROCFS_PATH(path, "status", pid);
+        f = fopen(path, "re");
         if (!f)
                 return -errno;
 
-        while (!feof(f)) {
-                char line[LINE_MAX], *l;
-
-                if (!fgets(line, sizeof(line), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        goto finish;
-                }
+        FOREACH_LINE(line, f, return -errno) {
+                char *l;
 
                 l = strstrip(line);
 
@@ -1099,17 +769,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
 
                         l[strcspn(l, WHITESPACE)] = 0;
 
-                        r = parse_uid(l, uid);
-                        goto finish;
+                        return parse_uid(l, uid);
                 }
         }
 
-        r = -EIO;
-
-finish:
-        fclose(f);
-
-        return r;
+        return -EIO;
 }
 
 int get_process_uid(pid_t pid, uid_t *uid) {
@@ -1232,15 +896,14 @@ int reset_all_signal_handlers(void) {
         int sig;
 
         for (sig = 1; sig < _NSIG; sig++) {
-                struct sigaction sa;
+                struct sigaction sa = {
+                        .sa_handler = SIG_DFL,
+                        .sa_flags = SA_RESTART,
+                };
 
                 if (sig == SIGKILL || sig == SIGSTOP)
                         continue;
 
-                zero(sa);
-                sa.sa_handler = SIG_DFL;
-                sa.sa_flags = SA_RESTART;
-
                 /* On Linux the first two RT signals are reserved by
                  * glibc, and sigaction() will return EINVAL for them. */
                 if ((sigaction(sig, &sa, NULL) < 0))
@@ -1371,7 +1034,6 @@ int rmdir_parents(const char *path, const char *stop) {
         return 0;
 }
 
-
 char hexchar(int x) {
         static const char table[16] = "0123456789abcdef";
 
@@ -1392,6 +1054,49 @@ int unhexchar(char c) {
         return -1;
 }
 
+char *hexmem(const void *p, size_t l) {
+        char *r, *z;
+        const uint8_t *x;
+
+        z = r = malloc(l * 2 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + l; x++) {
+                *(z++) = hexchar(*x >> 4);
+                *(z++) = hexchar(*x & 15);
+        }
+
+        *z = 0;
+        return r;
+}
+
+void *unhexmem(const char *p, size_t l) {
+        uint8_t *r, *z;
+        const char *x;
+
+        assert(p);
+
+        z = r = malloc((l + 1) / 2 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < p + l; x += 2) {
+                int a, b;
+
+                a = unhexchar(x[0]);
+                if (x+1 < p + l)
+                        b = unhexchar(x[1]);
+                else
+                        b = 0;
+
+                *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+        }
+
+        *z = 0;
+        return r;
+}
+
 char octchar(int x) {
         return '0' + (x & 7);
 }
@@ -1667,16 +1372,24 @@ char *bus_path_escape(const char *s) {
         assert(s);
 
         /* Escapes all chars that D-Bus' object path cannot deal
-         * with. Can be reverse with bus_path_unescape() */
+         * with. Can be reverse with bus_path_unescape(). We special
+         * case the empty string. */
 
-        if (!(r = new(char, strlen(s)*3+1)))
+        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 >= '0' && *f <= '9')) {
+                    !(f > s && *f >= '0' && *f <= '9')) {
                         *(t++) = '_';
                         *(t++) = hexchar(*f >> 4);
                         *(t++) = hexchar(*f);
@@ -1694,7 +1407,12 @@ char *bus_path_unescape(const char *f) {
 
         assert(f);
 
-        if (!(r = strdup(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++) {
@@ -2146,29 +1864,28 @@ int open_terminal(const char *name, int mode) {
 }
 
 int flush_fd(int fd) {
-        struct pollfd pollfd;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = POLLIN;
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN,
+        };
 
         for (;;) {
                 char buf[LINE_MAX];
                 ssize_t l;
                 int r;
 
-                if ((r = poll(&pollfd, 1, 0)) < 0) {
-
+                r = poll(&pollfd, 1, 0);
+                if (r < 0) {
                         if (errno == EINTR)
                                 continue;
 
                         return -errno;
-                }
 
-                if (r == 0)
+                } else if (r == 0)
                         return 0;
 
-                if ((l = read(fd, buf, sizeof(buf))) < 0) {
+                l = read(fd, buf, sizeof(buf));
+                if (l < 0) {
 
                         if (errno == EINTR)
                                 continue;
@@ -2177,9 +1894,7 @@ int flush_fd(int fd) {
                                 return 0;
 
                         return -errno;
-                }
-
-                if (l <= 0)
+                } else if (l == 0)
                         return 0;
         }
 }
@@ -2193,7 +1908,6 @@ int acquire_terminal(
 
         int fd = -1, notify = -1, r = 0, wd = -1;
         usec_t ts = 0;
-        struct sigaction sa_old, sa_new;
 
         assert(name);
 
@@ -2228,6 +1942,11 @@ int acquire_terminal(
         }
 
         for (;;) {
+                struct sigaction sa_old, sa_new = {
+                        .sa_handler = SIG_IGN,
+                        .sa_flags = SA_RESTART,
+                };
+
                 if (notify >= 0) {
                         r = flush_fd(notify);
                         if (r < 0)
@@ -2243,9 +1962,6 @@ int acquire_terminal(
 
                 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
                  * if we already own the tty. */
-                zero(sa_new);
-                sa_new.sa_handler = SIG_IGN;
-                sa_new.sa_flags = SA_RESTART;
                 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
 
                 /* First, try to get the tty */
@@ -2352,18 +2068,19 @@ fail:
 }
 
 int release_terminal(void) {
-        int r = 0, fd;
-        struct sigaction sa_old, sa_new;
+        int r = 0;
+        struct sigaction sa_old, sa_new = {
+                .sa_handler = SIG_IGN,
+                .sa_flags = SA_RESTART,
+        };
+        int _cleanup_close_ fd;
 
-        if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
+        fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+        if (fd < 0)
                 return -errno;
 
         /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
          * by our own TIOCNOTTY */
-
-        zero(sa_new);
-        sa_new.sa_handler = SIG_IGN;
-        sa_new.sa_flags = SA_RESTART;
         assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
 
         if (ioctl(fd, TIOCNOTTY) < 0)
@@ -2371,7 +2088,6 @@ int release_terminal(void) {
 
         assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
 
-        close_nointr_nofail(fd);
         return r;
 }
 
@@ -2389,13 +2105,13 @@ int sigaction_many(const struct sigaction *sa, ...) {
 }
 
 int ignore_signals(int sig, ...) {
-        struct sigaction sa;
+        struct sigaction sa = {
+                .sa_handler = SIG_IGN,
+                .sa_flags = SA_RESTART,
+        };
         va_list ap;
         int r = 0;
 
-        zero(sa);
-        sa.sa_handler = SIG_IGN;
-        sa.sa_flags = SA_RESTART;
 
         if (sigaction(sig, &sa, NULL) < 0)
                 r = -errno;
@@ -2410,14 +2126,13 @@ int ignore_signals(int sig, ...) {
 }
 
 int default_signals(int sig, ...) {
-        struct sigaction sa;
+        struct sigaction sa = {
+                .sa_handler = SIG_DFL,
+                .sa_flags = SA_RESTART,
+        };
         va_list ap;
         int r = 0;
 
-        zero(sa);
-        sa.sa_handler = SIG_DFL;
-        sa.sa_flags = SA_RESTART;
-
         if (sigaction(sig, &sa, NULL) < 0)
                 r = -errno;
 
@@ -2466,11 +2181,10 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
                                 continue;
 
                         if (k < 0 && errno == EAGAIN && do_poll) {
-                                struct pollfd pollfd;
-
-                                zero(pollfd);
-                                pollfd.fd = fd;
-                                pollfd.events = POLLIN;
+                                struct pollfd pollfd = {
+                                        .fd = fd,
+                                        .events = POLLIN,
+                                };
 
                                 if (poll(&pollfd, 1, -1) < 0) {
                                         if (errno == EINTR)
@@ -2515,11 +2229,10 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
                                 continue;
 
                         if (k < 0 && errno == EAGAIN && do_poll) {
-                                struct pollfd pollfd;
-
-                                zero(pollfd);
-                                pollfd.fd = fd;
-                                pollfd.events = POLLOUT;
+                                struct pollfd pollfd = {
+                                        .fd = fd,
+                                        .events = POLLOUT,
+                                };
 
                                 if (poll(&pollfd, 1, -1) < 0) {
                                         if (errno == EINTR)
@@ -2575,7 +2288,7 @@ int parse_bytes(const char *t, off_t *bytes) {
                 errno = 0;
                 l = strtoll(p, &e, 10);
 
-                if (errno != 0)
+                if (errno > 0)
                         return -errno;
 
                 if (l < 0)
@@ -2667,6 +2380,24 @@ int dir_is_empty(const char *path) {
         }
 }
 
+char* dirname_malloc(const char *path) {
+        char *d, *dir, *dir2;
+
+        d = strdup(path);
+        if (!d)
+                return NULL;
+        dir = dirname(d);
+        assert(dir);
+
+        if (dir != d) {
+                dir2 = strdup(dir);
+                free(d);
+                return dir2;
+        }
+
+        return dir;
+}
+
 unsigned long long random_ull(void) {
         _cleanup_close_ int fd;
         uint64_t ull;
@@ -2880,7 +2611,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
 
 int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         int k;
-        char fn[PATH_MAX], *s, *b, *p;
+        char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *s, *b, *p;
         dev_t devnr;
 
         assert(r);
@@ -2890,7 +2621,6 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
                 return k;
 
         snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
-        char_array_0(fn);
 
         k = readlink_malloc(fn, &s);
         if (k < 0) {
@@ -3201,12 +2931,13 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
         }
 }
 
-int status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
         static const char status_indent[] = "         "; /* "[" STATUS "] " */
         _cleanup_free_ char *s = NULL;
         _cleanup_close_ int fd = -1;
-        struct iovec iovec[5];
+        struct iovec iovec[6] = {};
         int n = 0;
+        static bool prev_ephemeral;
 
         assert(format);
 
@@ -3242,7 +2973,9 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
                 }
         }
 
-        zero(iovec);
+        if (prev_ephemeral)
+                IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+        prev_ephemeral = ephemeral;
 
         if (status) {
                 if (!isempty(status)) {
@@ -3254,7 +2987,8 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
         }
 
         IOVEC_SET_STRING(iovec[n++], s);
-        IOVEC_SET_STRING(iovec[n++], "\n");
+        if (!ephemeral)
+                IOVEC_SET_STRING(iovec[n++], "\n");
 
         if (writev(fd, iovec, n) < 0)
                 return -errno;
@@ -3262,14 +2996,14 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
         return 0;
 }
 
-int status_printf(const char *status, bool ellipse, const char *format, ...) {
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
         va_list ap;
         int r;
 
         assert(format);
 
         va_start(ap, format);
-        r = status_vprintf(status, ellipse, format, ap);
+        r = status_vprintf(status, ellipse, ephemeral, format, ap);
         va_end(ap);
 
         return r;
@@ -3286,7 +3020,7 @@ int status_welcome(void) {
         if (r < 0 && r != -ENOENT)
                 log_warning("Failed to read /etc/os-release: %s", strerror(-r));
 
-        return status_printf(NULL, false,
+        return status_printf(NULL, false, false,
                              "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
                              isempty(ansi_color) ? "1" : ansi_color,
                              isempty(pretty_name) ? "Linux" : pretty_name);
@@ -3341,10 +3075,10 @@ char *replace_env(const char *format, char **env) {
                         if (*e == '}') {
                                 const char *t;
 
-                                if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
-                                        t = "";
+                                t = strempty(strv_env_get_n(env, word+2, e-word-2));
 
-                                if (!(k = strappend(r, t)))
+                                k = strappend(r, t);
+                                if (!k)
                                         goto fail;
 
                                 free(r);
@@ -3385,7 +3119,8 @@ char **replace_env_argv(char **argv, char **env) {
                         char **w, **m;
                         unsigned q;
 
-                        if ((e = strv_env_get(env, *i+1))) {
+                        e = strv_env_get(env, *i+1);
+                        if (e) {
 
                                 if (!(m = strv_split_quoted(e))) {
                                         r[k] = NULL;
@@ -3427,8 +3162,7 @@ char **replace_env_argv(char **argv, char **env) {
 }
 
 int fd_columns(int fd) {
-        struct winsize ws;
-        zero(ws);
+        struct winsize ws = {};
 
         if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
                 return -errno;
@@ -3462,8 +3196,7 @@ unsigned columns(void) {
 }
 
 int fd_lines(int fd) {
-        struct winsize ws;
-        zero(ws);
+        struct winsize ws = {};
 
         if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
                 return -errno;
@@ -3512,13 +3245,9 @@ bool on_tty(void) {
 }
 
 int running_in_chroot(void) {
-        struct stat a, b;
-
-        zero(a);
-        zero(b);
+        struct stat a = {}, b = {};
 
         /* Only works as root */
-
         if (stat("/proc/1/root", &a) < 0)
                 return -errno;
 
@@ -3844,6 +3573,29 @@ int vtnr_from_tty(const char *tty) {
         return i;
 }
 
+char *resolve_dev_console(char **active) {
+        char *tty;
+
+        /* Resolve where /dev/console is pointing to, if /sys is actually ours
+         * (i.e. not read-only-mounted which is a sign for container setups) */
+
+        if (path_is_read_only_fs("/sys") > 0)
+                return NULL;
+
+        if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+                return NULL;
+
+        /* If multiple log outputs are configured the last one is what
+         * /dev/console points to */
+        tty = strrchr(*active, ' ');
+        if (tty)
+                tty++;
+        else
+                tty = *active;
+
+        return tty;
+}
+
 bool tty_is_vc_resolve(const char *tty) {
         char *active = NULL;
         bool b;
@@ -3853,19 +3605,11 @@ bool tty_is_vc_resolve(const char *tty) {
         if (startswith(tty, "/dev/"))
                 tty += 5;
 
-        /* Resolve where /dev/console is pointing to, if /sys is
-         * actually ours (i.e. not read-only-mounted which is a sign
-         * for container setups) */
-        if (streq(tty, "console") && path_is_read_only_fs("/sys") <= 0)
-                if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
-                        /* If multiple log outputs are configured the
-                         * last one is what /dev/console points to */
-                        tty = strrchr(active, ' ');
-                        if (tty)
-                                tty++;
-                        else
-                                tty = active;
-                }
+        if (streq(tty, "console")) {
+                tty = resolve_dev_console(&active);
+                if (!tty)
+                        return false;
+        }
 
         b = tty_is_vc(tty);
         free(active);
@@ -3914,8 +3658,8 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
 
         assert(directory);
 
-        /* Executes all binaries in a directory in parallel and waits
-         * until all they all finished. */
+        /* Executes all binaries in a directory in parallel and
+         * waits for them to finish. */
 
         if (!d) {
                 if (!(_d = opendir(directory))) {
@@ -3981,10 +3725,9 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
 
         while (!hashmap_isempty(pids)) {
                 pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
-                siginfo_t si;
+                siginfo_t si = {};
                 char *path;
 
-                zero(si);
                 if (waitid(P_PID, pid, &si, WEXITED) < 0) {
 
                         if (errno == EINTR)
@@ -4064,13 +3807,27 @@ static bool hostname_valid_char(char c) {
 
 bool hostname_is_valid(const char *s) {
         const char *p;
+        bool dot;
 
         if (isempty(s))
                 return false;
 
-        for (p = s; *p; p++)
-                if (!hostname_valid_char(*p))
-                        return false;
+        for (p = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot)
+                                return false;
+
+                        dot = true;
+                } else {
+                        if (!hostname_valid_char(*p))
+                                return false;
+
+                        dot = false;
+                }
+        }
+
+        if (dot)
+                return false;
 
         if (p-s > HOST_NAME_MAX)
                 return false;
@@ -4080,29 +3837,33 @@ bool hostname_is_valid(const char *s) {
 
 char* hostname_cleanup(char *s) {
         char *p, *d;
+        bool dot;
 
-        for (p = s, d = s; *p; p++)
-                if ((*p >= 'a' && *p <= 'z') ||
-                    (*p >= 'A' && *p <= 'Z') ||
-                    (*p >= '0' && *p <= '9') ||
-                    *p == '-' ||
-                    *p == '_' ||
-                    *p == '.')
+        for (p = s, d = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot || p[1] == 0)
+                                continue;
+
+                        dot = true;
+                } else
+                        dot = false;
+
+                if (hostname_valid_char(*p))
                         *(d++) = *p;
+        }
 
         *d = 0;
-
         strshorten(s, HOST_NAME_MAX);
+
         return s;
 }
 
 int pipe_eof(int fd) {
-        struct pollfd pollfd;
         int r;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = POLLIN|POLLHUP;
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN|POLLHUP,
+        };
 
         r = poll(&pollfd, 1, 0);
         if (r < 0)
@@ -4115,12 +3876,11 @@ int pipe_eof(int fd) {
 }
 
 int fd_wait_for_event(int fd, int event, usec_t t) {
-        struct pollfd pollfd;
         int r;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = event;
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = event,
+        };
 
         r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
         if (r < 0)
@@ -4447,7 +4207,7 @@ int get_user_creds(
         }
 
         if (!p)
-                return errno != 0 ? -errno : -ESRCH;
+                return errno > 0 ? -errno : -ESRCH;
 
         if (uid)
                 *uid = p->pw_uid;
@@ -4481,6 +4241,23 @@ char* uid_to_name(uid_t uid) {
         return r;
 }
 
+char* gid_to_name(gid_t gid) {
+        struct group *p;
+        char *r;
+
+        if (gid == 0)
+                return strdup("root");
+
+        p = getgrgid(gid);
+        if (p)
+                return strdup(p->gr_name);
+
+        if (asprintf(&r, "%lu", (unsigned long) gid) < 0)
+                return NULL;
+
+        return r;
+}
+
 int get_group_creds(const char **groupname, gid_t *gid) {
         struct group *g;
         gid_t id;
@@ -4511,7 +4288,7 @@ int get_group_creds(const char **groupname, gid_t *gid) {
         }
 
         if (!g)
-                return errno != 0 ? -errno : -ESRCH;
+                return errno > 0 ? -errno : -ESRCH;
 
         if (gid)
                 *gid = g->gr_gid;
@@ -4519,14 +4296,10 @@ int get_group_creds(const char **groupname, gid_t *gid) {
         return 0;
 }
 
-int in_group(const char *name) {
-        gid_t gid, *gids;
+int in_gid(gid_t gid) {
+        gid_t *gids;
         int ngroups_max, r, i;
 
-        r = get_group_creds(&name, &gid);
-        if (r < 0)
-                return r;
-
         if (getgid() == gid)
                 return 1;
 
@@ -4549,13 +4322,23 @@ int in_group(const char *name) {
         return 0;
 }
 
+int in_group(const char *name) {
+        int r;
+        gid_t gid;
+
+        r = get_group_creds(&name, &gid);
+        if (r < 0)
+                return r;
+
+        return in_gid(gid);
+}
+
 int glob_exists(const char *path) {
-        glob_t g;
+        glob_t _cleanup_globfree_ g = {};
         int r, k;
 
         assert(path);
 
-        zero(g);
         errno = 0;
         k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
 
@@ -4568,8 +4351,6 @@ int glob_exists(const char *path) {
         else
                 r = errno ? -errno : -EIO;
 
-        globfree(&g);
-
         return r;
 }
 
@@ -4971,7 +4752,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[12];
+        static __thread char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
         const char *name;
 
         name = __signal_to_string(signo);
@@ -4979,10 +4760,10 @@ const char *signal_to_string(int signo) {
                 return name;
 
         if (signo >= SIGRTMIN && signo <= SIGRTMAX)
-                snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
+                snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
         else
-                snprintf(buf, sizeof(buf) - 1, "%d", signo);
-        char_array_0(buf);
+                snprintf(buf, sizeof(buf), "%d", signo);
+
         return buf;
 }
 
@@ -5250,7 +5031,7 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
 }
 
 int getenv_for_pid(pid_t pid, const char *field, char **_value) {
-        char path[sizeof("/proc/")-1+10+sizeof("/environ")], *value = NULL;
+        char path[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/environ")], *value = NULL;
         int r;
         FILE *f;
         bool done = false;
@@ -5263,7 +5044,6 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
                 pid = getpid();
 
         snprintf(path, sizeof(path), "/proc/%lu/environ", (unsigned long) pid);
-        char_array_0(path);
 
         f = fopen(path, "re");
         if (!f)
@@ -5484,7 +5264,7 @@ int get_home_dir(char **_h) {
         errno = 0;
         p = getpwuid(u);
         if (!p)
-                return errno ? -errno : -ESRCH;
+                return errno > 0 ? -errno : -ESRCH;
 
         if (!path_is_absolute(p->pw_dir))
                 return -EINVAL;
@@ -5497,81 +5277,6 @@ int get_home_dir(char **_h) {
         return 0;
 }
 
-int get_shell(char **_sh) {
-        char *sh;
-        const char *e;
-        uid_t u;
-        struct passwd *p;
-
-        assert(_sh);
-
-        /* Take the user specified one */
-        e = getenv("SHELL");
-        if (e) {
-                sh = strdup(e);
-                if (!sh)
-                        return -ENOMEM;
-
-                *_sh = sh;
-                return 0;
-        }
-
-        /* Hardcode home directory for root to avoid NSS */
-        u = getuid();
-        if (u == 0) {
-                sh = strdup("/bin/sh");
-                if (!sh)
-                        return -ENOMEM;
-
-                *_sh = sh;
-                return 0;
-        }
-
-        /* Check the database... */
-        errno = 0;
-        p = getpwuid(u);
-        if (!p)
-                return errno ? -errno : -ESRCH;
-
-        if (!path_is_absolute(p->pw_shell))
-                return -EINVAL;
-
-        sh = strdup(p->pw_shell);
-        if (!sh)
-                return -ENOMEM;
-
-        *_sh = sh;
-        return 0;
-}
-
-void freep(void *p) {
-        free(*(void**) p);
-}
-
-void fclosep(FILE **f) {
-        if (*f)
-                fclose(*f);
-}
-
-void pclosep(FILE **f) {
-        if (*f)
-                pclose(*f);
-}
-
-void closep(int *fd) {
-        if (*fd >= 0)
-                close_nointr_nofail(*fd);
-}
-
-void closedirp(DIR **d) {
-        if (*d)
-                closedir(*d);
-}
-
-void umaskp(mode_t *u) {
-        umask(*u);
-}
-
 bool filename_is_safe(const char *p) {
 
         if (isempty(p))
@@ -5608,6 +5313,18 @@ bool string_is_safe(const char *p) {
         return true;
 }
 
+bool string_has_cc(const char *p) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++)
+                if (*t > 0 && *t < ' ')
+                        return true;
+
+        return false;
+}
+
 bool path_is_safe(const char *p) {
 
         if (isempty(p))
@@ -5670,7 +5387,23 @@ bool is_locale_utf8(void) {
                 goto out;
         }
 
-        cached_answer = streq(set, "UTF-8");
+        if(streq(set, "UTF-8")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        /* For LC_CTYPE=="C" return true,
+         * because CTYPE is effectly unset and
+         * everything defaults to UTF-8 nowadays. */
+
+        set = setlocale(LC_CTYPE, NULL);
+        if (!set) {
+                cached_answer = true;
+                goto out;
+        }
+
+        cached_answer = streq(set, "C");
+
 out:
         return (bool)cached_answer;
 }
@@ -5840,7 +5573,6 @@ int on_ac_power(void) {
         for (;;) {
                 struct dirent *de;
                 union dirent_storage buf;
-                _cleanup_free_ char *p = NULL;
                 _cleanup_close_ int fd = -1, device = -1;
                 char contents[6];
                 ssize_t n;
@@ -5906,3 +5638,220 @@ int on_ac_power(void) {
 
         return found_online || !found_offline;
 }
+
+static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) {
+        char **i;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (!path_strv_canonicalize_uniq(search))
+                return -ENOMEM;
+
+        STRV_FOREACH(i, search) {
+                _cleanup_free_ char *p = NULL;
+                FILE *f;
+
+                p = strjoin(*i, "/", path, NULL);
+                if (!p)
+                        return -ENOMEM;
+
+                f = fopen(p, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                if (errno != ENOENT)
+                        return -errno;
+        }
+
+        return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) {
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        copy = strv_copy((char**) search);
+        if (!copy)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) {
+        _cleanup_strv_free_ char **s = NULL;
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        s = strv_split_nulstr(search);
+        if (!s)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, s, _f);
+}
+
+int create_tmp_dir(char template[], char** dir_name) {
+        int r = 0;
+        char *d, *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;
+        char *r, *p;
+
+        assert(x);
+
+        l = f = *x ? strlen(*x) : 0;
+
+        va_start(ap, x);
+        for (;;) {
+                const char *t;
+                size_t n;
+
+                t = va_arg(ap, const char *);
+                if (!t)
+                        break;
+
+                n = strlen(t);
+                if (n > ((size_t) -1) - l) {
+                        va_end(ap);
+                        return NULL;
+                }
+
+                l += n;
+        }
+        va_end(ap);
+
+        r = realloc(*x, l+1);
+        if (!r)
+                return NULL;
+
+        p = r + f;
+
+        va_start(ap, x);
+        for (;;) {
+                const char *t;
+
+                t = va_arg(ap, const char *);
+                if (!t)
+                        break;
+
+                p = stpcpy(p, t);
+        }
+        va_end(ap);
+
+        *p = 0;
+        *x = r;
+
+        return r + l;
+}
+
+char *strrep(const char *s, unsigned n) {
+        size_t l;
+        char *r, *p;
+        unsigned i;
+
+        assert(s);
+
+        l = strlen(s);
+        p = r = malloc(l * n + 1);
+        if (!r)
+                return NULL;
+
+        for (i = 0; i < n; i++)
+                p = stpcpy(p, s);
+
+        *p = 0;
+        return r;
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need) {
+        size_t a;
+        void *q;
+
+        if (*allocated >= need)
+                return *p;
+
+        a = MAX(64u, need * 2);
+        q = realloc(*p, a);
+        if (!q)
+                return NULL;
+
+        *p = q;
+        *allocated = a;
+        return q;
+}