chiark / gitweb /
shared - reinstate plymouth_running()
[elogind.git] / src / shared / util.c
index 4a970af3b1972f3a28abe28d878c50b2966d1875..5cbbe8fb7d2886ffbbe36fab7cde07aba50fbe2c 100644 (file)
@@ -94,6 +94,9 @@
 #include "def.h"
 #include "sparse-endian.h"
 
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
+
 int saved_argc = 0;
 char **saved_argv = NULL;
 
@@ -2326,6 +2329,17 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
         return n;
 }
 
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+        ssize_t n;
+
+        n = loop_read(fd, buf, nbytes, do_poll);
+        if (n < 0)
+                return n;
+        if ((size_t) n != nbytes)
+                return -EIO;
+        return 0;
+}
+
 int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
         const uint8_t *p = buf;
 
@@ -2580,8 +2594,9 @@ char* dirname_malloc(const char *path) {
 
 int dev_urandom(void *p, size_t n) {
         static int have_syscall = -1;
-        int r, fd;
-        ssize_t k;
+
+        _cleanup_close_ int fd = -1;
+        int r;
 
         /* Gathers some randomness from the kernel. This call will
          * never block, and will always return some data from the
@@ -2616,22 +2631,14 @@ int dev_urandom(void *p, size_t n) {
                                 return -errno;
                 } else
                         /* too short read? */
-                        return -EIO;
+                        return -ENODATA;
         }
 
         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)
-                return -EIO;
-
-        return 0;
+        return loop_read_exact(fd, p, n, true);
 }
 
 void initialize_srand(void) {
@@ -2919,31 +2926,30 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
 
                 /* This is an ugly hack */
                 if (major(devnr) == 136) {
-                        asprintf(&b, "pts/%u", minor(devnr));
-                        goto finish;
-                }
+                        if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
+                                return -ENOMEM;
+                } else {
+                        /* Probably something like the ptys which have no
+                         * symlink in /dev/char. Let's return something
+                         * vaguely useful. */
 
-                /* Probably something like the ptys which have no
-                 * symlink in /dev/char. Let's return something
-                 * vaguely useful. */
+                        b = strdup(fn + 5);
+                        if (!b)
+                                return -ENOMEM;
+                }
+        } else {
+                if (startswith(s, "/dev/"))
+                        p = s + 5;
+                else if (startswith(s, "../"))
+                        p = s + 3;
+                else
+                        p = s;
 
-                b = strdup(fn + 5);
-                goto finish;
+                b = strdup(p);
+                if (!b)
+                        return -ENOMEM;
         }
 
-        if (startswith(s, "/dev/"))
-                p = s + 5;
-        else if (startswith(s, "../"))
-                p = s + 3;
-        else
-                p = s;
-
-        b = strdup(p);
-
-finish:
-        if (!b)
-                return -ENOMEM;
-
         *r = b;
         if (_devnr)
                 *_devnr = devnr;
@@ -4115,8 +4121,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
                         if (null_or_empty_path(path)) {
                                 log_debug("%s is empty (a mask).", path);
                                 continue;
-                        } else
-                                log_debug("%s will be executed.", path);
+                        }
 
                         pid = fork();
                         if (pid < 0) {
@@ -5999,7 +6004,7 @@ int on_ac_power(void) {
 
         d = opendir("/sys/class/power_supply");
         if (!d)
-                return -errno;
+                return errno == ENOENT ? true : -errno;
 
         for (;;) {
                 struct dirent *de;
@@ -6664,7 +6669,7 @@ int getpeersec(int fd, char **ret) {
 
         if (isempty(s)) {
                 free(s);
-                return -ENOTSUP;
+                return -EOPNOTSUPP;
         }
 
         *ret = s;
@@ -8118,3 +8123,44 @@ void cmsg_close_all(struct msghdr *mh) {
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
                         close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
 }
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+        struct stat buf;
+        int ret;
+
+        ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+        if (ret >= 0)
+                return 0;
+
+        /* Even though renameat2() exists since Linux 3.15, btrfs added
+         * support for it later. If it is not implemented, fallback to another
+         * method. */
+        if (errno != EINVAL)
+                return -errno;
+
+        /* The link()/unlink() fallback does not work on directories. But
+         * renameat() without RENAME_NOREPLACE gives the same semantics on
+         * directories, except when newpath is an *empty* directory. This is
+         * good enough. */
+        ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+        if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+                ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+                return ret >= 0 ? 0 : -errno;
+        }
+
+        /* If it is not a directory, use the link()/unlink() fallback. */
+        ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+        if (ret < 0)
+                return -errno;
+
+        ret = unlinkat(olddirfd, oldpath, 0);
+        if (ret < 0) {
+                /* backup errno before the following unlinkat() alters it */
+                ret = errno;
+                (void) unlinkat(newdirfd, newpath, 0);
+                errno = ret;
+                return -errno;
+        }
+
+        return 0;
+}