chiark / gitweb /
util: look at STDOUT, not STDIN, when determining tty columns
[elogind.git] / src / util.c
index b5513dd74c45174b74b3a8fad55ea719d3f08a18..f982586cee9b1d2c1939e8b7f8d4ecd13a3e5e81 100644 (file)
@@ -1,4 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8 -*-*/
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
 /***
   This file is part of systemd.
@@ -47,6 +47,8 @@
 #include <sys/utsname.h>
 #include <pwd.h>
 #include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
 
 #include "macro.h"
 #include "util.h"
@@ -54,6 +56,8 @@
 #include "missing.h"
 #include "log.h"
 #include "strv.h"
+#include "label.h"
+#include "exit-status.h"
 
 bool streq_ptr(const char *a, const char *b) {
 
@@ -968,7 +972,7 @@ char *file_in_same_dir(const char *path, const char *filename) {
 int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         struct stat st;
 
-        if (mkdir(path, mode) >= 0)
+        if (label_mkdir(path, mode) >= 0)
                 if (chmod_and_chown(path, mode, uid, gid) < 0)
                         return -errno;
 
@@ -1011,7 +1015,7 @@ int mkdir_parents(const char *path, mode_t mode) {
                 if (!(t = strndup(path, e - path)))
                         return -ENOMEM;
 
-                r = mkdir(t, mode);
+                r = label_mkdir(t, mode);
                 free(t);
 
                 if (r < 0 && errno != EEXIST)
@@ -1027,7 +1031,7 @@ int mkdir_p(const char *path, mode_t mode) {
         if ((r = mkdir_parents(path, mode)) < 0)
                 return r;
 
-        if (mkdir(path, mode) < 0 && errno != EEXIST)
+        if (label_mkdir(path, mode) < 0 && errno != EEXIST)
                 return -errno;
 
         return 0;
@@ -1655,6 +1659,63 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
         return buf;
 }
 
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
+        usec_t n, d;
+
+        n = now(CLOCK_REALTIME);
+
+        if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
+                return NULL;
+
+        d = n - t;
+
+        if (d >= USEC_PER_YEAR)
+                snprintf(buf, l, "%llu years and %llu months ago",
+                         (unsigned long long) (d / USEC_PER_YEAR),
+                         (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
+        else if (d >= USEC_PER_MONTH)
+                snprintf(buf, l, "%llu months and %llu days ago",
+                         (unsigned long long) (d / USEC_PER_MONTH),
+                         (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
+        else if (d >= USEC_PER_WEEK)
+                snprintf(buf, l, "%llu weeks and %llu days ago",
+                         (unsigned long long) (d / USEC_PER_WEEK),
+                         (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
+        else if (d >= 2*USEC_PER_DAY)
+                snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
+        else if (d >= 25*USEC_PER_HOUR)
+                snprintf(buf, l, "1 day and %lluh ago",
+                         (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
+        else if (d >= 6*USEC_PER_HOUR)
+                snprintf(buf, l, "%lluh ago",
+                         (unsigned long long) (d / USEC_PER_HOUR));
+        else if (d >= USEC_PER_HOUR)
+                snprintf(buf, l, "%lluh %llumin ago",
+                         (unsigned long long) (d / USEC_PER_HOUR),
+                         (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
+        else if (d >= 5*USEC_PER_MINUTE)
+                snprintf(buf, l, "%llumin ago",
+                         (unsigned long long) (d / USEC_PER_MINUTE));
+        else if (d >= USEC_PER_MINUTE)
+                snprintf(buf, l, "%llumin %llus ago",
+                         (unsigned long long) (d / USEC_PER_MINUTE),
+                         (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
+        else if (d >= USEC_PER_SEC)
+                snprintf(buf, l, "%llus ago",
+                         (unsigned long long) (d / USEC_PER_SEC));
+        else if (d >= USEC_PER_MSEC)
+                snprintf(buf, l, "%llums ago",
+                         (unsigned long long) (d / USEC_PER_MSEC));
+        else if (d > 0)
+                snprintf(buf, l, "%lluus ago",
+                         (unsigned long long) d);
+        else
+                snprintf(buf, l, "now");
+
+        buf[l-1] = 0;
+        return buf;
+}
+
 char *format_timespan(char *buf, size_t l, usec_t t) {
         static const struct {
                 const char *suffix;
@@ -1678,6 +1739,12 @@ char *format_timespan(char *buf, size_t l, usec_t t) {
         if (t == (usec_t) -1)
                 return NULL;
 
+        if (t == 0) {
+                snprintf(p, l, "0");
+                p[l-1] = 0;
+                return p;
+        }
+
         /* The result of this function can be parsed with parse_usec */
 
         for (i = 0; i < ELEMENTSOF(table); i++) {
@@ -1844,10 +1911,22 @@ int ask(char *ret, const char *replies, const char *text, ...) {
 int reset_terminal(int fd) {
         struct termios termios;
         int r = 0;
+        long arg;
+
+        /* Set terminal to some sane defaults */
 
         assert(fd >= 0);
 
-        /* Set terminal to some sane defaults */
+        /* First, unlock termios */
+        zero(termios);
+        ioctl(fd, TIOCSLCKTRMIOS, &termios);
+
+        /* Disable exclusive mode, just in case */
+        ioctl(fd, TIOCNXCL);
+
+        /* Enable console unicode mode */
+        arg = K_UNICODE;
+        ioctl(fd, KDSKBMODE, &arg);
 
         if (tcgetattr(fd, &termios) < 0) {
                 r = -errno;
@@ -2377,6 +2456,16 @@ bool is_clean_exit(int code, int status) {
         return false;
 }
 
+bool is_clean_exit_lsb(int code, int status) {
+
+        if (is_clean_exit(code, status))
+                return true;
+
+        return
+                code == CLD_EXITED &&
+                (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+}
+
 bool is_device_path(const char *path) {
 
         /* Returns true on paths that refer to a device, either in
@@ -2472,18 +2561,6 @@ char* gethostname_malloc(void) {
         return strdup(u.sysname);
 }
 
-int getmachineid_malloc(char **b) {
-        int r;
-
-        assert(b);
-
-        if ((r = read_one_line_file("/var/lib/dbus/machine-id", b)) < 0)
-                return r;
-
-        strstrip(*b);
-        return 0;
-}
-
 char* getlogname_malloc(void) {
         uid_t uid;
         long bufsize;
@@ -2522,11 +2599,12 @@ char* getlogname_malloc(void) {
 
 int getttyname_malloc(char **r) {
         char path[PATH_MAX], *p, *c;
+        int k;
 
         assert(r);
 
-        if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0)
-                return -errno;
+        if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0)
+                return -k;
 
         char_array_0(path);
 
@@ -2552,7 +2630,8 @@ static int rm_rf_children(int fd, bool only_dirs) {
 
         if (!(d = fdopendir(fd))) {
                 close_nointr_nofail(fd);
-                return -errno;
+
+                return errno == ENOENT ? 0 : -errno;
         }
 
         for (;;) {
@@ -2576,7 +2655,7 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         struct stat st;
 
                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
@@ -2589,7 +2668,7 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         int subdir_fd;
 
                         if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
@@ -2600,13 +2679,13 @@ static int rm_rf_children(int fd, bool only_dirs) {
                         }
 
                         if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                         }
                 } else  if (!only_dirs) {
 
                         if (unlinkat(fd, de->d_name, 0) < 0) {
-                                if (ret == 0)
+                                if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                         }
                 }
@@ -2764,7 +2843,7 @@ void status_welcome(void) {
 char *replace_env(const char *format, char **env) {
         enum {
                 WORD,
-                DOLLAR,
+                CURLY,
                 VARIABLE
         } state = WORD;
 
@@ -2779,11 +2858,11 @@ char *replace_env(const char *format, char **env) {
 
                 case WORD:
                         if (*e == '$')
-                                state = DOLLAR;
+                                state = CURLY;
                         break;
 
-                case DOLLAR:
-                        if (*e == '(') {
+                case CURLY:
+                        if (*e == '{') {
                                 if (!(k = strnappend(r, word, e-word-1)))
                                         goto fail;
 
@@ -2807,19 +2886,19 @@ char *replace_env(const char *format, char **env) {
                         break;
 
                 case VARIABLE:
-                        if (*e == ')') {
-                                char *t;
+                        if (*e == '}') {
+                                const char *t;
 
-                                if ((t = strv_env_get_with_length(env, word+2, e-word-2))) {
-                                        if (!(k = strappend(r, t)))
-                                                goto fail;
+                                if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
+                                        t = "";
 
-                                        free(r);
-                                        r = k;
+                                if (!(k = strappend(r, t)))
+                                        goto fail;
 
-                                        word = e+1;
-                                }
+                                free(r);
+                                r = k;
 
+                                word = e+1;
                                 state = WORD;
                         }
                         break;
@@ -2839,12 +2918,52 @@ fail:
 
 char **replace_env_argv(char **argv, char **env) {
         char **r, **i;
-        unsigned k = 0;
+        unsigned k = 0, l = 0;
 
-        if (!(r = new(char*, strv_length(argv)+1)))
+        l = strv_length(argv);
+
+        if (!(r = new(char*, l+1)))
                 return NULL;
 
         STRV_FOREACH(i, argv) {
+
+                /* If $FOO appears as single word, replace it by the split up variable */
+                if ((*i)[0] == '$' && (*i)[1] != '{') {
+                        char *e;
+                        char **w, **m;
+                        unsigned q;
+
+                        if ((e = strv_env_get(env, *i+1))) {
+
+                                if (!(m = strv_split_quoted(e))) {
+                                        r[k] = NULL;
+                                        strv_free(r);
+                                        return NULL;
+                                }
+                        } else
+                                m = NULL;
+
+                        q = strv_length(m);
+                        l = l + q - 1;
+
+                        if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+                                r[k] = NULL;
+                                strv_free(r);
+                                strv_free(m);
+                                return NULL;
+                        }
+
+                        r = w;
+                        if (m) {
+                                memcpy(r + k, m, q * sizeof(char*));
+                                free(m);
+                        }
+
+                        k += q;
+                        continue;
+                }
+
+                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
                 if (!(r[k++] = replace_env(*i, env))) {
                         strv_free(r);
                         return NULL;
@@ -2869,7 +2988,7 @@ int columns(void) {
                 struct winsize ws;
                 zero(ws);
 
-                if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+                if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
                         parsed_columns = ws.ws_col;
         }
 
@@ -2898,6 +3017,63 @@ int running_in_chroot(void) {
                 a.st_ino != b.st_ino;
 }
 
+char *ellipsize(const char *s, unsigned length, unsigned percent) {
+        size_t l, x;
+        char *r;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(length >= 3);
+
+        l = strlen(s);
+
+        if (l <= 3 || l <= length)
+                return strdup(s);
+
+        if (!(r = new0(char, length+1)))
+                return r;
+
+        x = (length * percent) / 100;
+
+        if (x > length - 3)
+                x = length - 3;
+
+        memcpy(r, s, x);
+        r[x] = '.';
+        r[x+1] = '.';
+        r[x+2] = '.';
+        memcpy(r + x + 3,
+               s + l - (length - x - 3),
+               length - x - 3);
+
+        return r;
+}
+
+int touch(const char *path) {
+        int fd;
+
+        assert(path);
+
+        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0)
+                return -errno;
+
+        close_nointr_nofail(fd);
+        return 0;
+}
+
+char *unquote(const char *s, const char quote) {
+        size_t l;
+        assert(s);
+
+        if ((l = strlen(s)) < 2)
+                return strdup(s);
+
+        if (s[0] == quote && s[l-1] == quote)
+                return strndup(s+1, l-2);
+
+        return strdup(s);
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",