]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/serialize.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.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"
18 #include "tmpfile-util.h"
20 int serialize_item(FILE *f
, const char *key
, const char *value
) {
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
);
40 int serialize_item_escaped(FILE *f
, const char *key
, const char *value
) {
41 _cleanup_free_
char *c
= NULL
;
49 c
= xescape(value
, " ");
53 return serialize_item(f
, key
, c
);
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!) */
68 /* First, let's try to format this into a stack buffer */
70 k
= vsnprintf(buf
, sizeof(buf
), format
, ap
);
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
);
78 if ((size_t) k
< sizeof(buf
))
79 b
= buf
; /* Yay, it fit! */
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 */
85 k
= vasprintf(&allocated
, format
, ap
);
89 return log_warning_errno(errno
, "Failed to serialize item '%s', ignoring: %m", key
);
102 int serialize_fd(FILE *f
, FDSet
*fds
, const char *key
, int fd
) {
112 copy
= fdset_put_dup(fds
, fd
);
114 return log_error_errno(copy
, "Failed to add file descriptor to serialization set: %m");
116 return serialize_item_format(f
, key
, "%i", copy
);
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
;
129 for (size_t i
= 0; i
< n_fd_array
; i
++) {
135 copy
= fdset_put_dup(fds
, fd_array
[i
]);
137 return log_error_errno(copy
, "Failed to add file descriptor to serialization set: %m");
139 if (strextendf_with_separator(&t
, " ", "%i", copy
) < 0)
143 return serialize_item(f
, key
, t
);
146 int serialize_usec(FILE *f
, const char *key
, usec_t usec
) {
150 if (usec
== USEC_INFINITY
)
153 return serialize_item_format(f
, key
, USEC_FMT
, usec
);
156 int serialize_dual_timestamp(FILE *f
, const char *name
, const dual_timestamp
*t
) {
161 if (!dual_timestamp_is_set(t
))
164 return serialize_item_format(f
, name
, USEC_FMT
" " USEC_FMT
, t
->realtime
, t
->monotonic
);
167 int serialize_strv(FILE *f
, const char *key
, char **l
) {
170 /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
173 r
= serialize_item_escaped(f
, key
, *i
);
174 if ((ret
>= 0 && r
< 0) ||
182 int serialize_pidref(FILE *f
, FDSet
*fds
, const char *key
, PidRef
*pidref
) {
188 if (!pidref_is_set(pidref
))
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). */
197 if (pidref
->fd
>= 0) {
198 int copy
= fdset_put_dup(fds
, pidref
->fd
);
200 return log_error_errno(copy
, "Failed to add file descriptor to serialization set: %m");
202 r
= serialize_item_format(f
, key
, "@%i:" PID_FMT
, copy
, pidref
->pid
);
207 return serialize_item_format(f
, key
, PID_FMT
, pidref
->pid
);
210 int serialize_ratelimit(FILE *f
, const char *key
, const RateLimit
*rl
) {
213 return serialize_item_format(f
, key
,
214 USEC_FMT
" " USEC_FMT
" %u %u",
221 int serialize_item_hexmem(FILE *f
, const char *key
, const void *p
, size_t l
) {
222 _cleanup_free_
char *encoded
= NULL
;
234 encoded
= hexmem(p
, l
);
236 return log_oom_debug();
238 r
= serialize_item(f
, key
, encoded
);
245 int serialize_item_base64mem(FILE *f
, const char *key
, const void *p
, size_t l
) {
246 _cleanup_free_
char *encoded
= NULL
;
259 len
= base64mem(p
, l
, &encoded
);
261 return log_oom_debug();
263 r
= serialize_item(f
, key
, encoded
);
270 int serialize_string_set(FILE *f
, const char *key
, Set
*s
) {
280 /* Serialize as individual items, as each element might contain separators and escapes */
283 r
= serialize_item(f
, key
, e
);
291 int serialize_image_policy(FILE *f
, const char *key
, const ImagePolicy
*p
) {
292 _cleanup_free_
char *policy
= NULL
;
301 r
= image_policy_to_string(p
, /* simplify= */ false, &policy
);
305 r
= serialize_item(f
, key
, policy
);
312 int deserialize_read_line(FILE *f
, char **ret
) {
313 _cleanup_free_
char *line
= NULL
;
319 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
321 return log_error_errno(r
, "Failed to read serialization line: %m");
322 if (r
== 0) { /* eof */
327 if (isempty(line
)) { /* End marker */
332 *ret
= TAKE_PTR(line
);
336 int deserialize_fd(FDSet
*fds
, const char *value
) {
337 _cleanup_close_
int our_fd
= -EBADF
;
342 parsed_fd
= parse_fd(value
);
344 return log_debug_errno(parsed_fd
, "Failed to parse file descriptor serialization: %s", value
);
346 our_fd
= fdset_remove(fds
, parsed_fd
); /* Take possession of the fd */
348 return log_debug_errno(our_fd
, "Failed to acquire fd from serialization fds: %m");
350 return TAKE_FD(our_fd
);
353 int deserialize_fd_many(FDSet
*fds
, const char *value
, size_t n
, int *ret
) {
354 int r
, *fd_array
= NULL
;
359 fd_array
= new(int, n
);
363 CLEANUP_ARRAY(fd_array
, m
, close_many_and_free
);
366 _cleanup_free_
char *w
= NULL
;
369 r
= extract_first_word(&value
, &w
, NULL
, 0);
373 if (m
< n
) /* Too few */
379 if (m
>= n
) /* Too many */
382 fd
= deserialize_fd(fds
, w
);
389 memcpy(ret
, fd_array
, m
* sizeof(int));
390 fd_array
= mfree(fd_array
);
395 int deserialize_strv(const char *value
, char ***l
) {
396 ssize_t unescaped_len
;
402 unescaped_len
= cunescape(value
, 0, &unescaped
);
403 if (unescaped_len
< 0)
404 return unescaped_len
;
406 return strv_consume(l
, unescaped
);
409 int deserialize_usec(const char *value
, usec_t
*ret
) {
415 r
= safe_atou64(value
, ret
);
417 return log_debug_errno(r
, "Failed to parse usec value \"%s\": %m", value
);
422 int deserialize_dual_timestamp(const char *value
, dual_timestamp
*ret
) {
429 pos
= strspn(value
, WHITESPACE
);
430 if (value
[pos
] == '-')
432 pos
+= strspn(value
+ pos
, DIGITS
);
433 pos
+= strspn(value
+ pos
, WHITESPACE
);
434 if (value
[pos
] == '-')
437 r
= sscanf(value
, "%" PRIu64
"%" PRIu64
"%n", &a
, &b
, &pos
);
439 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
440 "Failed to parse dual timestamp value \"%s\".",
443 if (value
[pos
] != '\0')
444 /* trailing garbage */
447 *ret
= (dual_timestamp
) {
455 int deserialize_environment(const char *value
, char ***list
) {
456 _cleanup_free_
char *unescaped
= NULL
;
463 /* Changes the *environment strv inline. */
465 l
= cunescape(value
, 0, &unescaped
);
467 return log_error_errno(l
, "Failed to unescape: %m");
469 r
= strv_env_replace_consume(list
, TAKE_PTR(unescaped
));
471 return log_error_errno(r
, "Failed to append environment variable: %m");
476 int deserialize_pidref(FDSet
*fds
, const char *value
, PidRef
*ret
) {
483 e
= startswith(value
, "@");
485 _cleanup_free_
char *fdstr
= NULL
, *pidstr
= NULL
;
486 _cleanup_close_
int fd
= -EBADF
;
488 r
= extract_many_words(&e
, ":", /* flags = */ 0, &fdstr
, &pidstr
);
490 return log_debug_errno(r
, "Failed to deserialize pidref '%s': %m", e
);
492 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Cannot deserialize pidref from empty string.");
496 fd
= deserialize_fd(fds
, fdstr
);
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
508 r
= parse_pid(pidstr
, &pid
);
510 return log_debug_errno(r
, "Failed to parse PID: %s", pidstr
);
517 r
= pidref_set_pidfd_consume(ret
, TAKE_FD(fd
));
521 r
= parse_pid(value
, &pid
);
523 return log_debug_errno(r
, "Failed to parse PID: %s", value
);
525 r
= pidref_set_pid(ret
, pid
);
528 return log_debug_errno(r
, "Failed to initialize pidref: %m");
533 void deserialize_ratelimit(RateLimit
*rl
, const char *name
, const char *value
) {
534 usec_t begin
, interval
;
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
);
544 /* Preserve the counter only if the configuration didn't change. */
545 rl
->num
= (interval
== rl
->interval
&& burst
== rl
->burst
) ? num
: 0;
549 int open_serialization_fd(const char *ident
) {
552 fd
= memfd_create_wrapper(ident
, MFD_CLOEXEC
| MFD_NOEXEC_SEAL
);
556 path
= getpid_cached() == 1 ? "/run/systemd" : "/tmp";
557 fd
= open_tmpfile_unlinkable(path
, O_RDWR
|O_CLOEXEC
);
561 log_debug("Serializing %s to %s.", ident
, path
);
563 log_debug("Serializing %s to memfd.", ident
);
568 int open_serialization_file(const char *ident
, FILE **ret
) {
569 _cleanup_fclose_
FILE *f
= NULL
;
570 _cleanup_close_
int fd
;
574 fd
= open_serialization_fd(ident
);
578 f
= take_fdopen(&fd
, "w+");