summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2019-07-05 09:11:40 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2019-07-05 09:13:53 +0200
commitbe8f7b5a5de6ed18d9e329e0151fd3065747c7b0 (patch)
tree8419ecf187370e3161aef7767e8c4338cc38d271
parentfe52a9467b1cdcd8a34a199aa229071c8ae6446a (diff)
parent55c47d4efa5969db32499c769d89709ccef80887 (diff)
downloadNetworkManager-be8f7b5a5de6ed18d9e329e0151fd3065747c7b0.tar.gz
systemd: merge branch systemd into master
-rw-r--r--Makefile.am3
-rw-r--r--config.h.meson16
-rw-r--r--configure.ac6
-rw-r--r--meson.build10
-rw-r--r--shared/meson.build1
-rw-r--r--shared/systemd/src/basic/env-util.c6
-rw-r--r--shared/systemd/src/basic/env-util.h7
-rw-r--r--shared/systemd/src/basic/escape.c73
-rw-r--r--shared/systemd/src/basic/escape.h6
-rw-r--r--shared/systemd/src/basic/extract-word.c2
-rw-r--r--shared/systemd/src/basic/extract-word.h2
-rw-r--r--shared/systemd/src/basic/fileio.c12
-rw-r--r--shared/systemd/src/basic/fileio.h1
-rw-r--r--shared/systemd/src/basic/format-util.c72
-rw-r--r--shared/systemd/src/basic/format-util.h83
-rw-r--r--shared/systemd/src/basic/fs-util.c128
-rw-r--r--shared/systemd/src/basic/fs-util.h8
-rw-r--r--shared/systemd/src/basic/hashmap.c7
-rw-r--r--shared/systemd/src/basic/in-addr-util.c12
-rw-r--r--shared/systemd/src/basic/in-addr-util.h1
-rw-r--r--shared/systemd/src/basic/io-util.c82
-rw-r--r--shared/systemd/src/basic/io-util.h17
-rw-r--r--shared/systemd/src/basic/memory-util.h4
-rw-r--r--shared/systemd/src/basic/parse-util.c63
-rw-r--r--shared/systemd/src/basic/parse-util.h6
-rw-r--r--shared/systemd/src/basic/path-util.c45
-rw-r--r--shared/systemd/src/basic/path-util.h8
-rw-r--r--shared/systemd/src/basic/process-util.c225
-rw-r--r--shared/systemd/src/basic/process-util.h10
-rw-r--r--shared/systemd/src/basic/random-util.c115
-rw-r--r--shared/systemd/src/basic/siphash24.h1
-rw-r--r--shared/systemd/src/basic/socket-util.c4
-rw-r--r--shared/systemd/src/basic/socket-util.h1
-rw-r--r--shared/systemd/src/basic/string-util.h13
-rw-r--r--shared/systemd/src/basic/time-util.c15
-rw-r--r--shared/systemd/src/basic/tmpfile-util.c6
-rw-r--r--shared/systemd/src/basic/utf8.c76
-rw-r--r--shared/systemd/src/basic/utf8.h5
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-option.c2
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c251
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h15
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c25
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c2
44 files changed, 986 insertions, 463 deletions
diff --git a/Makefile.am b/Makefile.am
index 20592ea028..3f918b04e4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1662,7 +1662,6 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \
shared/systemd/sd-adapt-shared/device-nodes.h \
shared/systemd/sd-adapt-shared/dirent-util.h \
shared/systemd/sd-adapt-shared/errno-list.h \
- shared/systemd/sd-adapt-shared/format-util.h \
shared/systemd/sd-adapt-shared/glob-util.h \
shared/systemd/sd-adapt-shared/gunicode.h \
shared/systemd/sd-adapt-shared/ioprio.h \
@@ -1702,6 +1701,8 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \
shared/systemd/src/basic/fd-util.h \
shared/systemd/src/basic/fileio.c \
shared/systemd/src/basic/fileio.h \
+ shared/systemd/src/basic/format-util.c \
+ shared/systemd/src/basic/format-util.h \
shared/systemd/src/basic/fs-util.c \
shared/systemd/src/basic/fs-util.h \
shared/systemd/src/basic/hash-funcs.c \
diff --git a/config.h.meson b/config.h.meson
index 94504a48cf..a8c694011f 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -118,14 +118,26 @@
/* Define to 1 if libsystemd-login is available */
#mesondefine SESSION_TRACKING_SYSTEMD
+/* The size of `pid_t', as computed by sizeof. */
+#mesondefine SIZEOF_PID_T
+
+/* The size of `uid_t', as computed by sizeof. */
+#mesondefine SIZEOF_UID_T
+
+/* The size of `gid_t', as computed by sizeof. */
+#mesondefine SIZEOF_GID_T
+
/* The size of `dev_t', as computed by sizeof. */
#mesondefine SIZEOF_DEV_T
+/* The size of `ino_t', as computed by sizeof. */
+#mesondefine SIZEOF_INO_T
+
/* The size of `time_t', as computed by sizeof. */
#mesondefine SIZEOF_TIME_T
-/* The size of `pid_t', as computed by sizeof. */
-#mesondefine SIZEOF_PID_T
+/* The size of `rlim_t', as computed by sizeof. */
+#mesondefine SIZEOF_RLIM_T
/* Define to 1 to use ConsoleKit2 suspend api */
#mesondefine SUSPEND_RESUME_CONSOLEKIT
diff --git a/configure.ac b/configure.ac
index ed46e53cb9..4033785e60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,9 +65,13 @@ dnl
dnl Checks for typedefs, structures, and compiler characteristics.
dnl
AC_TYPE_PID_T
+AC_CHECK_SIZEOF(pid_t)
+AC_CHECK_SIZEOF(uid_t)
+AC_CHECK_SIZEOF(gid_t)
AC_CHECK_SIZEOF(dev_t)
+AC_CHECK_SIZEOF(ino_t)
AC_CHECK_SIZEOF(time_t)
-AC_CHECK_SIZEOF(pid_t)
+AC_CHECK_SIZEOF(rlim_t,,[[#include <sys/resource.h>]])
AC_CHECK_DECLS([
explicit_bzero],
diff --git a/meson.build b/meson.build
index a8716c6070..e7931bc82a 100644
--- a/meson.build
+++ b/meson.build
@@ -98,9 +98,13 @@ config_h.set10('HAVE_DECL_EXPLICIT_BZERO', cc.has_function('explicit_bzero', pre
config_h.set10('HAVE_DECL_MEMFD_CREATE', cc.has_function('memfd_create', prefix: '#include <sys/mman.h>'))
# types
-config_h.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix: '#include <sys/types.h>'))
-config_h.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix: '#include <sys/types.h>'))
-config_h.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix: '#include <sys/types.h>'))
+config_h.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include <sys/types.h>'))
+config_h.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include <sys/types.h>'))
+config_h.set('SIZEOF_GID_T', cc.sizeof('gid_t', prefix : '#include <sys/types.h>'))
+config_h.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix : '#include <sys/types.h>'))
+config_h.set('SIZEOF_INO_T', cc.sizeof('ino_t', prefix : '#include <sys/types.h>'))
+config_h.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include <sys/time.h>'))
+config_h.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h>'))
if not cc.has_type('pid_t', prefix: '#include <sys/types.h>')
config_h.set('pid_t', 'int')
diff --git a/shared/meson.build b/shared/meson.build
index fdb9cc0fee..b29a7c91b7 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -214,6 +214,7 @@ libnm_systemd_shared = static_library(
'systemd/src/basic/extract-word.c',
'systemd/src/basic/fd-util.c',
'systemd/src/basic/fileio.c',
+ 'systemd/src/basic/format-util.c',
'systemd/src/basic/fs-util.c',
'systemd/src/basic/hash-funcs.c',
'systemd/src/basic/hashmap.c',
diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c
index dc10362d08..47afee1c47 100644
--- a/shared/systemd/src/basic/env-util.c
+++ b/shared/systemd/src/basic/env-util.c
@@ -75,7 +75,7 @@ bool env_value_is_valid(const char *e) {
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
- if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
+ if (strlen(e) > sc_arg_max() - 3)
return false;
return true;
@@ -98,7 +98,7 @@ bool env_assignment_is_valid(const char *e) {
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
- if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
+ if (strlen(e) > sc_arg_max() - 1)
return false;
return true;
@@ -691,7 +691,7 @@ char **replace_env_argv(char **argv, char **env) {
if (e) {
int r;
- r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
+ r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
diff --git a/shared/systemd/src/basic/env-util.h b/shared/systemd/src/basic/env-util.h
index d54f99658b..92802ed774 100644
--- a/shared/systemd/src/basic/env-util.h
+++ b/shared/systemd/src/basic/env-util.h
@@ -4,10 +4,17 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
+#include <unistd.h>
#include "macro.h"
#include "string.h"
+static inline size_t sc_arg_max(void) {
+ long l = sysconf(_SC_ARG_MAX);
+ assert(l > 0);
+ return (size_t) l;
+}
+
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c
index 8f7a1b33b9..06d823c762 100644
--- a/shared/systemd/src/basic/escape.c
+++ b/shared/systemd/src/basic/escape.c
@@ -370,34 +370,81 @@ int cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
-char *xescape(const char *s, const char *bad) {
- char *r, *t;
+#if 0 /* NM_IGNORED */
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
+ char *ans, *t, *prev, *prev2;
const char *f;
- /* Escapes all chars in bad, in addition to \ and all special
- * chars, in \xFF style escaping. May be reversed with
- * cunescape(). */
+ /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
+ * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
+ * This corresponds to non-ASCII printable characters in pre-unicode encodings.
+ *
+ * If console_width is reached, output is truncated and "..." is appended. */
- r = new(char, strlen(s) * 4 + 1);
- if (!r)
+ if (console_width == 0)
+ return strdup("");
+
+ ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
+ if (!ans)
return NULL;
- for (f = s, t = r; *f; f++) {
+ memset(ans, '_', MIN(strlen(s), console_width) * 4);
+ ans[MIN(strlen(s), console_width) * 4] = 0;
+
+ for (f = s, t = prev = prev2 = ans; ; f++) {
+ char *tmp_t = t;
+
+ if (!*f) {
+ *t = 0;
+ return ans;
+ }
+
+ if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
+ *f == '\\' || strchr(bad, *f)) {
+ if ((size_t) (t - ans) + 4 > console_width)
+ break;
- if ((*f < ' ') || (*f >= 127) ||
- (*f == '\\') || strchr(bad, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
- } else
+ } else {
+ if ((size_t) (t - ans) + 1 > console_width)
+ break;
+
*(t++) = *f;
+ }
+
+ /* We might need to go back two cycles to fit three dots, so remember two positions */
+ prev2 = prev;
+ prev = tmp_t;
}
- *t = 0;
+ /* We can just write where we want, since chars are one-byte */
+ size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
+ size_t off;
+ if (console_width - c >= (size_t) (t - ans))
+ off = (size_t) (t - ans);
+ else if (console_width - c >= (size_t) (prev - ans))
+ off = (size_t) (prev - ans);
+ else if (console_width - c >= (size_t) (prev2 - ans))
+ off = (size_t) (prev2 - ans);
+ else
+ off = console_width - c;
+ assert(off <= (size_t) (t - ans));
- return r;
+ memcpy(ans + off, "...", c);
+ ans[off + c] = '\0';
+ return ans;
+}
+
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
+ if (eight_bit)
+ return xescape_full(str, "", console_width, true);
+ else
+ return utf8_escape_non_printable_full(str, console_width);
}
+#endif /* NM_IGNORED */
char *octescape(const char *s, size_t len) {
char *r, *t;
diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h
index 515620993d..b26054c5df 100644
--- a/shared/systemd/src/basic/escape.h
+++ b/shared/systemd/src/basic/escape.h
@@ -46,8 +46,12 @@ int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **r
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
-char *xescape(const char *s, const char *bad);
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
+static inline char *xescape(const char *s, const char *bad) {
+ return xescape_full(s, bad, SIZE_MAX, false);
+}
char *octescape(const char *s, size_t len);
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
char *shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, EscapeStyle style);
diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c
index 782c868bc1..b584dc42d0 100644
--- a/shared/systemd/src/basic/extract-word.c
+++ b/shared/systemd/src/basic/extract-word.c
@@ -137,7 +137,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
- else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) {
+ else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
quote = c;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h
index 705ebbe95b..e2d433893a 100644
--- a/shared/systemd/src/basic/extract-word.h
+++ b/shared/systemd/src/basic/extract-word.h
@@ -7,7 +7,7 @@ typedef enum ExtractFlags {
EXTRACT_RELAX = 1 << 0,
EXTRACT_CUNESCAPE = 1 << 1,
EXTRACT_CUNESCAPE_RELAX = 1 << 2,
- EXTRACT_QUOTES = 1 << 3,
+ EXTRACT_UNQUOTE = 1 << 3,
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
EXTRACT_RETAIN_ESCAPE = 1 << 5,
} ExtractFlags;
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index e845fe54e6..88743e3914 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "macro.h"
#include "missing.h"
+#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "stdio-util.h"
@@ -177,6 +178,12 @@ int write_string_file_ts(
/* We don't know how to verify whether the file contents was already on-disk. */
assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
+ if (flags & WRITE_STRING_FILE_MKDIR_0755) {
+ r = mkdir_parents(fn, 0755);
+ if (r < 0)
+ return r;
+ }
+
if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(flags & WRITE_STRING_FILE_CREATE);
@@ -589,10 +596,7 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
_cleanup_free_ char *p = NULL;
FILE *f;
- if (root)
- p = strjoin(root, *i, "/", path);
- else
- p = strjoin(*i, "/", path);
+ p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h
index eb551c7ac1..05f6c89da0 100644
--- a/shared/systemd/src/basic/fileio.h
+++ b/shared/systemd/src/basic/fileio.h
@@ -21,6 +21,7 @@ typedef enum {
WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
+ WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
diff --git a/shared/systemd/src/basic/format-util.c b/shared/systemd/src/basic/format-util.c
new file mode 100644
index 0000000000..7a3e735ba1
--- /dev/null
+++ b/shared/systemd/src/basic/format-util.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <stdio.h>
+
+#include "format-util.h"
+#include "memory-util.h"
+
+char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+ /* Buffer is always cleared */
+ memzero(buf, IF_NAMESIZE + 1);
+ return if_indextoname(ifindex, buf);
+}
+
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
+ typedef struct {
+ const char *suffix;
+ uint64_t factor;
+ } suffix_table;
+ static const suffix_table table_iec[] = {
+ { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "M", UINT64_C(1024)*UINT64_C(1024) },
+ { "K", UINT64_C(1024) },
+ }, table_si[] = {
+ { "E", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "P", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "T", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "G", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "M", UINT64_C(1000)*UINT64_C(1000) },
+ { "K", UINT64_C(1000) },
+ };
+ const suffix_table *table;
+ size_t n, i;
+
+ assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si));
+
+ if (t == (uint64_t) -1)
+ return NULL;
+
+ table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si;
+ n = ELEMENTSOF(table_iec);
+
+ for (i = 0; i < n; i++)
+ if (t >= table[i].factor) {
+ if (flag & FORMAT_BYTES_BELOW_POINT) {
+ snprintf(buf, l,
+ "%" PRIu64 ".%" PRIu64 "%s",
+ t / table[i].factor,
+ i != n - 1 ?
+ (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
+ (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+ table[i].suffix);
+ } else
+ snprintf(buf, l,
+ "%" PRIu64 "%s",
+ t / table[i].factor,
+ table[i].suffix);
+
+ goto finish;
+ }
+
+ snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
+
+finish:
+ buf[l-1] = 0;
+ return buf;
+
+}
diff --git a/shared/systemd/src/basic/format-util.h b/shared/systemd/src/basic/format-util.h
new file mode 100644
index 0000000000..e0d184a541
--- /dev/null
+++ b/shared/systemd/src/basic/format-util.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <net/if.h>
+#include <stdbool.h>
+
+#if SIZEOF_PID_T == 4
+# define PID_PRI PRIi32
+#elif SIZEOF_PID_T == 2
+# define PID_PRI PRIi16
+#else
+# error Unknown pid_t size
+#endif
+#define PID_FMT "%" PID_PRI
+
+#if SIZEOF_UID_T == 4
+# define UID_FMT "%" PRIu32
+#elif SIZEOF_UID_T == 2
+# define UID_FMT "%" PRIu16
+#else
+# error Unknown uid_t size
+#endif
+
+#if SIZEOF_GID_T == 4
+# define GID_FMT "%" PRIu32
+#elif SIZEOF_GID_T == 2
+# define GID_FMT "%" PRIu16
+#else
+# error Unknown gid_t size
+#endif
+
+#if SIZEOF_TIME_T == 8
+# define PRI_TIME PRIi64
+#elif SIZEOF_TIME_T == 4
+# define PRI_TIME "li"
+#else
+# error Unknown time_t size
+#endif
+
+#if defined __x86_64__ && defined __ILP32__
+# define PRI_TIMEX PRIi64
+#else
+# define PRI_TIMEX "li"
+#endif
+
+#if SIZEOF_RLIM_T == 8
+# define RLIM_FMT "%" PRIu64
+#elif SIZEOF_RLIM_T == 4
+# define RLIM_FMT "%" PRIu32
+#else
+# error Unknown rlim_t size
+#endif
+
+#if SIZEOF_DEV_T == 8
+# define DEV_FMT "%" PRIu64
+#elif SIZEOF_DEV_T == 4
+# define DEV_FMT "%" PRIu32
+#else
+# error Unknown dev_t size
+#endif
+
+#if SIZEOF_INO_T == 8
+# define INO_FMT "%" PRIu64
+#elif SIZEOF_INO_T == 4
+# define INO_FMT "%" PRIu32
+#else
+# error Unknown ino_t size
+#endif
+
+char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
+
+typedef enum {
+ FORMAT_BYTES_USE_IEC = 1 << 0,
+ FORMAT_BYTES_BELOW_POINT = 1 << 1,
+ FORMAT_BYTES_TRAILING_B = 1 << 2,
+} FormatBytesFlag;
+
+#define FORMAT_BYTES_MAX 8
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
+static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
+ return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
+}
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c
index be85eef1e9..56385fa29b 100644
--- a/shared/systemd/src/basic/fs-util.c
+++ b/shared/systemd/src/basic/fs-util.c
@@ -218,113 +218,65 @@ int readlink_and_make_absolute(const char *p, char **r) {
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
- bool st_valid = false;
- struct stat st;
- int r;
assert(path);
- /* Under the assumption that we are running privileged we first change the access mode and only then
- * hand out ownership to avoid a window where access is too open. */
-
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change
* mode/owner on the same file */
if (fd < 0)
return -errno;
- xsprintf(fd_path, "/proc/self/fd/%i", fd);
-
- if (mode != MODE_INVALID) {
- if ((mode & S_IFMT) != 0) {
-
- if (stat(fd_path, &st) < 0)
- return -errno;
-
- if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
- return -EINVAL;
-
- st_valid = true;
- }
-
- if (chmod(fd_path, mode & 07777) < 0) {
- r = -errno;
-
- if (!st_valid && stat(fd_path, &st) < 0)
- return -errno;
-
- if ((mode & 07777) != (st.st_mode & 07777))
- return r;
-
- st_valid = true;
- }
- }
-
- if (uid != UID_INVALID || gid != GID_INVALID) {
- if (chown(fd_path, uid, gid) < 0) {
- r = -errno;
-
- if (!st_valid && stat(fd_path, &st) < 0)
- return -errno;
-
- if (uid != UID_INVALID && st.st_uid != uid)
- return r;
- if (gid != GID_INVALID && st.st_gid != gid)
- return r;
- }
- }
-
- return 0;
+ return fchmod_and_chown(fd, mode, uid, gid);
}
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- bool st_valid = false;
+ bool do_chown, do_chmod;
struct stat st;
- int r;
- /* Under the assumption that we are running privileged we first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
+ /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no
+ * point in time the access mode is above the old access mode under the old ownership or the new
+ * access mode under the new ownership. Note: this call tries hard to leave the access mode
+ * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
+ * on chown().
+ *
+ * This call is happy with O_PATH fds. */
- if (mode != MODE_INVALID) {
- if ((mode & S_IFMT) != 0) {
+ if (fstat(fd, &st) < 0)
+ return -errno;
- if (fstat(fd, &st) < 0)
- return -errno;
+ do_chown =
+ (uid != UID_INVALID && st.st_uid != uid) ||
+ (gid != GID_INVALID && st.st_gid != gid);
- if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
- return -EINVAL;
+ do_chmod =
+ !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
+ ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
+ do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
+ * modifies the access mode too */
- st_valid = true;
- }
+ if (mode == MODE_INVALID)
+ mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
+ else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
+ return -EINVAL; /* insist on the right file type if it was specified */
- if (fchmod(fd, mode & 07777) < 0) {
- r = -errno;
+ if (do_chown && do_chmod) {
+ mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
- if (!st_valid && fstat(fd, &st) < 0)
+ if (((minimal ^ st.st_mode) & 07777) != 0)
+ if (fchmod_opath(fd, minimal & 07777) < 0)
return -errno;
-
- if ((mode & 07777) != (st.st_mode & 07777))
- return r;
-
- st_valid = true;
- }
}
- if (uid != UID_INVALID || gid != GID_INVALID)
- if (fchown(fd, uid, gid) < 0) {
- r = -errno;
-
- if (!st_valid && fstat(fd, &st) < 0)
- return -errno;
+ if (do_chown)
+ if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
+ return -errno;
- if (uid != UID_INVALID && st.st_uid != uid)
- return r;
- if (gid != GID_INVALID && st.st_gid != gid)
- return r;
- }
+ if (do_chmod)
+ if (fchmod_opath(fd, mode & 07777) < 0)
+ return -errno;
- return 0;
+ return do_chown || do_chmod;
}
#endif /* NM_IGNORED */
@@ -411,13 +363,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
* something fchown(), fchmod(), futimensat() don't allow. */
xsprintf(fdpath, "/proc/self/fd/%i", fd);
- if (mode != MODE_INVALID)
- if (chmod(fdpath, mode) < 0)
- ret = -errno;
-
- if (uid_is_valid(uid) || gid_is_valid(gid))
- if (chown(fdpath, uid, gid) < 0 && ret >= 0)
- ret = -errno;
+ ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
@@ -1043,9 +989,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Prefix what's left to do with what we just read, and start the loop again, but
* remain in the current directory. */
- joined = strjoin(destination, todo);
+ joined = path_join(destination, todo);
} else
- joined = strjoin("/", destination, todo);
+ joined = path_join("/", destination, todo);
if (!joined)
return -ENOMEM;
diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h
index b9651205e6..c5527cc44f 100644
--- a/shared/systemd/src/basic/fs-util.h
+++ b/shared/systemd/src/basic/fs-util.h
@@ -7,12 +7,20 @@
#include <stdbool.h>
#include <stdint.h>
#include <sys/inotify.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "errno-util.h"
#include "time-util.h"
+#define MODE_INVALID ((mode_t) -1)
+
+/* The following macros add 1 when converting things, since 0 is a valid mode, while the pointer
+ * NULL is special */
+#define PTR_TO_MODE(p) ((mode_t) ((uintptr_t) (p)-1))
+#define MODE_TO_PTR(u) ((void *) ((uintptr_t) (u)+1))
+
int unlink_noerrno(const char *path);
int rmdir_parents(const char *path, const char *stop);
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index 9418dbd820..4e7e247795 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -13,6 +13,7 @@
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"
+#include "missing.h"
#include "process-util.h"
#include "random-util.h"
#include "set.h"
@@ -287,7 +288,11 @@ _destructor_ static void cleanup_pools(void) {
/* The pool is only allocated by the main thread, but the memory can
* be passed to other threads. Let's clean up if we are the main thread
* and no other threads are live. */
- if (!is_main_thread())
+ /* We build our own is_main_thread() here, which doesn't use C11
+ * TLS based caching of the result. That's because valgrind apparently
+ * doesn't like malloc() (which C11 TLS internally uses) to be called
+ * from a GCC destructors. */
+ if (getpid() != gettid())
return;
r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t);
diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c
index 5899f62f3c..e6a0f63177 100644
--- a/shared/systemd/src/basic/in-addr-util.c
+++ b/shared/systemd/src/basic/in-addr-util.c
@@ -747,4 +747,16 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
}
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+
+static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
+ assert(addr);
+
+ siphash24_compress(addr, sizeof(*addr), state);
+}
+
+static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h
index a6a685b918..2ca7f4b32f 100644
--- a/shared/systemd/src/basic/in-addr-util.h
+++ b/shared/systemd/src/basic/in-addr-util.h
@@ -72,3 +72,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in6_addr_hash_ops;
diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c
index 3f47eff554..e0238ce7a5 100644
--- a/shared/systemd/src/basic/io-util.c
+++ b/shared/systemd/src/basic/io-util.c
@@ -269,4 +269,86 @@ char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *f
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
return x;
}
+
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
+ char *x;
+
+ x = set_iovec_string_field(iovec, n_iovec, field, value);
+ free(value);
+ return x;
+}
+
+struct iovec_wrapper *iovw_new(void) {
+ return malloc0(sizeof(struct iovec_wrapper));
+}
+
+void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
+ if (free_vectors)
+ for (size_t i = 0; i < iovw->count; i++)
+ free(iovw->iovec[i].iov_base);
+
+ iovw->iovec = mfree(iovw->iovec);
+ iovw->count = 0;
+ iovw->size_bytes = 0;
+}
+
+struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
+ iovw_free_contents(iovw, true);
+
+ return mfree(iovw);
+}
+
+struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
+ iovw_free_contents(iovw, false);
+
+ return mfree(iovw);
+}
+
+int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
+ if (iovw->count >= IOV_MAX)
+ return -E2BIG;
+
+ if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
+ return log_oom();
+
+ iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
+ return 0;
+}
+
+int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
+ _cleanup_free_ char *x = NULL;
+ int r;
+
+ x = strappend(field, value);
+ if (!x)
+ return log_oom();
+
+ r = iovw_put(iovw, x, strlen(x));
+ if (r >= 0)
+ TAKE_PTR(x);
+
+ return r;
+}
+
+int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
+ _cleanup_free_ _unused_ char *free_ptr = value;
+
+ return iovw_put_string_field(iovw, field, value);
+}
+
+void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
+ size_t i;
+
+ for (i = 0; i < iovw->count; i++)
+ iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
+}
+
+size_t iovw_size(struct iovec_wrapper *iovw) {
+ size_t n = 0, i;
+
+ for (i = 0; i < iovw->count; i++)
+ n += iovw->iovec[i].iov_len;
+
+ return n;
+}
#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/io-util.h b/shared/systemd/src/basic/io-util.h
index 792a64ad5e..719e19e85d 100644
--- a/shared/systemd/src/basic/io-util.h
+++ b/shared/systemd/src/basic/io-util.h
@@ -73,3 +73,20 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
+
+struct iovec_wrapper {
+ struct iovec *iovec;
+ size_t count;
+ size_t size_bytes;
+};
+
+struct iovec_wrapper *iovw_new(void);
+struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
+struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
+void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
+int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
+int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
+int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
+void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
+size_t iovw_size(struct iovec_wrapper *iovw);
diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h
index 915c24a5dd..0e8957b783 100644
--- a/shared/systemd/src/basic/memory-util.h
+++ b/shared/systemd/src/basic/memory-util.h
@@ -37,8 +37,8 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
#define memzero(x,l) \
({ \
size_t _l_ = (l); \
- void *_x_ = (x); \
- _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \
+ if (_l_ > 0) \
+ memset(x, 0, _l_); \
})
#define zero(x) (memzero(&(x), sizeof(x)))
diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c
index 02ef426f68..76ef6e093b 100644
--- a/shared/systemd/src/basic/parse-util.c
+++ b/shared/systemd/src/basic/parse-util.c
@@ -6,6 +6,7 @@
#include <inttypes.h>
#include <linux/oom.h>
#include <locale.h>
+#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -86,6 +87,9 @@ int parse_mode(const char *s, mode_t *ret) {
int parse_ifindex(const char *s, int *ret) {
int ifi, r;
+ assert(s);
+ assert(ret);
+
r = safe_atoi(s, &ifi);
if (r < 0)
return r;
@@ -96,6 +100,24 @@ int parse_ifindex(const char *s, int *ret) {
return 0;
}
+int parse_ifindex_or_ifname(const char *s, int *ret) {
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ r = parse_ifindex(s, ret);
+ if (r >= 0)
+ return r;
+
+ r = (int) if_nametoindex(s);
+ if (r <= 0)
+ return -errno;
+
+ *ret = r;
+ return 0;
+}
+
int parse_mtu(int family, const char *s, uint32_t *ret) {
uint64_t u;
size_t m;
@@ -342,47 +364,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
return 0;
}
-
-char *format_bytes(char *buf, size_t l, uint64_t t) {
- unsigned i;
-
- /* This only does IEC units so far */
-
- static const struct {
- const char *suffix;
- uint64_t factor;
- } table[] = {
- { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "M", UINT64_C(1024)*UINT64_C(1024) },
- { "K", UINT64_C(1024) },
- };
-
- if (t == (uint64_t) -1)
- return NULL;
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
-
- if (t >= table[i].factor) {
- snprintf(buf, l,
- "%" PRIu64 ".%" PRIu64 "%s",
- t / table[i].factor,
- ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
- table[i].suffix);
-
- goto finish;
- }
- }
-
- snprintf(buf, l, "%" PRIu64 "B", t);
-
-finish:
- buf[l-1] = 0;
- return buf;
-
-}
#endif /* NM_IGNORED */
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h
index e47641b429..3a70b79276 100644
--- a/shared/systemd/src/basic/parse-util.h
+++ b/shared/systemd/src/basic/parse-util.h
@@ -9,13 +9,12 @@
#include "macro.h"
-#define MODE_INVALID ((mode_t) -1)
-
int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s, int *ret);
+int parse_ifindex_or_ifname(const char *s, int *ret);
int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
@@ -23,9 +22,6 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);
int parse_syscall_and_errno(const char *in, char **name, int *error);
-#define FORMAT_BYTES_MAX 8
-char *format_bytes(char *buf, size_t l, uint64_t t);
-
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {
diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c
index 29955a3632..7fd6ac185b 100644
--- a/shared/systemd/src/basic/path-util.c
+++ b/shared/systemd/src/basic/path-util.c
@@ -71,10 +71,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
- if (endswith(prefix, "/"))
- return strjoin(prefix, p);
- else
- return strjoin(prefix, "/", p);
+ return path_join(prefix, p);
}
int safe_getcwd(char **ret) {
@@ -259,7 +256,7 @@ char **path_strv_resolve(char **l, const char *root) {
if (root) {
orig = *s;
- t = prefix_root(root, orig);
+ t = path_join(root, orig);
if (!t) {
enomem = true;
continue;
@@ -586,7 +583,7 @@ int find_binary(const char *name, char **ret) {
if (!path_is_absolute(element))
continue;
- j = strjoin(element, "/", name);
+ j = path_join(element, name);
if (!j)
return -ENOMEM;
@@ -691,40 +688,6 @@ int mkfs_exists(const char *fstype) {
return binary_is_good(mkfs);
}
-char *prefix_root(const char *root, const char *path) {
- char *n, *p;
- size_t l;
-
- /* If root is passed, prefixes path with it. Otherwise returns
- * it as is. */
-
- assert(path);
-
- /* First, drop duplicate prefixing slashes from the path */
- while (path[0] == '/' && path[1] == '/')
- path++;
-
- if (empty_or_root(root))
- return strdup(path);
-
- l = strlen(root) + 1 + strlen(path) + 1;
-
- n = new(char, l);
- if (!n)
- return NULL;
-
- p = stpcpy(n, root);
-
- while (p > n && p[-1] == '/')
- p--;
-
- if (path[0] != '/')
- *(p++) = '/';
-
- strcpy(p, path);
- return n;
-}
-
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
char *p;
int r;
@@ -1034,7 +997,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
_cleanup_free_ char *path = NULL;
char *c, **name;
- path = prefix_root(root, pattern);
+ path = path_join(root, pattern);
if (!path)
return -ENOMEM;
diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h
index 5204adaa10..64e40f4d3a 100644
--- a/shared/systemd/src/basic/path-util.h
+++ b/shared/systemd/src/basic/path-util.h
@@ -118,10 +118,8 @@ int mkfs_exists(const char *fstype);
_slash && ((*_slash = 0), true); \
_slash = strrchr((prefix), '/'))
-char *prefix_root(const char *root, const char *path);
-
-/* Similar to prefix_root(), but returns an alloca() buffer, or
- * possibly a const pointer into the path parameter */
+/* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns
+ * an alloca() buffer, or possibly a const pointer into the path parameter. */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
@@ -129,7 +127,7 @@ char *prefix_root(const char *root, const char *path);
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
- if (empty_or_root(_root)) \
+ if (isempty(_root)) \
_ret = _path; \
else { \
_l = strlen(_root) + 1 + strlen(_path) + 1; \
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index cdce8e36b5..317815fe05 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -6,7 +6,6 @@
#include <errno.h>
#include <limits.h>
#include <linux/oom.h>
-#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
@@ -29,10 +28,12 @@
#include "alloc-util.h"
#include "architecture.h"
#include "escape.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "ioprio.h"
+#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
@@ -47,9 +48,16 @@
#include "string-util.h"
#include "terminal-util.h"
#include "user-util.h"
+#include "utf8.h"
#if 0 /* NM_IGNORED */
-int get_process_state(pid_t pid) {
+
+/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own
+ * workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel.
+ */
+#define COMM_MAX_LEN 128
+
+static int get_process_state(pid_t pid) {
const char *p;
char state;
int r;
@@ -85,7 +93,7 @@ int get_process_comm(pid_t pid, char **ret) {
assert(ret);
assert(pid >= 0);
- escaped = new(char, TASK_COMM_LEN);
+ escaped = new(char, COMM_MAX_LEN);
if (!escaped)
return -ENOMEM;
@@ -98,28 +106,31 @@ int get_process_comm(pid_t pid, char **ret) {
return r;
/* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
- cellescape(escaped, TASK_COMM_LEN, comm);
+ cellescape(escaped, COMM_MAX_LEN, comm);
*ret = TAKE_PTR(escaped);
return 0;
}
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_fclose_ FILE *f = NULL;
- bool space = false;
- char *k;
- _cleanup_free_ char *ans = NULL;
+ _cleanup_free_ char *t = NULL, *ans = NULL;
const char *p;
- int c, r;
+ int r;
+ size_t k;
+
+ /* This is supposed to be a safety guard against runaway command lines. */
+ size_t max_length = sc_arg_max();
assert(line);
assert(pid >= 0);
- /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
- * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
- * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
- * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
- * command line that resolves to the empty string will return the "comm" name of the process instead.
+ /* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (�). If
+ * max_columns is != -1 will return a string of the specified console width at most, abbreviated with
+ * an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
+ * line set (the case for kernel threads), or has a command line that resolves to the empty string
+ * will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
+ * input data.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* comm_fallback is false). Returns 0 and sets *line otherwise. */
@@ -131,130 +142,56 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
if (r < 0)
return r;
- if (max_length == 0) {
- /* This is supposed to be a safety guard against runaway command lines. */
- long l = sysconf(_SC_ARG_MAX);
- assert(l > 0);
- max_length = l;
- }
-
- if (max_length == 1) {
+ /* We assume that each four-byte character uses one or two columns. If we ever check for combining
+ * characters, this assumption will need to be adjusted. */
+ if ((size_t) 4 * max_columns + 1 < max_columns)
+ max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
- /* If there's only room for one byte, return the empty string */
- ans = new0(char, 1);
- if (!ans)
- return -ENOMEM;
+ t = new(char, max_length);
+ if (!t)
+ return -ENOMEM;
- *line = TAKE_PTR(ans);
- return 0;
+ k = fread(t, 1, max_length, f);
+ if (k > 0) {
+ /* Arguments are separated by NULs. Let's replace those with spaces. */
+ for (size_t i = 0; i < k - 1; i++)
+ if (t[i] == '\0')
+ t[i] = ' ';
+ t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
} else {
- bool dotdotdot = false;
- size_t left;
-
- ans = new(char, max_length);
- if (!ans)
- return -ENOMEM;
-
- k = ans;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
-
- if (space) {
- if (left <= 2) {
- dotdotdot = true;
- break;
- }
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 1) {
- dotdotdot = true;
- break;
- }
-
- *(k++) = (char) c;
- left--;
- } else if (k > ans)
- space = true;
- }
-
- if (dotdotdot) {
- if (max_length <= 4) {
- k = ans;
- left = max_length;
- } else {
- k = ans + max_length - 4;
- left = 4;
-
- /* Eat up final spaces */
- while (k > ans && isspace(k[-1])) {
- k--;
- left++;
- }
- }
-
- strncpy(k, "...", left-1);
- k[left-1] = 0;
- } else
- *k = 0;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(ans)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- ans = mfree(ans);
+ /* We only treat getting nothing as an error. We *could* also get an error after reading some
+ * data, but we ignore that case, as such an error is rather unlikely and we prefer to get
+ * some data rather than none. */
+ if (ferror(f))
+ return -errno;
- if (!comm_fallback)
+ if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- size_t l = strlen(t);
+ /* Kernel threads have no argv[] */
+ _cleanup_free_ char *t2 = NULL;
- if (l + 3 <= max_length) {
- ans = strjoin("[", t, "]");
- if (!ans)
- return -ENOMEM;
-
- } else if (max_length <= 6) {
- ans = new(char, max_length);
- if (!ans)
- return -ENOMEM;
+ r = get_process_comm(pid, &t2);
+ if (r < 0)
+ return r;
- memcpy(ans, "[...]", max_length-1);
- ans[max_length-1] = 0;
- } else {
- t[max_length - 6] = 0;
+ mfree(t);
+ t = strjoin("[", t2, "]");
+ if (!t)
+ return -ENOMEM;
+ }
- /* Chop off final spaces */
- delete_trailing_chars(t, WHITESPACE);
+ delete_trailing_chars(t, WHITESPACE);
- ans = strjoin("[", t, "...]");
- if (!ans)
- return -ENOMEM;
- }
+ bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
- *line = TAKE_PTR(ans);
- return 0;
- }
-
- k = realloc(ans, strlen(ans) + 1);
- if (!k)
+ ans = escape_non_printable_full(t, max_columns, eight_bit);
+ if (!ans)
return -ENOMEM;
- ans = NULL;
- *line = k;
-
+ (void) str_realloc(&ans);
+ *line = TAKE_PTR(ans);
return 0;
}
@@ -286,7 +223,7 @@ int rename_process(const char name[]) {
* can use PR_SET_NAME, which sets the thread name for the calling thread. */
if (prctl(PR_SET_NAME, name) < 0)
log_debug_errno(errno, "PR_SET_NAME failed: %m");
- if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */
+ if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
truncated = true;
/* Second step, change glibc's ID of the process name. */
@@ -1543,45 +1480,11 @@ int set_oom_score_adjust(int value) {
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
}
-int cpus_in_affinity_mask(void) {
- size_t n = 16;
- int r;
-
- for (;;) {
- cpu_set_t *c;
-
- c = CPU_ALLOC(n);
- if (!c)
- return -ENOMEM;
-
- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
- int k;
-
- k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
- CPU_FREE(c);
-
- if (k <= 0)
- return -EINVAL;
-
- return k;
- }
-
- r = -errno;
- CPU_FREE(c);
-
- if (r != -EINVAL)
- return r;
- if (n > SIZE_MAX/2)
- return -ENOMEM;
- n *= 2;
- }
-}
-
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
[IOPRIO_CLASS_BE] = "best-effort",
- [IOPRIO_CLASS_IDLE] = "idle"
+ [IOPRIO_CLASS_IDLE] = "idle",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
@@ -1602,7 +1505,7 @@ static const char* const sched_policy_table[] = {
[SCHED_BATCH] = "batch",
[SCHED_IDLE] = "idle",
[SCHED_FIFO] = "fifo",
- [SCHED_RR] = "rr"
+ [SCHED_RR] = "rr",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 3933bee64b..3c941f50a2 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -31,9 +31,13 @@
_r_; \
})
-int get_process_state(pid_t pid);
+typedef enum ProcessCmdlineFlags {
+ PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
+ PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
+} ProcessCmdlineFlags;
+
int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
@@ -196,5 +200,3 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
(pid) = 0; \
_pid_; \
})
-
-int cpus_in_affinity_mask(void);
diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c
index e7fedea880..c70871bfd7 100644
--- a/shared/systemd/src/basic/random-util.c
+++ b/shared/systemd/src/basic/random-util.c
@@ -35,6 +35,66 @@
int rdrand(unsigned long *ret) {
+ /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here,
+ * instead of sticking to /dev/urandom or getrandom()?
+ *
+ * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and
+ * getrandom() is generally not initialized yet. It is very common that initialization of the random
+ * pool takes a longer time (up to many minutes), in particular on embedded devices that have no
+ * explicit hardware random generator, as well as in virtualized environments such as major cloud
+ * installations that do not provide virtio-rng or a similar mechanism.
+ *
+ * In such an environment using getrandom() synchronously means we'd block the entire system boot-up
+ * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK)
+ * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean
+ * generating many kmsg log messages about our use of it before the random pool is properly
+ * initialized. Neither of these outcomes is desirable.
+ *
+ * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND
+ * provides us quickly and relatively reliably with random values, without having to delay boot,
+ * without triggering warning messages in kmsg.
+ *
+ * Note that we use RDRAND only under very specific circumstances, when the requirements on the
+ * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use
+ * RDRAND:
+ *
+ * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic
+ * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be
+ * generated in a way that is reliably unique, but they do not require ultimate trust into
+ * the entropy generator. systemd generates a number of UUIDs during early boot, including
+ * 'invocation IDs' for every unit spawned that identify the specific invocation of the
+ * service globally, and a number of others. Other alternatives for generating these UUIDs
+ * have been considered, but don't really work: for example, hashing uuids from a local
+ * system identifier combined with a counter falls flat because during early boot disk
+ * storage is not yet available (think: initrd) and thus a system-specific ID cannot be
+ * stored or retrieved yet.
+ *
+ * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are
+ * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive
+ * O(n) access complexity if an attacker manages to trigger a large number of hash
+ * collisions. Thus, systemd (as any software employing hash tables should) uses seeded
+ * hash functions for its hash tables, with a seed generated randomly. The hash tables
+ * systemd employs watch the fill level closely and reseed if necessary. This allows use of
+ * a low quality RNG initially, as long as it improves should a hash table be under attack:
+ * the attacker after all needs to to trigger many collisions to exploit it for the purpose
+ * of DoS, but if doing so improves the seed the attack surface is reduced as the attack
+ * takes place.
+ *
+ * Some cases where we do NOT use RDRAND are:
+ *
+ * • Generation of cryptographic key material 🔑
+ *
+ * • Generation of cryptographic salt values 🧂
+ *
+ * This function returns:
+ *
+ * -EOPNOTSUPP → RDRAND is not available on this system 😔
+ * -EAGAIN → The operation failed this time, but is likely to work if you try again a few
+ * times ♻
+ * -EUCLEAN → We got some random value, but it looked strange, so we refused using it.
+ * This failure might or might not be temporary. 😕
+ */
+
#if defined(__i386__) || defined(__x86_64__)
static int have_rdrand = -1;
unsigned long v;
@@ -92,10 +152,20 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
bool got_some = false;
int r;
- /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This
- * call won't block, unless the RANDOM_BLOCK flag is set. If RANDOM_MAY_FAIL is set, an error is
- * returned if the random pool is not initialized. Otherwise it will always return some data from the
- * kernel, regardless of whether the random pool is fully initialized or not. */
+ /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
+ * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
+ * flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not
+ * initialized. Otherwise it will always return some data from the kernel, regardless of whether the
+ * random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not
+ * enough better quality randomness could be acquired, the rest is filled up with low quality
+ * randomness.
+ *
+ * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND
+ * or even RANDOM_EXTEND_WITH_PSEUDO.
+ *
+ * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use
+ * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via
+ * an "all bets are off" wrapper, such as random_bytes(), see below. */
if (n == 0)
return 0;
@@ -264,6 +334,11 @@ void initialize_srand(void) {
void pseudo_random_bytes(void *p, size_t n) {
uint8_t *q;
+ /* This returns pseudo-random data using libc's rand() function. You probably never want to call this
+ * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes()
+ * instead, see below: it will fall back to this function if there's nothing better to get, but only
+ * then. */
+
initialize_srand();
for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
@@ -285,6 +360,38 @@ void pseudo_random_bytes(void *p, size_t n) {
void random_bytes(void *p, size_t n) {
+ /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason
+ * it is not available we'll try some crappy fallbacks.
+ *
+ * What this function will do:
+ *
+ * • This function will preferably use the CPU's RDRAND operation, if it is available, in
+ * order to return "mid-quality" random values cheaply.
+ *
+ * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are
+ * cheaply available.
+ *
+ * • This function will return pseudo-random data, generated via libc rand() if nothing
+ * better is available.
+ *
+ * • This function will work fine in early boot
+ *
+ * • This function will always succeed
+ *
+ * What this function won't do:
+ *
+ * • This function will never fail: it will give you randomness no matter what. It might not
+ * be high quality, but it will return some, possibly generated via libc's rand() call.
+ *
+ * • This function will never block: if the only way to get good randomness is a blocking,
+ * synchronous getrandom() we'll instead provide you with pseudo-random data.
+ *
+ * This function is hence great for things like seeding hash tables, generating random numeric UNIX
+ * user IDs (that are checked for collisions before use) and such.
+ *
+ * This function is hence not useful for generating UUIDs or cryptographic key material.
+ */
+
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0)
return;
diff --git a/shared/systemd/src/basic/siphash24.h b/shared/systemd/src/basic/siphash24.h
index be1d3e009b..c4e919dfbc 100644
--- a/shared/systemd/src/basic/siphash24.h
+++ b/shared/systemd/src/basic/siphash24.h
@@ -47,6 +47,7 @@ siphash24 (const void *in, size_t inlen, const uint8_t k[16])
void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+void siphash24_compress_boolean(bool in, struct siphash *state);
#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
uint64_t siphash24_finalize(struct siphash *state);
diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c
index b55c6c31d5..334c00e727 100644
--- a/shared/systemd/src/basic/socket-util.c
+++ b/shared/systemd/src/basic/socket-util.c
@@ -1395,7 +1395,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
}
int socket_bind_to_ifindex(int fd, int ifindex) {
- char ifname[IFNAMSIZ] = "";
+ char ifname[IF_NAMESIZE + 1];
assert(fd >= 0);
@@ -1413,7 +1413,7 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
return -errno;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
- if (!if_indextoname(ifindex, ifname))
+ if (!format_ifname(ifindex, ifname))
return -errno;
return socket_bind_to_ifname(fd, ifname);
diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h
index 15443f1ef5..4c3a96399d 100644
--- a/shared/systemd/src/basic/socket-util.h
+++ b/shared/systemd/src/basic/socket-util.h
@@ -9,6 +9,7 @@
#include <netinet/in.h>
#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index a630856236..47b17c9d3e 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -257,3 +257,16 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch
return (uint8_t*) p + n;
}
+
+static inline char* str_realloc(char **p) {
+ /* Reallocate *p to actual size */
+
+ if (!*p)
+ return NULL;
+
+ char *t = realloc(*p, strlen(*p) + 1);
+ if (!t)
+ return NULL;
+
+ return (*p = t);
+}
diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c
index 14b17bfc1d..82f0911555 100644
--- a/shared/systemd/src/basic/time-util.c
+++ b/shared/systemd/src/basic/time-util.c
@@ -580,7 +580,6 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
*/
assert(t);
- assert(usec);
if (t[0] == '@' && !with_tz)
return parse_sec(t + 1, usec);
@@ -808,8 +807,8 @@ finish:
else
return -EINVAL;
- *usec = ret;
-
+ if (usec)
+ *usec = ret;
return 0;
}
@@ -866,7 +865,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
- if (tmp.return_value == 0)
+ if (tmp.return_value == 0 && usec)
*usec = tmp.usec;
return tmp.return_value;
@@ -928,7 +927,6 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
bool something = false;
assert(t);
- assert(usec);
assert(default_unit > 0);
p = t;
@@ -940,7 +938,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
if (*s != 0)
return -EINVAL;
- *usec = USEC_INFINITY;
+ if (usec)
+ *usec = USEC_INFINITY;
return 0;
}
@@ -1012,8 +1011,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
}
}
- *usec = r;
-
+ if (usec)
+ *usec = r;
return 0;
}
diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c
index aec9f1cd11..d8a689e08d 100644
--- a/shared/systemd/src/basic/tmpfile-util.c
+++ b/shared/systemd/src/basic/tmpfile-util.c
@@ -59,13 +59,11 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) {
- _cleanup_umask_ mode_t u = 0;
+ _unused_ _cleanup_umask_ mode_t u = umask(0077);
int fd;
assert(pattern);
- u = umask(077);
-
fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -326,7 +324,7 @@ int mkdtemp_malloc(const char *template, char **ret) {
if (r < 0)
return r;
- p = strjoin(tmp, "/XXXXXX");
+ p = path_join(tmp, "XXXXXX");
}
if (!p)
return -ENOMEM;
diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c
index f1c6ac1faf..3c51fa1ff6 100644
--- a/shared/systemd/src/basic/utf8.c
+++ b/shared/systemd/src/basic/utf8.c
@@ -34,6 +34,7 @@
#include "gunicode.h"
#include "hexdecoct.h"
#include "macro.h"
+#include "string-util.h"
#include "utf8.h"
bool unichar_is_valid(char32_t ch) {
@@ -198,47 +199,94 @@ char *utf8_escape_invalid(const char *str) {
}
*s = '\0';
-
+ (void) str_realloc(&p);
return p;
}
#if 0 /* NM_IGNORED */
-char *utf8_escape_non_printable(const char *str) {
- char *p, *s;
+static int utf8_char_console_width(const char *str) {
+ char32_t c;
+ int r;
+
+ r = utf8_encoded_to_unichar(str, &c);
+ if (r < 0)
+ return r;
+
+ /* TODO: we should detect combining characters */
+
+ return unichar_iswide(c) ? 2 : 1;
+}
+
+char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
+ char *p, *s, *prev_s;
+ size_t n = 0; /* estimated print width */
assert(str);
- p = s = malloc(strlen(str) * 4 + 1);
+ if (console_width == 0)
+ return strdup("");
+
+ p = s = prev_s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
- while (*str) {
+ for (;;) {
int len;
+ char *saved_s = s;
+
+ if (!*str) /* done! */
+ goto finish;
len = utf8_encoded_valid_unichar(str, (size_t) -1);
if (len > 0) {
if (utf8_is_printable(str, len)) {
+ int w;
+
+ w = utf8_char_console_width(str);
+ assert(w >= 0);
+ if (n + w > console_width)
+ goto truncation;
+
s = mempcpy(s, str, len);
str += len;
+ n += w;
+
} else {
- while (len > 0) {
+ for (; len > 0; len--) {
+ if (n + 4 > console_width)
+ goto truncation;
+
*(s++) = '\\';
*(s++) = 'x';
*(s++) = hexchar((int) *str >> 4);
*(s++) = hexchar((int) *str);
str += 1;
- len--;
+ n += 4;
}
}
} else {
- s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+ if (n + 1 > console_width)
+ goto truncation;
+
+ s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
str += 1;
+ n += 1;
}
+
+ prev_s = saved_s;
}
- *s = '\0';
+ truncation:
+ /* Try to go back one if we don't have enough space for the ellipsis */
+ if (n + 1 >= console_width)
+ s = prev_s;
+
+ s = mempcpy(s, "…", strlen("…"));
+ finish:
+ *s = '\0';
+ (void) str_realloc(&p);
return p;
}
#endif /* NM_IGNORED */
@@ -532,15 +580,15 @@ size_t utf8_console_width(const char *str) {
/* Returns the approximate width a string will take on screen when printed on a character cell
* terminal/console. */
- while (*str != 0) {
- char32_t c;
+ while (*str) {
+ int w;
- if (utf8_encoded_to_unichar(str, &c) < 0)
+ w = utf8_char_console_width(str);
+ if (w < 0)
return (size_t) -1;
+ n += w;
str = utf8_next_char(str);
-
- n += unichar_iswide(c) ? 2 : 1;
}
return n;
diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h
index 6df70921db..62e99b7280 100644
--- a/shared/systemd/src/basic/utf8.h
+++ b/shared/systemd/src/basic/utf8.h
@@ -22,7 +22,10 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pu
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
-char *utf8_escape_non_printable(const char *str);
+char *utf8_escape_non_printable_full(const char *str, size_t console_width);
+static inline char *utf8_escape_non_printable(const char *str) {
+ return utf8_escape_non_printable_full(str, (size_t) -1);
+}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
size_t utf16_encode_unichar(char16_t *out, char32_t c);
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
index e3af362d2f..b28e0ba084 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.c
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -197,7 +197,7 @@ int dhcp_identifier_set_iaid(
/* device is under renaming */
return -EBUSY;
- name = net_get_name(device);
+ name = net_get_name_persistent(device);
}
}
diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c
index eaf044bf3f..8f65737e31 100644
--- a/src/systemd/src/libsystemd-network/dhcp-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp-option.c
@@ -199,7 +199,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
if (memchr(option, 0, len - 1))
return -EINVAL;
- string = strndup((const char *) option, len);
+ string = memdup_suffix0((const char *) option, len);
if (!string)
return -ENOMEM;
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 1c339613f4..07d459f6fe 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -14,6 +14,7 @@
#include "conf-parser.h"
#include "device-util.h"
#include "dhcp-lease-internal.h"
+#include "env-util.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "log.h"
@@ -27,7 +28,7 @@
#include "util.h"
#if 0 /* NM_IGNORED */
-const char *net_get_name(sd_device *device) {
+const char *net_get_name_persistent(sd_device *device) {
const char *name, *field;
assert(device);
@@ -42,7 +43,7 @@ const char *net_get_name(sd_device *device) {
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) {
size_t l, sz = 0;
const char *name;
int r;
@@ -50,10 +51,10 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
assert(device);
- /* net_get_name() will return one of the device names based on stable information about the
- * device. If this is not available, we fall back to using the device name. */
- name = net_get_name(device);
- if (!name)
+ /* net_get_name_persistent() will return one of the device names based on stable information about
+ * the device. If this is not available, we fall back to using the actual device name. */
+ name = net_get_name_persistent(device);
+ if (!name && use_sysname)
(void) sd_device_get_sysname(device, &name);
if (!name)
return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
@@ -76,26 +77,66 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
return 0;
}
-static bool net_condition_test_strv(char * const *raw_patterns,
- const char *string) {
- if (strv_isempty(raw_patterns))
+static bool net_condition_test_strv(char * const *patterns, const char *string) {
+ char * const *p;
+ bool match = false, has_positive_rule = false;
+
+ if (strv_isempty(patterns))
return true;
- /* If the patterns begin with "!", edit it out and negate the test. */
- if (raw_patterns[0][0] == '!') {
- char **patterns;
- size_t i, length;
+ STRV_FOREACH(p, patterns) {
+ const char *q = *p;
+ bool invert;
+
+ invert = *q == '!';
+ q += invert;
+
+ if (!invert)
+ has_positive_rule = true;
+
+ if (string && fnmatch(q, string, 0) == 0) {
+ if (invert)
+ return false;
+ else
+ match = true;
+ }
+ }
+
+ return has_positive_rule ? match : true;
+}
+
+static int net_condition_test_property(char * const *match_property, sd_device *device) {
+ char * const *p;
+
+ if (strv_isempty(match_property))
+ return true;
+
+ STRV_FOREACH(p, match_property) {
+ _cleanup_free_ char *key = NULL;
+ const char *val, *dev_val;
+ bool invert, v;
+
+ invert = **p == '!';
+
+ val = strchr(*p + invert, '=');
+ if (!val)
+ return -EINVAL;
- length = strv_length(raw_patterns) + 1; /* Include the NULL. */
- patterns = newa(char*, length);
- patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */
- for (i = 1; i < length; i++)
- patterns[i] = raw_patterns[i];
+ key = strndup(*p + invert, val - *p - invert);
+ if (!key)
+ return -ENOMEM;
+
+ val++;
- return !string || !strv_fnmatch(patterns, string, 0);
+ v = device &&
+ sd_device_get_property_value(device, key, &dev_val) >= 0 &&
+ fnmatch(val, dev_val, 0) == 0;
+
+ if (invert ? v : !v)
+ return false;
}
- return string && strv_fnmatch(raw_patterns, string, 0);
+ return true;
}
bool net_match_config(Set *match_mac,
@@ -103,12 +144,25 @@ bool net_match_config(Set *match_mac,
char * const *match_drivers,
char * const *match_types,
char * const *match_names,
+ char * const *match_property,
+ sd_device *device,
const struct ether_addr *dev_mac,
- const char *dev_path,
- const char *dev_driver,
- const char *dev_type,
const char *dev_name) {
+ const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
+
+ if (device) {
+ (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
+ (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
+ (void) sd_device_get_devtype(device, &dev_type);
+
+ if (!dev_name)
+ (void) sd_device_get_sysname(device, &dev_name);
+ if (!dev_mac &&
+ sd_device_get_sysattr_value(device, "address", &mac_str) >= 0)
+ dev_mac = ether_aton(mac_str);
+ }
+
if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
return false;
@@ -124,6 +178,9 @@ bool net_match_config(Set *match_mac,
if (!net_condition_test_strv(match_names, dev_name))
return false;
+ if (!net_condition_test_property(match_property, device))
+ return false;
+
return true;
}
@@ -167,7 +224,7 @@ int config_parse_net_condition(const char *unit,
return 0;
}
-int config_parse_ifnames(
+int config_parse_match_strv(
const char *unit,
const char *filename,
unsigned line,
@@ -179,7 +236,9 @@ int config_parse_ifnames(
void *data,
void *userdata) {
+ const char *p = rvalue;
char ***sv = data;
+ bool invert;
int r;
assert(filename);
@@ -187,30 +246,154 @@ int config_parse_ifnames(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ *sv = strv_free(*sv);
+ return 0;
+ }
+
+ invert = *p == '!';
+ p += invert;
+
for (;;) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&rvalue, &word, NULL, 0);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+ if (r == 0)
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
return 0;
}
+
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
+}
+
+int config_parse_match_ifnames(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *p = rvalue;
+ char ***sv = data;
+ bool invert;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ invert = *p == '!';
+ p += invert;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&p, &word, NULL, 0);
if (r == 0)
- break;
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse interface name list: %s", rvalue);
+ return 0;
+ }
if (!ifname_valid(word)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
- return 0;
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Interface name is not valid or too long, ignoring assignment: %s", word);
+ continue;
}
- r = strv_push(sv, word);
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
if (r < 0)
return log_oom();
-
- word = NULL;
}
+}
- return 0;
+int config_parse_match_property(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *p = rvalue;
+ char ***sv = data;
+ bool invert;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ invert = *p == '!';
+ p += invert;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+ if (r == 0)
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (!env_assignment_is_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid property or value, ignoring assignment: %s", word);
+ continue;
+ }
+
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
}
int config_parse_ifalias(const char *unit,
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index d895103b5a..487421fbd9 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -14,27 +14,28 @@
#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128
#define LINK_BRIDGE_PORT_PRIORITY_MAX 63
+#if 0 /* NM_IGNORED */
bool net_match_config(Set *match_mac,
char * const *match_path,
char * const *match_driver,
char * const *match_type,
char * const *match_name,
+ char * const *match_property,
+ sd_device *device,
const struct ether_addr *dev_mac,
- const char *dev_path,
- const char *dev_driver,
- const char *dev_type,
const char *dev_name);
-#if 0 /* NM_IGNORED */
CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
-CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_strv);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_property);
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
-int net_get_unique_predictable_data(sd_device *device, uint64_t *result);
-const char *net_get_name(sd_device *device);
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result);
+const char *net_get_name_persistent(sd_device *device);
#endif /* NM_IGNORED */
size_t serialize_in_addrs(FILE *f,
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 28e6eb8555..28feda064b 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -1389,6 +1389,23 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
return 0;
}
+static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
+ if (a->address != b->address)
+ return false;
+
+ if (a->subnet_mask != b->subnet_mask)
+ return false;
+
+ if (a->router_size != b->router_size)
+ return false;
+
+ for (size_t i = 0; i < a->router_size; i++)
+ if (a->router[i].s_addr != b->router[i].s_addr)
+ return false;
+
+ return true;
+}
+
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char *error_message = NULL;
@@ -1441,12 +1458,10 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
- if (client->lease->address != lease->address ||
- client->lease->subnet_mask != lease->subnet_mask ||
- client->lease->router != lease->router) {
- r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
- } else
+ if (lease_equal(client->lease, lease))
r = SD_DHCP_CLIENT_EVENT_RENEW;
+ else
+ r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
client->lease = sd_dhcp_lease_unref(client->lease);
}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index 13851da196..762c19f4ff 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -334,7 +334,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
if (memchr(option, 0, len - 1))
return -EINVAL;
- string = strndup((const char *) option, len);
+ string = memdup_suffix0((const char *) option, len);
if (!string)
return -ENOMEM;