chiark / gitweb /
always use /etc/os-release and ignore all distro-specific files
[elogind.git] / src / util.c
index b6e490d457a3562ad1e97037bdd9fbe7a9687bd9..e9869ea4fad3bfd3695d41a6de7059db6deedfed 100644 (file)
@@ -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;
         }
@@ -1100,6 +1107,37 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         return 0;
 }
 
+int is_kernel_thread(pid_t pid) {
+        char *p;
+        size_t count;
+        char c;
+        bool eof;
+        FILE *f;
+
+        if (pid == 0)
+                return 0;
+
+        if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+                return -ENOMEM;
+
+        f = fopen(p, "re");
+        free(p);
+
+        if (!f)
+                return -errno;
+
+        count = fread(&c, 1, 1, f);
+        eof = feof(f);
+        fclose(f);
+
+        /* Kernel threads have an empty cmdline */
+
+        if (count <= 0)
+                return eof ? 1 : -errno;
+
+        return 0;
+}
+
 int get_process_exe(pid_t pid, char **name) {
         int r;
 
@@ -2403,7 +2441,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];
@@ -2421,6 +2459,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);
@@ -2436,7 +2481,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);
@@ -2478,7 +2527,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.");
@@ -2501,7 +2551,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;
 
@@ -2517,7 +2567,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);
@@ -2571,7 +2622,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;
@@ -2765,7 +2816,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;
@@ -3218,11 +3270,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));
@@ -3483,7 +3539,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                         }
 
                         if (honour_sticky)
-                                keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
 
                         is_dir = S_ISDIR(st.st_mode);
 
@@ -3497,7 +3555,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                                         continue;
                                 }
 
-                                keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
                         }
 
                         is_dir = de->d_type == DT_DIR;
@@ -3559,7 +3619,7 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
 
         if (delete_root) {
 
-                if (honour_sticky && file_is_sticky(path) > 0)
+                if (honour_sticky && file_is_priv_sticky(path) > 0)
                         return r;
 
                 if (rmdir(path) < 0 && errno != ENOENT) {
@@ -3578,11 +3638,13 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
          * first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
-        if (chmod(path, mode) < 0)
-                return -errno;
+        if (mode != (mode_t) -1)
+                if (chmod(path, mode) < 0)
+                        return -errno;
 
-        if (chown(path, uid, gid) < 0)
-                return -errno;
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (chown(path, uid, gid) < 0)
+                        return -errno;
 
         return 0;
 }
@@ -3726,139 +3788,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";
 
@@ -4042,6 +3971,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;
 
@@ -4112,7 +4074,8 @@ char *unquote(const char *s, const char* quotes) {
         size_t l;
         assert(s);
 
-        if ((l = strlen(s)) < 2)
+        l = strlen(s);
+        if (l < 2)
                 return strdup(s);
 
         if (strchr(quotes, s[0]) && s[l-1] == s[0])
@@ -4534,11 +4497,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;
@@ -4812,7 +4776,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;
 
@@ -4820,7 +4784,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;
 
@@ -5148,13 +5112,84 @@ int hwclock_reset_localtime_delta(void) {
         return 0;
 }
 
+int rtc_open(int flags) {
+        int fd;
+        DIR *d;
+
+        /* 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)
+                goto fallback;
+
+        for (;;) {
+                char *p, *v;
+                struct dirent buf, *de;
+                int r;
+
+                r = readdir_r(d, &buf, &de);
+                if (r != 0)
+                        goto fallback;
+
+                if (!de)
+                        goto fallback;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
+                if (!p) {
+                        closedir(d);
+                        return -ENOMEM;
+                }
+
+                r = read_one_line_file(p, &v);
+                free(p);
+
+                if (r < 0)
+                        continue;
+
+                r = parse_boolean(v);
+                free(v);
+
+                if (r <= 0)
+                        continue;
+
+                p = strappend("/dev/", de->d_name);
+                fd = open(p, flags);
+                free(p);
+
+                if (fd >= 0) {
+                        closedir(d);
+                        return fd;
+                }
+        }
+
+fallback:
+        if (d)
+                closedir(d);
+
+        fd = open("/dev/rtc0", flags);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
 int hwclock_get_time(struct tm *tm) {
         int fd;
         int err = 0;
 
         assert(tm);
 
-        fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
@@ -5178,7 +5213,7 @@ int hwclock_set_time(const struct tm *tm) {
 
         assert(tm);
 
-        fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
@@ -5810,7 +5845,7 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
         return -ENOENT;
 }
 
-int file_is_sticky(const char *p) {
+int file_is_priv_sticky(const char *p) {
         struct stat st;
 
         assert(p);
@@ -5819,7 +5854,7 @@ int file_is_sticky(const char *p) {
                 return -errno;
 
         return
-                st.st_uid == 0 &&
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
                 (st.st_mode & S_ISVTX);
 }
 
@@ -6125,3 +6160,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;
+}