chiark / gitweb /
Disallow sizes with increasing unit size
[elogind.git] / src / shared / util.c
index 72b1e2f3c89da92c302b10c6f80e3c5940d42f28..285a263cdbe5cc13a8946bc6c699e6e92fb88e43 100644 (file)
@@ -58,6 +58,7 @@
 #include <limits.h>
 #include <langinfo.h>
 #include <locale.h>
+#include <sys/personality.h>
 #include <libgen.h>
 #undef basename
 
@@ -2134,30 +2135,71 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
         return n;
 }
 
-int parse_bytes(const char *t, off_t *bytes) {
-        static const struct {
+int parse_size(const char *t, off_t base, off_t *size) {
+
+        /* Soo, sometimes we want to parse IEC binary suffxies, and
+         * sometimes SI decimal suffixes. This function can parse
+         * both. Which one is the right way depends on the
+         * context. Wikipedia suggests that SI is customary for
+         * hardrware metrics and network speeds, while IEC is
+         * customary for most data sizes used by software and volatile
+         * (RAM) memory. Hence be careful which one you pick!
+         *
+         * In either case we use just K, M, G as suffix, and not Ki,
+         * Mi, Gi or so (as IEC would suggest). That's because that's
+         * frickin' ugly. But this means you really need to make sure
+         * to document which base you are parsing when you use this
+         * call. */
+
+        struct table {
                 const char *suffix;
                 unsigned long long factor;
-        } table[] = {
-                { "B", 1 },
-                { "K", 1024ULL },
-                { "M", 1024ULL*1024ULL },
-                { "G", 1024ULL*1024ULL*1024ULL },
-                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
-                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+        };
+
+        static const struct table iec[] = {
                 { "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 },
+                { "B", 1 },
+                { "", 1 },
+        };
+
+        static const struct table si[] = {
+                { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+                { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+                { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
+                { "G", 1000ULL*1000ULL*1000ULL },
+                { "M", 1000ULL*1000ULL },
+                { "K", 1000ULL },
+                { "B", 1 },
                 { "", 1 },
         };
 
+        const struct table *table;
         const char *p;
         unsigned long long r = 0;
+        unsigned n_entries, start_pos = 0;
 
         assert(t);
-        assert(bytes);
+        assert(base == 1000 || base == 1024);
+        assert(size);
+
+        if (base == 1000) {
+                table = si;
+                n_entries = ELEMENTSOF(si);
+        } else {
+                table = iec;
+                n_entries = ELEMENTSOF(iec);
+        }
 
         p = t;
         do {
                 long long l;
+                unsigned long long l2;
+                double frac = 0;
                 char *e;
                 unsigned i;
 
@@ -2173,14 +2215,32 @@ int parse_bytes(const char *t, off_t *bytes) {
                 if (e == p)
                         return -EINVAL;
 
+                if (*e == '.') {
+                        e++;
+                        if (*e >= '0' && *e <= '9') {
+                                char *e2;
+
+                                /* strotoull itself would accept space/+/- */
+                                l2 = strtoull(e, &e2, 10);
+
+                                if (errno == ERANGE)
+                                        return -errno;
+
+                                /* Ignore failure. E.g. 10.M is valid */
+                                frac = l2;
+                                for (; e < e2; e++)
+                                        frac /= 10;
+                        }
+                }
+
                 e += strspn(e, WHITESPACE);
 
-                for (i = 0; i < ELEMENTSOF(table); i++)
+                for (i = start_pos; i < n_entries; i++)
                         if (startswith(e, table[i].suffix)) {
                                 unsigned long long tmp;
-                                if ((unsigned long long) l > ULLONG_MAX / table[i].factor)
+                                if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor)
                                         return -ERANGE;
-                                tmp = l * table[i].factor;
+                                tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
                                 if (tmp > ULLONG_MAX - r)
                                         return -ERANGE;
 
@@ -2189,15 +2249,17 @@ int parse_bytes(const char *t, off_t *bytes) {
                                         return -ERANGE;
 
                                 p = e + strlen(table[i].suffix);
+
+                                start_pos = i + 1;
                                 break;
                         }
 
-                if (i >= ELEMENTSOF(table))
+                if (i >= n_entries)
                         return -EINVAL;
 
         } while (*p);
 
-        *bytes = r;
+        *size = r;
 
         return 0;
 }
@@ -2525,9 +2587,11 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
 }
 
 int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
-        int k;
-        char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *s, *b, *p;
+        char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
+        _cleanup_free_ char *s = NULL;
+        const char *p;
         dev_t devnr;
+        int k;
 
         assert(r);
 
@@ -2545,14 +2609,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
 
                 /* This is an ugly hack */
                 if (major(devnr) == 136) {
-                        if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
-                                return -ENOMEM;
-
-                        *r = b;
-                        if (_devnr)
-                                *_devnr = devnr;
-
-                        return 0;
+                        asprintf(&b, "pts/%lu", (unsigned long) minor(devnr));
+                        goto finish;
                 }
 
                 /* Probably something like the ptys which have no
@@ -2560,14 +2618,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
                  * vaguely useful. */
 
                 b = strdup(fn + 5);
-                if (!b)
-                        return -ENOMEM;
-
-                *r = b;
-                if (_devnr)
-                        *_devnr = devnr;
-
-                return 0;
+                goto finish;
         }
 
         if (startswith(s, "/dev/"))
@@ -2578,8 +2629,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
                 p = s;
 
         b = strdup(p);
-        free(s);
 
+finish:
         if (!b)
                 return -ENOMEM;
 
@@ -5902,7 +5953,7 @@ int proc_cmdline(char **ret) {
         int r;
 
         if (detect_container(NULL) > 0) {
-                char *buf, *p;
+                char *buf = NULL, *p;
                 size_t sz = 0;
 
                 r = read_full_file("/proc/1/cmdline", &buf, &sz);
@@ -6192,3 +6243,51 @@ int fd_warn_permissions(const char *path, int fd) {
 
         return 0;
 }
+
+unsigned long personality_from_string(const char *p) {
+
+        /* Parse a personality specifier. We introduce our own
+         * identifiers that indicate specific ABIs, rather than just
+         * hints regarding the register size, since we want to keep
+         * things open for multiple locally supported ABIs for the
+         * same register size. We try to reuse the ABI identifiers
+         * used by libseccomp. */
+
+#if defined(__x86_64__)
+
+        if (streq(p, "x86"))
+                return PER_LINUX32;
+
+        if (streq(p, "x86-64"))
+                return PER_LINUX;
+
+#elif defined(__i386__)
+
+        if (streq(p, "x86"))
+                return PER_LINUX;
+#endif
+
+        /* personality(7) documents that 0xffffffffUL is used for
+         * querying the current personality, hence let's use that here
+         * as error indicator. */
+        return 0xffffffffUL;
+}
+
+const char* personality_to_string(unsigned long p) {
+
+#if defined(__x86_64__)
+
+        if (p == PER_LINUX32)
+                return "x86";
+
+        if (p == PER_LINUX)
+                return "x86-64";
+
+#elif defined(__i386__)
+
+        if (p == PER_LINUX)
+                return "x86";
+#endif
+
+        return NULL;
+}