chiark / gitweb /
util: add (x)bsearch_r(), the missing counterpart of qsort_r()
[elogind.git] / src / shared / util.c
index fd2c5b075f77649f943ccf4cb59d02a3fc673e9d..2f0aba88ae6e1c443f54b1d821ed25d73649a069 100644 (file)
 #include <linux/kd.h>
 #include <dlfcn.h>
 #include <sys/wait.h>
-#include <sys/capability.h>
 #include <sys/time.h>
-#include <linux/rtc.h>
 #include <glob.h>
 #include <grp.h>
 #include <sys/mman.h>
-#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <limits.h>
 
 #include "macro.h"
 #include "util.h"
 #include "log.h"
 #include "strv.h"
 #include "label.h"
+#include "path-util.h"
 #include "exit-status.h"
 #include "hashmap.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
 
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -78,10 +82,10 @@ size_t page_size(void) {
         if (_likely_(pgsz > 0))
                 return pgsz;
 
-        assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
+        r = sysconf(_SC_PAGESIZE);
+        assert(r > 0);
 
         pgsz = (size_t) r;
-
         return pgsz;
 }
 
@@ -140,6 +144,10 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
+        if (ts->tv_sec == (time_t) -1 &&
+            ts->tv_nsec == (long) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) ts->tv_sec * USEC_PER_SEC +
                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
