]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/serialize.c
docs/RANDOM_SEEDS: update NetBSD link
[thirdparty/systemd.git] / src / shared / serialize.c
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 "fd-util.h"
9 #include "fileio.h"
10 #include "hexdecoct.h"
11 #include "memfd-util.h"
12 #include "missing_mman.h"
13 #include "missing_syscall.h"
14 #include "parse-util.h"
15 #include "process-util.h"
16 #include "serialize.h"
17 #include "strv.h"
18 #include "tmpfile-util.h"
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. */
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);
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 = xescape(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, ...) {
57 _cleanup_free_ char *allocated = NULL;
58 char buf[256]; /* Something reasonably short that fits nicely on any stack (i.e. is considerably less
59 * than LONG_LINE_MAX (1MiB!) */
60 const char *b;
61 va_list ap;
62 int k;
63
64 assert(f);
65 assert(key);
66 assert(format);
67
68 /* First, let's try to format this into a stack buffer */
69 va_start(ap, format);
70 k = vsnprintf(buf, sizeof(buf), format, ap);
71 va_end(ap);
72
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;
92 }
93
94 fputs(key, f);
95 fputc('=', f);
96 fputs(b, f);
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);
106 assert(fds);
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
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
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;
169
170 /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
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
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
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
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);
221
222 if (!p && l > 0)
223 return -EINVAL;
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
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);
246
247 if (!p && l > 0)
248 return -EINVAL;
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
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
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
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
313 r = read_stripped_line(f, LONG_LINE_MAX, &line);
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
321 if (isempty(line)) { /* End marker */
322 *ret = NULL;
323 return 0;
324 }
325
326 *ret = TAKE_PTR(line);
327 return 1;
328 }
329
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
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
389 int deserialize_strv(const char *value, char ***l) {
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
403 int deserialize_usec(const char *value, usec_t *ret) {
404 int r;
405
406 assert(value);
407 assert(ret);
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
416 int deserialize_dual_timestamp(const char *value, dual_timestamp *ret) {
417 uint64_t a, b;
418 int r, pos;
419
420 assert(value);
421 assert(ret);
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);
432 if (r != 2)
433 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
434 "Failed to parse dual timestamp value \"%s\".",
435 value);
436
437 if (value[pos] != '\0')
438 /* trailing garbage */
439 return -EINVAL;
440
441 *ret = (dual_timestamp) {
442 .realtime = a,
443 .monotonic = b,
444 };
445
446 return 0;
447 }
448
449 int deserialize_environment(const char *value, char ***list) {
450 _cleanup_free_ char *unescaped = NULL;
451 ssize_t l;
452 int r;
453
454 assert(value);
455 assert(list);
456
457 /* Changes the *environment strv inline. */
458
459 l = cunescape(value, 0, &unescaped);
460 if (l < 0)
461 return log_error_errno(l, "Failed to unescape: %m");
462
463 r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
464 if (r < 0)
465 return log_error_errno(r, "Failed to append environment variable: %m");
466
467 return 0;
468 }
469
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) {
479 int fd = deserialize_fd(fds, e);
480
481 if (fd < 0)
482 return fd;
483
484 r = pidref_set_pidfd_consume(ret, fd);
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
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
516 int open_serialization_fd(const char *ident) {
517 int fd;
518
519 fd = memfd_create_wrapper(ident, MFD_CLOEXEC | MFD_NOEXEC_SEAL);
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 }
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 }