X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=67ec5ae747777d7121bb7a9626ea85fd471bf8d9;hb=9473414219330b9febc1d0712bbf49ad74cf962f;hp=a055ce197ddf44881182d4e3b4d9da65fa4d6ab5;hpb=bbc98d32560cc456531bf254f7b69054921082bd;p=elogind.git diff --git a/src/shared/util.c b/src/shared/util.c index a055ce197..67ec5ae74 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -50,12 +50,10 @@ #include #include #include -#include #include #include #include #include -#include #include "macro.h" #include "util.h" @@ -64,6 +62,7 @@ #include "log.h" #include "strv.h" #include "label.h" +#include "path-util.h" #include "exit-status.h" #include "hashmap.h" @@ -486,21 +485,6 @@ char *split_quoted(const char *c, size_t *l, char **state) { return (char*) current; } -char **split_path_and_make_absolute(const char *p) { - char **l; - assert(p); - - if (!(l = strv_split(p, ":"))) - return NULL; - - if (!strv_path_make_absolute_cwd(l)) { - strv_free(l); - return NULL; - } - - return l; -} - int get_parent_of_pid(pid_t pid, pid_t *_ppid) { int r; FILE *f; @@ -612,7 +596,8 @@ int write_one_line_file(const char *fn, const char *line) { assert(fn); assert(line); - if (!(f = fopen(fn, "we"))) + f = fopen(fn, "we"); + if (!f) return -errno; errno = 0; @@ -1313,187 +1298,6 @@ int readlink_and_canonicalize(const char *p, char **r) { return 0; } -int parent_of_path(const char *path, char **_r) { - const char *e, *a = NULL, *b = NULL, *p; - char *r; - bool slash = false; - - assert(path); - assert(_r); - - if (!*path) - return -EINVAL; - - for (e = path; *e; e++) { - - if (!slash && *e == '/') { - a = b; - b = e; - slash = true; - } else if (slash && *e != '/') - slash = false; - } - - if (*(e-1) == '/') - p = a; - else - p = b; - - if (!p) - return -EINVAL; - - if (p == path) - r = strdup("/"); - else - r = strndup(path, p-path); - - if (!r) - return -ENOMEM; - - *_r = r; - return 0; -} - - -char *file_name_from_path(const char *p) { - char *r; - - assert(p); - - if ((r = strrchr(p, '/'))) - return r + 1; - - return (char*) p; -} - -bool path_is_absolute(const char *p) { - assert(p); - - return p[0] == '/'; -} - -bool is_path(const char *p) { - - return !!strchr(p, '/'); -} - -char *path_make_absolute(const char *p, const char *prefix) { - assert(p); - - /* Makes every item in the list an absolute path by prepending - * the prefix, if specified and necessary */ - - if (path_is_absolute(p) || !prefix) - return strdup(p); - - return join(prefix, "/", p, NULL); -} - -char *path_make_absolute_cwd(const char *p) { - char *cwd, *r; - - assert(p); - - /* Similar to path_make_absolute(), but prefixes with the - * current working directory. */ - - if (path_is_absolute(p)) - return strdup(p); - - if (!(cwd = get_current_dir_name())) - return NULL; - - r = path_make_absolute(p, cwd); - free(cwd); - - return r; -} - -char **strv_path_make_absolute_cwd(char **l) { - char **s; - - /* Goes through every item in the string list and makes it - * absolute. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t; - - if (!(t = path_make_absolute_cwd(*s))) - return NULL; - - free(*s); - *s = t; - } - - return l; -} - -char **strv_path_canonicalize(char **l) { - char **s; - unsigned k = 0; - bool enomem = false; - - if (strv_isempty(l)) - return l; - - /* Goes through every item in the string list and canonicalize - * the path. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t, *u; - - t = path_make_absolute_cwd(*s); - free(*s); - - if (!t) { - enomem = true; - continue; - } - - errno = 0; - u = canonicalize_file_name(t); - free(t); - - if (!u) { - if (errno == ENOMEM || !errno) - enomem = true; - - continue; - } - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **strv_path_remove_empty(char **l) { - char **f, **t; - - if (!l) - return NULL; - - for (f = t = l; *f; f++) { - - if (dir_is_empty(*f) > 0) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - int reset_all_signal_handlers(void) { int sig; @@ -1972,107 +1776,6 @@ char *bus_path_unescape(const char *f) { return r; } -char *path_kill_slashes(char *path) { - char *f, *t; - bool slash = false; - - /* Removes redundant inner and trailing slashes. Modifies the - * passed string in-place. - * - * ///foo///bar/ becomes /foo/bar - */ - - for (f = path, t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - slash = false; - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we are talking of the root directory, a - trailing slash is good */ - - if (t == path && slash) - *(t++) = '/'; - - *t = 0; - return path; -} - -bool path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - if ((path[0] == '/') != (prefix[0] == '/')) - return false; - - for (;;) { - size_t a, b; - - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return true; - - if (*path == 0) - return false; - - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); - - if (a != b) - return false; - - if (memcmp(path, prefix, a) != 0) - return false; - - path += a; - prefix += b; - } -} - -bool path_equal(const char *a, const char *b) { - assert(a); - assert(b); - - if ((a[0] == '/') != (b[0] == '/')) - return false; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return true; - - if (*a == 0 || *b == 0) - return false; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - if (j != k) - return false; - - if (memcmp(a, b, j) != 0) - return false; - - a += j; - b += k; - } -} - char *ascii_strlower(char *t) { char *p; @@ -2986,36 +2689,6 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { return n; } -int path_is_mount_point(const char *t, bool allow_symlink) { - struct stat a, b; - char *parent; - int r; - - if (allow_symlink) - r = stat(t, &a); - else - r = lstat(t, &a); - - if (r < 0) { - if (errno == ENOENT) - return 0; - - return -errno; - } - - r = parent_of_path(t, &parent); - if (r < 0) - return r; - - r = lstat(parent, &b); - free(parent); - - if (r < 0) - return -errno; - - return a.st_dev != b.st_dev; -} - int parse_usec(const char *t, usec_t *usec) { static const struct { const char *suffix; @@ -3033,7 +2706,7 @@ int parse_usec(const char *t, usec_t *usec) { { "m", USEC_PER_MINUTE }, { "usec", 1ULL }, { "us", 1ULL }, - { "", USEC_PER_SEC }, + { "", USEC_PER_SEC }, /* default is sec */ }; const char *p; @@ -3079,6 +2752,71 @@ int parse_usec(const char *t, usec_t *usec) { return 0; } +int parse_nsec(const char *t, nsec_t *nsec) { + static const struct { + const char *suffix; + nsec_t nsec; + } table[] = { + { "sec", NSEC_PER_SEC }, + { "s", NSEC_PER_SEC }, + { "min", NSEC_PER_MINUTE }, + { "hr", NSEC_PER_HOUR }, + { "h", NSEC_PER_HOUR }, + { "d", NSEC_PER_DAY }, + { "w", NSEC_PER_WEEK }, + { "msec", NSEC_PER_MSEC }, + { "ms", NSEC_PER_MSEC }, + { "m", NSEC_PER_MINUTE }, + { "usec", NSEC_PER_USEC }, + { "us", NSEC_PER_USEC }, + { "nsec", 1ULL }, + { "ns", 1ULL }, + { "", 1ULL }, /* default is nsec */ + }; + + const char *p; + nsec_t r = 0; + + assert(t); + assert(nsec); + + p = t; + do { + long long l; + char *e; + unsigned i; + + errno = 0; + l = strtoll(p, &e, 10); + + if (errno != 0) + return -errno; + + if (l < 0) + return -ERANGE; + + if (e == p) + return -EINVAL; + + e += strspn(e, WHITESPACE); + + for (i = 0; i < ELEMENTSOF(table); i++) + if (startswith(e, table[i].suffix)) { + r += (nsec_t) l * table[i].nsec; + p = e + strlen(table[i].suffix); + break; + } + + if (i >= ELEMENTSOF(table)) + return -EINVAL; + + } while (*p != 0); + + *nsec = r; + + return 0; +} + int parse_bytes(const char *t, off_t *bytes) { static const struct { const char *suffix; @@ -3275,12 +3013,20 @@ char* gethostname_malloc(void) { assert_se(uname(&u) >= 0); - if (u.nodename[0]) + if (!isempty(u.nodename) && !streq(u.nodename, "(none)")) return strdup(u.nodename); return strdup(u.sysname); } +bool hostname_is_set(void) { + struct utsname u; + + assert_se(uname(&u) >= 0); + + return !isempty(u.nodename) && !streq(u.nodename, "(none)"); +} + char* getlogname_malloc(void) { uid_t uid; long bufsize; @@ -3457,16 +3203,17 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { +int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { DIR *d; int ret = 0; assert(fd >= 0); /* This returns the first error we run into, but nevertheless - * tries to go on */ + * tries to go on. This closes the passed fd. */ - if (!(d = fdopendir(fd))) { + d = fdopendir(fd); + if (!d) { close_nointr_nofail(fd); return errno == ENOENT ? 0 : -errno; @@ -3474,12 +3221,13 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { for (;;) { struct dirent buf, *de; - bool is_dir, keep_around = false; + bool is_dir, keep_around; + struct stat st; int r; - if ((r = readdir_r(d, &buf, &de)) != 0) { - if (ret == 0) - ret = -r; + r = readdir_r(d, &buf, &de); + if (r != 0 && ret == 0) { + ret = -r; break; } @@ -3489,54 +3237,43 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { if (streq(de->d_name, ".") || streq(de->d_name, "..")) continue; - if (de->d_type == DT_UNKNOWN) { - struct stat st; - + if (de->d_type == DT_UNKNOWN || + honour_sticky || + (de->d_type == DT_DIR && root_dev)) { if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { if (ret == 0 && errno != ENOENT) ret = -errno; continue; } - if (honour_sticky) - keep_around = - (st.st_uid == 0 || st.st_uid == getuid()) && - (st.st_mode & S_ISVTX); - is_dir = S_ISDIR(st.st_mode); - + keep_around = + honour_sticky && + (st.st_uid == 0 || st.st_uid == getuid()) && + (st.st_mode & S_ISVTX); } else { - if (honour_sticky) { - struct stat st; - - if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - continue; - } - - keep_around = - (st.st_uid == 0 || st.st_uid == getuid()) && - (st.st_mode & S_ISVTX); - } - is_dir = de->d_type == DT_DIR; + keep_around = false; } if (is_dir) { int subdir_fd; - subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + /* if root_dev is set, remove subdirectories only, if device is same as dir */ + if (root_dev && st.st_dev != root_dev->st_dev) + continue; + + subdir_fd = openat(fd, de->d_name, + O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (subdir_fd < 0) { if (ret == 0 && errno != ENOENT) ret = -errno; continue; } - if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) { - if (ret == 0) - ret = r; - } + r = rm_rf_children(subdir_fd, only_dirs, honour_sticky, root_dev); + if (r < 0 && ret == 0) + ret = r; if (!keep_around) if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { @@ -3564,19 +3301,20 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky assert(path); - if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) { if (errno != ENOTDIR) return -errno; if (delete_root && !only_dirs) - if (unlink(path) < 0) + if (unlink(path) < 0 && errno != ENOENT) return -errno; return 0; } - r = rm_rf_children(fd, only_dirs, honour_sticky); + r = rm_rf_children(fd, only_dirs, honour_sticky, NULL); if (delete_root) { @@ -3655,15 +3393,15 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) { } void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) { - char *s = NULL, *spaces = NULL, *e; - int fd = -1, c; - size_t emax, sl, left; + char *s = NULL; + static const char status_indent[] = " "; /* "[" STATUS "] " */ + int fd = -1; struct iovec iovec[5]; int n = 0; assert(format); - /* This independent of logging, as status messages are + /* This is independent of logging, as status messages are * optional and go exclusively to the console. */ if (vasprintf(&s, format, ap) < 0) @@ -3674,15 +3412,19 @@ void status_vprintf(const char *status, bool ellipse, const char *format, va_lis goto finish; if (ellipse) { + char *e; + size_t emax, sl; + int c; + c = fd_columns(fd); if (c <= 0) c = 80; - if (status) { - sl = 2 + 6 + 1; /* " [" status "]" */ - emax = (size_t) c > sl ? c - sl - 1 : 0; - } else - emax = c - 1; + sl = status ? strlen(status_indent) : 0; + + emax = c - sl - 1; + if (emax < 3) + emax = 3; e = ellipsize(s, emax, 75); if (e) { @@ -3692,34 +3434,23 @@ void status_vprintf(const char *status, bool ellipse, const char *format, va_lis } zero(iovec); - IOVEC_SET_STRING(iovec[n++], s); - if (ellipse) { - sl = strlen(s); - left = emax > sl ? emax - sl : 0; - if (left > 0) { - spaces = malloc(left); - if (spaces) { - memset(spaces, ' ', left); - iovec[n].iov_base = spaces; - iovec[n].iov_len = left; - n++; - } - } + if (status) { + if (!isempty(status)) { + IOVEC_SET_STRING(iovec[n++], "["); + IOVEC_SET_STRING(iovec[n++], status); + IOVEC_SET_STRING(iovec[n++], "] "); + } else + IOVEC_SET_STRING(iovec[n++], status_indent); } - if (status) { - IOVEC_SET_STRING(iovec[n++], " ["); - IOVEC_SET_STRING(iovec[n++], status); - IOVEC_SET_STRING(iovec[n++], "]\n"); - } else - IOVEC_SET_STRING(iovec[n++], "\n"); + IOVEC_SET_STRING(iovec[n++], s); + IOVEC_SET_STRING(iovec[n++], "\n"); writev(fd, iovec, n); finish: free(s); - free(spaces); if (fd >= 0) close_nointr_nofail(fd); @@ -4433,8 +4164,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) { _argv[1] = NULL; argv = _argv; } else - if (!argv[0]) - argv[0] = path; + argv[0] = path; execv(path, argv); @@ -4746,7 +4476,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { if (!t) return -ENOMEM; - fn = file_name_from_path(path); + fn = path_get_file_name(path); k = fn-path; memcpy(t, path, k); t[k] = '.'; @@ -4928,8 +4658,8 @@ int symlink_or_copy(const char *from, const char *to) { assert(from); assert(to); - if (parent_of_path(from, &pf) < 0 || - parent_of_path(to, &pt) < 0) { + if (path_get_parent(from, &pf) < 0 || + path_get_parent(to, &pt) < 0) { r = -ENOMEM; goto finish; } @@ -4976,7 +4706,7 @@ int symlink_or_copy_atomic(const char *from, const char *to) { if (!t) return -ENOMEM; - fn = file_name_from_path(to); + fn = path_get_file_name(to); k = fn-to; memcpy(t, to, k); t[k] = '.'; @@ -5218,7 +4948,7 @@ int in_search_path(const char *path, char **search) { char **i, *parent; int r; - r = parent_of_path(path, &parent); + r = path_get_parent(path, &parent); if (r < 0) return r; @@ -5857,17 +5587,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { return 0; } -int path_is_read_only_fs(const char *path) { - struct statvfs st; - - assert(path); - - if (statvfs(path, &st) < 0) - return -errno; - - return !!(st.f_flag & ST_RDONLY); -} - int getenv_for_pid(pid_t pid, const char *field, char **_value) { char path[sizeof("/proc/")-1+10+sizeof("/environ")], *value = NULL; int r; @@ -5929,3 +5648,80 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { return r; } + +int can_sleep(const char *type) { + char *p, *w, *state; + size_t l, k; + bool found = false; + int r; + + assert(type); + + r = read_one_line_file("/sys/power/state", &p); + if (r < 0) + return r == -ENOENT ? 0 : r; + + k = strlen(type); + + FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) { + if (l == k && strncmp(w, type, l) == 0) { + found = true; + break; + } + } + + free(p); + return found; +} + +bool is_valid_documentation_url(const char *url) { + assert(url); + + if (startswith(url, "http://") && url[7]) + return true; + + if (startswith(url, "https://") && url[8]) + return true; + + if (startswith(url, "file:") && url[5]) + return true; + + if (startswith(url, "info:") && url[5]) + return true; + + if (startswith(url, "man:") && url[4]) + return true; + + return false; +} + +bool in_initrd(void) { + static int saved = -1; + + if (saved < 0) + saved = access("/etc/initrd-release", F_OK) >= 0; + + return saved; +} + +void warn_melody(void) { + int fd; + + fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return; + + /* Yeah, this is synchronous. Kinda sucks. Bute well... */ + + ioctl(fd, KIOCSOUND, (int)(1193180/440)); + usleep(125*USEC_PER_MSEC); + + ioctl(fd, KIOCSOUND, (int)(1193180/220)); + usleep(125*USEC_PER_MSEC); + + ioctl(fd, KIOCSOUND, (int)(1193180/220)); + usleep(125*USEC_PER_MSEC); + + ioctl(fd, KIOCSOUND, 0); + close_nointr_nofail(fd); +}