X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=06bd1b9f04b4fdc4714d6c5d0ae7a872b5ecf93b;hp=d04d73872e3ad49dfa32edf0798dc8efb470f4e2;hb=4968105790c65af58d4ab42bffa2a4bedc0be8ee;hpb=2c39ea529b35383022946a07eeecd6711bcd2684 diff --git a/src/shared/util.c b/src/shared/util.c index d04d73872..06bd1b9f0 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -61,6 +61,8 @@ #include #include #include +#include +#include #undef basename #ifdef HAVE_SYS_AUXV_H @@ -1152,7 +1154,7 @@ char *delete_chars(char *s, const char *bad) { } char *file_in_same_dir(const char *path, const char *filename) { - char *e, *r; + char *e, *ret; size_t k; assert(path); @@ -1165,17 +1167,17 @@ char *file_in_same_dir(const char *path, const char *filename) { if (path_is_absolute(filename)) return strdup(filename); - if (!(e = strrchr(path, '/'))) + e = strrchr(path, '/'); + if (!e) return strdup(filename); k = strlen(filename); - if (!(r = new(char, e-path+1+k+1))) + ret = new(char, (e + 1 - path) + k + 1); + if (!ret) return NULL; - memcpy(r, path, e-path+1); - memcpy(r+(e-path)+1, filename, k+1); - - return r; + memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); + return ret; } int rmdir_parents(const char *path, const char *stop) { @@ -3456,7 +3458,7 @@ unsigned columns(void) { c = 80; cached_columns = c; - return c; + return cached_columns; } int fd_lines(int fd) { @@ -3473,7 +3475,7 @@ int fd_lines(int fd) { unsigned lines(void) { const char *e; - unsigned l; + int l; if (_likely_(cached_lines > 0)) return cached_lines; @@ -3481,7 +3483,7 @@ unsigned lines(void) { l = 0; e = getenv("LINES"); if (e) - (void) safe_atou(e, &l); + (void) safe_atoi(e, &l); if (l <= 0) l = fd_lines(STDOUT_FILENO); @@ -4004,7 +4006,7 @@ bool tty_is_vc_resolve(const char *tty) { const char *default_term_for_tty(const char *tty) { assert(tty); - return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt102"; + return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; } bool dirent_is_file(const struct dirent *de) { @@ -4035,117 +4037,112 @@ 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, usec_t timeout, char *argv[]) { - pid_t executor_pid; - int r; +static int do_execute(const char *directory, usec_t timeout, char *argv[]) { + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; + _cleanup_closedir_ DIR *d; + struct dirent *de; - assert(directory); + /* We fork this all off from a child process so that we can + * somewhat cleanly make use of SIGALRM to set a time limit */ - /* Executes all binaries in a directory in parallel and waits - * for them to finish. Optionally a timeout is applied. */ + reset_all_signal_handlers(); + reset_signal_mask(); - executor_pid = fork(); - if (executor_pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return; + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - } else if (executor_pid == 0) { - _cleanup_hashmap_free_free_ Hashmap *pids = NULL; - _cleanup_closedir_ DIR *_d = NULL; - struct dirent *de; + d = opendir(directory); + if (!d) { + if (errno == ENOENT) + return 0; - /* We fork this all off from a child process so that - * we can somewhat cleanly make use of SIGALRM to set - * a time limit */ + return log_error_errno(errno, "Failed to open directory %s: %m", directory); + } - reset_all_signal_handlers(); - reset_signal_mask(); + pids = hashmap_new(NULL); + if (!pids) + return log_oom(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + FOREACH_DIRENT(de, d, break) { + _cleanup_free_ char *path = NULL; + pid_t pid; + int r; - if (!d) { - d = _d = opendir(directory); - if (!d) { - if (errno == ENOENT) - _exit(EXIT_SUCCESS); + if (!dirent_is_file(de)) + continue; - log_error_errno(errno, "Failed to enumerate directory %s: %m", directory); - _exit(EXIT_FAILURE); - } - } + path = strjoin(directory, "/", de->d_name, NULL); + if (!path) + return log_oom(); - pids = hashmap_new(NULL); - if (!pids) { - log_oom(); - _exit(EXIT_FAILURE); - } + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + continue; + } else if (pid == 0) { + char *_argv[2]; - FOREACH_DIRENT(de, d, break) { - _cleanup_free_ char *path = NULL; - pid_t pid; + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - if (!dirent_is_file(de)) - continue; + if (!argv) { + _argv[0] = path; + _argv[1] = NULL; + argv = _argv; + } else + argv[0] = path; - path = strjoin(directory, "/", de->d_name, NULL); - if (!path) { - log_oom(); - _exit(EXIT_FAILURE); - } + execv(path, argv); + return log_error_errno(errno, "Failed to execute %s: %m", path); + } - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - continue; - } else if (pid == 0) { - char *_argv[2]; + log_debug("Spawned %s as " PID_FMT ".", path, pid); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + r = hashmap_put(pids, UINT_TO_PTR(pid), path); + if (r < 0) + return log_oom(); - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; - } else - argv[0] = path; + path = NULL; + } - execv(path, argv); - log_error_errno(errno, "Failed to execute %s: %m", path); - _exit(EXIT_FAILURE); - } + /* Abort execution of this process after the timout. We simply + * rely on SIGALRM as default action terminating the process, + * and turn on alarm(). */ - log_debug("Spawned %s as " PID_FMT ".", path, pid); + if (timeout != USEC_INFINITY) + alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - r = hashmap_put(pids, UINT_TO_PTR(pid), path); - if (r < 0) { - log_oom(); - _exit(EXIT_FAILURE); - } + while (!hashmap_isempty(pids)) { + _cleanup_free_ char *path = NULL; + pid_t pid; - path = NULL; - } + pid = PTR_TO_UINT(hashmap_first_key(pids)); + assert(pid > 0); - /* Abort execution of this process after the - * timout. We simply rely on SIGALRM as default action - * terminating the process, and turn on alarm(). */ + path = hashmap_remove(pids, UINT_TO_PTR(pid)); + assert(path); - if (timeout != USEC_INFINITY) - alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + wait_for_terminate_and_warn(path, pid, true); + } - while (!hashmap_isempty(pids)) { - _cleanup_free_ char *path = NULL; - pid_t pid; + return 0; +} - pid = PTR_TO_UINT(hashmap_first_key(pids)); - assert(pid > 0); +void execute_directory(const char *directory, usec_t timeout, char *argv[]) { + pid_t executor_pid; + int r; - path = hashmap_remove(pids, UINT_TO_PTR(pid)); - assert(path); + assert(directory); - wait_for_terminate_and_warn(path, pid, true); - } + /* Executes all binaries in the directory in parallel and waits + * for them to finish. Optionally a timeout is applied. */ - _exit(EXIT_SUCCESS); + executor_pid = fork(); + if (executor_pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + return; + + } else if (executor_pid == 0) { + r = do_execute(directory, timeout, argv); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } wait_for_terminate_and_warn(directory, executor_pid, true); @@ -5184,6 +5181,9 @@ char *format_bytes(char *buf, size_t l, off_t t) { { "K", 1024ULL }, }; + if (t == (off_t) -1) + return NULL; + for (i = 0; i < ELEMENTSOF(table); i++) { if (t >= table[i].factor) { @@ -6855,6 +6855,15 @@ int umount_recursive(const char *prefix, int flags) { return r ? r : n; } +static int get_mount_flags(const char *path, unsigned long *flags) { + struct statvfs buf; + + if (statvfs(path, &buf) < 0) + return -errno; + *flags = buf.f_flag; + return 0; +} + int bind_remount_recursive(const char *prefix, bool ro) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; @@ -6889,6 +6898,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { _cleanup_set_free_free_ Set *todo = NULL; bool top_autofs = false; char *x; + unsigned long orig_flags; todo = set_new(&string_hash_ops); if (!todo) @@ -6966,7 +6976,11 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) return -errno; - if (mount(NULL, prefix, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) + orig_flags = 0; + (void) get_mount_flags(cleaned, &orig_flags); + orig_flags &= ~MS_RDONLY; + + if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) return -errno; x = strdup(cleaned); @@ -6986,7 +7000,14 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (r < 0) return r; - if (mount(NULL, x, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) { + /* Try to reuse the original flag set, but + * don't care for errors, in case of + * obstructed mounts */ + orig_flags = 0; + (void) get_mount_flags(x, &orig_flags); + orig_flags &= ~MS_RDONLY; + + if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) { /* Deal with mount points that are * obstructed by a later mount */ @@ -7644,9 +7665,109 @@ int fd_setcrtime(int fd, usec_t usec) { assert(fd >= 0); + if (usec <= 0) + usec = now(CLOCK_REALTIME); + le = htole64((uint64_t) usec); if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0) return -errno; return 0; } + +int same_fd(int a, int b) { + struct stat sta, stb; + pid_t pid; + int r, fa, fb; + + assert(a >= 0); + assert(b >= 0); + + /* Compares two file descriptors. Note that semantics are + * quite different depending on whether we have kcmp() or we + * don't. If we have kcmp() this will only return true for + * dup()ed file descriptors, but not otherwise. If we don't + * have kcmp() this will also return true for two fds of the same + * file, created by separate open() calls. Since we use this + * call mostly for filtering out duplicates in the fd store + * this difference hopefully doesn't matter too much. */ + + if (a == b) + return true; + + /* Try to use kcmp() if we have it. */ + pid = getpid(); + r = kcmp(pid, pid, KCMP_FILE, a, b); + if (r == 0) + return true; + if (r > 0) + return false; + if (errno != ENOSYS) + return -errno; + + /* We don't have kcmp(), use fstat() instead. */ + if (fstat(a, &sta) < 0) + return -errno; + + if (fstat(b, &stb) < 0) + return -errno; + + if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) + return false; + + /* We consider all device fds different, since two device fds + * might refer to quite different device contexts even though + * they share the same inode and backing dev_t. */ + + if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) + return false; + + if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) + return false; + + /* The fds refer to the same inode on disk, let's also check + * if they have the same fd flags. This is useful to + * distuingish the read and write side of a pipe created with + * pipe(). */ + fa = fcntl(a, F_GETFL); + if (fa < 0) + return -errno; + + fb = fcntl(b, F_GETFL); + if (fb < 0) + return -errno; + + return fa == fb; +} + +int chattr_fd(int fd, bool b, int mask) { + int old_attr, new_attr; + + assert(fd >= 0); + + if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0) + return -errno; + + if (b) + new_attr = old_attr | mask; + else + new_attr = old_attr & ~mask; + + if (new_attr == old_attr) + return 0; + + if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0) + return -errno; + + return 0; +} + +int chattr_path(const char *p, bool b, int mask) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return chattr_fd(fd, b, mask); +}