chiark / gitweb /
Unifiy free() usage
[elogind.git] / src / basic / util.c
index f17a473d6c6c3c59186a9eff84275394a1a6d4ce..7e0a913f51c88bc97247e14058984ea9cc0c5f7c 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <stdarg.h>
-#include <poll.h>
-#include <ctype.h>
-#include <sys/prctl.h>
-#include <sys/utsname.h>
-#include <pwd.h>
-#include <netinet/ip.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <glob.h>
-#include <grp.h>
-#include <sys/mman.h>
-#include <sys/vfs.h>
-#include <sys/mount.h>
-#include <linux/magic.h>
-#include <limits.h>
-#include <langinfo.h>
-#include <locale.h>
-#include <sys/personality.h>
-#include <sys/xattr.h>
-#include <sys/statvfs.h>
-#include <sys/file.h>
-#include <linux/fs.h>
+// #include  <string.h>
+// #include  <unistd.h>
+#include  <errno.h>
+// #include  <stdlib.h>
+// #include  <signal.h>
+// #include  <libintl.h>
+// #include  <stdio.h>
+// #include  <syslog.h>
+// #include  <sched.h>
+// #include  <sys/resource.h>
+// #include  <linux/sched.h>
+// #include  <sys/types.h>
+// #include  <sys/stat.h>
+// #include  <fcntl.h>
+// #include  <dirent.h>
+// #include  <sys/ioctl.h>
+// #include  <stdarg.h>
+#include  <poll.h>
+// #include  <ctype.h>
+#include  <sys/prctl.h>
+// #include  <sys/utsname.h>
+#include  <pwd.h>
+#include  <netinet/ip.h>
+// #include  <sys/wait.h>
+// #include  <sys/time.h>
+// #include  <glob.h>
+#include  <grp.h>
+// #include  <sys/mman.h>
+// #include  <sys/vfs.h>
+// #include  <sys/mount.h>
+#include  <linux/magic.h>
+// #include  <limits.h>
+#include  <langinfo.h>
+// #include  <locale.h>
+// #include  <sys/personality.h>
+#include  <sys/xattr.h>
+// #include  <sys/statvfs.h>
+// #include  <sys/file.h>
+#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 POSIX
  * version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
+// #include <libgen.h>
+// #undef basename
 
 #ifdef HAVE_SYS_AUXV_H
 #include <sys/auxv.h>
 #endif
 
