X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=d754c836f2b5386cd01f711fb71b74e030a4aa30;hb=61cc634bc208b264bd2fbe6af241b345a2002509;hp=08c0c2be131c2531c6168f1c1f8d62d9d1ab8c7e;hpb=565d91fdf198b88f7c2d72c67cfc6c30341a3596;p=elogind.git diff --git a/src/shared/util.c b/src/shared/util.c index 08c0c2be1..d754c836f 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -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; @@ -186,18 +183,25 @@ bool first_word(const char *s, const char *word) { } int close_nointr(int fd) { - assert(fd >= 0); - - for (;;) { - int r; + int r; - r = close(fd); - if (r >= 0) - return r; + assert(fd >= 0); + r = close(fd); - if (errno != EINTR) - return -errno; - } + /* Just ignore EINTR; a retry loop is the wrong + * thing to do on Linux. + * + * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + * https://bugzilla.gnome.org/show_bug.cgi?id=682819 + * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR + * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain + */ + if (_unlikely_(r < 0 && errno == EINTR)) + return 0; + else if (r >= 0) + return r; + else + return -errno; } void close_nointr_nofail(int fd) { @@ -221,9 +225,9 @@ void close_many(const int fds[], unsigned n_fd) { int parse_boolean(const char *v) { assert(v); - if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) + if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || strcaseeq(v, "on")) return 1; - else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) + else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || strcaseeq(v, "off")) return 0; return -EINVAL; @@ -561,9 +565,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); @@ -585,12 +589,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 @@ -601,9 +602,6 @@ finish: if (r < 0) unlink(p); - fclose(f); - free(p); - return r; } @@ -729,7 +727,7 @@ int parse_env_file( value = va_arg(ap, char **); n = strlen(key); - if (strncmp(p, key, n) != 0 || + if (!strneq(p, key, n) || p[n] != '=') continue; @@ -775,102 +773,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 *b; - char *c = NULL; - 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, *cs; - char **t; + char l[LINE_MAX], *p, *cs, *b; if (!fgets(l, sizeof(l), f)) { - if(!feof(f)) { - r = -errno; - goto finish; - } - else if (!c) - break; + if (ferror(f)) + return -errno; + + /* The previous line was a continuation line? + * Let's process it now, before we leave the + * loop */ + if (c) + goto process; + break; } + /* 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) { - r = log_oom(); - goto finish; - } + if (!b) + return -ENOMEM; + free(c); c = b; - *l = '\0'; continue; } + /* If the previous line was a continuation line, + * append the current line to it */ if (c) { b = strappend(c, l); - if (!b) { - r = log_oom(); - goto finish; - } + if (!b) + return -ENOMEM; + free(c); c = b; } + process: p = strstrip(c ? c : l); - if (!*p) - continue; + if (*p && !strchr(COMMENTS, *p)) { + _cleanup_free_ char *u; + int k; - if (strchr(COMMENTS, *p)) - continue; + u = normalize_env_assignment(p); + if (!u) + return -ENOMEM; - if (!(u = normalize_env_assignment(p))) { - r = log_oom(); - goto finish; + k = strv_extend(&m, u); + if (k < 0) + return -ENOMEM; } + free(c); c = NULL; - - t = strv_append(m, u); - free(u); - - if (!t) { - r = log_oom(); - goto finish; - } - - strv_free(m); - m = t; } - r = 0; - *rl = m; m = NULL; -finish: - if (f) - fclose(f); - - free(c); - - strv_free(m); - - return r; + return 0; } int write_env_file(const char *fname, char **l) { @@ -1091,10 +1075,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) @@ -1104,21 +1088,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); @@ -1128,17 +1102,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) { @@ -3370,10 +3338,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); @@ -3414,7 +3382,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; @@ -5582,6 +5551,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); @@ -5632,6 +5606,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) { @@ -5909,3 +5916,82 @@ int on_ac_power(void) { 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); +}