chiark / gitweb /
shared/util: Fix glob_extend() argument
[elogind.git] / src / shared / util.c
index 1dde8afcadfc3a24af14620219d148edba185e31..182277030440d34840ce9d1ad032736d25f5b6be 100644 (file)
@@ -73,6 +73,9 @@
 #include "hashmap.h"
 #include "env-util.h"
 #include "fileio.h"
+#include "device-nodes.h"
+#include "utf8.h"
+#include "gunicode.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -640,7 +643,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
 
         /* Kernel threads have no argv[] */
         if (r == NULL || r[0] == 0) {
-                char *t;
+                _cleanup_free_ char *t = NULL;
                 int h;
 
                 free(r);
@@ -653,8 +656,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                         return h;
 
                 r = strjoin("[", t, "]", NULL);
-                free(t);
-
                 if (!r)
                         return -ENOMEM;
         }
@@ -694,9 +695,6 @@ int is_kernel_thread(pid_t pid) {
 
 int get_process_capeff(pid_t pid, char **capeff) {
         const char *p;
-        _cleanup_free_ char *status = NULL;
-        char *t = NULL;
-        int r;
 
         assert(capeff);
         assert(pid >= 0);
@@ -706,25 +704,7 @@ int get_process_capeff(pid_t pid, char **capeff) {
         else
                 p = procfs_file_alloca(pid, "status");
 
-        r = read_full_file(p, &status, NULL);
-        if (r < 0)
-                return r;
-
-        t = strstr(status, "\nCapEff:\t");
-        if (!t)
-                return -ENOENT;
-
-        for (t += strlen("\nCapEff:\t"); t[0] == '0'; t++)
-                continue;
-
-        if (t[0] == '\n')
-                t--;
-
-        *capeff = strndup(t, strchr(t, '\n') - t);
-        if (!*capeff)
-                return -ENOMEM;
-
-        return 0;
+        return get_status_field(p, "\nCapEff:", capeff);
 }
 
 int get_process_exe(pid_t pid, char **name) {
@@ -2206,8 +2186,10 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
                                         return n > 0 ? n : -errno;
                                 }
 
-                                if (pollfd.revents != POLLIN)
-                                        return n > 0 ? n : -EIO;
+                                /* We knowingly ignore the revents value here,
+                                 * and expect that any error/EOF is reported
+                                 * via read()/write()
+                                 */
 
                                 continue;
                         }
@@ -2254,8 +2236,10 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
                                         return n > 0 ? n : -errno;
                                 }
 
-                                if (pollfd.revents != POLLOUT)
-                                        return n > 0 ? n : -EIO;
+                                /* We knowingly ignore the revents value here,
+                                 * and expect that any error/EOF is reported
+                                 * via read()/write()
+                                 */
 
                                 continue;
                         }
@@ -2440,6 +2424,25 @@ fallback:
         return random() * RAND_MAX + random();
 }
 
+unsigned random_u(void) {
+        _cleanup_close_ int fd;
+        unsigned u;
+        ssize_t r;
+
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                goto fallback;
+
+        r = loop_read(fd, &u, sizeof(u), true);
+        if (r != sizeof(u))
+                goto fallback;
+
+        return u;
+
+fallback:
+        return random() * RAND_MAX + random();
+}
+
 void rename_process(const char name[8]) {
         assert(name);
 
@@ -3285,7 +3288,7 @@ int running_in_chroot(void) {
                 a.st_ino != b.st_ino;
 }
 
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
         size_t x;
         char *r;
 
@@ -3316,6 +3319,80 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
         return r;
 }
 
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+        size_t x;
+        char *e;
+        const char *i, *j;
+        unsigned k, len, len2;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(new_length >= 3);
+
+        /* if no multibyte characters use ascii_ellipsize_mem for speed */
+        if (ascii_is_valid(s))
+                return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+        if (old_length <= 3 || old_length <= new_length)
+                return strndup(s, old_length);
+
+        x = (new_length * percent) / 100;
+
+        if (x > new_length - 3)
+                x = new_length - 3;
+
+        k = 0;
+        for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+                int c;
+
+                c = utf8_encoded_to_unichar(i);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+
+        if (k > x) /* last character was wide and went over quota */
+                x ++;
+
+        for (j = s + old_length; k < new_length && j > i; ) {
+                int c;
+
+                j = utf8_prev_char(j);
+                c = utf8_encoded_to_unichar(j);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+        assert(i <= j);
+
+        /* we don't actually need to ellipsize */
+        if (i == j)
+                return memdup(s, old_length + 1);
+
+        /* make space for ellipsis */
+        j = utf8_next_char(j);
+
+        len = i - s;
+        len2 = s + old_length - j;
+        e = new(char, len + 3 + len2 + 1);
+        if (!e)
+                return NULL;
+
+        /*
+        printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+               old_length, new_length, x, len, len2, k);
+        */
+
+        memcpy(e, s, len);
+        e[len]   = 0xe2; /* tri-dot ellipsis: … */
+        e[len + 1] = 0x80;
+        e[len + 2] = 0xa6;
+
+        memcpy(e + len + 3, j, len2 + 1);
+
+        return e;
+}
+
 char *ellipsize(const char *s, size_t length, unsigned percent) {
         return ellipsize_mem(s, strlen(s), length, percent);
 }
