]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
d68c645b | 2 | |
f5947a5e | 3 | #include <fcntl.h> |
0a2152f0 | 4 | |
d68c645b | 5 | #include "alloc-util.h" |
d68c645b LP |
6 | #include "env-util.h" |
7 | #include "escape.h" | |
2a7451dc | 8 | #include "fd-util.h" |
fde32028 | 9 | #include "fileio.h" |
3f12c5ff | 10 | #include "hexdecoct.h" |
c29715a8 | 11 | #include "memfd-util.h" |
f5947a5e YW |
12 | #include "missing_mman.h" |
13 | #include "missing_syscall.h" | |
d68c645b | 14 | #include "parse-util.h" |
0a2152f0 | 15 | #include "process-util.h" |
d68c645b LP |
16 | #include "serialize.h" |
17 | #include "strv.h" | |
0a2152f0 | 18 | #include "tmpfile-util.h" |
d68c645b LP |
19 | |
20 | int serialize_item(FILE *f, const char *key, const char *value) { | |
21 | assert(f); | |
22 | assert(key); | |
23 | ||
24 | if (!value) | |
25 | return 0; | |
26 | ||
27 | /* Make sure that anything we serialize we can also read back again with read_line() with a maximum line size | |
28 | * of LONG_LINE_MAX. This is a safety net only. All code calling us should filter this out earlier anyway. */ | |
f96a32c7 LP |
29 | if (strlen(key) + 1 + strlen(value) + 1 > LONG_LINE_MAX) |
30 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key); | |
d68c645b LP |
31 | |
32 | fputs(key, f); | |
33 | fputc('=', f); | |
34 | fputs(value, f); | |
35 | fputc('\n', f); | |
36 | ||
37 | return 1; | |
38 | } | |
39 | ||
40 | int serialize_item_escaped(FILE *f, const char *key, const char *value) { | |
41 | _cleanup_free_ char *c = NULL; | |
42 | ||
43 | assert(f); | |
44 | assert(key); | |
45 | ||
46 | if (!value) | |
47 | return 0; | |
48 | ||
49 | c = cescape(value); | |
50 | if (!c) | |
51 | return log_oom(); | |
52 | ||
53 | return serialize_item(f, key, c); | |
54 | } | |
55 | ||
56 | int serialize_item_format(FILE *f, const char *key, const char *format, ...) { | |
f96a32c7 | 57 | _cleanup_free_ char *allocated = NULL; |
fcdd21ec | 58 | char buf[256]; /* Something reasonably short that fits nicely on any stack (i.e. is considerably less |
f96a32c7 LP |
59 | * than LONG_LINE_MAX (1MiB!) */ |
60 | const char *b; | |
d68c645b LP |
61 | va_list ap; |
62 | int k; | |
63 | ||
64 | assert(f); | |
65 | assert(key); | |
66 | assert(format); | |
67 | ||
f96a32c7 | 68 | /* First, let's try to format this into a stack buffer */ |
d68c645b LP |
69 | va_start(ap, format); |
70 | k = vsnprintf(buf, sizeof(buf), format, ap); | |
71 | va_end(ap); | |
72 | ||
f96a32c7 LP |
73 | if (k < 0) |
74 | return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key); | |
75 | if (strlen(key) + 1 + k + 1 > LONG_LINE_MAX) /* See above */ | |
76 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key); | |
77 | ||
78 | if ((size_t) k < sizeof(buf)) | |
79 | b = buf; /* Yay, it fit! */ | |
80 | else { | |
81 | /* So the string didn't fit in the short buffer above, but was not above our total limit, | |
82 | * hence let's format it via dynamic memory */ | |
83 | ||
84 | va_start(ap, format); | |
85 | k = vasprintf(&allocated, format, ap); | |
86 | va_end(ap); | |
87 | ||
88 | if (k < 0) | |
89 | return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key); | |
90 | ||
91 | b = allocated; | |
d68c645b LP |
92 | } |
93 | ||
94 | fputs(key, f); | |
95 | fputc('=', f); | |
f96a32c7 | 96 | fputs(b, f); |
d68c645b LP |
97 | fputc('\n', f); |
98 | ||
99 | return 1; | |
100 | } | |
101 | ||
102 | int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd) { | |
103 | int copy; | |
104 | ||
105 | assert(f); | |
2a7451dc | 106 | assert(fds); |
d68c645b LP |
107 | assert(key); |
108 | ||
109 | if (fd < 0) | |
110 | return 0; | |
111 | ||
112 | copy = fdset_put_dup(fds, fd); | |
113 | if (copy < 0) | |
114 | return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m"); | |
115 | ||
116 | return serialize_item_format(f, key, "%i", copy); | |
117 | } | |
118 | ||
3b444970 LP |
119 | int serialize_fd_many(FILE *f, FDSet *fds, const char *key, const int fd_array[], size_t n_fd_array) { |
120 | _cleanup_free_ char *t = NULL; | |
121 | ||
122 | assert(f); | |
123 | ||
124 | if (n_fd_array == 0) | |
125 | return 0; | |
126 | ||
127 | assert(fd_array); | |
128 | ||
129 | for (size_t i = 0; i < n_fd_array; i++) { | |
130 | int copy; | |
131 | ||
132 | if (fd_array[i] < 0) | |
133 | return -EBADF; | |
134 | ||
135 | copy = fdset_put_dup(fds, fd_array[i]); | |
136 | if (copy < 0) | |
137 | return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m"); | |
138 | ||
139 | if (strextendf_with_separator(&t, " ", "%i", copy) < 0) | |
140 | return log_oom(); | |
141 | } | |
142 | ||
143 | return serialize_item(f, key, t); | |
144 | } | |
145 | ||
d68c645b LP |
146 | int serialize_usec(FILE *f, const char *key, usec_t usec) { |
147 | assert(f); | |
148 | assert(key); | |
149 | ||
150 | if (usec == USEC_INFINITY) | |
151 | return 0; | |
152 | ||
153 | return serialize_item_format(f, key, USEC_FMT, usec); | |
154 | } | |
155 | ||
156 | int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t) { | |
157 | assert(f); | |
158 | assert(name); | |
159 | assert(t); | |
160 | ||
161 | if (!dual_timestamp_is_set(t)) | |
162 | return 0; | |
163 | ||
164 | return serialize_item_format(f, name, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic); | |
165 | } | |
166 | ||
167 | int serialize_strv(FILE *f, const char *key, char **l) { | |
168 | int ret = 0, r; | |
d68c645b | 169 | |
d8351049 | 170 | /* Returns the first error, or positive if anything was serialized, 0 otherwise. */ |
d68c645b LP |
171 | |
172 | STRV_FOREACH(i, l) { | |
173 | r = serialize_item_escaped(f, key, *i); | |
174 | if ((ret >= 0 && r < 0) || | |
175 | (ret == 0 && r > 0)) | |
176 | ret = r; | |
177 | } | |
178 | ||
179 | return ret; | |
180 | } | |
181 | ||
2a7451dc LP |
182 | int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) { |
183 | int copy; | |
184 | ||
185 | assert(f); | |
186 | assert(fds); | |
187 | ||
188 | if (!pidref_is_set(pidref)) | |
189 | return 0; | |
190 | ||
191 | /* If we have a pidfd we serialize the fd and encode the fd number prefixed by "@" in the | |
192 | * serialization. Otherwise we serialize the numeric PID as it is. */ | |
193 | ||
194 | if (pidref->fd < 0) | |
195 | return serialize_item_format(f, key, PID_FMT, pidref->pid); | |
196 | ||
197 | copy = fdset_put_dup(fds, pidref->fd); | |
198 | if (copy < 0) | |
199 | return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m"); | |
200 | ||
201 | return serialize_item_format(f, key, "@%i", copy); | |
202 | } | |
203 | ||
07a6647a ZJS |
204 | int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) { |
205 | assert(rl); | |
206 | ||
207 | return serialize_item_format(f, key, | |
208 | USEC_FMT " " USEC_FMT " %u %u", | |
209 | rl->begin, | |
210 | rl->interval, | |
211 | rl->num, | |
212 | rl->burst); | |
213 | } | |
214 | ||
3f12c5ff LB |
215 | int serialize_item_hexmem(FILE *f, const char *key, const void *p, size_t l) { |
216 | _cleanup_free_ char *encoded = NULL; | |
217 | int r; | |
218 | ||
219 | assert(f); | |
220 | assert(key); | |
892eb4d7 FS |
221 | |
222 | if (!p && l > 0) | |
223 | return -EINVAL; | |
3f12c5ff LB |
224 | |
225 | if (l == 0) | |
226 | return 0; | |
227 | ||
228 | encoded = hexmem(p, l); | |
229 | if (!encoded) | |
230 | return log_oom_debug(); | |
231 | ||
232 | r = serialize_item(f, key, encoded); | |
233 | if (r < 0) | |
234 | return r; | |
235 | ||
236 | return 1; | |
237 | } | |
238 | ||
6dae20ec LB |
239 | int serialize_item_base64mem(FILE *f, const char *key, const void *p, size_t l) { |
240 | _cleanup_free_ char *encoded = NULL; | |
241 | ssize_t len; | |
242 | int r; | |
243 | ||
244 | assert(f); | |
245 | assert(key); | |
892eb4d7 FS |
246 | |
247 | if (!p && l > 0) | |
248 | return -EINVAL; | |
6dae20ec LB |
249 | |
250 | if (l == 0) | |
251 | return 0; | |
252 | ||
253 | len = base64mem(p, l, &encoded); | |
254 | if (len <= 0) | |
255 | return log_oom_debug(); | |
256 | ||
257 | r = serialize_item(f, key, encoded); | |
258 | if (r < 0) | |
259 | return r; | |
260 | ||
261 | return 1; | |
262 | } | |
263 | ||
1b30ccd8 LB |
264 | int serialize_string_set(FILE *f, const char *key, Set *s) { |
265 | const char *e; | |
266 | int r; | |
267 | ||
268 | assert(f); | |
269 | assert(key); | |
270 | ||
271 | if (set_isempty(s)) | |
272 | return 0; | |
273 | ||
274 | /* Serialize as individual items, as each element might contain separators and escapes */ | |
275 | ||
276 | SET_FOREACH(e, s) { | |
277 | r = serialize_item(f, key, e); | |
278 | if (r < 0) | |
279 | return r; | |
280 | } | |
281 | ||
282 | return 1; | |
283 | } | |
284 | ||
665c6e46 LB |
285 | int serialize_image_policy(FILE *f, const char *key, const ImagePolicy *p) { |
286 | _cleanup_free_ char *policy = NULL; | |
287 | int r; | |
288 | ||
289 | assert(f); | |
290 | assert(key); | |
291 | ||
292 | if (!p) | |
293 | return 0; | |
294 | ||
295 | r = image_policy_to_string(p, /* simplify= */ false, &policy); | |
296 | if (r < 0) | |
297 | return r; | |
298 | ||
299 | r = serialize_item(f, key, policy); | |
300 | if (r < 0) | |
301 | return r; | |
302 | ||
303 | return 1; | |
304 | } | |
305 | ||
0df7d525 LP |
306 | int deserialize_read_line(FILE *f, char **ret) { |
307 | _cleanup_free_ char *line = NULL; | |
308 | int r; | |
309 | ||
310 | assert(f); | |
311 | assert(ret); | |
312 | ||
0ff6ff2b | 313 | r = read_stripped_line(f, LONG_LINE_MAX, &line); |
0df7d525 LP |
314 | if (r < 0) |
315 | return log_error_errno(r, "Failed to read serialization line: %m"); | |
316 | if (r == 0) { /* eof */ | |
317 | *ret = NULL; | |
318 | return 0; | |
319 | } | |
320 | ||
0ff6ff2b | 321 | if (isempty(line)) { /* End marker */ |
0df7d525 LP |
322 | *ret = NULL; |
323 | return 0; | |
324 | } | |
325 | ||
0df7d525 LP |
326 | *ret = TAKE_PTR(line); |
327 | return 1; | |
328 | } | |
329 | ||
dff9808a LP |
330 | int deserialize_fd(FDSet *fds, const char *value) { |
331 | _cleanup_close_ int our_fd = -EBADF; | |
332 | int parsed_fd; | |
333 | ||
334 | assert(value); | |
335 | ||
336 | parsed_fd = parse_fd(value); | |
337 | if (parsed_fd < 0) | |
338 | return log_debug_errno(parsed_fd, "Failed to parse file descriptor serialization: %s", value); | |
339 | ||
340 | our_fd = fdset_remove(fds, parsed_fd); /* Take possession of the fd */ | |
341 | if (our_fd < 0) | |
342 | return log_debug_errno(our_fd, "Failed to acquire fd from serialization fds: %m"); | |
343 | ||
344 | return TAKE_FD(our_fd); | |
345 | } | |
346 | ||
3b444970 LP |
347 | int deserialize_fd_many(FDSet *fds, const char *value, size_t n, int *ret) { |
348 | int r, *fd_array = NULL; | |
349 | size_t m = 0; | |
350 | ||
351 | assert(value); | |
352 | ||
353 | fd_array = new(int, n); | |
354 | if (!fd_array) | |
355 | return -ENOMEM; | |
356 | ||
357 | CLEANUP_ARRAY(fd_array, m, close_many_and_free); | |
358 | ||
359 | for (;;) { | |
360 | _cleanup_free_ char *w = NULL; | |
361 | int fd; | |
362 | ||
363 | r = extract_first_word(&value, &w, NULL, 0); | |
364 | if (r < 0) | |
365 | return r; | |
366 | if (r == 0) { | |
367 | if (m < n) /* Too few */ | |
368 | return -EINVAL; | |
369 | ||
370 | break; | |
371 | } | |
372 | ||
373 | if (m >= n) /* Too many */ | |
374 | return -EINVAL; | |
375 | ||
376 | fd = deserialize_fd(fds, w); | |
377 | if (fd < 0) | |
378 | return fd; | |
379 | ||
380 | fd_array[m++] = fd; | |
381 | } | |
382 | ||
383 | memcpy(ret, fd_array, m * sizeof(int)); | |
384 | fd_array = mfree(fd_array); | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
c2e42d4b | 389 | int deserialize_strv(const char *value, char ***l) { |
1d6f1e2f LB |
390 | ssize_t unescaped_len; |
391 | char *unescaped; | |
392 | ||
393 | assert(l); | |
394 | assert(value); | |
395 | ||
396 | unescaped_len = cunescape(value, 0, &unescaped); | |
397 | if (unescaped_len < 0) | |
398 | return unescaped_len; | |
399 | ||
400 | return strv_consume(l, unescaped); | |
401 | } | |
402 | ||
d68c645b LP |
403 | int deserialize_usec(const char *value, usec_t *ret) { |
404 | int r; | |
405 | ||
406 | assert(value); | |
af632185 | 407 | assert(ret); |
d68c645b LP |
408 | |
409 | r = safe_atou64(value, ret); | |
410 | if (r < 0) | |
411 | return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value); | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
af632185 | 416 | int deserialize_dual_timestamp(const char *value, dual_timestamp *ret) { |
d68c645b LP |
417 | uint64_t a, b; |
418 | int r, pos; | |
419 | ||
420 | assert(value); | |
af632185 | 421 | assert(ret); |
d68c645b LP |
422 | |
423 | pos = strspn(value, WHITESPACE); | |
424 | if (value[pos] == '-') | |
425 | return -EINVAL; | |
426 | pos += strspn(value + pos, DIGITS); | |
427 | pos += strspn(value + pos, WHITESPACE); | |
428 | if (value[pos] == '-') | |
429 | return -EINVAL; | |
430 | ||
431 | r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); | |
baaa35ad ZJS |
432 | if (r != 2) |
433 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
434 | "Failed to parse dual timestamp value \"%s\".", | |
435 | value); | |
d68c645b LP |
436 | |
437 | if (value[pos] != '\0') | |
438 | /* trailing garbage */ | |
439 | return -EINVAL; | |
440 | ||
af632185 LP |
441 | *ret = (dual_timestamp) { |
442 | .realtime = a, | |
443 | .monotonic = b, | |
444 | }; | |
d68c645b LP |
445 | |
446 | return 0; | |
447 | } | |
448 | ||
449 | int deserialize_environment(const char *value, char ***list) { | |
31780147 | 450 | _cleanup_free_ char *unescaped = NULL; |
e437538f | 451 | ssize_t l; |
d68c645b LP |
452 | int r; |
453 | ||
454 | assert(value); | |
455 | assert(list); | |
456 | ||
457 | /* Changes the *environment strv inline. */ | |
458 | ||
e437538f ZJS |
459 | l = cunescape(value, 0, &unescaped); |
460 | if (l < 0) | |
461 | return log_error_errno(l, "Failed to unescape: %m"); | |
d68c645b | 462 | |
31780147 | 463 | r = strv_env_replace_consume(list, TAKE_PTR(unescaped)); |
d68c645b LP |
464 | if (r < 0) |
465 | return log_error_errno(r, "Failed to append environment variable: %m"); | |
31780147 | 466 | |
d68c645b LP |
467 | return 0; |
468 | } | |
0a2152f0 | 469 | |
2a7451dc LP |
470 | int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) { |
471 | const char *e; | |
472 | int r; | |
473 | ||
474 | assert(value); | |
475 | assert(ret); | |
476 | ||
477 | e = startswith(value, "@"); | |
478 | if (e) { | |
dff9808a | 479 | int fd = deserialize_fd(fds, e); |
2a7451dc | 480 | |
dff9808a LP |
481 | if (fd < 0) |
482 | return fd; | |
2a7451dc | 483 | |
dff9808a | 484 | r = pidref_set_pidfd_consume(ret, fd); |
2a7451dc LP |
485 | } else { |
486 | pid_t pid; | |
487 | ||
488 | r = parse_pid(value, &pid); | |
489 | if (r < 0) | |
490 | return log_debug_errno(r, "Failed to parse PID: %s", value); | |
491 | ||
492 | r = pidref_set_pid(ret, pid); | |
493 | } | |
494 | if (r < 0) | |
495 | return log_debug_errno(r, "Failed to initialize pidref: %m"); | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
07a6647a ZJS |
500 | void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value) { |
501 | usec_t begin, interval; | |
502 | unsigned num, burst; | |
503 | ||
504 | assert(rl); | |
505 | assert(name); | |
506 | assert(value); | |
507 | ||
508 | if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4) | |
509 | return log_notice("Failed to parse %s, ignoring: %s", name, value); | |
510 | ||
511 | /* Preserve the counter only if the configuration didn't change. */ | |
512 | rl->num = (interval == rl->interval && burst == rl->burst) ? num : 0; | |
513 | rl->begin = begin; | |
514 | } | |
515 | ||
0a2152f0 LP |
516 | int open_serialization_fd(const char *ident) { |
517 | int fd; | |
518 | ||
c29715a8 | 519 | fd = memfd_create_wrapper(ident, MFD_CLOEXEC | MFD_NOEXEC_SEAL); |
0a2152f0 LP |
520 | if (fd < 0) { |
521 | const char *path; | |
522 | ||
523 | path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; | |
524 | fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); | |
525 | if (fd < 0) | |
526 | return fd; | |
527 | ||
528 | log_debug("Serializing %s to %s.", ident, path); | |
529 | } else | |
530 | log_debug("Serializing %s to memfd.", ident); | |
531 | ||
532 | return fd; | |
533 | } | |
81b913f0 LB |
534 | |
535 | int open_serialization_file(const char *ident, FILE **ret) { | |
536 | _cleanup_fclose_ FILE *f = NULL; | |
537 | _cleanup_close_ int fd; | |
538 | ||
539 | assert(ret); | |
540 | ||
541 | fd = open_serialization_fd(ident); | |
542 | if (fd < 0) | |
543 | return fd; | |
544 | ||
545 | f = take_fdopen(&fd, "w+"); | |
546 | if (!f) | |
547 | return -errno; | |
548 | ||
549 | *ret = TAKE_PTR(f); | |
550 | ||
551 | return 0; | |
552 | } |