chiark / gitweb /
core: refuse doing %h, %s, %U specifier resolving in PID 1
[elogind.git] / src / shared / util.c
index 1fc6c5aa1adf63476e1c7406b527b51f2896bd1f..20aec2a5c9cafa236b76806dd21b84b934a11ee6 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"
 #include "hashmap.h"
 #include "env-util.h"
 #include "fileio.h"
+#include "device-nodes.h"
+#include "utf8.h"
+#include "gunicode.h"
+#include "virt.h"
+#include "def.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -80,15 +85,6 @@ char **saved_argv = NULL;
 static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
 
-#define procfs_file_alloca(pid, field)                                  \
-        ({                                                              \
-                pid_t _pid_ = (pid);                                    \
-                char *_r_;                                              \
-                _r_ = alloca(sizeof("/proc/") -1 + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
-                sprintf(_r_, "/proc/%lu/" field, (unsigned long) _pid_); \
-                _r_;                                                    \
-        })
-
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -137,40 +133,6 @@ char* endswith(const char *s, const char *postfix) {
         return (char*) s + sl - pl;
 }
 
-char* startswith(const char *s, const char *prefix) {
-        const char *a, *b;
-
-        assert(s);
-        assert(prefix);
-
-        a = s, b = prefix;
-        for (;;) {
-                if (*b == 0)
-                        return (char*) a;
-                if (*a != *b)
-                        return NULL;
-
-                a++, b++;
-        }
-}
-
-char* startswith_no_case(const char *s, const char *prefix) {
-        const char *a, *b;
-
-        assert(s);
-        assert(prefix);
-
-        a = s, b = prefix;
-        for (;;) {
-                if (*b == 0)
-                        return (char*) a;
-                if (tolower(*a) != tolower(*b))
-                        return NULL;
-
-                a++, b++;
-        }
-}
-
 bool first_word(const char *s, const char *word) {
         size_t sl, wl;
 
@@ -376,13 +338,15 @@ int safe_atolli(const char *s, long long int *ret_lli) {
 
 int safe_atod(const char *s, double *ret_d) {
         char *x = NULL;
-        double d;
+        double d = 0;
 
         assert(s);
         assert(ret_d);
 
-        errno = 0;
-        d = strtod(s, &x);
+        RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") {
+                errno = 0;
+                d = strtod(s, &x);
+        }
 
         if (!x || x == s || *x || errno)
                 return errno ? -errno : -EINVAL;
@@ -410,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++) {
@@ -433,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 ++;
 
@@ -447,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)
@@ -458,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;
@@ -528,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))
@@ -594,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);
@@ -603,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) {
@@ -681,7 +656,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) {
-                char *t;
+                _cleanup_free_ char *t = NULL;
                 int h;
 
                 free(r);
@@ -694,8 +669,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                         return h;
 
                 r = strjoin("[", t, "]", NULL);
-                free(t);
-
                 if (!r)
                         return -ENOMEM;
         }
@@ -733,9 +706,24 @@ int is_kernel_thread(pid_t pid) {
         return 0;
 }
 