@@ -3516,26 +3593,23 @@ int signal_from_string_try_harder(const char *s) {
 }
 
 static char *tag_to_udev_node(const char *tagvalue, const char *by) {
-        char *dn, *t, *u;
-        int r;
-
-        /* FIXME: to follow udev's logic 100% we need to leave valid
-         * UTF8 chars unescaped */
+        _cleanup_free_ char *t = NULL, *u = NULL;
+        char *dn;
+        size_t enc_len;
 
         u = unquote(tagvalue, "\"\'");
         if (u == NULL)
                 return NULL;
 
-        t = xescape(u, "/ ");
-        free(u);
-
+        enc_len = strlen(u) * 4 + 1;
+        t = new(char, enc_len);
         if (t == NULL)
                 return NULL;
 
-        r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t);
-        free(t);
+        if (encode_devnode_name(u, t, enc_len) < 0)
+                return NULL;
 
-        if (r < 0)
+        if (asprintf(&dn, "/dev/disk/by-%s/%s", by, t) < 0)
                 return NULL;
 
         return dn;
@@ -4056,8 +4130,9 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-int copy_file(const char *from, const char *to) {
-        int r, fdf, fdt;
+int copy_file(const char *from, const char *to, int flags) {
+        _cleanup_close_ int fdf = -1;
+        int r, fdt;
 
         assert(from);
         assert(to);
@@ -4066,11 +4141,9 @@ int copy_file(const char *from, const char *to) {
         if (fdf < 0)
                 return -errno;
 
-        fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
-        if (fdt < 0) {
-                close_nointr_nofail(fdf);
+        fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
+        if (fdt < 0)
                 return -errno;
-        }
 
         for (;;) {
                 char buf[PIPE_BUF];
@@ -4080,7 +4153,6 @@ int copy_file(const char *from, const char *to) {
                 if (n < 0) {
                         r = -errno;
 
-                        close_nointr_nofail(fdf);
                         close_nointr(fdt);
                         unlink(to);
 
@@ -4095,15 +4167,13 @@ int copy_file(const char *from, const char *to) {
                 if (n != k) {
                         r = k < 0 ? k : (errno ? -errno : -EIO);
 
-                        close_nointr_nofail(fdf);
                         close_nointr(fdt);
-
                         unlink(to);
+
                         return r;
                 }
         }
 
-        close_nointr_nofail(fdf);
         r = close_nointr(fdt);
 
         if (r < 0) {
@@ -4391,7 +4461,7 @@ int glob_extend(char ***strv, const char *path) {
         char **p;
 
         errno = 0;
-        k = glob(optarg, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
 
         if (k == GLOB_NOMATCH)
                 return -ENOENT;
@@ -4435,38 +4505,31 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
 }
 
 int in_search_path(const char *path, char **search) {
-        char **i, *parent;
+        char **i;
+        _cleanup_free_ char *parent = NULL;
         int r;
 
         r = path_get_parent(path, &parent);
         if (r < 0)
                 return r;
 
-        r = 0;
-
-        STRV_FOREACH(i, search) {
-                if (path_equal(parent, *i)) {
-                        r = 1;
-                        break;
-                }
-        }
-
-        free(parent);
+        STRV_FOREACH(i, search)
+                if (path_equal(parent, *i))
+                        return 1;
 
-        return r;
+        return 0;
 }
 
 int get_files_in_directory(const char *path, char ***list) {
-        DIR *d;
-        int r = 0;
-        unsigned n = 0;
-        char **l = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        size_t bufsize = 0, n = 0;
+        _cleanup_strv_free_ char **l = NULL;
 
         assert(path);
 
         /* Returns all files in a directory in *list, and the number
          * of files as return value. If list is NULL returns only the
-         * number */
+         * number. */
 
         d = opendir(path);
         if (!d)
@@ -4478,11 +4541,9 @@ int get_files_in_directory(const char *path, char ***list) {
                 int k;
 
                 k = readdir_r(d, &buf.de, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
-                }
-
+                assert(k >= 0);
+                if (k > 0)
+                        return -k;
                 if (!de)
                         break;
 
@@ -4492,43 +4553,25 @@ int get_files_in_directory(const char *path, char ***list) {
                         continue;
 
                 if (list) {
-                        if ((unsigned) r >= n) {
-                                char **t;
-
-                                n = MAX(16, 2*r);
-                                t = realloc(l, sizeof(char*) * n);
-                                if (!t) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                l = t;
-                        }
-
-                        assert((unsigned) r < n);
+                        /* one extra slot is needed for the terminating NULL */
+                        if (!GREEDY_REALLOC(l, bufsize, n + 2))
+                                return -ENOMEM;
 
-                        l[r] = strdup(de->d_name);
-                        if (!l[r]) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        l[n] = strdup(de->d_name);
+                        if (!l[n])
+                                return -ENOMEM;
 
-                        l[++r] = NULL;
+                        l[++n] = NULL;
                 } else
-                        r++;
+                        n++;
         }
 
-finish:
-        if (d)
-                closedir(d);
-
-        if (r >= 0) {
-                if (list)
-                        *list = l;
-        } else
-                strv_free(l);
+        if (list) {
+                *list = l;
+                l = NULL; /* avoid freeing */
+        }
 
-        return r;
+        return n;
 }
 
 char *strjoin(const char *x, ...) {
@@ -5311,13 +5354,17 @@ bool string_is_safe(const char *p) {
         return true;
 }
 
+/**
+ * Check if a string contains control characters.
+ * Spaces and tabs are not considered control characters.
+ */
 bool string_has_cc(const char *p) {
         const char *t;
 
         assert(p);
 
         for (t = p; *t; t++)
-                if (*t > 0 && *t < ' ')
+                if (*t > 0 && *t < ' ' && *t != '\t')
                         return true;
 
         return false;