chiark / gitweb /
util: unify how we see srand()
[elogind.git] / src / shared / util.c
index fdcf571..0f44eb5 100644 (file)
@@ -1878,9 +1878,6 @@ int open_terminal(const char *name, int mode) {
                 c++;
         }
 
-        if (fd < 0)
-                return -errno;
-
         r = isatty(fd);
         if (r < 0) {
                 safe_close(fd);
@@ -2077,7 +2074,7 @@ int acquire_terminal(
                  * ended our handle will be dead. It's important that
                  * we do this after sleeping, so that we don't enter
                  * an endless loop. */
-                safe_close(fd);
+                fd = safe_close(fd);
         }
 
         safe_close(notify);
@@ -2469,14 +2466,53 @@ char* dirname_malloc(const char *path) {
 }
 
 int dev_urandom(void *p, size_t n) {
-        _cleanup_close_ int fd;
+        static int have_syscall = -1;
+        int r, fd;
         ssize_t k;
 
+        /* Gathers some randomness from the kernel. This call will
+         * never block, and will always return some data from the
+         * kernel, regardless if the random pool is fully initialized
+         * or not. It thus makes no guarantee for the quality of the
+         * returned entropy, but is good enough for or usual usecases
+         * of seeding the hash functions for hashtable */
+
+        /* Use the getrandom() syscall unless we know we don't have
+         * it, or when the requested size is too large for it. */
+        if (have_syscall != 0 || (size_t) (int) n != n) {
+                r = getrandom(p, n, GRND_NONBLOCK);
+                if (r == (int) n) {
+                        have_syscall = true;
+                        return 0;
+                }
+
+                if (r < 0) {
+                        if (errno == ENOSYS)
+                                /* we lack the syscall, continue with
+                                 * reading from /dev/urandom */
+                                have_syscall = false;
+                        else if (errno == EAGAIN)
+                                /* not enough entropy for now. Let's
+                                 * remember to use the syscall the
+                                 * next time, again, but also read
+                                 * from /dev/urandom for now, which
+                                 * doesn't care about the current
+                                 * amount of entropy.  */
+                                have_syscall = true;
+                        else
+                                return -errno;
+                } else
+                        /* too short read? */
+                        return -EIO;
+        }
+
         fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
         if (fd < 0)
                 return errno == ENOENT ? -ENOSYS : -errno;
 
         k = loop_read(fd, p, n, true);
+        safe_close(fd);
+
         if (k < 0)
                 return (int) k;
         if ((size_t) k != n)
@@ -2485,8 +2521,36 @@ int dev_urandom(void *p, size_t n) {
         return 0;
 }
 
-void random_bytes(void *p, size_t n) {
+void initialize_srand(void) {
         static bool srand_called = false;
+        unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+        void *auxv;
+#endif
+
+        if (srand_called)
+                return;
+
+        x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+        /* The kernel provides us with a bit of entropy in auxv, so
+         * let's try to make use of that to seed the pseudo-random
+         * generator. It's better than nothing... */
+
+        auxv = (void*) getauxval(AT_RANDOM);
+        if (auxv)
+                x ^= *(unsigned*) auxv;
+#endif
+
+        x ^= (unsigned) now(CLOCK_REALTIME);
+        x ^= (unsigned) gettid();
+
+        srand(x);
+        srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
         uint8_t *q;
         int r;
 
@@ -2497,28 +2561,7 @@ void random_bytes(void *p, size_t n) {
         /* If some idiot made /dev/urandom unavailable to us, he'll
          * get a PRNG instead. */
 
-        if (!srand_called) {
-                unsigned x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
-                /* The kernel provides us with a bit of entropy in
-                 * auxv, so let's try to make use of that to seed the
-                 * pseudo-random generator. It's better than
-                 * nothing... */
-
-                void *auxv;
-
-                auxv = (void*) getauxval(AT_RANDOM);
-                if (auxv)
-                        x ^= *(unsigned*) auxv;
-#endif
-
-                x ^= (unsigned) now(CLOCK_REALTIME);
-                x ^= (unsigned) gettid();
-
-                srand(x);
-                srand_called = true;
-        }
+        initialize_srand();
 
         for (q = p; q < (uint8_t*) p + n; q ++)
                 *q = rand();
@@ -2604,7 +2647,7 @@ bool hostname_is_set(void) {
         return !isempty(u.nodename) && !streq(u.nodename, "(none)");
 }
 
-static char *lookup_uid(uid_t uid) {
+char *lookup_uid(uid_t uid) {
         long bufsize;
         char *name;
         _cleanup_free_ char *buf = NULL;
@@ -3276,7 +3319,7 @@ unsigned columns(void) {
         c = 0;
         e = getenv("COLUMNS");
         if (e)
-                safe_atoi(e, &c);
+                (void) safe_atoi(e, &c);
 
         if (c <= 0)
                 c = fd_columns(STDOUT_FILENO);
@@ -3310,7 +3353,7 @@ unsigned lines(void) {
         l = 0;
         e = getenv("LINES");
         if (e)
-                safe_atou(e, &l);
+                (void) safe_atou(e, &l);
 
         if (l <= 0)
                 l = fd_lines(STDOUT_FILENO);
@@ -3911,7 +3954,7 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv
                         }
                 }
 
-                pids = hashmap_new(NULL, NULL);
+                pids = hashmap_new(NULL);
                 if (!pids) {
                         log_oom();
                         _exit(EXIT_FAILURE);
@@ -4981,24 +5024,6 @@ bool kexec_loaded(void) {
        return loaded;
 }
 
-int strdup_or_null(const char *a, char **b) {
-        char *c;
-
-        assert(b);
-
-        if (!a) {
-                *b = NULL;
-                return 0;
-        }
-
-        c = strdup(a);
-        if (!c)
-                return -ENOMEM;
-
-        *b = c;
-        return 0;
-}
-
 int prot_from_flags(int flags) {
 
         switch (flags & O_ACCMODE) {
@@ -5102,9 +5127,9 @@ int fd_inc_rcvbuf(int fd, size_t n) {
 }
 
 int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
-        pid_t parent_pid, agent_pid;
-        int fd;
         bool stdout_is_tty, stderr_is_tty;
+        pid_t parent_pid, agent_pid;
+        sigset_t ss, saved_ss;
         unsigned n, i;
         va_list ap;
         char **l;
@@ -5112,16 +5137,25 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
         assert(pid);
         assert(path);
 
-        parent_pid = getpid();
-
         /* Spawns a temporary TTY agent, making sure it goes away when
          * we go away */
 
+        parent_pid = getpid();
+
+        /* First we temporarily block all signals, so that the new
+         * child has them blocked initially. This way, we can be sure
+         * that SIGTERMs are not lost we might send to the agent. */
+        assert_se(sigfillset(&ss) >= 0);
+        assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
+
         agent_pid = fork();
-        if (agent_pid < 0)
+        if (agent_pid < 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
                 return -errno;
+        }
 
         if (agent_pid != 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
                 *pid = agent_pid;
                 return 0;
         }
@@ -5132,24 +5166,26 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
         if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
                 _exit(EXIT_FAILURE);
 
+        /* Make sure we actually can kill the agent, if we need to, in
+         * case somebody invoked us from a shell script that trapped
+         * SIGTERM or so... */
+        reset_all_signal_handlers();
+        reset_signal_mask();
+
         /* Check whether our parent died before we were able
-         * to set the death signal */
+         * to set the death signal and unblock the signals */
         if (getppid() != parent_pid)
                 _exit(EXIT_SUCCESS);
 
         /* Don't leak fds to the agent */
         close_all_fds(except, n_except);
 
-        /* Make sure we actually can kill the agent, if we need to, in
-         * case somebody invoked us from a shell script that trapped
-         * SIGTERM or so... */
-        reset_all_signal_handlers();
-        reset_signal_mask();
-
         stdout_is_tty = isatty(STDOUT_FILENO);
         stderr_is_tty = isatty(STDERR_FILENO);
 
         if (!stdout_is_tty || !stderr_is_tty) {
+                int fd;
+
                 /* Detach from stdout/stderr. and reopen
                  * /dev/tty for them. This is important to
                  * ensure that when systemctl is started via
@@ -6701,7 +6737,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
 
         path_kill_slashes(cleaned);
 
-        done = set_new(string_hash_func, string_compare_func);
+        done = set_new(&string_hash_ops);
         if (!done)
                 return -ENOMEM;
 
@@ -6711,7 +6747,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
                 bool top_autofs = false;
                 char *x;
 
-                todo = set_new(string_hash_func, string_compare_func);
+                todo = set_new(&string_hash_ops);
                 if (!todo)
                         return -ENOMEM;
 
@@ -6943,10 +6979,21 @@ int is_symlink(const char *path) {
         if (lstat(path, &info) < 0)
                 return -errno;
 
-        if (S_ISLNK(info.st_mode))
-                return 1;
+        return !!S_ISLNK(info.st_mode);
+}
 
-        return 0;
+int is_dir(const char* path, bool follow) {
+        struct stat st;
+
+        if (follow) {
+                if (stat(path, &st) < 0)
+                        return -errno;
+        } else {
+                if (lstat(path, &st) < 0)
+                        return -errno;
+        }
+
+        return !!S_ISDIR(st.st_mode);
 }
 
 int unquote_first_word(const char **p, char **ret) {
@@ -7174,3 +7221,23 @@ int free_and_strdup(char **p, const char *s) {
 
         return 0;
 }
+
+int sethostname_idempotent(const char *s) {
+        int r;
+        char buf[HOST_NAME_MAX + 1] = {};
+
+        assert(s);
+
+        r = gethostname(buf, sizeof(buf));
+        if (r < 0)
+                return -errno;
+
+        if (streq(buf, s))
+                return 0;
+
+        r = sethostname(s, strlen(s));
+        if (r < 0)
+                return -errno;
+
+        return 1;
+}