chiark / gitweb /
Prep v234: Apply missing upstream fixes in src/basic (1/6)
[elogind.git] / src / basic / fileio.c
index 88fd7d09436f7d6d52e8fd7e3650c16ef1815854..7bafe9038cc139ad542182a937083033faa0f580 100644 (file)
@@ -37,6 +37,7 @@
 #include "hexdecoct.h"
 //#include "log.h"
 //#include "macro.h"
+#include "missing.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "random-util.h"
@@ -49,7 +50,7 @@
 
 #define READ_FULL_BYTES_MAX (4U*1024U*1024U)
 
-int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
+int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts) {
 
         assert(f);
         assert(line);
@@ -58,6 +59,13 @@ int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
         if (enforce_newline && !endswith(line, "\n"))
                 fputc('\n', f);
 
+        if (ts) {
+                struct timespec twice[2] = {*ts, *ts};
+
+                if (futimens(fileno(f), twice) < 0)
+                        return -errno;
+        }
+
         return fflush_and_check(f);
 }
 
@@ -87,7 +95,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor
         return r;
 }
 
-int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
+int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) {
         _cleanup_fclose_ FILE *f = NULL;
         int q, r;
 
@@ -102,7 +110,8 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla
                         goto fail;
 
                 return r;
-        }
+        } else
+                assert(ts == NULL);
 
         if (flags & WRITE_STRING_FILE_CREATE) {
                 f = fopen(fn, "we");
@@ -129,7 +138,7 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla
                 }
         }
 
-        r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+        r = write_string_stream_ts(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE), ts);
         if (r < 0)
                 goto fail;
 
@@ -552,13 +561,14 @@ static int parse_env_file_internal(
                 }
         }
 
