X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=2b76a5c885e93fba0faf881f2d9ea56b479fa2bc;hp=57943b64117f9afa5ac99f13599d46254e45f26b;hb=22f5f6281fc25f43a85938e9dad2480b2595c471;hpb=4b73a0c0612d26d49791f389e92f85d6444c36af diff --git a/src/shared/util.c b/src/shared/util.c index 57943b641..2b76a5c88 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -73,6 +73,7 @@ #include "hashmap.h" #include "env-util.h" #include "fileio.h" +#include "utf8.h" int saved_argc = 0; char **saved_argv = NULL; @@ -80,16 +81,6 @@ char **saved_argv = NULL; static volatile unsigned cached_columns = 0; static volatile unsigned cached_lines = 0; -#define PROCFS_PATH_LEN (sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t)) - -#define FORMAT_PROCFS_PATH(buffer, path, pid) \ - do { \ - assert_cc(sizeof(buffer) == (PROCFS_PATH_LEN + 1 + sizeof(path))); \ - snprintf(buffer, sizeof(buffer) - 1, "/proc/%lu/%s", (unsigned long) pid, path); \ - char_array_0(buffer); \ - } while(0) - - size_t page_size(void) { static __thread size_t pgsz = 0; long r; @@ -138,40 +129,6 @@ char* endswith(const char *s, const char *postfix) { return (char*) s + sl - pl; } -char* startswith(const char *s, const char *prefix) { - const char *a, *b; - - assert(s); - assert(prefix); - - a = s, b = prefix; - for (;;) { - if (*b == 0) - return (char*) a; - if (*a != *b) - return NULL; - - a++, b++; - } -} - -char* startswith_no_case(const char *s, const char *prefix) { - const char *a, *b; - - assert(s); - assert(prefix); - - a = s, b = prefix; - for (;;) { - if (*b == 0) - return (char*) a; - if (tolower(*a) != tolower(*b)) - return NULL; - - a++, b++; - } -} - bool first_word(const char *s, const char *word) { size_t sl, wl; @@ -377,13 +334,15 @@ int safe_atolli(const char *s, long long int *ret_lli) { int safe_atod(const char *s, double *ret_d) { char *x = NULL; - double d; + double d = 0; assert(s); assert(ret_d); - errno = 0; - d = strtod(s, &x); + RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") { + errno = 0; + d = strtod(s, &x); + } if (!x || x == s || *x || errno) return errno ? -errno : -EINVAL; @@ -468,15 +427,20 @@ char *split_quoted(const char *c, size_t *l, char **state) { int get_parent_of_pid(pid_t pid, pid_t *_ppid) { int r; _cleanup_fclose_ FILE *f = NULL; - char fn[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/stat")], line[LINE_MAX], *p; + char line[LINE_MAX]; long unsigned ppid; + const char *p; - assert(pid > 0); + assert(pid >= 0); assert(_ppid); - assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1)); + if (pid == 0) { + *_ppid = getppid(); + return 0; + } - f = fopen(fn, "re"); + p = procfs_file_alloca(pid, "stat"); + f = fopen(p, "re"); if (!f) return -errno; @@ -511,14 +475,18 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { int get_starttime_of_pid(pid_t pid, unsigned long long *st) { _cleanup_fclose_ FILE *f = NULL; - char fn[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/stat")], line[LINE_MAX], *p; + char line[LINE_MAX]; + const char *p; - assert(pid > 0); + assert(pid >= 0); assert(st); - assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1)); + if (pid == 0) + p = "/proc/self/stat"; + else + p = procfs_file_alloca(pid, "stat"); - f = fopen(fn, "re"); + f = fopen(p, "re"); if (!f) return -errno; @@ -585,60 +553,60 @@ char *truncate_nl(char *s) { } int get_process_comm(pid_t pid, char **name) { - int r; + const char *p; assert(name); + assert(pid >= 0); if (pid == 0) - r = read_one_line_file("/proc/self/comm", name); - else { - char path[PROCFS_PATH_LEN + sizeof("/comm")]; - FORMAT_PROCFS_PATH(path, "comm", pid); - r = read_one_line_file(path, name); - } + p = "/proc/self/comm"; + else + p = procfs_file_alloca(pid, "comm"); - return r; + return read_one_line_file(p, name); } int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + _cleanup_fclose_ FILE *f = NULL; char *r = NULL, *k; + const char *p; int c; - FILE *f; assert(line); + assert(pid >= 0); if (pid == 0) - f = fopen("/proc/self/cmdline", "re"); - else { - char path[PROCFS_PATH_LEN + sizeof("/cmdline")]; - FORMAT_PROCFS_PATH(path, "cmdline", pid); - f = fopen(path, "re"); - } + p = "/proc/self/cmdline"; + else + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); if (!f) return -errno; + if (max_length == 0) { - size_t len = 1; + size_t len = 0, allocated = 0; + while ((c = getc(f)) != EOF) { - k = realloc(r, len+1); - if (k == NULL) { + + if (!GREEDY_REALLOC(r, allocated, len+2)) { free(r); - fclose(f); return -ENOMEM; } - r = k; - r[len-1] = isprint(c) ? c : ' '; - r[len] = 0; - len++; + + r[len++] = isprint(c) ? c : ' '; } + + if (len > 0) + r[len-1] = 0; + } else { bool space = false; size_t left; + r = new(char, max_length); - if (!r) { - fclose(f); + if (!r) return -ENOMEM; - } k = r; left = max_length; @@ -671,8 +639,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * *k = 0; } - fclose(f); - /* Kernel threads have no argv[] */ if (r == NULL || r[0] == 0) { char *t; @@ -699,7 +665,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * } int is_kernel_thread(pid_t pid) { - char path[PROCFS_PATH_LEN + sizeof("/cmdline")]; + const char *p; size_t count; char c; bool eof; @@ -708,9 +674,10 @@ int is_kernel_thread(pid_t pid) { if (pid == 0) return 0; - FORMAT_PROCFS_PATH(path, "cmdline", pid); - f = fopen(path, "re"); + assert(pid > 0); + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); if (!f) return -errno; @@ -726,26 +693,48 @@ int is_kernel_thread(pid_t pid) { return 0; } +int get_process_capeff(pid_t pid, char **capeff) { + const char *p; + + assert(capeff); + assert(pid >= 0); + + if (pid == 0) + p = "/proc/self/status"; + else + p = procfs_file_alloca(pid, "status"); + + return get_status_field(p, "\nCapEff:", capeff); +} + int get_process_exe(pid_t pid, char **name) { + const char *p; + char *d; int r; + assert(pid >= 0); assert(name); if (pid == 0) - r = readlink_malloc("/proc/self/exe", name); - else { - char path[PROCFS_PATH_LEN + sizeof("/exe")]; - FORMAT_PROCFS_PATH(path, "exe", pid); - r = readlink_malloc(path, name); - } + p = "/proc/self/exe"; + else + p = procfs_file_alloca(pid, "exe"); - return r; + r = readlink_malloc(p, name); + if (r < 0) + return r; + + d = endswith(*name, " (deleted)"); + if (d) + *d = '\0'; + + return 0; } static int get_process_id(pid_t pid, const char *field, uid_t *uid) { _cleanup_fclose_ FILE *f = NULL; - char path[PROCFS_PATH_LEN + sizeof("/status")]; char line[LINE_MAX]; + const char *p; assert(field); assert(uid); @@ -753,8 +742,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { if (pid == 0) return getuid(); - FORMAT_PROCFS_PATH(path, "status", pid); - f = fopen(path, "re"); + p = procfs_file_alloca(pid, "status"); + f = fopen(p, "re"); if (!f) return -errno; @@ -781,6 +770,7 @@ int get_process_uid(pid_t pid, uid_t *uid) { } int get_process_gid(pid_t pid, gid_t *gid) { + assert_cc(sizeof(uid_t) == sizeof(gid_t)); return get_process_id(pid, "Gid:", gid); } @@ -850,18 +840,18 @@ int readlink_malloc(const char *p, char **r) { } int readlink_and_make_absolute(const char *p, char **r) { - char *target, *k; + _cleanup_free_ char *target = NULL; + char *k; int j; assert(p); assert(r); - if ((j = readlink_malloc(p, &target)) < 0) + j = readlink_malloc(p, &target); + if (j < 0) return j; k = file_in_same_dir(p, target); - free(target); - if (!k) return -ENOMEM; @@ -1449,7 +1439,7 @@ char *ascii_strlower(char *t) { return t; } -static bool ignore_file_allow_backup(const char *filename) { +_pure_ static bool ignore_file_allow_backup(const char *filename) { assert(filename); return @@ -1512,7 +1502,7 @@ int fd_cloexec(int fd, bool cloexec) { return 0; } -static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { +_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { unsigned i; assert(n_fdset == 0 || fdset); @@ -1600,6 +1590,7 @@ bool fstype_is_network(const char *fstype) { "cifs\0" "smbfs\0" "ncpfs\0" + "ncp\0" "nfs\0" "nfs4\0" "gfs\0" @@ -1830,8 +1821,10 @@ int open_terminal(const char *name, int mode) { * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 */ + assert(!(mode & O_CREAT)); + for (;;) { - fd = open(name, mode); + fd = open(name, mode, 0); if (fd >= 0) break; @@ -2073,7 +2066,7 @@ int release_terminal(void) { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART, }; - int _cleanup_close_ fd; + _cleanup_close_ int fd; fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC); if (fd < 0) @@ -2261,7 +2254,7 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { int parse_bytes(const char *t, off_t *bytes) { static const struct { const char *suffix; - off_t factor; + unsigned long long factor; } table[] = { { "B", 1 }, { "K", 1024ULL }, @@ -2274,7 +2267,7 @@ int parse_bytes(const char *t, off_t *bytes) { }; const char *p; - off_t r = 0; + unsigned long long r = 0; assert(t); assert(bytes); @@ -2301,7 +2294,17 @@ int parse_bytes(const char *t, off_t *bytes) { for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - r += (off_t) l * table[i].factor; + unsigned long long tmp; + if ((unsigned long long) l > ULLONG_MAX / table[i].factor) + return -ERANGE; + tmp = l * table[i].factor; + if (tmp > ULLONG_MAX - r) + return -ERANGE; + + r += tmp; + if ((unsigned long long) (off_t) r != r) + return -ERANGE; + p = e + strlen(table[i].suffix); break; } @@ -2309,7 +2312,7 @@ int parse_bytes(const char *t, off_t *bytes) { if (i >= ELEMENTSOF(table)) return -EINVAL; - } while (*p != 0); + } while (*p); *bytes = r; @@ -2566,27 +2569,29 @@ int getttyname_harder(int fd, char **r) { } int get_ctty_devnr(pid_t pid, dev_t *d) { - int k; - char line[LINE_MAX], *p, *fn; + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX], *p; unsigned long ttynr; - FILE *f; + const char *fn; + int k; - if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0) - return -ENOMEM; + assert(pid >= 0); + assert(d); + + if (pid == 0) + fn = "/proc/self/stat"; + else + fn = procfs_file_alloca(pid, "stat"); f = fopen(fn, "re"); - free(fn); if (!f) return -errno; if (!fgets(line, sizeof(line), f)) { k = feof(f) ? -EIO : -errno; - fclose(f); return k; } - fclose(f); - p = strrchr(line, ')'); if (!p) return -EIO; @@ -2768,10 +2773,11 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct return ret; } -static int is_temporary_fs(struct statfs *s) { +_pure_ static int is_temporary_fs(struct statfs *s) { assert(s); - return s->f_type == TMPFS_MAGIC || - (long)s->f_type == (long)RAMFS_MAGIC; + return + F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) || + F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); } int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { @@ -3272,7 +3278,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne r = new0(char, new_length+1); if (!r) - return r; + return NULL; x = (new_length * percent) / 100; @@ -3462,7 +3468,9 @@ DIR *xopendirat(int fd, const char *name, int flags) { int nfd; DIR *d; - nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags); + assert(!(flags & O_CREAT)); + + nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); if (nfd < 0) return NULL; @@ -3488,26 +3496,23 @@ int signal_from_string_try_harder(const char *s) { } static char *tag_to_udev_node(const char *tagvalue, const char *by) { - char *dn, *t, *u; - int r; - - /* FIXME: to follow udev's logic 100% we need to leave valid - * UTF8 chars unescaped */ + _cleanup_free_ char *t = NULL, *u = NULL; + char *dn; + size_t enc_len; u = unquote(tagvalue, "\"\'"); if (u == NULL) return NULL; - t = xescape(u, "/ "); - free(u); - + enc_len = strlen(u) * 4; + t = new(char, enc_len); if (t == NULL) return NULL; - r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t); - free(t); + if (udev_encode_string(u, t, enc_len) < 0) + return NULL; - if (r < 0) + if (asprintf(&dn, "/dev/disk/by-%s/%s", by, t) < 0) return NULL; return dn; @@ -3835,21 +3840,31 @@ bool hostname_is_valid(const char *s) { return true; } -char* hostname_cleanup(char *s) { +char* hostname_cleanup(char *s, bool lowercase) { char *p, *d; + bool dot; - for (p = s, d = s; *p; p++) - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-' || - *p == '_' || - *p == '.') - *(d++) = *p; + for (p = s, d = s, dot = true; *p; p++) { + if (*p == '.') { + if (dot) + continue; - *d = 0; + *(d++) = '.'; + dot = true; + } else if (hostname_valid_char(*p)) { + *(d++) = lowercase ? tolower(*p) : *p; + dot = false; + } + + } + + if (dot && d > s) + d[-1] = 0; + else + *d = 0; strshorten(s, HOST_NAME_MAX); + return s; } @@ -4329,8 +4344,8 @@ int in_group(const char *name) { } int glob_exists(const char *path) { - glob_t _cleanup_globfree_ g = {}; - int r, k; + _cleanup_globfree_ glob_t g = {}; + int k; assert(path); @@ -4338,15 +4353,37 @@ int glob_exists(const char *path) { k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); if (k == GLOB_NOMATCH) - r = 0; + return 0; else if (k == GLOB_NOSPACE) - r = -ENOMEM; + return -ENOMEM; else if (k == 0) - r = !strv_isempty(g.gl_pathv); + return !strv_isempty(g.gl_pathv); else - r = errno ? -errno : -EIO; + return errno ? -errno : -EIO; +} - return r; +int glob_extend(char ***strv, const char *path) { + _cleanup_globfree_ glob_t g = {}; + int k; + char **p; + + errno = 0; + k = glob(optarg, GLOB_NOSORT|GLOB_BRACE, NULL, &g); + + if (k == GLOB_NOMATCH) + return -ENOENT; + else if (k == GLOB_NOSPACE) + return -ENOMEM; + else if (k != 0 || strv_isempty(g.gl_pathv)) + return errno ? -errno : -EIO; + + STRV_FOREACH(p, g.gl_pathv) { + k = strv_extend(strv, *p); + if (k < 0) + break; + } + + return k; } int dirent_ensure_type(DIR *d, struct dirent *de) { @@ -5026,19 +5063,21 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { } int getenv_for_pid(pid_t pid, const char *field, char **_value) { - char path[sizeof("/proc/")-1 + DECIMAL_STR_MAX(pid_t) + sizeof("/environ")], *value = NULL; + _cleanup_fclose_ FILE *f = NULL; + char *value = NULL; int r; - FILE *f; bool done = false; size_t l; + const char *path; + assert(pid >= 0); assert(field); assert(_value); if (pid == 0) - pid = getpid(); - - snprintf(path, sizeof(path), "/proc/%lu/environ", (unsigned long) pid); + path = "/proc/self/environ"; + else + path = procfs_file_alloca(pid, "environ"); f = fopen(path, "re"); if (!f) @@ -5067,10 +5106,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { if (memcmp(line, field, l) == 0 && line[l] == '=') { value = strdup(line + l + 1); - if (!value) { - r = -ENOMEM; - break; - } + if (!value) + return -ENOMEM; r = 1; break; @@ -5078,67 +5115,10 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { } while (!done); - fclose(f); - - if (r >= 0) - *_value = value; - + *_value = value; return r; } -int can_sleep(const char *type) { - char *w, *state; - size_t l, k; - int r; - _cleanup_free_ char *p = NULL; - - 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 false; - - k = strlen(type); - FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) - if (l == k && memcmp(w, type, l) == 0) - return true; - - return false; -} - -int can_sleep_disk(const char *type) { - char *w, *state; - size_t l, k; - int r; - _cleanup_free_ char *p = NULL; - - 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 false; - - k = strlen(type); - FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) { - if (l == k && memcmp(w, type, l) == 0) - return true; - - if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']') - return true; - } - - return false; -} - bool is_valid_documentation_url(const char *url) { assert(url); @@ -5259,7 +5239,7 @@ int get_home_dir(char **_h) { errno = 0; p = getpwuid(u); if (!p) - return errno ? -errno : -ESRCH; + return errno > 0 ? -errno : -ESRCH; if (!path_is_absolute(p->pw_dir)) return -EINVAL; @@ -5272,26 +5252,6 @@ int get_home_dir(char **_h) { return 0; } -void fclosep(FILE **f) { - if (*f) - fclose(*f); -} - -void pclosep(FILE **f) { - if (*f) - pclose(*f); -} - -void closep(int *fd) { - if (*fd >= 0) - close_nointr_nofail(*fd); -} - -void closedirp(DIR **d) { - if (*d) - closedir(*d); -} - bool filename_is_safe(const char *p) { if (isempty(p)) @@ -5328,13 +5288,17 @@ bool string_is_safe(const char *p) { return true; } +/** + * Check if a string contains control characters. + * Spaces and tabs are not considered control characters. + */ bool string_has_cc(const char *p) { const char *t; assert(p); for (t = p; *t; t++) - if (*t > 0 && *t < ' ') + if (*t > 0 && *t < ' ' && *t != '\t') return true; return false; @@ -5402,9 +5366,29 @@ bool is_locale_utf8(void) { goto out; } - cached_answer = streq(set, "UTF-8"); + if(streq(set, "UTF-8")) { + cached_answer = true; + goto out; + } + + /* For LC_CTYPE=="C" return true, because CTYPE is effectly + * unset and everything can do to UTF-8 nowadays. */ + set = setlocale(LC_CTYPE, NULL); + if (!set) { + cached_answer = true; + goto out; + } + + /* Check result, but ignore the result if C was set + * explicitly. */ + cached_answer = + streq(set, "C") && + !getenv("LC_ALL") && + !getenv("LC_CTYPE") && + !getenv("LANG"); + out: - return (bool)cached_answer; + return (bool) cached_answer; } const char *draw_special_char(DrawSpecialChar ch) { @@ -5719,7 +5703,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *sear int create_tmp_dir(char template[], char** dir_name) { int r = 0; - char *d, *dt; + char *d = NULL, *dt; assert(dir_name); @@ -5854,3 +5838,89 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) { *allocated = a; return q; } + +bool id128_is_valid(const char *s) { + size_t i, l; + + l = strlen(s); + if (l == 32) { + + /* Simple formatted 128bit hex string */ + + for (i = 0; i < l; i++) { + char c = s[i]; + + if (!(c >= '0' && c <= '9') && + !(c >= 'a' && c <= 'z') && + !(c >= 'A' && c <= 'Z')) + return false; + } + + } else if (l == 36) { + + /* Formatted UUID */ + + for (i = 0; i < l; i++) { + char c = s[i]; + + if ((i == 8 || i == 13 || i == 18 || i == 23)) { + if (c != '-') + return false; + } else { + if (!(c >= '0' && c <= '9') && + !(c >= 'a' && c <= 'z') && + !(c >= 'A' && c <= 'Z')) + return false; + } + } + + } else + return false; + + return true; +} + +void parse_user_at_host(char *arg, char **user, char **host) { + assert(arg); + assert(user); + assert(host); + + *host = strchr(arg, '@'); + if (*host == NULL) + *host = arg; + else { + *host[0]++ = '\0'; + *user = arg; + } +} + +int split_pair(const char *s, const char *sep, char **l, char **r) { + char *x, *a, *b; + + assert(s); + assert(sep); + assert(l); + assert(r); + + if (isempty(sep)) + return -EINVAL; + + x = strstr(s, sep); + if (!x) + return -EINVAL; + + a = strndup(s, x - s); + if (!a) + return -ENOMEM; + + b = strdup(x + strlen(sep)); + if (!b) { + free(a); + return -ENOMEM; + } + + *l = a; + *r = b; + + return 0; +}