chiark / gitweb /
Prep v225: Applying various fixes and changes to src/basic that got lost during git...
authorSven Eden <yamakuzure@gmx.net>
Wed, 4 Jan 2017 05:40:46 +0000 (06:40 +0100)
committerSven Eden <yamakuzure@gmx.net>
Tue, 14 Mar 2017 09:18:46 +0000 (10:18 +0100)
16 files changed:
src/basic/cgroup-util.c
src/basic/fileio.c
src/basic/hostname-util.c
src/basic/hostname-util.h
src/basic/macro.h
src/basic/path-util.c
src/basic/process-util.c
src/basic/strv.c
src/basic/strv.h
src/basic/terminal-util.c
src/basic/time-util.c
src/basic/time-util.h
src/basic/unit-name.c
src/basic/util.c
src/basic/util.h
src/basic/virt.c

index 479254159e54c6b05f2e021fbe89adfacac47592..b1063e9db9991819e40f8ab252edc5d5860ef5a7 100644 (file)
@@ -837,14 +837,12 @@ int cg_install_release_agent(const char *controller, const char *agent) {
         } else if (!streq(sc, agent))
                 return -EEXIST;
 
-        free(fs);
-        fs = NULL;
+        fs = mfree(fs);
         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
         if (r < 0)
                 return r;
 
-        free(contents);
-        contents = NULL;
+        contents = mfree(contents);
         r = read_one_line_file(fs, &contents);
         if (r < 0)
                 return r;
@@ -876,8 +874,7 @@ int cg_uninstall_release_agent(const char *controller) {
         if (r < 0)
                 return r;
 
-        free(fs);
-        fs = NULL;
+        fs = mfree(fs);
 
         r = cg_get_path(controller, NULL, "release_agent", &fs);
         if (r < 0)
index db2c46cfb1d8e76dca4f599d44e41aac42f2143f..f666380a414e8f5554c9f2511d932aca3c5519bd 100644 (file)
 #include "fileio.h"
 
 int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
+
         assert(f);
         assert(line);
 
-        errno = 0;
-
         fputs(line, f);
         if (enforce_newline && !endswith(line, "\n"))
                 fputc('\n', f);
 
-        fflush(f);
-
-        if (ferror(f))
-                return errno ? -errno : -EIO;
-
-        return 0;
+        return fflush_and_check(f);
 }
 
 static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
