chiark / gitweb /
util: don't consider tabs special in string_has_cc() anymore
[elogind.git] / src / shared / util.c
index 7fa3742b4afa69f7e83e067d83c166bcee75ced2..d223ecf7114ddd809d65d954e1367f04551fe725 100644 (file)
@@ -280,6 +280,14 @@ int parse_uid(const char *s, uid_t* ret_uid) {
         if ((unsigned long) uid != ul)
                 return -ERANGE;
 
+        /* Some libc APIs use (uid_t) -1 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;
+
         *ret_uid = uid;
         return 0;
 }
@@ -804,7 +812,7 @@ char *strappend(const char *s, const char *suffix) {
         return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
 }
 
-int readlink_malloc(const char *p, char **ret) {
+int readlinkat_malloc(int fd, const char *p, char **ret) {
         size_t l = 100;
         int r;
 
@@ -819,7 +827,7 @@ int readlink_malloc(const char *p, char **ret) {
                 if (!c)
                         return -ENOMEM;
 
-                n = readlink(p, c, l-1);
+                n = readlinkat(fd, p, c, l-1);
                 if (n < 0) {
                         r = -errno;
                         free(c);
@@ -837,6 +845,10 @@ int readlink_malloc(const char *p, char **ret) {
         }
 }
 
+int readlink_malloc(const char *p, char **ret) {
+        return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
 int readlink_and_make_absolute(const char *p, char **r) {
         _cleanup_free_ char *target = NULL;
         char *k;
@@ -1244,7 +1256,7 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                         a = unhexchar(f[1]);
                         b = unhexchar(f[2]);
 
-                        if (a < 0 || b < 0) {
+                        if (a < 0 || b < 0 || (a == 0 && b == 0)) {
                                 /* Invalid escape code, let's take it literal then */
                                 *(t++) = '\\';
                                 *(t++) = 'x';
@@ -1271,7 +1283,7 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
                         b = unoctchar(f[1]);
                         c = unoctchar(f[2]);
 
-                        if (a < 0 || b < 0 || c < 0) {
+                        if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
                                 /* Invalid escape code, let's take it literal then */
                                 *(t++) = '\\';
                                 *(t++) = f[0];
@@ -1436,7 +1448,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
 }
 
 int close_all_fds(const int except[], unsigned n_except) {
-        DIR *d;
+        _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
         int r = 0;
 
@@ -1491,7 +1503,6 @@ int close_all_fds(const int except[], unsigned n_except) {
                 }
         }
 
-        closedir(d);
         return r;
 }
 
@@ -1510,6 +1521,7 @@ bool fstype_is_network(const char *fstype) {
         static const char table[] =
                 "cifs\0"
                 "smbfs\0"
+                "sshfs\0"
                 "ncpfs\0"
                 "ncp\0"
                 "nfs\0"
@@ -1554,8 +1566,7 @@ int chvt(int vt) {
 
 int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
         struct termios old_termios, new_termios;
-        char c;
-        char line[LINE_MAX];
+        char c, line[LINE_MAX];
 
         assert(f);
         assert(ret);
@@ -1592,12 +1603,14 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
                 }
         }
 
-        if (t != (usec_t) -1)
+        if (t != (usec_t) -1) {
                 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
                         return -ETIMEDOUT;
+        }
 
+        errno = 0;
         if (!fgets(line, sizeof(line), f))
-                return -EIO;
+                return errno ? -errno : -EIO;
 
         truncate_nl(line);
 
@@ -1612,6 +1625,7 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
 }
 
 int ask(char *ret, const char *replies, const char *text, ...) {
+        int r;
 
         assert(ret);
         assert(replies);
@@ -1620,7 +1634,6 @@ int ask(char *ret, const char *replies, const char *text, ...) {
         for (;;) {
                 va_list ap;
                 char c;
-                int r;
                 bool need_nl = true;
 
                 if (on_tty())
@@ -2667,7 +2680,7 @@ finish:
 }
 
 int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
-        DIR *d;
+        _cleanup_closedir_ DIR *d = NULL;
         int ret = 0;
 
         assert(fd >= 0);
@@ -2690,15 +2703,12 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
 
                 errno = 0;
                 de = readdir(d);
-                if (!de && errno != 0) {
-                        if (ret == 0)
+                if (!de) {
+                        if (errno != 0 && ret == 0)
                                 ret = -errno;
-                        break;
+                        return ret;
                 }
 
-                if (!de)
-                        break;
-
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
@@ -2754,10 +2764,6 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
                         }
                 }
         }
-
-        closedir(d);
-
-        return ret;
 }
 
 _pure_ static int is_temporary_fs(struct statfs *s) {
@@ -3477,6 +3483,17 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
         }
 }
 
