X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=dfc1dc6b85a94a7dec0bf01cc2b74dfc08938c9f;hp=ef6eea4b9f591321a430a67920c4673251945b3d;hb=ee4cbc2c85edacab721c710e1f99aedd5c2e3a3a;hpb=1e5678d023ab57807d3de08e10304e36bf9a1950 diff --git a/src/util.c b/src/util.c index ef6eea4b9..dfc1dc6b8 100644 --- a/src/util.c +++ b/src/util.c @@ -705,15 +705,22 @@ int read_one_line_file(const char *fn, char **line) { assert(fn); assert(line); - if (!(f = fopen(fn, "re"))) + f = fopen(fn, "re"); + if (!f) return -errno; - if (!(fgets(t, sizeof(t), f))) { - r = feof(f) ? -EIO : -errno; - goto finish; + if (!fgets(t, sizeof(t), f)) { + + if (ferror(f)) { + r = -errno; + goto finish; + } + + t[0] = 0; } - if (!(c = strdup(t))) { + c = strdup(t); + if (!c) { r = -ENOMEM; goto finish; } @@ -885,7 +892,7 @@ int load_env_file( char ***rl) { FILE *f; - char **m = 0; + char **m = NULL; int r; assert(fname); @@ -1826,7 +1833,8 @@ char *cunescape_length(const char *s, size_t length) { /* Undoes C style string escaping */ - if (!(r = new(char, length+1))) + r = new(char, length+1); + if (!r) return r; for (f = s, t = r; f < s + length; f++) { @@ -1880,8 +1888,10 @@ char *cunescape_length(const char *s, size_t length) { /* hexadecimal encoding */ int a, b; - if ((a = unhexchar(f[1])) < 0 || - (b = unhexchar(f[2])) < 0) { + a = unhexchar(f[1]); + b = unhexchar(f[2]); + + if (a < 0 || b < 0) { /* Invalid escape code, let's take it literal then */ *(t++) = '\\'; *(t++) = 'x'; @@ -1904,9 +1914,11 @@ char *cunescape_length(const char *s, size_t length) { /* octal encoding */ int a, b, c; - if ((a = unoctchar(f[0])) < 0 || - (b = unoctchar(f[1])) < 0 || - (c = unoctchar(f[2])) < 0) { + a = unoctchar(f[0]); + b = unoctchar(f[1]); + c = unoctchar(f[2]); + + if (a < 0 || b < 0 || c < 0) { /* Invalid escape code, let's take it literal then */ *(t++) = '\\'; *(t++) = f[0]; @@ -2195,13 +2207,47 @@ int fd_cloexec(int fd, bool cloexec) { return 0; } +static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { + unsigned i; + + assert(n_fdset == 0 || fdset); + + for (i = 0; i < n_fdset; i++) + if (fdset[i] == fd) + return true; + + return false; +} + int close_all_fds(const int except[], unsigned n_except) { DIR *d; struct dirent *de; int r = 0; - if (!(d = opendir("/proc/self/fd"))) - return -errno; + assert(n_except == 0 || except); + + d = opendir("/proc/self/fd"); + if (!d) { + int fd; + struct rlimit rl; + + /* When /proc isn't available (for example in chroots) + * the fallback is brute forcing through the fd + * table */ + + assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); + for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + + if (fd_in_set(fd, except, n_except)) + continue; + + if (close_nointr(fd) < 0) + if (errno != EBADF && r == 0) + r = -errno; + } + + return r; + } while ((de = readdir(d))) { int fd = -1; @@ -2219,20 +2265,8 @@ int close_all_fds(const int except[], unsigned n_except) { if (fd == dirfd(d)) continue; - if (except) { - bool found; - unsigned i; - - found = false; - for (i = 0; i < n_except; i++) - if (except[i] == fd) { - found = true; - break; - } - - if (found) - continue; - } + if (fd_in_set(fd, except, n_except)) + continue; if (close_nointr(fd) < 0) { /* Valgrind has its own FD and doesn't want to have it closed */ @@ -2434,7 +2468,7 @@ fail: return r; } -int read_one_char(FILE *f, char *ret, bool *need_nl) { +int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { struct termios old_termios, new_termios; char c; char line[LINE_MAX]; @@ -2452,6 +2486,13 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { size_t k; + if (t != (usec_t) -1) { + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { + tcsetattr(fileno(f), TCSADRAIN, &old_termios); + return -ETIMEDOUT; + } + } + k = fread(&c, 1, 1, f); tcsetattr(fileno(f), TCSADRAIN, &old_termios); @@ -2467,7 +2508,11 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { } } - if (!(fgets(line, sizeof(line), f))) + if (t != (usec_t) -1) + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) + return -ETIMEDOUT; + + if (!fgets(line, sizeof(line), f)) return -EIO; truncate_nl(line); @@ -2509,7 +2554,8 @@ int ask(char *ret, const char *replies, const char *text, ...) { fflush(stdout); - if ((r = read_one_char(stdin, &c, &need_nl)) < 0) { + r = read_one_char(stdin, &c, (usec_t) -1, &need_nl); + if (r < 0) { if (r == -EBADMSG) { puts("Bad input, please try again."); @@ -2532,7 +2578,7 @@ int ask(char *ret, const char *replies, const char *text, ...) { } } -int reset_terminal_fd(int fd) { +int reset_terminal_fd(int fd, bool switch_to_text) { struct termios termios; int r = 0; @@ -2548,7 +2594,8 @@ int reset_terminal_fd(int fd) { ioctl(fd, TIOCNXCL); /* Switch to text mode */ - ioctl(fd, KDSETMODE, KD_TEXT); + if (switch_to_text) + ioctl(fd, KDSETMODE, KD_TEXT); /* Enable console unicode mode */ ioctl(fd, KDSKBMODE, K_UNICODE); @@ -2602,7 +2649,7 @@ int reset_terminal(const char *name) { if (fd < 0) return fd; - r = reset_terminal_fd(fd); + r = reset_terminal_fd(fd, true); close_nointr_nofail(fd); return r; @@ -2796,7 +2843,8 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst if (notify >= 0) close_nointr_nofail(notify); - if ((r = reset_terminal_fd(fd)) < 0) + r = reset_terminal_fd(fd, true); + if (r < 0) log_warning("Failed to reset terminal: %s", strerror(-r)); return fd; @@ -3249,11 +3297,15 @@ fallback: void rename_process(const char name[8]) { assert(name); - prctl(PR_SET_NAME, name); + /* This is a like a poor man's setproctitle(). It changes the + * comm field, argv[0], and also the glibc's internally used + * name of the process. For the first one a limit of 16 chars + * applies, to the second one usually one of 10 (i.e. length + * of "/sbin/init"), to the third one one of 7 (i.e. length of + * "systemd"). If you pass a longer string it will be + * truncated */ - /* This is a like a poor man's setproctitle(). The string - * passed should fit in 7 chars (i.e. the length of - * "systemd") */ + prctl(PR_SET_NAME, name); if (program_invocation_name) strncpy(program_invocation_name, name, strlen(program_invocation_name)); @@ -3541,7 +3593,8 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { if (is_dir) { int subdir_fd; - if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { + subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (subdir_fd < 0) { if (ret == 0 && errno != ENOENT) ret = -errno; continue; @@ -3763,139 +3816,6 @@ void status_welcome(void) { log_warning("Failed to read /etc/os-release: %s", strerror(-r)); } -#if defined(TARGET_FEDORA) - if (!pretty_name) { - if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/system-release: %s", strerror(-r)); - } - } - - if (!ansi_color && pretty_name) { - - /* This tries to mimic the color magic the old Red Hat sysinit - * script did. */ - - if (startswith(pretty_name, "Red Hat")) - const_color = "0;31"; /* Red for RHEL */ - else if (startswith(pretty_name, "Fedora")) - const_color = "0;34"; /* Blue for Fedora */ - } - -#elif defined(TARGET_SUSE) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "0;32"; /* Green for openSUSE */ - -#elif defined(TARGET_GENTOO) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "1;34"; /* Light Blue for Gentoo */ - -#elif defined(TARGET_ALTLINUX) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "0;36"; /* Cyan for ALTLinux */ - - -#elif defined(TARGET_DEBIAN) - - if (!pretty_name) { - char *version; - - if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/debian_version: %s", strerror(-r)); - } else { - pretty_name = strappend("Debian ", version); - free(version); - - if (!pretty_name) - log_warning("Failed to allocate Debian version string."); - } - } - - if (!ansi_color) - const_color = "1;31"; /* Light Red for Debian */ - -#elif defined(TARGET_UBUNTU) - - if ((r = parse_env_file("/etc/lsb-release", NEWLINE, - "DISTRIB_DESCRIPTION", &pretty_name, - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/lsb-release: %s", strerror(-r)); - } - - if (!ansi_color) - const_color = "0;33"; /* Orange/Brown for Ubuntu */ - -#elif defined(TARGET_MANDRIVA) - - if (!pretty_name) { - char *s, *p; - - if ((r = read_one_line_file("/etc/mandriva-release", &s) < 0)) { - if (r != -ENOENT) - log_warning("Failed to read /etc/mandriva-release: %s", strerror(-r)); - } else { - p = strstr(s, " release "); - if (p) { - *p = '\0'; - p += 9; - p[strcspn(p, " ")] = '\0'; - - /* This corresponds to standard rc.sysinit */ - if (asprintf(&pretty_name, "%s\x1B[0;39m %s", s, p) > 0) - const_color = "1;36"; - else - log_warning("Failed to allocate Mandriva version string."); - } else - log_warning("Failed to parse /etc/mandriva-release"); - free(s); - } - } -#elif defined(TARGET_MEEGO) - - if (!pretty_name) { - if ((r = read_one_line_file("/etc/meego-release", &pretty_name)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/meego-release: %s", strerror(-r)); - } - } - - if (!ansi_color) - const_color = "1;35"; /* Bright Magenta for MeeGo */ -#endif - if (!pretty_name && !const_pretty) const_pretty = "Linux"; @@ -4079,6 +3999,39 @@ unsigned columns(void) { return parsed_columns; } +int fd_lines(int fd) { + struct winsize ws; + zero(ws); + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +unsigned lines(void) { + static __thread int parsed_lines = 0; + const char *e; + + if (_likely_(parsed_lines > 0)) + return parsed_lines; + + e = getenv("LINES"); + if (e) + parsed_lines = atoi(e); + + if (parsed_lines <= 0) + parsed_lines = fd_lines(STDOUT_FILENO); + + if (parsed_lines <= 0) + parsed_lines = 25; + + return parsed_lines; +} + int running_in_chroot(void) { struct stat a, b; @@ -4252,7 +4205,7 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) { } -void freeze(void) { +_noreturn_ void freeze(void) { /* Make sure nobody waits for us on a socket anymore */ close_all_fds(NULL, 0); @@ -4572,11 +4525,12 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) { } while (!hashmap_isempty(pids)) { + pid_t pid = PTR_TO_UINT(hashmap_first_key(pids)); siginfo_t si; char *path; zero(si); - if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + if (waitid(P_PID, pid, &si, WEXITED) < 0) { if (errno == EINTR) continue; @@ -4850,7 +4804,7 @@ int pipe_eof(int fd) { return pollfd.revents & POLLHUP; } -int fd_wait_for_event(int fd, int event) { +int fd_wait_for_event(int fd, int event, usec_t t) { struct pollfd pollfd; int r; @@ -4858,7 +4812,7 @@ int fd_wait_for_event(int fd, int event) { pollfd.fd = fd; pollfd.events = event; - r = poll(&pollfd, 1, -1); + r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC)); if (r < 0) return -errno; @@ -5190,8 +5144,14 @@ int rtc_open(int flags) { int fd; DIR *d; - /* We open the first RTC which has hctosys=1 set. If we don't - * find any we just take the first one */ + /* First, we try to make use of the /dev/rtc symlink. If that + * doesn't exist, we open the first RTC which has hctosys=1 + * set. If we don't find any we just take the first RTC that + * exists at all. */ + + fd = open("/dev/rtc", flags); + if (fd >= 0) + return fd; d = opendir("/sys/class/rtc"); if (!d) @@ -5649,6 +5609,36 @@ int get_group_creds(const char **groupname, gid_t *gid) { return 0; } +int in_group(const char *name) { + gid_t gid, *gids; + int ngroups_max, r, i; + + r = get_group_creds(&name, &gid); + if (r < 0) + return r; + + if (getgid() == gid) + return 1; + + if (getegid() == gid) + return 1; + + ngroups_max = sysconf(_SC_NGROUPS_MAX); + assert(ngroups_max > 0); + + gids = alloca(sizeof(gid_t) * ngroups_max); + + r = getgroups(ngroups_max, gids); + if (r < 0) + return -errno; + + for (i = 0; i < r; i++) + if (gids[i] == gid) + return 1; + + return 0; +} + int glob_exists(const char *path) { glob_t g; int r, k; @@ -6228,3 +6218,39 @@ void* memdup(const void *p, size_t l) { memcpy(r, p, l); return r; } + +int fd_inc_sndbuf(int fd, size_t n) { + int r, value; + socklen_t l = sizeof(value); + + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); + if (r >= 0 && + l == sizeof(value) && + (size_t) value >= n*2) + return 0; + + value = (int) n; + r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)); + if (r < 0) + return -errno; + + return 1; +} + +int fd_inc_rcvbuf(int fd, size_t n) { + int r, value; + socklen_t l = sizeof(value); + + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); + if (r >= 0 && + l == sizeof(value) && + (size_t) value >= n*2) + return 0; + + value = (int) n; + r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)); + if (r < 0) + return -errno; + + return 1; +}