@@ -795,7 +789,7 @@ int executable_is_script(const char *path, char **interpreter) {
  */
 int get_status_field(const char *filename, const char *pattern, char **field) {
         _cleanup_free_ char *status = NULL;
-        char *t;
+        char *t, *f;
         size_t len;
         int r;
 
@@ -829,9 +823,10 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
 
         len = strcspn(t, WHITESPACE);
 
-        *field = strndup(t, len);
-        if (!*field)
+        f = strndup(t, len);
+        if (!f)
                 return -ENOMEM;
 
+        *field = f;
         return 0;
 }
index 8f6a5b4316e4103c48e37cc5d681c63df0510267..f221c6ff1dc31fb3cf5c922413f42057fc6d6574 100644 (file)
@@ -64,14 +64,25 @@ static bool hostname_valid_char(char c) {
                 c == '.';
 }
 
-bool hostname_is_valid(const char *s) {
+/**
+ * Check if s looks like a valid host name or FQDN. This does not do
+ * full DNS validation, but only checks if the name is composed of
+ * allowed characters and the length is not above the maximum allowed
+ * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
+ * allow_trailing_dot is true and at least two components are present
+ * in the name. Note that due to the restricted charset and length
+ * this call is substantially more conservative than
+ * dns_domain_is_valid().
+ */
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
+        unsigned n_dots = 0;
         const char *p;
         bool dot;
 
         if (isempty(s))
                 return false;
 
-        /* Doesn't accept empty hostnames, hostnames with trailing or
+        /* Doesn't accept empty hostnames, hostnames with
          * leading dots, and hostnames with multiple dots in a
          * sequence. Also ensures that the length stays below
          * HOST_NAME_MAX. */
@@ -82,6 +93,7 @@ bool hostname_is_valid(const char *s) {
                                 return false;
 
                         dot = true;
+                        n_dots ++;
                 } else {
                         if (!hostname_valid_char(*p))
                                 return false;
@@ -90,16 +102,18 @@ bool hostname_is_valid(const char *s) {
                 }
         }
 
-        if (dot)
+        if (dot && (n_dots < 2 || !allow_trailing_dot))
                 return false;
 
-        if (p-s > HOST_NAME_MAX)
+        if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
+                                  * Linux, but DNS allows domain names
+                                  * up to 255 characters */
                 return false;
 
         return true;
 }
 
-char* hostname_cleanup(char *s, bool lowercase) {
+char* hostname_cleanup(char *s) {
         char *p, *d;
         bool dot;
 
@@ -113,7 +127,7 @@ char* hostname_cleanup(char *s, bool lowercase) {
                         *(d++) = '.';
                         dot = true;
                 } else if (hostname_valid_char(*p)) {
-                        *(d++) = lowercase ? tolower(*p) : *p;
+                        *(d++) = *p;
                         dot = false;
                 }
 
@@ -135,14 +149,25 @@ bool is_localhost(const char *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.");
+        return strcaseeq(hostname, "localhost") ||
+               strcaseeq(hostname, "localhost.") ||
+               strcaseeq(hostname, "localdomain.") ||
+               strcaseeq(hostname, "localdomain") ||
+               endswith_no_case(hostname, ".localhost") ||
+               endswith_no_case(hostname, ".localhost.") ||
+               endswith_no_case(hostname, ".localdomain") ||
+               endswith_no_case(hostname, ".localdomain.");
+}
+
+bool is_gateway_hostname(const char *hostname) {
+        assert(hostname);
+
+        /* This tries to identify the valid syntaxes for the our
+         * synthetic "gateway" host. */
+
+        return
+                strcaseeq(hostname, "gateway") ||
+                strcaseeq(hostname, "gateway.");
 }
 
 /// UNNEEDED by elogind
@@ -181,7 +206,7 @@ int read_hostname_config(const char *path, char **hostname) {
                 truncate_nl(l);
                 if (l[0] != '\0' && l[0] != '#') {
                         /* found line with value */
-                        name = hostname_cleanup(l, false);
+                        name = hostname_cleanup(l);
                         name = strdup(name);
                         if (!name)
                                 return -ENOMEM;
index c92b19cdc85bb106fd29c3346f5e7d695cd6b950..aab3a495b026780c7429968b9e16b0010b5e8e79 100644 (file)
 
 // UNNEEDED char* gethostname_malloc(void);
 
-bool hostname_is_valid(const char *s) _pure_;
-char* hostname_cleanup(char *s, bool lowercase);
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
+char* hostname_cleanup(char *s);
+
+#define machine_name_is_valid(s) hostname_is_valid(s, false)
 
 bool is_localhost(const char *hostname);
+bool is_gateway_hostname(const char *hostname);
 
 // UNNEEDED int sethostname_idempotent(const char *s);
 
index 58530a398089a1ff16b199111bde046659a3bf40..627d768b76bada29f5f7d106a13ca215a55470cf 100644 (file)
@@ -407,12 +407,12 @@ do {                                                                    \
 
 #define IN_SET(x, y, ...)                                               \
         ({                                                              \
-                const typeof(y) _y = (y);                               \
-                const typeof(_y) _x = (x);                              \
+                static const typeof(y) _array[] = { (y), __VA_ARGS__ }; \
+                const typeof(y) _x = (x);                               \
                 unsigned _i;                                            \
                 bool _found = false;                                    \
-                for (_i = 0; _i < 1 + sizeof((const typeof(_x)[]) { __VA_ARGS__ })/sizeof(const typeof(_x)); _i++) \
-                        if (((const typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
+                for (_i = 0; _i < ELEMENTSOF(_array); _i++)             \
+                        if (_array[_i] == _x) {                         \
                                 _found = true;                          \
                                 break;                                  \
                         }                                               \
index 4325f55f236793faf6d523cc1277ef79fe32d487..6bdc8342e6807c9c749feb5aa2029942eadeeec9 100644 (file)
@@ -665,9 +665,11 @@ int path_is_mount_point(const char *t, int flags) {
                 canonical = canonicalize_file_name(t);
                 if (!canonical)
                         return -errno;
+
+                t = canonical;
         }
 
-        r = path_get_parent(canonical ?: t, &parent);
+        r = path_get_parent(t, &parent);
         if (r < 0)
                 return r;
 
@@ -675,7 +677,7 @@ int path_is_mount_point(const char *t, int flags) {
         if (fd < 0)
                 return -errno;
 
-        return fd_is_mount_point(fd, basename(canonical ?: t), flags);
+        return fd_is_mount_point(fd, basename(t), flags);
 }
 
 int path_is_read_only_fs(const char *path) {
index b05591eb866c0fa747f2aa6448312b6fec7fb360..07469871704e586700a3894b5392053d0f85b5b9 100644 (file)
@@ -43,7 +43,10 @@ int get_process_state(pid_t pid) {
         assert(pid >= 0);
 
         p = procfs_file_alloca(pid, "stat");
+
         r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
                 return r;
 
@@ -87,8 +90,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         p = procfs_file_alloca(pid, "cmdline");
 
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         if (max_length == 0) {
                 size_t len = 0, allocated = 0;
@@ -182,8 +188,11 @@ int is_kernel_thread(pid_t pid) {
 
         p = procfs_file_alloca(pid, "cmdline");
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         count = fread(&c, 1, 1, f);
         eof = feof(f);
@@ -201,13 +210,18 @@ int is_kernel_thread(pid_t pid) {
 #if 0
 int get_process_capeff(pid_t pid, char **capeff) {
         const char *p;
+        int r;
 
         assert(capeff);
         assert(pid >= 0);
 
         p = procfs_file_alloca(pid, "status");
 
-        return get_status_field(p, "\nCapEff:", capeff);
+        r = get_status_field(p, "\nCapEff:", capeff);
+        if (r == -ENOENT)
+                return -ESRCH;
+
+        return r;
 }
 #endif // 0
 
@@ -218,8 +232,10 @@ static int get_process_link_contents(const char *proc_file, char **name) {
         assert(name);
 
         r = readlink_malloc(proc_file, name);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
-                return r == -ENOENT ? -ESRCH : r;
+                return r;
 
         return 0;
 }
@@ -258,8 +274,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
 
         p = procfs_file_alloca(pid, "status");
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         FOREACH_LINE(line, f, return -errno) {
                 char *l;
@@ -321,8 +340,11 @@ int get_process_environ(pid_t pid, char **env) {
         p = procfs_file_alloca(pid, "environ");
 
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         while ((c = fgetc(f)) != EOF) {
                 if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
@@ -334,7 +356,13 @@ int get_process_environ(pid_t pid, char **env) {
                         sz += cescape_char(c, outcome + sz);
         }
 
+        if (!outcome) {
+                outcome = strdup("");
+                if (!outcome)
+                        return -ENOMEM;
+        } else
         outcome[sz] = '\0';
+
         *env = outcome;
         outcome = NULL;
 
@@ -358,6 +386,8 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
 
         p = procfs_file_alloca(pid, "stat");
         r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
                 return r;
 
@@ -481,8 +511,11 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         path = procfs_file_alloca(pid, "environ");
 
         f = fopen(path, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         l = strlen(field);
         r = 0;
@@ -541,7 +574,7 @@ bool pid_is_alive(pid_t pid) {
                 return false;
 
         r = get_process_state(pid);
-        if (r == -ENOENT || r == 'Z')
+        if (r == -ESRCH || r == 'Z')
                 return false;
 
         return true;
index 5bf066ff00e9199d259729fab14a4692e0c7fcba..b6e6d505013619cd20e4daa929634d7b0eb58449 100644 (file)
@@ -283,7 +283,7 @@ char **strv_split_newlines(const char *s) {
         return l;
 }
 
-int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
+int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
         size_t n = 0, allocated = 0;
         _cleanup_strv_free_ char **l = NULL;
         int r;
@@ -294,11 +294,12 @@ int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
         for (;;) {
                 _cleanup_free_ char *word = NULL;
 
-                r = unquote_first_word(&s, &word, flags);
+                r = extract_first_word(&s, &word, separators, flags);
                 if (r < 0)
                         return r;
-                if (r == 0)
+                if (r == 0) {
                         break;
+                }
 
                 if (!GREEDY_REALLOC(l, allocated, n + 2))
                         return -ENOMEM;
@@ -714,6 +715,26 @@ char **strv_reverse(char **l) {
 }
 #endif // 0
 
+char **strv_shell_escape(char **l, const char *bad) {
+        char **s;
+
+        /* Escapes every character in every string in l that is in bad,
+         * edits in-place, does not roll-back on error. */
+
+        STRV_FOREACH(s, l) {
+                char *v;
+
+                v = shell_escape(*s, bad);
+                if (!v)
+                        return NULL;
+
+                free(*s);
+                *s = v;
+        }
+
+        return l;
+}
+
 bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
         char* const* p;
 
index 9629480d86a703362a4918afdc7bd595d13f9b3b..2dbd6d7f5fd58fadad42517d74763c0bac7aa9d4 100644 (file)
@@ -145,6 +145,7 @@ void strv_print(char **l);
         }))
 
 // UNNEEDED char **strv_reverse(char **l);
+char **strv_shell_escape(char **l, const char *bad);
 
 bool strv_fnmatch(char* const* patterns, const char *s, int flags);
 
index 28ae5dc08bd9dd0ff8e6269909e8ef6c729036c2..73e5c2e571b681233dfc5135bf5616fd7903d280 100644 (file)
@@ -233,14 +233,14 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
          * interfere with that. */
 
         /* Disable exclusive mode, just in case */
-        ioctl(fd, TIOCNXCL);
+        (void) ioctl(fd, TIOCNXCL);
 
         /* Switch to text mode */
         if (switch_to_text)
-                ioctl(fd, KDSETMODE, KD_TEXT);
+                (void) ioctl(fd, KDSETMODE, KD_TEXT);
 
         /* Enable console unicode mode */
-        ioctl(fd, KDSKBMODE, K_UNICODE);
+        (void) ioctl(fd, KDSKBMODE, K_UNICODE);
 
         if (tcgetattr(fd, &termios) < 0) {
                 r = -errno;
@@ -279,7 +279,7 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
 
 finish:
         /* Just in case, flush all crap out */
-        tcflush(fd, TCIOFLUSH);
+        (void) tcflush(fd, TCIOFLUSH);
 
         return r;
 }
@@ -421,9 +421,8 @@ int acquire_terminal(
                 if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
                         r = 0;
 
-                if (r < 0 && (force || fail || r != -EPERM)) {
+                if (r < 0 && (force || fail || r != -EPERM))
                         goto fail;
-                }
 
                 if (r >= 0)
                         break;
@@ -623,16 +622,16 @@ void warn_melody(void) {
 
         /* Yeah, this is synchronous. Kinda sucks. But well... */
 
-        ioctl(fd, KIOCSOUND, (int)(1193180/440));
+        (void) ioctl(fd, KIOCSOUND, (int)(1193180/440));
         usleep(125*USEC_PER_MSEC);
 
-        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
         usleep(125*USEC_PER_MSEC);
 
-        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
         usleep(125*USEC_PER_MSEC);
 
-        ioctl(fd, KIOCSOUND, 0);
+        (void) ioctl(fd, KIOCSOUND, 0);
 }
 
 /// UNNEEDED by elogind
index e849ccc57d96841cc3822eeb6293630be941a55b..de9263bf77fb09602774b959dbb812c8a7fc7b82 100644 (file)
@@ -91,6 +91,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
 }
 #endif // 0
 
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+
+        if (u == USEC_INFINITY) {
+                ts->realtime = ts->monotonic = USEC_INFINITY;
+                return ts;
+        }
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
+
+        if ((int64_t) ts->realtime > delta)
+                ts->realtime -= delta;
+        else
+                ts->realtime = 0;
+
+        if ((int64_t) ts->monotonic > delta)
+                ts->monotonic -= delta;
+        else
+                ts->monotonic = 0;
+
+        return ts;
+}
+
+
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
index 5913639ec8e4dfb4c1d90a0b6bf8153792691e98..c02e73c252f253eec898ecdeb3efb354b8870df6 100644 (file)
@@ -74,6 +74,7 @@ usec_t now(clockid_t clock);
 dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 // UNNEEDED dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
 
 static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
         return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
index 5dac442b1b110fb132ac5ddc5bcd61a1fceaf6f4..f8965262c1130c806250532d44160554214fa247 100644 (file)
@@ -687,6 +687,7 @@ int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, c
 #if 0
 int slice_build_parent_slice(const char *slice, char **ret) {
         char *s, *dash;
+        int r;
 
         assert(slice);
         assert(ret);
@@ -707,11 +708,11 @@ int slice_build_parent_slice(const char *slice, char **ret) {
         if (dash)
                 strcpy(dash, ".slice");
         else {
+                r = free_and_strdup(&s, "-.slice");
+                if (r < 0) {
                 free(s);
-
-                s = strdup("-.slice");
-                if (!s)
-                        return -ENOMEM;
+                        return r;
+                }
         }
 
         *ret = s;
index 328010209293f6a36f9ba55414684b22dfe36191..d5c758e6ff9aeb8edf64b2381dfc4304b60d4b31 100644 (file)
@@ -115,17 +115,23 @@ size_t page_size(void) {
         return pgsz;
 }
 
-bool streq_ptr(const char *a, const char *b) {
-
-        /* Like streq(), but tries to make sense of NULL pointers */
+int strcmp_ptr(const char *a, const char *b) {
 
+        /* Like strcmp(), but tries to make sense of NULL pointers */
         if (a && b)
-                return streq(a, b);
+                return strcmp(a, b);
 
-        if (!a && !b)
-                return true;
+        if (!a && b)
+                return -1;
 
-        return false;
+        if (a && !b)
+                return 1;
+
+        return 0;
+}
+
+bool streq_ptr(const char *a, const char *b) {
+        return strcmp_ptr(a, b) == 0;
 }
 
 char* endswith(const char *s, const char *postfix) {
@@ -928,32 +934,573 @@ char *hexmem(const void *p, size_t l) {
         return r;
 }
 
-void *unhexmem(const char *p, size_t l) {
-        uint8_t *r, *z;
+int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        uint8_t *z;
         const char *x;
 
+        assert(mem);
+        assert(len);
         assert(p);
 
         z = r = malloc((l + 1) / 2 + 1);
         if (!r)
-                return NULL;
+                return -ENOMEM;
 
         for (x = p; x < p + l; x += 2) {
                 int a, b;
 
                 a = unhexchar(x[0]);
-                if (x+1 < p + l)
+                if (a < 0)
+                        return a;
+                else if (x+1 < p + l) {
                         b = unhexchar(x[1]);
-                else
+                        if (b < 0)
+                                return b;
+                } else
                         b = 0;
 
                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
         }
 
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *len = (l + 1) / 2;
+
+        return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-6
+ * Notice that base32hex differs from base32 in the alphabet it uses.
+ * The distinction is that the base32hex representation preserves the
+ * order of the underlying data when compared as bytestrings, this is
+ * useful when representing NSEC3 hashes, as one can then verify the
+ * order of hashes directly from their representation. */
+char base32hexchar(int x) {
+        static const char table[32] = "0123456789"
+                                      "ABCDEFGHIJKLMNOPQRSTUV";
+
+        return table[x & 31];
+}
+
+int unbase32hexchar(char c) {
+        unsigned offset;
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        offset = '9' - '0' + 1;
+
+        if (c >= 'A' && c <= 'V')
+                return c - 'A' + offset;
+
+        return -EINVAL;
+}
+
+char *base32hexmem(const void *p, size_t l, bool padding) {
+        char *r, *z;
+        const uint8_t *x;
+        size_t len;
+
+        if (padding)
+                /* five input bytes makes eight output bytes, padding is added so we must round up */
+                len = 8 * (l + 4) / 5;
+        else {
+                /* same, but round down as there is no padding */
+                len = 8 * l / 5;
+
+                switch (l % 5) {
+                case 4:
+                        len += 7;
+                        break;
+                case 3:
+                        len += 5;
+                        break;
+                case 2:
+                        len += 4;
+                        break;
+                case 1:
+                        len += 2;
+                        break;
+                }
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
+                   x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);  /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5);  /* 000QQWWW */
+                *(z++) = base32hexchar((x[4] & 31));                  /* 000WWWWW */
+        }
+
+        switch (l % 5) {
+        case 4:
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);   /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3);              /* 000QQ000 */
+                if (padding)
+                        *(z++) = '=';
+
+                break;
+
+        case 3:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1);            /* 000ZZZZ0 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 2:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4);             /* 000Y0000 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 1:
+                *(z++) = base32hexchar(x[0] >> 3);       /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+        }
+
+        *z = 0;
+        return r;
+}
+
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d, e, f, g, h;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+        unsigned pad = 0;
+
+        assert(p);
+
+        /* padding ensures any base32hex input has input divisible by 8 */
+        if (padding && l % 8 != 0)
+                return -EINVAL;
+
+        if (padding) {
+                /* strip the padding */
+                while (l > 0 && p[l - 1] == '=' && pad < 7) {
+                        pad ++;
+                        l --;
+                }
+        }
+
+        /* a group of eight input bytes needs five output bytes, in case of
+           padding we need to add some extra bytes */
+        len = (l / 8) * 5;
+
+        switch (l % 8) {
+        case 7:
+                len += 4;
+                break;
+        case 5:
+                len += 3;
+                break;
+        case 4:
+                len += 2;
+                break;
+        case 2:
+                len += 1;
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 8) * 8; x += 8) {
+                /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
+                   e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                h = unbase32hexchar(x[7]);
+                if (h < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+                *(z++) = (uint8_t) g << 5 | (uint8_t) h;                         /* VVVRRRRR */
+        }
+
+        switch (l % 8) {
+        case 7:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                /* g == 000VV000 */
+                if (g & 7)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+
+                break;
+        case 5:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                /* e == 000SSSS0 */
+                if (e & 1)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+
+                break;
+        case 4:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                /* d == 000W0000 */
+                if (d & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+
+                break;
+        case 2:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 000YYY00 */
+                if (b & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-4 */
+char base64char(int x) {
+        static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                      "abcdefghijklmnopqrstuvwxyz"
+                                      "0123456789+/";
+        return table[x & 63];
+}
+
+int unbase64char(char c) {
+        unsigned offset;
+
+        if (c >= 'A' && c <= 'Z')
+                return c - 'A';
+
+        offset = 'Z' - 'A' + 1;
+
+        if (c >= 'a' && c <= 'z')
+                return c - 'a' + offset;
+
+        offset += 'z' - 'a' + 1;
+
+        if (c >= '0' && c <= '9')
+                return c - '0' + offset;
+
+        offset += '9' - '0' + 1;
+
+        if (c == '+')
+                return offset;
+
+        offset ++;
+
+        if (c == '/')
+                return offset;
+
+        return -EINVAL;
+}
+
+char *base64mem(const void *p, size_t l) {
+        char *r, *z;
+        const uint8_t *x;
+
+        /* three input bytes makes four output bytes, padding is added so we must round up */
+        z = r = malloc(4 * (l + 2) / 3 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+                *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+                *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
+        }
+
+        switch (l % 3) {
+        case 2:
+                *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
+                *(z++) = '=';
+
+                break;
+        case 1:
+                *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
+                *(z++) = '=';
+                *(z++) = '=';
+
+                break;
+        }
+
         *z = 0;
         return r;
 }
 
+int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+
+        assert(p);
+
+        /* padding ensures any base63 input has input divisible by 4 */
+        if (l % 4 != 0)
+                return -EINVAL;
+
+        /* strip the padding */
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+
+        /* a group of four input bytes needs three output bytes, in case of
+           padding we need to add two or three extra bytes */
+        len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 4) * 4; x += 4) {
+                /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase64char(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+                *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
+        }
+
+        switch (l % 4) {
+        case 3:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                /* c == 00ZZZZ00 */
+                if (c & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+
+                break;
+        case 2:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 00YY0000 */
+                if (b & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+
+                break;
+        case 0:
+
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
 char octchar(int x) {
         return '0' + (x & 7);
 }
@@ -2504,21 +3051,6 @@ char* strshorten(char *s, size_t l) {
         return s;
 }
 
-bool machine_name_is_valid(const char *s) {
-
-        if (!hostname_is_valid(s))
-                return false;
-
-        /* Machine names should be useful hostnames, but also be
-         * useful in unit names, hence we enforce a stricter length
-         * limitation. */
-
-        if (strlen(s) > 64)
-                return false;
-
-        return true;
-}
-
 int pipe_eof(int fd) {
         struct pollfd pollfd = {
                 .fd = fd,
@@ -2578,8 +3110,9 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
 
         f = fdopen(fd, "we");
         if (!f) {
-                unlink(t);
+                unlink_noerrno(t);
                 free(t);
+                safe_close(fd);
                 return -errno;
         }
 
@@ -3690,7 +4223,7 @@ bool string_is_safe(const char *p) {
                 if (*t > 0 && *t < ' ')
                         return false;
 
-                if (strchr("\\\"\'\0x7f", *t))
+                if (strchr("\\\"\'\x7f", *t))
                         return false;
         }
 
@@ -4370,7 +4903,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
                 _cleanup_free_ char *word = NULL;
                 char *value = NULL;
 
-                r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -4410,7 +4943,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
                 _cleanup_free_ char *word = NULL;
                 const char *e;
 
-                r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -4457,6 +4990,9 @@ int container_get_leader(const char *machine, pid_t *pid) {
         assert(machine);
         assert(pid);
 
+        if (!machine_name_is_valid(machine))
+                return -EINVAL;
+
         p = strjoina("/run/systemd/machines/", machine);
         r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
         if (r == -ENOENT)
@@ -4480,8 +5016,8 @@ int container_get_leader(const char *machine, pid_t *pid) {
 }
 #endif // 0
 
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
         int rfd = -1;
 
         assert(pid >= 0);
@@ -4513,6 +5049,15 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
                         return -errno;
         }
 
+        if (userns_fd) {
+                const char *userns;
+
+                userns = procfs_file_alloca(pid, "ns/user");
+                usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (usernsfd < 0 && errno != ENOENT)
+                        return -errno;
+        }
+
         if (root_fd) {
                 const char *root;
 
@@ -4531,15 +5076,33 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
         if (netns_fd)
                 *netns_fd = netnsfd;
 
+        if (userns_fd)
+                *userns_fd = usernsfd;
+
         if (root_fd)
                 *root_fd = rfd;
 
-        pidnsfd = mntnsfd = netnsfd = -1;
+        pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
 
         return 0;
 }
 
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
+        if (userns_fd >= 0) {
+                /* Can't setns to your own userns, since then you could
+                 * escalate from non-root to root in your own namespace, so
+                 * check if namespaces equal before attempting to enter. */
+                _cleanup_free_ char *userns_fd_path = NULL;
+                int r;
+                if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
+                        return -ENOMEM;
+
+                r = files_same(userns_fd_path, "/proc/self/ns/user");
+                if (r < 0)
+                        return r;
+                if (r)
+                        userns_fd = -1;
+        }
 
         if (pidns_fd >= 0)
                 if (setns(pidns_fd, CLONE_NEWPID) < 0)
@@ -4553,6 +5116,10 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
                 if (setns(netns_fd, CLONE_NEWNET) < 0)
                         return -errno;
 
+        if (userns_fd >= 0)
+                if (setns(userns_fd, CLONE_NEWUSER) < 0)
+                        return -errno;
+
         if (root_fd >= 0) {
                 if (fchdir(root_fd) < 0)
                         return -errno;
@@ -4719,10 +5286,7 @@ unsigned long personality_from_string(const char *p) {
 
         return PERSONALITY_INVALID;
 }
-#endif // 0
 
-/// UNNEEDED by elogind
-#if 0
 const char* personality_to_string(unsigned long p) {
 
 #if defined(__x86_64__)
@@ -5246,7 +5810,7 @@ int is_device_node(const char *path) {
 }
 #endif // 0
 
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
         _cleanup_free_ char *s = NULL;
         size_t allocated = 0, sz = 0;
         int r;
@@ -5259,13 +5823,19 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                 SINGLE_QUOTE_ESCAPE,
                 DOUBLE_QUOTE,
                 DOUBLE_QUOTE_ESCAPE,
-                SPACE,
+                SEPARATOR,
         } state = START;
 
         assert(p);
-        assert(*p);
         assert(ret);
 
+        if (!separators)
+                separators = WHITESPACE;
+
+        /* Bail early if called after last value or with no input */
+        if (!*p)
+                goto finish_force_terminate;
+
         /* Parses the first word of a string, and returns it in
          * *ret. Removes all quotes in the process. When parsing fails
          * (because of an uneven number of quotes or similar), leaves
@@ -5277,26 +5847,46 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                 switch (state) {
 
                 case START:
-                        if (c == 0)
-                                goto finish;
-                        else if (strchr(WHITESPACE, c))
+                        if (c == 0) {
+                                if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+                                        if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                                return -ENOMEM;
+                                goto finish_force_terminate;
+                        } else if (strchr(separators, c)) {
+                                if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+                                        if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                                return -ENOMEM;
+                                        (*p) ++;
+                                        goto finish_force_next;
+                                }
                                 break;
+                        }
 
                         state = VALUE;
                         /* fallthrough */
 
                 case VALUE:
                         if (c == 0)
-                                goto finish;
-                        else if (c == '\'')
+                                goto finish_force_terminate;
+                        else if (c == '\'' && (flags & EXTRACT_QUOTES)) {
+                                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                        return -ENOMEM;
+
                                 state = SINGLE_QUOTE;
-                        else if (c == '\\')
+                        else if (c == '\\')
                                 state = VALUE_ESCAPE;
-                        else if (c == '\"')
+                        else if (c == '\"' && (flags & EXTRACT_QUOTES)) {
+                                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                        return -ENOMEM;
+
                                 state = DOUBLE_QUOTE;
-                        else if (strchr(WHITESPACE, c))
-                                state = SPACE;
-                        else {
+                        } else if (strchr(separators, c)) {
+                                if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+                                        (*p) ++;
+                                        goto finish_force_next;
+                                }
+                                state = SEPARATOR;
+                        } else {
                                 if (!GREEDY_REALLOC(s, allocated, sz+2))
                                         return -ENOMEM;
 
@@ -5307,8 +5897,8 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
 
                 case SINGLE_QUOTE:
                         if (c == 0) {
-                                if (flags & UNQUOTE_RELAX)
-                                        goto finish;
+                                if (flags & EXTRACT_RELAX)
+                                        goto finish_force_terminate;
                                 return -EINVAL;
                         } else if (c == '\'')
                                 state = VALUE;
@@ -5346,29 +5936,29 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                                 return -ENOMEM;
 
                         if (c == 0) {
-                                if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
-                                    (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
+                                if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
+                                    (state == VALUE_ESCAPE || flags & EXTRACT_RELAX)) {
                                         /* If we find an unquoted trailing backslash and we're in
-                                         * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
+                                         * EXTRACT_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.
+                                         * Unbalanced quotes will only be allowed in EXTRACT_RELAX
+                                         * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
                                          */
                                         s[sz++] = '\\';
-                                        goto finish;
+                                        goto finish_force_terminate;
                                 }
-                                if (flags & UNQUOTE_RELAX)
-                                        goto finish;
+                                if (flags & EXTRACT_RELAX)
+                                        goto finish_force_terminate;
                                 return -EINVAL;
                         }
 
-                        if (flags & UNQUOTE_CUNESCAPE) {
+                        if (flags & EXTRACT_CUNESCAPE) {
                                 uint32_t u;
 
                                 r = cunescape_one(*p, (size_t) -1, &c, &u);
                                 if (r < 0) {
-                                        if (flags & UNQUOTE_CUNESCAPE_RELAX) {
+                                        if (flags & EXTRACT_CUNESCAPE_RELAX) {
                                                 s[sz++] = '\\';
                                                 s[sz++] = c;
                                                 goto end_escape;
@@ -5391,24 +5981,29 @@ end_escape:
                                 VALUE;
                         break;
 
-                case SPACE:
+                case SEPARATOR:
                         if (c == 0)
+                                goto finish_force_terminate;
+                        if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+                                goto finish_force_next;
+                        if (!strchr(separators, c))
                                 goto finish;
-                        if (!strchr(WHITESPACE, c))
-                                goto finish;
-
                         break;
                 }
 
                 (*p) ++;
         }
 
+finish_force_terminate:
+        *p = NULL;
 finish:
         if (!s) {
+                *p = NULL;
                 *ret = NULL;
                 return 0;
         }
 
+finish_force_next:
         s[sz] = 0;
         *ret = s;
         s = NULL;
@@ -5418,26 +6013,27 @@ finish:
 
 /// UNNEEDED by elogind
 #if 0
-int unquote_first_word_and_warn(
+int extract_first_word_and_warn(
                 const char **p,
                 char **ret,
-                UnquoteFlags flags,
+                const char *separators,
+                ExtractFlags 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
+         * time using EXTRACT_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. */
+        r = extract_first_word(p, ret, separators, flags);
+        if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
+                /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
                 *p = save;
-                r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX);
+                r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
                 if (r < 0)
                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
                                    "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
@@ -5448,7 +6044,7 @@ int unquote_first_word_and_warn(
         return r;
 }
 
-int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
         va_list ap;
         char **l;
         int n = 0, i, c, r;
@@ -5474,7 +6070,7 @@ int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
         l = newa0(char*, n);
         for (c = 0; c < n; c++) {
 
-                r = unquote_first_word(p, &l[c], flags);
+                r = extract_first_word(p, &l[c], separators, flags);
                 if (r < 0) {
                         int j;
 
@@ -5559,7 +6155,7 @@ int ptsname_malloc(int fd, char **ret) {
 }
 
 int openpt_in_namespace(pid_t pid, int flags) {
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
         _cleanup_close_pair_ int pair[2] = { -1, -1 };
         union {
                 struct cmsghdr cmsghdr;
@@ -5576,7 +6172,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
 
         assert(pid > 0);
 
-        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
         if (r < 0)
                 return r;
 
@@ -5592,7 +6188,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
 
                 pair[0] = safe_close(pair[0]);
 
-                r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+                r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
@@ -6041,10 +6637,9 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
         if (ret >= 0)
                 return 0;
 
-        /* Even though renameat2() exists since Linux 3.15, btrfs added
-         * support for it later. If it is not implemented, fallback to another
-         * method. */
-        if (errno != EINVAL)
+        /* renameat2() exists since Linux 3.15, btrfs added support for it later.
+         * If it is not implemented, fallback to another method. */
+        if (!IN_SET(errno, EINVAL, ENOSYS))
                 return -errno;
 
         /* The link()/unlink() fallback does not work on directories. But
@@ -6075,6 +6670,32 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
 }
 #endif // 0
 
+static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
+        assert(bad);
+
+        for (; *s; s++) {
+                if (*s == '\\' || strchr(bad, *s))
+                        *(t++) = '\\';
+
+                *(t++) = *s;
+        }
+
+        return t;
+}
+
+char *shell_escape(const char *s, const char *bad) {
+        char *r, *t;
+
+        r = new(char, strlen(s)*2+1);
+        if (!r)
+                return NULL;
+
+        t = strcpy_backslash_escaped(r, s, bad);
+        *t = 0;
+
+        return r;
+}
+
 char *shell_maybe_quote(const char *s) {
         const char *p;
         char *r, *t;
@@ -6101,13 +6722,7 @@ char *shell_maybe_quote(const char *s) {
         *(t++) = '"';
         t = mempcpy(t, s, p - s);
 
-        for (; *p; p++) {
-
-                if (strchr(SHELL_NEED_ESCAPE, *p))
-                        *(t++) = '\\';
-
-                *(t++) = *p;
-        }
+        t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
 
         *(t++)= '"';
         *t = 0;
@@ -6170,3 +6785,73 @@ int reset_uid_gid(void) {
 
         return 0;
 }
+
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) {
+        char *v;
+        size_t l;
+        ssize_t n;
+
+        assert(path);
+        assert(name);
+        assert(value);
+
+        for (l = 100; ; l = (size_t) n + 1) {
+                v = new0(char, l);
+                if (!v)
+                        return -ENOMEM;
+
+                if (allow_symlink)
+                        n = lgetxattr(path, name, v, l);
+                else
+                        n = getxattr(path, name, v, l);
+
+                if (n >= 0 && (size_t) n < l) {
+                        *value = v;
+                        return n;
+                }
+
+                free(v);
+
+                if (n < 0 && errno != ERANGE)
+                        return -errno;
+
+                if (allow_symlink)
+                        n = lgetxattr(path, name, NULL, 0);
+                else
+                        n = getxattr(path, name, NULL, 0);
+                if (n < 0)
+                        return -errno;
+        }
+}
+
+int fgetxattr_malloc(int fd, const char *name, char **value) {
+        char *v;
+        size_t l;
+        ssize_t n;
+
+        assert(fd >= 0);
+        assert(name);
+        assert(value);
+
+        for (l = 100; ; l = (size_t) n + 1) {
+                v = new0(char, l);
+                if (!v)
+                        return -ENOMEM;
+
+                n = fgetxattr(fd, name, v, l);
+
+                if (n >= 0 && (size_t) n < l) {
+                        *value = v;
+                        return n;
+                }
+
+                free(v);
+
+                if (n < 0 && errno != ERANGE)
+                        return -errno;
+
+                n = fgetxattr(fd, name, NULL, 0);
+                if (n < 0)
+                        return -errno;
+        }
+}
index 0d0b881b95fee0357eb652e5d4e279c651382617..110f0cdd337577b7f73f0952fb58b64e0841ce1d 100644 (file)
@@ -71,6 +71,7 @@ size_t page_size(void) _pure_;
 #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
 
 bool streq_ptr(const char *a, const char *b) _pure_;
+int strcmp_ptr(const char *a, const char *b) _pure_;
 
 #define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
 
@@ -84,6 +85,11 @@ bool streq_ptr(const char *a, const char *b) _pure_;
 
 #define malloc0(n) (calloc((n), 1))
 
+static inline void *mfree(void *memory) {
+        free(memory);
+        return NULL;
+}
+
 static inline const char* yes_no(bool b) {
         return b ? "yes" : "no";
 }
@@ -240,6 +246,10 @@ char octchar(int x) _const_;
 int unoctchar(char c) _const_;
 char decchar(int x) _const_;
 int undecchar(char c) _const_;
+char base32hexchar(int x) _const_;
+int unbase32hexchar(char c) _const_;
+char base64char(int x) _const_;
+int unbase64char(char c) _const_;
 
 char *cescape(const char *s);
 size_t cescape_char(char c, char *buf);
@@ -614,7 +624,13 @@ static inline void *mempset(void *s, int c, size_t n) {
 }
 
 char *hexmem(const void *p, size_t l);
-void *unhexmem(const char *p, size_t l);
+int unhexmem(const char *p, size_t l, void **mem, size_t *len);
+
+char *base32hexmem(const void *p, size_t l, bool padding);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+
+char *base64mem(const void *p, size_t l);
+int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
 
 char *strextend(char **x, ...) _sentinel_;
 char *strrep(const char *s, unsigned n);
@@ -838,15 +854,22 @@ int tempfn_random(const char *p, const char *extra, char **ret);
 int is_dir(const char *path, bool follow);
 // UNNEEDED int is_device_node(const char *path);
 
-typedef enum UnquoteFlags {
-        UNQUOTE_RELAX           = 1,
-        UNQUOTE_CUNESCAPE       = 2,
-        UNQUOTE_CUNESCAPE_RELAX = 4,
-} UnquoteFlags;
+typedef enum ExtractFlags {
+        EXTRACT_RELAX           = 1,
+        EXTRACT_CUNESCAPE       = 2,
+        EXTRACT_CUNESCAPE_RELAX = 4,
+        EXTRACT_QUOTES          = 8,
+        EXTRACT_DONT_COALESCE_SEPARATORS = 16,
+} ExtractFlags;
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
+int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
 
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
-// UNNEEDED int unquote_first_word_and_warn(const char **p, char **ret, UnquoteFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
-// UNNEEDED int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
+static inline void free_and_replace(char **s, char *v) {
+        free(*s);
+        *s = v;
+}
 
 int free_and_strdup(char **p, const char *s);
 
@@ -896,6 +919,7 @@ void sigkill_wait(pid_t *pid);
 
 // UNNEEDED int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
 
+char *shell_escape(const char *s, const char *bad);
 char *shell_maybe_quote(const char *s);
 
 int parse_mode(const char *s, mode_t *ret);
@@ -903,3 +927,6 @@ int parse_mode(const char *s, mode_t *ret);
 // UNNEEDED int mount_move_root(const char *path);
 
 int reset_uid_gid(void);
+
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
+int fgetxattr_malloc(int fd, const char *name, char **value);
index b2383e52658c242c5f25dec13245a9514f9d07a4..764de955a5b75cb9bd55415fef65770383a891e3 100644 (file)
@@ -188,7 +188,7 @@ int detect_vm(const char **id) {
         _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
         static thread_local int cached_found = -1;
         static thread_local const char *cached_id = NULL;
-        const char *_id = NULL;
+        const char *_id = NULL, *_id_cpuid = NULL;
         int r;
 
         if (_likely_(cached_found >= 0)) {
@@ -234,10 +234,26 @@ int detect_vm(const char **id) {
 
         /* this will set _id to "other" and return 0 for unknown hypervisors */
         r = detect_vm_cpuid(&_id);
-        if (r != 0)
+
+        /* finish when found a known hypervisor other than kvm */
+        if (r < 0 || (r > 0 && !streq(_id, "kvm")))
                 goto finish;
 
+        _id_cpuid = _id;
+
         r = detect_vm_dmi(&_id);
+
+        /* kvm with and without Virtualbox */
+        if (streq_ptr(_id_cpuid, "kvm")) {
+                if (r > 0 && streq(_id, "oracle"))
+                        goto finish;
+
+                _id = _id_cpuid;
+                r = 1;
+                goto finish;
+        }
+
+        /* information from dmi */
         if (r != 0)
                 goto finish;