chiark / gitweb /
binfmt,tmpfiles,modules-load,sysctl: rework the various early-boot services that...
[elogind.git] / src / shared / util.c
index d5f975e7445724b5d28da3b5aa3267239027843d..24f9e7ee58338c4ad293012e7811671e3a1dad9e 100644 (file)
@@ -70,6 +70,7 @@
 #include "path-util.h"
 #include "exit-status.h"
 #include "hashmap.h"
+#include "env-util.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -77,10 +78,6 @@ char **saved_argv = NULL;
 static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
 
-bool is_efiboot(void) {
-        return access("/sys/firmware/efi", F_OK) >= 0;
-}
-
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -439,7 +436,6 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
 
         if (!fgets(line, sizeof(line), f)) {
                 r = feof(f) ? -EIO : -errno;
-                fclose(f);
                 return r;
         }
 
@@ -562,9 +558,9 @@ int fchmod_umask(int fd, mode_t m) {
 }
 
 int write_one_line_file_atomic(const char *fn, const char *line) {
-        FILE *f;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *p = NULL;
         int r;
-        char *p;
 
         assert(fn);
         assert(line);
@@ -586,12 +582,9 @@ int write_one_line_file_atomic(const char *fn, const char *line) {
 
         fflush(f);
 
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else {
+        if (ferror(f))
+                r = errno ? -errno : -EIO;
+        else {
                 if (rename(p, fn) < 0)
                         r = -errno;
                 else
@@ -602,9 +595,6 @@ finish:
         if (r < 0)
                 unlink(p);
 
-        fclose(f);
-        free(p);
-
         return r;
 }
 
@@ -642,6 +632,9 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
         _cleanup_free_ char *buf = NULL;
         struct stat st;
 
+        assert(fn);
+        assert(contents);
+
         f = fopen(fn, "re");
         if (!f)
                 return -errno;
@@ -773,69 +766,88 @@ fail:
         return r;
 }
 
-int load_env_file(
-                const char *fname,
-                char ***rl) {
+int load_env_file(const char *fname, char ***rl) {
 
-        FILE *f;
-        char **m = NULL;
-        int r;
+        _cleanup_fclose_ FILE *f;
+        _cleanup_strv_free_ char **m = NULL;
+        _cleanup_free_ char *c = NULL;
 
         assert(fname);
         assert(rl);
 
-        if (!(f = fopen(fname, "re")))
+        /* This reads an environment file, but will not complain about
+         * any invalid assignments, that needs to be done by the
+         * caller */
+
+        f = fopen(fname, "re");
+        if (!f)
                 return -errno;
 
         while (!feof(f)) {
-                char l[LINE_MAX], *p, *u;
-                char **t;
+                char l[LINE_MAX], *p, *cs, *b;
 
                 if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
+                        if (ferror(f))
+                                return -errno;
 
-                        r = -errno;
-                        goto finish;
+                        /* The previous line was a continuation line?
+                         * Let's process it now, before we leave the
+                         * loop */
+                        if (c)
+                                goto process;
+
+                        break;
                 }
 
-                p = strstrip(l);
+                /* Is this a continuation line? If so, just append
+                 * this to c, and go to next line right-away */
+                cs = endswith(l, "\\\n");
+                if (cs) {
+                        *cs = '\0';
+                        b = strappend(c, l);
+                        if (!b)
+                                return -ENOMEM;
 
-                if (!*p)
+                        free(c);
+                        c = b;
                         continue;
+                }
 
-                if (strchr(COMMENTS, *p))
-                        continue;
+                /* If the previous line was a continuation line,
+                 * append the current line to it */
+                if (c) {
+                        b = strappend(c, l);
+                        if (!b)
+                                return -ENOMEM;
 
-                if (!(u = normalize_env_assignment(p))) {
-                        r = log_oom();
-                        goto finish;
+                        free(c);
+                        c = b;
                 }
 
-                t = strv_append(m, u);
-                free(u);
+        process:
+                p = strstrip(c ? c : l);
 
-                if (!t) {
-                        r = log_oom();
-                        goto finish;
+                if (*p && !strchr(COMMENTS, *p)) {
+                        _cleanup_free_ char *u;
+                        int k;
+
+                        u = normalize_env_assignment(p);
+                        if (!u)
+                                return -ENOMEM;
+
+                        k = strv_extend(&m, u);
+                        if (k < 0)
+                                return -ENOMEM;
                 }
 
-                strv_free(m);
-                m = t;
+                free(c);
+                c = NULL;
         }
 
-        r = 0;
-
         *rl = m;
         m = NULL;
 
-finish:
-        if (f)
-                fclose(f);
-
-        strv_free(m);
-
-        return r;
+        return 0;
 }
 
 int write_env_file(const char *fname, char **l) {
@@ -905,13 +917,10 @@ int get_process_comm(pid_t pid, char **name) {
 }
 
 int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
-        char *r, *k;
+        char *r = NULL, *k;
         int c;
-        bool space = false;
-        size_t left;
         FILE *f;
 
-        assert(max_length > 0);
         assert(line);
 
         if (pid == 0)
@@ -927,47 +936,64 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
 
         if (!f)
                 return -errno;
+        if (max_length == 0) {
+                size_t len = 1;
+                while ((c = getc(f)) != EOF) {
+                        k = realloc(r, len+1);
+                        if (k == NULL) {
+                                free(r);
+                                fclose(f);
+                                return -ENOMEM;
+                        }
+                        r = k;
+                        r[len-1] = isprint(c) ? c : ' ';
+                        r[len] = 0;
+                        len++;
+                }
+        } else {
+                bool space = false;
+                size_t left;
+                r = new(char, max_length);
+                if (!r) {
+                        fclose(f);
+                        return -ENOMEM;
+                }
 
-        r = new(char, max_length);
-        if (!r) {
-                fclose(f);
-                return -ENOMEM;
-        }
+                k = r;
+                left = max_length;
+                while ((c = getc(f)) != EOF) {
 
-        k = r;
-        left = max_length;
-        while ((c = getc(f)) != EOF) {
+                        if (isprint(c)) {
+                                if (space) {
+                                        if (left <= 4)
+                                                break;
+
+                                        *(k++) = ' ';
+                                        left--;
+                                        space = false;
+                                }
 
-                if (isprint(c)) {
-                        if (space) {
                                 if (left <= 4)
                                         break;
 
-                                *(k++) = ' ';
+                                *(k++) = (char) c;
                                 left--;
-                                space = false;
-                        }
-
-                        if (left <= 4)
-                                break;
+                        }  else
+                                space = true;
+                }
 
-                        *(k++) = (char) c;
-                        left--;
-                }  else
-                        space = true;
+                if (left <= 4) {
+                        size_t n = MIN(left-1, 3U);
+                        memcpy(k, "...", n);
+                        k[n] = 0;
+                } else
+                        *k = 0;
         }
 
-        if (left <= 4) {
-                size_t n = MIN(left-1, 3U);
-                memcpy(k, "...", n);
-                k[n] = 0;
-        } else
-                *k = 0;
-
         fclose(f);
 
         /* Kernel threads have no argv[] */
-        if (r[0] == 0) {
+        if (r == NULL || r[0] == 0) {
                 char *t;
                 int h;
 
@@ -1042,10 +1068,10 @@ int get_process_exe(pid_t pid, char **name) {
 }
 
 static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
-        char *p;
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *p = NULL;
 
+        assert(field);
         assert(uid);
 
         if (pid == 0)
@@ -1055,21 +1081,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
                 return -ENOMEM;
 
         f = fopen(p, "re");
-        free(p);
-
         if (!f)
                 return -errno;
 
-        while (!feof(f)) {
-                char line[LINE_MAX], *l;
-
-                if (!fgets(line, sizeof(line), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        goto finish;
-                }
+        FOREACH_LINE(f, line, return -errno) {
+                char *l;
 
                 l = strstrip(line);
 
@@ -1079,17 +1095,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
 
                         l[strcspn(l, WHITESPACE)] = 0;
 
-                        r = parse_uid(l, uid);
-                        goto finish;
+                        return parse_uid(l, uid);
                 }
         }
 
-        r = -EIO;
-
-finish:
-        fclose(f);
-
-        return r;
+        return -EIO;
 }
 
 int get_process_uid(pid_t pid, uid_t *uid) {
@@ -2851,6 +2861,9 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
                    &ttynr) != 1)
                 return -EIO;
 
+        if (major(ttynr) == 0 && minor(ttynr) == 0)
+                return -ENOENT;
+
         *d = (dev_t) ttynr;
         return 0;
 }
@@ -2869,7 +2882,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
         char_array_0(fn);
 
-        if ((k = readlink_malloc(fn, &s)) < 0) {
+        k = readlink_malloc(fn, &s);
+        if (k < 0) {
 
                 if (k != -ENOENT)
                         return k;
@@ -2890,7 +2904,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
                  * symlink in /dev/char. Let's return something
                  * vaguely useful. */
 
-                if (!(b = strdup(fn + 5)))
+                b = strdup(fn + 5);
+                if (!b)
                         return -ENOMEM;
 
                 *r = b;
@@ -3316,10 +3331,10 @@ char *replace_env(const char *format, char **env) {
                         if (*e == '}') {
                                 const char *t;
 
-                                if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
-                                        t = "";
+                                t = strempty(strv_env_get_n(env, word+2, e-word-2));
 
-                                if (!(k = strappend(r, t)))
+                                k = strappend(r, t);
+                                if (!k)
                                         goto fail;
 
                                 free(r);
@@ -3360,7 +3375,8 @@ char **replace_env_argv(char **argv, char **env) {
                         char **w, **m;
                         unsigned q;
 
-                        if ((e = strv_env_get(env, *i+1))) {
+                        e = strv_env_get(env, *i+1);
+                        if (e) {
 
                                 if (!(m = strv_split_quoted(e))) {
                                         r[k] = NULL;
@@ -4439,6 +4455,23 @@ int get_user_creds(
         return 0;
 }
 
+char* uid_to_name(uid_t uid) {
+        struct passwd *p;
+        char *r;
+
+        if (uid == 0)
+                return strdup("root");
+
+        p = getpwuid(uid);
+        if (p)
+                return strdup(p->pw_name);
+
+        if (asprintf(&r, "%lu", (unsigned long) uid) < 0)
+                return NULL;
+
+        return r;
+}
+
 int get_group_creds(const char **groupname, gid_t *gid) {
         struct group *g;
         gid_t id;
@@ -5277,9 +5310,13 @@ int can_sleep(const char *type) {
 
         assert(type);
 
+        /* If /sys is read-only we cannot sleep */
+        if (access("/sys/power/state", W_OK) < 0)
+                return false;
+
         r = read_one_line_file("/sys/power/state", &p);
         if (r < 0)
-                return r == -ENOENT ? 0 : r;
+                return false;
 
         k = strlen(type);
         FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
@@ -5297,9 +5334,14 @@ int can_sleep_disk(const char *type) {
 
         assert(type);
 
+        /* If /sys is read-only we cannot sleep */
+        if (access("/sys/power/state", W_OK) < 0 ||
+            access("/sys/power/disk", W_OK) < 0)
+                return false;
+
         r = read_one_line_file("/sys/power/disk", &p);
         if (r < 0)
-                return r == -ENOENT ? 0 : r;
+                return false;
 
         k = strlen(type);
         FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
@@ -5502,6 +5544,11 @@ void fclosep(FILE **f) {
                 fclose(*f);
 }
 
+void pclosep(FILE **f) {
+        if (*f)
+                pclose(*f);
+}
+
 void closep(int *fd) {
         if (*fd >= 0)
                 close_nointr_nofail(*fd);
@@ -5552,6 +5599,39 @@ bool string_is_safe(const char *p) {
         return true;
 }
 
+bool string_has_cc(const char *p) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++)
+                if (*t > 0 && *t < ' ')
+                        return true;
+
+        return false;
+}
+
+bool path_is_safe(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+                return false;
+
+        if (strlen(p) > PATH_MAX)
+                return false;
+
+        /* The following two checks are not really dangerous, but hey, they still are confusing */
+        if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+                return false;
+
+        if (strstr(p, "//"))
+                return false;
+
+        return true;
+}
+
 /* hey glibc, APIs with callbacks without a user pointer are so useless */
 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
                  int (*compar) (const void *, const void *, void *), void *arg) {
@@ -5604,12 +5684,14 @@ const char *draw_special_char(DrawSpecialChar ch) {
                         [DRAW_TREE_VERT]          = "\342\224\202 ",            /* │  */
                         [DRAW_TREE_BRANCH]        = "\342\224\234\342\224\200", /* ├─ */
                         [DRAW_TREE_RIGHT]         = "\342\224\224\342\224\200", /* └─ */
+                        [DRAW_TREE_SPACE]         = "  ",                       /*    */
                         [DRAW_TRIANGULAR_BULLET]  = "\342\200\243 ",            /* ‣  */
                 },
                 /* ASCII fallback */ {
                         [DRAW_TREE_VERT]          = "| ",
                         [DRAW_TREE_BRANCH]        = "|-",
                         [DRAW_TREE_RIGHT]         = "`-",
+                        [DRAW_TREE_SPACE]         = "  ",
                         [DRAW_TRIANGULAR_BULLET]  = "> ",
                 }
         };
@@ -5666,3 +5748,243 @@ oom:
         free(r);
         return NULL;
 }
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+        const char *i, *begin = NULL;
+        enum {
+                STATE_OTHER,
+                STATE_ESCAPE,
+                STATE_BRACKET
+        } state = STATE_OTHER;
+        char *obuf = NULL;
+        size_t osz = 0, isz;
+        FILE *f;
+
+        assert(ibuf);
+        assert(*ibuf);
+
+        /* Strips ANSI color and replaces TABs by 8 spaces */
+
+        isz = _isz ? *_isz : strlen(*ibuf);
+
+        f = open_memstream(&obuf, &osz);
+        if (!f)
+                return NULL;
+
+        for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+
+                switch (state) {
+
+                case STATE_OTHER:
+                        if (i >= *ibuf + isz) /* EOT */
+                                break;
+                        else if (*i == '\x1B')
+                                state = STATE_ESCAPE;
+                        else if (*i == '\t')
+                                fputs("        ", f);
+                        else
+                                fputc(*i, f);
+                        break;
+
+                case STATE_ESCAPE:
+                        if (i >= *ibuf + isz) { /* EOT */
+                                fputc('\x1B', f);
+                                break;
+                        } else if (*i == '[') {
+                                state = STATE_BRACKET;
+                                begin = i + 1;
+                        } else {
+                                fputc('\x1B', f);
+                                fputc(*i, f);
+                                state = STATE_OTHER;
+                        }
+
+                        break;
+
+                case STATE_BRACKET:
+
+                        if (i >= *ibuf + isz || /* EOT */
+                            (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+                                fputc('\x1B', f);
+                                fputc('[', f);
+                                state = STATE_OTHER;
+                                i = begin-1;
+                        } else if (*i == 'm')
+                                state = STATE_OTHER;
+                        break;
+                }
+        }
+
+        if (ferror(f)) {
+                fclose(f);
+                free(obuf);
+                return NULL;
+        }
+
+        fclose(f);
+
+        free(*ibuf);
+        *ibuf = obuf;
+
+        if (_isz)
+                *_isz = osz;
+
+        return obuf;
+}
+
+int on_ac_power(void) {
+        bool found_offline = false, found_online = false;
+        _cleanup_closedir_ DIR *d = NULL;
+
+        d = opendir("/sys/class/power_supply");
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent *de;
+                union dirent_storage buf;
+                _cleanup_free_ char *p = NULL;
+                _cleanup_close_ int fd = -1, device = -1;
+                char contents[6];
+                ssize_t n;
+                int k;
+
+                k = readdir_r(d, &buf.de, &de);
+                if (k != 0)
+                        return -k;
+
+                if (!de)
+                        break;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (device < 0) {
+                        if (errno == ENOENT || errno == ENOTDIR)
+                                continue;
+
+                        return -errno;
+                }
+
+                fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                n = read(fd, contents, sizeof(contents));
+                if (n < 0)
+                        return -errno;
+
+                if (n != 6 || memcmp(contents, "Mains\n", 6))
+                        continue;
+
+                close_nointr_nofail(fd);
+                fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                n = read(fd, contents, sizeof(contents));
+                if (n < 0)
+                        return -errno;
+
+                if (n != 2 || contents[1] != '\n')
+                        return -EIO;
+
+                if (contents[0] == '1') {
+                        found_online = true;
+                        break;
+                } else if (contents[0] == '0')
+                        found_offline = true;
+                else
+                        return -EIO;
+        }
+
+        return found_online || !found_offline;
+}
+
+static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) {
+        char **i;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (!path_strv_canonicalize_uniq(search))
+                return -ENOMEM;
+
+        STRV_FOREACH(i, search) {
+                _cleanup_free_ char *p = NULL;
+                FILE *f;
+
+                p = strjoin(*i, "/", path, NULL);
+                if (!p)
+                        return -ENOMEM;
+
+                f = fopen(p, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                if (errno != ENOENT)
+                        return -errno;
+        }
+
+        return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) {
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        copy = strv_copy((char**) search);
+        if (!copy)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) {
+        _cleanup_strv_free_ char **s = NULL;
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        s = strv_split_nulstr(search);
+        if (!s)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, s, _f);
+}