+int get_process_capeff(pid_t pid, char **capeff) {
+        const char *p;
+
+        assert(capeff);
+        assert(pid >= 0);
+
+        if (pid == 0)
+                p = "/proc/self/status";
+        else
+                p = procfs_file_alloca(pid, "status");
+
+        return get_status_field(p, "\nCapEff:", capeff);
+}
 
 int get_process_exe(pid_t pid, char **name) {
         const char *p;
+        char *d;
+        int r;
 
         assert(pid >= 0);
         assert(name);
@@ -745,7 +733,15 @@ int get_process_exe(pid_t pid, char **name) {
         else
                 p = procfs_file_alloca(pid, "exe");
 
-        return readlink_malloc(p, name);
+        r = readlink_malloc(p, name);
+        if (r < 0)
+                return r == -ENOENT ? -ESRCH : r;
+
+        d = endswith(*name, " (deleted)");
+        if (d)
+                *d = '\0';
+
+        return 0;
 }
 
 static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
@@ -857,18 +853,18 @@ int readlink_malloc(const char *p, char **r) {
 }
 
 int readlink_and_make_absolute(const char *p, char **r) {
-        char *target, *k;
+        _cleanup_free_ char *target = NULL;
+        char *k;
         int j;
 
         assert(p);
         assert(r);
 
-        if ((j = readlink_malloc(p, &target)) < 0)
+        j = readlink_malloc(p, &target);
+        if (j < 0)
                 return j;
 
         k = file_in_same_dir(p, target);
-        free(target);
-
         if (!k)
                 return -ENOMEM;
 
@@ -1372,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 reverse 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;
 
@@ -1456,7 +1380,7 @@ char *ascii_strlower(char *t) {
         return t;
 }
 
-static bool ignore_file_allow_backup(const char *filename) {
+_pure_ static bool ignore_file_allow_backup(const char *filename) {
         assert(filename);
 
         return
@@ -1519,7 +1443,7 @@ int fd_cloexec(int fd, bool cloexec) {
         return 0;
 }
 
-static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
         unsigned i;
 
         assert(n_fdset == 0 || fdset);
@@ -1607,6 +1531,7 @@ bool fstype_is_network(const char *fstype) {
                 "cifs\0"
                 "smbfs\0"
                 "ncpfs\0"
+                "ncp\0"
                 "nfs\0"
                 "nfs4\0"
                 "gfs\0"
@@ -1837,8 +1762,10 @@ int open_terminal(const char *name, int mode) {
          * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
          */
 
+        assert(!(mode & O_CREAT));
+
         for (;;) {
-                fd = open(name, mode);
+                fd = open(name, mode, 0);
                 if (fd >= 0)
                         break;
 
@@ -2200,8 +2127,10 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
                                         return n > 0 ? n : -errno;
                                 }
 
-                                if (pollfd.revents != POLLIN)
-                                        return n > 0 ? n : -EIO;
+                                /* We knowingly ignore the revents value here,
+                                 * and expect that any error/EOF is reported
+                                 * via read()/write()
+                                 */
 
                                 continue;
                         }
@@ -2248,8 +2177,10 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
                                         return n > 0 ? n : -errno;
                                 }
 
-                                if (pollfd.revents != POLLOUT)
-                                        return n > 0 ? n : -EIO;
+                                /* We knowingly ignore the revents value here,
+                                 * and expect that any error/EOF is reported
+                                 * via read()/write()
+                                 */
 
                                 continue;
                         }
@@ -2268,7 +2199,7 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
 int parse_bytes(const char *t, off_t *bytes) {
         static const struct {
                 const char *suffix;
-                off_t factor;
+                unsigned long long factor;
         } table[] = {
                 { "B", 1 },
                 { "K", 1024ULL },
@@ -2281,7 +2212,7 @@ int parse_bytes(const char *t, off_t *bytes) {
         };
 
         const char *p;
-        off_t r = 0;
+        unsigned long long r = 0;
 
         assert(t);
         assert(bytes);
@@ -2308,7 +2239,17 @@ int parse_bytes(const char *t, off_t *bytes) {
 
                 for (i = 0; i < ELEMENTSOF(table); i++)
                         if (startswith(e, table[i].suffix)) {
-                                r += (off_t) l * table[i].factor;
+                                unsigned long long tmp;
+                                if ((unsigned long long) l > ULLONG_MAX / table[i].factor)
+                                        return -ERANGE;
+                                tmp = l * table[i].factor;
+                                if (tmp > ULLONG_MAX - r)
+                                        return -ERANGE;
+
+                                r += tmp;
+                                if ((unsigned long long) (off_t) r != r)
+                                        return -ERANGE;
+
                                 p = e + strlen(table[i].suffix);
                                 break;
                         }
@@ -2316,7 +2257,7 @@ int parse_bytes(const char *t, off_t *bytes) {
                 if (i >= ELEMENTSOF(table))
                         return -EINVAL;
 
-        } while (*p != 0);
+        } while (*p);
 
         *bytes = r;
 
@@ -2424,6 +2365,25 @@ fallback:
         return random() * RAND_MAX + random();
 }
 
+unsigned random_u(void) {
+        _cleanup_close_ int fd;
+        unsigned u;
+        ssize_t r;
+
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                goto fallback;
+
+        r = loop_read(fd, &u, sizeof(u), true);
+        if (r != sizeof(u))
+                goto fallback;
+
+        return u;
+
+fallback:
+        return random() * RAND_MAX + random();
+}
+
 void rename_process(const char name[8]) {
         assert(name);
 
@@ -2542,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);
@@ -2577,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";
@@ -2591,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)
@@ -2614,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;
 }
 
@@ -2777,11 +2735,11 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
         return ret;
 }
 
-static int is_temporary_fs(struct statfs *s) {
+_pure_ static int is_temporary_fs(struct statfs *s) {
         assert(s);
         return
-                F_TYPE_CMP(s->f_type, TMPFS_MAGIC) ||
-                F_TYPE_CMP(s->f_type, RAMFS_MAGIC);
+                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) {
@@ -2904,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;
 }
@@ -3020,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));
 
