chiark / gitweb /
util: open the first RTC that has hctosys=1 set
[elogind.git] / src / util.c
index 195835425d2fef51e4ec08f8121f432853eb6d5d..de5feeb8d03eb4a2db31f929d167a5b99c29a63b 100644 (file)
@@ -517,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;
         }
@@ -562,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;
         }
@@ -709,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;
         }
 
@@ -1119,6 +1119,57 @@ int get_process_exe(pid_t pid, char **name) {
         return r;
 }
 
+int get_process_uid(pid_t pid, uid_t *uid) {
+        char *p;
+        FILE *f;
+        int r;
+
+        assert(uid);
+
+        if (pid == 0)
+                return getuid();
+
+        if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
+                return -ENOMEM;
+
+        f = fopen(p, "re");
+        free(p);
+
+        if (!f)
+                return -errno;
+
+        while (!feof(f)) {
+                char line[LINE_MAX], *l;
+
+                if (!fgets(line, sizeof(line), f)) {
+                        if (feof(f))
+                                break;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                l = strstrip(line);
+
+                if (startswith(l, "Uid:")) {
+                        l += 4;
+                        l += strspn(l, WHITESPACE);
+
+                        l[strcspn(l, WHITESPACE)] = 0;
+
+                        r = parse_uid(l, uid);
+                        goto finish;
+                }
+        }
+
+        r = -EIO;
+
+finish:
+        fclose(f);
+
+        return r;
+}
+
 char *strnappend(const char *s, const char *suffix, size_t b) {
         size_t a;
         char *r;
@@ -2416,14 +2467,14 @@ int ask(char *ret, const char *replies, const char *text, ...) {
                 bool need_nl = true;
 
                 if (on_tty)
-                        fputs("\x1B[1m", stdout);
+                        fputs(ANSI_HIGHLIGHT_ON, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
                 if (on_tty)
-                        fputs("\x1B[0m", stdout);
+                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
 
                 fflush(stdout);
 
@@ -2453,7 +2504,6 @@ int ask(char *ret, const char *replies, const char *text, ...) {
 int reset_terminal_fd(int fd) {
         struct termios termios;
         int r = 0;
-        long arg;
 
         /* Set terminal to some sane defaults */
 
@@ -2466,9 +2516,11 @@ int reset_terminal_fd(int fd) {
         /* Disable exclusive mode, just in case */
         ioctl(fd, TIOCNXCL);
 
+        /* Switch to text mode */
+        ioctl(fd, KDSETMODE, KD_TEXT);
+
         /* Enable console unicode mode */
-        arg = K_UNICODE;
-        ioctl(fd, KDSKBMODE, &arg);
+        ioctl(fd, KDSKBMODE, K_UNICODE);
 
         if (tcgetattr(fd, &termios) < 0) {
                 r = -errno;
@@ -2885,7 +2937,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;
@@ -3024,6 +3077,8 @@ int parse_bytes(const char *t, off_t *bytes) {
                 { "M", 1024ULL*1024ULL },
                 { "G", 1024ULL*1024ULL*1024ULL },
                 { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
                 { "", 1 },
         };
 
@@ -3295,7 +3350,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;
         }
@@ -3428,7 +3483,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);
 
@@ -3442,7 +3499,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;
@@ -3504,7 +3563,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) {
@@ -3523,11 +3582,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;
 }
@@ -3576,9 +3637,12 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
         }
 }
 
-void status_vprintf(const char *format, va_list ap) {
-        char *s = NULL;
-        int fd = -1;
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+        char *s = NULL, *spaces = NULL, *e;
+        int fd = -1, c;
+        size_t emax, sl, left;
+        struct iovec iovec[5];
+        int n = 0;
 
         assert(format);
 
@@ -3588,25 +3652,69 @@ void status_vprintf(const char *format, va_list ap) {
         if (vasprintf(&s, format, ap) < 0)
                 goto finish;
 
-        if ((fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0)
+        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
                 goto finish;
 
-        write(fd, s, strlen(s));
+        if (ellipse) {
+                c = fd_columns(fd);
+                if (c <= 0)
+                        c = 80;
+
+                if (status) {
+                        sl = 2 + 6 + 1; /* " [" status "]" */
+                        emax = (size_t) c > sl ? c - sl - 1 : 0;
+                } else
+                        emax = c - 1;
+
+                e = ellipsize(s, emax, 75);
+                if (e) {
+                        free(s);
+                        s = e;
+                }
+        }
+
+        zero(iovec);
+        IOVEC_SET_STRING(iovec[n++], s);
+
+        if (ellipse) {
+                sl = strlen(s);
+                left = emax > sl ? emax - sl : 0;
+                if (left > 0) {
+                        spaces = malloc(left);
+                        if (spaces) {
+                                memset(spaces, ' ', left);
+                                iovec[n].iov_base = spaces;
+                                iovec[n].iov_len = left;
+                                n++;
+                        }
+                }
+        }
+
+        if (status) {
+                IOVEC_SET_STRING(iovec[n++], " [");
+                IOVEC_SET_STRING(iovec[n++], status);
+                IOVEC_SET_STRING(iovec[n++], "]\n");
+        } else
+                IOVEC_SET_STRING(iovec[n++], "\n");
+
+        writev(fd, iovec, n);
 
 finish:
         free(s);
+        free(spaces);
 
         if (fd >= 0)
                 close_nointr_nofail(fd);
 }
 
-void status_printf(const char *format, ...) {
+void status_printf(const char *status, bool ellipse, const char *format, ...) {
         va_list ap;
 
         assert(format);
 
         va_start(ap, format);
-        status_vprintf(format, ap);
+        status_vprintf(status, ellipse, format, ap);
         va_end(ap);
 }
 
@@ -3763,7 +3871,9 @@ void status_welcome(void) {
         if (!ansi_color && !const_color)
                 const_color = "1";
 
-        status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n",
+        status_printf(NULL,
+                      false,
+                      "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
                       const_color ? const_color : ansi_color,
                       const_pretty ? const_pretty : pretty_name);
 
@@ -3905,6 +4015,19 @@ char **replace_env_argv(char **argv, char **env) {
         return r;
 }
 
+int fd_columns(int fd) {
+        struct winsize ws;
+        zero(ws);
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return -errno;
+
+        if (ws.ws_col <= 0)
+                return -EIO;
+
+        return ws.ws_col;
+}
+
 unsigned columns(void) {
         static __thread int parsed_columns = 0;
         const char *e;
@@ -3912,16 +4035,12 @@ unsigned columns(void) {
         if (_likely_(parsed_columns > 0))
                 return parsed_columns;
 
-        if ((e = getenv("COLUMNS")))
+        e = getenv("COLUMNS");
+        if (e)
                 parsed_columns = atoi(e);
 
-        if (parsed_columns <= 0) {
-                struct winsize ws;
-                zero(ws);
-
-                if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
-                        parsed_columns = ws.ws_col;
-        }
+        if (parsed_columns <= 0)
+                parsed_columns = fd_columns(STDOUT_FILENO);
 
         if (parsed_columns <= 0)
                 parsed_columns = 80;
@@ -3999,7 +4118,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])
@@ -4291,31 +4411,37 @@ int vtnr_from_tty(const char *tty) {
         return i;
 }
 
-const char *default_term_for_tty(const char *tty) {
+bool tty_is_vc_resolve(const char *tty) {
         char *active = NULL;
-        const char *term;
+        bool b;
 
         assert(tty);
 
         if (startswith(tty, "/dev/"))
                 tty += 5;
 
-        /* Resolve where /dev/console is pointing when determining
-         * TERM */
+        /* Resolve where /dev/console is pointing to */
         if (streq(tty, "console"))
                 if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
                         /* If multiple log outputs are configured the
                          * last one is what /dev/console points to */
-                        if ((tty = strrchr(active, ' ')))
+                        tty = strrchr(active, ' ');
+                        if (tty)
                                 tty++;
                         else
                                 tty = active;
                 }
 
-        term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100";
+        b = tty_is_vc(tty);
         free(active);
 
-        return term;
+        return b;
+}
+
+const char *default_term_for_tty(const char *tty) {
+        assert(tty);
+
+        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
 }
 
 bool dirent_is_file(const struct dirent *de) {
@@ -4693,6 +4819,24 @@ int pipe_eof(int fd) {
         return pollfd.revents & POLLHUP;
 }
 
+int fd_wait_for_event(int fd, int event) {
+        struct pollfd pollfd;
+        int r;
+
+        zero(pollfd);
+        pollfd.fd = fd;
+        pollfd.events = event;
+
+        r = poll(&pollfd, 1, -1);
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents;
+}
+
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         FILE *f;
         char *t;
@@ -5011,13 +5155,78 @@ int hwclock_reset_localtime_delta(void) {
         return 0;
 }
 
+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 */
+
+        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;
 
@@ -5041,7 +5250,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;
 
@@ -5673,7 +5882,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);
@@ -5682,7 +5891,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);
 }
 
@@ -5939,3 +6148,52 @@ unsigned long cap_last_cap(void) {
 
         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[] = {
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "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;
+
+}
+
+void* memdup(const void *p, size_t l) {
+        void *r;
+
+        assert(p);
+
+        r = malloc(l);
+        if (!r)
+                return NULL;
+
+        memcpy(r, p, l);
+        return r;
+}