chiark / gitweb /
util: unify line caching and column caching
[elogind.git] / src / shared / util.c
index 84f8565e0c0542299eb2ef9df148bbb0c0593302..ef30cb2dab15b2dfade9be5dde7d7e101fa9f31a 100644 (file)
@@ -56,6 +56,7 @@
 #include <sys/mman.h>
 #include <sys/vfs.h>
 #include <linux/magic.h>
+#include <limits.h>
 
 #include "macro.h"
 #include "util.h"
@@ -71,6 +72,9 @@
 int saved_argc = 0;
 char **saved_argv = NULL;
 
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -140,6 +144,10 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
+        if (ts->tv_sec == (time_t) -1 &&
+            ts->tv_nsec == (long) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) ts->tv_sec * USEC_PER_SEC +
                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
@@ -148,6 +156,12 @@ usec_t timespec_load(const struct timespec *ts) {
 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
         assert(ts);
 
+        if (u == (usec_t) -1) {
+                ts->tv_sec = (time_t) -1;
+                ts->tv_nsec = (long) -1;
+                return ts;
+        }
+
         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
 
@@ -157,6 +171,10 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
 usec_t timeval_load(const struct timeval *tv) {
         assert(tv);
 
+        if (tv->tv_sec == (time_t) -1 &&
+            tv->tv_usec == (suseconds_t) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) tv->tv_sec * USEC_PER_SEC +
                 (usec_t) tv->tv_usec;
@@ -165,6 +183,12 @@ usec_t timeval_load(const struct timeval *tv) {
 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         assert(tv);
 
+        if (u == (usec_t) -1) {
+                tv->tv_sec = (time_t) -1;
+                tv->tv_usec = (suseconds_t) -1;
+                return tv;
+        }
+
         tv->tv_sec = (time_t) (u / USEC_PER_SEC);
         tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
 
@@ -1108,7 +1132,7 @@ int get_process_exe(pid_t pid, char **name) {
         return r;
 }
 
-int get_process_uid(pid_t pid, uid_t *uid) {
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
         char *p;
         FILE *f;
         int r;
@@ -1140,8 +1164,8 @@ int get_process_uid(pid_t pid, uid_t *uid) {
 
                 l = strstrip(line);
 
-                if (startswith(l, "Uid:")) {
-                        l += 4;
+                if (startswith(l, field)) {
+                        l += strlen(field);
                         l += strspn(l, WHITESPACE);
 
                         l[strcspn(l, WHITESPACE)] = 0;
@@ -1159,6 +1183,14 @@ finish:
         return r;
 }
 
+int get_process_uid(pid_t pid, uid_t *uid) {
+        return get_process_id(pid, "Uid:", uid);
+}
+
+int get_process_gid(pid_t pid, gid_t *gid) {
+        return get_process_id(pid, "Gid:", gid);
+}
+
 char *strnappend(const char *s, const char *suffix, size_t b) {
         size_t a;
         char *r;
@@ -1176,8 +1208,11 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
         assert(suffix);
 
         a = strlen(s);
+        if (b > ((size_t) -1) - a)
+                return NULL;
 
-        if (!(r = new(char, a+b+1)))
+        r = new(char, a+b+1);
+        if (!r)
                 return NULL;
 
         memcpy(r, s, a);
@@ -1675,7 +1710,8 @@ char *xescape(const char *s, const char *bad) {
          * chars, in \xFF style escaping. May be reversed with
          * cunescape. */
 
-        if (!(r = new(char, strlen(s)*4+1)))
+        r = new(char, strlen(s) * 4 + 1);
+        if (!r)
                 return NULL;
 
         for (f = s, t = r; *f; f++) {
@@ -1924,7 +1960,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
 
         sec = (time_t) (t / USEC_PER_SEC);
 
-        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+        if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
                 return NULL;
 
         return buf;
@@ -2140,28 +2176,25 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
 }
 
 int ask(char *ret, const char *replies, const char *text, ...) {
-        bool on_tty;
 
         assert(ret);
         assert(replies);
         assert(text);
 
-        on_tty = isatty(STDOUT_FILENO);
-
         for (;;) {
                 va_list ap;
                 char c;
                 int r;
                 bool need_nl = true;
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_ON, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_OFF, stdout);
 
                 fflush(stdout);
@@ -2718,16 +2751,31 @@ int parse_usec(const char *t, usec_t *usec) {
                 const char *suffix;
                 usec_t usec;
         } table[] = {
+                { "seconds", USEC_PER_SEC },
+                { "second", USEC_PER_SEC },
                 { "sec", USEC_PER_SEC },
                 { "s", USEC_PER_SEC },
+                { "minutes", USEC_PER_MINUTE },
+                { "minute", USEC_PER_MINUTE },
                 { "min", USEC_PER_MINUTE },
+                { "months", USEC_PER_MONTH },
+                { "month", USEC_PER_MONTH },
+                { "msec", USEC_PER_MSEC },
+                { "ms", USEC_PER_MSEC },
+                { "m", USEC_PER_MINUTE },
+                { "hours", USEC_PER_HOUR },
+                { "hour", USEC_PER_HOUR },
                 { "hr", USEC_PER_HOUR },
                 { "h", USEC_PER_HOUR },
+                { "days", USEC_PER_DAY },
+                { "day", USEC_PER_DAY },
                 { "d", USEC_PER_DAY },
+                { "weeks", USEC_PER_WEEK },
+                { "week", USEC_PER_WEEK },
                 { "w", USEC_PER_WEEK },
-                { "msec", USEC_PER_MSEC },
-                { "ms", USEC_PER_MSEC },
-                { "m", USEC_PER_MINUTE },
+                { "years", USEC_PER_YEAR },
+                { "year", USEC_PER_YEAR },
+                { "y", USEC_PER_YEAR },
                 { "usec", 1ULL },
                 { "us", 1ULL },
                 { "", USEC_PER_SEC }, /* default is sec */
@@ -2781,16 +2829,31 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                 const char *suffix;
                 nsec_t nsec;
         } table[] = {
+                { "seconds", NSEC_PER_SEC },
+                { "second", NSEC_PER_SEC },
                 { "sec", NSEC_PER_SEC },
                 { "s", NSEC_PER_SEC },
+                { "minutes", NSEC_PER_MINUTE },
+                { "minute", NSEC_PER_MINUTE },
                 { "min", NSEC_PER_MINUTE },
+                { "months", NSEC_PER_MONTH },
+                { "month", NSEC_PER_MONTH },
+                { "msec", NSEC_PER_MSEC },
+                { "ms", NSEC_PER_MSEC },
+                { "m", NSEC_PER_MINUTE },
+                { "hours", NSEC_PER_HOUR },
+                { "hour", NSEC_PER_HOUR },
                 { "hr", NSEC_PER_HOUR },
                 { "h", NSEC_PER_HOUR },
+                { "days", NSEC_PER_DAY },
+                { "day", NSEC_PER_DAY },
                 { "d", NSEC_PER_DAY },
+                { "weeks", NSEC_PER_WEEK },
+                { "week", NSEC_PER_WEEK },
                 { "w", NSEC_PER_WEEK },
-                { "msec", NSEC_PER_MSEC },
-                { "ms", NSEC_PER_MSEC },
-                { "m", NSEC_PER_MINUTE },
+                { "years", NSEC_PER_YEAR },
+                { "year", NSEC_PER_YEAR },
+                { "y", NSEC_PER_YEAR },
                 { "usec", NSEC_PER_USEC },
                 { "us", NSEC_PER_USEC },
                 { "nsec", 1ULL },
@@ -2948,9 +3011,10 @@ int dir_is_empty(const char *path) {
                 return -errno;
 
         for (;;) {
-                struct dirent buf, *de;
+                struct dirent *de;
+                union dirent_storage buf;
 
-                r = readdir_r(d, &buf, &de);
+                r = readdir_r(d, &buf.de, &de);
                 if (r > 0)
                         return -r;
 
@@ -3252,12 +3316,13 @@ int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct
         }
 
         for (;;) {
-                struct dirent buf, *de;
+                struct dirent *de;
+                union dirent_storage buf;
                 bool is_dir, keep_around;
                 struct stat st;
                 int r;
 
-                r = readdir_r(d, &buf, &de);
+                r = readdir_r(d, &buf.de, &de);
                 if (r != 0 && ret == 0) {
                         ret = -r;
                         break;
@@ -3726,41 +3791,26 @@ int fd_columns(int fd) {
         return ws.ws_col;
 }
 
-static unsigned columns_cached(bool cached) {
-        static __thread int parsed_columns = 0, env_columns = -1;
+unsigned columns(void) {
         const char *e;
+        unsigned c;
 
-        if (_likely_(parsed_columns > 0 && cached))
-                return parsed_columns;
+        if (_likely_(cached_columns > 0))
+                return cached_columns;
 
-        if (_unlikely_(env_columns == -1)) {
-                e = getenv("COLUMNS");
-                if (e)
-                        env_columns = atoi(e);
-                else
-                        env_columns = 0;
-        }
-
-        if (env_columns > 0) {
-                parsed_columns = env_columns;
-                return parsed_columns;
-        }
-
-        if (parsed_columns <= 0 || !cached)
-                parsed_columns = fd_columns(STDOUT_FILENO);
-
-        if (parsed_columns <= 0)
-                parsed_columns = 80;
+        c = 0;
+        e = getenv("COLUMNS");
+        if (e)
+                safe_atou(e, &c);
 
-        return parsed_columns;
-}
+        if (c <= 0)
+                c = fd_columns(STDOUT_FILENO);
 
-unsigned columns(void) {
-        return columns_cached(true);
-}
+        if (c <= 0)
+                c = 80;
 
-unsigned columns_uncached(void) {
-        return columns_cached(false);
+        cached_columns = c;
+        return c;
 }
 
 int fd_lines(int fd) {
@@ -3777,23 +3827,40 @@ int fd_lines(int fd) {
 }
 
 unsigned lines(void) {
-        static __thread int parsed_lines = 0;
         const char *e;
+        unsigned l;
 
-        if (_likely_(parsed_lines > 0))
-                return parsed_lines;
+        if (_likely_(cached_lines > 0))
+                return cached_lines;
 
+        l = 0;
         e = getenv("LINES");
         if (e)
-                parsed_lines = atoi(e);
+                safe_atou(e, &l);
+
+        if (l <= 0)
+                l = fd_lines(STDOUT_FILENO);
+
+        if (l <= 0)
+                l = 24;
 
-        if (parsed_lines <= 0)
-                parsed_lines = fd_lines(STDOUT_FILENO);
+        cached_lines = l;
+        return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+        cached_columns = 0;
+        cached_lines = 0;
+}
 
-        if (parsed_lines <= 0)
-                parsed_lines = 25;
+bool on_tty(void) {
+        static int cached_on_tty = -1;
 
-        return parsed_lines;
+        if (_unlikely_(cached_on_tty < 0))
+                cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+        return cached_on_tty;
 }
 
 int running_in_chroot(void) {
@@ -3873,7 +3940,8 @@ char *unquote(const char *s, const char* quotes) {
 
         /* This is rather stupid, simply removes the heading and
          * trailing quotes if there is one. Doesn't care about
-         * escaping or anything. */
+         * escaping or anything. We should make this smarter one
+         * day...*/
 
         l = strlen(s);
         if (l < 2)
@@ -3886,39 +3954,40 @@ char *unquote(const char *s, const char* quotes) {
 }
 
 char *normalize_env_assignment(const char *s) {
-        char *name, *value, *p, *r;
+        _cleanup_free_ char *name = NULL, *value = NULL, *p = NULL;
+        char *eq, *r;
 
-        p = strchr(s, '=');
+        eq = strchr(s, '=');
+        if (!eq) {
+                char *t;
 
-        if (!p) {
-                if (!(r = strdup(s)))
+                r = strdup(s);
+                if (!r)
                         return NULL;
 
-                return strstrip(r);
+                t = strstrip(r);
+                if (t == r)
+                        return r;
+
+                memmove(r, t, strlen(t) + 1);
+                return r;
         }
 
-        if (!(name = strndup(s, p - s)))
+        name = strndup(s, eq - s);
+        if (!name)
                 return NULL;
 
-        if (!(p = strdup(p+1))) {
-                free(name);
+        p = strdup(eq + 1);
+        if (!p)
                 return NULL;
-        }
 
         value = unquote(strstrip(p), QUOTES);
-        free(p);
-
-        if (!value) {
-                free(name);
+        if (!value)
                 return NULL;
-        }
 
-        if (asprintf(&r, "%s=%s", name, value) < 0)
+        if (asprintf(&r, "%s=%s", strstrip(name), value) < 0)
                 r = NULL;
 
-        free(value);
-        free(name);
-
         return r;
 }
 
@@ -4097,6 +4166,8 @@ static char *tag_to_udev_node(const char *tagvalue, const char *by) {
 }
 
 char *fstab_node_to_udev_node(const char *p) {
+        assert(p);
+
         if (startswith(p, "LABEL="))
                 return tag_to_udev_node(p+6, "label");
 
@@ -4930,10 +5001,11 @@ int get_files_in_directory(const char *path, char ***list) {
                 return -errno;
 
         for (;;) {
-                struct dirent buffer, *de;
+                struct dirent *de;
+                union dirent_storage buf;
                 int k;
 
-                k = readdir_r(d, &buffer, &de);
+                k = readdir_r(d, &buf.de, &de);
                 if (k != 0) {
                         r = -k;
                         goto finish;
@@ -4999,12 +5071,19 @@ char *strjoin(const char *x, ...) {
 
                 for (;;) {
                         const char *t;
+                        size_t n;
 
                         t = va_arg(ap, const char *);
                         if (!t)
                                 break;
 
-                        l += strlen(t);
+                        n = strlen(t);
+                        if (n > ((size_t) -1) - l) {
+                                va_end(ap);
+                                return NULL;
+                        }
+
+                        l += n;
                 }
         } else
                 l = 0;
@@ -5276,7 +5355,7 @@ int signal_from_string(const char *s) {
         int offset = 0;
         unsigned u;
 
-        signo =__signal_from_string(s);
+        signo = __signal_from_string(s);
         if (signo > 0)
                 return signo;
 
@@ -5668,7 +5747,7 @@ void warn_melody(void) {
         if (fd < 0)
                 return;
 
-        /* Yeah, this is synchronous. Kinda sucks. Bute well... */
+        /* Yeah, this is synchronous. Kinda sucks. But well... */
 
         ioctl(fd, KIOCSOUND, (int)(1193180/440));
         usleep(125*USEC_PER_MSEC);
@@ -5818,3 +5897,172 @@ void closedirp(DIR **d) {
 void umaskp(mode_t *u) {
         umask(*u);
 }
+
+bool filename_is_safe(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (strchr(p, '/'))
+                return false;
+
+        if (streq(p, "."))
+                return false;
+
+        if (streq(p, ".."))
+                return false;
+
+        if (strlen(p) > FILENAME_MAX)
+                return false;
+
+        return true;
+}
+
+bool string_is_safe(const char *p) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++) {
+                if (*t > 0 && *t < ' ')
+                        return false;
+
+                if (strchr("\\\"\'", *t))
+                        return false;
+        }
+
+        return true;
+}
+
+int parse_timestamp(const char *t, usec_t *usec) {
+        const char *k;
+        struct tm tm, copy;
+        time_t x;
+        usec_t plus = 0, minus = 0, ret;
+        int r;
+
+        /*
+         * Allowed syntaxes:
+         *
+         *   2012-09-22 16:34:22
+         *   2012-09-22 16:34     (seconds will be set to 0)
+         *   2012-09-22           (time will be set to 00:00:00)
+         *   16:34:22             (date will be set to today)
+         *   16:34                (date will be set to today, seconds to 0)
+         *   now
+         *   yesterday            (time is set to 00:00:00)
+         *   today                (time is set to 00:00:00)
+         *   tomorrow             (time is set to 00:00:00)
+         *   +5min
+         *   -5days
+         *
+         */
+
+        assert(t);
+        assert(usec);
+
+        x = time(NULL);
+        assert_se(localtime_r(&x, &tm));
+
+        if (streq(t, "now"))
+                goto finish;
+
+        else if (streq(t, "today")) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "yesterday")) {
+                tm.tm_mday --;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "tomorrow")) {
+                tm.tm_mday ++;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (t[0] == '+') {
+
+                r = parse_usec(t+1, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        } else if (t[0] == '-') {
+
+                r = parse_usec(t+1, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        }
+
+        copy = tm;
+        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        return -EINVAL;
+
+finish:
+        x = mktime(&tm);
+        if (x == (time_t) -1)
+                return -EINVAL;
+
+        ret = (usec_t) x * USEC_PER_SEC;
+
+        ret += plus;
+        if (ret > minus)
+                ret -= minus;
+        else
+                ret = 0;
+
+        *usec = ret;
+
+        return 0;
+}