@@ -3269,7 +3230,7 @@ int running_in_chroot(void) {
                 a.st_ino != b.st_ino;
 }
 
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
         size_t x;
         char *r;
 
@@ -3282,7 +3243,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
 
         r = new0(char, new_length+1);
         if (!r)
-                return r;
+                return NULL;
 
         x = (new_length * percent) / 100;
 
@@ -3300,6 +3261,80 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
         return r;
 }
 
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+        size_t x;
+        char *e;
+        const char *i, *j;
+        unsigned k, len, len2;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(new_length >= 3);
+
+        /* if no multibyte characters use ascii_ellipsize_mem for speed */
+        if (ascii_is_valid(s))
+                return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+        if (old_length <= 3 || old_length <= new_length)
+                return strndup(s, old_length);
+
+        x = (new_length * percent) / 100;
+
+        if (x > new_length - 3)
+                x = new_length - 3;
+
+        k = 0;
+        for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+                int c;
+
+                c = utf8_encoded_to_unichar(i);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+
+        if (k > x) /* last character was wide and went over quota */
+                x ++;
+
+        for (j = s + old_length; k < new_length && j > i; ) {
+                int c;
+
+                j = utf8_prev_char(j);
+                c = utf8_encoded_to_unichar(j);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+        assert(i <= j);
+
+        /* we don't actually need to ellipsize */
+        if (i == j)
+                return memdup(s, old_length + 1);
+
+        /* make space for ellipsis */
+        j = utf8_next_char(j);
+
+        len = i - s;
+        len2 = s + old_length - j;
+        e = new(char, len + 3 + len2 + 1);
+        if (!e)
+                return NULL;
+
+        /*
+        printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+               old_length, new_length, x, len, len2, k);
+        */
+
+        memcpy(e, s, len);
+        e[len]   = 0xe2; /* tri-dot ellipsis: … */
+        e[len + 1] = 0x80;
+        e[len + 2] = 0xa6;
+
+        memcpy(e + len + 3, j, len2 + 1);
+
+        return e;
+}
+
 char *ellipsize(const char *s, size_t length, unsigned percent) {
         return ellipsize_mem(s, strlen(s), length, percent);
 }
@@ -3472,7 +3507,9 @@ DIR *xopendirat(int fd, const char *name, int flags) {
         int nfd;
         DIR *d;
 
-        nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags);
+        assert(!(flags & O_CREAT));
+
+        nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
         if (nfd < 0)
                 return NULL;
 
@@ -3498,26 +3535,23 @@ int signal_from_string_try_harder(const char *s) {
 }
 
 static char *tag_to_udev_node(const char *tagvalue, const char *by) {
-        char *dn, *t, *u;
-        int r;
-
-        /* FIXME: to follow udev's logic 100% we need to leave valid
-         * UTF8 chars unescaped */
+        _cleanup_free_ char *t = NULL, *u = NULL;
+        char *dn;
+        size_t enc_len;
 
         u = unquote(tagvalue, "\"\'");
         if (u == NULL)
                 return NULL;
 
-        t = xescape(u, "/ ");
-        free(u);
-
+        enc_len = strlen(u) * 4 + 1;
+        t = new(char, enc_len);
         if (t == NULL)
                 return NULL;
 
-        r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t);
-        free(t);
+        if (encode_devnode_name(u, t, enc_len) < 0)
+                return NULL;
 
-        if (r < 0)
+        if (asprintf(&dn, "/dev/disk/by-%s/%s", by, t) < 0)
                 return NULL;
 
         return dn;
@@ -3603,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);
 