+/*
+ * Return values:
+ * < 0 : wait_for_terminate() failed to get the state of the
+ *       process, the process was terminated by a signal, or
+ *       failed for an unknown reason.
+ * >=0 : The process terminated normally, and its exit code is
+ *       returned.
+ *
+ * That is, success is indicated by a return value of zero, and an
+ * error is indicated by a non-zero value.
+ */
 int wait_for_terminate_and_warn(const char *name, pid_t pid) {
         int r;
         siginfo_t status;
@@ -3615,9 +3632,6 @@ char *fstab_node_to_udev_node(const char *p) {
 bool tty_is_vc(const char *tty) {
         assert(tty);
 
-        if (startswith(tty, "/dev/"))
-                tty += 5;
-
         return vtnr_from_tty(tty) >= 0;
 }
 
@@ -3962,6 +3976,21 @@ char* hostname_cleanup(char *s, bool lowercase) {
         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,
@@ -4003,24 +4032,16 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         FILE *f;
         char *t;
-        const char *fn;
-        size_t k;
         int fd;
 
         assert(path);
         assert(_f);
         assert(_temp_path);
 
-        t = new(char, strlen(path) + 1 + 6 + 1);
+        t = tempfn_xxxxxx(path);
         if (!t)
                 return -ENOMEM;
 
-        fn = basename(path);
-        k = fn - path;
-        memcpy(t, path, k);
-        t[k] = '.';
-        stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
-
         fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
         if (fd < 0) {
                 free(t);
@@ -4128,97 +4149,62 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-int copy_file(const char *from, const char *to, int flags) {
-        _cleanup_close_ int fdf = -1;
-        int r, fdt;
+int symlink_atomic(const char *from, const char *to) {
+        _cleanup_free_ char *t = NULL;
 
         assert(from);
         assert(to);
 
-        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fdf < 0)
-                return -errno;
+        t = tempfn_random(to);
+        if (!t)
+                return -ENOMEM;
 
-        fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
-        if (fdt < 0)
+        if (symlink(from, t) < 0)
                 return -errno;
 
-        for (;;) {
-                char buf[PIPE_BUF];
-                ssize_t n, k;
-
-                n = read(fdf, buf, sizeof(buf));
-                if (n < 0) {
-                        r = -errno;
-
-                        close_nointr(fdt);
-                        unlink(to);
-
-                        return r;
-                }
+        if (rename(t, to) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
 
-                if (n == 0)
-                        break;
+        return 0;
+}
 
-                errno = 0;
-                k = loop_write(fdt, buf, n, false);
-                if (n != k) {
-                        r = k < 0 ? k : (errno ? -errno : -EIO);
+int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
+        _cleanup_free_ char *t = NULL;
 
-                        close_nointr(fdt);
-                        unlink(to);
+        assert(path);
 
-                        return r;
-                }
-        }
+        t = tempfn_random(path);
+        if (!t)
+                return -ENOMEM;
 
-        r = close_nointr(fdt);
+        if (mknod(t, mode, dev) < 0)
+                return -errno;
 
-        if (r < 0) {
-                unlink(to);
-                return r;
+        if (rename(t, path) < 0) {
+                unlink_noerrno(t);
+                return -errno;
         }
 
         return 0;
 }
 
-int symlink_atomic(const char *from, const char *to) {
-        char *x;
-        _cleanup_free_ char *t;
-        const char *fn;
-        size_t k;
-        uint64_t u;
-        unsigned i;
-        int r;
+int mkfifo_atomic(const char *path, mode_t mode) {
+        _cleanup_free_ char *t = NULL;
 
-        assert(from);
-        assert(to);
+        assert(path);
 
-        t = new(char, strlen(to) + 1 + 16 + 1);
+        t = tempfn_random(path);
         if (!t)
                 return -ENOMEM;
 
-        fn = basename(to);
-        k = fn-to;
-        memcpy(t, to, k);
-        t[k] = '.';
-        x = stpcpy(t+k+1, fn);
-
-        u = random_u64();
-        for (i = 0; i < 16; i++) {
-                *(x++) = hexchar(u & 0xF);
-                u >>= 4;
-        }
-
-        *x = 0;
-
-        if (symlink(from, t) < 0)
+        if (mkfifo(t, mode) < 0)
                 return -errno;
 
-        if (rename(t, to) < 0) {
-                r = -errno;
-                unlink(t);
-                return r;
+        if (rename(t, path) < 0) {
+                unlink_noerrno(t);
+                return -errno;
         }
 
         return 0;
@@ -4502,22 +4488,6 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
         return 0;
 }
 
-int in_search_path(const char *path, char **search) {
-        char **i;
-        _cleanup_free_ char *parent = NULL;
-        int r;
-
-        r = path_get_parent(path, &parent);
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH(i, search)
-                if (path_equal(parent, *i))
-                        return 1;
-
-        return 0;
-}
-
 int get_files_in_directory(const char *path, char ***list) {
         _cleanup_closedir_ DIR *d = NULL;
         size_t bufsize = 0, n = 0;
@@ -5272,8 +5242,8 @@ int get_home_dir(char **_h) {
         assert(_h);
 
         /* Take the user specified one */
-        e = getenv("HOME");
-        if (e) {
+        e = secure_getenv("HOME");
+        if (e && path_is_absolute(e)) {
                 h = strdup(e);
                 if (!h)
                         return -ENOMEM;
@@ -5380,13 +5350,14 @@ bool filename_is_safe(const char *p) {
 bool string_is_safe(const char *p) {
         const char *t;
 
-        assert(p);
+        if (!p)
+                return false;
 
         for (t = p; *t; t++) {
                 if (*t > 0 && *t < ' ')
                         return false;
 
-                if (strchr("\\\"\'", *t))
+                if (strchr("\\\"\'\0x7f", *t))
                         return false;
         }
 
@@ -5394,18 +5365,25 @@ bool string_is_safe(const char *p) {
 }
 
 /**
- * Check if a string contains control characters.
- * Spaces and tabs are not considered control characters.
+ * Check if a string contains control characters. If 'ok' is non-NULL
+ * it may be a string containing additional CCs to be considered OK.
  */
-bool string_has_cc(const char *p) {
+bool string_has_cc(const char *p, const char *ok) {
         const char *t;
 
         assert(p);
 
-        for (t = p; *t; t++)
-                if (*t > 0 && *t < ' ' && *t != '\t')
+        for (t = p; *t; t++) {
+                if (ok && strchr(ok, *t))
+                        return false;
+
+                if (*t > 0 && *t < ' ')
                         return true;
 
+                if (*t == 127)
+                        return true;
+        }
+
         return false;
 }
 
@@ -5507,6 +5485,7 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TRIANGULAR_BULLET]  = "\342\200\243",             /* ‣ */
                         [DRAW_BLACK_CIRCLE]       = "\342\227\217",             /* ● */
                         [DRAW_ARROW]              = "\342\206\222",             /* → */
+                        [DRAW_DASH]               = "\342\200\223",             /* – */
                 },
 
                 /* ASCII fallback */ {
@@ -5517,6 +5496,7 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TRIANGULAR_BULLET]  = ">",
                         [DRAW_BLACK_CIRCLE]       = "*",
                         [DRAW_ARROW]              = "->",
+                        [DRAW_DASH]               = "-",
                 }
         };
 
@@ -5739,14 +5719,17 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
         assert(mode);
         assert(_f);
 
-        if (!path_strv_canonicalize_absolute_uniq(search, root))
+        if (!path_strv_resolve_uniq(search, root))
                 return -ENOMEM;
 
         STRV_FOREACH(i, search) {
                 _cleanup_free_ char *p = NULL;
                 FILE *f;
 
-                p = strjoin(*i, "/", path, NULL);
+                if (root)
+                        p = strjoin(root, *i, "/", path, NULL);
+                else
+                        p = strjoin(*i, "/", path, NULL);
                 if (!p)
                         return -ENOMEM;
 
@@ -6713,3 +6696,78 @@ int bind_remount_recursive(const char *prefix, bool ro) {
                 }
         }
 }
+
+int fflush_and_check(FILE *f) {
+        assert(f);
+
+        errno = 0;
+        fflush(f);
+
+        if (ferror(f))
+                return errno ? -errno : -EIO;
+
+        return 0;
+}
+
+char *tempfn_xxxxxx(const char *p) {
+        const char *fn;
+        char *t;
+        size_t k;
+
+        assert(p);
+
+        t = new(char, strlen(p) + 1 + 6 + 1);
+        if (!t)
+                return NULL;
+
+        fn = basename(p);
+        k = fn - p;
+
+        strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX");
+
+        return t;
+}
+
+char *tempfn_random(const char *p) {
+        const char *fn;
+        char *t, *x;
+        uint64_t u;
+        size_t k;
+        unsigned i;
+
+        assert(p);
+
+        t = new(char, strlen(p) + 1 + 16 + 1);
+        if (!t)
+                return NULL;
+
+        fn = basename(p);
+        k = fn - p;
+
+        x = stpcpy(stpcpy(mempcpy(t, p, k), "."), fn);
+
+        u = random_u64();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(u & 0xF);
+                u >>= 4;
+        }
+
+        *x = 0;
+
+        return t;
+}
+
+/* make sure the hostname is not "localhost" */
+bool is_localhost(const char *hostname) {
+        assert(hostname);
+
+        /* This tries to identify local hostnames described in RFC6761
+         * plus the redhatism of .localdomain */
+
+        return streq(hostname, "localhost") ||
+               streq(hostname, "localhost.") ||
+               endswith(hostname, ".localhost") ||
+               endswith(hostname, ".localhost.") ||
+               endswith(hostname, ".localdomain") ||
+               endswith(hostname, ".localdomain.");
+}