chiark / gitweb /
shared: add is_efiboot()
[elogind.git] / src / shared / util.c
index 1c97a8a94ae65386b7731c672e4e1248d7f65826..99836953bfafd8853ba3d217476b5cd42139b9f2 100644 (file)
@@ -57,6 +57,8 @@
 #include <sys/vfs.h>
 #include <linux/magic.h>
 #include <limits.h>
+#include <langinfo.h>
+#include <locale.h>
 
 #include "macro.h"
 #include "util.h"
 int saved_argc = 0;
 char **saved_argv = NULL;
 
-static int parsed_columns = 0;
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
+bool is_efiboot(void) {
+        return access("/sys/firmware/efi", F_OK) >= 0;
+}
 
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
@@ -147,6 +154,9 @@ usec_t timespec_load(const struct timespec *ts) {
             ts->tv_nsec == (long) -1)
                 return (usec_t) -1;
 
+        if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
+                return (usec_t) -1;
+
         return
                 (usec_t) ts->tv_sec * USEC_PER_SEC +
                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
@@ -174,6 +184,9 @@ usec_t timeval_load(const struct timeval *tv) {
             tv->tv_usec == (suseconds_t) -1)
                 return (usec_t) -1;
 
+        if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
+                return (usec_t) -1;
+
         return
                 (usec_t) tv->tv_sec * USEC_PER_SEC +
                 (usec_t) tv->tv_usec;
@@ -194,7 +207,7 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         return tv;
 }
 