@@ -3621,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) {
@@ -3845,24 +3885,29 @@ bool hostname_is_valid(const char *s) {
         return true;
 }
 
-char* hostname_cleanup(char *s) {
+char* hostname_cleanup(char *s, bool lowercase) {
         char *p, *d;
         bool dot;
 
         for (p = s, d = s, dot = true; *p; p++) {
                 if (*p == '.') {
-                        if (dot || p[1] == 0)
+                        if (dot)
                                 continue;
 
+                        *(d++) = '.';
                         dot = true;
-                } else
+                } else if (hostname_valid_char(*p)) {
+                        *(d++) = lowercase ? tolower(*p) : *p;
                         dot = false;
+                }
 
-                if (hostname_valid_char(*p))
-                        *(d++) = *p;
         }
 
-        *d = 0;
+        if (dot && d > s)
+                d[-1] = 0;
+        else
+                *d = 0;
+
         strshorten(s, HOST_NAME_MAX);
 
         return s;
@@ -3917,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");
@@ -4033,8 +4078,9 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-int copy_file(const char *from, const char *to) {
-        int r, fdf, fdt;
+int copy_file(const char *from, const char *to, int flags) {
+        _cleanup_close_ int fdf = -1;
+        int r, fdt;
 
         assert(from);
         assert(to);
@@ -4043,11 +4089,9 @@ int copy_file(const char *from, const char *to) {
         if (fdf < 0)
                 return -errno;
 
-        fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
-        if (fdt < 0) {
-                close_nointr_nofail(fdf);
+        fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
+        if (fdt < 0)
                 return -errno;
-        }
 
         for (;;) {
                 char buf[PIPE_BUF];
@@ -4057,7 +4101,6 @@ int copy_file(const char *from, const char *to) {
                 if (n < 0) {
                         r = -errno;
 
-                        close_nointr_nofail(fdf);
                         close_nointr(fdt);
                         unlink(to);
 
@@ -4072,15 +4115,13 @@ int copy_file(const char *from, const char *to) {
                 if (n != k) {
                         r = k < 0 ? k : (errno ? -errno : -EIO);
 
-                        close_nointr_nofail(fdf);
                         close_nointr(fdt);
-
                         unlink(to);
+
                         return r;
                 }
         }
 
-        close_nointr_nofail(fdf);
         r = close_nointr(fdt);
 
         if (r < 0) {
@@ -4107,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] = '.';
@@ -4345,7 +4386,7 @@ int in_group(const char *name) {
 
 int glob_exists(const char *path) {
         _cleanup_globfree_ glob_t g = {};
-        int r, k;
+        int k;
 
         assert(path);
 
@@ -4353,15 +4394,37 @@ int glob_exists(const char *path) {
         k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
 
         if (k == GLOB_NOMATCH)
-                r = 0;
+                return 0;
         else if (k == GLOB_NOSPACE)
-                r = -ENOMEM;
+                return -ENOMEM;
         else if (k == 0)
-                r = !strv_isempty(g.gl_pathv);
+                return !strv_isempty(g.gl_pathv);
         else
-                r = errno ? -errno : -EIO;
+                return errno ? -errno : -EIO;
+}
 
-        return r;
+int glob_extend(char ***strv, const char *path) {
+        _cleanup_globfree_ glob_t g = {};
+        int k;
+        char **p;
+
+        errno = 0;
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+        if (k == GLOB_NOMATCH)
+                return -ENOENT;
+        else if (k == GLOB_NOSPACE)
+                return -ENOMEM;
+        else if (k != 0 || strv_isempty(g.gl_pathv))
+                return errno ? -errno : -EIO;
+
+        STRV_FOREACH(p, g.gl_pathv) {
+                k = strv_extend(strv, *p);
+                if (k < 0)
+                        break;
+        }
+
+        return k;
 }
 
 int dirent_ensure_type(DIR *d, struct dirent *de) {
@@ -4390,38 +4453,31 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
 }
 
 int in_search_path(const char *path, char **search) {
-        char **i, *parent;
+        char **i;
+        _cleanup_free_ char *parent = NULL;
         int r;
 
         r = path_get_parent(path, &parent);
         if (r < 0)
                 return r;
 
-        r = 0;
-
-        STRV_FOREACH(i, search) {
-                if (path_equal(parent, *i)) {
-                        r = 1;
-                        break;
-                }
-        }
-
-        free(parent);
+        STRV_FOREACH(i, search)
+                if (path_equal(parent, *i))
+                        return 1;
 
-        return r;
+        return 0;
 }
 
 int get_files_in_directory(const char *path, char ***list) {
-        DIR *d;
-        int r = 0;
-        unsigned n = 0;
-        char **l = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        size_t bufsize = 0, n = 0;
+        _cleanup_strv_free_ char **l = NULL;
 
         assert(path);
 
         /* Returns all files in a directory in *list, and the number
          * of files as return value. If list is NULL returns only the
-         * number */
+         * number. */
 
         d = opendir(path);
         if (!d)
@@ -4433,11 +4489,9 @@ int get_files_in_directory(const char *path, char ***list) {
                 int k;
 
                 k = readdir_r(d, &buf.de, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
-                }
-
+                assert(k >= 0);
+                if (k > 0)
+                        return -k;
                 if (!de)
                         break;
 
@@ -4447,43 +4501,25 @@ int get_files_in_directory(const char *path, char ***list) {
                         continue;
 
                 if (list) {
-                        if ((unsigned) r >= n) {
-                                char **t;
-
-                                n = MAX(16, 2*r);
-                                t = realloc(l, sizeof(char*) * n);
-                                if (!t) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                l = t;
-                        }
-
-                        assert((unsigned) r < n);
+                        /* one extra slot is needed for the terminating NULL */
+                        if (!GREEDY_REALLOC(l, bufsize, n + 2))
+                                return -ENOMEM;
 
-                        l[r] = strdup(de->d_name);
-                        if (!l[r]) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        l[n] = strdup(de->d_name);
+                        if (!l[n])
+                                return -ENOMEM;
 
-                        l[++r] = NULL;
+                        l[++n] = NULL;
                 } else
-                        r++;
+                        n++;
         }
 
-finish:
-        if (d)
-                closedir(d);
-
-        if (r >= 0) {
-                if (list)
-                        *list = l;
-        } else
-                strv_free(l);
+        if (list) {
+                *list = l;
+                l = NULL; /* avoid freeing */
+        }
 
-        return r;
+        return n;
 }
 
 char *strjoin(const char *x, ...) {
@@ -5097,59 +5133,6 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         return r;
 }
 
-int can_sleep(const char *type) {
-        char *w, *state;
-        size_t l, k;
-        int r;
-        _cleanup_free_ char *p = NULL;
-
-        assert(type);
-
-        /* If /sys is read-only we cannot sleep */
-        if (access("/sys/power/state", W_OK) < 0)
-                return false;
-
-        r = read_one_line_file("/sys/power/state", &p);
-        if (r < 0)
-                return false;
-
-        k = strlen(type);
-        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
-                if (l == k && memcmp(w, type, l) == 0)
-                        return true;
-
-        return false;
-}
-
-int can_sleep_disk(const char *type) {
-        char *w, *state;
-        size_t l, k;
-        int r;
-        _cleanup_free_ char *p = NULL;
-
-        assert(type);
-
-        /* If /sys is read-only we cannot sleep */
-        if (access("/sys/power/state", W_OK) < 0 ||
-            access("/sys/power/disk", W_OK) < 0)
-                return false;
-
-        r = read_one_line_file("/sys/power/disk", &p);
-        if (r < 0)
-                return false;
-
-        k = strlen(type);
-        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
-                if (l == k && memcmp(w, type, l) == 0)
-                        return true;
-
-                if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']')
-                        return true;
-        }
-
-        return false;
-}
-
 bool is_valid_documentation_url(const char *url) {
         assert(url);
 
@@ -5237,10 +5220,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);
 
@@ -5283,6 +5266,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))
@@ -5319,13 +5349,17 @@ bool string_is_safe(const char *p) {
         return true;
 }
 
+/**
+ * Check if a string contains control characters.
+ * Spaces and tabs are not considered control characters.
+ */
 bool string_has_cc(const char *p) {
         const char *t;
 
         assert(p);
 
         for (t = p; *t; t++)
-                if (*t > 0 && *t < ' ')
+                if (*t > 0 && *t < ' ' && *t != '\t')
                         return true;
 
         return false;
@@ -5393,25 +5427,29 @@ bool is_locale_utf8(void) {
                 goto out;
         }
 
-        if(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. */
-
+        /* For LC_CTYPE=="C" return true, because CTYPE is effectly
+         * unset and everything can do to UTF-8 nowadays. */
         set = setlocale(LC_CTYPE, NULL);
         if (!set) {
                 cached_answer = true;
                 goto out;
         }
 
-        cached_answer = streq(set, "C");
+        /* Check result, but ignore the result if C was set
+         * explicitly. */
+        cached_answer =
+                streq(set, "C") &&
+                !getenv("LC_ALL") &&
+                !getenv("LC_CTYPE") &&
+                !getenv("LANG");
 
 out:
-        return (bool)cached_answer;
+        return (bool) cached_answer;
 }
 
 const char *draw_special_char(DrawSpecialChar ch) {
@@ -5422,6 +5460,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]          = "| ",
@@ -5429,6 +5468,7 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TREE_RIGHT]         = "`-",
                         [DRAW_TREE_SPACE]         = "  ",
                         [DRAW_TRIANGULAR_BULLET]  = "> ",
+                        [DRAW_BLACK_CIRCLE]       = "* ",
                 }
         };
 
@@ -5724,56 +5764,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, *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;
@@ -5849,10 +5839,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;
@@ -5861,3 +5859,221 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
         *allocated = a;
         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;
+
+        l = strlen(s);
+        if (l == 32) {
+
+                /* Simple formatted 128bit hex string */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if (!(c >= '0' && c <= '9') &&
+                            !(c >= 'a' && c <= 'z') &&
+                            !(c >= 'A' && c <= 'Z'))
+                                return false;
+                }
+
+        } else if (l == 36) {
+
+                /* Formatted UUID */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+                                if (c != '-')
+                                        return false;
+                        } else {
+                                if (!(c >= '0' && c <= '9') &&
+                                    !(c >= 'a' && c <= 'z') &&
+                                    !(c >= 'A' && c <= 'Z'))
+                                        return false;
+                        }
+                }
+
+        } else
+                return false;
+
+        return true;
+}
+
+void parse_user_at_host(char *arg, char **user, char **host) {
+        assert(arg);
+        assert(user);
+        assert(host);
+
+        *host = strchr(arg, '@');
+        if (*host == NULL)
+                *host = arg;
+        else {
+                *host[0]++ = '\0';
+                *user = arg;
+        }
+}
+
+int split_pair(const char *s, const char *sep, char **l, char **r) {
+        char *x, *a, *b;
+
+        assert(s);
+        assert(sep);
+        assert(l);
+        assert(r);
+
+        if (isempty(sep))
+                return -EINVAL;
+
+        x = strstr(s, sep);
+        if (!x)
+                return -EINVAL;
+
+        a = strndup(s, x - s);
+        if (!a)
+                return -ENOMEM;
+
+        b = strdup(x + strlen(sep));
+        if (!b) {
+                free(a);
+                return -ENOMEM;
+        }
+
+        *l = a;
+        *r = b;
+
+        return 0;
+}
+
+int shall_restore_state(void) {
+        _cleanup_free_ char *line;
+        char *w, *state;
+        size_t l;
+        int r;
+
+        r = proc_cmdline(&line);
+        if (r < 0)
+                return r;
+        if (r == 0) /* Container ... */
+                return 1;
+
+        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;
+        }
+
+        r = read_one_line_file("/proc/cmdline", ret);
+        if (r < 0)
+                return r;
+
+        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 *namespace_fd, int *root_fd) {
+        _cleanup_close_ int nsfd = -1;
+        const char *ns, *root;
+        int rfd;
+
+        assert(pid >= 0);
+        assert(namespace_fd);
+        assert(root_fd);
+
+        ns = procfs_file_alloca(pid, "ns/mnt");
+        nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        if (nsfd < 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;
+
+        *namespace_fd = nsfd;
+        *root_fd = rfd;
+        nsfd = -1;
+
+        return 0;
+}
+
+int namespace_enter(int namespace_fd, int root_fd) {
+        assert(namespace_fd >= 0);
+        assert(root_fd >= 0);
+
+        if (setns(namespace_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;
+}