chiark / gitweb /
when resetting signal handlers, set them to SA_RESTART
[elogind.git] / util.c
diff --git a/util.c b/util.c
index 4bec220f7a1dbfad1c1868d8228d973bb77863df..654b93d79c084ceb25b944aa3e62a1112204303a 100644 (file)
--- a/util.c
+++ b/util.c
@@ -5,6 +5,8 @@
 #include <unistd.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
 
 #include "macro.h"
 #include "util.h"
@@ -81,7 +83,7 @@ bool startswith(const char *s, const char *prefix) {
         return memcmp(s, prefix, pl) == 0;
 }
 
-int nointr_close(int fd) {
+int close_nointr(int fd) {
         assert(fd >= 0);
 
         for (;;) {
@@ -95,12 +97,20 @@ int nointr_close(int fd) {
         }
 }
 
+void close_nointr_nofail(int fd) {
+
+        /* like close_nointr() but cannot fail, and guarantees errno
+         * is unchanged */
+
+        assert_se(close_nointr(fd) == 0);
+}
+
 int parse_boolean(const char *v) {
         assert(v);
 
-        if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
                 return 1;
-        else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
                 return 0;
 
         return -EINVAL;
@@ -108,7 +118,7 @@ int parse_boolean(const char *v) {
 
 int safe_atou(const char *s, unsigned *ret_u) {
         char *x = NULL;
-        unsigned l;
+        unsigned long l;
 
         assert(s);
         assert(ret_u);
@@ -119,7 +129,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
         if (!x || *x || errno)
                 return errno ? -errno : -EINVAL;
 
-        if ((unsigned) l != l)
+        if ((unsigned long) (unsigned) l != l)
                 return -ERANGE;
 
         *ret_u = (unsigned) l;
@@ -128,7 +138,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
 
 int safe_atoi(const char *s, int *ret_i) {
         char *x = NULL;
-        int l;
+        long l;
 
         assert(s);
         assert(ret_i);
@@ -139,15 +149,80 @@ int safe_atoi(const char *s, int *ret_i) {
         if (!x || *x || errno)
                 return errno ? -errno : -EINVAL;
 
-        if ((int) l != l)
+        if ((long) (int) l != l)
                 return -ERANGE;
 
-        *ret_i = (unsigned) l;
+        *ret_i = (int) l;
+        return 0;
+}
+
+int safe_atolu(const char *s, long unsigned *ret_lu) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret_lu);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_lu = l;
+        return 0;
+}
+
+int safe_atoli(const char *s, long int *ret_li) {
+        char *x = NULL;
+        long l;
+
+        assert(s);
+        assert(ret_li);
+
+        errno = 0;
+        l = strtol(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_li = l;
+        return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+        char *x = NULL;
+        unsigned long long l;
+
+        assert(s);
+        assert(ret_llu);
+
+        errno = 0;
+        l = strtoull(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_llu = l;
         return 0;
 }
 
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n"
+int safe_atolli(const char *s, long long int *ret_lli) {
+        char *x = NULL;
+        long long l;
+
+        assert(s);
+        assert(ret_lli);
+
+        errno = 0;
+        l = strtoll(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_lli = l;
+        return 0;
+}
 
 /* Split a string into words. */
 char *split_spaces(const char *c, size_t *l, char **state) {
@@ -164,3 +239,311 @@ char *split_spaces(const char *c, size_t *l, char **state) {
 
         return (char*) current;
 }
+
+/* Split a string into words, but consider strings enclosed in '' and
+ * "" as words even if they include spaces. */
+char *split_quoted(const char *c, size_t *l, char **state) {
+        char *current;
+
+        current = *state ? *state : (char*) c;
+
+        if (!*current || *c == 0)
+                return NULL;
+
+        current += strspn(current, WHITESPACE);
+
+        if (*current == '\'') {
+                current ++;
+                *l = strcspn(current, "'");
+                *state = current+*l;
+
+                if (**state == '\'')
+                        (*state)++;
+        } else if (*current == '\"') {
+                current ++;
+                *l = strcspn(current, "\"");
+                *state = current+*l;
+
+                if (**state == '\"')
+                        (*state)++;
+        } else {
+                *l = strcspn(current, WHITESPACE);
+                *state = current+*l;
+        }
+
+        /* FIXME: Cannot deal with strings that have spaces AND ticks
+         * in them */
+
+        return (char*) current;
+}
+
+const char *sigchld_code(int code) {
+
+        if (code == CLD_EXITED)
+                return "exited";
+        else if (code == CLD_KILLED)
+                return "killed";
+        else if (code == CLD_DUMPED)
+                return "dumped";
+        else if (code == CLD_TRAPPED)
+                return "trapped";
+        else if (code == CLD_STOPPED)
+                return "stopped";
+        else if (code == CLD_CONTINUED)
+                return "continued";
+
+        return "unknown";
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+        int r;
+        FILE *f;
+        char fn[132], line[256], *p;
+        long long unsigned ppid;
+
+        assert(pid >= 0);
+        assert(_ppid);
+
+        assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/stat", (unsigned long long) pid) < (int) (sizeof(fn)-1));
+        fn[sizeof(fn)-1] = 0;
+
+        if (!(f = fopen(fn, "r")))
+                return -errno;
+
+        if (!(fgets(line, sizeof(line), f))) {
+                r = -errno;
+                fclose(f);
+                return r;
+        }
+
+        fclose(f);
+
+        /* Let's skip the pid and comm fields. The latter is enclosed
+         * in () but does not escape any () in its value, so let's
+         * skip over it manually */
+
+        if (!(p = strrchr(line, ')')))
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%llu ", /* ppid */
+                   &ppid) != 1)
+                return -EIO;
+
+        if ((long long unsigned) (pid_t) ppid != ppid)
+                return -ERANGE;
+
+        *_ppid = (pid_t) ppid;
+
+        return 0;
+}
+
+int write_one_line_file(const char *fn, const char *line) {
+        FILE *f;
+        int r;
+
+        assert(fn);
+        assert(line);
+
+        if (!(f = fopen(fn, "we")))
+                return -errno;
+
+        if (fputs(line, f) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        r = 0;
+finish:
+        fclose(f);
+        return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+        FILE *f;
+        int r;
+        char t[64], *c;
+
+        assert(fn);
+        assert(line);
+
+        if (!(f = fopen(fn, "re")))
+                return -errno;
+
+        if (!(fgets(t, sizeof(t), f))) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!(c = strdup(t))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        *line = c;
+        r = 0;
+
+finish:
+        fclose(f);
+        return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+        size_t a, b;
+        char *r;
+
+        assert(s);
+        assert(suffix);
+
+        a = strlen(s);
+        b = strlen(suffix);
+
+        if (!(r = new(char, a+b+1)))
+                return NULL;
+
+        memcpy(r, s, a);
+        memcpy(r+a, suffix, b);
+        r[a+b] = 0;
+
+        return r;
+}
+
+int readlink_malloc(const char *p, char **r) {
+        size_t l = 100;
+
+        assert(p);
+        assert(r);
+
+        for (;;) {
+                char *c;
+                ssize_t n;
+
+                if (!(c = new(char, l)))
+                        return -ENOMEM;
+
+                if ((n = readlink(p, c, l-1)) < 0) {
+                        int ret = -errno;
+                        free(c);
+                        return ret;
+                }
+
+                if ((size_t) n < l-1) {
+                        c[n] = 0;
+                        *r = c;
+                        return 0;
+                }
+
+                free(c);
+                l *= 2;
+        }
+}
+
+char *file_name_from_path(const char *p) {
+        char *r;
+
+        assert(p);
+
+        if ((r = strrchr(p, '/')))
+                return r + 1;
+
+        return (char*) p;
+}
+
+bool path_is_absolute(const char *p) {
+        assert(p);
+
+        return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+
+        return !!strchr(p, '/');
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+        char *r;
+
+        assert(p);
+
+        if (path_is_absolute(p) || !prefix)
+                return strdup(p);
+
+        if (asprintf(&r, "%s/%s", prefix, p) < 0)
+                return NULL;
+
+        return r;
+}
+
+int reset_all_signal_handlers(void) {
+        int sig;
+
+        for (sig = 1; sig < _NSIG; sig++) {
+                struct sigaction sa;
+
+                if (sig == SIGKILL || sig == SIGSTOP)
+                        continue;
+
+                zero(sa);
+                sa.sa_handler = SIG_DFL;
+                sa.sa_flags = SA_RESTART;
+
+                /* On Linux the first two RT signals are reserved by
+                 * glibc, and sigaction() will return EINVAL for them. */
+                if ((sigaction(sig, &sa, NULL) < 0))
+                        if (errno != EINVAL)
+                                return -errno;
+        }
+
+    return 0;
+}
+
+char *strstrip(char *s) {
+        char *e, *l = NULL;
+
+        /* Drops trailing whitespace. Modifies the string in
+         * place. Returns pointer to first non-space character */
+
+        s += strspn(s, WHITESPACE);
+
+        for (e = s; *e; e++)
+                if (!strchr(WHITESPACE, *e))
+                        l = e;
+
+        if (l)
+                *(l+1) = 0;
+        else
+                *s = 0;
+
+        return s;
+
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+        char *e, *r;
+        size_t k;
+
+        assert(path);
+        assert(filename);
+
+        /* This removes the last component of path and appends
+         * filename, unless the latter is absolute anyway or the
+         * former isn't */
+
+        if (path_is_absolute(filename))
+                return strdup(filename);
+
+        if (!(e = strrchr(path, '/')))
+                return strdup(filename);
+
+        k = strlen(filename);
+        if (!(r = new(char, e-path+1+k+1)))
+                return NULL;
+
+        memcpy(r, path, e-path+1);
+        memcpy(r+(e-path)+1, filename, k+1);
+
+        return r;
+}