-        if (state == PRE_VALUE ||
-            state == VALUE ||
-            state == VALUE_ESCAPE ||
-            state == SINGLE_QUOTE_VALUE ||
-            state == SINGLE_QUOTE_VALUE_ESCAPE ||
-            state == DOUBLE_QUOTE_VALUE ||
-            state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+        if (IN_SET(state,
+                   PRE_VALUE,
+                   VALUE,
+                   VALUE_ESCAPE,
+                   SINGLE_QUOTE_VALUE,
+                   SINGLE_QUOTE_VALUE_ESCAPE,
+                   DOUBLE_QUOTE_VALUE,
+                   DOUBLE_QUOTE_VALUE_ESCAPE)) {
 
                 key[n_key] = 0;
 
@@ -585,14 +595,9 @@ fail:
         return r;
 }
 
-static int parse_env_file_push(
+static int check_utf8ness_and_warn(
                 const char *filename, unsigned line,
-                const char *key, char *value,
-                void *userdata,
-                int *n_pushed) {
-
-        const char *k;
-        va_list aq, *ap = userdata;
+                const char *key, char *value) {
 
         if (!utf8_is_valid(key)) {
                 _cleanup_free_ char *p = NULL;
@@ -610,6 +615,23 @@ static int parse_env_file_push(
                 return -EINVAL;
         }
 
+        return 0;
+}
+
+static int parse_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+
+        const char *k;
+        va_list aq, *ap = userdata;
+        int r;
+
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
+
         va_copy(aq, *ap);
 
         while ((k = va_arg(aq, const char *))) {
@@ -662,27 +684,19 @@ static int load_env_file_push(
         char *p;
         int r;
 
-        if (!utf8_is_valid(key)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(key);
-
-                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
-                return -EINVAL;
-        }
-
-        if (value && !utf8_is_valid(value)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(value);
-
-                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
-                return -EINVAL;
-        }
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
 
-        p = strjoin(key, "=", strempty(value), NULL);
+        p = strjoin(key, "=", value);
         if (!p)
                 return -ENOMEM;
 
-        r = strv_consume(m, p);
-        if (r < 0)
+        r = strv_env_replace(m, p);
+        if (r < 0) {
+                free(p);
                 return r;
+        }
 
         if (n_pushed)
                 (*n_pushed)++;
@@ -707,6 +721,7 @@ int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
         *rl = m;
         return 0;
 }
+#endif // 0
 
 static int load_env_file_push_pairs(
                 const char *filename, unsigned line,
@@ -716,19 +731,9 @@ static int load_env_file_push_pairs(
         char ***m = userdata;
         int r;
 
-        if (!utf8_is_valid(key)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(key);
-
-                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
-                return -EINVAL;
-        }
-
-        if (value && !utf8_is_valid(value)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(value);
-
-                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
-                return -EINVAL;
-        }
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
 
         r = strv_extend(m, key);
         if (r < 0)
@@ -766,6 +771,53 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
         *rl = m;
         return 0;
 }
+#if 0 /// UNNEEDED by elogind
+
+static int merge_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+
+        char ***env = userdata;
+        char *expanded_value;
+
+        assert(env);
+
+        if (!value) {
+                log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
+                return 0;
+        }
+
+        if (!env_name_is_valid(key)) {
+                log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
+                free(value);
+                return 0;
+        }
+
+        expanded_value = replace_env(value, *env,
+                                     REPLACE_ENV_USE_ENVIRONMENT|
+                                     REPLACE_ENV_ALLOW_BRACELESS|
+                                     REPLACE_ENV_ALLOW_EXTENDED);
+        if (!expanded_value)
+                return -ENOMEM;
+
+        free_and_replace(value, expanded_value);
+
+        return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+                char ***env,
+                FILE *f,
+                const char *fname) {
+
+        /* NOTE: this function supports braceful and braceless variable expansions,
+         * plus "extended" substitutions, unlike other exported parsing functions.
+         */
+
+        return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
+}
 
 static void write_env_var(FILE *f, const char *v) {
         const char *p;
@@ -965,9 +1017,9 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
                 FILE *f;
 
                 if (root)
-                        p = strjoin(root, *i, "/", path, NULL);
+                        p = strjoin(root, *i, "/", path);
                 else
-                        p = strjoin(*i, "/", path, NULL);
+                        p = strjoin(*i, "/", path);
                 if (!p)
                         return -ENOMEM;
 
@@ -1046,7 +1098,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         if (r < 0)
                 return r;
 
-        fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+        fd = mkostemp_safe(t);
         if (fd < 0) {
                 free(t);
                 return -errno;
@@ -1079,7 +1131,7 @@ int fflush_and_check(FILE *f) {
 }
 
 /* This is much like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
+int mkostemp_safe(char *pattern) {
         _cleanup_umask_ mode_t u = 0;
         int fd;
 
@@ -1087,7 +1139,7 @@ int mkostemp_safe(char *pattern, int flags) {
 
         u = umask(077);
 
-        fd = mkostemp(pattern, flags);
+        fd = mkostemp(pattern, O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
@@ -1272,6 +1324,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
 
         return fputs(s, f);
 }
+#endif // 0
 
 int open_tmpfile_unlinkable(const char *directory, int flags) {
         char *p;
@@ -1285,17 +1338,15 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
 
         /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
 
-#ifdef O_TMPFILE
         /* Try O_TMPFILE first, if it is supported */
         fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
         if (fd >= 0)
                 return fd;
-#endif
 
         /* Fall back to unguessable name + unlinking */
         p = strjoina(directory, "/systemd-tmp-XXXXXX");
 
-        fd = mkostemp_safe(p, flags);
+        fd = mkostemp_safe(p);
         if (fd < 0)
                 return fd;
 
@@ -1304,6 +1355,7 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
         return fd;
 }
 
+#if 0 /// UNNEEDED by elogind
 int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         _cleanup_free_ char *tmp = NULL;
         int r, fd;
@@ -1318,7 +1370,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
          * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
          * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
 
-#ifdef O_TMPFILE
         {
                 _cleanup_free_ char *dn = NULL;
 
@@ -1334,7 +1385,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
 
                 log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
         }
-#endif
 
         r = tempfn_random(target, NULL, &tmp);
         if (r < 0)
@@ -1349,7 +1399,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
 
         return fd;
 }
+#endif // 0
+
+int open_serialization_fd(const char *ident) {
+        int fd = -1;
+
+        fd = memfd_create(ident, MFD_CLOEXEC);
+        if (fd < 0) {
+                const char *path;
+
+                path = getpid() == 1 ? "/run/systemd" : "/tmp";
+                fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+                if (fd < 0)
+                        return fd;
+
+                log_debug("Serializing %s to %s.", ident, path);
+        } else
+                log_debug("Serializing %s to memfd.", ident);
+
+        return fd;
+}
 
+#if 0 /// UNNEEDED by elogind
 int link_tmpfile(int fd, const char *path, const char *target) {
 
         assert(fd >= 0);
@@ -1376,7 +1447,6 @@ int link_tmpfile(int fd, const char *path, const char *target) {
 
         return 0;
 }
-#endif // 0
 
 int read_nul_string(FILE *f, char **ret) {
         _cleanup_free_ char *x = NULL;
@@ -1418,3 +1488,23 @@ int read_nul_string(FILE *f, char **ret) {
 
         return 0;
 }
+
+int mkdtemp_malloc(const char *template, char **ret) {
+        char *p;
+
+        assert(template);
+        assert(ret);
+
+        p = strdup(template);
+        if (!p)
+                return -ENOMEM;
+
+        if (!mkdtemp(p)) {
+                free(p);
+                return -errno;
+        }
+
+        *ret = p;
+        return 0;
+}
+#endif // 0