X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=dd67c2264164b0c04c32a630e11cc872e162245e;hp=8c7cfbd6df7f548dd21e12b72c3471e82598ea3b;hb=3d94f76c99da13e5603831d0b278f8c8c21bcb02;hpb=517d56b1d0f67dcf76710bc1e17b05518b8cabe6 diff --git a/src/shared/util.c b/src/shared/util.c index 8c7cfbd6d..dd67c2264 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -183,13 +183,22 @@ int close_nointr(int fd) { return -errno; } -void close_nointr_nofail(int fd) { - PROTECT_ERRNO; +int safe_close(int fd) { - /* like close_nointr() but cannot fail, and guarantees errno - * is unchanged */ + /* + * Like close_nointr() but cannot fail. Guarantees errno is + * unchanged. Is a NOP with negative fds passed, and returns + * -1, so that it can be used in this syntax: + * + * fd = safe_close(fd); + */ - assert_se(close_nointr(fd) == 0); + if (fd >= 0) { + PROTECT_ERRNO; + assert_se(close_nointr(fd) == 0); + } + + return -1; } void close_many(const int fds[], unsigned n_fd) { @@ -198,7 +207,7 @@ void close_many(const int fds[], unsigned n_fd) { assert(fds || n_fd <= 0); for (i = 0; i < n_fd; i++) - close_nointr_nofail(fds[i]); + safe_close(fds[i]); } int unlink_noerrno(const char *path) { @@ -920,19 +929,6 @@ 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; @@ -1706,16 +1702,13 @@ finish: } int reset_terminal(const char *name) { - int fd, r; + _cleanup_close_ int fd = -1; fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) return fd; - r = reset_terminal_fd(fd, true); - close_nointr_nofail(fd); - - return r; + return reset_terminal_fd(fd, true); } int open_terminal(const char *name, int mode) { @@ -1754,12 +1747,12 @@ int open_terminal(const char *name, int mode) { r = isatty(fd); if (r < 0) { - close_nointr_nofail(fd); + safe_close(fd); return -errno; } if (!r) { - close_nointr_nofail(fd); + safe_close(fd); return -ENOTTY; } @@ -1948,11 +1941,10 @@ int acquire_terminal( * ended our handle will be dead. It's important that * we do this after sleeping, so that we don't enter * an endless loop. */ - close_nointr_nofail(fd); + safe_close(fd); } - if (notify >= 0) - close_nointr_nofail(notify); + safe_close(notify); r = reset_terminal_fd(fd, true); if (r < 0) @@ -1961,11 +1953,8 @@ int acquire_terminal( return fd; fail: - if (fd >= 0) - close_nointr_nofail(fd); - - if (notify >= 0) - close_nointr_nofail(notify); + safe_close(fd); + safe_close(notify); return r; } @@ -2048,22 +2037,18 @@ int default_signals(int sig, ...) { return r; } -int close_pipe(int p[]) { - int a = 0, b = 0; - +void safe_close_pair(int p[]) { assert(p); - if (p[0] >= 0) { - a = close_nointr(p[0]); - p[0] = -1; - } - - if (p[1] >= 0) { - b = close_nointr(p[1]); - p[1] = -1; + if (p[0] == p[1]) { + /* Special case pairs which use the same fd in both + * directions... */ + p[0] = p[1] = safe_close(p[0]); + return; } - return a < 0 ? a : b; + p[0] = safe_close(p[0]); + p[1] = safe_close(p[1]); } ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { @@ -2275,7 +2260,7 @@ int make_stdio(int fd) { t = dup3(fd, STDERR_FILENO, 0); if (fd >= 3) - close_nointr_nofail(fd); + safe_close(fd); if (r < 0 || s < 0 || t < 0) return -errno; @@ -2653,7 +2638,7 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct d = fdopendir(fd); if (!d) { - close_nointr_nofail(fd); + safe_close(fd); return errno == ENOENT ? 0 : -errno; } @@ -2749,7 +2734,7 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root assert(fd >= 0); if (fstatfs(fd, &s) < 0) { - close_nointr_nofail(fd); + safe_close(fd); return -errno; } @@ -2758,7 +2743,7 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root * non-state data */ if (!is_temporary_fs(&s)) { log_error("Attempted to remove disk file system, and we can't allow that."); - close_nointr_nofail(fd); + safe_close(fd); return -EPERM; } @@ -2804,13 +2789,13 @@ static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bo if (!dangerous) { if (fstatfs(fd, &s) < 0) { - close_nointr_nofail(fd); + safe_close(fd); return -errno; } if (!is_temporary_fs(&s)) { log_error("Attempted to remove disk file system, and we can't allow that."); - close_nointr_nofail(fd); + safe_close(fd); return -EPERM; } } @@ -3198,19 +3183,27 @@ bool on_tty(void) { return cached_on_tty; } -int running_in_chroot(void) { - struct stat a = {}, b = {}; +int files_same(const char *filea, const char *fileb) { + struct stat a, b; - /* Only works as root */ - if (stat("/proc/1/root", &a) < 0) + if (stat(filea, &a) < 0) return -errno; - if (stat("/", &b) < 0) + if (stat(fileb, &b) < 0) return -errno; - return - a.st_dev != b.st_dev || - a.st_ino != b.st_ino; + return a.st_dev == b.st_dev && + a.st_ino == b.st_ino; +} + +int running_in_chroot(void) { + int ret; + + ret = files_same("/proc/1/root", "/"); + if (ret < 0) + return ret; + + return ret == 0; } static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { @@ -3323,7 +3316,7 @@ char *ellipsize(const char *s, size_t length, unsigned percent) { } int touch(const char *path) { - int fd; + _cleanup_close_ int fd; assert(path); @@ -3335,7 +3328,6 @@ int touch(const char *path) { if (fd < 0) return -errno; - close_nointr_nofail(fd); return 0; } @@ -3498,7 +3490,7 @@ DIR *xopendirat(int fd, const char *name, int flags) { d = fdopendir(nfd); if (!d) { - close_nointr_nofail(nfd); + safe_close(nfd); return NULL; } @@ -3519,25 +3511,21 @@ int signal_from_string_try_harder(const char *s) { static char *tag_to_udev_node(const char *tagvalue, const char *by) { _cleanup_free_ char *t = NULL, *u = NULL; - char *dn; size_t enc_len; u = unquote(tagvalue, "\"\'"); - if (u == NULL) + if (!u) return NULL; enc_len = strlen(u) * 4 + 1; t = new(char, enc_len); - if (t == NULL) + if (!t) return NULL; if (encode_devnode_name(u, t, enc_len) < 0) return NULL; - if (asprintf(&dn, "/dev/disk/by-%s/%s", by, t) < 0) - return NULL; - - return dn; + return strjoin("/dev/disk/by-", by, "/", t, NULL); } char *fstab_node_to_udev_node(const char *p) { @@ -3684,111 +3672,123 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { return endswith(de->d_name, suffix); } -void execute_directory(const char *directory, DIR *d, char *argv[]) { - DIR *_d = NULL; - struct dirent *de; - Hashmap *pids = NULL; +void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv[]) { + pid_t executor_pid; + int r; assert(directory); - /* Executes all binaries in a directory in parallel and - * waits for them to finish. */ + /* Executes all binaries in a directory in parallel and waits + * for them to finish. Optionally a timeout is applied. */ - if (!d) { - if (!(_d = opendir(directory))) { + executor_pid = fork(); + if (executor_pid < 0) { + log_error("Failed to fork: %m"); + return; - if (errno == ENOENT) - return; + } else if (executor_pid == 0) { + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; + _cleanup_closedir_ DIR *_d = NULL; + struct dirent *de; + sigset_t ss; - log_error("Failed to enumerate directory %s: %m", directory); - return; - } + /* We fork this all off from a child process so that + * we can somewhat cleanly make use of SIGALRM to set + * a time limit */ - d = _d; - } + reset_all_signal_handlers(); - if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) { - log_error("Failed to allocate set."); - goto finish; - } + assert_se(sigemptyset(&ss) == 0); + assert_se(sigprocmask(SIG_SETMASK, &ss, NULL) == 0); - while ((de = readdir(d))) { - char *path; - pid_t pid; - int k; + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - if (!dirent_is_file(de)) - continue; + if (!d) { + d = _d = opendir(directory); + if (!d) { + if (errno == ENOENT) + _exit(EXIT_SUCCESS); - if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) { - log_oom(); - continue; + log_error("Failed to enumerate directory %s: %m", directory); + _exit(EXIT_FAILURE); + } } - if ((pid = fork()) < 0) { - log_error("Failed to fork: %m"); - free(path); - continue; + pids = hashmap_new(NULL, NULL); + if (!pids) { + log_oom(); + _exit(EXIT_FAILURE); } - if (pid == 0) { - char *_argv[2]; - /* Child */ + FOREACH_DIRENT(de, d, break) { + _cleanup_free_ char *path = NULL; + pid_t pid; - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; - } else - argv[0] = path; + if (!dirent_is_file(de)) + continue; - execv(path, argv); + if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) { + log_oom(); + _exit(EXIT_FAILURE); + } - log_error("Failed to execute %s: %m", path); - _exit(EXIT_FAILURE); - } + pid = fork(); + if (pid < 0) { + log_error("Failed to fork: %m"); + continue; + } else if (pid == 0) { + char *_argv[2]; - log_debug("Spawned %s as %lu", path, (unsigned long) pid); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) { - log_error("Failed to add PID to set: %s", strerror(-k)); - free(path); - } - } + if (!argv) { + _argv[0] = path; + _argv[1] = NULL; + argv = _argv; + } else + argv[0] = path; - while (!hashmap_isempty(pids)) { - pid_t pid = PTR_TO_UINT(hashmap_first_key(pids)); - siginfo_t si = {}; - char *path; + execv(path, argv); + log_error("Failed to execute %s: %m", path); + _exit(EXIT_FAILURE); + } - if (waitid(P_PID, pid, &si, WEXITED) < 0) { - if (errno == EINTR) - continue; + log_debug("Spawned %s as " PID_FMT ".", path, pid); - log_error("waitid() failed: %m"); - goto finish; + r = hashmap_put(pids, UINT_TO_PTR(pid), path); + if (r < 0) { + log_oom(); + _exit(EXIT_FAILURE); + } + + path = NULL; } - if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) { - if (!is_clean_exit(si.si_code, si.si_status, NULL)) { - if (si.si_code == CLD_EXITED) - log_error("%s exited with exit status %i.", path, si.si_status); - else - log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status)); - } else - log_debug("%s exited successfully.", path); + /* Abort execution of this process after the + * timout. We simply rely on SIGALRM as default action + * terminating the process, and turn on alarm(). */ + + if (timeout != (usec_t) -1) + alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + + while (!hashmap_isempty(pids)) { + _cleanup_free_ char *path = NULL; + pid_t pid; - free(path); + pid = PTR_TO_UINT(hashmap_first_key(pids)); + assert(pid > 0); + + path = hashmap_remove(pids, UINT_TO_PTR(pid)); + assert(path); + + wait_for_terminate_and_warn(path, pid); } - } -finish: - if (_d) - closedir(_d); + _exit(EXIT_SUCCESS); + } - if (pids) - hashmap_free_free(pids); + wait_for_terminate_and_warn(directory, executor_pid); } int kill_and_sigcont(pid_t pid, int sig) { @@ -3984,16 +3984,13 @@ int terminal_vhangup_fd(int fd) { } int terminal_vhangup(const char *name) { - int fd, r; + _cleanup_close_ int fd; fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) return fd; - r = terminal_vhangup_fd(fd); - close_nointr_nofail(fd); - - return r; + return terminal_vhangup_fd(fd); } int vt_disallocate(const char *name) { @@ -4020,7 +4017,7 @@ int vt_disallocate(const char *name) { "\033[H" /* move home */ "\033[2J", /* clear screen */ 10, false); - close_nointr_nofail(fd); + safe_close(fd); return 0; } @@ -4041,7 +4038,7 @@ int vt_disallocate(const char *name) { return fd; r = ioctl(fd, VT_DISALLOCATE, u); - close_nointr_nofail(fd); + safe_close(fd); if (r >= 0) return 0; @@ -4060,7 +4057,7 @@ int vt_disallocate(const char *name) { "\033[H" /* move home */ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */ 10, false); - close_nointr_nofail(fd); + safe_close(fd); return 0; } @@ -4182,7 +4179,7 @@ int socket_from_display(const char *display, char **path) { k = strspn(display+1, "0123456789"); - f = new(char, sizeof("/tmp/.X11-unix/X") + k); + f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); if (!f) return -ENOMEM; @@ -5637,7 +5634,7 @@ int on_ac_power(void) { if (n != 6 || memcmp(contents, "Mains\n", 6)) continue; - close_nointr_nofail(fd); + safe_close(fd); fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { if (errno == ENOENT) @@ -5665,14 +5662,14 @@ 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) { +static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { char **i; assert(path); assert(mode); assert(_f); - if (!path_strv_canonicalize_absolute_uniq(search, NULL)) + if (!path_strv_canonicalize_absolute_uniq(search, root)) return -ENOMEM; STRV_FOREACH(i, search) { @@ -5696,7 +5693,7 @@ static int search_and_fopen_internal(const char *path, const char *mode, char ** return -ENOENT; } -int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) { +int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) { _cleanup_strv_free_ char **copy = NULL; assert(path); @@ -5719,10 +5716,10 @@ int search_and_fopen(const char *path, const char *mode, const char **search, FI if (!copy) return -ENOMEM; - return search_and_fopen_internal(path, mode, copy, _f); + return search_and_fopen_internal(path, mode, root, copy, _f); } -int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) { +int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) { _cleanup_strv_free_ char **s = NULL; if (path_is_absolute(path)) { @@ -5741,7 +5738,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *sear if (!s) return -ENOMEM; - return search_and_fopen_internal(path, mode, s, _f); + return search_and_fopen_internal(path, mode, root, s, _f); } char *strextend(char **x, ...) { @@ -5932,7 +5929,7 @@ int split_pair(const char *s, const char *sep, char **l, char **r) { } int shall_restore_state(void) { - _cleanup_free_ char *line; + _cleanup_free_ char *line = NULL; char *w, *state; size_t l; int r; @@ -5943,11 +5940,26 @@ int shall_restore_state(void) { if (r == 0) /* Container ... */ return 1; - FOREACH_WORD_QUOTED(w, l, line, state) - if (l == 23 && strneq(w, "systemd.restore_state=0", 23)) - return 0; + r = 1; - return 1; + FOREACH_WORD_QUOTED(w, l, line, state) { + const char *e; + char n[l+1]; + int k; + + memcpy(n, w, l); + n[l] = 0; + + e = startswith(n, "systemd.restore_state="); + if (!e) + continue; + + k = parse_boolean(e); + if (k >= 0) + r = k; + } + + return r; } int proc_cmdline(char **ret) { @@ -5965,7 +5977,7 @@ int proc_cmdline(char **ret) { if (*p == 0) *p = ' '; - *p = 0; + *p = 0; *ret = buf; return 1; } @@ -5977,12 +5989,14 @@ int proc_cmdline(char **ret) { return 1; } -int parse_proc_cmdline(int (*parse_word)(const char *word)) { +int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { _cleanup_free_ char *line = NULL; char *w, *state; size_t l; int r; + assert(parse_item); + r = proc_cmdline(&line); if (r < 0) log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); @@ -5990,17 +6004,23 @@ int parse_proc_cmdline(int (*parse_word)(const char *word)) { return 0; FOREACH_WORD_QUOTED(w, l, line, state) { - _cleanup_free_ char *word; + char word[l+1], *value; - word = strndup(w, l); - if (!word) - return log_oom(); + memcpy(word, w, l); + word[l] = 0; - r = parse_word(word); - if (r < 0) { - log_error("Failed on cmdline argument %s: %s", word, strerror(-r)); + /* Filter out arguments that are intended only for the + * initrd */ + if (!in_initrd() && startswith(word, "rd.")) + continue; + + value = strchr(word, '='); + if (value) + *(value++) = 0; + + r = parse_item(word, value); + if (r < 0) return r; - } } return 0; @@ -6304,3 +6324,63 @@ uint64_t physical_memory(void) { return (uint64_t) mem * (uint64_t) page_size(); } + +char* mount_test_option(const char *haystack, const char *needle) { + + struct mntent me = { + .mnt_opts = (char*) haystack + }; + + assert(needle); + + /* Like glibc's hasmntopt(), but works on a string, not a + * struct mntent */ + + if (!haystack) + return NULL; + + return hasmntopt(&me, needle); +} + +void hexdump(FILE *f, const void *p, size_t s) { + const uint8_t *b = p; + unsigned n = 0; + + assert(s == 0 || b); + + while (s > 0) { + size_t i; + + fprintf(f, "%04x ", n); + + for (i = 0; i < 16; i++) { + + if (i >= s) + fputs(" ", f); + else + fprintf(f, "%02x ", b[i]); + + if (i == 7) + fputc(' ', f); + } + + fputc(' ', f); + + for (i = 0; i < 16; i++) { + + if (i >= s) + fputc(' ', f); + else + fputc(isprint(b[i]) ? (char) b[i] : '.', f); + } + + fputc('\n', f); + + if (s < 16) + break; + + n += 16; + b += 16; + s -= 16; + } +}