chiark / gitweb /
Prep v220: Apply "Fixes to user and session saving"
[elogind.git] / src / shared / util.c
index d3dacfcfb5300b0d55eb439273c277f2f7c0f0a5..950519ed06ddfe96578ff98454c52580c24cbe73 100644 (file)
@@ -60,8 +60,8 @@
 #include <linux/fs.h>
 
 /* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the XDG
- * version which is really broken. */
+ * undefine basename() since libgen.h defines it as a macro to the POSIX
+ * version which is really broken. We prefer GNU basename(). */
 #include <libgen.h>
 #undef basename
 
@@ -92,6 +92,8 @@
 #include "process-util.h"
 #include "random-util.h"
 #include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
 
 /* Put this test here for a lack of better place */
 assert_cc(EAGAIN == EWOULDBLOCK);
@@ -147,6 +149,27 @@ char* endswith(const char *s, const char *postfix) {
         return (char*) s + sl - pl;
 }
 
+char* endswith_no_case(const char *s, const char *postfix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(postfix);
+
+        sl = strlen(s);
+        pl = strlen(postfix);
+
+        if (pl == 0)
+                return (char*) s + sl;
+
+        if (sl < pl)
+                return NULL;
+
+        if (strcasecmp(s + sl - pl, postfix) != 0)
+                return NULL;
+
+        return (char*) s + sl - pl;
+}
+
 char* first_word(const char *s, const char *word) {
         size_t sl, wl;
         const char *p;
@@ -571,13 +594,12 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
                 char quotechars[2] = {*current, '\0'};
 
                 *l = strcspn_escaped(current + 1, quotechars);
-                if (current[*l + 1] == '\0' ||
+                if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
                     (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
                         /* right quote missing or garbage at the end */
                         *state = current;
                         return NULL;
                 }
-                assert(current[*l + 1] == quotechars[0]);
                 *state = current++ + *l + 2;
         } else if (quoted) {
                 *l = strcspn_escaped(current, separator);
@@ -750,41 +772,6 @@ int readlink_and_canonicalize(const char *p, char **r) {
         return 0;
 }
 
-int reset_all_signal_handlers(void) {
-        int sig, r = 0;
-
-        for (sig = 1; sig < _NSIG; sig++) {
-                struct sigaction sa = {
-                        .sa_handler = SIG_DFL,
-                        .sa_flags = SA_RESTART,
-                };
-
-                /* These two cannot be caught... */
-                if (sig == SIGKILL || sig == SIGSTOP)
-                        continue;
-
-                /* On Linux the first two RT signals are reserved by
-                 * glibc, and sigaction() will return EINVAL for them. */
-                if ((sigaction(sig, &sa, NULL) < 0))
-                        if (errno != EINVAL && r == 0)
-                                r = -errno;
-        }
-
-        return r;
-}
-
-int reset_signal_mask(void) {
-        sigset_t ss;
-
-        if (sigemptyset(&ss) < 0)
-                return -errno;
-
-        if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
-                return -errno;
-
-        return 0;
-}
-
 char *strstrip(char *s) {
         char *e;
 
@@ -1165,7 +1152,7 @@ static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_
                 int a, b, c;
                 uint32_t m;
 
-                if (length != (size_t) -1 && length < 4)
+                if (length != (size_t) -1 && length < 3)
                         return -EINVAL;
 
                 a = unoctchar(p[0]);
@@ -1540,59 +1527,6 @@ int flush_fd(int fd) {
         }
 }
 
-int sigaction_many(const struct sigaction *sa, ...) {
-        va_list ap;
-        int r = 0, sig;
-
-        va_start(ap, sa);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
-int ignore_signals(int sig, ...) {
-        struct sigaction sa = {
-                .sa_handler = SIG_IGN,
-                .sa_flags = SA_RESTART,
-        };
-        va_list ap;
-        int r = 0;
-
-        if (sigaction(sig, &sa, NULL) < 0)
-                r = -errno;
-
-        va_start(ap, sig);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, &sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
-int default_signals(int sig, ...) {
-        struct sigaction sa = {
-                .sa_handler = SIG_DFL,
-                .sa_flags = SA_RESTART,
-        };
-        va_list ap;
-        int r = 0;
-
-        if (sigaction(sig, &sa, NULL) < 0)
-                r = -errno;
-
-        va_start(ap, sig);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, &sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
 void safe_close_pair(int p[]) {
         assert(p);
 
@@ -1697,7 +1631,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
 
 int parse_size(const char *t, off_t base, off_t *size) {
 
-        /* Soo, sometimes we want to parse IEC binary suffxies, and
+        /* Soo, sometimes we want to parse IEC binary suffixes, and
          * sometimes SI decimal suffixes. This function can parse
          * both. Which one is the right way depends on the
          * context. Wikipedia suggests that SI is customary for
@@ -1906,55 +1840,6 @@ void rename_process(const char name[8]) {
         }
 }
 
-void sigset_add_many(sigset_t *ss, ...) {
-        va_list ap;
-        int sig;
-
-        assert(ss);
-
-        va_start(ap, ss);
-        while ((sig = va_arg(ap, int)) > 0)
-                assert_se(sigaddset(ss, sig) == 0);
-        va_end(ap);
-}
-
-int sigprocmask_many(int how, ...) {
-        va_list ap;
-        sigset_t ss;
-        int sig;
-
-        assert_se(sigemptyset(&ss) == 0);
-
-        va_start(ap, how);
-        while ((sig = va_arg(ap, int)) > 0)
-                assert_se(sigaddset(&ss, sig) == 0);
-        va_end(ap);
-
-        if (sigprocmask(how, &ss, NULL) < 0)
-                return -errno;
-
-        return 0;
-}
-
-char* gethostname_malloc(void) {
-        struct utsname u;
-
-        assert_se(uname(&u) >= 0);
-
-        if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
-                return strdup(u.nodename);
-
-        return strdup(u.sysname);
-}
-
-bool hostname_is_set(void) {
-        struct utsname u;
-
-        assert_se(uname(&u) >= 0);
-
-        return !isempty(u.nodename) && !streq(u.nodename, "(none)");
-}
-
 char *lookup_uid(uid_t uid) {
         long bufsize;
         char *name;
@@ -2343,18 +2228,6 @@ DIR *xopendirat(int fd, const char *name, int flags) {
         return d;
 }
 
-int signal_from_string_try_harder(const char *s) {
-        int signo;
-        assert(s);
-
-        signo = signal_from_string(s);
-        if (signo <= 0)
-                if (startswith(s, "SIG"))
-                        return signal_from_string(s+3);
-
-        return signo;
-}
-
 static char *tag_to_udev_node(const char *tagvalue, const char *by) {
         _cleanup_free_ char *t = NULL, *u = NULL;
         size_t enc_len;
@@ -2428,8 +2301,8 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
         /* We fork this all off from a child process so that we can
          * somewhat cleanly make use of SIGALRM to set a time limit */
 
-        reset_all_signal_handlers();
-        reset_signal_mask();
+        (void) reset_all_signal_handlers();
+        (void) reset_signal_mask();
 
         assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
 
@@ -2586,79 +2459,6 @@ char* strshorten(char *s, size_t l) {
         return s;
 }
 
-static bool hostname_valid_char(char c) {
-        return
-                (c >= 'a' && c <= 'z') ||
-                (c >= 'A' && c <= 'Z') ||
-                (c >= '0' && c <= '9') ||
-                c == '-' ||
-                c == '_' ||
-                c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
-        const char *p;
-        bool dot;
-
-        if (isempty(s))
-                return false;
-
-        /* Doesn't accept empty hostnames, hostnames with trailing or
-         * leading dots, and hostnames with multiple dots in a
-         * sequence. Also ensures that the length stays below
-         * HOST_NAME_MAX. */
-
-        for (p = s, dot = true; *p; p++) {
-                if (*p == '.') {
-                        if (dot)
-                                return false;
-
-                        dot = true;
-                } else {
-                        if (!hostname_valid_char(*p))
-                                return false;
-
-                        dot = false;
-                }
-        }
-
-        if (dot)
-                return false;
-
-        if (p-s > HOST_NAME_MAX)
-                return false;
-
-        return true;
-}
-
-char* hostname_cleanup(char *s, bool lowercase) {
-        char *p, *d;
-        bool dot;
-
-        for (p = s, d = s, dot = true; *p; p++) {
-                if (*p == '.') {
-                        if (dot)
-                                continue;
-
-                        *(d++) = '.';
-                        dot = true;
-                } else if (hostname_valid_char(*p)) {
-                        *(d++) = lowercase ? tolower(*p) : *p;
-                        dot = false;
-                }
-
-        }
-
-        if (dot && d > s)
-                d[-1] = 0;
-        else
-                *d = 0;
-
-        strshorten(s, HOST_NAME_MAX);
-
-        return s;
-}
-
 bool machine_name_is_valid(const char *s) {
 
         if (!hostname_is_valid(s))
@@ -2721,7 +2521,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         assert(_f);
         assert(_temp_path);
 
-        r = tempfn_xxxxxx(path, &t);
+        r = tempfn_xxxxxx(path, NULL, &t);
         if (r < 0)
                 return r;
 
@@ -2751,7 +2551,7 @@ int symlink_atomic(const char *from, const char *to) {
         assert(from);
         assert(to);
 
-        r = tempfn_random(to, &t);
+        r = tempfn_random(to, NULL, &t);
         if (r < 0)
                 return r;
 
@@ -2794,7 +2594,7 @@ int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
 
         assert(path);
 
-        r = tempfn_random(path, &t);
+        r = tempfn_random(path, NULL, &t);
         if (r < 0)
                 return r;
 
@@ -2815,7 +2615,7 @@ int mkfifo_atomic(const char *path, mode_t mode) {
 
         assert(path);
 
-        r = tempfn_random(path, &t);
+        r = tempfn_random(path, NULL, &t);
         if (r < 0)
                 return r;
 
@@ -3384,81 +3184,6 @@ static const char* const ip_tos_table[] = {
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
 
-static const char *const __signal_table[] = {
-        [SIGHUP] = "HUP",
-        [SIGINT] = "INT",
-        [SIGQUIT] = "QUIT",
-        [SIGILL] = "ILL",
-        [SIGTRAP] = "TRAP",
-        [SIGABRT] = "ABRT",
-        [SIGBUS] = "BUS",
-        [SIGFPE] = "FPE",
-        [SIGKILL] = "KILL",
-        [SIGUSR1] = "USR1",
-        [SIGSEGV] = "SEGV",
-        [SIGUSR2] = "USR2",
-        [SIGPIPE] = "PIPE",
-        [SIGALRM] = "ALRM",
-        [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
-        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
-        [SIGCHLD] = "CHLD",
-        [SIGCONT] = "CONT",
-        [SIGSTOP] = "STOP",
-        [SIGTSTP] = "TSTP",
-        [SIGTTIN] = "TTIN",
-        [SIGTTOU] = "TTOU",
-        [SIGURG] = "URG",
-        [SIGXCPU] = "XCPU",
-        [SIGXFSZ] = "XFSZ",
-        [SIGVTALRM] = "VTALRM",
-        [SIGPROF] = "PROF",
-        [SIGWINCH] = "WINCH",
-        [SIGIO] = "IO",
-        [SIGPWR] = "PWR",
-        [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
-        static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
-        const char *name;
-
-        name = __signal_to_string(signo);
-        if (name)
-                return name;
-
-        if (signo >= SIGRTMIN && signo <= SIGRTMAX)
-                snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
-        else
-                snprintf(buf, sizeof(buf), "%d", signo);
-
-        return buf;
-}
-
-int signal_from_string(const char *s) {
-        int signo;
-        int offset = 0;
-        unsigned u;
-
-        signo = __signal_from_string(s);
-        if (signo > 0)
-                return signo;
-
-        if (startswith(s, "RTMIN+")) {
-                s += 6;
-                offset = SIGRTMIN;
-        }
-        if (safe_atou(s, &u) >= 0) {
-                signo = (int) u + offset;
-                if (signo > 0 && signo < _NSIG)
-                        return signo;
-        }
-        return -EINVAL;
-}
-
 bool kexec_loaded(void) {
        bool loaded = false;
        char *s;
@@ -3619,8 +3344,8 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
         /* Make sure we actually can kill the agent, if we need to, in
          * case somebody invoked us from a shell script that trapped
          * SIGTERM or so... */
-        reset_all_signal_handlers();
-        reset_signal_mask();
+        (void) reset_all_signal_handlers();
+        (void) reset_signal_mask();
 
         /* Check whether our parent died before we were able
          * to set the death signal and unblock the signals */
@@ -4758,16 +4483,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
                         return -errno;
         }
 
-        if (setresgid(0, 0, 0) < 0)
-                return -errno;
-
-        if (setgroups(0, NULL) < 0)
-                return -errno;
-
-        if (setresuid(0, 0, 0) < 0)
-                return -errno;
-
-        return 0;
+        return reset_uid_gid();
 }
 
 int getpeercred(int fd, struct ucred *ucred) {
@@ -4918,10 +4634,7 @@ unsigned long personality_from_string(const char *p) {
                 return PER_LINUX;
 #endif
 
-        /* personality(7) documents that 0xffffffffUL is used for
-         * querying the current personality, hence let's use that here
-         * as error indicator. */
-        return 0xffffffffUL;
+        return PERSONALITY_INVALID;
 }
 
 const char* personality_to_string(unsigned long p) {
@@ -5003,7 +4716,7 @@ int update_reboot_param_file(const char *param) {
 
         if (param) {
 
-                r = write_string_file(REBOOT_PARAM_FILE, param);
+                r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
                 if (r < 0)
                         log_error("Failed to write reboot param to "
                                   REBOOT_PARAM_FILE": %s", strerror(-r));
@@ -5219,7 +4932,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
                 while ((x = set_steal_first(todo))) {
 
                         r = set_consume(done, x);
-                        if (r == -EEXIST)
+                        if (r == -EEXIST || r == 0)
                                 continue;
                         if (r < 0)
                                 return r;
@@ -5256,7 +4969,7 @@ int fflush_and_check(FILE *f) {
         return 0;
 }
 
-int tempfn_xxxxxx(const char *p, char **ret) {
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
         const char *fn;
         char *t;
 
@@ -5268,24 +4981,27 @@ int tempfn_xxxxxx(const char *p, char **ret) {
          *         /foo/bar/waldo
          *
          * Into this:
-         *         /foo/bar/.#waldoXXXXXX
+         *         /foo/bar/.#<extra>waldoXXXXXX
          */
 
         fn = basename(p);
         if (!filename_is_valid(fn))
                 return -EINVAL;
 
-        t = new(char, strlen(p) + 2 + 6 + 1);
+        if (extra == NULL)
+                extra = "";
+
+        t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
         if (!t)
                 return -ENOMEM;
 
-        strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
+        strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
 
         *ret = path_kill_slashes(t);
         return 0;
 }
 
-int tempfn_random(const char *p, char **ret) {
+int tempfn_random(const char *p, const char *extra, char **ret) {
         const char *fn;
         char *t, *x;
         uint64_t u;
@@ -5299,18 +5015,21 @@ int tempfn_random(const char *p, char **ret) {
          *         /foo/bar/waldo
          *
          * Into this:
-         *         /foo/bar/.#waldobaa2a261115984a9
+         *         /foo/bar/.#<extra>waldobaa2a261115984a9
          */
 
         fn = basename(p);
         if (!filename_is_valid(fn))
                 return -EINVAL;
 
-        t = new(char, strlen(p) + 2 + 16 + 1);
+        if (!extra)
+                extra = "";
+
+        t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
         if (!t)
                 return -ENOMEM;
 
-        x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn);
+        x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
 
         u = random_u64();
         for (i = 0; i < 16; i++) {
@@ -5324,7 +5043,7 @@ int tempfn_random(const char *p, char **ret) {
         return 0;
 }
 
-int tempfn_random_child(const char *p, char **ret) {
+int tempfn_random_child(const char *p, const char *extra, char **ret) {
         char *t, *x;
         uint64_t u;
         unsigned i;
@@ -5335,14 +5054,17 @@ int tempfn_random_child(const char *p, char **ret) {
         /* Turns this:
          *         /foo/bar/waldo
          * Into this:
-         *         /foo/bar/waldo/.#3c2b6219aa75d7d0
+         *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
          */
 
-        t = new(char, strlen(p) + 3 + 16 + 1);
+        if (!extra)
+                extra = "";
+
+        t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
         if (!t)
                 return -ENOMEM;
 
-        x = stpcpy(stpcpy(t, p), "/.#");
+        x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
 
         u = random_u64();
         for (i = 0; i < 16; i++) {
@@ -5356,23 +5078,6 @@ int tempfn_random_child(const char *p, char **ret) {
         return 0;
 }
 
-/* make sure the hostname is not "localhost" */
-bool is_localhost(const char *hostname) {
-        assert(hostname);
-
-        /* This tries to identify local host and domain names
-         * described in RFC6761 plus the redhatism of .localdomain */
-
-        return streq(hostname, "localhost") ||
-               streq(hostname, "localhost.") ||
-               streq(hostname, "localdomain.") ||
-               streq(hostname, "localdomain") ||
-               endswith(hostname, ".localhost") ||
-               endswith(hostname, ".localhost.") ||
-               endswith(hostname, ".localdomain") ||
-               endswith(hostname, ".localdomain.");
-}
-
 int take_password_lock(const char *root) {
 
         struct flock flock = {
@@ -5504,35 +5209,6 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
 
                         break;
 
-                case VALUE_ESCAPE:
-                        if (c == 0) {
-                                if (flags & UNQUOTE_RELAX)
-                                        goto finish;
-                                return -EINVAL;
-                        }
-
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
-                                return -ENOMEM;
-
-                        if (flags & UNQUOTE_CUNESCAPE) {
-                                uint32_t u;
-
-                                r = cunescape_one(*p, (size_t) -1, &c, &u);
-                                if (r < 0)
-                                        return -EINVAL;
-
-                                (*p) += r - 1;
-
-                                if (c != 0)
-                                        s[sz++] = c; /* normal explicit char */
-                                else
-                                        sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
-                        } else
-                                s[sz++] = c;
-
-                        state = VALUE;
-                        break;
-
                 case SINGLE_QUOTE:
                         if (c == 0) {
                                 if (flags & UNQUOTE_RELAX)
@@ -5551,35 +5227,6 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
 
                         break;
 
-                case SINGLE_QUOTE_ESCAPE:
-                        if (c == 0) {
-                                if (flags & UNQUOTE_RELAX)
-                                        goto finish;
-                                return -EINVAL;
-                        }
-
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
-                                return -ENOMEM;
-
-                        if (flags & UNQUOTE_CUNESCAPE) {
-                                uint32_t u;
-
-                                r = cunescape_one(*p, (size_t) -1, &c, &u);
-                                if (r < 0)
-                                        return -EINVAL;
-
-                                (*p) += r - 1;
-
-                                if (c != 0)
-                                        s[sz++] = c;
-                                else
-                                        sz += utf8_encode_unichar(s + sz, u);
-                        } else
-                                s[sz++] = c;
-
-                        state = SINGLE_QUOTE;
-                        break;
-
                 case DOUBLE_QUOTE:
                         if (c == 0)
                                 return -EINVAL;
@@ -5596,33 +5243,56 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
 
                         break;
 
+                case SINGLE_QUOTE_ESCAPE:
                 case DOUBLE_QUOTE_ESCAPE:
+                case VALUE_ESCAPE:
+                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                                return -ENOMEM;
+
                         if (c == 0) {
+                                if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
+                                    (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
+                                        /* If we find an unquoted trailing backslash and we're in
+                                         * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
+                                         * output.
+                                         *
+                                         * Unbalanced quotes will only be allowed in UNQUOTE_RELAX
+                                         * mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
+                                         */
+                                        s[sz++] = '\\';
+                                        goto finish;
+                                }
                                 if (flags & UNQUOTE_RELAX)
                                         goto finish;
                                 return -EINVAL;
                         }
 
-                        if (!GREEDY_REALLOC(s, allocated, sz+7))
-                                return -ENOMEM;
-
                         if (flags & UNQUOTE_CUNESCAPE) {
                                 uint32_t u;
 
                                 r = cunescape_one(*p, (size_t) -1, &c, &u);
-                                if (r < 0)
+                                if (r < 0) {
+                                        if (flags & UNQUOTE_CUNESCAPE_RELAX) {
+                                                s[sz++] = '\\';
+                                                s[sz++] = c;
+                                                goto end_escape;
+                                        }
                                         return -EINVAL;
+                                }
 
                                 (*p) += r - 1;
 
                                 if (c != 0)
-                                        s[sz++] = c;
+                                        s[sz++] = c; /* normal explicit char */
                                 else
-                                        sz += utf8_encode_unichar(s + sz, u);
+                                        sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
                         } else
                                 s[sz++] = c;
 
-                        state = DOUBLE_QUOTE;
+end_escape:
+                        state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE :
+                                (state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE :
+                                VALUE;
                         break;
 
                 case SPACE:
@@ -5650,6 +5320,36 @@ finish:
         return 1;
 }
 
+int unquote_first_word_and_warn(
+                const char **p,
+                char **ret,
+                UnquoteFlags flags,
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *rvalue) {
+        /* Try to unquote it, if it fails, warn about it and try again but this
+         * time using UNQUOTE_CUNESCAPE_RELAX to keep the backslashes verbatim
+         * in invalid escape sequences. */
+        const char *save;
+        int r;
+
+        save = *p;
+        r = unquote_first_word(p, ret, flags);
+        if (r < 0 && !(flags&UNQUOTE_CUNESCAPE_RELAX)) {
+                /* Retry it with UNQUOTE_CUNESCAPE_RELAX. */
+                *p = save;
+                r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX);
+                if (r < 0)
+                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                                   "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
+                else
+                        log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
+                                   "Invalid escape sequences in command line: \"%s\"", rvalue);
+        }
+        return r;
+}
+
 int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
         va_list ap;
         char **l;
@@ -5730,26 +5430,6 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
-int sethostname_idempotent(const char *s) {
-        int r;
-        char buf[HOST_NAME_MAX + 1] = {};
-
-        assert(s);
-
-        r = gethostname(buf, sizeof(buf));
-        if (r < 0)
-                return -errno;
-
-        if (streq(buf, s))
-                return 0;
-
-        r = sethostname(s, strlen(s));
-        if (r < 0)
-                return -errno;
-
-        return 1;
-}
-
 int ptsname_malloc(int fd, char **ret) {
         size_t l = 100;
 
@@ -5844,7 +5524,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
                 return -errno;
 
-        for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
+        CMSG_FOREACH(cmsg, &mh)
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
                         int *fds;
                         unsigned n_fds;
@@ -5956,6 +5636,71 @@ int fd_setcrtime(int fd, usec_t usec) {
         return 0;
 }
 
+int same_fd(int a, int b) {
+        struct stat sta, stb;
+        pid_t pid;
+        int r, fa, fb;
+
+        assert(a >= 0);
+        assert(b >= 0);
+
+        /* Compares two file descriptors. Note that semantics are
+         * quite different depending on whether we have kcmp() or we
+         * don't. If we have kcmp() this will only return true for
+         * dup()ed file descriptors, but not otherwise. If we don't
+         * have kcmp() this will also return true for two fds of the same
+         * file, created by separate open() calls. Since we use this
+         * call mostly for filtering out duplicates in the fd store
+         * this difference hopefully doesn't matter too much. */
+
+        if (a == b)
+                return true;
+
+        /* Try to use kcmp() if we have it. */
+        pid = getpid();
+        r = kcmp(pid, pid, KCMP_FILE, a, b);
+        if (r == 0)
+                return true;
+        if (r > 0)
+                return false;
+        if (errno != ENOSYS)
+                return -errno;
+
+        /* We don't have kcmp(), use fstat() instead. */
+        if (fstat(a, &sta) < 0)
+                return -errno;
+
+        if (fstat(b, &stb) < 0)
+                return -errno;
+
+        if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+                return false;
+
+        /* We consider all device fds different, since two device fds
+         * might refer to quite different device contexts even though
+         * they share the same inode and backing dev_t. */
+
+        if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+                return false;
+
+        if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+                return false;
+
+        /* The fds refer to the same inode on disk, let's also check
+         * if they have the same fd flags. This is useful to
+         * distinguish the read and write side of a pipe created with
+         * pipe(). */
+        fa = fcntl(a, F_GETFL);
+        if (fa < 0)
+                return -errno;
+
+        fb = fcntl(b, F_GETFL);
+        if (fb < 0)
+                return -errno;
+
+        return fa == fb;
+}
+
 int chattr_fd(int fd, unsigned value, unsigned mask) {
         unsigned old_attr, new_attr;
         struct stat st;
@@ -6167,7 +5912,7 @@ void cmsg_close_all(struct msghdr *mh) {
 
         assert(mh);
 
-        for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg))
+        CMSG_FOREACH(cmsg, mh)
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
                         close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
 }
@@ -6273,3 +6018,35 @@ int parse_mode(const char *s, mode_t *ret) {
         *ret = (mode_t) l;
         return 0;
 }
+
+int mount_move_root(const char *path) {
+        assert(path);
+
+        if (chdir(path) < 0)
+                return -errno;
+
+        if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+                return -errno;
+
+        if (chroot(".") < 0)
+                return -errno;
+
+        if (chdir("/") < 0)
+                return -errno;
+
+        return 0;
+}
+
+int reset_uid_gid(void) {
+
+        if (setgroups(0, NULL) < 0)
+                return -errno;
+
+        if (setresgid(0, 0, 0) < 0)
+                return -errno;
+
+        if (setresuid(0, 0, 0) < 0)
+                return -errno;
+
+        return 0;
+}