summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-06-13 15:53:00 +0200
committerThomas Haller <thaller@redhat.com>2020-06-13 17:08:22 +0200
commitecf32c2c8f8c97d64f0794e0c64c80ef05293ffc (patch)
tree58de978b29d0ee9bc19cb5a27cddb3ae4f069e44
parent707d7c9ef91826bf11a2d3fdcbc29c9c7c280eac (diff)
parentbbf57b114e13bea52120d19e90662a274dad4536 (diff)
downloadNetworkManager-ecf32c2c8f8c97d64f0794e0c64c80ef05293ffc.tar.gz
systemd: merge branch systemd into master
-rw-r--r--Makefile.am5
-rw-r--r--shared/systemd/sd-adapt-shared/blockdev-util.h3
-rw-r--r--shared/systemd/src/basic/cgroup-util.h25
-rw-r--r--shared/systemd/src/basic/errno-util.h8
-rw-r--r--shared/systemd/src/basic/escape.c18
-rw-r--r--shared/systemd/src/basic/escape.h9
-rw-r--r--shared/systemd/src/basic/fd-util.c31
-rw-r--r--shared/systemd/src/basic/fileio.c83
-rw-r--r--shared/systemd/src/basic/fileio.h8
-rw-r--r--shared/systemd/src/basic/fs-util.c189
-rw-r--r--shared/systemd/src/basic/fs-util.h13
-rw-r--r--shared/systemd/src/basic/hash-funcs.c6
-rw-r--r--shared/systemd/src/basic/hash-funcs.h2
-rw-r--r--shared/systemd/src/basic/hashmap.c105
-rw-r--r--shared/systemd/src/basic/hashmap.h179
-rw-r--r--shared/systemd/src/basic/hexdecoct.c21
-rw-r--r--shared/systemd/src/basic/hostname-util.c48
-rw-r--r--shared/systemd/src/basic/hostname-util.h1
-rw-r--r--shared/systemd/src/basic/in-addr-util.c74
-rw-r--r--shared/systemd/src/basic/in-addr-util.h1
-rw-r--r--shared/systemd/src/basic/io-util.c30
-rw-r--r--shared/systemd/src/basic/macro.h35
-rw-r--r--shared/systemd/src/basic/memory-util.h2
-rw-r--r--shared/systemd/src/basic/missing_random.h4
-rw-r--r--shared/systemd/src/basic/missing_socket.h5
-rw-r--r--shared/systemd/src/basic/parse-util.c181
-rw-r--r--shared/systemd/src/basic/parse-util.h18
-rw-r--r--shared/systemd/src/basic/path-util.c2
-rw-r--r--shared/systemd/src/basic/process-util.c151
-rw-r--r--shared/systemd/src/basic/process-util.h20
-rw-r--r--shared/systemd/src/basic/random-util.c25
-rw-r--r--shared/systemd/src/basic/random-util.h1
-rw-r--r--shared/systemd/src/basic/set.h52
-rw-r--r--shared/systemd/src/basic/socket-util.c94
-rw-r--r--shared/systemd/src/basic/socket-util.h23
-rw-r--r--shared/systemd/src/basic/sort-util.h4
-rw-r--r--shared/systemd/src/basic/stat-util.c50
-rw-r--r--shared/systemd/src/basic/stat-util.h4
-rw-r--r--shared/systemd/src/basic/string-util.c17
-rw-r--r--shared/systemd/src/basic/string-util.h1
-rw-r--r--shared/systemd/src/basic/strv.c20
-rw-r--r--shared/systemd/src/basic/strv.h23
-rw-r--r--shared/systemd/src/basic/user-util.h21
-rw-r--r--shared/systemd/src/basic/utf8.c4
-rw-r--r--src/dhcp/nm-dhcp-systemd.c3
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h6
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h19
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h17
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c167
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-protocol.h35
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c25
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.h1
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c72
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h22
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c264
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c169
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c277
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c4
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c6
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h6
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h17
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h20
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h5
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-option.h37
-rw-r--r--src/systemd/src/systemd/sd-lldp.h4
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h2
68 files changed, 2007 insertions, 790 deletions
diff --git a/Makefile.am b/Makefile.am
index 78144f6ca4..86226a2c38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1824,6 +1824,7 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \
shared/systemd/nm-sd-utils-shared.h \
shared/systemd/sd-adapt-shared/architecture.h \
shared/systemd/sd-adapt-shared/arphrd-list.h \
+ shared/systemd/sd-adapt-shared/blockdev-util.h \
shared/systemd/sd-adapt-shared/build.h \
shared/systemd/sd-adapt-shared/copy.h \
shared/systemd/sd-adapt-shared/def.h \
@@ -2026,12 +2027,14 @@ src_libnm_systemd_core_la_SOURCES = \
src/systemd/src/systemd/sd-dhcp-option.h \
src/systemd/src/systemd/sd-dhcp6-client.h \
src/systemd/src/systemd/sd-dhcp6-lease.h \
+ src/systemd/src/systemd/sd-dhcp6-option.h \
src/systemd/src/systemd/sd-event.h \
src/systemd/src/systemd/sd-id128.h \
src/systemd/src/systemd/sd-ipv4acd.h \
src/systemd/src/systemd/sd-ipv4ll.h \
src/systemd/src/systemd/sd-lldp.h \
- src/systemd/src/systemd/sd-ndisc.h
+ src/systemd/src/systemd/sd-ndisc.h \
+ $(NULL)
src_libnm_systemd_core_la_CPPFLAGS = $(src_libnm_systemd_core_la_cppflags)
src_libnm_systemd_core_la_LIBADD = \
diff --git a/shared/systemd/sd-adapt-shared/blockdev-util.h b/shared/systemd/sd-adapt-shared/blockdev-util.h
new file mode 100644
index 0000000000..637892c2d6
--- /dev/null
+++ b/shared/systemd/sd-adapt-shared/blockdev-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h
index 237139fad0..2b88571bc1 100644
--- a/shared/systemd/src/basic/cgroup-util.h
+++ b/shared/systemd/src/basic/cgroup-util.h
@@ -180,9 +180,31 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_rmdir(const char *controller, const char *path);
+typedef enum {
+ CG_KEY_MODE_GRACEFUL = 1 << 0,
+} CGroupKeyMode;
+
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
-int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
+int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode);
+
+static inline int cg_get_keyed_attribute(
+ const char *controller,
+ const char *path,
+ const char *attribute,
+ char **keys,
+ char **ret_values) {
+ return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0);
+}
+
+static inline int cg_get_keyed_attribute_graceful(
+ const char *controller,
+ const char *path,
+ const char *attribute,
+ char **keys,
+ char **ret_values) {
+ return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL);
+}
int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret);
@@ -238,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret);
int cg_kernel_controllers(Set **controllers);
bool cg_ns_supported(void);
+bool cg_freezer_supported(void);
int cg_all_unified(void);
int cg_hybrid_unified(void);
diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h
index 65a6384eeb..0ca650f48f 100644
--- a/shared/systemd/src/basic/errno-util.h
+++ b/shared/systemd/src/basic/errno-util.h
@@ -87,12 +87,16 @@ static inline bool ERRNO_IS_RESOURCE(int r) {
ENOMEM);
}
-/* Three different errors for "operation/system call/ioctl not supported" */
+/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
return IN_SET(abs(r),
EOPNOTSUPP,
ENOTTY,
- ENOSYS);
+ ENOSYS,
+ EAFNOSUPPORT,
+ EPFNOSUPPORT,
+ EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT);
}
/* Two different errors for access problems */
diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c
index 2cc5be186a..1ea0b6a64e 100644
--- a/shared/systemd/src/basic/escape.c
+++ b/shared/systemd/src/basic/escape.c
@@ -522,22 +522,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) {
return NULL;
t = r;
- if (style == ESCAPE_BACKSLASH)
+ switch (style) {
+ case ESCAPE_BACKSLASH:
+ case ESCAPE_BACKSLASH_ONELINE:
*(t++) = '"';
- else if (style == ESCAPE_POSIX) {
+ break;
+ case ESCAPE_POSIX:
*(t++) = '$';
*(t++) = '\'';
- } else
+ break;
+ default:
assert_not_reached("Bad EscapeStyle");
+ }
t = mempcpy(t, s, p - s);
- if (style == ESCAPE_BACKSLASH)
- t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
+ if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
+ t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE,
+ style == ESCAPE_BACKSLASH_ONELINE);
else
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
- if (style == ESCAPE_BACKSLASH)
+ if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
*(t++) = '"';
else
*(t++) = '\'';
diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h
index b8eb137c3d..0b00b116ed 100644
--- a/shared/systemd/src/basic/escape.h
+++ b/shared/systemd/src/basic/escape.h
@@ -34,8 +34,13 @@ typedef enum UnescapeFlags {
} UnescapeFlags;
typedef enum EscapeStyle {
- ESCAPE_BACKSLASH = 1,
- ESCAPE_POSIX = 2,
+ ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single
+ argument, possibly multiline. Tabs and newlines are not escaped. */
+ ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line
+ string instead. Shell escape sequences are produced for tabs and
+ newlines. */
+ ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape
+ * syntax (a string enclosed in $'') instead of plain quotes. */
} EscapeStyle;
char *cescape(const char *s);
diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c
index ea2c161206..a401663e27 100644
--- a/shared/systemd/src/basic/fd-util.c
+++ b/shared/systemd/src/basic/fd-util.c
@@ -23,9 +23,10 @@
#include "path-util.h"
#include "process-util.h"
#include "socket-util.h"
+#include "stat-util.h"
#include "stdio-util.h"
-#include "util.h"
#include "tmpfile-util.h"
+#include "util.h"
/* The maximum number of iterations in the loop to close descriptors in the fallback case
* when /proc/self/fd/ is inaccessible. */
@@ -104,13 +105,16 @@ int fclose_nointr(FILE *f) {
/* Same as close_nointr(), but for fclose() */
+ errno = 0; /* Extra safety: if the FILE* object is not encapsulating an fd, it might not set errno
+ * correctly. Let's hence initialize it to zero first, so that we aren't confused by any
+ * prior errno here */
if (fclose(f) == 0)
return 0;
if (errno == EINTR)
return 0;
- return -errno;
+ return errno_or_else(EIO);
}
FILE* safe_fclose(FILE *f) {
@@ -146,11 +150,7 @@ int fd_nonblock(int fd, bool nonblock) {
if (flags < 0)
return -errno;
- if (nonblock)
- nflags = flags | O_NONBLOCK;
- else
- nflags = flags & ~O_NONBLOCK;
-
+ nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock);
if (nflags == flags)
return 0;
@@ -169,11 +169,7 @@ int fd_cloexec(int fd, bool cloexec) {
if (flags < 0)
return -errno;
- if (cloexec)
- nflags = flags | FD_CLOEXEC;
- else
- nflags = flags & ~FD_CLOEXEC;
-
+ nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec);
if (nflags == flags)
return 0;
@@ -954,8 +950,15 @@ int fd_reopen(int fd, int flags) {
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
new_fd = open(procfs_path, flags);
- if (new_fd < 0)
- return -errno;
+ if (new_fd < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ if (proc_mounted() == 0)
+ return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+
+ return -ENOENT;
+ }
return new_fd;
}
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index 543dd4eb9b..309032638c 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -122,7 +122,7 @@ int write_string_stream_ts(
struct timespec *ts) {
bool needs_nl;
- int r;
+ int r, fd;
assert(f);
assert(line);
@@ -130,6 +130,14 @@ int write_string_stream_ts(
if (ferror(f))
return -EIO;
+ if (ts) {
+ /* If we shall set the timestamp we need the fd. But fmemopen() streams generally don't have
+ * an fd. Let's fail early in that case. */
+ fd = fileno(f);
+ if (fd < 0)
+ return -EBADF;
+ }
+
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
@@ -157,7 +165,7 @@ int write_string_stream_ts(
if (ts) {
struct timespec twice[2] = {*ts, *ts};
- if (futimens(fileno(f), twice) < 0)
+ if (futimens(fd, twice) < 0)
return -errno;
}
@@ -197,6 +205,13 @@ static int write_string_file_atomic(
goto fail;
}
+ if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
+ /* Sync the rename, too */
+ r = fsync_directory_of_file(fileno(f));
+ if (r < 0)
+ return r;
+ }
+
return 0;
fail:
@@ -415,7 +430,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
break;
}
- if (errno != -EINTR)
+ if (errno != EINTR)
return -errno;
}
@@ -895,7 +910,7 @@ int fflush_and_check(FILE *f) {
#if 0 /* NM_IGNORED */
int fflush_sync_and_check(FILE *f) {
- int r;
+ int r, fd;
assert(f);
@@ -903,10 +918,16 @@ int fflush_sync_and_check(FILE *f) {
if (r < 0)
return r;
- if (fsync(fileno(f)) < 0)
+ /* Not all file streams have an fd associated (think: fmemopen()), let's handle this gracefully and
+ * assume that in that case we need no explicit syncing */
+ fd = fileno(f);
+ if (fd < 0)
+ return 0;
+
+ if (fsync(fd) < 0)
return -errno;
- r = fsync_directory_of_file(fileno(f));
+ r = fsync_directory_of_file(fd);
if (r < 0)
return r;
@@ -1006,7 +1027,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
_cleanup_free_ char *buffer = NULL;
- int r, tty = -1;
+ int r;
assert(f);
@@ -1081,13 +1102,23 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
count++;
if (eol != EOL_NONE) {
- /* If we are on a tty, we can't wait for more input. But we expect only
- * \n as the single EOL marker, so there is no need to wait. We check
- * this condition last to avoid isatty() check if not necessary. */
-
- if (tty < 0)
- tty = isatty(fileno(f));
- if (tty > 0)
+ /* If we are on a tty, we can't shouldn't wait for more input, because that
+ * generally means waiting for the user, interactively. In the case of a TTY
+ * we expect only \n as the single EOL marker, so we are in the lucky
+ * position that there is no need to wait. We check this condition last, to
+ * avoid isatty() check if not necessary. */
+
+ if ((flags & (READ_LINE_IS_A_TTY|READ_LINE_NOT_A_TTY)) == 0) {
+ int fd;
+
+ fd = fileno(f);
+ if (fd < 0) /* Maybe an fmemopen() stream? Handle this gracefully,
+ * and don't call isatty() on an invalid fd */
+ flags |= READ_LINE_NOT_A_TTY;
+ else
+ flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
+ }
+ if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
}
@@ -1168,3 +1199,27 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
filename, st->st_mode & 07777);
return 0;
}
+
+#if 0 /* NM_IGNORED */
+int sync_rights(int from, int to) {
+ struct stat st;
+
+ if (fstat(from, &st) < 0)
+ return -errno;
+
+ return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid);
+}
+
+int rename_and_apply_smack_floor_label(const char *from, const char *to) {
+ int r = 0;
+ if (rename(from, to) < 0)
+ return -errno;
+
+#ifdef SMACK_RUN_LABEL
+ r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
+ if (r < 0)
+ return r;
+#endif
+ return r;
+}
+#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h
index 58daabaa8f..e2830b7963 100644
--- a/shared/systemd/src/basic/fileio.h
+++ b/shared/systemd/src/basic/fileio.h
@@ -88,7 +88,9 @@ int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
- READ_LINE_ONLY_NUL = 1 << 0,
+ READ_LINE_ONLY_NUL = 1 << 0,
+ READ_LINE_IS_A_TTY = 1 << 1,
+ READ_LINE_NOT_A_TTY = 1 << 2,
} ReadLineFlags;
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
@@ -104,3 +106,7 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
int safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
+
+int sync_rights(int from, int to);
+
+int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c
index 7996657108..75df913acf 100644
--- a/shared/systemd/src/basic/fs-util.c
+++ b/shared/systemd/src/basic/fs-util.c
@@ -10,8 +10,10 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "dirent-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "fs-util.h"
#include "locale-util.h"
#include "log.h"
@@ -23,6 +25,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "random-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -342,34 +345,51 @@ int fchmod_opath(int fd, mode_t m) {
* fchownat() does. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- if (chmod(procfs_path, m) < 0)
- return -errno;
+ if (chmod(procfs_path, m) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ if (proc_mounted() == 0)
+ return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+
+ return -ENOENT;
+ }
return 0;
}
-int fd_warn_permissions(const char *path, int fd) {
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return -errno;
+int stat_warn_permissions(const char *path, const struct stat *st) {
+ assert(path);
+ assert(st);
/* Don't complain if we are reading something that is not a file, for example /dev/null */
- if (!S_ISREG(st.st_mode))
+ if (!S_ISREG(st->st_mode))
return 0;
- if (st.st_mode & 0111)
+ if (st->st_mode & 0111)
log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
- if (st.st_mode & 0002)
+ if (st->st_mode & 0002)
log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
- if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
+ if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044)
log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
return 0;
}
+int fd_warn_permissions(const char *path, int fd) {
+ struct stat st;
+
+ assert(path);
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return stat_warn_permissions(path, &st);
+}
+
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd = -1;
@@ -702,29 +722,31 @@ int unlink_or_warn(const char *filename) {
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
- int r;
+ int wd;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
xsprintf(path, "/proc/self/fd/%i", what);
- r = inotify_add_watch(fd, path, mask);
- if (r < 0)
+ wd = inotify_add_watch(fd, path, mask);
+ if (wd < 0)
return -errno;
- return r;
+ return wd;
}
#if 0 /* NM_IGNORED */
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
+ int wd;
- if (inotify_add_watch(fd, pathname, mask) < 0) {
+ wd = inotify_add_watch(fd, pathname, mask);
+ if (wd < 0) {
if (errno == ENOSPC)
return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
}
- return 0;
+ return wd;
}
static bool unsafe_transition(const struct stat *a, const struct stat *b) {
@@ -1303,11 +1325,13 @@ void unlink_tempfilep(char (*p)[]) {
(void) unlink_noerrno(*p);
}
-int unlinkat_deallocate(int fd, const char *name, int flags) {
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -1;
struct stat st;
off_t l, bs;
+ assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
+
/* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
* link to it. This is useful to ensure that other processes that might have the file open for reading won't be
* able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
@@ -1324,7 +1348,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
* Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
* primary job – to delete the file – is accomplished. */
- if ((flags & AT_REMOVEDIR) == 0) {
+ if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) {
truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
if (truncate_fd < 0) {
@@ -1340,7 +1364,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
}
}
- if (unlinkat(fd, name, flags) < 0)
+ if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0)
return -errno;
if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
@@ -1351,7 +1375,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
return 0;
}
- if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
+ if (!S_ISREG(st.st_mode))
+ return 0;
+
+ if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) {
+ uint64_t left = st.st_size;
+ char buffer[64 * 1024];
+
+ /* If erasing is requested, let's overwrite the file with random data once before deleting
+ * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough
+ * for stuff backed by tmpfs at least.
+ *
+ * Note that we only erase like this if the link count of the file is zero. If it is higher it
+ * is still linked by someone else and we'll leave it to them to remove it securely
+ * eventually! */
+
+ random_bytes(buffer, sizeof(buffer));
+
+ while (left > 0) {
+ ssize_t n;
+
+ n = write(truncate_fd, buffer, MIN(sizeof(buffer), left));
+ if (n < 0) {
+ log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name);
+ break;
+ }
+
+ assert(left >= (size_t) n);
+ left -= n;
+ }
+
+ /* Let's refresh metadata */
+ if (fstat(truncate_fd, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
+ return 0;
+ }
+ }
+
+ /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
+ if (st.st_blocks == 0 || st.st_nlink > 0)
return 0;
/* If this is a regular file, it actually took up space on disk and there are no other links it's time to
@@ -1490,4 +1552,89 @@ int open_parent(const char *path, int flags, mode_t mode) {
return fd;
}
+
+static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
+ _cleanup_free_ char *p = NULL, *uuids = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ int r, found_encrypted = false;
+
+ assert(sysfs_path);
+
+ if (depth_left == 0)
+ return -EINVAL;
+
+ p = path_join(sysfs_path, "dm/uuid");
+ if (!p)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &uuids);
+ if (r != -ENOENT) {
+ if (r < 0)
+ return r;
+
+ /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
+ if (startswith(uuids, "CRYPT-"))
+ return true;
+ }
+
+ /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
+ * subdir. */
+
+ p = mfree(p);
+ p = path_join(sysfs_path, "slaves");
+ if (!p)
+ return -ENOMEM;
+
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT) /* Doesn't have slaves */
+ return false;
+
+ return -errno;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *q = NULL;
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return -errno;
+
+ break; /* No more slaves */
+ }
+
+ q = path_join(p, de->d_name);
+ if (!q)
+ return -ENOMEM;
+
+ r = blockdev_is_encrypted(q, depth_left - 1);
+ if (r < 0)
+ return r;
+ if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
+ return false;
+
+ found_encrypted = true;
+ }
+
+ return found_encrypted;
+}
+
+int path_is_encrypted(const char *path) {
+ char p[SYS_BLOCK_PATH_MAX(NULL)];
+ dev_t devt;
+ int r;
+
+ r = get_block_device(path, &devt);
+ if (r < 0)
+ return r;
+ if (r == 0) /* doesn't have a block device */
+ return false;
+
+ xsprintf_sys_block_path(p, NULL, devt);
+
+ return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
+}
#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h
index 6b9ade2ec1..b184570f9f 100644
--- a/shared/systemd/src/basic/fs-util.h
+++ b/shared/systemd/src/basic/fs-util.h
@@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode);
int fchmod_opath(int fd, mode_t m);
int fd_warn_permissions(const char *path, int fd);
+int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
@@ -82,7 +83,7 @@ enum {
CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */
CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
- CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's
+ CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
* right-most component refers to symlink, return O_PATH fd of the symlink. */
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */
};
@@ -113,7 +114,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
void unlink_tempfilep(char (*p)[]);
-int unlinkat_deallocate(int fd, const char *name, int flags);
+
+typedef enum UnlinkDeallocateFlags {
+ UNLINK_REMOVEDIR = 1 << 0,
+ UNLINK_ERASE = 1 << 1,
+} UnlinkDeallocateFlags;
+
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
int fsync_directory_of_file(int fd);
int fsync_full(int fd);
@@ -122,3 +129,5 @@ int fsync_path_at(int at_fd, const char *path);
int syncfs_path(int atfd, const char *path);
int open_parent(const char *path, int flags, mode_t mode);
+
+int path_is_encrypted(const char *path);
diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c
index 1b0d129237..b1c19c9572 100644
--- a/shared/systemd/src/basic/hash-funcs.c
+++ b/shared/systemd/src/basic/hash-funcs.c
@@ -11,12 +11,14 @@ void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
-#if 0 /* NM_IGNORED */
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
+ char, string_hash_func, string_compare_func, free);
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
char, string_hash_func, string_compare_func, free,
char, free);
+#if 0 /* NM_IGNORED */
void path_hash_func(const char *q, struct siphash *state) {
size_t n;
@@ -56,6 +58,8 @@ void path_hash_func(const char *q, struct siphash *state) {
}
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
+ char, path_hash_func, path_compare, free);
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h
index 7bb5d1cd02..005d1b21d2 100644
--- a/shared/systemd/src/basic/hash-funcs.h
+++ b/shared/systemd/src/basic/hash-funcs.h
@@ -76,10 +76,12 @@ struct hash_ops {
void string_hash_func(const char *p, struct siphash *state);
#define string_compare_func strcmp
extern const struct hash_ops string_hash_ops;
+extern const struct hash_ops string_hash_ops_free;
extern const struct hash_ops string_hash_ops_free_free;
void path_hash_func(const char *p, struct siphash *state);
extern const struct hash_ops path_hash_ops;
+extern const struct hash_ops path_hash_ops_free;
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
* or suchlike. */
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index 1aa009478a..c0296bde36 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -147,12 +147,7 @@ struct hashmap_debug_info {
/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
-
-#else /* !ENABLE_DEBUG_HASHMAP */
-#define HASHMAP_DEBUG_FIELDS
-#endif /* ENABLE_DEBUG_HASHMAP */
+#endif
enum HashmapType {
HASHMAP_TYPE_PLAIN,
@@ -214,7 +209,10 @@ struct HashmapBase {
bool from_pool:1; /* whether was allocated from mempool */
bool dirty:1; /* whether dirtied since last iterated_cache_get() */
bool cached:1; /* whether this hashmap is being cached */
- HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
+
+#if ENABLE_DEBUG_HASHMAP
+ struct hashmap_debug_info debug;
+#endif
};
/* Specific hash types
@@ -256,7 +254,7 @@ struct hashmap_type_info {
unsigned n_direct_buckets;
};
-static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
[HASHMAP_TYPE_PLAIN] = {
.head_size = sizeof(Hashmap),
.entry_size = sizeof(struct plain_hashmap_entry),
@@ -709,7 +707,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
: hashmap_iterate_in_internal_order(h, i);
}
-bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
+bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
struct hashmap_base_entry *e;
void *data;
unsigned idx;
@@ -735,7 +733,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v
}
bool set_iterate(const Set *s, Iterator *i, void **value) {
- return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
+ return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
}
#define HASHMAP_FOREACH_IDX(idx, h, i) \
@@ -743,7 +741,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) {
(idx != IDX_NIL); \
(idx) = hashmap_iterate_entry((h), &(i)))
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) {
+IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) {
IteratedCache *cache;
assert(h);
@@ -811,15 +809,15 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
return h;
}
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
@@ -837,18 +835,18 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
return -ENOMEM;
*h = q;
- return 0;
+ return 1;
}
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
@@ -870,16 +868,16 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(h);
}
-HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
if (h) {
- internal_hashmap_clear(h, default_free_key, default_free_value);
+ _hashmap_clear(h, default_free_key, default_free_value);
hashmap_free_no_clear(h);
}
return NULL;
}
-void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
free_func_t free_key, free_value;
if (!h)
return;
@@ -893,11 +891,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f
* hash table, and only then call the destructor functions. If these destructors then try to unregister
* themselves from our hash table a second time, the entry is already gone. */
- while (internal_hashmap_size(h) > 0) {
+ while (_hashmap_size(h) > 0) {
void *k = NULL;
void *v;
- v = internal_hashmap_first_key_and_value(h, true, &k);
+ v = _hashmap_first_key_and_value(h, true, &k);
if (free_key)
free_key(k);
@@ -1303,7 +1301,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
return 0;
}
-void *internal_hashmap_get(HashmapBase *h, const void *key) {
+void *_hashmap_get(HashmapBase *h, const void *key) {
struct hashmap_base_entry *e;
unsigned hash, idx;
@@ -1338,7 +1336,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
return e->value;
}
-bool internal_hashmap_contains(HashmapBase *h, const void *key) {
+bool _hashmap_contains(HashmapBase *h, const void *key) {
unsigned hash;
if (!h)
@@ -1348,7 +1346,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) {
return bucket_scan(h, hash, key) != IDX_NIL;
}
-void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+void *_hashmap_remove(HashmapBase *h, const void *key) {
struct hashmap_base_entry *e;
unsigned hash, idx;
void *data;
@@ -1486,7 +1484,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
return 0;
}
-void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
+void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
struct hashmap_base_entry *e;
unsigned hash, idx;
@@ -1516,7 +1514,7 @@ static unsigned find_first_entry(HashmapBase *h) {
return hashmap_iterate_entry(h, &i);
}
-void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
+void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
struct hashmap_base_entry *e;
void *key, *data;
unsigned idx;
@@ -1541,21 +1539,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r
return data;
}
-unsigned internal_hashmap_size(HashmapBase *h) {
+unsigned _hashmap_size(HashmapBase *h) {
if (!h)
return 0;
return n_entries(h);
}
-unsigned internal_hashmap_buckets(HashmapBase *h) {
+unsigned _hashmap_buckets(HashmapBase *h) {
if (!h)
return 0;
return n_buckets(h);
}
-int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+int _hashmap_merge(Hashmap *h, Hashmap *other) {
Iterator i;
unsigned idx;
@@ -1591,7 +1589,7 @@ int set_merge(Set *s, Set *other) {
return 0;
}
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
+int _hashmap_reserve(HashmapBase *h, unsigned entries_add) {
int r;
assert(h);
@@ -1609,7 +1607,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
* Returns: 0 on success.
* -ENOMEM on alloc failure, in which case no move has been done.
*/
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+int _hashmap_move(HashmapBase *h, HashmapBase *other) {
struct swap_entries swap;
struct hashmap_base_entry *e, *n;
Iterator i;
@@ -1654,7 +1652,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
return 0;
}
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
struct swap_entries swap;
unsigned h_hash, other_hash, idx;
struct hashmap_base_entry *e, *n;
@@ -1691,7 +1689,7 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke
return 0;
}
-HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+HashmapBase *_hashmap_copy(HashmapBase *h) {
HashmapBase *copy;
int r;
@@ -1714,14 +1712,14 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
}
if (r < 0) {
- internal_hashmap_free(copy, false, false);
+ _hashmap_free(copy, false, false);
return NULL;
}
return copy;
}
-char **internal_hashmap_get_strv(HashmapBase *h) {
+char **_hashmap_get_strv(HashmapBase *h) {
char **sv;
Iterator i;
unsigned idx, n;
@@ -1778,42 +1776,55 @@ int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) {
return r;
_cleanup_free_ char *kdup = NULL, *vdup = NULL;
+
kdup = strdup(k);
- vdup = strdup(v);
- if (!kdup || !vdup)
+ if (!kdup)
return -ENOMEM;
+ if (v) {
+ vdup = strdup(v);
+ if (!vdup)
+ return -ENOMEM;
+ }
+
r = hashmap_put(*h, kdup, vdup);
if (r < 0) {
- if (r == -EEXIST && streq(v, hashmap_get(*h, kdup)))
+ if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup)))
return 0;
return r;
}
- assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */
- kdup = vdup = NULL;
+ /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */
+ assert(vdup == NULL || r > 0);
+ if (r > 0)
+ kdup = vdup = NULL;
- return 0;
+ return r;
}
#endif /* NM_IGNORED */
-int set_put_strdup(Set *s, const char *p) {
+int set_put_strdup(Set **s, const char *p) {
char *c;
+ int r;
assert(s);
assert(p);
- if (set_contains(s, (char*) p))
+ r = set_ensure_allocated(s, &string_hash_ops_free);
+ if (r < 0)
+ return r;
+
+ if (set_contains(*s, (char*) p))
return 0;
c = strdup(p);
if (!c)
return -ENOMEM;
- return set_consume(s, c);
+ return set_consume(*s, c);
}
-int set_put_strdupv(Set *s, char **l) {
+int set_put_strdupv(Set **s, char **l) {
int n = 0, r;
char **i;
diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h
index 65adc92513..230d322213 100644
--- a/shared/systemd/src/basic/hashmap.h
+++ b/shared/systemd/src/basic/hashmap.h
@@ -14,7 +14,7 @@
* will be treated as empty hashmap for all read operations. That way it is not
* necessary to instantiate an object for each Hashmap use.
*
- * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap),
* the implementation will:
* - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
* - perform extra checks for invalid use of iterators
@@ -24,10 +24,9 @@
typedef void* (*hashmap_destroy_t)(void *p);
-/* The base type for all hashmap and set types. Many functions in the
- * implementation take (HashmapBase*) parameters and are run-time polymorphic,
- * though the API is not meant to be polymorphic (do not call functions
- * internal_*() directly). */
+/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*)
+ * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call
+ * underscore-prefixed functions directly). */
typedef struct HashmapBase HashmapBase;
/* Specific hashmap/set types */
@@ -84,62 +83,70 @@ typedef struct {
# define HASHMAP_DEBUG_PASS_ARGS
#endif
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+#define hashmap_free_and_replace(a, b) \
+ ({ \
+ hashmap_free(a); \
+ (a) = (b); \
+ (b) = NULL; \
+ 0; \
+ })
-HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline Hashmap *hashmap_free(Hashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
static inline Hashmap *hashmap_free_free(Hashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
}
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
}
static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
}
static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
}
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
- return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
+ return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
}
IteratedCache *iterated_cache_free(IteratedCache *cache);
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
-HashmapBase *internal_hashmap_copy(HashmapBase *h);
+HashmapBase *_hashmap_copy(HashmapBase *h);
static inline Hashmap *hashmap_copy(Hashmap *h) {
- return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+ return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
- return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+ return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h));
}
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h);
+IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h);
static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
- return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
+ return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
}
static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
- return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
+ return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
}
int hashmap_put(Hashmap *h, const void *key, void *value);
@@ -159,12 +166,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo
return hashmap_replace(PLAIN_HASHMAP(h), key, value);
}
-void *internal_hashmap_get(HashmapBase *h, const void *key);
+void *_hashmap_get(HashmapBase *h, const void *key);
static inline void *hashmap_get(Hashmap *h, const void *key) {
- return internal_hashmap_get(HASHMAP_BASE(h), key);
+ return _hashmap_get(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
- return internal_hashmap_get(HASHMAP_BASE(h), key);
+ return _hashmap_get(HASHMAP_BASE(h), key);
}
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
@@ -172,20 +179,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi
return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
}
-bool internal_hashmap_contains(HashmapBase *h, const void *key);
+bool _hashmap_contains(HashmapBase *h, const void *key);
static inline bool hashmap_contains(Hashmap *h, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE(h), key);
+ return _hashmap_contains(HASHMAP_BASE(h), key);
}
static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE(h), key);
+ return _hashmap_contains(HASHMAP_BASE(h), key);
}
-void *internal_hashmap_remove(HashmapBase *h, const void *key);
+void *_hashmap_remove(HashmapBase *h, const void *key);
static inline void *hashmap_remove(Hashmap *h, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(h), key);
+ return _hashmap_remove(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(h), key);
+ return _hashmap_remove(HASHMAP_BASE(h), key);
}
void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
@@ -193,9 +200,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key,
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
-void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
+void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
- return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value);
+ return _hashmap_remove_value(HASHMAP_BASE(h), key, value);
}
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
@@ -214,41 +221,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo
/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
* should just work, allow this by having looser type-checking here. */
-int internal_hashmap_merge(Hashmap *h, Hashmap *other);
-#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+int _hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+int _hashmap_reserve(HashmapBase *h, unsigned entries_add);
static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+ return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+ return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+int _hashmap_move(HashmapBase *h, HashmapBase *other);
/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
static inline int hashmap_move(Hashmap *h, Hashmap *other) {
- return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+ return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
- return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+ return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+ return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+ return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
-unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+unsigned _hashmap_size(HashmapBase *h) _pure_;
static inline unsigned hashmap_size(Hashmap *h) {
- return internal_hashmap_size(HASHMAP_BASE(h));
+ return _hashmap_size(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
- return internal_hashmap_size(HASHMAP_BASE(h));
+ return _hashmap_size(HASHMAP_BASE(h));
}
static inline bool hashmap_isempty(Hashmap *h) {
@@ -258,49 +265,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
return ordered_hashmap_size(h) == 0;
}
-unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+unsigned _hashmap_buckets(HashmapBase *h) _pure_;
static inline unsigned hashmap_buckets(Hashmap *h) {
- return internal_hashmap_buckets(HASHMAP_BASE(h));
+ return _hashmap_buckets(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
- return internal_hashmap_buckets(HASHMAP_BASE(h));
+ return _hashmap_buckets(HASHMAP_BASE(h));
}
-bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
+bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
- return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+ return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
- return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+ return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
-void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline void hashmap_clear(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+ _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+ _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
static inline void hashmap_clear_free(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
+ _hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
+ _hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
static inline void hashmap_clear_free_key(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+ _hashmap_clear(HASHMAP_BASE(h), free, NULL);
}
static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+ _hashmap_clear(HASHMAP_BASE(h), free, NULL);
}
static inline void hashmap_clear_free_free(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), free, free);
+ _hashmap_clear(HASHMAP_BASE(h), free, free);
}
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h), free, free);
+ _hashmap_clear(HASHMAP_BASE(h), free, free);
}
/*
@@ -314,50 +321,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
* the first entry is O(1).
*/
-void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
+void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
}
static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
}
static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
}
static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
}
static inline void *hashmap_steal_first(Hashmap *h) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
}
static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
}
static inline void *hashmap_first(Hashmap *h) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
}
static inline void *ordered_hashmap_first(OrderedHashmap *h) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
}
-static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) {
+static inline void *_hashmap_first_key(HashmapBase *h, bool remove) {
void *key = NULL;
- (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
+ (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
return key;
}
static inline void *hashmap_steal_first_key(Hashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h), true);
+ return _hashmap_first_key(HASHMAP_BASE(h), true);
}
static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h), true);
+ return _hashmap_first_key(HASHMAP_BASE(h), true);
}
static inline void *hashmap_first_key(Hashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h), false);
+ return _hashmap_first_key(HASHMAP_BASE(h), false);
}
static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h), false);
+ return _hashmap_first_key(HASHMAP_BASE(h), false);
}
#define hashmap_clear_with_destructor(_s, _f) \
@@ -386,12 +393,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
/* no hashmap_next */
void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
-char **internal_hashmap_get_strv(HashmapBase *h);
+char **_hashmap_get_strv(HashmapBase *h);
static inline char **hashmap_get_strv(Hashmap *h) {
- return internal_hashmap_get_strv(HASHMAP_BASE(h));
+ return _hashmap_get_strv(HASHMAP_BASE(h));
}
static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
- return internal_hashmap_get_strv(HASHMAP_BASE(h));
+ return _hashmap_get_strv(HASHMAP_BASE(h));
}
/*
diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c
index 36174ec3bc..1fb52305cc 100644
--- a/shared/systemd/src/basic/hexdecoct.c
+++ b/shared/systemd/src/basic/hexdecoct.c
@@ -604,13 +604,13 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
static int base64_append_width(
char **prefix, int plen,
- const char *sep, int indent,
+ char sep, int indent,
const void *p, size_t l,
int width) {
_cleanup_free_ char *x = NULL;
char *t, *s;
- ssize_t len, slen, avail, line, lines;
+ ssize_t len, avail, line, lines;
len = base64mem(p, l, &x);
if (len <= 0)
@@ -618,21 +618,20 @@ static int base64_append_width(
lines = DIV_ROUND_UP(len, width);
- slen = strlen_ptr(sep);
- if (plen >= SSIZE_MAX - 1 - slen ||
- lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
+ if ((size_t) plen >= SSIZE_MAX - 1 - 1 ||
+ lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1))
return -ENOMEM;
- t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines);
+ t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
- memcpy_safe(t + plen, sep, slen);
+ t[plen] = sep;
- for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
+ for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) {
int act = MIN(width, avail);
- if (line > 0 || sep) {
+ if (line > 0 || sep == '\n') {
memset(s, ' ', indent);
s += indent;
}
@@ -655,10 +654,10 @@ int base64_append(
if (plen > width / 2 || plen + indent > width)
/* leave indent on the left, keep last column free */
- return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
+ return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1);
else
/* leave plen on the left, keep last column free */
- return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1);
+ return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1);
}
#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c
index 00a92cb79a..82fc56dbd6 100644
--- a/shared/systemd/src/basic/hostname-util.c
+++ b/shared/systemd/src/basic/hostname-util.c
@@ -14,6 +14,7 @@
#include "hostname-util.h"
#include "macro.h"
#include "string-util.h"
+#include "strv.h"
#if 0 /* NM_IGNORED */
bool hostname_is_set(void) {
@@ -24,7 +25,7 @@ bool hostname_is_set(void) {
if (isempty(u.nodename))
return false;
- /* This is the built-in kernel default host name */
+ /* This is the built-in kernel default hostname */
if (streq(u.nodename, "(none)"))
return false;
@@ -33,6 +34,7 @@ bool hostname_is_set(void) {
char* gethostname_malloc(void) {
struct utsname u;
+ const char *s;
/* This call tries to return something useful, either the actual hostname
* or it makes something up. The only reason it might fail is OOM.
@@ -40,10 +42,28 @@ char* gethostname_malloc(void) {
assert_se(uname(&u) >= 0);
- if (isempty(u.nodename) || streq(u.nodename, "(none)"))
- return strdup(FALLBACK_HOSTNAME);
+ s = u.nodename;
+ if (isempty(s) || streq(s, "(none)"))
+ s = FALLBACK_HOSTNAME;
- return strdup(u.nodename);
+ return strdup(s);
+}
+
+char* gethostname_short_malloc(void) {
+ struct utsname u;
+ const char *s;
+
+ /* Like above, but kills the FQDN part if present. */
+
+ assert_se(uname(&u) >= 0);
+
+ s = u.nodename;
+ if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
+ s = FALLBACK_HOSTNAME;
+ assert(s[0] != '.');
+ }
+
+ return strndup(s, strcspn(s, "."));
}
#endif /* NM_IGNORED */
@@ -81,7 +101,7 @@ bool valid_ldh_char(char c) {
}
/**
- * Check if s looks like a valid host name or FQDN. This does not do
+ * Check if s looks like a valid hostname or FQDN. This does not do
* full DNS validation, but only checks if the name is composed of
* allowed characters and the length is not above the maximum allowed
* by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
@@ -184,14 +204,16 @@ bool is_localhost(const char *hostname) {
/* This tries to identify local host and domain names
* described in RFC6761 plus the redhatism of localdomain */
- return strcaseeq(hostname, "localhost") ||
- strcaseeq(hostname, "localhost.") ||
- strcaseeq(hostname, "localhost.localdomain") ||
- strcaseeq(hostname, "localhost.localdomain.") ||
- endswith_no_case(hostname, ".localhost") ||
- endswith_no_case(hostname, ".localhost.") ||
- endswith_no_case(hostname, ".localhost.localdomain") ||
- endswith_no_case(hostname, ".localhost.localdomain.");
+ return STRCASE_IN_SET(
+ hostname,
+ "localhost",
+ "localhost.",
+ "localhost.localdomain",
+ "localhost.localdomain.") ||
+ endswith_no_case(hostname, ".localhost") ||
+ endswith_no_case(hostname, ".localhost.") ||
+ endswith_no_case(hostname, ".localhost.localdomain") ||
+ endswith_no_case(hostname, ".localhost.localdomain.");
}
#if 0 /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/hostname-util.h b/shared/systemd/src/basic/hostname-util.h
index 7ba386a0fd..cafd6f020b 100644
--- a/shared/systemd/src/basic/hostname-util.h
+++ b/shared/systemd/src/basic/hostname-util.h
@@ -9,6 +9,7 @@
bool hostname_is_set(void);
char* gethostname_malloc(void);
+char* gethostname_short_malloc(void);
int gethostname_strict(char **ret);
bool valid_ldh_char(char c) _const_;
diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c
index 0e77062101..cddc269f4a 100644
--- a/shared/systemd/src/basic/in-addr-util.c
+++ b/shared/systemd/src/basic/in-addr-util.c
@@ -180,47 +180,89 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen)
assert(u);
/* Increases the network part of an address by one. Returns
- * positive it that succeeds, or 0 if this overflows. */
+ * positive if that succeeds, or -ERANGE if this overflows. */
+
+ return in_addr_prefix_nth(family, u, prefixlen, 1);
+}
+
+/*
+ * Calculates the nth prefix of size prefixlen starting from the address denoted by u.
+ *
+ * On success 1 will be returned and the calculated prefix will be available in
+ * u. In the case nth == 0 the input will be left unchanged and 1 will be returned.
+ * In case the calculation cannot be performed (invalid prefix length,
+ * overflows would occur) -ERANGE is returned. If the address family given isn't
+ * supported -EAFNOSUPPORT will be returned.
+ *
+ *
+ * Examples:
+ * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u
+ * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written
+ * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written
+ * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written
+ * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u
+ */
+int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) {
+ assert(u);
if (prefixlen <= 0)
- return 0;
+ return -ERANGE;
- if (family == AF_INET) {
- uint32_t c, n;
+ if (nth == 0)
+ return 1;
+ if (family == AF_INET) {
+ uint32_t c, n, t;
if (prefixlen > 32)
prefixlen = 32;
c = be32toh(u->in.s_addr);
- n = c + (1UL << (32 - prefixlen));
- if (n < c)
- return 0;
- n &= 0xFFFFFFFFUL << (32 - prefixlen);
+ t = nth << (32 - prefixlen);
+
+ /* Check for wrap */
+ if (c > UINT32_MAX - t)
+ return -ERANGE;
+
+ n = c + t;
+
+ n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen);
u->in.s_addr = htobe32(n);
return 1;
}
if (family == AF_INET6) {
- struct in6_addr add = {}, result;
+ struct in6_addr result = {};
uint8_t overflow = 0;
- unsigned i;
+ uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */
+ unsigned start_byte = (prefixlen - 1) / 8;
if (prefixlen > 128)
prefixlen = 128;
/* First calculate what we have to add */
- add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
+ delta = nth << ((128 - prefixlen) % 8);
- for (i = 16; i > 0; i--) {
+ for (unsigned i = 16; i > 0; i--) {
unsigned j = i - 1;
+ unsigned d = 0;
- result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
- overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
+ if (j <= start_byte) {
+ int16_t t;
+
+ d = delta & 0xFF;
+ delta >>= 8;
+
+ t = u->in6.s6_addr[j] + d + overflow;
+ overflow = t > UINT8_MAX ? t - UINT8_MAX : 0;
+
+ result.s6_addr[j] = (uint8_t)t;
+ } else
+ result.s6_addr[j] = u->in6.s6_addr[j];
}
- if (overflow)
- return 0;
+ if (overflow || delta != 0)
+ return -ERANGE;
u->in6 = result;
return 1;
diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h
index ae2dad0bb1..90d79a5ef5 100644
--- a/shared/systemd/src/basic/in-addr-util.h
+++ b/shared/systemd/src/basic/in-addr-util.h
@@ -36,6 +36,7 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c
index 4f57f04477..f1df3fac90 100644
--- a/shared/systemd/src/basic/io-util.c
+++ b/shared/systemd/src/basic/io-util.c
@@ -14,10 +14,6 @@
#if 0 /* NM_IGNORED */
int flush_fd(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN,
- };
int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
@@ -30,19 +26,18 @@ int flush_fd(int fd) {
ssize_t l;
int r;
- r = poll(&pollfd, 1, 0);
+ r = fd_wait_for_event(fd, POLLIN, 0);
if (r < 0) {
- if (errno == EINTR)
+ if (r == -EINTR)
continue;
- return -errno;
-
- } else if (r == 0)
+ return r;
+ }
+ if (r == 0)
return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
-
if (errno == EINTR)
continue;
@@ -160,21 +155,15 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
}
int pipe_eof(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN|POLLHUP,
- };
-
int r;
- r = poll(&pollfd, 1, 0);
+ r = fd_wait_for_event(fd, POLLIN, 0);
if (r < 0)
- return -errno;
-
+ return r;
if (r == 0)
return 0;
- return pollfd.revents & POLLHUP;
+ return !!(r & POLLHUP);
}
#endif /* NM_IGNORED */
@@ -194,6 +183,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
if (r == 0)
return 0;
+ if (pollfd.revents & POLLNVAL)
+ return -EBADF;
+
return pollfd.revents;
}
diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h
index 6cd9c516fb..0c2eb894c0 100644
--- a/shared/systemd/src/basic/macro.h
+++ b/shared/systemd/src/basic/macro.h
@@ -84,6 +84,14 @@
#define _variable_no_sanitize_address_
#endif
+/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this
+ * unconditionally on llvm */
+#if defined(__clang__)
+#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow")))
+#else
+#define _function_no_sanitize_float_cast_overflow_
+#endif
+
#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
/* Temporarily disable some warnings */
#define DISABLE_WARNING_FORMAT_NONLITERAL \
@@ -115,6 +123,14 @@
_Pragma("GCC diagnostic push")
#endif
+#define DISABLE_WARNING_FLOAT_EQUAL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
#else
@@ -441,6 +457,8 @@ static inline int __coverity_check_and_return__(int condition) {
#define char_array_0(x) x[sizeof(x)-1] = 0;
+#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
+
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix (hence works correctly on signed
@@ -460,8 +478,10 @@ static inline int __coverity_check_and_return__(int condition) {
ans; \
})
+#define UPDATE_FLAG(orig, flag, b) \
+ ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
- (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
+ (v) = UPDATE_FLAG(v, flag, b)
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
@@ -592,4 +612,17 @@ static inline int __coverity_check_and_return__(int condition) {
DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
+/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
+ * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
+ * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
+ * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence
+ * memcpy() is great for our purposes. */
+#define READ_NOW(x) \
+ ({ \
+ typeof(x) _copy; \
+ memcpy(&_copy, &(x), sizeof(_copy)); \
+ asm volatile ("" : : : "memory"); \
+ _copy; \
+ })
+
#include "log.h"
diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h
index b7e2e67e84..a6a2ccdbc2 100644
--- a/shared/systemd/src/basic/memory-util.h
+++ b/shared/systemd/src/basic/memory-util.h
@@ -12,7 +12,7 @@
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1))
+#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h
index 2e76031b32..17af87a3ae 100644
--- a/shared/systemd/src/basic/missing_random.h
+++ b/shared/systemd/src/basic/missing_random.h
@@ -14,3 +14,7 @@
#ifndef GRND_RANDOM
#define GRND_RANDOM 0x0002
#endif
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004
+#endif
diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h
index 29828dbae7..647e56f1c4 100644
--- a/shared/systemd/src/basic/missing_socket.h
+++ b/shared/systemd/src/basic/missing_socket.h
@@ -64,3 +64,8 @@ struct sockaddr_vm {
#ifndef IP_TRANSPARENT
#define IP_TRANSPARENT 19
#endif
+
+/* linux/sockios.h */
+#ifndef SIOCGSKNS
+#define SIOCGSKNS 0x894C
+#endif
diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c
index 475a06ccf8..0b343b2a8a 100644
--- a/shared/systemd/src/basic/parse-util.c
+++ b/shared/systemd/src/basic/parse-util.c
@@ -20,14 +20,28 @@
#include "process-util.h"
#include "stat-util.h"
#include "string-util.h"
+#include "strv.h"
int parse_boolean(const char *v) {
if (!v)
return -EINVAL;
- if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
+ if (STRCASE_IN_SET(v,
+ "1",
+ "yes",
+ "y",
+ "true",
+ "t",
+ "on"))
return 1;
- else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
+
+ if (STRCASE_IN_SET(v,
+ "0",
+ "no",
+ "n",
+ "false",
+ "f",
+ "off"))
return 0;
return -EINVAL;
@@ -59,26 +73,24 @@ int parse_pid(const char *s, pid_t* ret_pid) {
}
int parse_mode(const char *s, mode_t *ret) {
- char *x;
- long l;
+ unsigned m;
+ int r;
assert(s);
- assert(ret);
- s += strspn(s, WHITESPACE);
- if (s[0] == '-')
- return -ERANGE;
-
- errno = 0;
- l = strtol(s, &x, 8);
- if (errno > 0)
- return -errno;
- if (!x || x == s || *x != 0)
- return -EINVAL;
- if (l < 0 || l > 07777)
+ r = safe_atou_full(s, 8 |
+ SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird,
+ * refuse. User might have wanted to add mode flags or
+ * so, but this parser doesn't allow that, so let's
+ * better be safe. */
+ &m);
+ if (r < 0)
+ return r;
+ if (m > 07777)
return -ERANGE;
- *ret = (mode_t) l;
+ if (ret)
+ *ret = m;
return 0;
}
@@ -344,30 +356,73 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
}
#endif /* NM_IGNORED */
+static const char *mangle_base(const char *s, unsigned *base) {
+ const char *k;
+
+ assert(s);
+ assert(base);
+
+ /* Base already explicitly specified, then don't do anything. */
+ if (SAFE_ATO_MASK_FLAGS(*base) != 0)
+ return s;
+
+ /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */
+ k = STARTSWITH_SET(s, "0b", "0B");
+ if (k) {
+ *base = 2 | (*base & SAFE_ATO_ALL_FLAGS);
+ return k;
+ }
+
+ k = STARTSWITH_SET(s, "0o", "0O");
+ if (k) {
+ *base = 8 | (*base & SAFE_ATO_ALL_FLAGS);
+ return k;
+ }
+
+ return s;
+}
+
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
assert(s);
- assert(base <= 16);
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
- /* strtoul() is happy to parse negative values, and silently
- * converts them to unsigned values without generating an
- * error. We want a clean error, hence let's look for the "-"
- * prefix on our own, and generate an error. But let's do so
- * only after strtoul() validated that the string is clean
- * otherwise, so that we return EINVAL preferably over
- * ERANGE. */
+ /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without
+ * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and
+ * generate an error. But let's do so only after strtoul() validated that the string is clean
+ * otherwise, so that we return EINVAL preferably over ERANGE. */
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
+ strchr(WHITESPACE, s[0]))
+ return -EINVAL;
s += strspn(s, WHITESPACE);
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
+ IN_SET(s[0], '+', '-'))
+ return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a
+ * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we
+ * blanket refuse +/- prefixed integers, while if it is missing we'll just
+ * return ERANGE, because the string actually parses correctly, but doesn't
+ * fit in the return type. */
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
+ s[0] == '0' && !streq(s, "0"))
+ return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal
+ * notation and assumed-to-be-decimal integers with a leading zero. */
+
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtoul(s, &x, base);
+ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual
+ * base is left */);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
- if (s[0] == '-')
+ if (l != 0 && s[0] == '-')
return -ERANGE;
if ((unsigned long) (unsigned) l != l)
return -ERANGE;
@@ -379,13 +434,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
}
int safe_atoi(const char *s, int *ret_i) {
+ unsigned base = 0;
char *x = NULL;
long l;
assert(s);
+ s += strspn(s, WHITESPACE);
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtol(s, &x, 0);
+ l = strtol(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
@@ -399,21 +458,36 @@ int safe_atoi(const char *s, int *ret_i) {
return 0;
}
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
+int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) {
char *x = NULL;
unsigned long long l;
assert(s);
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
+ strchr(WHITESPACE, s[0]))
+ return -EINVAL;
s += strspn(s, WHITESPACE);
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
+ IN_SET(s[0], '+', '-'))
+ return -EINVAL;
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
+ s[0] == '0' && s[1] != 0)
+ return -EINVAL;
+
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtoull(s, &x, 0);
+ l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base));
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
- if (*s == '-')
+ if (l != 0 && s[0] == '-')
return -ERANGE;
if (ret_llu)
@@ -423,13 +497,17 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
}
int safe_atolli(const char *s, long long int *ret_lli) {
+ unsigned base = 0;
char *x = NULL;
long long l;
assert(s);
+ s += strspn(s, WHITESPACE);
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtoll(s, &x, 0);
+ l = strtoll(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
@@ -442,20 +520,22 @@ int safe_atolli(const char *s, long long int *ret_lli) {
}
int safe_atou8(const char *s, uint8_t *ret) {
- char *x = NULL;
+ unsigned base = 0;
unsigned long l;
+ char *x = NULL;
assert(s);
s += strspn(s, WHITESPACE);
+ s = mangle_base(s, &base);
errno = 0;
- l = strtoul(s, &x, 0);
+ l = strtoul(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
- if (s[0] == '-')
+ if (l != 0 && s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint8_t) l != l)
return -ERANGE;
@@ -470,34 +550,53 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
unsigned long l;
assert(s);
- assert(ret);
- assert(base <= 16);
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
+ strchr(WHITESPACE, s[0]))
+ return -EINVAL;
s += strspn(s, WHITESPACE);
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
+ IN_SET(s[0], '+', '-'))
+ return -EINVAL;
+
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
+ s[0] == '0' && s[1] != 0)
+ return -EINVAL;
+
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtoul(s, &x, base);
+ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base));
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
- if (s[0] == '-')
+ if (l != 0 && s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint16_t) l != l)
return -ERANGE;
- *ret = (uint16_t) l;
+ if (ret)
+ *ret = (uint16_t) l;
+
return 0;
}
int safe_atoi16(const char *s, int16_t *ret) {
+ unsigned base = 0;
char *x = NULL;
long l;
assert(s);
+ s += strspn(s, WHITESPACE);
+ s = mangle_base(s, &base);
+
errno = 0;
- l = strtol(s, &x, 0);
+ l = strtol(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h
index 36d76ba576..9a516ce5f6 100644
--- a/shared/systemd/src/basic/parse-util.h
+++ b/shared/systemd/src/basic/parse-util.h
@@ -21,6 +21,12 @@ 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 SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
+#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)
+#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28)
+#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE)
+#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS)
+
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {
@@ -28,7 +34,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) {
}
int safe_atoi(const char *s, int *ret_i);
-int safe_atollu(const char *s, unsigned long long *ret_u);
int safe_atolli(const char *s, long long int *ret_i);
int safe_atou8(const char *s, uint8_t *ret);
@@ -59,6 +64,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) {
return safe_atoi(s, (int*) ret_i);
}
+int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu);
+
+static inline int safe_atollu(const char *s, long long unsigned *ret_llu) {
+ return safe_atollu_full(s, 0, ret_llu);
+}
+
static inline int safe_atou64(const char *s, uint64_t *ret_u) {
assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
return safe_atollu(s, (unsigned long long*) ret_u);
@@ -69,6 +80,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) {
return safe_atolli(s, (long long int*) ret_i);
}
+static inline int safe_atoux64(const char *s, uint64_t *ret) {
+ assert_cc(sizeof(int64_t) == sizeof(long long unsigned));
+ return safe_atollu_full(s, 16, (long long unsigned*) ret);
+}
+
#if LONG_MAX == INT_MAX
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c
index 7baab4be50..45477bc245 100644
--- a/shared/systemd/src/basic/path-util.c
+++ b/shared/systemd/src/basic/path-util.c
@@ -1062,7 +1062,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
if (!path)
return -ENOMEM;
- r = glob_extend(&names, path);
+ r = glob_extend(&names, path, 0);
if (r == -ENOENT)
continue;
if (r < 0)
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index 2de3541cb8..6dcb26fb6b 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -212,50 +212,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
return 0;
}
-int rename_process(const char name[]) {
- static size_t mm_size = 0;
- static char *mm = NULL;
- bool truncated = false;
- size_t l;
-
- /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
- * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
- * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
- * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
- * truncated.
- *
- * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
-
- if (isempty(name))
- return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
-
- if (!is_main_thread())
- return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
- * cache things without locking, and we make assumptions that PR_SET_NAME sets the
- * process name that isn't correct on any other threads */
-
- l = strlen(name);
-
- /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
- * 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 userspace process names can be 15 chars at max */
- truncated = true;
+static int update_argv(const char name[], size_t l) {
+ static int can_do = -1;
- /* Second step, change glibc's ID of the process name. */
- if (program_invocation_name) {
- size_t k;
-
- k = strlen(program_invocation_name);
- strncpy(program_invocation_name, name, k);
- if (l > k)
- truncated = true;
- }
-
- /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
- * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
- * the end. This is the best option for changing /proc/self/cmdline. */
+ if (can_do == 0)
+ return 0;
+ can_do = false; /* We'll set it to true only if the whole process works */
/* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
* CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
@@ -263,22 +225,29 @@ int rename_process(const char name[]) {
* PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
* mmap() is not. */
if (geteuid() != 0)
- log_debug("Skipping PR_SET_MM, as we don't have privileges.");
- else if (mm_size < l+1) {
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+ "Skipping PR_SET_MM, as we don't have privileges.");
+
+ static size_t mm_size = 0;
+ static char *mm = NULL;
+ int r;
+
+ if (mm_size < l+1) {
size_t nn_size;
char *nn;
nn_size = PAGE_ALIGN(l+1);
nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- if (nn == MAP_FAILED) {
- log_debug_errno(errno, "mmap() failed: %m");
- goto use_saved_argv;
- }
+ if (nn == MAP_FAILED)
+ return log_debug_errno(errno, "mmap() failed: %m");
strncpy(nn, name, nn_size);
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+ if (ERRNO_IS_PRIVILEGE(errno))
+ return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
+
/* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
* below the desired start address, in which case the kernel may have kicked this back due
* to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
@@ -290,15 +259,13 @@ int rename_process(const char name[]) {
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
- log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
+ r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
(void) munmap(nn, nn_size);
- goto use_saved_argv;
+ return r;
}
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
- log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
- goto use_saved_argv;
- }
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
+ return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
} else {
/* And update the end pointer to the new end, too. If this fails, we don't really know what
* to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
@@ -320,13 +287,56 @@ int rename_process(const char name[]) {
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
}
-use_saved_argv:
+ can_do = true;
+ return 0;
+}
+
+int rename_process(const char name[]) {
+ bool truncated = false;
+
+ /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
+ * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
+ * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
+ * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
+ * truncated.
+ *
+ * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
+
+ if (isempty(name))
+ return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
+
+ if (!is_main_thread())
+ return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
+ * cache things without locking, and we make assumptions that PR_SET_NAME sets the
+ * process name that isn't correct on any other threads */
+
+ size_t l = strlen(name);
+
+ /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
+ * 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 userspace process names can be 15 chars at max */
+ truncated = true;
+
+ /* Second step, change glibc's ID of the process name. */
+ if (program_invocation_name) {
+ size_t k;
+
+ k = strlen(program_invocation_name);
+ strncpy(program_invocation_name, name, k);
+ if (l > k)
+ truncated = true;
+ }
+
+ /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
+ * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
+ * the end. This is the best option for changing /proc/self/cmdline. */
+ (void) update_argv(name, l);
+
/* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
* it still looks here */
-
if (saved_argc > 0) {
- int i;
-
if (saved_argv[0]) {
size_t k;
@@ -336,7 +346,7 @@ use_saved_argv:
truncated = true;
}
- for (i = 1; i < saved_argc; i++) {
+ for (int i = 1; i < saved_argc; i++) {
if (!saved_argv[i])
break;
@@ -634,6 +644,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
return 0;
}
+int get_process_umask(pid_t pid, mode_t *umask) {
+ _cleanup_free_ char *m = NULL;
+ const char *p;
+ int r;
+
+ assert(umask);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "status");
+
+ r = get_proc_field(p, "Umask", WHITESPACE, &m);
+ if (r == -ENOENT)
+ return -ESRCH;
+
+ return parse_mode(m, umask);
+}
+
int wait_for_terminate(pid_t pid, siginfo_t *status) {
siginfo_t dummy;
@@ -1284,8 +1311,8 @@ int safe_fork_full(
r, "Failed to rename process, ignoring: %m");
}
- if (flags & FORK_DEATHSIG)
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
+ if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
+ if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) {
log_full_errno(prio, errno, "Failed to set death signal: %m");
_exit(EXIT_FAILURE);
}
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 7b70c9f300..0501237790 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -45,6 +45,7 @@ int get_process_cwd(pid_t pid, char **cwd);
int get_process_root(pid_t pid, char **root);
int get_process_environ(pid_t pid, char **environ);
int get_process_ppid(pid_t pid, pid_t *ppid);
+int get_process_umask(pid_t pid, mode_t *umask);
int wait_for_terminate(pid_t pid, siginfo_t *status);
@@ -151,15 +152,16 @@ int must_be_root(void);
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
- FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
- FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
- FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
- FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
- FORK_WAIT = 1 << 6, /* Wait until child exited */
- FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
- FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
- FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
- FORK_STDOUT_TO_STDERR = 1 << 10, /* Make stdout a copy of stderr */
+ FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
+ FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */
+ FORK_NULL_STDIO = 1 << 4, /* Connect 0,1,2 to /dev/null */
+ FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */
+ FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */
+ FORK_WAIT = 1 << 7, /* Wait until child exited */
+ FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */
+ FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
+ FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+ FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c
index 1a29494466..512e7af9cb 100644
--- a/shared/systemd/src/basic/random-util.c
+++ b/shared/systemd/src/basic/random-util.c
@@ -21,6 +21,7 @@
#endif
#include "alloc-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
@@ -215,7 +216,9 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
r = -1;
errno = ENOSYS;
#else
- r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK);
+ r = getrandom(p, n,
+ (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
+ (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
#endif
if (r > 0) {
have_syscall = true;
@@ -246,7 +249,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
have_syscall = true;
return -EIO;
- } else if (errno == ENOSYS) {
+ } else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
/* We lack the syscall, continue with reading from /dev/urandom. */
have_syscall = false;
break;
@@ -272,6 +275,18 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
/* Use /dev/urandom instead */
break;
+
+ } else if (errno == EINVAL) {
+
+ /* Most likely: unknown flag. We know that GRND_INSECURE might cause this,
+ * hence try without. */
+
+ if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) {
+ flags = flags &~ RANDOM_ALLOW_INSECURE;
+ continue;
+ }
+
+ return -errno;
} else
return -errno;
}
@@ -334,9 +349,11 @@ void initialize_srand(void) {
/* INT_MAX gives us only 31 bits, so use 24 out of that. */
#if RAND_MAX >= INT_MAX
+assert_cc(RAND_MAX >= 16777215);
# define RAND_STEP 3
#else
-/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */
+/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */
+assert_cc(RAND_MAX >= 255);
# define RAND_STEP 1
#endif
@@ -401,7 +418,7 @@ void random_bytes(void *p, size_t n) {
* 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)
+ if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0)
return;
/* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h
index facc11b976..d8e067d96e 100644
--- a/shared/systemd/src/basic/random-util.h
+++ b/shared/systemd/src/basic/random-util.h
@@ -10,6 +10,7 @@ typedef enum RandomFlags {
RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */
RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */
+ RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */
} RandomFlags;
int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */
diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h
index 5f1956177e..621e83bf27 100644
--- a/shared/systemd/src/basic/set.h
+++ b/shared/systemd/src/basic/set.h
@@ -5,40 +5,48 @@
#include "hashmap.h"
#include "macro.h"
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define set_free_and_replace(a, b) \
+ ({ \
+ set_free(a); \
+ (a) = (b); \
+ (b) = NULL; \
+ 0; \
+ })
+
+Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
- return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
+ return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
}
static inline Set *set_free_free(Set *s) {
- return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
+ return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
}
/* no set_free_free_free */
static inline Set *set_copy(Set *s) {
- return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
+ return (Set*) _hashmap_copy(HASHMAP_BASE(s));
}
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
/* no set_replace */
static inline void *set_get(const Set *s, void *key) {
- return internal_hashmap_get(HASHMAP_BASE((Set *) s), key);
+ return _hashmap_get(HASHMAP_BASE((Set *) s), key);
}
/* no set_get2 */
static inline bool set_contains(const Set *s, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key);
+ return _hashmap_contains(HASHMAP_BASE((Set *) s), key);
}
static inline void *set_remove(Set *s, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(s), key);
+ return _hashmap_remove(HASHMAP_BASE(s), key);
}
/* no set_remove2 */
@@ -48,19 +56,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
int set_merge(Set *s, Set *other);
static inline int set_reserve(Set *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+ return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int set_move(Set *s, Set *other) {
- return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+ return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
}
static inline int set_move_one(Set *s, Set *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+ return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
}
static inline unsigned set_size(const Set *s) {
- return internal_hashmap_size(HASHMAP_BASE((Set *) s));
+ return _hashmap_size(HASHMAP_BASE((Set *) s));
}
static inline bool set_isempty(const Set *s) {
@@ -68,23 +76,23 @@ static inline bool set_isempty(const Set *s) {
}
static inline unsigned set_buckets(const Set *s) {
- return internal_hashmap_buckets(HASHMAP_BASE((Set *) s));
+ return _hashmap_buckets(HASHMAP_BASE((Set *) s));
}
bool set_iterate(const Set *s, Iterator *i, void **value);
static inline void set_clear(Set *s) {
- internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
+ _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
}
static inline void set_clear_free(Set *s) {
- internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
+ _hashmap_clear(HASHMAP_BASE(s), free, NULL);
}
/* no set_clear_free_free */
static inline void *set_steal_first(Set *s) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
}
#define set_clear_with_destructor(_s, _f) \
@@ -103,18 +111,18 @@ static inline void *set_steal_first(Set *s) {
/* no set_first_key */
static inline void *set_first(const Set *s) {
- return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL);
+ return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL);
}
/* no set_next */
static inline char **set_get_strv(Set *s) {
- return internal_hashmap_get_strv(HASHMAP_BASE(s));
+ return _hashmap_get_strv(HASHMAP_BASE(s));
}
int set_consume(Set *s, void *value);
-int set_put_strdup(Set *s, const char *p);
-int set_put_strdupv(Set *s, char **l);
+int set_put_strdup(Set **s, const char *p);
+int set_put_strdupv(Set **s, char **l);
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
#define SET_FOREACH(e, s, i) \
diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c
index 452b9ced20..9ced3a1291 100644
--- a/shared/systemd/src/basic/socket-util.c
+++ b/shared/systemd/src/basic/socket-util.c
@@ -25,6 +25,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "io-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
@@ -823,10 +824,7 @@ ssize_t send_one_fd_iov_sa(
const struct sockaddr *sa, socklen_t len,
int flags) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {};
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
@@ -855,8 +853,6 @@ ssize_t send_one_fd_iov_sa(
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = CMSG_SPACE(sizeof(int));
}
k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
if (k < 0)
@@ -882,17 +878,14 @@ ssize_t receive_one_fd_iov(
int flags,
int *ret_fd) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
.msg_iov = iov,
.msg_iovlen = iovlen,
};
- struct cmsghdr *cmsg, *found = NULL;
+ struct cmsghdr *found;
ssize_t k;
assert(transport_fd >= 0);
@@ -906,26 +899,18 @@ ssize_t receive_one_fd_iov(
* combination with send_one_fd().
*/
- k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
+ k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
if (k < 0)
- return (ssize_t) -errno;
+ return k;
- CMSG_FOREACH(cmsg, &mh) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
- assert(!found);
- found = cmsg;
- break;
- }
- }
-
- if (!found)
+ found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
+ if (!found) {
cmsg_close_all(&mh);
- /* If didn't receive an FD or any data, return an error. */
- if (k == 0 && !found)
- return -EIO;
+ /* If didn't receive an FD or any data, return an error. */
+ if (k == 0)
+ return -EIO;
+ }
if (found)
*ret_fd = *(int*) CMSG_DATA(found);
@@ -991,10 +976,6 @@ fallback:
int flush_accept(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN,
- };
int r, b;
socklen_t l = sizeof(b);
@@ -1015,12 +996,12 @@ int flush_accept(int fd) {
for (unsigned iteration = 0;; iteration++) {
int cfd;
- r = poll(&pollfd, 1, 0);
+ r = fd_wait_for_event(fd, POLLIN, 0);
if (r < 0) {
- if (errno == EINTR)
+ if (r == -EINTR)
continue;
- return -errno;
+ return r;
}
if (r == 0)
return 0;
@@ -1043,6 +1024,7 @@ int flush_accept(int fd) {
safe_close(cfd);
}
}
+#endif /* NM_IGNORED */
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
struct cmsghdr *cmsg;
@@ -1058,6 +1040,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
return NULL;
}
+#if 0 /* NM_IGNORED */
int socket_ioctl_fd(void) {
int fd;
@@ -1179,3 +1162,46 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
return socket_bind_to_ifname(fd, ifname);
}
+
+ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
+ ssize_t n;
+
+ /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably
+ * safe way, closing any SCM_RIGHTS fds in the error path.
+ *
+ * Note that unlike our usual coding style this might modify *msg on failure. */
+
+ n = recvmsg(sockfd, msg, flags);
+ if (n < 0)
+ return -errno;
+
+ if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) {
+ cmsg_close_all(msg);
+ return -EXFULL; /* a recognizable error code */
+ }
+
+ return n;
+}
+
+int socket_pass_pktinfo(int fd, bool b) {
+ int af;
+ socklen_t sl = sizeof(af);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0)
+ return -errno;
+
+ switch (af) {
+
+ case AF_INET:
+ return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b);
+
+ case AF_INET6:
+ return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b);
+
+ case AF_NETLINK:
+ return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b);
+
+ default:
+ return -EAFNOSUPPORT;
+ }
+}
diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h
index 2596c540ca..5e5cf73182 100644
--- a/shared/systemd/src/basic/socket-util.h
+++ b/shared/systemd/src/basic/socket-util.h
@@ -160,6 +160,25 @@ int flush_accept(int fd);
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
+/* Type-safe, dereferencing version of cmsg_find() */
+#define CMSG_FIND_DATA(mh, level, type, ctype) \
+ ({ \
+ struct cmsghdr *_found; \
+ _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \
+ (ctype*) (_found ? CMSG_DATA(_found) : NULL); \
+ })
+
+/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type
+ * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr"
+ * structures. */
+#define CMSG_BUFFER_TYPE(size) \
+ union { \
+ struct cmsghdr cmsghdr; \
+ uint8_t buf[size]; \
+ uint8_t align_check[(size) >= CMSG_SPACE(0) && \
+ (size) == CMSG_ALIGN(size) ? 1 : -1]; \
+ }
+
/*
* Certain hardware address types (e.g Infiniband) do not fit into sll_addr
* (8 bytes) and run over the structure. This macro returns the correct size that
@@ -201,3 +220,7 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) {
int socket_bind_to_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex);
+
+ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
+
+int socket_pass_pktinfo(int fd, bool b);
diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h
index e029f8646e..a8dc3bb6ed 100644
--- a/shared/systemd/src/basic/sort-util.h
+++ b/shared/systemd/src/basic/sort-util.h
@@ -39,7 +39,7 @@ static inline void* bsearch_safe(const void *key, const void *base,
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
-static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
+static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
if (nmemb <= 1)
return;
@@ -52,7 +52,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn
#define typesafe_qsort(p, n, func) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
- qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
+ _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
})
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index a895c1c34e..5c51e13a4b 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -99,10 +99,10 @@ bool null_or_empty(struct stat *st) {
if (S_ISREG(st->st_mode) && st->st_size <= 0)
return true;
- /* We don't want to hardcode the major/minor of /dev/null,
- * hence we do a simpler "is this a device node?" check. */
+ /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character
+ * device node?" check. */
- if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ if (S_ISCHR(st->st_mode))
return true;
return false;
@@ -113,6 +113,10 @@ int null_or_empty_path(const char *fn) {
assert(fn);
+ /* If we have the path, let's do an easy text comparison first. */
+ if (path_equal(fn, "/dev/null"))
+ return true;
+
if (stat(fn, &st) < 0)
return -errno;
@@ -183,13 +187,12 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
}
int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
- _cleanup_close_ int fd = -1;
+ struct statfs s;
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
- if (fd < 0)
+ if (statfs(path, &s) < 0)
return -errno;
- return fd_is_fs_type(fd, magic_value);
+ return is_fs_type(&s, magic_value);
}
bool is_temporary_fs(const struct statfs *s) {
@@ -384,4 +387,37 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
return 0;
}
+
+int proc_mounted(void) {
+ int r;
+
+ /* A quick check of procfs is properly mounted */
+
+ r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
+ if (r == -ENOENT) /* not mounted at all */
+ return false;
+
+ return r;
+}
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
+
+ /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
+ * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
+ * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
+ * size, backing device, inode type and if this refers to a device not the major/minor.
+ *
+ * Note that we don't care if file attributes such as ownership or access mode change, this here is
+ * about contents of the file. The purpose here is to detect file contents changes, and nothing
+ * else. */
+
+ return a && b &&
+ (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
+ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
+ a->st_mtime == b->st_mtime &&
+ (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
+ a->st_dev == b->st_dev &&
+ a->st_ino == b->st_ino &&
+ (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
+}
#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h
index 7824af3500..59aedcb7c4 100644
--- a/shared/systemd/src/basic/stat-util.h
+++ b/shared/systemd/src/basic/stat-util.h
@@ -87,3 +87,7 @@ int fd_verify_directory(int fd);
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
+
+int proc_mounted(void);
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c
index 9f15cacae5..c30e88c590 100644
--- a/shared/systemd/src/basic/string-util.c
+++ b/shared/systemd/src/basic/string-util.c
@@ -21,18 +21,19 @@
#include "util.h"
int strcmp_ptr(const char *a, const char *b) {
-
/* Like strcmp(), but tries to make sense of NULL pointers */
+
if (a && b)
return strcmp(a, b);
+ return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
+}
- if (!a && b)
- return -1;
-
- if (a && !b)
- return 1;
+int strcasecmp_ptr(const char *a, const char *b) {
+ /* Like strcasecmp(), but tries to make sense of NULL pointers */
- return 0;
+ if (a && b)
+ return strcasecmp(a, b);
+ return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
}
char* endswith(const char *s, const char *postfix) {
@@ -219,7 +220,6 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
return r;
}
-#if 0 /* NM_IGNORED */
char *strjoin_real(const char *x, ...) {
va_list ap;
size_t l;
@@ -277,6 +277,7 @@ char *strjoin_real(const char *x, ...) {
return r;
}
+#if 0 /* NM_IGNORED */
char *strstrip(char *s) {
if (!s)
return NULL;
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index 2a344b996f..09131455bf 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -27,6 +27,7 @@
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
int strcmp_ptr(const char *a, const char *b) _pure_;
+int strcasecmp_ptr(const char *a, const char *b) _pure_;
static inline bool streq_ptr(const char *a, const char *b) {
return strcmp_ptr(a, b) == 0;
diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c
index be1c8325b6..6f81213933 100644
--- a/shared/systemd/src/basic/strv.c
+++ b/shared/systemd/src/basic/strv.c
@@ -30,6 +30,18 @@ char *strv_find(char * const *l, const char *name) {
return NULL;
}
+char *strv_find_case(char * const *l, const char *name) {
+ char * const *i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (strcaseeq(*i, name))
+ return *i;
+
+ return NULL;
+}
+
char *strv_find_prefix(char * const *l, const char *name) {
char * const *i;
@@ -944,20 +956,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c
return 1;
}
-int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
+int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
int r;
- r = hashmap_ensure_allocated(h, &string_strv_hash_ops);
+ r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return string_strv_hashmap_put_internal(*h, key, value);
}
-int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
+int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
int r;
- r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops);
+ r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h
index dd3323c223..2ad927bce5 100644
--- a/shared/systemd/src/basic/strv.h
+++ b/shared/systemd/src/basic/strv.h
@@ -14,9 +14,13 @@
#include "string-util.h"
char *strv_find(char * const *l, const char *name) _pure_;
+char *strv_find_case(char * const *l, const char *name) _pure_;
char *strv_find_prefix(char * const *l, const char *name) _pure_;
char *strv_find_startswith(char * const *l, const char *name) _pure_;
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+#define strv_contains_case(l, s) (!!strv_find_case((l), (s)))
+
char **strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
@@ -54,8 +58,6 @@ static inline bool strv_equal(char * const *a, char * const *b) {
return strv_compare(a, b) == 0;
}
-#define strv_contains(l, s) (!!strv_find((l), (s)))
-
char **strv_new_internal(const char *x, ...) _sentinel_;
char **strv_new_ap(const char *x, va_list ap);
#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
@@ -104,14 +106,14 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
#define STRV_FOREACH_BACKWARDS(s, l) \
for (s = ({ \
- char **_l = l; \
+ typeof(l) _l = l; \
_l ? _l + strv_length(_l) - 1U : NULL; \
}); \
(l) && ((s) >= (l)); \
(s)--)
#define STRV_FOREACH_PAIR(x, y, l) \
- for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+ for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
char **strv_sort(char **l);
void strv_print(char * const *l);
@@ -156,6 +158,13 @@ void strv_print(char * const *l);
_x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
})
+#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x)
+#define STRCASEPTR_IN_SET(x, ...) \
+ ({ \
+ const char* _x = (x); \
+ _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
+ })
+
#define STARTSWITH_SET(p, ...) \
({ \
const char *_p = (p); \
@@ -217,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
})
extern const struct hash_ops string_strv_hash_ops;
-int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value);
-int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value);
+int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
+int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
+#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
+#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
diff --git a/shared/systemd/src/basic/user-util.h b/shared/systemd/src/basic/user-util.h
index 562f2c5ce1..acb6e573c4 100644
--- a/shared/systemd/src/basic/user-util.h
+++ b/shared/systemd/src/basic/user-util.h
@@ -99,20 +99,13 @@ static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
-bool valid_user_group_name_full(const char *u, bool strict);
-bool valid_user_group_name_or_id_full(const char *u, bool strict);
-static inline bool valid_user_group_name(const char *u) {
- return valid_user_group_name_full(u, true);
-}
-static inline bool valid_user_group_name_or_id(const char *u) {
- return valid_user_group_name_or_id_full(u, true);
-}
-static inline bool valid_user_group_name_compat(const char *u) {
- return valid_user_group_name_full(u, false);
-}
-static inline bool valid_user_group_name_or_id_compat(const char *u) {
- return valid_user_group_name_or_id_full(u, false);
-}
+typedef enum ValidUserFlags {
+ VALID_USER_RELAX = 1 << 0,
+ VALID_USER_WARN = 1 << 1,
+ VALID_USER_ALLOW_NUMERIC = 1 << 2,
+} ValidUserFlags;
+
+bool valid_user_group_name(const char *u, ValidUserFlags flags);
bool valid_gecos(const char *d);
bool valid_home(const char *p);
diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c
index ba28e12968..f2e27b6834 100644
--- a/shared/systemd/src/basic/utf8.c
+++ b/shared/systemd/src/basic/utf8.c
@@ -50,7 +50,6 @@ bool unichar_is_valid(char32_t ch) {
return true;
}
-#if 0 /* NM_IGNORED */
static bool unichar_is_control(char32_t ch) {
/*
@@ -62,7 +61,6 @@ static bool unichar_is_control(char32_t ch) {
return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
(0x7F <= ch && ch <= 0x9F);
}
-#endif /* NM_IGNORED */
/* count of characters used to encode one unicode char */
static size_t utf8_encoded_expected_len(uint8_t c) {
@@ -127,7 +125,6 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
return 0;
}
-#if 0 /* NM_IGNORED */
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
const char *p;
@@ -154,7 +151,6 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
return true;
}
-#endif /* NM_IGNORED */
char *utf8_is_valid(const char *str) {
const char *p;
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 72f60ccbf0..f65937d8e0 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -735,7 +735,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
{
gs_unref_object NMIP6Config *ip6_config = NULL;
gs_unref_hashtable GHashTable *options = NULL;
- struct in6_addr tmp_addr, *dns;
+ struct in6_addr tmp_addr;
+ const struct in6_addr *dns;
uint32_t lft_pref, lft_valid;
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
char **domains;
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
index b28e0ba084..026015a717 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.c
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -17,7 +17,6 @@
#include "udev-util.h"
#include "virt.h"
-#define SYSTEMD_PEN 43793
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h
index b3115125d9..76abd6583e 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.h
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h
@@ -8,6 +8,8 @@
#include "time-util.h"
#include "unaligned.h"
+#define SYSTEMD_PEN 43793
+
typedef enum DUIDType {
DUID_TYPE_LLT = 1,
DUID_TYPE_EN = 2,
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index 6a803d7b05..7e8149487a 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -7,7 +7,6 @@
#include <linux/if_packet.h>
#include <net/ethernet.h>
-#include <net/if_arp.h>
#include <stdint.h>
#include "sd-dhcp-client.h"
@@ -23,6 +22,11 @@ typedef struct sd_dhcp_option {
size_t length;
} sd_dhcp_option;
+typedef struct DHCPServerData {
+ struct in_addr *addr;
+ size_t size;
+} DHCPServerData;
+
extern const struct hash_ops dhcp_option_hash_ops;
int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
index 5cbebb4a34..aed30d6118 100644
--- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -5,11 +5,9 @@
Copyright © 2013 Intel Corporation. All rights reserved.
***/
-#include <stdint.h>
-#include <linux/if_packet.h>
-
#include "sd-dhcp-client.h"
+#include "dhcp-internal.h"
#include "dhcp-protocol.h"
#include "list.h"
#include "util.h"
@@ -52,20 +50,7 @@ struct sd_dhcp_lease {
struct in_addr *router;
size_t router_size;
- struct in_addr *dns;
- size_t dns_size;
-
- struct in_addr *ntp;
- size_t ntp_size;
-
- struct in_addr *sip;
- size_t sip_size;
-
- struct in_addr *pop3_server;
- size_t pop3_server_size;
-
- struct in_addr *smtp_server;
- size_t smtp_server_size;
+ DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
struct sd_dhcp_route *static_route;
size_t static_route_size, static_route_allocated;
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index 517e357d3d..b0d1216eed 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -11,9 +11,21 @@
#include "sd-event.h"
#include "list.h"
+#include "hashmap.h"
#include "macro.h"
#include "sparse-endian.h"
+typedef struct sd_dhcp6_option {
+ unsigned n_ref;
+
+ uint32_t enterprise_identifier;
+ uint16_t option;
+ void *data;
+ size_t length;
+} sd_dhcp6_option;
+
+extern const struct hash_ops dhcp6_option_hash_ops;
+
/* Common option header */
typedef struct DHCP6Option {
be16_t code;
@@ -87,10 +99,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count,
size_t *allocated);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index bb4c4d9130..d596752b3b 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -11,6 +11,7 @@
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
+#include "dhcp-identifier.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
@@ -80,6 +81,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
return 0;
}
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
+ sd_dhcp6_option *options;
+ Iterator i;
+ int r;
+
+ assert(buf);
+ assert(*buf);
+ assert(buflen);
+ assert(vendor_options);
+
+ ORDERED_HASHMAP_FOREACH(options, vendor_options, i) {
+ _cleanup_free_ uint8_t *p = NULL;
+ size_t total;
+
+ total = 4 + 2 + 2 + options->length;
+
+ p = malloc(total);
+ if (!p)
+ return -ENOMEM;
+
+ unaligned_write_be32(p, options->enterprise_identifier);
+ unaligned_write_be16(p + 4, options->option);
+ unaligned_write_be16(p + 6, options->length);
+ memcpy(p + 8, options->data, options->length);
+
+ r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
@@ -169,6 +203,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
return r;
}
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
+ _cleanup_free_ uint8_t *p = NULL;
+ size_t total = 0, offset = 0;
+ char **s;
+
+ assert_return(buf && *buf && buflen && user_class, -EINVAL);
+
+ STRV_FOREACH(s, user_class) {
+ size_t len = strlen(*s);
+ uint8_t *q;
+
+ if (len > 0xffff)
+ return -ENAMETOOLONG;
+ q = realloc(p, total + len + 2);
+ if (!q)
+ return -ENOMEM;
+
+ p = q;
+
+ unaligned_write_be16(&p[offset], len);
+ memcpy(&p[offset + 2], *s, len);
+
+ offset += 2 + len;
+ total += 2 + len;
+ }
+
+ return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
+}
+
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
+ _cleanup_free_ uint8_t *p = NULL;
+ uint32_t enterprise_identifier;
+ size_t total, offset;
+ char **s;
+
+ assert(buf);
+ assert(*buf);
+ assert(buflen);
+ assert(vendor_class);
+
+ enterprise_identifier = htobe32(SYSTEMD_PEN);
+
+ p = memdup(&enterprise_identifier, sizeof(enterprise_identifier));
+ if (!p)
+ return -ENOMEM;
+
+ total = sizeof(enterprise_identifier);
+ offset = total;
+
+ STRV_FOREACH(s, vendor_class) {
+ size_t len = strlen(*s);
+ uint8_t *q;
+
+ q = realloc(p, total + len + 2);
+ if (!q)
+ return -ENOMEM;
+
+ p = q;
+
+ unaligned_write_be16(&p[offset], len);
+ memcpy(&p[offset + 2], *s, len);
+
+ offset += 2 + len;
+ total += 2 + len;
+ }
+
+ return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
+}
+
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
DHCP6Option *option = (DHCP6Option *)buf;
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
@@ -349,13 +452,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
return 0;
}
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
+ uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
uint16_t iatype, optlen;
- size_t i, len;
+ size_t iaaddr_offset;
int r = 0, status;
+ size_t i, len;
uint16_t opt;
- size_t iaaddr_offset;
- uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
assert_return(ia, -EINVAL);
assert_return(!ia->addresses, -EINVAL);
@@ -465,11 +568,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
if (status < 0)
return status;
+
if (status > 0) {
- log_dhcp6_client(client, "IA status %d",
- status);
+ if (ret_status_code)
+ *ret_status_code = status;
- return -EINVAL;
+ log_dhcp6_client(client, "IA status %s",
+ dhcp6_message_status_to_string(status));
+
+ return 0;
}
break;
@@ -513,7 +620,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
break;
}
- return 0;
+ if (ret_status_code)
+ *ret_status_code = 0;
+
+ return 1;
}
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
@@ -599,3 +709,44 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
return idx;
}
+
+static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
+ if (!i)
+ return NULL;
+
+ free(i->data);
+ return mfree(i);
+}
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
+ assert_return(ret, -EINVAL);
+ assert_return(length == 0 || data, -EINVAL);
+
+ _cleanup_free_ void *q = memdup(data, length);
+ if (!q)
+ return -ENOMEM;
+
+ sd_dhcp6_option *p = new(sd_dhcp6_option, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (sd_dhcp6_option) {
+ .n_ref = 1,
+ .option = option,
+ .enterprise_identifier = enterprise_identifier,
+ .length = length,
+ .data = TAKE_PTR(q),
+ };
+
+ *ret = p;
+ return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp6_option_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ sd_dhcp6_option,
+ sd_dhcp6_option_unref);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
index ffae4453ac..f7a2702860 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
@@ -82,14 +82,35 @@ enum {
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
};
+/*
+ * RFC 8415, RFC 5007 and RFC 7653 status codes:
+ * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
+ */
enum {
- DHCP6_STATUS_SUCCESS = 0,
- DHCP6_STATUS_UNSPEC_FAIL = 1,
- DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
- DHCP6_STATUS_NO_BINDING = 3,
- DHCP6_STATUS_NOT_ON_LINK = 4,
- DHCP6_STATUS_USE_MULTICAST = 5,
- _DHCP6_STATUS_MAX = 6,
+ DHCP6_STATUS_SUCCESS = 0,
+ DHCP6_STATUS_UNSPEC_FAIL = 1,
+ DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
+ DHCP6_STATUS_NO_BINDING = 3,
+ DHCP6_STATUS_NOT_ON_LINK = 4,
+ DHCP6_STATUS_USE_MULTICAST = 5,
+ DHCP6_STATUS_NO_PREFIX_AVAIL = 6,
+ DHCP6_STATUS_UNKNOWN_QUERY_TYPE = 7,
+ DHCP6_STATUS_MALFORMED_QUERY = 8,
+ DHCP6_STATUS_NOT_CONFIGURED = 9,
+ DHCP6_STATUS_NOT_ALLOWED = 10,
+ DHCP6_STATUS_QUERY_TERMINATED = 11,
+ DHCP6_STATUS_DATA_MISSING = 12,
+ DHCP6_STATUS_CATCHUP_COMPLETE = 13,
+ DHCP6_STATUS_NOT_SUPPORTED = 14,
+ DHCP6_STATUS_TLS_CONNECTION_REFUSED = 15,
+ DHCP6_STATUS_ADDRESS_IN_USE = 16,
+ DHCP6_STATUS_CONFIGURATION_CONFLICT = 17,
+ DHCP6_STATUS_MISSING_BINDING_INFORMATION = 18,
+ DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19,
+ DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20,
+ DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
+ DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
+ _DHCP6_STATUS_MAX = 23,
};
enum {
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
index 238d718bfb..9baa8e8ca9 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.c
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -52,6 +52,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
free(n->port_description);
free(n->system_name);
free(n->system_description);
+ free(n->mud_url);
free(n->chassis_id_as_string);
free(n->port_id_as_string);
free(n);
@@ -294,9 +295,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
break;
- case SD_LLDP_TYPE_PRIVATE:
+ case SD_LLDP_TYPE_PRIVATE: {
if (length < 4)
log_lldp("Found private TLV that is too short, ignoring.");
+ else {
+ /* RFC 8520: MUD URL */
+ if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
+ p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
+ r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
+ length - 1 - sizeof(SD_LLDP_OUI_MUD));
+ if (r < 0)
+ return r;
+ }
+ }
+ }
break;
}
@@ -595,6 +607,17 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
return 0;
}
+_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
+ assert_return(n, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!n->mud_url)
+ return -ENODATA;
+
+ *ret = n->mud_url;
+ return 0;
+}
+
_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h
index 62dbff42ca..74175edf54 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.h
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h
@@ -54,6 +54,7 @@ struct sd_lldp_neighbor {
char *port_description;
char *system_name;
char *system_description;
+ char *mud_url;
uint16_t port_vlan_id;
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 94a6236a57..6a7382ac07 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -195,30 +195,34 @@ bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
char * const *match_paths,
char * const *match_drivers,
- char * const *match_types,
+ char * const *match_iftypes,
char * const *match_names,
char * const *match_property,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
- unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
+ const char *dev_driver,
+ unsigned short dev_iftype,
const char *dev_name,
char * const *alternative_names,
- enum nl80211_iftype wifi_iftype,
- const char *ssid,
- const struct ether_addr *bssid) {
+ enum nl80211_iftype dev_wifi_iftype,
+ const char *dev_ssid,
+ const struct ether_addr *dev_bssid) {
- const char *dev_path = NULL, *dev_driver = NULL, *mac_str;
- _cleanup_free_ char *dev_type;
+ _cleanup_free_ char *dev_iftype_str;
+ const char *dev_path = NULL;
- dev_type = link_get_type_string(iftype, device);
+ dev_iftype_str = link_get_type_string(dev_iftype, device);
if (device) {
+ const char *mac_str;
+
(void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
- (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
+ if (!dev_driver)
+ (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
if (!dev_name)
(void) sd_device_get_sysname(device, &dev_name);
if (!dev_mac &&
@@ -241,7 +245,7 @@ bool net_match_config(Set *match_mac,
if (!net_condition_test_strv(match_drivers, dev_driver))
return false;
- if (!net_condition_test_strv(match_types, dev_type))
+ if (!net_condition_test_strv(match_iftypes, dev_iftype_str))
return false;
if (!net_condition_test_ifname(match_names, dev_name, alternative_names))
@@ -250,13 +254,13 @@ bool net_match_config(Set *match_mac,
if (!net_condition_test_property(match_property, device))
return false;
- if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(wifi_iftype)))
+ if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(dev_wifi_iftype)))
return false;
- if (!net_condition_test_strv(match_ssid, ssid))
+ if (!net_condition_test_strv(match_ssid, dev_ssid))
return false;
- if (match_bssid && (!bssid || !set_contains(match_bssid, bssid)))
+ if (match_bssid && (!dev_bssid || !set_contains(match_bssid, dev_bssid)))
return false;
return true;
@@ -660,27 +664,27 @@ int config_parse_bridge_port_priority(
size_t serialize_in_addrs(FILE *f,
const struct in_addr *addresses,
size_t size,
- bool with_leading_space,
+ bool *with_leading_space,
bool (*predicate)(const struct in_addr *addr)) {
- size_t count;
- size_t i;
-
assert(f);
assert(addresses);
- count = 0;
+ size_t count = 0;
+ bool _space = false;
+ if (!with_leading_space)
+ with_leading_space = &_space;
- for (i = 0; i < size; i++) {
+ for (size_t i = 0; i < size; i++) {
char sbuf[INET_ADDRSTRLEN];
if (predicate && !predicate(&addresses[i]))
continue;
- if (with_leading_space)
+
+ if (*with_leading_space)
fputc(' ', f);
- else
- with_leading_space = true;
fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
count++;
+ *with_leading_space = true;
}
return count;
@@ -722,20 +726,22 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
return size;
}
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
- unsigned i;
-
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) {
assert(f);
assert(addresses);
assert(size);
- for (i = 0; i < size; i++) {
- char buffer[INET6_ADDRSTRLEN];
+ bool _space = false;
+ if (!with_leading_space)
+ with_leading_space = &_space;
- fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+ for (size_t i = 0; i < size; i++) {
+ char buffer[INET6_ADDRSTRLEN];
- if (i < size - 1)
+ if (*with_leading_space)
fputc(' ', f);
+ fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+ *with_leading_space = true;
}
}
@@ -776,8 +782,6 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
}
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
- unsigned i;
-
assert(f);
assert(key);
assert(routes);
@@ -785,7 +789,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
fprintf(f, "%s=", key);
- for (i = 0; i < size; i++) {
+ for (size_t i = 0; i < size; i++) {
char sbuf[INET_ADDRSTRLEN];
struct in_addr dest, gw;
uint8_t length;
@@ -794,8 +798,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
- fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length);
- fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": "");
+ fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length);
+ fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": "");
}
fputs("\n", f);
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index 6724cbbf1f..8e6377e0b0 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -17,23 +17,24 @@
#if 0 /* NM_IGNORED */
bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
- char * const *match_path,
- char * const *match_driver,
- char * const *match_type,
- char * const *match_name,
+ char * const *match_paths,
+ char * const *match_drivers,
+ char * const *match_iftypes,
+ char * const *match_names,
char * const *match_property,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
- unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
+ const char *dev_driver,
+ unsigned short dev_iftype,
const char *dev_name,
char * const *alternative_names,
- enum nl80211_iftype wifi_iftype,
- const char *ssid,
- const struct ether_addr *bssid);
+ enum nl80211_iftype dev_wifi_iftype,
+ const char *dev_ssid,
+ const struct ether_addr *dev_bssid);
CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
@@ -51,11 +52,12 @@ const char *net_get_name_persistent(sd_device *device);
size_t serialize_in_addrs(FILE *f,
const struct in_addr *addresses,
size_t size,
- bool with_leading_space,
+ bool *with_leading_space,
bool (*predicate)(const struct in_addr *addr));
int deserialize_in_addrs(struct in_addr **addresses, const char *string);
void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
- size_t size);
+ size_t size,
+ bool *with_leading_space);
int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 63321cca64..70821ac279 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -29,6 +29,7 @@
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
+#include "utf8.h"
#include "web-util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
@@ -37,6 +38,32 @@
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
+typedef struct sd_dhcp_client_id {
+ uint8_t type;
+ union {
+ struct {
+ /* 0: Generic (non-LL) (RFC 2132) */
+ uint8_t data[MAX_CLIENT_ID_LEN];
+ } _packed_ gen;
+ struct {
+ /* 1: Ethernet Link-Layer (RFC 2132) */
+ uint8_t haddr[ETH_ALEN];
+ } _packed_ eth;
+ struct {
+ /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+ uint8_t haddr[0];
+ } _packed_ ll;
+ struct {
+ /* 255: Node-specific (RFC 4361) */
+ be32_t iaid;
+ struct duid duid;
+ } _packed_ ns;
+ struct {
+ uint8_t data[MAX_CLIENT_ID_LEN];
+ } _packed_ raw;
+ };
+} _packed_ sd_dhcp_client_id;
+
struct sd_dhcp_client {
unsigned n_ref;
@@ -58,37 +85,14 @@ struct sd_dhcp_client {
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
- struct {
- uint8_t type;
- union {
- struct {
- /* 0: Generic (non-LL) (RFC 2132) */
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ gen;
- struct {
- /* 1: Ethernet Link-Layer (RFC 2132) */
- uint8_t haddr[ETH_ALEN];
- } _packed_ eth;
- struct {
- /* 2 - 254: ARP/Link-Layer (RFC 2132) */
- uint8_t haddr[0];
- } _packed_ ll;
- struct {
- /* 255: Node-specific (RFC 4361) */
- be32_t iaid;
- struct duid duid;
- } _packed_ ns;
- struct {
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ raw;
- };
- } _packed_ client_id;
+ sd_dhcp_client_id client_id;
size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
char *mudurl;
char **user_class;
uint32_t mtu;
+ uint32_t fallback_lease_lifetime;
uint32_t xid;
usec_t start_time;
uint64_t attempt;
@@ -152,6 +156,60 @@ static int client_receive_message_udp(
void *userdata);
static void client_stop(sd_dhcp_client *client, int error);
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
+ const sd_dhcp_client_id *client_id = data;
+ _cleanup_free_ char *t = NULL;
+ int r = 0;
+
+ assert_return(data, -EINVAL);
+ assert_return(len >= 1, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ len -= 1;
+ if (len > MAX_CLIENT_ID_LEN)
+ return -EINVAL;
+
+ switch (client_id->type) {
+ case 0:
+ if (utf8_is_printable((char *) client_id->gen.data, len))
+ r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
+ else
+ r = asprintf(&t, "DATA");
+ break;
+ case 1:
+ if (len != sizeof_field(sd_dhcp_client_id, eth))
+ return -EINVAL;
+
+ r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
+ client_id->eth.haddr[0],
+ client_id->eth.haddr[1],
+ client_id->eth.haddr[2],
+ client_id->eth.haddr[3],
+ client_id->eth.haddr[4],
+ client_id->eth.haddr[5]);
+ break;
+ case 2 ... 254:
+ r = asprintf(&t, "ARP/LL");
+ break;
+ case 255:
+ if (len < 6)
+ return -EINVAL;
+
+ uint32_t iaid = be32toh(client_id->ns.iaid);
+ uint16_t duid_type = be16toh(client_id->ns.duid.type);
+ if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
+ return -EINVAL;
+
+ r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+ break;
+ }
+
+ if (r < 0)
+ return -ENOMEM;
+ *ret = TAKE_PTR(t);
+ return 0;
+}
+
int sd_dhcp_client_set_callback(
sd_dhcp_client *client,
sd_dhcp_client_callback_t cb,
@@ -617,6 +675,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
return 0;
}
+int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
+ assert_return(client, -EINVAL);
+ assert_return(fallback_lease_lifetime > 0, -EINVAL);
+
+ client->fallback_lease_lifetime = fallback_lease_lifetime;
+
+ return 0;
+}
+
static int client_notify(sd_dhcp_client *client, int event) {
assert(client);
@@ -855,36 +922,12 @@ static int dhcp_client_send_raw(
packet, len);
}
-static int client_send_discover(sd_dhcp_client *client) {
- _cleanup_free_ DHCPPacket *discover = NULL;
- size_t optoffset, optlen;
+static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
sd_dhcp_option *j;
Iterator i;
int r;
assert(client);
- assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
- r = client_message_init(client, &discover, DHCP_DISCOVER,
- &optlen, &optoffset);
- if (r < 0)
- return r;
-
- /* the client may suggest values for the network address
- and lease time in the DHCPDISCOVER message. The client may include
- the ’requested IP address’ option to suggest that a particular IP
- address be assigned, and may include the ’IP address lease time’
- option to suggest the lease time it would like.
- */
- /* RFC7844 section 3:
- SHOULD NOT contain any other option. */
- if (!client->anonymize && client->last_addr != INADDR_ANY) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->last_addr);
- if (r < 0)
- return r;
- }
if (client->hostname) {
/* According to RFC 4702 "clients that send the Client FQDN option in
@@ -895,18 +938,18 @@ static int client_send_discover(sd_dhcp_client *client) {
/* it is unclear from RFC 2131 if client should send hostname in
DHCPDISCOVER but dhclient does and so we do as well
*/
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
SD_DHCP_OPTION_HOST_NAME,
strlen(client->hostname), client->hostname);
} else
- r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+ r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
client->hostname);
if (r < 0)
return r;
}
if (client->vendor_class_identifier) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
strlen(client->vendor_class_identifier),
client->vendor_class_identifier);
@@ -915,7 +958,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
if (client->mudurl) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
SD_DHCP_OPTION_MUD_URL,
strlen(client->mudurl),
client->mudurl);
@@ -924,7 +967,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
if (client->user_class) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
SD_DHCP_OPTION_USER_CLASS,
strv_length(client->user_class),
client->user_class);
@@ -933,7 +976,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
j->option, j->length, j->data);
if (r < 0)
return r;
@@ -941,13 +984,50 @@ static int client_send_discover(sd_dhcp_client *client) {
if (!ordered_hashmap_isempty(client->vendor_options)) {
r = dhcp_option_append(
- &discover->dhcp, optlen, &optoffset, 0,
+ &packet->dhcp, optlen, optoffset, 0,
SD_DHCP_OPTION_VENDOR_SPECIFIC,
ordered_hashmap_size(client->vendor_options), client->vendor_options);
if (r < 0)
return r;
}
+
+ return 0;
+}
+
+static int client_send_discover(sd_dhcp_client *client) {
+ _cleanup_free_ DHCPPacket *discover = NULL;
+ size_t optoffset, optlen;
+ int r;
+
+ assert(client);
+ assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
+
+ r = client_message_init(client, &discover, DHCP_DISCOVER,
+ &optlen, &optoffset);
+ if (r < 0)
+ return r;
+
+ /* the client may suggest values for the network address
+ and lease time in the DHCPDISCOVER message. The client may include
+ the ’requested IP address’ option to suggest that a particular IP
+ address be assigned, and may include the ’IP address lease time’
+ option to suggest the lease time it would like.
+ */
+ /* RFC7844 section 3:
+ SHOULD NOT contain any other option. */
+ if (!client->anonymize && client->last_addr != INADDR_ANY) {
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ 4, &client->last_addr);
+ if (r < 0)
+ return r;
+ }
+
+ r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
+ if (r < 0)
+ return r;
+
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
@@ -1039,36 +1119,9 @@ static int client_send_request(sd_dhcp_client *client) {
return -EINVAL;
}
- if (client->hostname) {
- if (dns_name_is_single_label(client->hostname))
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
- else
- r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
- client->hostname);
- if (r < 0)
- return r;
- }
-
- if (client->vendor_class_identifier) {
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
- strlen(client->vendor_class_identifier),
- client->vendor_class_identifier);
- if (r < 0)
- return r;
- }
-
- if (client->mudurl) {
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_MUD_URL,
- strlen(client->mudurl),
- client->mudurl);
- if (r < 0)
- return r;
- }
-
+ r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
+ if (r < 0)
+ return r;
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
@@ -1424,6 +1477,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
lease->next_server = offer->siaddr;
lease->address = offer->yiaddr;
+ if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
+ lease->lifetime = client->fallback_lease_lifetime;
+
if (lease->address == 0 ||
lease->server_address == 0 ||
lease->lifetime == 0) {
@@ -1904,13 +1960,13 @@ static int client_receive_message_raw(
sd_dhcp_client *client = userdata;
_cleanup_free_ DHCPPacket *packet = NULL;
- uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
struct iovec iov = {};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
- .msg_control = cmsgbuf,
- .msg_controllen = sizeof(cmsgbuf),
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
bool checksum = true;
@@ -1932,25 +1988,21 @@ static int client_receive_message_raw(
iov = IOVEC_MAKE(packet, buflen);
- len = recvmsg(fd, &msg, 0);
- if (len < 0) {
- if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
- return 0;
-
- return log_dhcp_client_errno(client, errno,
- "Could not receive message from raw socket: %m");
- } else if ((size_t)len < sizeof(DHCPPacket))
+ len = recvmsg_safe(fd, &msg, 0);
+ if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
return 0;
+ if (len < 0)
+ return log_dhcp_client_errno(client, len,
+ "Could not receive message from raw socket: %m");
- CMSG_FOREACH(cmsg, &msg)
- if (cmsg->cmsg_level == SOL_PACKET &&
- cmsg->cmsg_type == PACKET_AUXDATA &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
- struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
+ if ((size_t) len < sizeof(DHCPPacket))
+ return 0;
- checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
- break;
- }
+ cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
+ if (cmsg) {
+ struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
+ checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
+ }
r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index 86f2129062..ae581f910c 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -98,59 +98,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
return 0;
}
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_servers(
+ sd_dhcp_lease *lease,
+ sd_dhcp_lease_server_type what,
+ const struct in_addr **addr) {
+
assert_return(lease, -EINVAL);
+ assert_return(what >= 0, -EINVAL);
+ assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->dns_size <= 0)
+ if (lease->servers[what].size <= 0)
return -ENODATA;
- *addr = lease->dns;
- return (int) lease->dns_size;
+ *addr = lease->servers[what].addr;
+ return (int) lease->servers[what].size;
}
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
+}
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
- assert_return(lease, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (lease->ntp_size <= 0)
- return -ENODATA;
-
- *addr = lease->ntp;
- return (int) lease->ntp_size;
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
}
-
int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
- assert_return(lease, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (lease->sip_size <= 0)
- return -ENODATA;
-
- *addr = lease->sip;
- return (int) lease->sip_size;
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
}
-
-int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr) {
- assert_return(lease, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (lease->pop3_server_size <= 0)
- return -ENODATA;
-
- *addr = lease->pop3_server;
- return (int) lease->pop3_server_size;
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
}
-
-int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr) {
- assert_return(lease, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (lease->smtp_server_size <= 0)
- return -ENODATA;
-
- *addr = lease->smtp_server;
- return (int) lease->smtp_server_size;
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
+}
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
+ return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
}
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
@@ -300,11 +281,10 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
free(lease->timezone);
free(lease->hostname);
free(lease->domainname);
- free(lease->dns);
- free(lease->ntp);
- free(lease->sip);
- free(lease->pop3_server);
- free(lease->smtp_server);
+
+ for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
+ free(lease->servers[i].addr);
+
free(lease->static_route);
free(lease->client_id);
free(lease->vendor_specific);
@@ -410,7 +390,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
}
static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
- assert(option);
+ assert(option || len == 0);
assert(ret);
assert(n_ret);
@@ -439,33 +419,24 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add
}
static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
- assert(option);
+ assert(option || len == 0);
assert(ret);
assert(n_ret);
- if (len <= 0) {
- *ret = mfree(*ret);
- *n_ret = 0;
- } else {
- size_t n_addresses;
- struct in_addr *addresses;
- int l = len - 1;
-
- if (l % 4 != 0)
- return -EINVAL;
-
- n_addresses = l / 4;
+ if (len <= 0)
+ return -EINVAL;
- addresses = newdup(struct in_addr, option + 1, n_addresses);
- if (!addresses)
- return -ENOMEM;
+ /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
+ * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
+ * the other fields */
- free(*ret);
- *ret = addresses;
- *n_ret = n_addresses;
+ if (option[0] != 1) { /* We only support IP address encoding for now */
+ *ret = mfree(*ret);
+ *n_ret = 0;
+ return 0;
}
- return 0;
+ return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
}
static int lease_parse_routes(
@@ -610,35 +581,41 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
+ r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
if (r < 0)
log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
break;
case SD_DHCP_OPTION_NTP_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
+ r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
if (r < 0)
log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
break;
case SD_DHCP_OPTION_SIP_SERVER:
- r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size);
+ r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
if (r < 0)
log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
break;
case SD_DHCP_OPTION_POP3_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->pop3_server, &lease->pop3_server_size);
+ r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
if (r < 0)
log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
break;
case SD_DHCP_OPTION_SMTP_SERVER:
- r = lease_parse_in_addrs(option, len, &lease->smtp_server, &lease->smtp_server_size);
+ r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
if (r < 0)
log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
break;
+ case SD_DHCP_OPTION_LPR_SERVER:
+ r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
+ break;
+
case SD_DHCP_OPTION_STATIC_ROUTE:
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
if (r < 0)
@@ -674,7 +651,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
case SD_DHCP_OPTION_HOST_NAME:
r = lease_parse_domain(option, len, &lease->hostname);
if (r < 0) {
- log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+ log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
return 0;
}
@@ -1075,8 +1052,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
*dns = NULL,
*ntp = NULL,
*sip = NULL,
- *pop3_server = NULL,
- *smtp_server = NULL,
+ *pop3 = NULL,
+ *smtp = NULL,
+ *lpr = NULL,
*mtu = NULL,
*routes = NULL,
*domains = NULL,
@@ -1106,8 +1084,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"DNS", &dns,
"NTP", &ntp,
"SIP", &sip,
- "POP3_SERVERS", &pop3_server,
- "SMTP_SERVERS", &smtp_server,
+ "POP3", &pop3,
+ "SMTP", &smtp,
+ "LPR", &lpr,
"MTU", &mtu,
"DOMAINNAME", &lease->domainname,
"HOSTNAME", &lease->hostname,
@@ -1197,43 +1176,51 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (dns) {
- r = deserialize_in_addrs(&lease->dns, dns);
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns);
if (r < 0)
log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
else
- lease->dns_size = r;
+ lease->servers[SD_DHCP_LEASE_DNS].size = r;
}
if (ntp) {
- r = deserialize_in_addrs(&lease->ntp, ntp);
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
if (r < 0)
log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
else
- lease->ntp_size = r;
+ lease->servers[SD_DHCP_LEASE_NTP].size = r;
}
if (sip) {
- r = deserialize_in_addrs(&lease->sip, sip);
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip);
if (r < 0)
log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
else
- lease->sip_size = r;
+ lease->servers[SD_DHCP_LEASE_SIP].size = r;
+ }
+
+ if (pop3) {
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3);
+ if (r < 0)
+ log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
+ else
+ lease->servers[SD_DHCP_LEASE_POP3].size = r;
}
- if (pop3_server) {
- r = deserialize_in_addrs(&lease->pop3_server, pop3_server);
+ if (smtp) {
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp);
if (r < 0)
- log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3_server);
+ log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
else
- lease->pop3_server_size = r;
+ lease->servers[SD_DHCP_LEASE_SMTP].size = r;
}
- if (smtp_server) {
- r = deserialize_in_addrs(&lease->smtp_server, smtp_server);
+ if (lpr) {
+ r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
if (r < 0)
- log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp_server);
+ log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
else
- lease->smtp_server_size = r;
+ lease->servers[SD_DHCP_LEASE_LPR].size = r;
}
if (mtu) {
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 02ebb0c201..d653b2571c 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -20,6 +20,7 @@
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "network-internal.h"
@@ -69,6 +70,8 @@ struct sd_dhcp6_client {
size_t req_opts_len;
char *fqdn;
char *mudurl;
+ char **user_class;
+ char **vendor_class;
sd_event_source *receive_message;
usec_t retransmit_time;
uint8_t retransmit_count;
@@ -80,6 +83,8 @@ struct sd_dhcp6_client {
size_t duid_len;
usec_t information_request_time_usec;
usec_t information_refresh_time_usec;
+ OrderedHashmap *extra_options;
+ OrderedHashmap *vendor_options;
};
static const uint16_t default_req_opts[] = {
@@ -108,12 +113,29 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
- [DHCP6_STATUS_SUCCESS] = "Success",
- [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
- [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
- [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
- [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
- [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+ [DHCP6_STATUS_SUCCESS] = "Success",
+ [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
+ [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
+ [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
+ [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
+ [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+ [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available",
+ [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type",
+ [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query",
+ [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured",
+ [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed",
+ [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated",
+ [DHCP6_STATUS_DATA_MISSING] = "Data missing",
+ [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete",
+ [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported",
+ [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused",
+ [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use",
+ [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict",
+ [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information",
+ [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
+ [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down",
+ [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported",
+ [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew",
};
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
@@ -207,6 +229,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
return 0;
}
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(v, -EINVAL);
+
+ r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(client->vendor_options, v, v);
+ if (r < 0)
+ return r;
+
+ sd_dhcp6_option_ref(v);
+
+ return 1;
+}
+
static int client_ensure_duid(sd_dhcp6_client *client) {
if (client->duid_len != 0)
return 0;
@@ -237,7 +278,8 @@ static int dhcp6_client_set_duid_internal(
r = dhcp_validate_duid_len(duid_type, duid_len, false);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
- log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+
+ log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
}
client->duid.type = htobe16(duid_type);
@@ -296,6 +338,48 @@ int sd_dhcp6_client_set_duid_llt(
return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
}
+static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_LLT] = "DUID-LLT",
+ [DUID_TYPE_EN] = "DUID-EN/Vendor",
+ [DUID_TYPE_LL] = "DUID-LL",
+ [DUID_TYPE_UUID] = "UUID",
+};
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType);
+
+int sd_dhcp6_client_duid_as_string(
+ sd_dhcp6_client *client,
+ char **duid) {
+ _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
+ const char *v;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->duid_len > 0, -ENODATA);
+
+ v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
+ if (v) {
+ s = strdup(v);
+ if (!s)
+ return -ENOMEM;
+ } else {
+ r = asprintf(&s, "%0x", client->duid.type);
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ t = hexmem(&client->duid.raw.data, client->duid_len);
+ if (!t)
+ return -ENOMEM;
+
+ p = strjoin(s, ":", t);
+ if (!p)
+ return -ENOMEM;
+
+ *duid = TAKE_PTR(p);
+
+ return 0;
+}
+
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@@ -307,6 +391,18 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
return 0;
}
+int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
+ assert_return(client, -EINVAL);
+ assert_return(iaid, -EINVAL);
+
+ if (!client->iaid_set)
+ return -ENODATA;
+
+ *iaid = be32toh(client->ia_na.ia_na.id);
+
+ return 0;
+}
+
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn) {
@@ -345,18 +441,8 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
- switch(option) {
-
- case SD_DHCP6_OPTION_DNS_SERVERS:
- case SD_DHCP6_OPTION_DOMAIN_LIST:
- case SD_DHCP6_OPTION_SNTP_SERVERS:
- case SD_DHCP6_OPTION_NTP_SERVER:
- case SD_DHCP6_OPTION_RAPID_COMMIT:
- break;
-
- default:
+ if (option <= 0 || option >= UINT8_MAX)
return -EINVAL;
- }
for (t = 0; t < client->req_opts_len; t++)
if (client->req_opts[t] == htobe16(option))
@@ -376,12 +462,55 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
assert_return(mudurl, -EINVAL);
- assert_return(strlen(mudurl) <= 255, -EINVAL);
+ assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
assert_return(http_url_is_valid(mudurl), -EINVAL);
return free_and_strdup(&client->mudurl, mudurl);
}
+int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
+ _cleanup_strv_free_ char **s = NULL;
+ char **p;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+ assert_return(user_class, -EINVAL);
+
+ STRV_FOREACH(p, user_class)
+ if (strlen(*p) > UINT16_MAX)
+ return -ENAMETOOLONG;
+
+ s = strv_copy(user_class);
+ if (!s)
+ return -ENOMEM;
+
+ client->user_class = TAKE_PTR(s);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
+ _cleanup_strv_free_ char **s = NULL;
+ char **p;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+ assert_return(vendor_class, -EINVAL);
+
+ STRV_FOREACH(p, vendor_class)
+ if (strlen(*p) > UINT8_MAX)
+ return -ENAMETOOLONG;
+
+ s = strv_copy(vendor_class);
+ if (!s)
+ return -ENOMEM;
+
+ client->vendor_class = TAKE_PTR(s);
+
+ return 0;
+}
+
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
assert_return(client, -EINVAL);
assert_return(delegation, -EINVAL);
@@ -436,6 +565,24 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
return 0;
}
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(v, -EINVAL);
+
+ r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
+ if (r < 0)
+ return r;
+
+ sd_dhcp6_option_ref(v);
+ return 0;
+}
+
static void client_notify(sd_dhcp6_client *client, int event) {
assert(client);
@@ -481,7 +628,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
_cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr all_servers =
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+ struct sd_dhcp6_option *j;
size_t len, optlen = 512;
+ Iterator i;
uint8_t *opt;
int r;
usec_t elapsed_usec;
@@ -542,6 +691,25 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->user_class) {
+ r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (client->vendor_class) {
+ r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (!ordered_hashmap_isempty(client->vendor_options)) {
+ r = dhcp6_option_append_vendor_option(&opt, &optlen,
+ client->vendor_options);
+ if (r < 0)
+ return r;
+ }
+
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
@@ -588,6 +756,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->user_class) {
+ r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (client->vendor_class) {
+ r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (!ordered_hashmap_isempty(client->vendor_options)) {
+ r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+ if (r < 0)
+ return r;
+ }
+
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)
@@ -622,6 +808,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->user_class) {
+ r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (client->vendor_class) {
+ r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+ if (r < 0)
+ return r;
+ }
+
+ if (!ordered_hashmap_isempty(client->vendor_options)) {
+ r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+ if (r < 0)
+ return r;
+ }
+
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)
@@ -661,6 +865,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
+ ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
+ r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
len - optlen);
if (r < 0)
@@ -888,10 +1098,11 @@ static int client_parse_message(
size_t len,
sd_dhcp6_lease *lease) {
+ uint16_t ia_na_status = 0, ia_pd_status = 0;
uint32_t lt_t1 = ~0, lt_t2 = ~0;
+ usec_t irt = IRT_DEFAULT;
bool clientid = false;
size_t pos = 0;
- usec_t irt = IRT_DEFAULT;
int r;
assert(client);
@@ -905,8 +1116,8 @@ static int client_parse_message(
DHCP6Option *option = (DHCP6Option *) &message->options[pos];
uint16_t optcode, optlen;
be32_t iaid_lease;
+ int status;
uint8_t *optval;
- int status;
if (len < pos + offsetof(DHCP6Option, data))
return -ENOBUFS;
@@ -983,10 +1194,15 @@ static int client_parse_message(
break;
}
- r = dhcp6_option_parse_ia(option, &lease->ia);
+ r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
if (r < 0 && r != -ENOMSG)
return r;
+ if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
+ pos += offsetof(DHCP6Option, data) + optlen;
+ continue;
+ }
+
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
if (r < 0)
return r;
@@ -1011,10 +1227,15 @@ static int client_parse_message(
break;
}
- r = dhcp6_option_parse_ia(option, &lease->pd);
+ r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
if (r < 0 && r != -ENOMSG)
return r;
+ if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
+ pos += offsetof(DHCP6Option, data) + optlen;
+ continue;
+ }
+
r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
if (r < 0)
return r;
@@ -1078,6 +1299,11 @@ static int client_parse_message(
pos += offsetof(DHCP6Option, data) + optlen;
}
+ if (ia_na_status > 0 && ia_pd_status > 0) {
+ log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
+ return -EINVAL;
+ }
+
if (!clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
@@ -1573,6 +1799,11 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
free(client->req_opts);
free(client->fqdn);
free(client->mudurl);
+
+ ordered_hashmap_free(client->extra_options);
+ strv_free(client->user_class);
+ strv_free(client->vendor_class);
+
return mfree(client);
}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
index 35caec7a23..b6dc027915 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -215,7 +215,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
return 0;
}
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL);
@@ -343,7 +343,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
}
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
- struct in6_addr **addrs) {
+ const struct in6_addr **addrs) {
assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index a610546c4f..0bfaf265a2 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -254,12 +254,14 @@ static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
return r;
}
- return 0;
+ return 1;
}
int sd_ipv4ll_start(sd_ipv4ll *ll) {
assert_return(ll, -EINVAL);
- assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
+
+ if (sd_ipv4ll_is_running(ll))
+ return 0;
return ipv4ll_start_internal(ll, true);
}
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index da2aa6c73b..ac3b5b369c 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -48,6 +48,7 @@ enum {
SD_DHCP_OPTION_TIME_OFFSET = 2,
SD_DHCP_OPTION_ROUTER = 3,
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
+ SD_DHCP_OPTION_LPR_SERVER = 9,
SD_DHCP_OPTION_HOST_NAME = 12,
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
SD_DHCP_OPTION_DOMAIN_NAME = 15,
@@ -184,6 +185,9 @@ int sd_dhcp_client_get_lease(
int sd_dhcp_client_set_service_type(
sd_dhcp_client *client,
int type);
+int sd_dhcp_client_set_fallback_lease_lifetime(
+ sd_dhcp_client *client,
+ uint32_t fallback_lease_lifetime);
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
@@ -201,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
* options when using RFC7844 Anonymity Profiles */
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
+
int sd_dhcp_client_attach_event(
sd_dhcp_client *client,
sd_event *event,
diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h
index 1ed5bf27a3..17bd491819 100644
--- a/src/systemd/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp-lease.h
@@ -33,6 +33,17 @@ typedef struct sd_dhcp_route sd_dhcp_route;
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
+typedef enum sd_dhcp_lease_server_type {
+ SD_DHCP_LEASE_DNS,
+ SD_DHCP_LEASE_NTP,
+ SD_DHCP_LEASE_SIP,
+ SD_DHCP_LEASE_POP3,
+ SD_DHCP_LEASE_SMTP,
+ SD_DHCP_LEASE_LPR,
+ _SD_DHCP_LEASE_SERVER_TYPE_MAX,
+ _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1,
+} sd_dhcp_lease_server_type;
+
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
@@ -42,11 +53,13 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr);
int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 091f8287ec..2b0d63a527 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include "sd-dhcp6-lease.h"
+#include "sd-dhcp6-option.h"
#include "sd-event.h"
#include "_sd-common.h"
@@ -109,6 +110,12 @@ int sd_dhcp6_client_set_duid_llt(
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
+int sd_dhcp6_client_get_iaid(
+ sd_dhcp6_client *client,
+ uint32_t *iaid);
+int sd_dhcp6_client_duid_as_string(
+ sd_dhcp6_client *client,
+ char **duid);
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn);
@@ -124,6 +131,12 @@ int sd_dhcp6_client_set_request_option(
int sd_dhcp6_client_set_request_mud_url(
sd_dhcp6_client *client,
const char *mudurl);
+int sd_dhcp6_client_set_request_user_class(
+ sd_dhcp6_client *client,
+ char** user_class);
+int sd_dhcp6_client_set_request_vendor_class(
+ sd_dhcp6_client *client,
+ char** vendor_class);
int sd_dhcp6_client_set_prefix_delegation_hint(
sd_dhcp6_client *client,
uint8_t prefixlen,
@@ -136,12 +149,17 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
int *request);
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int request);
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
+ uint32_t transaction_id);
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
+ sd_dhcp6_option *v);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
sd_dhcp6_lease **ret);
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v);
+
int sd_dhcp6_client_stop(sd_dhcp6_client *client);
int sd_dhcp6_client_start(sd_dhcp6_client *client);
int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h
index 33a32a6dc5..4301c6db87 100644
--- a/src/systemd/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -39,10 +39,9 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
-int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
- struct in6_addr **addrs);
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
diff --git a/src/systemd/src/systemd/sd-dhcp6-option.h b/src/systemd/src/systemd/sd-dhcp6-option.h
new file mode 100644
index 0000000000..88a4986315
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp6-option.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#ifndef foosddhcp6optionhfoo
+#define foosddhcp6optionhfoo
+
+/***
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp6_option sd_dhcp6_option;
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
+sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
+sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
index bf3afadcec..2dc9f63246 100644
--- a/src/systemd/src/systemd/sd-lldp.h
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -96,6 +96,9 @@ enum {
#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
+#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E }
+#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01
+
/* IEEE 802.1AB-2009 Annex E */
enum {
SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
@@ -169,6 +172,7 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
index d1bee343a2..3ddfc8cb6d 100644
--- a/src/systemd/src/systemd/sd-ndisc.h
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -30,7 +30,7 @@
_SD_BEGIN_DECLARATIONS;
-/* Neightbor Discovery Options, RFC 4861, Section 4.6 and
+/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
* https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
enum {
SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1,