chiark / gitweb /
systemctl: add "systemctl cat"
[elogind.git] / src / shared / fileio.c
index 23bc742e75ad71c0674d3345cf580aa0fb693331..ac1b409a1c7efa1c4aaf274055922a0d7508cd88 100644 (file)
 ***/
 
 #include <unistd.h>
+#include <sys/sendfile.h>
 #include "fileio.h"
 #include "util.h"
 #include "strv.h"
 #include "utf8.h"
+#include "ctype.h"
 
 int write_string_to_file(FILE *f, const char *line) {
         errno = 0;
@@ -116,6 +118,77 @@ int read_one_line_file(const char *fn, char **line) {
         return 0;
 }
 
+ssize_t sendfile_full(int out_fd, const char *fn) {
+        _cleanup_fclose_ FILE *f;
+        struct stat st;
+        int r;
+        ssize_t s;
+
+        size_t n, l;
+        _cleanup_free_ char *buf = NULL;
+
+        assert(out_fd > 0);
+        assert(fn);
+
+        f = fopen(fn, "r");
+        if (!f)
+                return -errno;
+
+        r = fstat(fileno(f), &st);
+        if (r < 0)
+                return -errno;
+
+        s = sendfile(out_fd, fileno(f), NULL, st.st_size);
+        if (s < 0)
+                if (errno == EINVAL || errno == ENOSYS) {
+                        /* continue below */
+                } else
+                        return -errno;
+        else
+                return s;
+
+        /* sendfile() failed, fall back to read/write */
+
+        /* Safety check */
+        if (st.st_size > 4*1024*1024)
+                return -E2BIG;
+
+        n = st.st_size > 0 ? st.st_size : LINE_MAX;
+        l = 0;
+
+        while (true) {
+                char *t;
+                size_t k;
+
+                t = realloc(buf, n);
+                if (!t)
+                        return -ENOMEM;
+
+                buf = t;
+                k = fread(buf + l, 1, n - l, f);
+
+                if (k <= 0) {
+                        if (ferror(f))
+                                return -errno;
+
+                        break;
+                }
+
+                l += k;
+                n *= 2;
+
+                /* Safety check */
+                if (n > 4*1024*1024)
+                        return -E2BIG;
+        }
+
+        r = write(out_fd, buf, l);
+        if (r < 0)
+                return -errno;
+
+        return (ssize_t) l;
+}
+
 int read_full_file(const char *fn, char **contents, size_t *size) {
         _cleanup_fclose_ FILE *f = NULL;
         size_t n, l;
@@ -167,7 +240,6 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
 
         buf[l] = 0;
         *contents = buf;
-        buf = NULL;
 
         if (size)
                 *size = l;
@@ -661,6 +733,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
         int r;
 
         assert(filename);
+        assert(pattern);
         assert(field);
 
         r = read_full_file(filename, &status, NULL);
@@ -672,12 +745,20 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
                 return -ENOENT;
 
         t += strlen(pattern);
-        /* Also skip zeros, because when this is used for capabilities,
-         * we don't want the zeros. This way the same cabality set
-         * always maps to the same string, irrespective of the total
-         * capability set size. For other numbers it shouldn't matter.
-         */
-        t += strspn(t, WHITESPACE "0");
+        if (*t) {
+                t += strspn(t, " \t");
+
+                /* Also skip zeros, because when this is used for
+                 * capabilities, we don't want the zeros. This way the
+                 * same capability set always maps to the same string,
+                 * irrespective of the total capability set size. For
+                 * other numbers it shouldn't matter. */
+                t += strspn(t, "0");
+                /* Back off one char if there's nothing but whitespace
+                   and zeros */
+                if (!*t || isspace(*t))
+                        t --;
+        }
 
         len = strcspn(t, WHITESPACE);