1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <fcntl.h>
4 
5 #include "alloc-util.h"
6 #include "env-util.h"
7 #include "escape.h"
8 #include "fileio.h"
9 #include "missing_mman.h"
10 #include "missing_syscall.h"
11 #include "parse-util.h"
12 #include "process-util.h"
13 #include "serialize.h"
14 #include "strv.h"
15 #include "tmpfile-util.h"
16 
serialize_item(FILE * f,const char * key,const char * value)17 int serialize_item(FILE *f, const char *key, const char *value) {
18         assert(f);
19         assert(key);
20 
21         if (!value)
22                 return 0;
23 
24         /* Make sure that anything we serialize we can also read back again with read_line() with a maximum line size
25          * of LONG_LINE_MAX. This is a safety net only. All code calling us should filter this out earlier anyway. */
26         if (strlen(key) + 1 + strlen(value) + 1 > LONG_LINE_MAX) {
27                 log_warning("Attempted to serialize overly long item '%s', refusing.", key);
28                 return -EINVAL;
29         }
30 
31         fputs(key, f);
32         fputc('=', f);
33         fputs(value, f);
34         fputc('\n', f);
35 
36         return 1;
37 }
38 
serialize_item_escaped(FILE * f,const char * key,const char * value)39 int serialize_item_escaped(FILE *f, const char *key, const char *value) {
40         _cleanup_free_ char *c = NULL;
41 
42         assert(f);
43         assert(key);
44 
45         if (!value)
46                 return 0;
47 
48         c = cescape(value);
49         if (!c)
50                 return log_oom();
51 
52         return serialize_item(f, key, c);
53 }
54 
serialize_item_format(FILE * f,const char * key,const char * format,...)55 int serialize_item_format(FILE *f, const char *key, const char *format, ...) {
56         char buf[LONG_LINE_MAX];
57         va_list ap;
58         int k;
59 
60         assert(f);
61         assert(key);
62         assert(format);
63 
64         va_start(ap, format);
65         k = vsnprintf(buf, sizeof(buf), format, ap);
66         va_end(ap);
67 
68         if (k < 0 || (size_t) k >= sizeof(buf) || strlen(key) + 1 + k + 1 > LONG_LINE_MAX) {
69                 log_warning("Attempted to serialize overly long item '%s', refusing.", key);
70                 return -EINVAL;
71         }
72 
73         fputs(key, f);
74         fputc('=', f);
75         fputs(buf, f);
76         fputc('\n', f);
77 
78         return 1;
79 }
80 
serialize_fd(FILE * f,FDSet * fds,const char * key,int fd)81 int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd) {
82         int copy;
83 
84         assert(f);
85         assert(key);
86 
87         if (fd < 0)
88                 return 0;
89 
90         copy = fdset_put_dup(fds, fd);
91         if (copy < 0)
92                 return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
93 
94         return serialize_item_format(f, key, "%i", copy);
95 }
96 
serialize_usec(FILE * f,const char * key,usec_t usec)97 int serialize_usec(FILE *f, const char *key, usec_t usec) {
98         assert(f);
99         assert(key);
100 
101         if (usec == USEC_INFINITY)
102                 return 0;
103 
104         return serialize_item_format(f, key, USEC_FMT, usec);
105 }
106 
serialize_dual_timestamp(FILE * f,const char * name,const dual_timestamp * t)107 int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t) {
108         assert(f);
109         assert(name);
110         assert(t);
111 
112         if (!dual_timestamp_is_set(t))
113                 return 0;
114 
115         return serialize_item_format(f, name, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic);
116 }
117 
serialize_strv(FILE * f,const char * key,char ** l)118 int serialize_strv(FILE *f, const char *key, char **l) {
119         int ret = 0, r;
120 
121         /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
122 
123         STRV_FOREACH(i, l) {
124                 r = serialize_item_escaped(f, key, *i);
125                 if ((ret >= 0 && r < 0) ||
126                     (ret == 0 && r > 0))
127                         ret = r;
128         }
129 
130         return ret;
131 }
132 
deserialize_usec(const char * value,usec_t * ret)133 int deserialize_usec(const char *value, usec_t *ret) {
134         int r;
135 
136         assert(value);
137 
138         r = safe_atou64(value, ret);
139         if (r < 0)
140                 return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value);
141 
142         return 0;
143 }
144 
deserialize_dual_timestamp(const char * value,dual_timestamp * t)145 int deserialize_dual_timestamp(const char *value, dual_timestamp *t) {
146         uint64_t a, b;
147         int r, pos;
148 
149         assert(value);
150         assert(t);
151 
152         pos = strspn(value, WHITESPACE);
153         if (value[pos] == '-')
154                 return -EINVAL;
155         pos += strspn(value + pos, DIGITS);
156         pos += strspn(value + pos, WHITESPACE);
157         if (value[pos] == '-')
158                 return -EINVAL;
159 
160         r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
161         if (r != 2)
162                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
163                                        "Failed to parse dual timestamp value \"%s\".",
164                                        value);
165 
166         if (value[pos] != '\0')
167                 /* trailing garbage */
168                 return -EINVAL;
169 
170         t->realtime = a;
171         t->monotonic = b;
172 
173         return 0;
174 }
175 
deserialize_environment(const char * value,char *** list)176 int deserialize_environment(const char *value, char ***list) {
177         _cleanup_free_ char *unescaped = NULL;
178         ssize_t l;
179         int r;
180 
181         assert(value);
182         assert(list);
183 
184         /* Changes the *environment strv inline. */
185 
186         l = cunescape(value, 0, &unescaped);
187         if (l < 0)
188                 return log_error_errno(l, "Failed to unescape: %m");
189 
190         r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
191         if (r < 0)
192                 return log_error_errno(r, "Failed to append environment variable: %m");
193 
194         return 0;
195 }
196 
open_serialization_fd(const char * ident)197 int open_serialization_fd(const char *ident) {
198         int fd;
199 
200         fd = memfd_create(ident, MFD_CLOEXEC);
201         if (fd < 0) {
202                 const char *path;
203 
204                 path = getpid_cached() == 1 ? "/run/systemd" : "/tmp";
205                 fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
206                 if (fd < 0)
207                         return fd;
208 
209                 log_debug("Serializing %s to %s.", ident, path);
210         } else
211                 log_debug("Serializing %s to memfd.", ident);
212 
213         return fd;
214 }
215