-bool endswith(const char *s, const char *postfix) {
+char* endswith(const char *s, const char *postfix) {
         size_t sl, pl;
 
         assert(s);
@@ -204,53 +217,49 @@ bool endswith(const char *s, const char *postfix) {
         pl = strlen(postfix);
 
         if (pl == 0)
-                return true;
+                return (char*) s + sl;
 
         if (sl < pl)
-                return false;
+                return NULL;
+
+        if (memcmp(s + sl - pl, postfix, pl) != 0)
+                return NULL;
 
-        return memcmp(s + sl - pl, postfix, pl) == 0;
+        return (char*) s + sl - pl;
 }
 
-bool startswith(const char *s, const char *prefix) {
-        size_t sl, pl;
+char* startswith(const char *s, const char *prefix) {
+        const char *a, *b;
 
         assert(s);
         assert(prefix);
 
-        sl = strlen(s);
-        pl = strlen(prefix);
-
-        if (pl == 0)
-                return true;
-
-        if (sl < pl)
-                return false;
+        a = s, b = prefix;
+        for (;;) {
+                if (*b == 0)
+                        return (char*) a;
+                if (*a != *b)
+                        return NULL;
 
-        return memcmp(s, prefix, pl) == 0;
+                a++, b++;
+        }
 }
 
-bool startswith_no_case(const char *s, const char *prefix) {
-        size_t sl, pl;
-        unsigned i;
+char* startswith_no_case(const char *s, const char *prefix) {
+        const char *a, *b;
 
         assert(s);
         assert(prefix);
 
-        sl = strlen(s);
-        pl = strlen(prefix);
-
-        if (pl == 0)
-                return true;
-
-        if (sl < pl)
-                return false;
-
-        for(i = 0; i < pl; ++i)
-                if (tolower(s[i]) != tolower(prefix[i]))
-                        return false;
+        a = s, b = prefix;
+        for (;;) {
+                if (*b == 0)
+                        return (char*) a;
+                if (tolower(*a) != tolower(*b))
+                        return NULL;
 
-        return true;
+                a++, b++;
+        }
 }
 
 bool first_word(const char *s, const char *word) {
@@ -374,7 +383,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
         errno = 0;
         l = strtoul(s, &x, 0);
 
-        if (!x || *x || errno)
+        if (!x || x == s || *x || errno)
                 return errno ? -errno : -EINVAL;
 
         if ((unsigned long) (unsigned) l != l)
@@ -394,7 +403,7 @@ int safe_atoi(const char *s, int *ret_i) {
         errno = 0;
         l = strtol(s, &x, 0);
 
-        if (!x || *x || errno)
+        if (!x || x == s || *x || errno)
                 return errno ? -errno : -EINVAL;
 
         if ((long) (int) l != l)
@@ -414,7 +423,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
         errno = 0;
         l = strtoull(s, &x, 0);
 
-        if (!x || *x || errno)
+        if (!x || x == s || *x || errno)
                 return errno ? -errno : -EINVAL;
 
         *ret_llu = l;
@@ -431,7 +440,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
         errno = 0;
         l = strtoll(s, &x, 0);
 
-        if (!x || *x || errno)
+        if (!x || x == s || *x || errno)
                 return errno ? -errno : -EINVAL;
 
         *ret_lli = l;
@@ -2175,28 +2184,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);
@@ -3793,46 +3799,26 @@ int fd_columns(int fd) {
         return ws.ws_col;
 }
 
-static unsigned columns_cached(bool cached) {
-        static __thread int env_columns = -1;
+unsigned columns(void) {
         const char *e;
+        unsigned c;
 
-        if (_likely_(parsed_columns > 0 && cached))
-                return parsed_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;
+        if (_likely_(cached_columns > 0))
+                return cached_columns;
 
-        return parsed_columns;
-}
+        c = 0;
+        e = getenv("COLUMNS");
+        if (e)
+                safe_atou(e, &c);
 
-unsigned columns(void) {
-        return columns_cached(true);
-}
+        if (c <= 0)
+                c = fd_columns(STDOUT_FILENO);
 
-unsigned columns_uncached(void) {
-        return columns_cached(false);
-}
+        if (c <= 0)
+                c = 80;
 
-/* intended to be used as a SIGWINCH sighandler */
-void columns_cache_reset(int signum) {
-        parsed_columns = 0;
+        cached_columns = c;
+        return c;
 }
 
 int fd_lines(int fd) {
@@ -3849,23 +3835,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 (parsed_lines <= 0)
-                parsed_lines = fd_lines(STDOUT_FILENO);
+        if (l <= 0)
+                l = fd_lines(STDOUT_FILENO);
 
-        if (parsed_lines <= 0)
-                parsed_lines = 25;
+        if (l <= 0)
+                l = 24;
 
-        return parsed_lines;
+        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;
+}
+
+bool on_tty(void) {
+        static int cached_on_tty = -1;
+
+        if (_unlikely_(cached_on_tty < 0))
+                cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+        return cached_on_tty;
 }
 
 int running_in_chroot(void) {
@@ -5210,7 +5213,7 @@ static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_IDLE] = "idle"
 };
 
-DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
 
 static const char *const sigchld_code_table[] = {
         [CLD_EXITED] = "exited",
@@ -5246,7 +5249,7 @@ static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
         [LOG_FAC(LOG_LOCAL7)] = "local7"
 };
 
-DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
 
 static const char *const log_level_table[] = {
         [LOG_EMERG] = "emerg",
@@ -5259,7 +5262,7 @@ static const char *const log_level_table[] = {
         [LOG_DEBUG] = "debug"
 };
 
-DEFINE_STRING_TABLE_LOOKUP(log_level, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
 
 static const char* const sched_policy_table[] = {
         [SCHED_OTHER] = "other",
@@ -5269,7 +5272,7 @@ static const char* const sched_policy_table[] = {
         [SCHED_RR] = "rr"
 };
 
-DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
 
 static const char* const rlimit_table[] = {
         [RLIMIT_CPU] = "LimitCPU",
@@ -5299,7 +5302,7 @@ static const char* const ip_tos_table[] = {
         [IPTOS_LOWCOST] = "low-cost",
 };
 
-DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
 
 static const char *const __signal_table[] = {
         [SIGHUP] = "HUP",
@@ -5700,6 +5703,30 @@ int can_sleep(const char *type) {
         return false;
 }
 
+int can_sleep_disk(const char *type) {
+        char *w, *state;
+        size_t l, k;
+        int r;
+        _cleanup_free_ char *p = NULL;
+
+        assert(type);
+
+        r = read_one_line_file("/sys/power/disk", &p);
+        if (r < 0)
+                return r == -ENOENT ? 0 : r;
+
+        k = strlen(type);
+        FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
+                if (l == k && memcmp(w, type, l) == 0)
+                        return true;
+
+                if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']')
+                        return true;
+        }
+
+        return false;
+}
+
 bool is_valid_documentation_url(const char *url) {
         assert(url);
 
@@ -6071,3 +6098,68 @@ finish:
 
         return 0;
 }
+
+/* hey glibc, APIs with callbacks without a user pointer are so useless */
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 int (*compar) (const void *, const void *, void *), void *arg) {
+        size_t l, u, idx;
+        const void *p;
+        int comparison;
+
+        l = 0;
+        u = nmemb;
+        while (l < u) {
+                idx = (l + u) / 2;
+                p = (void *)(((const char *) base) + (idx * size));
+                comparison = compar(key, p, arg);
+                if (comparison < 0)
+                        u = idx;
+                else if (comparison > 0)
+                        l = idx + 1;
+                else
+                        return (void *)p;
+        }
+        return NULL;
+}
+
+bool is_locale_utf8(void) {
+        const char *set;
+        static int cached_answer = -1;
+
+        if (cached_answer >= 0)
+                goto out;
+
+        if (!setlocale(LC_ALL, "")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        set = nl_langinfo(CODESET);
+        if (!set) {
+                cached_answer = true;
+                goto out;
+        }
+
+        cached_answer = streq(set, "UTF-8");
+out:
+        return (bool)cached_answer;
+}
+
+const char *draw_special_char(DrawSpecialChar ch) {
+        static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
+                /* UTF-8 */ {
+                        [DRAW_BOX_VERT]           = "\342\224\202", /* │ */
+                        [DRAW_BOX_VERT_AND_RIGHT] = "\342\224\234", /* ├ */
+                        [DRAW_BOX_UP_AND_RIGHT]   = "\342\224\224", /* └ */
+                        [DRAW_TRIANGULAR_BULLET]  = "\342\200\243", /* ‣ */
+                },
+                /* ASCII fallback */ {
+                        [DRAW_BOX_VERT] = "|",
+                        [DRAW_BOX_VERT_AND_RIGHT] = "+",
+                        [DRAW_BOX_UP_AND_RIGHT]   = "\\",
+                        [DRAW_TRIANGULAR_BULLET]  = ">",
+                }
+        };
+
+        return draw_table[!is_locale_utf8()][ch];
+}