@@ -148,6 +156,12 @@ usec_t timespec_load(const struct timespec *ts) {
 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
         assert(ts);
 
+        if (u == (usec_t) -1) {
+                ts->tv_sec = (time_t) -1;
+                ts->tv_nsec = (long) -1;
+                return ts;
+        }
+
         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
 
@@ -157,6 +171,10 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
 usec_t timeval_load(const struct timeval *tv) {
         assert(tv);
 
+        if (tv->tv_sec == (time_t) -1 &&
+            tv->tv_usec == (suseconds_t) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) tv->tv_sec * USEC_PER_SEC +
                 (usec_t) tv->tv_usec;
@@ -165,13 +183,19 @@ usec_t timeval_load(const struct timeval *tv) {
 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         assert(tv);
 
+        if (u == (usec_t) -1) {
+                tv->tv_sec = (time_t) -1;
+                tv->tv_usec = (suseconds_t) -1;
+                return tv;
+        }
+
         tv->tv_sec = (time_t) (u / USEC_PER_SEC);
         tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
 
         return tv;
 }
 
-bool endswith(const char *s, const char *postfix) {
+char* endswith(const char *s, const char *postfix) {
         size_t sl, pl;
 
         assert(s);
@@ -181,12 +205,15 @@ bool endswith(const char *s, const char *postfix) {
         pl = strlen(postfix);
 
         if (pl == 0)
-                return true;
+                return (char*) s + sl;
 
         if (sl < pl)
-                return false;
+                return NULL;
+
+        if (memcmp(s + sl - pl, postfix, pl) != 0)
+                return NULL;
 
-        return memcmp(s + sl - pl, postfix, pl) == 0;
+        return (char*) s + sl - pl;
 }
 
 bool startswith(const char *s, const char *prefix) {
@@ -223,10 +250,9 @@ bool startswith_no_case(const char *s, const char *prefix) {
         if (sl < pl)
                 return false;
 
-        for(i = 0; i < pl; ++i) {
+        for(i = 0; i < pl; ++i)
                 if (tolower(s[i]) != tolower(prefix[i]))
                         return false;
-        }
 
         return true;
 }
@@ -305,7 +331,8 @@ int parse_pid(const char *s, pid_t* ret_pid) {
         assert(s);
         assert(ret_pid);
 
-        if ((r = safe_atolu(s, &ul)) < 0)
+        r = safe_atolu(s, &ul);
+        if (r < 0)
                 return r;
 
         pid = (pid_t) ul;
@@ -328,7 +355,8 @@ int parse_uid(const char *s, uid_t* ret_uid) {
         assert(s);
         assert(ret_uid);
 
-        if ((r = safe_atolu(s, &ul)) < 0)
+        r = safe_atolu(s, &ul);
+        if (r < 0)
                 return r;
 
         uid = (uid_t) ul;
@@ -487,24 +515,9 @@ char *split_quoted(const char *c, size_t *l, char **state) {
         return (char*) current;
 }
 
-char **split_path_and_make_absolute(const char *p) {
-        char **l;
-        assert(p);
-
-        if (!(l = strv_split(p, ":")))
-                return NULL;
-
-        if (!strv_path_make_absolute_cwd(l)) {
-                strv_free(l);
-                return NULL;
-        }
-
-        return l;
-}
-
 int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
         int r;
-        FILE *f;
+        _cleanup_fclose_ FILE *f = NULL;
         char fn[PATH_MAX], line[LINE_MAX], *p;
         long unsigned ppid;
 
@@ -514,22 +527,22 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
         assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
         char_array_0(fn);
 
-        if (!(f = fopen(fn, "re")))
+        f = fopen(fn, "re");
+        if (!f)
                 return -errno;
 
-        if (!(fgets(line, sizeof(line), f))) {
+        if (!fgets(line, sizeof(line), f)) {
                 r = feof(f) ? -EIO : -errno;
                 fclose(f);
                 return r;
         }
 
-        fclose(f);
-
         /* Let's skip the pid and comm fields. The latter is enclosed
          * in () but does not escape any () in its value, so let's
          * skip over it manually */
 
-        if (!(p = strrchr(line, ')')))
+        p = strrchr(line, ')');
+        if (!p)
                 return -EIO;
 
         p++;
@@ -549,8 +562,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
 }
 
 int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
-        int r;
-        FILE *f;
+        _cleanup_fclose_ FILE *f = NULL;
         char fn[PATH_MAX], line[LINE_MAX], *p;
 
         assert(pid > 0);
@@ -559,22 +571,23 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
         assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
         char_array_0(fn);
 
-        if (!(f = fopen(fn, "re")))
+        f = fopen(fn, "re");
+        if (!f)
                 return -errno;
 
-        if (!(fgets(line, sizeof(line), f))) {
-                r = feof(f) ? -EIO : -errno;
-                fclose(f);
-                return r;
-        }
+        if (!fgets(line, sizeof(line), f)) {
+                if (ferror(f))
+                        return -errno;
 
-        fclose(f);
+                return -EIO;
+        }
 
         /* Let's skip the pid and comm fields. The latter is enclosed
          * in () but does not escape any () in its value, so let's
          * skip over it manually */
 
-        if (!(p = strrchr(line, ')')))
+        p = strrchr(line, ')');
+        if (!p)
                 return -EIO;
 
         p++;
@@ -607,37 +620,28 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
 }
 
 int write_one_line_file(const char *fn, const char *line) {
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
 
         assert(fn);
         assert(line);
 
-        if (!(f = fopen(fn, "we")))
+        f = fopen(fn, "we");
+        if (!f)
                 return -errno;
 
         errno = 0;
-        if (fputs(line, f) < 0) {
-                r = -errno;
-                goto finish;
-        }
+        if (fputs(line, f) < 0)
+                return errno ? -errno : -EIO;
 
         if (!endswith(line, "\n"))
                 fputc('\n', f);
 
         fflush(f);
 
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else
-                r = 0;
+        if (ferror(f))
+                return errno ? -errno : -EIO;
 
-finish:
-        fclose(f);
-        return r;
+        return 0;
 }
 
 int fchmod_umask(int fd, mode_t m) {
@@ -699,8 +703,7 @@ finish:
 }
 
 int read_one_line_file(const char *fn, char **line) {
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
         char t[LINE_MAX], *c;
 
         assert(fn);
@@ -712,50 +715,37 @@ int read_one_line_file(const char *fn, char **line) {
 
         if (!fgets(t, sizeof(t), f)) {
 
-                if (ferror(f)) {
-                        r = -errno;
-                        goto finish;
-                }
+                if (ferror(f))
+                        return errno ? -errno : -EIO;
 
                 t[0] = 0;
         }
 
         c = strdup(t);
-        if (!c) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
+        if (!c)
+                return -ENOMEM;
         truncate_nl(c);
 
         *line = c;
-        r = 0;
-
-finish:
-        fclose(f);
-        return r;
+        return 0;
 }
 
 int read_full_file(const char *fn, char **contents, size_t *size) {
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
         size_t n, l;
-        char *buf = NULL;
+        _cleanup_free_ char *buf = NULL;
         struct stat st;
 
-        if (!(f = fopen(fn, "re")))
+        f = fopen(fn, "re");
+        if (!f)
                 return -errno;
 
-        if (fstat(fileno(f), &st) < 0) {
-                r = -errno;
-                goto finish;
-        }
+        if (fstat(fileno(f), &st) < 0)
+                return -errno;
 
         /* Safety check */
-        if (st.st_size > 4*1024*1024) {
-                r = -E2BIG;
-                goto finish;
-        }
+        if (st.st_size > 4*1024*1024)
+                return -E2BIG;
 
         n = st.st_size > 0 ? st.st_size : LINE_MAX;
         l = 0;
@@ -764,19 +754,16 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
                 char *t;
                 size_t k;
 
-                if (!(t = realloc(buf, n+1))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                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)) {
-                                r = -errno;
-                                goto finish;
-                        }
+                        if (ferror(f))
+                                return -errno;
 
                         break;
                 }
@@ -785,10 +772,8 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
                 n *= 2;
 
                 /* Safety check */
-                if (n > 4*1024*1024) {
-                        r = -E2BIG;
-                        goto finish;
-                }
+                if (n > 4*1024*1024)
+                        return -E2BIG;
         }
 
         buf[l] = 0;
@@ -798,13 +783,7 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
         if (size)
                 *size = l;
 
-        r = 0;
-
-finish:
-        fclose(f);
-        free(buf);
-
-        return r;
+        return 0;
 }
 
 int parse_env_file(
@@ -923,8 +902,7 @@ int load_env_file(
                         continue;
 
                 if (!(u = normalize_env_assignment(p))) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
+                        r = log_oom();
                         goto finish;
                 }
 
@@ -932,8 +910,7 @@ int load_env_file(
                 free(u);
 
                 if (!t) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
+                        r = log_oom();
                         goto finish;
                 }
 
@@ -1097,7 +1074,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 if (h < 0)
                         return h;
 
-                r = join("[", t, "]", NULL);
+                r = strjoin("[", t, "]", NULL);
                 free(t);
 
                 if (!r)
@@ -1158,7 +1135,7 @@ int get_process_exe(pid_t pid, char **name) {
         return r;
 }
 
-int get_process_uid(pid_t pid, uid_t *uid) {
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
         char *p;
         FILE *f;
         int r;
@@ -1190,8 +1167,8 @@ int get_process_uid(pid_t pid, uid_t *uid) {
 
                 l = strstrip(line);
 
-                if (startswith(l, "Uid:")) {
-                        l += 4;
+                if (startswith(l, field)) {
+                        l += strlen(field);
                         l += strspn(l, WHITESPACE);
 
                         l[strcspn(l, WHITESPACE)] = 0;
@@ -1209,6 +1186,14 @@ finish:
         return r;
 }
 
+int get_process_uid(pid_t pid, uid_t *uid) {
+        return get_process_id(pid, "Uid:", uid);
+}
+
+int get_process_gid(pid_t pid, gid_t *gid) {
+        return get_process_id(pid, "Gid:", gid);
+}
+
 char *strnappend(const char *s, const char *suffix, size_t b) {
         size_t a;
         char *r;
@@ -1226,8 +1211,11 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
         assert(suffix);
 
         a = strlen(s);
+        if (b > ((size_t) -1) - a)
+                return NULL;
 
-        if (!(r = new(char, a+b+1)))
+        r = new(char, a+b+1);
+        if (!r)
                 return NULL;
 
         memcpy(r, s, a);
@@ -1314,187 +1302,6 @@ int readlink_and_canonicalize(const char *p, char **r) {
         return 0;
 }
 
-int parent_of_path(const char *path, char **_r) {
-        const char *e, *a = NULL, *b = NULL, *p;
-        char *r;
-        bool slash = false;
-
-        assert(path);
-        assert(_r);
-
-        if (!*path)
-                return -EINVAL;
-
-        for (e = path; *e; e++) {
-
-                if (!slash && *e == '/') {
-                        a = b;
-                        b = e;
-                        slash = true;
-                } else if (slash && *e != '/')
-                        slash = false;
-        }
-
-        if (*(e-1) == '/')
-                p = a;
-        else
-                p = b;
-
-        if (!p)
-                return -EINVAL;
-
-        if (p == path)
-                r = strdup("/");
-        else
-                r = strndup(path, p-path);
-
-        if (!r)
-                return -ENOMEM;
-
-        *_r = r;
-        return 0;
-}
-
-
-char *file_name_from_path(const char *p) {
-        char *r;
-
-        assert(p);
-
-        if ((r = strrchr(p, '/')))
-                return r + 1;
-
-        return (char*) p;
-}
-
-bool path_is_absolute(const char *p) {
-        assert(p);
-
-        return p[0] == '/';
-}
-
-bool is_path(const char *p) {
-
-        return !!strchr(p, '/');
-}
-
-char *path_make_absolute(const char *p, const char *prefix) {
-        assert(p);
-
-        /* Makes every item in the list an absolute path by prepending
-         * the prefix, if specified and necessary */
-
-        if (path_is_absolute(p) || !prefix)
-                return strdup(p);
-
-        return join(prefix, "/", p, NULL);
-}
-
-char *path_make_absolute_cwd(const char *p) {
-        char *cwd, *r;
-
-        assert(p);
-
-        /* Similar to path_make_absolute(), but prefixes with the
-         * current working directory. */
-
-        if (path_is_absolute(p))
-                return strdup(p);
-
-        if (!(cwd = get_current_dir_name()))
-                return NULL;
-
-        r = path_make_absolute(p, cwd);
-        free(cwd);
-
-        return r;
-}
-
-char **strv_path_make_absolute_cwd(char **l) {
-        char **s;
-
-        /* Goes through every item in the string list and makes it
-         * absolute. This works in place and won't rollback any
-         * changes on failure. */
-
-        STRV_FOREACH(s, l) {
-                char *t;
-
-                if (!(t = path_make_absolute_cwd(*s)))
-                        return NULL;
-
-                free(*s);
-                *s = t;
-        }
-
-        return l;
-}
-
-char **strv_path_canonicalize(char **l) {
-        char **s;
-        unsigned k = 0;
-        bool enomem = false;
-
-        if (strv_isempty(l))
-                return l;
-
-        /* Goes through every item in the string list and canonicalize
-         * the path. This works in place and won't rollback any
-         * changes on failure. */
-
-        STRV_FOREACH(s, l) {
-                char *t, *u;
-
-                t = path_make_absolute_cwd(*s);
-                free(*s);
-
-                if (!t) {
-                        enomem = true;
-                        continue;
-                }
-
-                errno = 0;
-                u = canonicalize_file_name(t);
-                free(t);
-
-                if (!u) {
-                        if (errno == ENOMEM || !errno)
-                                enomem = true;
-
-                        continue;
-                }
-
-                l[k++] = u;
-        }
-
-        l[k] = NULL;
-
-        if (enomem)
-                return NULL;
-
-        return l;
-}
-
-char **strv_path_remove_empty(char **l) {
-        char **f, **t;
-
-        if (!l)
-                return NULL;
-
-        for (f = t = l; *f; f++) {
-
-                if (dir_is_empty(*f) > 0) {
-                        free(*f);
-                        continue;
-                }
-
-                *(t++) = *f;
-        }
-
-        *t = NULL;
-        return l;
-}
-
 int reset_all_signal_handlers(void) {
         int sig;
 
@@ -1759,19 +1566,25 @@ char *cescape(const char *s) {
         return r;
 }
 
-char *cunescape_length(const char *s, size_t length) {
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
         char *r, *t;
         const char *f;
+        size_t pl;
 
         assert(s);
 
-        /* Undoes C style string escaping */
+        /* Undoes C style string escaping, and optionally prefixes it. */
 
-        r = new(char, length+1);
+        pl = prefix ? strlen(prefix) : 0;
+
+        r = new(char, pl+length+1);
         if (!r)
                 return r;
 
-        for (f = s, t = r; f < s + length; f++) {
+        if (prefix)
+                memcpy(r, prefix, pl);
+
+        for (f = s, t = r + pl; f < s + length; f++) {
 
                 if (*f != '\\') {
                         *(t++) = *f;
@@ -1882,7 +1695,13 @@ finish:
         return r;
 }
 
+char *cunescape_length(const char *s, size_t length) {
+        return cunescape_length_with_prefix(s, length, NULL);
+}
+
 char *cunescape(const char *s) {
+        assert(s);
+
         return cunescape_length(s, strlen(s));
 }
 
@@ -1894,7 +1713,8 @@ char *xescape(const char *s, const char *bad) {
          * chars, in \xFF style escaping. May be reversed with
          * cunescape. */
 
-        if (!(r = new(char, strlen(s)*4+1)))
+        r = new(char, strlen(s) * 4 + 1);
+        if (!r)
                 return NULL;
 
         for (f = s, t = r; *f; f++) {
@@ -1973,128 +1793,26 @@ char *bus_path_unescape(const char *f) {
         return r;
 }
 
-char *path_kill_slashes(char *path) {
-        char *f, *t;
-        bool slash = false;
-
-        /* Removes redundant inner and trailing slashes. Modifies the
-         * passed string in-place.
-         *
-         * ///foo///bar/ becomes /foo/bar
-         */
-
-        for (f = path, t = path; *f; f++) {
-
-                if (*f == '/') {
-                        slash = true;
-                        continue;
-                }
-
-                if (slash) {
-                        slash = false;
-                        *(t++) = '/';
-                }
-
-                *(t++) = *f;
-        }
+char *ascii_strlower(char *t) {
+        char *p;
 
-        /* Special rule, if we are talking of the root directory, a
-        trailing slash is good */
+        assert(t);
 
-        if (t == path && slash)
-                *(t++) = '/';
+        for (p = t; *p; p++)
+                if (*p >= 'A' && *p <= 'Z')
+                        *p = *p - 'A' + 'a';
 
-        *t = 0;
-        return path;
+        return t;
 }
 
-bool path_startswith(const char *path, const char *prefix) {
-        assert(path);
-        assert(prefix);
-
-        if ((path[0] == '/') != (prefix[0] == '/'))
-                return false;
-
-        for (;;) {
-                size_t a, b;
-
-                path += strspn(path, "/");
-                prefix += strspn(prefix, "/");
-
-                if (*prefix == 0)
-                        return true;
-
-                if (*path == 0)
-                        return false;
-
-                a = strcspn(path, "/");
-                b = strcspn(prefix, "/");
-
-                if (a != b)
-                        return false;
-
-                if (memcmp(path, prefix, a) != 0)
-                        return false;
-
-                path += a;
-                prefix += b;
-        }
-}
-
-bool path_equal(const char *a, const char *b) {
-        assert(a);
-        assert(b);
-
-        if ((a[0] == '/') != (b[0] == '/'))
-                return false;
-
-        for (;;) {
-                size_t j, k;
-
-                a += strspn(a, "/");
-                b += strspn(b, "/");
-
-                if (*a == 0 && *b == 0)
-                        return true;
-
-                if (*a == 0 || *b == 0)
-                        return false;
-
-                j = strcspn(a, "/");
-                k = strcspn(b, "/");
-
-                if (j != k)
-                        return false;
-
-                if (memcmp(a, b, j) != 0)
-                        return false;
-
-                a += j;
-                b += k;
-        }
-}
-
-char *ascii_strlower(char *t) {
-        char *p;
-
-        assert(t);
-
-        for (p = t; *p; p++)
-                if (*p >= 'A' && *p <= 'Z')
-                        *p = *p - 'A' + 'a';
-
-        return t;
-}
-
-bool ignore_file(const char *filename) {
-        assert(filename);
+static bool ignore_file_allow_backup(const char *filename) {
+        assert(filename);
 
         return
                 filename[0] == '.' ||
                 streq(filename, "lost+found") ||
                 streq(filename, "aquota.user") ||
                 streq(filename, "aquota.group") ||
-                endswith(filename, "~") ||
                 endswith(filename, ".rpmnew") ||
                 endswith(filename, ".rpmsave") ||
                 endswith(filename, ".rpmorig") ||
@@ -2103,6 +1821,15 @@ bool ignore_file(const char *filename) {
                 endswith(filename, ".swp");
 }
 
+bool ignore_file(const char *filename) {
+        assert(filename);
+
+        if (endswith(filename, "~"))
+                return false;
+
+        return ignore_file_allow_backup(filename);
+}
+
 int fd_nonblock(int fd, bool nonblock) {
         int flags;
 
@@ -2236,7 +1963,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
 
         sec = (time_t) (t / USEC_PER_SEC);
 
-        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+        if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
                 return NULL;
 
         return buf;
@@ -2355,29 +2082,23 @@ char *format_timespan(char *buf, size_t l, usec_t t) {
 }
 
 bool fstype_is_network(const char *fstype) {
-        static const char * const table[] = {
-                "cifs",
-                "smbfs",
-                "ncpfs",
-                "nfs",
-                "nfs4",
-                "gfs",
-                "gfs2"
-        };
-
-        unsigned i;
-
-        for (i = 0; i < ELEMENTSOF(table); i++)
-                if (streq(table[i], fstype))
-                        return true;
+        static const char table[] =
+                "cifs\0"
+                "smbfs\0"
+                "ncpfs\0"
+                "nfs\0"
+                "nfs4\0"
+                "gfs\0"
+                "gfs2\0";
 
-        return false;
+        return nulstr_contains(table, fstype);
 }
 
 int chvt(int vt) {
-        int fd, r = 0;
+        _cleanup_close_ int fd;
 
-        if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
                 return -errno;
 
         if (vt < 0) {
@@ -2386,20 +2107,16 @@ int chvt(int vt) {
                         0
                 };
 
-                if (ioctl(fd, TIOCLINUX, tiocl) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
+                if (ioctl(fd, TIOCLINUX, tiocl) < 0)
+                        return -errno;
 
                 vt = tiocl[0] <= 0 ? 1 : tiocl[0];
         }
 
         if (ioctl(fd, VT_ACTIVATE, vt) < 0)
-                r = -errno;
+                return -errno;
 
-fail:
-        close_nointr_nofail(fd);
-        return r;
+        return 0;
 }
 
 int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
@@ -2462,28 +2179,25 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
 }
 
 int ask(char *ret, const char *replies, const char *text, ...) {
-        bool on_tty;
 
         assert(ret);
         assert(replies);
         assert(text);
 
-        on_tty = isatty(STDOUT_FILENO);
-
         for (;;) {
                 va_list ap;
                 char c;
                 int r;
                 bool need_nl = true;
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_ON, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_OFF, stdout);
 
                 fflush(stdout);
@@ -2603,12 +2317,14 @@ int open_terminal(const char *name, int mode) {
          */
 
         for (;;) {
-                if ((fd = open(name, mode)) >= 0)
+                fd = open(name, mode);
+                if (fd >= 0)
                         break;
 
                 if (errno != EIO)
                         return -errno;
 
+                /* Max 1s in total */
                 if (c >= 20)
                         return -errno;
 
@@ -2619,7 +2335,8 @@ int open_terminal(const char *name, int mode) {
         if (fd < 0)
                 return -errno;
 
-        if ((r = isatty(fd)) < 0) {
+        r = isatty(fd);
+        if (r < 0) {
                 close_nointr_nofail(fd);
                 return -errno;
         }
@@ -2671,8 +2388,16 @@ int flush_fd(int fd) {
         }
 }
 
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
-        int fd = -1, notify = -1, r, wd = -1;
+int acquire_terminal(
+                const char *name,
+                bool fail,
+                bool force,
+                bool ignore_tiocstty_eperm,
+                usec_t timeout) {
+
+        int fd = -1, notify = -1, r = 0, wd = -1;
+        usec_t ts = 0;
+        struct sigaction sa_old, sa_new;
 
         assert(name);
 
@@ -2689,40 +2414,57 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst
          * on the same tty as an untrusted user this should not be a
          * problem. (Which he probably should not do anyway.) */
 
+        if (timeout != (usec_t) -1)
+                ts = now(CLOCK_MONOTONIC);
+
         if (!fail && !force) {
-                if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+                notify = inotify_init1(IN_CLOEXEC | (timeout != (usec_t) -1 ? IN_NONBLOCK : 0));
+                if (notify < 0) {
                         r = -errno;
                         goto fail;
                 }
 
-                if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+                wd = inotify_add_watch(notify, name, IN_CLOSE);
+                if (wd < 0) {
                         r = -errno;
                         goto fail;
                 }
         }
 
         for (;;) {
-                if (notify >= 0)
-                        if ((r = flush_fd(notify)) < 0)
+                if (notify >= 0) {
+                        r = flush_fd(notify);
+                        if (r < 0)
                                 goto fail;
+                }
 
                 /* We pass here O_NOCTTY only so that we can check the return
                  * value TIOCSCTTY and have a reliable way to figure out if we
                  * successfully became the controlling process of the tty */
-                if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+                if (fd < 0)
                         return fd;
 
+                /* 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 */
-                r = ioctl(fd, TIOCSCTTY, force);
+                if (ioctl(fd, TIOCSCTTY, force) < 0)
+                        r = -errno;
+
+                assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
 
                 /* Sometimes it makes sense to ignore TIOCSCTTY
                  * returning EPERM, i.e. when very likely we already
                  * are have this controlling terminal. */
-                if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
+                if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
                         r = 0;
 
-                if (r < 0 && (force || fail || errno != EPERM)) {
-                        r = -errno;
+                if (r < 0 && (force || fail || r != -EPERM)) {
                         goto fail;
                 }
 
@@ -2738,9 +2480,29 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst
                         ssize_t l;
                         struct inotify_event *e;
 
-                        if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+                        if (timeout != (usec_t) -1) {
+                                usec_t n;
+
+                                n = now(CLOCK_MONOTONIC);
+                                if (ts + timeout < n) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+
+                                r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+                                if (r < 0)
+                                        goto fail;
+
+                                if (r == 0) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+                        }
+
+                        l = read(notify, inotify_buffer, sizeof(inotify_buffer));
+                        if (l < 0) {
 
-                                if (errno == EINTR)
+                                if (errno == EINTR || errno == EAGAIN)
                                         continue;
 
                                 r = -errno;
@@ -2987,54 +2749,39 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
         return n;
 }
 
-int path_is_mount_point(const char *t, bool allow_symlink) {
-        struct stat a, b;
-        char *parent;
-        int r;
-
-        if (allow_symlink)
-                r = stat(t, &a);
-        else
-                r = lstat(t, &a);
-
-        if (r < 0) {
-                if (errno == ENOENT)
-                        return 0;
-
-                return -errno;
-        }
-
-        r = parent_of_path(t, &parent);
-        if (r < 0)
-                return r;
-
-        r = lstat(parent, &b);
-        free(parent);
-
-        if (r < 0)
-                return -errno;
-
-        return a.st_dev != b.st_dev;
-}
-
 int parse_usec(const char *t, usec_t *usec) {
         static const struct {
                 const char *suffix;
                 usec_t usec;
         } table[] = {
+                { "seconds", USEC_PER_SEC },
+                { "second", USEC_PER_SEC },
                 { "sec", USEC_PER_SEC },
                 { "s", USEC_PER_SEC },
+                { "minutes", USEC_PER_MINUTE },
+                { "minute", USEC_PER_MINUTE },
                 { "min", USEC_PER_MINUTE },
+                { "months", USEC_PER_MONTH },
+                { "month", USEC_PER_MONTH },
+                { "msec", USEC_PER_MSEC },
+                { "ms", USEC_PER_MSEC },
+                { "m", USEC_PER_MINUTE },
+                { "hours", USEC_PER_HOUR },
+                { "hour", USEC_PER_HOUR },
                 { "hr", USEC_PER_HOUR },
                 { "h", USEC_PER_HOUR },
+                { "days", USEC_PER_DAY },
+                { "day", USEC_PER_DAY },
                 { "d", USEC_PER_DAY },
+                { "weeks", USEC_PER_WEEK },
+                { "week", USEC_PER_WEEK },
                 { "w", USEC_PER_WEEK },
-                { "msec", USEC_PER_MSEC },
-                { "ms", USEC_PER_MSEC },
-                { "m", USEC_PER_MINUTE },
+                { "years", USEC_PER_YEAR },
+                { "year", USEC_PER_YEAR },
+                { "y", USEC_PER_YEAR },
                 { "usec", 1ULL },
                 { "us", 1ULL },
-                { "", USEC_PER_SEC },
+                { "", USEC_PER_SEC }, /* default is sec */
         };
 
         const char *p;
@@ -3080,6 +2827,86 @@ int parse_usec(const char *t, usec_t *usec) {
         return 0;
 }
 
+int parse_nsec(const char *t, nsec_t *nsec) {
+        static const struct {
+                const char *suffix;
+                nsec_t nsec;
+        } table[] = {
+                { "seconds", NSEC_PER_SEC },
+                { "second", NSEC_PER_SEC },
+                { "sec", NSEC_PER_SEC },
+                { "s", NSEC_PER_SEC },
+                { "minutes", NSEC_PER_MINUTE },
+                { "minute", NSEC_PER_MINUTE },
+                { "min", NSEC_PER_MINUTE },
+                { "months", NSEC_PER_MONTH },
+                { "month", NSEC_PER_MONTH },
+                { "msec", NSEC_PER_MSEC },
+                { "ms", NSEC_PER_MSEC },
+                { "m", NSEC_PER_MINUTE },
+                { "hours", NSEC_PER_HOUR },
+                { "hour", NSEC_PER_HOUR },
+                { "hr", NSEC_PER_HOUR },
+                { "h", NSEC_PER_HOUR },
+                { "days", NSEC_PER_DAY },
+                { "day", NSEC_PER_DAY },
+                { "d", NSEC_PER_DAY },
+                { "weeks", NSEC_PER_WEEK },
+                { "week", NSEC_PER_WEEK },
+                { "w", NSEC_PER_WEEK },
+                { "years", NSEC_PER_YEAR },
+                { "year", NSEC_PER_YEAR },
+                { "y", NSEC_PER_YEAR },
+                { "usec", NSEC_PER_USEC },
+                { "us", NSEC_PER_USEC },
+                { "nsec", 1ULL },
+                { "ns", 1ULL },
+                { "", 1ULL }, /* default is nsec */
+        };
+
+        const char *p;
+        nsec_t r = 0;
+
+        assert(t);
+        assert(nsec);
+
+        p = t;
+        do {
+                long long l;
+                char *e;
+                unsigned i;
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno != 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                r += (nsec_t) l * table[i].nsec;
+                                p = e + strlen(table[i].suffix);
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        } while (*p != 0);
+
+        *nsec = r;
+
+        return 0;
+}
+
 int parse_bytes(const char *t, off_t *bytes) {
         static const struct {
                 const char *suffix;
@@ -3143,9 +2970,9 @@ int make_stdio(int fd) {
 
         assert(fd >= 0);
 
-        r = dup2(fd, STDIN_FILENO);
-        s = dup2(fd, STDOUT_FILENO);
-        t = dup2(fd, STDERR_FILENO);
+        r = dup3(fd, STDIN_FILENO, 0);
+        s = dup3(fd, STDOUT_FILENO, 0);
+        t = dup3(fd, STDERR_FILENO, 0);
 
         if (fd >= 3)
                 close_nointr_nofail(fd);
@@ -3153,9 +2980,7 @@ int make_stdio(int fd) {
         if (r < 0 || s < 0 || t < 0)
                 return -errno;
 
-        fd_cloexec(STDIN_FILENO, false);
-        fd_cloexec(STDOUT_FILENO, false);
-        fd_cloexec(STDERR_FILENO, false);
+        /* We rely here that the new fd has O_CLOEXEC not set */
 
         return 0;
 }
@@ -3163,7 +2988,8 @@ int make_stdio(int fd) {
 int make_null_stdio(void) {
         int null_fd;
 
-        if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+        null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+        if (null_fd < 0)
                 return -errno;
 
         return make_stdio(null_fd);
@@ -3180,45 +3006,39 @@ bool is_device_path(const char *path) {
 }
 
 int dir_is_empty(const char *path) {
-        DIR *d;
+        _cleanup_closedir_ DIR *d;
         int r;
-        struct dirent buf, *de;
 
-        if (!(d = opendir(path)))
+        d = opendir(path);
+        if (!d)
                 return -errno;
 
         for (;;) {
-                if ((r = readdir_r(d, &buf, &de)) > 0) {
-                        r = -r;
-                        break;
-                }
+                struct dirent *de;
+                union dirent_storage buf;
 
-                if (!de) {
-                        r = 1;
-                        break;
-                }
+                r = readdir_r(d, &buf.de, &de);
+                if (r > 0)
+                        return -r;
 
-                if (!ignore_file(de->d_name)) {
-                        r = 0;
-                        break;
-                }
-        }
+                if (!de)
+                        return 1;
 
-        closedir(d);
-        return r;
+                if (!ignore_file(de->d_name))
+                        return 0;
+        }
 }
 
 unsigned long long random_ull(void) {
-        int fd;
+        _cleanup_close_ int fd;
         uint64_t ull;
         ssize_t r;
 
-        if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
                 goto fallback;
 
         r = loop_read(fd, &ull, sizeof(ull), true);
-        close_nointr_nofail(fd);
-
         if (r != sizeof(ull))
                 goto fallback;
 
@@ -3276,41 +3096,40 @@ char* gethostname_malloc(void) {
 
         assert_se(uname(&u) >= 0);
 
-        if (u.nodename[0])
+        if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
                 return strdup(u.nodename);
 
         return strdup(u.sysname);
 }
 
-char* getlogname_malloc(void) {
-        uid_t uid;
+bool hostname_is_set(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        return !isempty(u.nodename) && !streq(u.nodename, "(none)");
+}
+
+static char *lookup_uid(uid_t uid) {
         long bufsize;
-        char *buf, *name;
+        char *name;
+        _cleanup_free_ char *buf = NULL;
         struct passwd pwbuf, *pw = NULL;
-        struct stat st;
-
-        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
-                uid = st.st_uid;
-        else
-                uid = getuid();
 
         /* Shortcut things to avoid NSS lookups */
         if (uid == 0)
                 return strdup("root");
 
-        if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+        bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+        if (bufsize <= 0)
                 bufsize = 4096;
 
-        if (!(buf = malloc(bufsize)))
+        buf = malloc(bufsize);
+        if (!buf)
                 return NULL;
 
-        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
-                name = strdup(pw->pw_name);
-                free(buf);
-                return name;
-        }
-
-        free(buf);
+        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
+                return strdup(pw->pw_name);
 
         if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
                 return NULL;
@@ -3318,18 +3137,42 @@ char* getlogname_malloc(void) {
         return name;
 }
 
+char* getlogname_malloc(void) {
+        uid_t uid;
+        struct stat st;
+
+        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        return lookup_uid(uid);
+}
+
+char *getusername_malloc(void) {
+        const char *e;
+
+        e = getenv("USER");
+        if (e)
+                return strdup(e);
+
+        return lookup_uid(getuid());
+}
+
 int getttyname_malloc(int fd, char **r) {
         char path[PATH_MAX], *c;
         int k;
 
         assert(r);
 
-        if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
+        k = ttyname_r(fd, path, sizeof(path));
+        if (k != 0)
                 return -k;
 
         char_array_0(path);
 
-        if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
+        c = strdup(startswith(path, "/dev/") ? path + 5 : path);
+        if (!c)
                 return -ENOMEM;
 
         *r = c;
@@ -3340,7 +3183,8 @@ int getttyname_harder(int fd, char **r) {
         int k;
         char *s;
 
-        if ((k = getttyname_malloc(fd, &s)) < 0)
+        k = getttyname_malloc(fd, &s);
+        if (k < 0)
                 return k;
 
         if (streq(s, "tty")) {
@@ -3458,29 +3302,32 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         return 0;
 }
 
-static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
         DIR *d;
         int ret = 0;
 
         assert(fd >= 0);
 
         /* This returns the first error we run into, but nevertheless
-         * tries to go on */
+         * tries to go on. This closes the passed fd. */
 
-        if (!(d = fdopendir(fd))) {
+        d = fdopendir(fd);
+        if (!d) {
                 close_nointr_nofail(fd);
 
                 return errno == ENOENT ? 0 : -errno;
         }
 
         for (;;) {
-                struct dirent buf, *de;
-                bool is_dir, keep_around = false;
+                struct dirent *de;
+                union dirent_storage buf;
+                bool is_dir, keep_around;
+                struct stat st;
                 int r;
 
-                if ((r = readdir_r(d, &buf, &de)) != 0) {
-                        if (ret == 0)
-                                ret = -r;
+                r = readdir_r(d, &buf.de, &de);
+                if (r != 0 && ret == 0) {
+                        ret = -r;
                         break;
                 }
 
@@ -3490,54 +3337,43 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
-                if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
-
+                if (de->d_type == DT_UNKNOWN ||
+                    honour_sticky ||
+                    (de->d_type == DT_DIR && root_dev)) {
                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        if (honour_sticky)
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-
                         is_dir = S_ISDIR(st.st_mode);
-
+                        keep_around =
+                                honour_sticky &&
+                                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                (st.st_mode & S_ISVTX);
                 } else {
-                        if (honour_sticky) {
-                                struct stat st;
-
-                                if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                        if (ret == 0 && errno != ENOENT)
-                                                ret = -errno;
-                                        continue;
-                                }
-
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-                        }
-
                         is_dir = de->d_type == DT_DIR;
+                        keep_around = false;
                 }
 
                 if (is_dir) {
                         int subdir_fd;
 
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        if (root_dev && st.st_dev != root_dev->st_dev)
+                                continue;
+
+                        subdir_fd = openat(fd, de->d_name,
+                                           O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
                         if (subdir_fd < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
-                                if (ret == 0)
-                                        ret = r;
-                        }
+                        r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
+                        if (r < 0 && ret == 0)
+                                ret = r;
 
                         if (!keep_around)
                                 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
@@ -3559,27 +3395,84 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
         return ret;
 }
 
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
-        int fd;
-        int r;
-
-        assert(path);
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        struct statfs s;
 
-        if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+        assert(fd >= 0);
 
-                if (errno != ENOTDIR)
-                        return -errno;
+        if (fstatfs(fd, &s) < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
 
-                if (delete_root && !only_dirs)
-                        if (unlink(path) < 0)
-                                return -errno;
+        /* We refuse to clean disk file systems with this call. This
+         * is extra paranoia just to be sure we never ever remove
+         * non-state data */
 
-                return 0;
+        if (s.f_type != TMPFS_MAGIC &&
+            s.f_type != RAMFS_MAGIC) {
+                log_error("Attempted to remove disk file system, and we can't allow that.");
+                close_nointr_nofail(fd);
+                return -EPERM;
         }
 
-        r = rm_rf_children(fd, only_dirs, honour_sticky);
+        return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
 
-        if (delete_root) {
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+        int fd, r;
+        struct statfs s;
+
+        assert(path);
+
+        /* We refuse to clean the root file system with this
+         * call. This is extra paranoia to never cause a really
+         * seriously broken system. */
+        if (path_equal(path, "/")) {
+                log_error("Attempted to remove entire root file system, and we can't allow that.");
+                return -EPERM;
+        }
+
+        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+        if (fd < 0) {
+
+                if (errno != ENOTDIR)
+                        return -errno;
+
+                if (!dangerous) {
+                        if (statfs(path, &s) < 0)
+                                return -errno;
+
+                        if (s.f_type != TMPFS_MAGIC &&
+                            s.f_type != RAMFS_MAGIC) {
+                                log_error("Attempted to remove disk file system, and we can't allow that.");
+                                return -EPERM;
+                        }
+                }
+
+                if (delete_root && !only_dirs)
+                        if (unlink(path) < 0 && errno != ENOENT)
+                                return -errno;
+
+                return 0;
+        }
+
+        if (!dangerous) {
+                if (fstatfs(fd, &s) < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                if (s.f_type != TMPFS_MAGIC &&
+                    s.f_type != RAMFS_MAGIC) {
+                        log_error("Attempted to remove disk file system, and we can't allow that.");
+                        close_nointr_nofail(fd);
+                        return -EPERM;
+                }
+        }
+
+        r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
+        if (delete_root) {
 
                 if (honour_sticky && file_is_priv_sticky(path) > 0)
                         return r;
@@ -3593,6 +3486,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
         return r;
 }
 
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         assert(path);
 
@@ -3655,35 +3556,39 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
         }
 }
 
-void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
-        char *s = NULL, *spaces = NULL, *e;
-        int fd = -1, c;
-        size_t emax, sl, left;
+int status_vprintf(const char *status, bool ellipse, 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];
         int n = 0;
 
         assert(format);
 
-        /* This independent of logging, as status messages are
+        /* This is independent of logging, as status messages are
          * optional and go exclusively to the console. */
 
         if (vasprintf(&s, format, ap) < 0)
-                goto finish;
+                return log_oom();
 
         fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
-                goto finish;
+                return fd;
 
         if (ellipse) {
+                char *e;
+                size_t emax, sl;
+                int c;
+
                 c = fd_columns(fd);
                 if (c <= 0)
                         c = 80;
 
-                if (status) {
-                        sl = 2 + 6 + 1; /* " [" status "]" */
-                        emax = (size_t) c > sl ? c - sl - 1 : 0;
-                } else
-                        emax = c - 1;
+                sl = status ? sizeof(status_indent)-1 : 0;
+
+                emax = c - sl - 1;
+                if (emax < 3)
+                        emax = 3;
 
                 e = ellipsize(s, emax, 75);
                 if (e) {
@@ -3693,77 +3598,53 @@ void status_vprintf(const char *status, bool ellipse, const char *format, va_lis
         }
 
         zero(iovec);
-        IOVEC_SET_STRING(iovec[n++], s);
-
-        if (ellipse) {
-                sl = strlen(s);
-                left = emax > sl ? emax - sl : 0;
-                if (left > 0) {
-                        spaces = malloc(left);
-                        if (spaces) {
-                                memset(spaces, ' ', left);
-                                iovec[n].iov_base = spaces;
-                                iovec[n].iov_len = left;
-                                n++;
-                        }
-                }
-        }
 
         if (status) {
-                IOVEC_SET_STRING(iovec[n++], " [");
-                IOVEC_SET_STRING(iovec[n++], status);
-                IOVEC_SET_STRING(iovec[n++], "]\n");
-        } else
-                IOVEC_SET_STRING(iovec[n++], "\n");
+                if (!isempty(status)) {
+                        IOVEC_SET_STRING(iovec[n++], "[");
+                        IOVEC_SET_STRING(iovec[n++], status);
+                        IOVEC_SET_STRING(iovec[n++], "] ");
+                } else
+                        IOVEC_SET_STRING(iovec[n++], status_indent);
+        }
 
-        writev(fd, iovec, n);
+        IOVEC_SET_STRING(iovec[n++], s);
+        IOVEC_SET_STRING(iovec[n++], "\n");
 
-finish:
-        free(s);
-        free(spaces);
+        if (writev(fd, iovec, n) < 0)
+                return -errno;
 
-        if (fd >= 0)
-                close_nointr_nofail(fd);
+        return 0;
 }
 
-void status_printf(const char *status, bool ellipse, const char *format, ...) {
+int status_printf(const char *status, bool ellipse, const char *format, ...) {
         va_list ap;
+        int r;
 
         assert(format);
 
         va_start(ap, format);
-        status_vprintf(status, ellipse, format, ap);
+        r = status_vprintf(status, ellipse, format, ap);
         va_end(ap);
+
+        return r;
 }
 
-void status_welcome(void) {
-        char *pretty_name = NULL, *ansi_color = NULL;
-        const char *const_pretty = NULL, *const_color = NULL;
+int status_welcome(void) {
         int r;
+        _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
 
-        if ((r = parse_env_file("/etc/os-release", NEWLINE,
-                                "PRETTY_NAME", &pretty_name,
-                                "ANSI_COLOR", &ansi_color,
-                                NULL)) < 0) {
-
-                if (r != -ENOENT)
-                        log_warning("Failed to read /etc/os-release: %s", strerror(-r));
-        }
-
-        if (!pretty_name && !const_pretty)
-                const_pretty = "Linux";
-
-        if (!ansi_color && !const_color)
-                const_color = "1";
-
-        status_printf(NULL,
-                      false,
-                      "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
-                      const_color ? const_color : ansi_color,
-                      const_pretty ? const_pretty : pretty_name);
+        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));
 
-        free(ansi_color);
-        free(pretty_name);
+        return status_printf(NULL, false,
+                             "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
+                             isempty(ansi_color) ? "1" : ansi_color,
+                             isempty(pretty_name) ? "Linux" : pretty_name);
 }
 
 char *replace_env(const char *format, char **env) {
@@ -3914,23 +3795,25 @@ int fd_columns(int fd) {
 }
 
 unsigned columns(void) {
-        static __thread int parsed_columns = 0;
         const char *e;
+        unsigned c;
 
-        if (_likely_(parsed_columns > 0))
-                return parsed_columns;
+        if (_likely_(cached_columns > 0))
+                return cached_columns;
 
+        c = 0;
         e = getenv("COLUMNS");
         if (e)
-                parsed_columns = atoi(e);
+                safe_atou(e, &c);
 
-        if (parsed_columns <= 0)
-                parsed_columns = fd_columns(STDOUT_FILENO);
+        if (c <= 0)
+                c = fd_columns(STDOUT_FILENO);
 
-        if (parsed_columns <= 0)
-                parsed_columns = 80;
+        if (c <= 0)
+                c = 80;
 
-        return parsed_columns;
+        cached_columns = c;
+        return c;
 }
 
 int fd_lines(int fd) {
@@ -3947,23 +3830,40 @@ int fd_lines(int fd) {
 }
 
 unsigned lines(void) {
-        static __thread int parsed_lines = 0;
         const char *e;
+        unsigned l;
 
-        if (_likely_(parsed_lines > 0))
-                return parsed_lines;
+        if (_likely_(cached_lines > 0))
+                return cached_lines;
 
+        l = 0;
         e = getenv("LINES");
         if (e)
-                parsed_lines = atoi(e);
+                safe_atou(e, &l);
+
+        if (l <= 0)
+                l = fd_lines(STDOUT_FILENO);
+
+        if (l <= 0)
+                l = 24;
+
+        cached_lines = l;
+        return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+        cached_columns = 0;
+        cached_lines = 0;
+}
 
-        if (parsed_lines <= 0)
-                parsed_lines = fd_lines(STDOUT_FILENO);
+bool on_tty(void) {
+        static int cached_on_tty = -1;
 
-        if (parsed_lines <= 0)
-                parsed_lines = 25;
+        if (_unlikely_(cached_on_tty < 0))
+                cached_on_tty = isatty(STDOUT_FILENO) > 0;
 
-        return parsed_lines;
+        return cached_on_tty;
 }
 
 int running_in_chroot(void) {
@@ -4025,7 +3925,12 @@ int touch(const char *path) {
 
         assert(path);
 
-        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
+        /* This just opens the file for writing, ensuring it
+         * exists. It doesn't call utimensat() the way /usr/bin/touch
+         * does it. */
+
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
+        if (fd < 0)
                 return -errno;
 
         close_nointr_nofail(fd);
@@ -4036,6 +3941,11 @@ char *unquote(const char *s, const char* quotes) {
         size_t l;
         assert(s);
 
+        /* This is rather stupid, simply removes the heading and
+         * trailing quotes if there is one. Doesn't care about
+         * escaping or anything. We should make this smarter one
+         * day...*/
+
         l = strlen(s);
         if (l < 2)
                 return strdup(s);
@@ -4047,39 +3957,40 @@ char *unquote(const char *s, const char* quotes) {
 }
 
 char *normalize_env_assignment(const char *s) {
-        char *name, *value, *p, *r;
+        _cleanup_free_ char *name = NULL, *value = NULL, *p = NULL;
+        char *eq, *r;
 
-        p = strchr(s, '=');
+        eq = strchr(s, '=');
+        if (!eq) {
+                char *t;
 
-        if (!p) {
-                if (!(r = strdup(s)))
+                r = strdup(s);
+                if (!r)
                         return NULL;
 
-                return strstrip(r);
+                t = strstrip(r);
+                if (t == r)
+                        return r;
+
+                memmove(r, t, strlen(t) + 1);
+                return r;
         }
 
-        if (!(name = strndup(s, p - s)))
+        name = strndup(s, eq - s);
+        if (!name)
                 return NULL;
 
-        if (!(p = strdup(p+1))) {
-                free(name);
+        p = strdup(eq + 1);
+        if (!p)
                 return NULL;
-        }
 
         value = unquote(strstrip(p), QUOTES);
-        free(p);
-
-        if (!value) {
-                free(name);
+        if (!value)
                 return NULL;
-        }
 
-        if (asprintf(&r, "%s=%s", name, value) < 0)
+        if (asprintf(&r, "%s=%s", strstrip(name), value) < 0)
                 r = NULL;
 
-        free(value);
-        free(name);
-
         return r;
 }
 
@@ -4113,7 +4024,8 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) {
         assert(name);
         assert(pid > 1);
 
-        if ((r = wait_for_terminate(pid, &status)) < 0) {
+        r = wait_for_terminate(pid, &status);
+        if (r < 0) {
                 log_warning("Failed to wait for %s: %s", name, strerror(-r));
                 return r;
         }
@@ -4136,7 +4048,6 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) {
 
         log_warning("%s failed due to unknown reason.", name);
         return -EPROTO;
-
 }
 
 _noreturn_ void freeze(void) {
@@ -4177,10 +4088,12 @@ DIR *xopendirat(int fd, const char *name, int flags) {
         int nfd;
         DIR *d;
 
-        if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+        nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags);
+        if (nfd < 0)
                 return NULL;
 
-        if (!(d = fdopendir(nfd))) {
+        d = fdopendir(nfd);
+        if (!d) {
                 close_nointr_nofail(nfd);
                 return NULL;
         }
@@ -4192,7 +4105,8 @@ int signal_from_string_try_harder(const char *s) {
         int signo;
         assert(s);
 
-        if ((signo = signal_from_string(s)) <= 0)
+        signo = signal_from_string(s);
+        if (signo <= 0)
                 if (startswith(s, "SIG"))
                         return signal_from_string(s+3);
 
@@ -4228,52 +4142,46 @@ void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
         }
 }
 
-char *fstab_node_to_udev_node(const char *p) {
+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 */
 
-        if (startswith(p, "LABEL=")) {
-
-                if (!(u = unquote(p+6, "\"\'")))
-                        return NULL;
-
-                t = xescape(u, "/ ");
-                free(u);
-
-                if (!t)
-                        return NULL;
+        u = unquote(tagvalue, "\"\'");
+        if (u == NULL)
+                return NULL;
 
-                r = asprintf(&dn, "/dev/disk/by-label/%s", t);
-                free(t);
+        t = xescape(u, "/ ");
+        free(u);
 
-                if (r < 0)
-                        return NULL;
+        if (t == NULL)
+                return NULL;
 
-                return dn;
-        }
+        r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t);
+        free(t);
 
-        if (startswith(p, "UUID=")) {
+        if (r < 0)
+                return NULL;
 
-                if (!(u = unquote(p+5, "\"\'")))
-                        return NULL;
+        return dn;
+}
 
-                t = xescape(u, "/ ");
-                free(u);
+char *fstab_node_to_udev_node(const char *p) {
+        assert(p);
 
-                if (!t)
-                        return NULL;
+        if (startswith(p, "LABEL="))
+                return tag_to_udev_node(p+6, "label");
 
-                r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
-                free(t);
+        if (startswith(p, "UUID="))
+                return tag_to_udev_node(p+5, "uuid");
 
-                if (r < 0)
-                        return NULL;
+        if (startswith(p, "PARTUUID="))
+                return tag_to_udev_node(p+9, "partuuid");
 
-                return dn;
-        }
+        if (startswith(p, "PARTLABEL="))
+                return tag_to_udev_node(p+10, "partlabel");
 
         return strdup(p);
 }
@@ -4372,7 +4280,12 @@ bool dirent_is_file(const struct dirent *de) {
 bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
         assert(de);
 
-        if (!dirent_is_file(de))
+        if (de->d_type != DT_REG &&
+            de->d_type != DT_LNK &&
+            de->d_type != DT_UNKNOWN)
+                return false;
+
+        if (ignore_file_allow_backup(de->d_name))
                 return false;
 
         return endswith(de->d_name, suffix);
@@ -4415,7 +4328,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
                         continue;
 
                 if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
-                        log_error("Out of memory");
+                        log_oom();
                         continue;
                 }
 
@@ -4434,8 +4347,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
                                 _argv[1] = NULL;
                                 argv = _argv;
                         } else
-                                if (!argv[0])
-                                        argv[0] = path;
+                                argv[0] = path;
 
                         execv(path, argv);
 
@@ -4467,7 +4379,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
                 }
 
                 if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
-                        if (!is_clean_exit(si.si_code, si.si_status)) {
+                        if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
                                 if (si.si_code == CLD_EXITED)
                                         log_error("%s exited with exit status %i.", path, si.si_status);
                                 else
@@ -4515,134 +4427,6 @@ bool plymouth_running(void) {
         return access("/run/plymouth/pid", F_OK) >= 0;
 }
 
-void parse_syslog_priority(char **p, int *priority) {
-        int a = 0, b = 0, c = 0;
-        int k;
-
-        assert(p);
-        assert(*p);
-        assert(priority);
-
-        if ((*p)[0] != '<')
-                return;
-
-        if (!strchr(*p, '>'))
-                return;
-
-        if ((*p)[2] == '>') {
-                c = undecchar((*p)[1]);
-                k = 3;
-        } else if ((*p)[3] == '>') {
-                b = undecchar((*p)[1]);
-                c = undecchar((*p)[2]);
-                k = 4;
-        } else if ((*p)[4] == '>') {
-                a = undecchar((*p)[1]);
-                b = undecchar((*p)[2]);
-                c = undecchar((*p)[3]);
-                k = 5;
-        } else
-                return;
-
-        if (a < 0 || b < 0 || c < 0)
-                return;
-
-        *priority = a*100+b*10+c;
-        *p += k;
-}
-
-void skip_syslog_pid(char **buf) {
-        char *p;
-
-        assert(buf);
-        assert(*buf);
-
-        p = *buf;
-
-        if (*p != '[')
-                return;
-
-        p++;
-        p += strspn(p, "0123456789");
-
-        if (*p != ']')
-                return;
-
-        p++;
-
-        *buf = p;
-}
-
-void skip_syslog_date(char **buf) {
-        enum {
-                LETTER,
-                SPACE,
-                NUMBER,
-                SPACE_OR_NUMBER,
-                COLON
-        } sequence[] = {
-                LETTER, LETTER, LETTER,
-                SPACE,
-                SPACE_OR_NUMBER, NUMBER,
-                SPACE,
-                SPACE_OR_NUMBER, NUMBER,
-                COLON,
-                SPACE_OR_NUMBER, NUMBER,
-                COLON,
-                SPACE_OR_NUMBER, NUMBER,
-                SPACE
-        };
-
-        char *p;
-        unsigned i;
-
-        assert(buf);
-        assert(*buf);
-
-        p = *buf;
-
-        for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
-                if (!*p)
-                        return;
-
-                switch (sequence[i]) {
-
-                case SPACE:
-                        if (*p != ' ')
-                                return;
-                        break;
-
-                case SPACE_OR_NUMBER:
-                        if (*p == ' ')
-                                break;
-
-                        /* fall through */
-
-                case NUMBER:
-                        if (*p < '0' || *p > '9')
-                                return;
-
-                        break;
-
-                case LETTER:
-                        if (!(*p >= 'A' && *p <= 'Z') &&
-                            !(*p >= 'a' && *p <= 'z'))
-                                return;
-
-                        break;
-
-                case COLON:
-                        if (*p != ':')
-                                return;
-                        break;
-
-                }
-        }
-
-        *buf = p;
-}
-
 char* strshorten(char *s, size_t l) {
         assert(s);
 
@@ -4747,7 +4531,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         if (!t)
                 return -ENOMEM;
 
-        fn = file_name_from_path(path);
+        fn = path_get_file_name(path);
         k = fn-path;
         memcpy(t, path, k);
         t[k] = '.';
@@ -4863,420 +4647,81 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-static int files_add(Hashmap *h, const char *path, const char *suffix) {
-        DIR *dir;
-        struct dirent buffer, *de;
-        int r = 0;
+int copy_file(const char *from, const char *to) {
+        int r, fdf, fdt;
 
-        dir = opendir(path);
-        if (!dir) {
-                if (errno == ENOENT)
-                        return 0;
+        assert(from);
+        assert(to);
+
+        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        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);
                 return -errno;
         }
 
         for (;;) {
-                int k;
-                char *p, *f;
+                char buf[PIPE_BUF];
+                ssize_t n, k;
 
-                k = readdir_r(dir, &buffer, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
+                n = read(fdf, buf, sizeof(buf));
+                if (n < 0) {
+                        r = -errno;
+
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
+                        unlink(to);
+
+                        return r;
                 }
 
-                if (!de)
+                if (n == 0)
                         break;
 
-                if (!dirent_is_file_with_suffix(de, suffix))
-                        continue;
+                errno = 0;
+                k = loop_write(fdt, buf, n, false);
+                if (n != k) {
+                        r = k < 0 ? k : (errno ? -errno : -EIO);
 
-                if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
 
-                f = canonicalize_file_name(p);
-                if (!f) {
-                        log_error("Failed to canonicalize file name '%s': %m", p);
-                        free(p);
-                        continue;
+                        unlink(to);
+                        return r;
                 }
-                free(p);
+        }
 
-                log_debug("found: %s\n", f);
-                if (hashmap_put(h, file_name_from_path(f), f) <= 0)
-                        free(f);
+        close_nointr_nofail(fdf);
+        r = close_nointr(fdt);
+
+        if (r < 0) {
+                unlink(to);
+                return r;
         }
 
-finish:
-        closedir(dir);
-        return r;
+        return 0;
 }
 
-static int base_cmp(const void *a, const void *b) {
-        const char *s1, *s2;
+int symlink_atomic(const char *from, const char *to) {
+        char *x;
+        _cleanup_free_ char *t;
+        const char *fn;
+        size_t k;
+        unsigned long long ull;
+        unsigned i;
+        int r;
 
-        s1 = *(char * const *)a;
-        s2 = *(char * const *)b;
-        return strcmp(file_name_from_path(s1), file_name_from_path(s2));
-}
+        assert(from);
+        assert(to);
 
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
-        Hashmap *fh = NULL;
-        char **dirs = NULL;
-        char **files = NULL;
-        char **p;
-        va_list ap;
-        int r = 0;
+        t = new(char, strlen(to) + 1 + 16 + 1);
+        if (!t)
+                return -ENOMEM;
 
-        va_start(ap, dir);
-        dirs = strv_new_ap(dir, ap);
-        va_end(ap);
-        if (!dirs) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        if (!strv_path_canonicalize(dirs)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        if (!strv_uniq(dirs)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        fh = hashmap_new(string_hash_func, string_compare_func);
-        if (!fh) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        STRV_FOREACH(p, dirs) {
-                if (files_add(fh, *p, suffix) < 0) {
-                        log_error("Failed to search for files.");
-                        r = -EINVAL;
-                        goto finish;
-                }
-        }
-
-        files = hashmap_get_strv(fh);
-        if (files == NULL) {
-                log_error("Failed to compose list of files.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
-
-finish:
-        strv_free(dirs);
-        hashmap_free(fh);
-        *strv = files;
-        return r;
-}
-
-int hwclock_is_localtime(void) {
-        FILE *f;
-        bool local = false;
-
-        /*
-         * The third line of adjtime is "UTC" or "LOCAL" or nothing.
-         *   # /etc/adjtime
-         *   0.0 0 0
-         *   0
-         *   UTC
-         */
-        f = fopen("/etc/adjtime", "re");
-        if (f) {
-                char line[LINE_MAX];
-                bool b;
-
-                b = fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f);
-
-                fclose(f);
-
-                if (!b)
-                        return -EIO;
-
-                truncate_nl(line);
-                local = streq(line, "LOCAL");
-
-        } else if (errno != -ENOENT)
-                return -errno;
-
-        return local;
-}
-
-int hwclock_apply_localtime_delta(int *min) {
-        const struct timeval *tv_null = NULL;
-        struct timespec ts;
-        struct tm *tm;
-        int minuteswest;
-        struct timezone tz;
-
-        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
-        assert_se(tm = localtime(&ts.tv_sec));
-        minuteswest = tm->tm_gmtoff / 60;
-
-        tz.tz_minuteswest = -minuteswest;
-        tz.tz_dsttime = 0; /* DST_NONE*/
-
-        /*
-         * If the hardware clock does not run in UTC, but in local time:
-         * The very first time we set the kernel's timezone, it will warp
-         * the clock so that it runs in UTC instead of local time.
-         */
-        if (settimeofday(tv_null, &tz) < 0)
-                return -errno;
-        if (min)
-                *min = minuteswest;
-        return 0;
-}
-
-int hwclock_reset_localtime_delta(void) {
-        const struct timeval *tv_null = NULL;
-        struct timezone tz;
-
-        tz.tz_minuteswest = 0;
-        tz.tz_dsttime = 0; /* DST_NONE*/
-
-        if (settimeofday(tv_null, &tz) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int rtc_open(int flags) {
-        int fd;
-        DIR *d;
-
-        /* First, we try to make use of the /dev/rtc symlink. If that
-         * doesn't exist, we open the first RTC which has hctosys=1
-         * set. If we don't find any we just take the first RTC that
-         * exists at all. */
-
-        fd = open("/dev/rtc", flags);
-        if (fd >= 0)
-                return fd;
-
-        d = opendir("/sys/class/rtc");
-        if (!d)
-                goto fallback;
-
-        for (;;) {
-                char *p, *v;
-                struct dirent buf, *de;
-                int r;
-
-                r = readdir_r(d, &buf, &de);
-                if (r != 0)
-                        goto fallback;
-
-                if (!de)
-                        goto fallback;
-
-                if (ignore_file(de->d_name))
-                        continue;
-
-                p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
-                if (!p) {
-                        closedir(d);
-                        return -ENOMEM;
-                }
-
-                r = read_one_line_file(p, &v);
-                free(p);
-
-                if (r < 0)
-                        continue;
-
-                r = parse_boolean(v);
-                free(v);
-
-                if (r <= 0)
-                        continue;
-
-                p = strappend("/dev/", de->d_name);
-                fd = open(p, flags);
-                free(p);
-
-                if (fd >= 0) {
-                        closedir(d);
-                        return fd;
-                }
-        }
-
-fallback:
-        if (d)
-                closedir(d);
-
-        fd = open("/dev/rtc0", flags);
-        if (fd < 0)
-                return -errno;
-
-        return fd;
-}
-
-int hwclock_get_time(struct tm *tm) {
-        int fd;
-        int err = 0;
-
-        assert(tm);
-
-        fd = rtc_open(O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        /* This leaves the timezone fields of struct tm
-         * uninitialized! */
-        if (ioctl(fd, RTC_RD_TIME, tm) < 0)
-                err = -errno;
-
-        /* We don't know daylight saving, so we reset this in order not
-         * to confused mktime(). */
-        tm->tm_isdst = -1;
-
-        close_nointr_nofail(fd);
-
-        return err;
-}
-
-int hwclock_set_time(const struct tm *tm) {
-        int fd;
-        int err = 0;
-
-        assert(tm);
-
-        fd = rtc_open(O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        if (ioctl(fd, RTC_SET_TIME, tm) < 0)
-                err = -errno;
-
-        close_nointr_nofail(fd);
-
-        return err;
-}
-
-int copy_file(const char *from, const char *to) {
-        int r, fdf, fdt;
-
-        assert(from);
-        assert(to);
-
-        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        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);
-                return -errno;
-        }
-
-        for (;;) {
-                char buf[PIPE_BUF];
-                ssize_t n, k;
-
-                n = read(fdf, buf, sizeof(buf));
-                if (n < 0) {
-                        r = -errno;
-
-                        close_nointr_nofail(fdf);
-                        close_nointr(fdt);
-                        unlink(to);
-
-                        return r;
-                }
-
-                if (n == 0)
-                        break;
-
-                errno = 0;
-                k = loop_write(fdt, buf, n, false);
-                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) {
-                unlink(to);
-                return r;
-        }
-
-        return 0;
-}
-
-int symlink_or_copy(const char *from, const char *to) {
-        char *pf = NULL, *pt = NULL;
-        struct stat a, b;
-        int r;
-
-        assert(from);
-        assert(to);
-
-        if (parent_of_path(from, &pf) < 0 ||
-            parent_of_path(to, &pt) < 0) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (stat(pf, &a) < 0 ||
-            stat(pt, &b) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (a.st_dev != b.st_dev) {
-                free(pf);
-                free(pt);
-
-                return copy_file(from, to);
-        }
-
-        if (symlink(from, to) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        r = 0;
-
-finish:
-        free(pf);
-        free(pt);
-
-        return r;
-}
-
-int symlink_or_copy_atomic(const char *from, const char *to) {
-        char *t, *x;
-        const char *fn;
-        size_t k;
-        unsigned long long ull;
-        unsigned i;
-        int r;
-
-        assert(from);
-        assert(to);
-
-        t = new(char, strlen(to) + 1 + 16 + 1);
-        if (!t)
-                return -ENOMEM;
-
-        fn = file_name_from_path(to);
+        fn = path_get_file_name(to);
         k = fn-to;
         memcpy(t, to, k);
         t[k] = '.';
@@ -5290,22 +4735,16 @@ int symlink_or_copy_atomic(const char *from, const char *to) {
 
         *x = 0;
 
-        r = symlink_or_copy(from, t);
-        if (r < 0) {
-                unlink(t);
-                free(t);
-                return r;
-        }
+        if (symlink(from, t) < 0)
+                return -errno;
 
         if (rename(t, to) < 0) {
                 r = -errno;
                 unlink(t);
-                free(t);
                 return r;
         }
 
-        free(t);
-        return r;
+        return 0;
 }
 
 bool display_is_local(const char *display) {
@@ -5342,7 +4781,12 @@ int socket_from_display(const char *display, char **path) {
         return 0;
 }
 
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+int get_user_creds(
+                const char **username,
+                uid_t *uid, gid_t *gid,
+                const char **home,
+                const char **shell) {
+
         struct passwd *p;
         uid_t u;
 
@@ -5363,6 +4807,10 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h
 
                 if (home)
                         *home = "/root";
+
+                if (shell)
+                        *shell = "/bin/sh";
+
                 return 0;
         }
 
@@ -5394,6 +4842,9 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h
         if (home)
                 *home = p->pw_dir;
 
+        if (shell)
+                *shell = p->pw_shell;
+
         return 0;
 }
 
@@ -5518,7 +4969,7 @@ int in_search_path(const char *path, char **search) {
         char **i, *parent;
         int r;
 
-        r = parent_of_path(path, &parent);
+        r = path_get_parent(path, &parent);
         if (r < 0)
                 return r;
 
@@ -5553,10 +5004,11 @@ int get_files_in_directory(const char *path, char ***list) {
                 return -errno;
 
         for (;;) {
-                struct dirent buffer, *de;
+                struct dirent *de;
+                union dirent_storage buf;
                 int k;
 
-                k = readdir_r(d, &buffer, &de);
+                k = readdir_r(d, &buf.de, &de);
                 if (k != 0) {
                         r = -k;
                         goto finish;
@@ -5610,7 +5062,7 @@ finish:
         return r;
 }
 
-char *join(const char *x, ...) {
+char *strjoin(const char *x, ...) {
         va_list ap;
         size_t l;
         char *r, *p;
@@ -5622,12 +5074,19 @@ char *join(const char *x, ...) {
 
                 for (;;) {
                         const char *t;
+                        size_t n;
 
                         t = va_arg(ap, const char *);
                         if (!t)
                                 break;
 
-                        l += strlen(t);
+                        n = strlen(t);
+                        if (n > ((size_t) -1) - l) {
+                                va_end(ap);
+                                return NULL;
+                        }
+
+                        l += n;
                 }
         } else
                 l = 0;
@@ -5899,7 +5358,7 @@ int signal_from_string(const char *s) {
         int offset = 0;
         unsigned u;
 
-        signo =__signal_from_string(s);
+        signo = __signal_from_string(s);
         if (signo > 0)
                 return signo;
 
@@ -6157,17 +5616,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
         return 0;
 }
 
-int path_is_read_only_fs(const char *path) {
-        struct statvfs st;
-
-        assert(path);
-
-        if (statvfs(path, &st) < 0)
-                return -errno;
-
-        return !!(st.f_flag & ST_RDONLY);
-}
-
 int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         char path[sizeof("/proc/")-1+10+sizeof("/environ")], *value = NULL;
         int r;
@@ -6229,3 +5677,419 @@ 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);
+
+        r = read_one_line_file("/sys/power/state", &p);
+        if (r < 0)
+                return r == -ENOENT ? 0 : r;
+
+        k = strlen(type);
+        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
+                if (l == k && memcmp(w, type, l) == 0)
+                        return true;
+
+        return false;
+}
+
+bool is_valid_documentation_url(const char *url) {
+        assert(url);
+
+        if (startswith(url, "http://") && url[7])
+                return true;
+
+        if (startswith(url, "https://") && url[8])
+                return true;
+
+        if (startswith(url, "file:") && url[5])
+                return true;
+
+        if (startswith(url, "info:") && url[5])
+                return true;
+
+        if (startswith(url, "man:") && url[4])
+                return true;
+
+        return false;
+}
+
+bool in_initrd(void) {
+        static __thread int saved = -1;
+        struct statfs s;
+
+        if (saved >= 0)
+                return saved;
+
+        /* We make two checks here:
+         *
+         * 1. the flag file /etc/initrd-release must exist
+         * 2. the root file system must be a memory file system
+         *
+         * The second check is extra paranoia, since misdetecting an
+         * initrd can have bad bad consequences due the initrd
+         * emptying when transititioning to the main systemd.
+         */
+
+        saved = access("/etc/initrd-release", F_OK) >= 0 &&
+                statfs("/", &s) >= 0 &&
+                (s.f_type == TMPFS_MAGIC || s.f_type == RAMFS_MAGIC);
+
+        return saved;
+}
+
+void warn_melody(void) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return;
+
+        /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/440));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+        int fd, r;
+
+        /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+        fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1);
+        if (fd < 0) {
+                log_error("Failed to acquire terminal: %s", strerror(-fd));
+                return fd;
+        }
+
+        r = make_stdio(fd);
+        if (r < 0) {
+                log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+                return r;
+        }
+
+        return 0;
+}
+
+int get_home_dir(char **_h) {
+        char *h;
+        const char *e;
+        uid_t u;
+        struct passwd *p;
+
+        assert(_h);
+
+        /* Take the user specified one */
+        e = getenv("HOME");
+        if (e) {
+                h = strdup(e);
+                if (!h)
+                        return -ENOMEM;
+
+                *_h = h;
+                return 0;
+        }
+
+        /* Hardcode home directory for root to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                h = strdup("/root");
+                if (!h)
+                        return -ENOMEM;
+
+                *_h = h;
+                return 0;
+        }
+
+        /* Check the database... */
+        errno = 0;
+        p = getpwuid(u);
+        if (!p)
+                return errno ? -errno : -ESRCH;
+
+        if (!path_is_absolute(p->pw_dir))
+                return -EINVAL;
+
+        h = strdup(p->pw_dir);
+        if (!h)
+                return -ENOMEM;
+
+        *_h = 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 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))
+                return false;
+
+        if (strchr(p, '/'))
+                return false;
+
+        if (streq(p, "."))
+                return false;
+
+        if (streq(p, ".."))
+                return false;
+
+        if (strlen(p) > FILENAME_MAX)
+                return false;
+
+        return true;
+}
+
+bool string_is_safe(const char *p) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++) {
+                if (*t > 0 && *t < ' ')
+                        return false;
+
+                if (strchr("\\\"\'", *t))
+                        return false;
+        }
+
+        return true;
+}
+
+int parse_timestamp(const char *t, usec_t *usec) {
+        const char *k;
+        struct tm tm, copy;
+        time_t x;
+        usec_t plus = 0, minus = 0, ret;
+        int r;
+
+        /*
+         * Allowed syntaxes:
+         *
+         *   2012-09-22 16:34:22
+         *   2012-09-22 16:34     (seconds will be set to 0)
+         *   2012-09-22           (time will be set to 00:00:00)
+         *   16:34:22             (date will be set to today)
+         *   16:34                (date will be set to today, seconds to 0)
+         *   now
+         *   yesterday            (time is set to 00:00:00)
+         *   today                (time is set to 00:00:00)
+         *   tomorrow             (time is set to 00:00:00)
+         *   +5min
+         *   -5days
+         *
+         */
+
+        assert(t);
+        assert(usec);
+
+        x = time(NULL);
+        assert_se(localtime_r(&x, &tm));
+
+        if (streq(t, "now"))
+                goto finish;
+
+        else if (streq(t, "today")) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "yesterday")) {
+                tm.tm_mday --;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "tomorrow")) {
+                tm.tm_mday ++;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (t[0] == '+') {
+
+                r = parse_usec(t+1, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        } else if (t[0] == '-') {
+
+                r = parse_usec(t+1, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        }
+
+        copy = tm;
+        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        return -EINVAL;
+
+finish:
+        x = mktime(&tm);
+        if (x == (time_t) -1)
+                return -EINVAL;
+
+        ret = (usec_t) x * USEC_PER_SEC;
+
+        ret += plus;
+        if (ret > minus)
+                ret -= minus;
+        else
+                ret = 0;
+
+        *usec = ret;
+
+        return 0;
+}
+
+/* hey glibc, APIs with callbacks without a user pointer are so useless */
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 int (*compar) (const void *, const void *, void *),
+                 void *arg) {
+        size_t l, u, idx;
+        const void *p;
+        int comparison;
+
+        l = 0;
+        u = nmemb;
+        while (l < u) {
+                idx = (l + u) / 2;
+                p = (void *)(((const char *) base) + (idx * size));
+                comparison = compar(key, p, arg);
+                if (comparison < 0)
+                        u = idx;
+                else if (comparison > 0)
+                        l = idx + 1;
+                else
+                        return (void *)p;
+        }
+        return NULL;
+}