]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/serialize.c
hwdb: Add mapping for Xiaomi Mipad 2 bottom bezel capacitive buttons
[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 r;
184
185 assert(f);
186 assert(fds);
187
188 if (!pidref_is_set(pidref))
189 return 0;
190
191 /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
192 * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
193 * serialize both the pid and pidfd, so that we can construct the exact same pidref after
194 * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
195 * from the pidfd anymore if the process is reaped). */
196
197 if (pidref->fd >= 0) {
198 int copy = fdset_put_dup(fds, pidref->fd);
199 if (copy < 0)
200 return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
201
202 r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
203 if (r < 0)
204 return r;
205 }
206
207 return serialize_item_format(f, key, PID_FMT, pidref->pid);
208 }
209
210 int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
211 assert(rl);
212
213 return serialize_item_format(f, key,
214 USEC_FMT " " USEC_FMT " %u %u",
215 rl->begin,
216 rl->interval,
217 rl->num,
218 rl->burst);
219 }
220
221 int serialize_item_hexmem(FILE *f, const char *key, const void *p, size_t l) {
222 _cleanup_free_ char *encoded = NULL;
223 int r;
224
225 assert(f);
226 assert(key);
227
228 if (!p && l > 0)
229 return -EINVAL;
230
231 if (l == 0)
232 return 0;
233
234 encoded = hexmem(p, l);
235 if (!encoded)
236 return log_oom_debug();
237
238 r = serialize_item(f, key, encoded);
239 if (r < 0)
240 return r;
241
242 return 1;
243 }
244
245 int serialize_item_base64mem(FILE *f, const char *key, const void *p, size_t l) {
246 _cleanup_free_ char *encoded = NULL;
247 ssize_t len;
248 int r;
249
250 assert(f);
251 assert(key);
252
253 if (!p && l > 0)
254 return -EINVAL;
255
256 if (l == 0)
257 return 0;
258
259 len = base64mem(p, l, &encoded);
260 if (len <= 0)
261 return log_oom_debug();
262
263 r = serialize_item(f, key, encoded);
264 if (r < 0)
265 return r;
266
267 return 1;
268 }
269
270 int serialize_string_set(FILE *f, const char *key, Set *s) {
271 const char *e;
272 int r;
273
274 assert(f);
275 assert(key);
276
277 if (set_isempty(s))
278 return 0;
279
280 /* Serialize as individual items, as each element might contain separators and escapes */
281
282 SET_FOREACH(e, s) {
283 r = serialize_item(f, key, e);
284 if (r < 0)
285 return r;
286 }
287
288 return 1;
289 }
290
291 int serialize_image_policy(FILE *f, const char *key, const ImagePolicy *p) {
292 _cleanup_free_ char *policy = NULL;
293 int r;
294
295 assert(f);
296 assert(key);
297
298 if (!p)
299 return 0;
300
301 r = image_policy_to_string(p, /* simplify= */ false, &policy);
302 if (r < 0)
303 return r;
304
305 r = serialize_item(f, key, policy);
306 if (r < 0)
307 return r;
308
309 return 1;
310 }
311
312 int deserialize_read_line(FILE *f, char **ret) {
313 _cleanup_free_ char *line = NULL;
314 int r;
315
316 assert(f);
317 assert(ret);
318
319 r = read_stripped_line(f, LONG_LINE_MAX, &line);
320 if (r < 0)
321 return log_error_errno(r, "Failed to read serialization line: %m");
322 if (r == 0) { /* eof */
323 *ret = NULL;
324 return 0;
325 }
326
327 if (isempty(line)) { /* End marker */
328 *ret = NULL;
329 return 0;
330 }
331
332 *ret = TAKE_PTR(line);
333 return 1;
334 }
335
336 int deserialize_fd(FDSet *fds, const char *value) {
337 _cleanup_close_ int our_fd = -EBADF;
338 int parsed_fd;
339
340 assert(value);
341
342 parsed_fd = parse_fd(value);
343 if (parsed_fd < 0)
344 return log_debug_errno(parsed_fd, "Failed to parse file descriptor serialization: %s", value);
345
346 our_fd = fdset_remove(fds, parsed_fd); /* Take possession of the fd */
347 if (our_fd < 0)
348 return log_debug_errno(our_fd, "Failed to acquire fd from serialization fds: %m");
349
350 return TAKE_FD(our_fd);
351 }
352
353 int deserialize_fd_many(FDSet *fds, const char *value, size_t n, int *ret) {
354 int r, *fd_array = NULL;
355 size_t m = 0;
356
357 assert(value);
358
359 fd_array = new(int, n);
360 if (!fd_array)
361 return -ENOMEM;
362
363 CLEANUP_ARRAY(fd_array, m, close_many_and_free);
364
365 for (;;) {
366 _cleanup_free_ char *w = NULL;
367 int fd;
368
369 r = extract_first_word(&value, &w, NULL, 0);
370 if (r < 0)
371 return r;
372 if (r == 0) {
373 if (m < n) /* Too few */
374 return -EINVAL;
375
376 break;
377 }
378
379 if (m >= n) /* Too many */
380 return -EINVAL;
381
382 fd = deserialize_fd(fds, w);
383 if (fd < 0)
384 return fd;
385
386 fd_array[m++] = fd;
387 }
388
389 memcpy(ret, fd_array, m * sizeof(int));
390 fd_array = mfree(fd_array);
391
392 return 0;
393 }
394
395 int deserialize_strv(const char *value, char ***l) {
396 ssize_t unescaped_len;
397 char *unescaped;
398
399 assert(l);
400 assert(value);
401
402 unescaped_len = cunescape(value, 0, &unescaped);
403 if (unescaped_len < 0)
404 return unescaped_len;
405
406 return strv_consume(l, unescaped);
407 }
408
409 int deserialize_usec(const char *value, usec_t *ret) {
410 int r;
411
412 assert(value);
413 assert(ret);
414
415 r = safe_atou64(value, ret);
416 if (r < 0)
417 return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value);
418
419 return 0;
420 }
421
422 int deserialize_dual_timestamp(const char *value, dual_timestamp *ret) {
423 uint64_t a, b;
424 int r, pos;
425
426 assert(value);
427 assert(ret);
428
429 pos = strspn(value, WHITESPACE);
430 if (value[pos] == '-')
431 return -EINVAL;
432 pos += strspn(value + pos, DIGITS);
433 pos += strspn(value + pos, WHITESPACE);
434 if (value[pos] == '-')
435 return -EINVAL;
436
437 r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
438 if (r != 2)
439 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
440 "Failed to parse dual timestamp value \"%s\".",
441 value);
442
443 if (value[pos] != '\0')
444 /* trailing garbage */
445 return -EINVAL;
446
447 *ret = (dual_timestamp) {
448 .realtime = a,
449 .monotonic = b,
450 };
451
452 return 0;
453 }
454
455 int deserialize_environment(const char *value, char ***list) {
456 _cleanup_free_ char *unescaped = NULL;
457 ssize_t l;
458 int r;
459
460 assert(value);
461 assert(list);
462
463 /* Changes the *environment strv inline. */
464
465 l = cunescape(value, 0, &unescaped);
466 if (l < 0)
467 return log_error_errno(l, "Failed to unescape: %m");
468
469 r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
470 if (r < 0)
471 return log_error_errno(r, "Failed to append environment variable: %m");
472
473 return 0;
474 }
475
476 int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
477 const char *e;
478 int r;
479
480 assert(value);
481 assert(ret);
482
483 e = startswith(value, "@");
484 if (e) {
485 _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
486 _cleanup_close_ int fd = -EBADF;
487
488 r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr);
489 if (r < 0)
490 return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
491 if (r == 0)
492 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
493
494 assert(r <= 2);
495
496 fd = deserialize_fd(fds, fdstr);
497 if (fd < 0)
498 return fd;
499
500 /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
501 * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
502 * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
503 * need to support older format as we might be upgrading from a version that still uses the
504 * old format. */
505 if (pidstr) {
506 pid_t pid;
507
508 r = parse_pid(pidstr, &pid);
509 if (r < 0)
510 return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
511
512 *ret = (PidRef) {
513 .pid = pid,
514 .fd = TAKE_FD(fd),
515 };
516 } else
517 r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
518 } else {
519 pid_t pid;
520
521 r = parse_pid(value, &pid);
522 if (r < 0)
523 return log_debug_errno(r, "Failed to parse PID: %s", value);
524
525 r = pidref_set_pid(ret, pid);
526 }
527 if (r < 0)
528 return log_debug_errno(r, "Failed to initialize pidref: %m");
529
530 return 0;
531 }
532
533 void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value) {
534 usec_t begin, interval;
535 unsigned num, burst;
536
537 assert(rl);
538 assert(name);
539 assert(value);
540
541 if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
542 return log_notice("Failed to parse %s, ignoring: %s", name, value);
543
544 /* Preserve the counter only if the configuration didn't change. */
545 rl->num = (interval == rl->interval && burst == rl->burst) ? num : 0;
546 rl->begin = begin;
547 }
548
549 int open_serialization_fd(const char *ident) {
550 int fd;
551
552 fd = memfd_create_wrapper(ident, MFD_CLOEXEC | MFD_NOEXEC_SEAL);
553 if (fd < 0) {
554 const char *path;
555
556 path = getpid_cached() == 1 ? "/run/systemd" : "/tmp";
557 fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
558 if (fd < 0)
559 return fd;
560
561 log_debug("Serializing %s to %s.", ident, path);
562 } else
563 log_debug("Serializing %s to memfd.", ident);
564
565 return fd;
566 }
567
568 int open_serialization_file(const char *ident, FILE **ret) {
569 _cleanup_fclose_ FILE *f = NULL;
570 _cleanup_close_ int fd;
571
572 assert(ret);
573
574 fd = open_serialization_fd(ident);
575 if (fd < 0)
576 return fd;
577
578 f = take_fdopen(&fd, "w+");
579 if (!f)
580 return -errno;
581
582 *ret = TAKE_PTR(f);
583
584 return 0;
585 }