diff options
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; |