X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Futil.c;h=de36d1595e8c5bc4345895bcbaad1ec538f052f3;hb=4f3656e1cec7fe3d7d3537e23a406cb88d734502;hp=4e2a4fab832f8c98c578097d0e4ba4857db2d67b;hpb=b636465bc0190448f960791cd059c4dd1fa37717;p=elogind.git diff --git a/src/util.c b/src/util.c index 4e2a4fab8..de36d1595 100644 --- a/src/util.c +++ b/src/util.c @@ -55,6 +55,7 @@ #include #include #include +#include #include "macro.h" #include "util.h" @@ -73,7 +74,7 @@ size_t page_size(void) { static __thread size_t pgsz = 0; long r; - if (pgsz) + if (_likely_(pgsz > 0)) return pgsz; assert_se((r = sysconf(_SC_PAGESIZE)) > 0); @@ -516,7 +517,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -561,7 +562,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) { return -errno; if (!(fgets(line, sizeof(line), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; fclose(f); return r; } @@ -708,7 +709,7 @@ int read_one_line_file(const char *fn, char **line) { return -errno; if (!(fgets(t, sizeof(t), f))) { - r = -errno; + r = feof(f) ? -EIO : -errno; goto finish; } @@ -782,13 +783,7 @@ int read_full_file(const char *fn, char **contents, size_t *size) { } } - if (buf) - buf[l] = 0; - else if (!(buf = calloc(1, 1))) { - r = -errno; - goto finish; - } - + buf[l] = 0; *contents = buf; buf = NULL; @@ -999,46 +994,51 @@ char *truncate_nl(char *s) { return s; } -int get_process_name(pid_t pid, char **name) { - char *p; +int get_process_comm(pid_t pid, char **name) { int r; - assert(pid >= 1); assert(name); - if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) - return -ENOMEM; - - r = read_one_line_file(p, name); - free(p); + if (pid == 0) + r = read_one_line_file("/proc/self/comm", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) + return -ENOMEM; - if (r < 0) - return r; + r = read_one_line_file(p, name); + free(p); + } - return 0; + return r; } -int get_process_cmdline(pid_t pid, size_t max_length, char **line) { - char *p, *r, *k; +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + char *r, *k; int c; bool space = false; size_t left; FILE *f; - assert(pid >= 1); assert(max_length > 0); assert(line); - if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + f = fopen("/proc/self/cmdline", "re"); + else { + char *p; + if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) + return -ENOMEM; - f = fopen(p, "re"); - free(p); + f = fopen(p, "re"); + free(p); + } if (!f) return -errno; - if (!(r = new(char, max_length))) { + r = new(char, max_length); + if (!r) { fclose(f); return -ENOMEM; } @@ -1082,13 +1082,17 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { free(r); - if ((h = get_process_name(pid, &t)) < 0) + if (!comm_fallback) + return -ENOENT; + + h = get_process_comm(pid, &t); + if (h < 0) return h; - h = asprintf(&r, "[%s]", t); + r = join("[", t, "]", NULL); free(t); - if (h < 0) + if (!r) return -ENOMEM; } @@ -1096,6 +1100,25 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { return 0; } +int get_process_exe(pid_t pid, char **name) { + int r; + + assert(name); + + if (pid == 0) + r = readlink_malloc("/proc/self/exe", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0) + return -ENOMEM; + + r = readlink_malloc(p, name); + free(p); + } + + return r; +} + char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -1439,6 +1462,19 @@ char *delete_chars(char *s, const char *bad) { return s; } +bool in_charset(const char *s, const char* charset) { + const char *i; + + assert(s); + assert(charset); + + for (i = s; *i; i++) + if (!strchr(charset, *i)) + return false; + + return true; +} + char *file_in_same_dir(const char *path, const char *filename) { char *e, *r; size_t k; @@ -2300,8 +2336,10 @@ int chvt(int vt) { 0 }; - if (ioctl(fd, TIOCLINUX, tiocl) < 0) - return -errno; + if (ioctl(fd, TIOCLINUX, tiocl) < 0) { + r = -errno; + goto fail; + } vt = tiocl[0] <= 0 ? 1 : tiocl[0]; } @@ -2309,7 +2347,8 @@ int chvt(int vt) { if (ioctl(fd, VT_ACTIVATE, vt) < 0) r = -errno; - close_nointr_nofail(r); +fail: + close_nointr_nofail(fd); return r; } @@ -2635,7 +2674,7 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst ssize_t l; struct inotify_event *e; - if ((l = read(notify, &inotify_buffer, sizeof(inotify_buffer))) < 0) { + if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) { if (errno == EINTR) continue; @@ -2846,7 +2885,8 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { while (nbytes > 0) { ssize_t k; - if ((k = write(fd, p, nbytes)) <= 0) { + k = write(fd, p, nbytes); + if (k <= 0) { if (k < 0 && errno == EINTR) continue; @@ -2882,19 +2922,25 @@ 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) { +int path_is_mount_point(const char *t, bool allow_symlink) { struct stat a, b; char *parent; int r; - if (lstat(t, &a) < 0) { + if (allow_symlink) + r = stat(t, &a); + else + r = lstat(t, &a); + + if (r < 0) { if (errno == ENOENT) return 0; return -errno; } - if ((r = parent_of_path(t, &parent)) < 0) + r = parent_of_path(t, &parent); + if (r < 0) return r; r = lstat(parent, &b); @@ -2969,6 +3015,62 @@ int parse_usec(const char *t, usec_t *usec) { return 0; } +int parse_bytes(const char *t, off_t *bytes) { + static const struct { + const char *suffix; + off_t factor; + } table[] = { + { "B", 1 }, + { "K", 1024ULL }, + { "M", 1024ULL*1024ULL }, + { "G", 1024ULL*1024ULL*1024ULL }, + { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, + { "", 1 }, + }; + + const char *p; + off_t r = 0; + + assert(t); + assert(bytes); + + 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 += (off_t) l * table[i].factor; + p = e + strlen(table[i].suffix); + break; + } + + if (i >= ELEMENTSOF(table)) + return -EINVAL; + + } while (*p != 0); + + *bytes = r; + + return 0; +} + int make_stdio(int fd) { int r, s, t; @@ -3194,7 +3296,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) { return -errno; if (!fgets(line, sizeof(line), f)) { - k = -errno; + k = feof(f) ? -EIO : -errno; fclose(f); return k; } @@ -3285,7 +3387,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -static int rm_rf_children(int fd, bool only_dirs) { +static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { DIR *d; int ret = 0; @@ -3302,7 +3404,7 @@ static int rm_rf_children(int fd, bool only_dirs) { for (;;) { struct dirent buf, *de; - bool is_dir; + bool is_dir, keep_around = false; int r; if ((r = readdir_r(d, &buf, &de)) != 0) { @@ -3326,9 +3428,26 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } + if (honour_sticky) + keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX); + is_dir = S_ISDIR(st.st_mode); - } else + + } 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_mode & S_ISVTX); + } + is_dir = de->d_type == DT_DIR; + } if (is_dir) { int subdir_fd; @@ -3339,16 +3458,18 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } - if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) { + if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) { if (ret == 0) ret = r; } - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - } else if (!only_dirs) { + if (!keep_around) + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } + + } else if (!only_dirs && !keep_around) { if (unlinkat(fd, de->d_name, 0) < 0) { if (ret == 0 && errno != ENOENT) @@ -3362,7 +3483,7 @@ static int rm_rf_children(int fd, bool only_dirs) { return ret; } -int rm_rf(const char *path, bool only_dirs, bool delete_root) { +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { int fd; int r; @@ -3380,13 +3501,18 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root) { return 0; } - r = rm_rf_children(fd, only_dirs); + r = rm_rf_children(fd, only_dirs, honour_sticky); + + if (delete_root) { + + if (honour_sticky && file_is_sticky(path) > 0) + return r; - if (delete_root) - if (rmdir(path) < 0) { + if (rmdir(path) < 0 && errno != ENOENT) { if (r == 0) r = -errno; } + } return r; } @@ -3407,6 +3533,22 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return 0; } +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { + assert(fd >= 0); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (fchmod(fd, mode) < 0) + return -errno; + + if (fchown(fd, uid, gid) < 0) + return -errno; + + return 0; +} + cpu_set_t* cpu_set_malloc(unsigned *ncpus) { cpu_set_t *r; unsigned n = 1024; @@ -3764,11 +3906,11 @@ char **replace_env_argv(char **argv, char **env) { return r; } -int columns(void) { +unsigned columns(void) { static __thread int parsed_columns = 0; const char *e; - if (parsed_columns > 0) + if (_likely_(parsed_columns > 0)) return parsed_columns; if ((e = getenv("COLUMNS"))) @@ -3807,38 +3949,41 @@ int running_in_chroot(void) { a.st_ino != b.st_ino; } -char *ellipsize(const char *s, unsigned length, unsigned percent) { - size_t l, x; +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { + size_t x; char *r; assert(s); assert(percent <= 100); - assert(length >= 3); + assert(new_length >= 3); - l = strlen(s); + if (old_length <= 3 || old_length <= new_length) + return strndup(s, old_length); - if (l <= 3 || l <= length) - return strdup(s); - - if (!(r = new0(char, length+1))) + r = new0(char, new_length+1); + if (!r) return r; - x = (length * percent) / 100; + x = (new_length * percent) / 100; - if (x > length - 3) - x = length - 3; + if (x > new_length - 3) + x = new_length - 3; memcpy(r, s, x); r[x] = '.'; r[x+1] = '.'; r[x+2] = '.'; memcpy(r + x + 3, - s + l - (length - x - 3), - length - x - 3); + s + old_length - (new_length - x - 3), + new_length - x - 3); return r; } +char *ellipsize(const char *s, size_t length, unsigned percent) { + return ellipsize_mem(s, strlen(s), length, percent); +} + int touch(const char *path) { int fd; @@ -4174,225 +4319,7 @@ const char *default_term_for_tty(const char *tty) { return term; } -/* Returns a short identifier for the various VM implementations */ -int detect_vm(const char **id) { - -#if defined(__i386__) || defined(__x86_64__) - - /* Both CPUID and DMI are x86 specific interfaces... */ - - static const char *const dmi_vendors[] = { - "/sys/class/dmi/id/sys_vendor", - "/sys/class/dmi/id/board_vendor", - "/sys/class/dmi/id/bios_vendor" - }; - - static const char dmi_vendor_table[] = - "QEMU\0" "qemu\0" - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - "VMware\0" "vmware\0" - "VMW\0" "vmware\0" - "Microsoft Corporation\0" "microsoft\0" - "innotek GmbH\0" "oracle\0" - "Xen\0" "xen\0" - "Bochs\0" "bochs\0"; - - static const char cpuid_vendor_table[] = - "XenVMMXenVMM\0" "xen\0" - "KVMKVMKVM\0" "kvm\0" - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - "VMwareVMware\0" "vmware\0" - /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ - "Microsoft Hv\0" "microsoft\0"; - - uint32_t eax, ecx; - union { - uint32_t sig32[3]; - char text[13]; - } sig; - unsigned i; - const char *j, *k; - bool hypervisor; - - /* http://lwn.net/Articles/301888/ */ - zero(sig); - -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#define REG_b "rbx" -#endif - - /* First detect whether there is a hypervisor */ - eax = 1; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); - - hypervisor = !!(ecx & 0x80000000U); - - if (hypervisor) { - - /* There is a hypervisor, see what it is */ - eax = 0x40000000U; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " mov %%ebx, %1 \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) - : "0" (eax) - ); - - NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) - if (streq(sig.text, j)) { - - if (id) - *id = k; - - return 1; - } - } - - for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { - char *s; - int r; - const char *found = NULL; - - if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { - if (r != -ENOENT) - return r; - - continue; - } - - NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) - if (startswith(s, j)) - found = k; - free(s); - - if (found) { - if (id) - *id = found; - - return 1; - } - } - - if (hypervisor) { - if (id) - *id = "other"; - - return 1; - } - -#endif - return 0; -} - -int detect_container(const char **id) { - FILE *f; - - /* Unfortunately many of these operations require root access - * in one way or another */ - - if (geteuid() != 0) - return -EPERM; - - if (running_in_chroot() > 0) { - - if (id) - *id = "chroot"; - - return 1; - } - - /* /proc/vz exists in container and outside of the container, - * /proc/bc only outside of the container. */ - if (access("/proc/vz", F_OK) >= 0 && - access("/proc/bc", F_OK) < 0) { - - if (id) - *id = "openvz"; - - return 1; - } - - if ((f = fopen("/proc/self/cgroup", "re"))) { - - for (;;) { - char line[LINE_MAX], *p; - - if (!fgets(line, sizeof(line), f)) - break; - - if (!(p = strchr(strstrip(line), ':'))) - continue; - - if (strncmp(p, ":ns:", 4)) - continue; - - if (!streq(p, ":ns:/")) { - fclose(f); - - if (id) - *id = "pidns"; - - return 1; - } - } - - fclose(f); - } - - return 0; -} - -/* Returns a short identifier for the various VM/container implementations */ -int detect_virtualization(const char **id) { - static __thread const char *cached_id = NULL; - const char *_id; - int r; - - if (cached_id) { - - if (cached_id == (const char*) -1) - return 0; - - if (id) - *id = cached_id; - - return 1; - } - - if ((r = detect_container(&_id)) != 0) - goto finish; - - r = detect_vm(&_id); - -finish: - if (r > 0) { - cached_id = _id; - - if (id) - *id = _id; - } else if (r == 0) - cached_id = (const char*) -1; - - return r; -} - -bool dirent_is_file(struct dirent *de) { +bool dirent_is_file(const struct dirent *de) { assert(de); if (ignore_file(de->d_name)) @@ -4406,6 +4333,15 @@ bool dirent_is_file(struct dirent *de) { return true; } +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { + assert(de); + + if (!dirent_is_file(de)) + return false; + + return endswith(de->d_name, suffix); +} + void execute_directory(const char *directory, DIR *d, char *argv[]) { DIR *_d = NULL; struct dirent *de; @@ -4578,6 +4514,98 @@ void parse_syslog_priority(char **p, int *priority) { *p += k; } +void skip_syslog_pid(char **buf) { + char *p; + + assert(buf); + assert(*buf); + + p = *buf; + + if (*p != '[') + return; + + p++; + p += strspn(p, "0123456789"); + + if (*p != ']') + return; + + p++; + + *buf = p; +} + +void skip_syslog_date(char **buf) { + enum { + LETTER, + SPACE, + NUMBER, + SPACE_OR_NUMBER, + COLON + } sequence[] = { + LETTER, LETTER, LETTER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + SPACE + }; + + char *p; + unsigned i; + + assert(buf); + assert(*buf); + + p = *buf; + + for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { + + if (!*p) + return; + + switch (sequence[i]) { + + case SPACE: + if (*p != ' ') + return; + break; + + case SPACE_OR_NUMBER: + if (*p == ' ') + break; + + /* fall through */ + + case NUMBER: + if (*p < '0' || *p > '9') + return; + + break; + + case LETTER: + if (!(*p >= 'A' && *p <= 'Z') && + !(*p >= 'a' && *p <= 'z')) + return; + + break; + + case COLON: + if (*p != ':') + return; + break; + + } + } + + *buf = p; +} + int have_effective_cap(int value) { cap_t cap; cap_flag_value_t fv; @@ -4797,21 +4825,6 @@ int vt_disallocate(const char *name) { return 0; } - -static int file_is_conf(const struct dirent *d, const char *suffix) { - assert(d); - - if (ignore_file(d->d_name)) - return 0; - - if (d->d_type != DT_REG && - d->d_type != DT_LNK && - d->d_type != DT_UNKNOWN) - return 0; - - return endswith(d->d_name, suffix); -} - static int files_add(Hashmap *h, const char *path, const char *suffix) { DIR *dir; struct dirent buffer, *de; @@ -4837,7 +4850,7 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) { if (!de) break; - if (!file_is_conf(de, suffix)) + if (!dirent_is_file_with_suffix(de, suffix)) continue; if (asprintf(&p, "%s/%s", path, de->d_name) < 0) { @@ -5188,21 +5201,27 @@ int symlink_or_copy_atomic(const char *from, const char *to) { } int audit_session_from_pid(pid_t pid, uint32_t *id) { - char *p, *s; + char *s; uint32_t u; int r; - assert(pid >= 1); assert(id); if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) return -ENOENT; - if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + r = read_one_line_file("/proc/self/sessionid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } - r = read_one_line_file(p, &s); - free(p); if (r < 0) return r; @@ -5219,6 +5238,51 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { return 0; } +int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { + char *s; + uid_t u; + int r; + + assert(uid); + + /* Only use audit login uid if we are executed with sufficient + * capabilities so that pam_loginuid could do its job. If we + * are lacking the CAP_AUDIT_CONTROL capabality we most likely + * are being run in a container and /proc/self/loginuid is + * useless since it probably contains a uid of the host + * system. */ + + if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) + return -ENOENT; + + if (pid == 0) + r = read_one_line_file("/proc/self/loginuid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } + + if (r < 0) + return r; + + r = parse_uid(s, &u); + free(s); + + if (r < 0) + return r; + + if (u == (uid_t) -1) + return -ENOENT; + + *uid = (uid_t) u; + return 0; +} + bool display_is_local(const char *display) { assert(display); @@ -5430,6 +5494,9 @@ int get_files_in_directory(const char *path, char ***list) { * number */ d = opendir(path); + if (!d) + return -errno; + for (;;) { struct dirent buffer, *de; int k; @@ -5530,6 +5597,8 @@ char *join(const char *x, ...) { p = stpcpy(p, t); } + + va_end(ap); } else r[0] = 0; @@ -5545,6 +5614,79 @@ bool is_main_thread(void) { return cached > 0; } +int block_get_whole_disk(dev_t d, dev_t *ret) { + char *p, *s; + int r; + unsigned n, m; + + assert(ret); + + /* If it has a queue this is good enough for us */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = d; + return 0; + } + + /* If it is a partition find the originating device */ + if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r < 0) + return -ENOENT; + + /* Get parent dev_t */ + if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + + if (r < 0) + return r; + + r = sscanf(s, "%u:%u", &m, &n); + free(s); + + if (r != 2) + return -EINVAL; + + /* Only return this if it is really good enough for us. */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = makedev(m, n); + return 0; + } + + return -ENOENT; +} + +int file_is_sticky(const char *p) { + struct stat st; + + assert(p); + + if (lstat(p, &st) < 0) + return -errno; + + return + st.st_uid == 0 && + (st.st_mode & S_ISVTX); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -5643,7 +5785,7 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP(ip_tos, int); -static const char *const signal_table[] = { +static const char *const __signal_table[] = { [SIGHUP] = "HUP", [SIGINT] = "INT", [SIGQUIT] = "QUIT", @@ -5679,4 +5821,156 @@ static const char *const signal_table[] = { [SIGSYS] = "SYS" }; -DEFINE_STRING_TABLE_LOOKUP(signal, int); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); + +const char *signal_to_string(int signo) { + static __thread char buf[12]; + const char *name; + + name = __signal_to_string(signo); + if (name) + return name; + + if (signo >= SIGRTMIN && signo <= SIGRTMAX) + snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN); + else + snprintf(buf, sizeof(buf) - 1, "%d", signo); + char_array_0(buf); + return buf; +} + +int signal_from_string(const char *s) { + int signo; + int offset = 0; + unsigned u; + + signo =__signal_from_string(s); + if (signo > 0) + return signo; + + if (startswith(s, "RTMIN+")) { + s += 6; + offset = SIGRTMIN; + } + if (safe_atou(s, &u) >= 0) { + signo = (int) u + offset; + if (signo > 0 && signo < _NSIG) + return signo; + } + return -1; +} + +bool kexec_loaded(void) { + bool loaded = false; + char *s; + + if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { + if (s[0] == '1') + loaded = true; + free(s); + } + return loaded; +} + +int strdup_or_null(const char *a, char **b) { + char *c; + + assert(b); + + if (!a) { + *b = NULL; + return 0; + } + + c = strdup(a); + if (!c) + return -ENOMEM; + + *b = c; + return 0; +} + +int prot_from_flags(int flags) { + + switch (flags & O_ACCMODE) { + + case O_RDONLY: + return PROT_READ; + + case O_WRONLY: + return PROT_WRITE; + + case O_RDWR: + return PROT_READ|PROT_WRITE; + + default: + return -EINVAL; + } +} + +unsigned long cap_last_cap(void) { + static __thread unsigned long saved; + static __thread bool valid = false; + unsigned long p; + + if (valid) + return saved; + + p = (unsigned long) CAP_LAST_CAP; + + if (prctl(PR_CAPBSET_READ, p) < 0) { + + /* Hmm, look downwards, until we find one that + * works */ + for (p--; p > 0; p --) + if (prctl(PR_CAPBSET_READ, p) >= 0) + break; + + } else { + + /* Hmm, look upwards, until we find one that doesn't + * work */ + for (;; p++) + if (prctl(PR_CAPBSET_READ, p+1) < 0) + break; + } + + saved = p; + valid = true; + + return p; +} + +char *format_bytes(char *buf, size_t l, off_t t) { + unsigned i; + + static const struct { + const char *suffix; + off_t factor; + } table[] = { + { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, + { "G", 1024ULL*1024ULL*1024ULL }, + { "M", 1024ULL*1024ULL }, + { "K", 1024ULL }, + }; + + for (i = 0; i < ELEMENTSOF(table); i++) { + + if (t >= table[i].factor) { + snprintf(buf, l, + "%llu.%llu%s", + (unsigned long long) (t / table[i].factor), + (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL), + table[i].suffix); + + goto finish; + } + } + + snprintf(buf, l, "%lluB", (unsigned long long) t); + +finish: + buf[l-1] = 0; + return buf; + +}