-#include "config.h"
-#include "macro.h"
-#include "util.h"
-#include "ioprio.h"
-#include "missing.h"
-#include "log.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "exit-status.h"
-#include "hashmap.h"
-#include "env-util.h"
-#include "fileio.h"
-#include "device-nodes.h"
-#include "utf8.h"
-#include "gunicode.h"
-#include "virt.h"
-#include "def.h"
-#include "sparse-endian.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "random-util.h"
-#include "terminal-util.h"
-#include "hostname-util.h"
-#include "signal-util.h"
+#include  "config.h"
+#include  "macro.h"
+#include  "util.h"
+// #include  "ioprio.h"
+// #include  "missing.h"
+// #include  "log.h"
+#include  "strv.h"
+#include  "mkdir.h"
+#include  "path-util.h"
+// #include  "exit-status.h"
+#include  "hashmap.h"
+#include  "set.h"
+// #include  "env-util.h"
+#include  "fileio.h"
+// #include  "device-nodes.h"
+#include  "utf8.h"
+#include  "gunicode.h"
+#include  "virt.h"
+// #include  "def.h"
+#include  "sparse-endian.h"
+// #include  "formats-util.h"
+#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);
@@ -115,17 +116,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) {
@@ -367,6 +374,19 @@ int parse_pid(const char *s, pid_t* ret_pid) {
         return 0;
 }
 
+bool uid_is_valid(uid_t uid) {
+
+        /* Some libc APIs use UID_INVALID as special placeholder */
+        if (uid == (uid_t) 0xFFFFFFFF)
+                return false;
+
+        /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
+        if (uid == (uid_t) 0xFFFF)
+                return false;
+
+        return true;
+}
+
 int parse_uid(const char *s, uid_t* ret_uid) {
         unsigned long ul = 0;
         uid_t uid;
@@ -383,13 +403,11 @@ int parse_uid(const char *s, uid_t* ret_uid) {
         if ((unsigned long) uid != ul)
                 return -ERANGE;
 
-        /* Some libc APIs use UID_INVALID as special placeholder */
-        if (uid == (uid_t) 0xFFFFFFFF)
-                return -ENXIO;
-
-        /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
-        if (uid == (uid_t) 0xFFFF)
-                return -ENXIO;
+        if (!uid_is_valid(uid))
+                return -ENXIO; /* we return ENXIO instead of EINVAL
+                                * here, to make it easy to distuingish
+                                * invalid numeric uids invalid
+                                * strings. */
 
         if (ret_uid)
                 *ret_uid = uid;
@@ -707,6 +725,8 @@ int readlink_malloc(const char *p, char **ret) {
         return readlinkat_malloc(AT_FDCWD, p, ret);
 }
 
+/// UNNEEDED by elogind
+#if 0
 int readlink_value(const char *p, char **ret) {
         _cleanup_free_ char *link = NULL;
         char *value;
@@ -728,6 +748,7 @@ int readlink_value(const char *p, char **ret) {
 
         return 0;
 }
+#endif // 0
 
 int readlink_and_make_absolute(const char *p, char **r) {
         _cleanup_free_ char *target = NULL;
@@ -749,6 +770,8 @@ int readlink_and_make_absolute(const char *p, char **r) {
         return 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int readlink_and_canonicalize(const char *p, char **r) {
         char *t, *s;
         int j;
@@ -771,6 +794,7 @@ int readlink_and_canonicalize(const char *p, char **r) {
 
         return 0;
 }
+#endif // 0
 
 char *strstrip(char *s) {
         char *e;
@@ -836,6 +860,8 @@ char *file_in_same_dir(const char *path, const char *filename) {
         return ret;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int rmdir_parents(const char *path, const char *stop) {
         size_t l;
         int r = 0;
@@ -881,6 +907,7 @@ int rmdir_parents(const char *path, const char *stop) {
 
         return 0;
 }
+#endif // 0
 
 char hexchar(int x) {
         static const char table[16] = "0123456789abcdef";
@@ -919,32 +946,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);
 }
@@ -1301,6 +1869,8 @@ char *xescape(const char *s, const char *bad) {
         return r;
 }
 
+/// UNNEEDED by elogind
+#if 0
 char *ascii_strlower(char *t) {
         char *p;
 
@@ -1312,6 +1882,7 @@ char *ascii_strlower(char *t) {
 
         return t;
 }
+#endif // 0
 
 _pure_ static bool hidden_file_allow_backup(const char *filename) {
         assert(filename);
@@ -1471,6 +2042,8 @@ bool chars_intersect(const char *a, const char *b) {
         return false;
 }
 
+/// UNNEEDED by elogind
+#if 0
 bool fstype_is_network(const char *fstype) {
         static const char table[] =
                 "afs\0"
@@ -1493,6 +2066,7 @@ bool fstype_is_network(const char *fstype) {
 
         return nulstr_contains(table, fstype);
 }
+#endif // 0
 
 int flush_fd(int fd) {
         struct pollfd pollfd = {
@@ -1771,6 +2345,8 @@ bool is_device_path(const char *path) {
                 path_startswith(path, "/sys/");
 }
 
+/// UNNEEDED by elogind
+#if 0
 int dir_is_empty(const char *path) {
         _cleanup_closedir_ DIR *d;
 
@@ -1842,6 +2418,7 @@ void rename_process(const char name[8]) {
                 }
         }
 }
+#endif // 0
 
 char *lookup_uid(uid_t uid) {
         long bufsize;
@@ -1870,6 +2447,8 @@ char *lookup_uid(uid_t uid) {
         return name;
 }
 
+/// UNNEEDED by elogind
+#if 0
 char* getlogname_malloc(void) {
         uid_t uid;
         struct stat st;
@@ -1891,6 +2470,7 @@ char *getusername_malloc(void) {
 
         return lookup_uid(getuid());
 }
+#endif // 0
 
 bool is_temporary_fs(const struct statfs *s) {
         assert(s);
@@ -1945,7 +2525,6 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
 
         return 0;
 }
-#endif // 0
 
 cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
         cpu_set_t *r;
@@ -1974,6 +2553,7 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
                 n *= 2;
         }
 }
+#endif // 0
 
 int files_same(const char *filea, const char *fileb) {
         struct stat a, b;
@@ -2308,8 +2888,6 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
         return endswith(de->d_name, suffix);
 }
 
-/// UNNEEDED by elogind
-#if 0
 static int do_execute(char **directories, usec_t timeout, char *argv[]) {
         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
         _cleanup_set_free_free_ Set *seen = NULL;
@@ -2449,7 +3027,6 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a
 
         wait_for_terminate_and_warn(name, executor_pid, true);
 }
-#endif // 0
 
 bool nulstr_contains(const char*nulstr, const char *needle) {
         const char *i;
@@ -2480,21 +3057,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,
@@ -2554,8 +3116,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;
         }
 
@@ -2565,6 +3128,8 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         return 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int symlink_atomic(const char *from, const char *to) {
         _cleanup_free_ char *t = NULL;
         int r;
@@ -2587,8 +3152,6 @@ int symlink_atomic(const char *from, const char *to) {
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
 int symlink_idempotent(const char *from, const char *to) {
         _cleanup_free_ char *p = NULL;
         int r;
@@ -2865,7 +3428,6 @@ int in_group(const char *name) {
 
         return in_gid(gid);
 }
-#endif // 0
 
 int glob_exists(const char *path) {
         _cleanup_globfree_ glob_t g = {};
@@ -2886,8 +3448,6 @@ int glob_exists(const char *path) {
                 return errno ? -errno : -EIO;
 }
 
-/// UNNEEDED by elogind
-#if 0
 int glob_extend(char ***strv, const char *path) {
         _cleanup_globfree_ glob_t g = {};
         int k;
@@ -3056,6 +3616,8 @@ bool is_main_thread(void) {
         return cached > 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int block_get_whole_disk(dev_t d, dev_t *ret) {
         char *p, *s;
         int r;
@@ -3124,6 +3686,7 @@ static const char *const ioprio_class_table[] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
+#endif // 0
 
 static const char *const sigchld_code_table[] = {
         [CLD_EXITED] = "exited",
@@ -3245,7 +3808,7 @@ int prot_from_flags(int flags) {
                 return -EINVAL;
         }
 }
-#endif // 0
+
 char *format_bytes(char *buf, size_t l, off_t t) {
         unsigned i;
 
@@ -3284,6 +3847,7 @@ finish:
         return buf;
 
 }
+#endif // 0
 
 void* memdup(const void *p, size_t l) {
         void *r;
@@ -3435,6 +3999,8 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
         _exit(EXIT_FAILURE);
 }
 
+/// UNNEEDED by elogind
+#if 0
 int setrlimit_closest(int resource, const struct rlimit *rlim) {
         struct rlimit highest, fixed;
 
@@ -3459,8 +4025,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
 bool http_etag_is_valid(const char *etag) {
         if (isempty(etag))
                 return false;
@@ -3585,6 +4149,8 @@ int get_home_dir(char **_h) {
         return 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int get_shell(char **_s) {
         struct passwd *p;
         const char *e;
@@ -3631,6 +4197,7 @@ int get_shell(char **_s) {
         *_s = s;
         return 0;
 }
+#endif // 0
 
 bool filename_is_valid(const char *p) {
 
@@ -3662,7 +4229,7 @@ bool string_is_safe(const char *p) {
                 if (*t > 0 && *t < ' ')
                         return false;
 
-                if (strchr("\\\"\'\0x7f", *t))
+                if (strchr("\\\"\'\x7f", *t))
                         return false;
         }
 
@@ -3778,7 +4345,7 @@ bool is_locale_utf8(void) {
         /* Check result, but ignore the result if C was set
          * explicitly. */
         cached_answer =
-                streq(set, "C") &&
+                STR_IN_SET(set, "C", "POSIX") &&
                 !getenv("LC_ALL") &&
                 !getenv("LC_CTYPE") &&
                 !getenv("LANG");
@@ -3816,6 +4383,8 @@ const char *draw_special_char(DrawSpecialChar ch) {
         return draw_table[!is_locale_utf8()][ch];
 }
 
+/// UNNEEDED by elogind
+#if 0
 char *strreplace(const char *text, const char *old_string, const char *new_string) {
         const char *f;
         char *t, *r;
@@ -4024,6 +4593,7 @@ int on_ac_power(void) {
 
         return found_online || !found_offline;
 }
+#endif // 0
 
 static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
         char **i;
@@ -4339,7 +4909,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)
@@ -4379,7 +4949,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)
@@ -4424,6 +4994,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)
@@ -4446,8 +5019,8 @@ int container_get_leader(const char *machine, pid_t *pid) {
         return 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);
@@ -4479,6 +5052,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;
 
@@ -4497,15 +5079,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)
@@ -4519,6 +5119,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;
@@ -4613,6 +5217,8 @@ int mkostemp_safe(char *pattern, int flags) {
         return fd;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int open_tmpfile(const char *path, int flags) {
         char *p;
         int fd;
@@ -4636,6 +5242,7 @@ int open_tmpfile(const char *path, int flags) {
         unlink(p);
         return fd;
 }
+#endif // 0
 
 int fd_warn_permissions(const char *path, int fd) {
         struct stat st;
@@ -4682,7 +5289,6 @@ unsigned long personality_from_string(const char *p) {
 
         return PERSONALITY_INVALID;
 }
-#endif // 0
 
 const char* personality_to_string(unsigned long p) {
 
@@ -4702,6 +5308,7 @@ const char* personality_to_string(unsigned long p) {
 
         return NULL;
 }
+#endif // 0
 
 uint64_t physical_memory(void) {
         long mem;
@@ -4759,7 +5366,6 @@ void hexdump(FILE *f, const void *p, size_t s) {
                 s -= 16;
         }
 }
-#endif // 0
 
 int update_reboot_param_file(const char *param) {
         int r = 0;
@@ -5006,6 +5612,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
                 }
         }
 }
+#endif // 0
 
 int fflush_and_check(FILE *f) {
         assert(f);
@@ -5169,7 +5776,6 @@ int take_password_lock(const char *root) {
 
         return fd;
 }
-#endif // 0
 
 int is_symlink(const char *path) {
         struct stat info;
@@ -5179,6 +5785,7 @@ int is_symlink(const char *path) {
 
         return !!S_ISLNK(info.st_mode);
 }
+#endif // 0
 
 int is_dir(const char* path, bool follow) {
         struct stat st;
@@ -5206,7 +5813,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;
@@ -5219,13 +5826,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
@@ -5237,26 +5850,45 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                 switch (state) {
 
                 case START:
+                        if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+                                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                        return -ENOMEM;
+
                         if (c == 0)
-                                goto finish;
-                        else if (strchr(WHITESPACE, c))
+                                goto finish_force_terminate;
+                        else if (strchr(separators, c)) {
+                                if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+                                        (*p) ++;
+                                        goto finish_force_next;
+                                }
                                 break;
+                        }
+
+                        /* We found a non-blank character, so we will always
+                         * want to return a string (even if it is empty),
+                         * allocate it here. */
+                        if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                return -ENOMEM;
 
                         state = VALUE;
                         /* fallthrough */
 
                 case VALUE:
                         if (c == 0)
-                                goto finish;
-                        else if (c == '\'')
+                                goto finish_force_terminate;
+                        else if (c == '\'' && (flags & EXTRACT_QUOTES))
                                 state = SINGLE_QUOTE;
                         else if (c == '\\')
                                 state = VALUE_ESCAPE;
-                        else if (c == '\"')
+                        else if (c == '\"' && (flags & EXTRACT_QUOTES))
                                 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;
 
@@ -5267,8 +5899,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;
@@ -5306,29 +5938,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;
@@ -5351,24 +5983,27 @@ end_escape:
                                 VALUE;
                         break;
 
-                case SPACE:
+                case SEPARATOR:
                         if (c == 0)
+                                goto finish_force_terminate;
+                        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;
@@ -5376,26 +6011,29 @@ finish:
         return 1;
 }
 
-int unquote_first_word_and_warn(
+/// UNNEEDED by elogind
+#if 0
+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);
@@ -5406,9 +6044,7 @@ int unquote_first_word_and_warn(
         return r;
 }
 
-/// UNNEEDED by elogind
-#if 0
-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;
@@ -5434,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;
 
@@ -5489,6 +6125,8 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int ptsname_malloc(int fd, char **ret) {
         size_t l = 100;
 
@@ -5516,10 +6154,8 @@ int ptsname_malloc(int fd, char **ret) {
         }
 }
 
-/// UNNEEDED by elogind
-#if 0
 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;
@@ -5536,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;
 
@@ -5552,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);
 
@@ -5560,6 +6196,9 @@ int openpt_in_namespace(pid_t pid, int flags) {
                 if (master < 0)
                         _exit(EXIT_FAILURE);
 
+                if (unlockpt(master) < 0)
+                        _exit(EXIT_FAILURE);
+
                 cmsg = CMSG_FIRSTHDR(&mh);
                 cmsg->cmsg_level = SOL_SOCKET;
                 cmsg->cmsg_type = SCM_RIGHTS;
@@ -5654,6 +6293,8 @@ int fd_getcrtime(int fd, usec_t *usec) {
         return parse_crtime(le, usec);
 }
 
+/// UNNEEDED by elogind
+#if 0
 int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
         le64_t le;
         ssize_t n;
@@ -5667,8 +6308,6 @@ int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
         return parse_crtime(le, usec);
 }
 
-/// UNNEEDED by elogind
-#if 0
 int path_getcrtime(const char *p, usec_t *usec) {
         le64_t le;
         ssize_t n;
@@ -5684,7 +6323,6 @@ int path_getcrtime(const char *p, usec_t *usec) {
 
         return parse_crtime(le, usec);
 }
-#endif // 0
 
 int fd_setcrtime(int fd, usec_t usec) {
         le64_t le;
@@ -5765,6 +6403,7 @@ int same_fd(int a, int b) {
 
         return fa == fb;
 }
+#endif // 0
 
 int chattr_fd(int fd, unsigned value, unsigned mask) {
         unsigned old_attr, new_attr;
@@ -5800,6 +6439,8 @@ int chattr_fd(int fd, unsigned value, unsigned mask) {
         return 1;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int chattr_path(const char *p, unsigned value, unsigned mask) {
         _cleanup_close_ int fd = -1;
 
@@ -5814,6 +6455,7 @@ int chattr_path(const char *p, unsigned value, unsigned mask) {
 
         return chattr_fd(fd, value, mask);
 }
+#endif // 0
 
 int read_attr_fd(int fd, unsigned *ret) {
         struct stat st;
@@ -5832,6 +6474,8 @@ int read_attr_fd(int fd, unsigned *ret) {
         return 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int read_attr_path(const char *p, unsigned *ret) {
         _cleanup_close_ int fd = -1;
 
@@ -5845,8 +6489,6 @@ int read_attr_path(const char *p, unsigned *ret) {
         return read_attr_fd(fd, ret);
 }
 
-/// UNNEEDED by elogind
-#if 0
 static size_t nul_length(const uint8_t *p, size_t sz) {
         size_t n = 0;
 
@@ -5978,6 +6620,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
         return -1;
 }
 
+/// UNNEEDED by elogind
+#if 0
 void cmsg_close_all(struct msghdr *mh) {
         struct cmsghdr *cmsg;
 
@@ -5996,10 +6640,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
@@ -6029,6 +6672,32 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
         return 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;
@@ -6055,19 +6724,14 @@ 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;
 
         return r;
 }
+#endif // 0
 
 int parse_mode(const char *s, mode_t *ret) {
         char *x;
@@ -6090,6 +6754,8 @@ int parse_mode(const char *s, mode_t *ret) {
         return 0;
 }
 
+/// UNNEEDED by elogind
+#if 0
 int mount_move_root(const char *path) {
         assert(path);
 
@@ -6107,6 +6773,7 @@ int mount_move_root(const char *path) {
 
         return 0;
 }
+#endif // 0
 
 int reset_uid_gid(void) {
 
@@ -6121,3 +6788,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;
+        }
+}