summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-02-15 10:25:44 +0100
committerThomas Haller <thaller@redhat.com>2018-02-15 10:26:10 +0100
commit23e4ef50927a24f599bfe13b6059a3b6a78d1f3c (patch)
tree20eb3bc14143f737696bcbb70d9ccce4ec875b7d
parentf9c50bf3d3e1e52d5803d55fa97bf56930fd3020 (diff)
parent4d923233f224d78eef82f0c0a37d2f736cecd882 (diff)
systemd: merge branch systemd into master
-rw-r--r--Makefile.am2
-rw-r--r--config.h.meson3
-rw-r--r--configure.ac1
-rw-r--r--meson.build1
-rw-r--r--shared/nm-utils/unaligned.h60
-rw-r--r--src/systemd/sd-adapt/locale-util.h3
-rw-r--r--src/systemd/sd-adapt/nm-sd-adapt.h11
-rw-r--r--src/systemd/sd-adapt/procfs-util.h3
-rw-r--r--src/systemd/src/basic/ether-addr-util.c1
-rw-r--r--src/systemd/src/basic/fd-util.c37
-rw-r--r--src/systemd/src/basic/fd-util.h2
-rw-r--r--src/systemd/src/basic/fileio.c29
-rw-r--r--src/systemd/src/basic/fs-util.c248
-rw-r--r--src/systemd/src/basic/fs-util.h16
-rw-r--r--src/systemd/src/basic/hash-funcs.c56
-rw-r--r--src/systemd/src/basic/hash-funcs.h23
-rw-r--r--src/systemd/src/basic/hashmap.c138
-rw-r--r--src/systemd/src/basic/hashmap.h17
-rw-r--r--src/systemd/src/basic/hostname-util.h1
-rw-r--r--src/systemd/src/basic/in-addr-util.c2
-rw-r--r--src/systemd/src/basic/io-util.c9
-rw-r--r--src/systemd/src/basic/log.h59
-rw-r--r--src/systemd/src/basic/macro.h10
-rw-r--r--src/systemd/src/basic/parse-util.c46
-rw-r--r--src/systemd/src/basic/path-util.c37
-rw-r--r--src/systemd/src/basic/path-util.h1
-rw-r--r--src/systemd/src/basic/process-util.c132
-rw-r--r--src/systemd/src/basic/process-util.h52
-rw-r--r--src/systemd/src/basic/random-util.c5
-rw-r--r--src/systemd/src/basic/signal-util.h7
-rw-r--r--src/systemd/src/basic/socket-util.c139
-rw-r--r--src/systemd/src/basic/socket-util.h15
-rw-r--r--src/systemd/src/basic/string-util.c29
-rw-r--r--src/systemd/src/basic/string-util.h8
-rw-r--r--src/systemd/src/basic/strv.c45
-rw-r--r--src/systemd/src/basic/strv.h8
-rw-r--r--src/systemd/src/basic/time-util.c10
-rw-r--r--src/systemd/src/basic/time-util.h4
-rw-r--r--src/systemd/src/basic/util.c33
-rw-r--r--src/systemd/src/basic/util.h2
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c5
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-network.c5
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h65
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c316
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-protocol.h1
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c1
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c12
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c2
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c241
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c36
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c1
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c55
-rw-r--r--src/systemd/src/libsystemd/sd-id128/id128-util.c1
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h5
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h5
-rw-r--r--src/systemd/src/systemd/sd-event.h5
58 files changed, 1538 insertions, 527 deletions
diff --git a/Makefile.am b/Makefile.am
index 52ec294ad7..325a7a428f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1235,9 +1235,11 @@ src_libsystemd_nm_la_SOURCES = \
src/systemd/sd-adapt/ioprio.h \
src/systemd/sd-adapt/khash.h \
src/systemd/sd-adapt/libudev.h \
+ src/systemd/sd-adapt/locale-util.h \
src/systemd/sd-adapt/memfd-util.h \
src/systemd/sd-adapt/missing.h \
src/systemd/sd-adapt/mkdir.h \
+ src/systemd/sd-adapt/procfs-util.h \
src/systemd/sd-adapt/raw-clone.h \
src/systemd/sd-adapt/sd-daemon.h \
src/systemd/sd-adapt/stat-util.h \
diff --git a/config.h.meson b/config.h.meson
index e07e128a65..9720475238 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -125,6 +125,9 @@
/* The size of `time_t', as computed by sizeof. */
#mesondefine SIZEOF_TIME_T
+/* The size of `pid_t', as computed by sizeof. */
+#mesondefine SIZEOF_PID_T
+
/* Define to 1 to use ConsoleKit2 suspend api */
#mesondefine SUSPEND_RESUME_CONSOLEKIT
diff --git a/configure.ac b/configure.ac
index 9a57e15211..f7a1eea937 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,7 @@ dnl
AC_TYPE_PID_T
AC_CHECK_SIZEOF(dev_t)
AC_CHECK_SIZEOF(time_t)
+AC_CHECK_SIZEOF(pid_t)
AC_CHECK_DECLS([
explicit_bzero],
diff --git a/meson.build b/meson.build
index 5d56556d7b..4b09a84cc1 100644
--- a/meson.build
+++ b/meson.build
@@ -110,6 +110,7 @@ config_h.set10('HAVE_DECL_EXPLICIT_BZERO', cc.has_function('explicit_bzero', pre
# types
config_h.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix: '#include<sys/types.h>'))
config_h.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix: '#include<sys/types.h>'))
+config_h.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix: '#include<sys/types.h>'))
if not cc.has_type('pid_t', prefix: '#include<sys/types.h>')
config_h.set('pid_t', 'int')
diff --git a/shared/nm-utils/unaligned.h b/shared/nm-utils/unaligned.h
index 201b3d227e..73302b4239 100644
--- a/shared/nm-utils/unaligned.h
+++ b/shared/nm-utils/unaligned.h
@@ -26,89 +26,77 @@
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[0]) << 8) |
- ((uint16_t) u[1]);
+ return be16toh(u->x);
}
static inline uint32_t unaligned_read_be32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_be16(u)) << 16) |
- ((uint32_t) unaligned_read_be16(u + 2));
+ return be32toh(u->x);
}
static inline uint64_t unaligned_read_be64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_be32(u)) << 32) |
- ((uint64_t) unaligned_read_be32(u + 4));
+ return be64toh(u->x);
}
static inline void unaligned_write_be16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) (a >> 8);
- u[1] = (uint8_t) a;
+ u->x = be16toh(a);
}
static inline void unaligned_write_be32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_be16(u, (uint16_t) (a >> 16));
- unaligned_write_be16(u + 2, (uint16_t) a);
+ u->x = be32toh(a);
}
static inline void unaligned_write_be64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_be32(u, (uint32_t) (a >> 32));
- unaligned_write_be32(u + 4, (uint32_t) a);
+ u->x = be64toh(a);
}
/* LE */
static inline uint16_t unaligned_read_le16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[1]) << 8) |
- ((uint16_t) u[0]);
+ return le16toh(u->x);
}
static inline uint32_t unaligned_read_le32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
- ((uint32_t) unaligned_read_le16(u));
+ return le32toh(u->x);
}
static inline uint64_t unaligned_read_le64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
- ((uint64_t) unaligned_read_le32(u));
+ return le64toh(u->x);
}
static inline void unaligned_write_le16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) a;
- u[1] = (uint8_t) (a >> 8);
+ u->x = le16toh(a);
}
static inline void unaligned_write_le32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_le16(u, (uint16_t) a);
- unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+ u->x = le32toh(a);
}
static inline void unaligned_write_le64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_le32(u, (uint32_t) a);
- unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+ u->x = le64toh(a);
}
#if __BYTE_ORDER == __BIG_ENDIAN
diff --git a/src/systemd/sd-adapt/locale-util.h b/src/systemd/sd-adapt/locale-util.h
new file mode 100644
index 0000000000..637892c2d6
--- /dev/null
+++ b/src/systemd/sd-adapt/locale-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h
index 90deb501fd..753bba082b 100644
--- a/src/systemd/sd-adapt/nm-sd-adapt.h
+++ b/src/systemd/sd-adapt/nm-sd-adapt.h
@@ -138,6 +138,15 @@ G_STMT_START { \
# endif
#endif
+static inline pid_t
+raw_getpid (void) {
+#if defined(__alpha__)
+ return (pid_t) syscall (__NR_getxpid);
+#else
+ return (pid_t) syscall (__NR_getpid);
+#endif
+}
+
/*****************************************************************************/
/* work around missing uchar.h */
@@ -146,8 +155,6 @@ typedef guint32 char32_t;
/*****************************************************************************/
-#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
-
static inline int
sd_notify (int unset_environment, const char *state)
{
diff --git a/src/systemd/sd-adapt/procfs-util.h b/src/systemd/sd-adapt/procfs-util.h
new file mode 100644
index 0000000000..637892c2d6
--- /dev/null
+++ b/src/systemd/sd-adapt/procfs-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c
index dad3e6285a..93922a28da 100644
--- a/src/systemd/src/basic/ether-addr-util.c
+++ b/src/systemd/src/basic/ether-addr-util.c
@@ -20,6 +20,7 @@
#include "nm-sd-adapt.h"
+#include <errno.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <sys/types.h>
diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c
index 82944908e8..971f5ec5e8 100644
--- a/src/systemd/src/basic/fd-util.c
+++ b/src/systemd/src/basic/fd-util.c
@@ -584,3 +584,40 @@ try_dev_shm_without_o_tmpfile:
return -EOPNOTSUPP;
}
#endif /* NM_IGNORED */
+
+int fd_move_above_stdio(int fd) {
+ int flags, copy;
+ PROTECT_ERRNO;
+
+ /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of
+ * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is
+ * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that
+ * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as
+ * stdin/stdout/stderr of unrelated code.
+ *
+ * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by
+ * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has
+ * been closed before.
+ *
+ * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an
+ * error we simply return the original file descriptor, and we do not touch errno. */
+
+ if (fd < 0 || fd > 2)
+ return fd;
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ return fd;
+
+ if (flags & FD_CLOEXEC)
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ else
+ copy = fcntl(fd, F_DUPFD, 3);
+ if (copy < 0)
+ return fd;
+
+ assert(copy > 2);
+
+ (void) close(fd);
+ return copy;
+}
diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h
index 84a0816f03..284856ae6d 100644
--- a/src/systemd/src/basic/fd-util.h
+++ b/src/systemd/src/basic/fd-util.h
@@ -91,3 +91,5 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags);
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
+
+int fd_move_above_stdio(int fd);
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index a23d82a4b2..693dea908c 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -65,16 +65,28 @@ int write_string_stream_ts(
WriteStringFileFlags flags,
struct timespec *ts) {
+ bool needs_nl;
+
assert(f);
assert(line);
if (ferror(f))
return -EIO;
+ needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
+
+ if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
+ /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
+ * that the write goes out in one go, instead of two */
+
+ line = strjoina(line, "\n");
+ needs_nl = false;
+ }
+
if (fputs(line, f) == EOF)
return -errno;
- if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"))
+ if (needs_nl)
if (fputc('\n', f) == EOF)
return -errno;
@@ -919,14 +931,16 @@ int write_env_file(const char *fname, char **l) {
}
int executable_is_script(const char *path, char **interpreter) {
- int r;
_cleanup_free_ char *line = NULL;
- int len;
+ size_t len;
char *ans;
+ int r;
assert(path);
r = read_one_line_file(path, &line);
+ if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
+ return 0;
if (r < 0)
return r;
@@ -1219,8 +1233,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
@@ -1254,8 +1267,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
@@ -1295,8 +1307,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
return r;
}
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
if (!t)
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
index 3c949059a8..ef2cfa8f2a 100644
--- a/src/systemd/src/basic/fs-util.c
+++ b/src/systemd/src/basic/fs-util.c
@@ -41,6 +41,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -229,49 +230,6 @@ int readlink_and_make_absolute(const char *p, char **r) {
return 0;
}
-int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
- char *t, *s;
- int r;
-
- assert(p);
- assert(ret);
-
- r = readlink_and_make_absolute(p, &t);
- if (r < 0)
- return r;
-
- r = chase_symlinks(t, root, 0, &s);
- if (r < 0)
- /* If we can't follow up, then let's return the original string, slightly cleaned up. */
- *ret = path_kill_slashes(t);
- else {
- *ret = s;
- free(t);
- }
-
- return 0;
-}
-
-int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
- _cleanup_free_ char *target = NULL, *t = NULL;
- const char *full;
- int r;
-
- full = prefix_roota(root, path);
- r = readlink_malloc(full, &target);
- if (r < 0)
- return r;
-
- t = file_in_same_dir(path, target);
- if (!t)
- return -ENOMEM;
-
- *ret = t;
- t = NULL;
-
- return 0;
-}
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
@@ -322,43 +280,60 @@ int fd_warn_permissions(const char *path, int fd) {
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- _cleanup_close_ int fd;
- int r;
+ char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ _cleanup_close_ int fd = -1;
+ int r, ret = 0;
assert(path);
- if (parents)
- mkdir_parents(path, 0755);
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
- IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
- if (fd < 0)
- return -errno;
+ /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
+ * itself which is updated, not its target
+ *
+ * Returns the first error we encounter, but tries to apply as much as possible. */
- if (mode != MODE_INVALID) {
- r = fchmod(fd, mode);
- if (r < 0)
+ if (parents)
+ (void) mkdir_parents(path, 0755);
+
+ /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
+ * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
+ * won't trigger any driver magic or so. */
+ fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno != ENOENT)
return -errno;
- }
- if (uid != UID_INVALID || gid != GID_INVALID) {
- r = fchown(fd, uid, gid);
- if (r < 0)
+ /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
+ * here, and nothing else */
+ fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
+ if (fd < 0)
return -errno;
}
+ /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
+ * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
+ * something fchown(), fchmod(), futimensat() don't allow. */
+ xsprintf(fdpath, "/proc/self/fd/%i", fd);
+
+ if (mode != MODE_INVALID)
+ if (chmod(fdpath, mode) < 0)
+ ret = -errno;
+
+ if (uid_is_valid(uid) || gid_is_valid(gid))
+ if (chown(fdpath, uid, gid) < 0 && ret >= 0)
+ ret = -errno;
+
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
- r = futimens(fd, ts);
+ r = utimensat(AT_FDCWD, fdpath, ts, 0);
} else
- r = futimens(fd, NULL);
- if (r < 0)
+ r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+ if (r < 0 && ret >= 0)
return -errno;
- return 0;
+ return ret;
}
int touch(const char *path) {
@@ -600,16 +575,35 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
+static bool safe_transition(const struct stat *a, const struct stat *b) {
+ /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
+ * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
+ * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
+
+ if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
+ return true;
+
+ return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
+}
+
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ struct stat previous_stat;
bool exists = true;
char *todo;
int r;
assert(path);
+ /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+ if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+ return -EINVAL;
+
+ if (isempty(path))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
@@ -630,13 +624,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
* specified path. */
+ /* A root directory of "/" or "" is identical to none */
+ if (isempty(original_root) || path_equal(original_root, "/"))
+ original_root = NULL;
+
if (original_root) {
r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
- if (flags & CHASE_PREFIX_ROOT)
+ if (flags & CHASE_PREFIX_ROOT) {
+
+ /* We don't support relative paths in combination with a root directory */
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
path = prefix_roota(root, path);
+ }
}
r = path_make_absolute_cwd(path, &buffer);
@@ -647,6 +651,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &previous_stat) < 0)
+ return -errno;
+ }
+
todo = buffer;
for (;;) {
_cleanup_free_ char *first = NULL;
@@ -685,7 +694,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Two dots? Then chop off the last bit of what we already found out. */
if (path_equal(first, "/..")) {
_cleanup_free_ char *parent = NULL;
- int fd_parent = -1;
+ _cleanup_close_ int fd_parent = -1;
/* If we already are at the top, then going up will not change anything. This is in-line with
* how the kernel handles this. */
@@ -708,8 +717,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd_parent < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd_parent, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
safe_close(fd);
fd = fd_parent;
+ fd_parent = -1;
continue;
}
@@ -742,6 +762,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(child, &st) < 0)
return -errno;
+ if ((flags & CHASE_SAFE) &&
+ !safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
return -EREMOTE;
@@ -772,6 +798,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
free(done);
/* Note that we do not revalidate the root, we take it as is. */
@@ -828,6 +864,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
done = NULL;
}
+ if (flags & CHASE_OPEN) {
+ int q;
+
+ /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+ * opening /proc/self/fd/xyz. */
+
+ assert(fd >= 0);
+ q = fd;
+ fd = -1;
+
+ return q;
+ }
+
return exists;
}
@@ -845,4 +894,73 @@ int access_fd(int fd, int mode) {
return r;
}
+
+int unlinkat_deallocate(int fd, const char *name, int flags) {
+ _cleanup_close_ int truncate_fd = -1;
+ struct stat st;
+ off_t l, bs;
+
+ /* 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
+ * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
+ * returned to the free pool.
+ *
+ * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
+ * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
+ * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
+ * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
+ * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
+ * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
+ *
+ * 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) {
+ truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+ if (truncate_fd < 0) {
+
+ /* If this failed because the file doesn't exist propagate the error right-away. Also,
+ * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
+ * returned when this is a directory but we are not supposed to delete those, hence propagate
+ * the error right-away too. */
+ if (IN_SET(errno, ENOENT, EISDIR))
+ return -errno;
+
+ if (errno != ELOOP) /* don't complain if this is a symlink */
+ log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
+ }
+ }
+
+ if (unlinkat(fd, name, flags) < 0)
+ return -errno;
+
+ if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
+ return 0;
+
+ if (fstat(truncate_fd, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name);
+ return 0;
+ }
+
+ if (!S_ISREG(st.st_mode) || 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
+ * punch-hole/truncate this to release the disk space. */
+
+ bs = MAX(st.st_blksize, 512);
+ l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
+
+ if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
+ return 0; /* Successfully punched a hole! 😊 */
+
+ /* Fall back to truncation */
+ if (ftruncate(truncate_fd, 0) < 0) {
+ log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
+ return 0;
+ }
+
+ return 0;
+}
#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
index a7ba61625d..ae40d6d37f 100644
--- a/src/systemd/src/basic/fs-util.h
+++ b/src/systemd/src/basic/fs-util.h
@@ -29,6 +29,7 @@
#include <unistd.h>
#include "time-util.h"
+#include "util.h"
int unlink_noerrno(const char *path);
@@ -40,8 +41,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, const char *root, char **r);
-int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
@@ -80,24 +79,29 @@ union inotify_event_buffer {
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
enum {
- CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
- CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
- CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
+ CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
};
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline void rmdir_and_free(char *p) {
+ PROTECT_ERRNO;
(void) rmdir(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
static inline void unlink_and_free(char *p) {
- (void) unlink(p);
+ (void) unlink_noerrno(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
+
+int unlinkat_deallocate(int fd, const char *name, int flags);
diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c
index 2fd9dc2875..234565ed46 100644
--- a/src/systemd/src/basic/hash-funcs.c
+++ b/src/systemd/src/basic/hash-funcs.c
@@ -21,12 +21,16 @@
#include "nm-sd-adapt.h"
+#include <string.h>
+
#include "hash-funcs.h"
+#include "path-util.h"
void string_hash_func(const void *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
+#if 0 /* NM_IGNORED */
int string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
@@ -36,6 +40,56 @@ const struct hash_ops string_hash_ops = {
.compare = string_compare_func
};
+
+void path_hash_func(const void *p, struct siphash *state) {
+ const char *q = p;
+ size_t n;
+
+ assert(q);
+ assert(state);
+
+ /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also
+ * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does
+ * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those
+ * which begin in a slash or not) will hash differently though. */
+
+ n = strspn(q, "/");
+ if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */
+ siphash24_compress(q, 1, state);
+ q += n;
+ }
+
+ for (;;) {
+ /* Determine length of next component */
+ n = strcspn(q, "/");
+ if (n == 0) /* Reached the end? */
+ break;
+
+ /* Add this component to the hash and skip over it */
+ siphash24_compress(q, n, state);
+ q += n;
+
+ /* How many slashes follow this component? */
+ n = strspn(q, "/");
+ if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */
+ break;
+
+ /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */
+ siphash24_compress(q, 1, state);
+ q += n;
+ }
+}
+
+int path_compare_func(const void *a, const void *b) {
+ return path_compare(a, b);
+}
+
+const struct hash_ops path_hash_ops = {
+ .hash = path_hash_func,
+ .compare = path_compare_func
+};
+#endif /* NM_IGNORED */
+
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&p, sizeof(p), state);
}
@@ -65,6 +119,7 @@ const struct hash_ops uint64_hash_ops = {
.compare = uint64_compare_func
};
+#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const void *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
@@ -82,3 +137,4 @@ const struct hash_ops devt_hash_ops = {
.compare = devt_compare_func
};
#endif
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h
index 959e2c101d..945b4c251c 100644
--- a/src/systemd/src/basic/hash-funcs.h
+++ b/src/systemd/src/basic/hash-funcs.h
@@ -36,29 +36,28 @@ void string_hash_func(const void *p, struct siphash *state);
int string_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops string_hash_ops;
-/* This will compare the passed pointers directly, and will not
- * dereference them. This is hence not useful for strings or
- * suchlike. */
+void path_hash_func(const void *p, struct siphash *state);
+int path_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops path_hash_ops;
+
+/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
+ * or suchlike. */
void trivial_hash_func(const void *p, struct siphash *state);
int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops;
-/* 32bit values we can always just embed in the pointer itself, but
- * in order to support 32bit archs we need store 64bit values
- * indirectly, since they don't fit in a pointer. */
+/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
+ * values indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const void *p, struct siphash *state);
int uint64_compare_func(const void *a, const void *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
- * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
+ * 64bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const void *p, struct siphash *state) _pure_;
int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
+extern const struct hash_ops devt_hash_ops;
#else
#define devt_hash_func uint64_hash_func
#define devt_compare_func uint64_compare_func
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
index 4a58736124..a76ec8e84e 100644
--- a/src/systemd/src/basic/hashmap.c
+++ b/src/systemd/src/basic/hashmap.c
@@ -231,6 +231,8 @@ struct HashmapBase {
unsigned n_direct_entries:3; /* Number of entries in direct storage.
* Only valid if !has_indirect. */
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 */
};
@@ -250,6 +252,17 @@ struct Set {
struct HashmapBase b;
};
+typedef struct CacheMem {
+ const void **ptr;
+ size_t n_populated, n_allocated;
+ bool active:1;
+} CacheMem;
+
+struct IteratedCache {
+ HashmapBase *hashmap;
+ CacheMem keys, values;
+};
+
DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
/* No need for a separate Set pool */
@@ -353,6 +366,11 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
}
#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
+static inline void base_set_dirty(HashmapBase *h) {
+ h->dirty = true;
+}
+#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h))
+
static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
static uint8_t current[HASH_KEY_SIZE];
static bool current_initialized = false;
@@ -570,6 +588,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) {
bucket_mark_free(h, prev);
n_entries_dec(h);
+ base_set_dirty(h);
}
#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
@@ -739,6 +758,25 @@ bool set_iterate(Set *s, Iterator *i, void **value) {
(idx != IDX_NIL); \
(idx) = hashmap_iterate_entry((h), &(i)))
+IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) {
+ IteratedCache *cache;
+
+ assert(h);
+ assert(!h->cached);
+
+ if (h->cached)
+ return NULL;
+
+ cache = new0(IteratedCache, 1);
+ if (!cache)
+ return NULL;
+
+ cache->hashmap = h;
+ h->cached = true;
+
+ return cache;
+}
+
static void reset_direct_storage(HashmapBase *h) {
const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
void *p;
@@ -899,6 +937,8 @@ void internal_hashmap_clear(HashmapBase *h) {
OrderedHashmap *lh = (OrderedHashmap*) h;
lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
}
+
+ base_set_dirty(h);
}
void internal_hashmap_clear_free(HashmapBase *h) {
@@ -1043,6 +1083,8 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
#endif
+ base_set_dirty(h);
+
return 1;
}
#define hashmap_put_boldly(h, idx, swap, may_resize) \
@@ -1279,6 +1321,8 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) {
#endif
e->b.key = key;
e->value = value;
+ hashmap_set_dirty(h);
+
return 0;
}
@@ -1301,6 +1345,8 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
e = plain_bucket_at(h, idx);
e->value = value;
+ hashmap_set_dirty(h);
+
return 0;
}
@@ -1853,3 +1899,95 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
return r;
}
}
+
+/* expand the cachemem if needed, return true if newly (re)activated. */
+static int cachemem_maintain(CacheMem *mem, unsigned size) {
+ assert(mem);
+
+ if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) {
+ if (size > 0)
+ return -ENOMEM;
+ }
+
+ if (!mem->active) {
+ mem->active = true;
+ return true;
+ }
+
+ return false;
+}
+
+int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) {
+ bool sync_keys = false, sync_values = false;
+ unsigned size;
+ int r;
+
+ assert(cache);
+ assert(cache->hashmap);
+
+ size = n_entries(cache->hashmap);
+
+ if (res_keys) {
+ r = cachemem_maintain(&cache->keys, size);
+ if (r < 0)
+ return r;
+
+ sync_keys = r;
+ } else
+ cache->keys.active = false;
+
+ if (res_values) {
+ r = cachemem_maintain(&cache->values, size);
+ if (r < 0)
+ return r;
+
+ sync_values = r;
+ } else
+ cache->values.active = false;
+
+ if (cache->hashmap->dirty) {
+ if (cache->keys.active)
+ sync_keys = true;
+ if (cache->values.active)
+ sync_values = true;
+
+ cache->hashmap->dirty = false;
+ }
+
+ if (sync_keys || sync_values) {
+ unsigned i, idx;
+ Iterator iter;
+
+ i = 0;
+ HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) {
+ struct hashmap_base_entry *e;
+
+ e = bucket_at(cache->hashmap, idx);
+
+ if (sync_keys)
+ cache->keys.ptr[i] = e->key;
+ if (sync_values)
+ cache->values.ptr[i] = entry_value(cache->hashmap, e);
+ i++;
+ }
+ }
+
+ if (res_keys)
+ *res_keys = cache->keys.ptr;
+ if (res_values)
+ *res_values = cache->values.ptr;
+ if (res_n_entries)
+ *res_n_entries = size;
+
+ return 0;
+}
+
+IteratedCache *iterated_cache_free(IteratedCache *cache) {
+ if (cache) {
+ free(cache->keys.ptr);
+ free(cache->values.ptr);
+ free(cache);
+ }
+
+ return NULL;
+}
diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h
index 0eb763944c..b674910397 100644
--- a/src/systemd/src/basic/hashmap.h
+++ b/src/systemd/src/basic/hashmap.h
@@ -53,6 +53,8 @@ typedef struct Hashmap Hashmap; /* Maps keys to values */
typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
typedef struct Set Set; /* Stores just keys */
+typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */
+
/* Ideally the Iterator would be an opaque struct, but it is instantiated
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
@@ -126,6 +128,9 @@ static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h)
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
}
+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);
static inline Hashmap *hashmap_copy(Hashmap *h) {
return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
@@ -139,6 +144,14 @@ int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct h
#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)
+IteratedCache *internal_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));
+}
+static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
+ return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
+}
+
int hashmap_put(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
return hashmap_put(PLAIN_HASHMAP(h), key, value);
@@ -394,3 +407,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
+
+#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep)
diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h
index d837d6f28c..edae52e3db 100644
--- a/src/systemd/src/basic/hostname-util.h
+++ b/src/systemd/src/basic/hostname-util.h
@@ -21,6 +21,7 @@
***/
#include <stdbool.h>
+#include <stdio.h>
#include "macro.h"
diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c
index 1e5770d988..b2b68089e2 100644
--- a/src/systemd/src/basic/in-addr-util.c
+++ b/src/systemd/src/basic/in-addr-util.c
@@ -333,6 +333,7 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
return -EINVAL;
}
+#if 0 /* NM_IGNORED */
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
const char *suffix;
int r, ifi = 0;
@@ -373,6 +374,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
return r;
}
+#endif /* NM_IGNORED */
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c
index 0bb10de7c3..3fb0886455 100644
--- a/src/systemd/src/basic/io-util.c
+++ b/src/systemd/src/basic/io-util.c
@@ -35,6 +35,7 @@ int flush_fd(int fd) {
.fd = fd,
.events = POLLIN,
};
+ int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
@@ -54,7 +55,7 @@ int flush_fd(int fd) {
return -errno;
} else if (r == 0)
- return 0;
+ return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
@@ -63,11 +64,13 @@ int flush_fd(int fd) {
continue;
if (errno == EAGAIN)
- return 0;
+ return count;
return -errno;
} else if (l == 0)
- return 0;
+ return count;
+
+ count += (int) l;
}
}
diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
index c954bc557c..86f16d2b6c 100644
--- a/src/systemd/src/basic/log.h
+++ b/src/systemd/src/basic/log.h
@@ -20,18 +20,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <sys/signalfd.h>
-#include <sys/socket.h>
#include <syslog.h>
-#include "sd-id128.h"
-
#include "macro.h"
-#include "process-util.h"
+
+/* Some structures we reference but don't want to pull in headers for */
+struct iovec;
+struct signalfd_siginfo;
typedef enum LogRealm {
LOG_REALM_SYSTEMD,
@@ -52,7 +50,6 @@ typedef enum LogTarget{
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
- LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
@@ -99,11 +96,6 @@ int log_open(void);
void log_close(void);
void log_forget_fds(void);
-void log_close_syslog(void);
-void log_close_journal(void);
-void log_close_kmsg(void);
-void log_close_console(void);
-
void log_parse_environment_realm(LogRealm realm);
#define log_parse_environment() \
log_parse_environment_realm(LOG_REALM)
@@ -199,7 +191,7 @@ int log_struct_iovec_internal(
const char *file,
int line,
const char *func,
- const struct iovec input_iovec[],
+ const struct iovec *input_iovec,
size_t n_input_iovec);
/* This modifies the buffer passed! */
@@ -246,9 +238,9 @@ void log_assert_failed_return_realm(
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
({ \
- int _level = (level), _e = (error); \
- (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \
- ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \
+ int _level = (level), _e = (error), _realm = (realm); \
+ (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
+ ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
__FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
@@ -258,13 +250,15 @@ void log_assert_failed_return_realm(
#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
+int log_emergency_level(void);
+
/* Normal logging */
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
-#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
@@ -272,7 +266,7 @@ void log_assert_failed_return_realm(
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
-#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
@@ -308,10 +302,20 @@ LogTarget log_target_from_string(const char *s) _pure_;
void log_received_signal(int level, const struct signalfd_siginfo *si);
+/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
void log_set_upgrade_syslog_to_journal(bool b);
+
+/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
void log_set_always_reopen_console(bool b);
+
+/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
+ * desired as we want to reuse our logging streams. It is useful however */
void log_set_open_when_needed(bool b);
+/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
+ * stderr, the console or kmsg */
+void log_set_prohibit_ipc(bool b);
+
int log_syntax_internal(
const char *unit,
int level,
@@ -323,6 +327,16 @@ int log_syntax_internal(
const char *func,
const char *format, ...) _printf_(9, 10);
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue);
+
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
@@ -334,12 +348,9 @@ int log_syntax_internal(
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
({ \
int _level = (level); \
- if (log_get_max_level() >= LOG_PRI(_level)) { \
- _cleanup_free_ char *_p = NULL; \
- _p = utf8_escape_invalid(rvalue); \
- log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
- "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
- } \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
+ : -EINVAL; \
})
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
index eebcf81fe2..237716b8b7 100644
--- a/src/systemd/src/basic/macro.h
+++ b/src/systemd/src/basic/macro.h
@@ -148,11 +148,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
+#ifndef __COVERITY__
+# define VOID_0 ((void)0)
+#else
+# define VOID_0 ((void*)0)
+#endif
+
#define ELEMENTSOF(x) \
__extension__ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
- (void)0))
+ VOID_0))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
@@ -190,7 +196,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
- (void)0))
+ VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
index 9e59a16591..cba2d250b6 100644
--- a/src/systemd/src/basic/parse-util.c
+++ b/src/systemd/src/basic/parse-util.c
@@ -30,6 +30,7 @@
#include "alloc-util.h"
#include "errno-list.h"
#include "extract-word.h"
+#include "locale-util.h"
#include "macro.h"
#include "parse-util.h"
#include "process-util.h"
@@ -70,7 +71,6 @@ int parse_pid(const char *s, pid_t* ret_pid) {
*ret_pid = pid;
return 0;
}
-#endif /* NM_IGNORED */
int parse_mode(const char *s, mode_t *ret) {
char *x;
@@ -87,7 +87,7 @@ int parse_mode(const char *s, mode_t *ret) {
l = strtol(s, &x, 8);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (l < 0 || l > 07777)
return -ERANGE;
@@ -237,7 +237,6 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
return 0;
}
-#if 0 /* NM_IGNORED */
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
_cleanup_free_ char *word = NULL;
unsigned l, u;
@@ -288,7 +287,8 @@ int parse_errno(const char *t) {
if (r < 0)
return r;
- if (e < 0 || e > ERRNO_MAX)
+ /* 0 is also allowed here */
+ if (!errno_is_valid(e) && e != 0)
return -ERANGE;
return e;
@@ -332,7 +332,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
return 0;
}
-#endif /* NM_IGNORED */
char *format_bytes(char *buf, size_t l, uint64_t t) {
unsigned i;
@@ -374,6 +373,7 @@ finish:
return buf;
}
+#endif /* NM_IGNORED */
int safe_atou(const char *s, unsigned *ret_u) {
char *x = NULL;
@@ -396,7 +396,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -418,7 +418,7 @@ int safe_atoi(const char *s, int *ret_i) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
@@ -440,7 +440,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
l = strtoull(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (*s == '-')
return -ERANGE;
@@ -460,7 +460,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
l = strtoll(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
*ret_lli = l;
@@ -480,7 +480,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -504,7 +504,7 @@ int safe_atou16(const char *s, uint16_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -526,7 +526,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int16_t) l != l)
return -ERANGE;
@@ -535,10 +535,11 @@ int safe_atoi16(const char *s, int16_t *ret) {
return 0;
}
+#if 0 /* NM_IGNORED */
int safe_atod(const char *s, double *ret_d) {
+ _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
char *x = NULL;
double d = 0;
- locale_t loc;
assert(s);
assert(ret_d);
@@ -549,16 +550,11 @@ int safe_atod(const char *s, double *ret_d) {
errno = 0;
d = strtod_l(s, &x, loc);
- if (errno > 0) {
- freelocale(loc);
+ if (errno > 0)
return -errno;
- }
- if (!x || x == s || *x) {
- freelocale(loc);
+ if (!x || x == s || *x != 0)
return -EINVAL;
- }
- freelocale(loc);
*ret_d = (double) d;
return 0;
}
@@ -601,19 +597,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
- unsigned v;
- int r;
+ int r, v;
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
n = strndupa(p, pc - p);
- r = safe_atou(n, &v);
+ r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
- return (int) v;
+ return v;
}
int parse_percent(const char *p) {
@@ -626,7 +623,6 @@ int parse_percent(const char *p) {
return v;
}
-#if 0 /* NM_IGNORED */
int parse_nice(const char *p, int *ret) {
int n, r;
diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c
index 7f2dddf1d7..92dfecffc6 100644
--- a/src/systemd/src/basic/path-util.c
+++ b/src/systemd/src/basic/path-util.c
@@ -84,14 +84,36 @@ char *path_make_absolute(const char *p, const char *prefix) {
/* Makes every item in the list an absolute path by prepending
* the prefix, if specified and necessary */
- if (path_is_absolute(p) || !prefix)
+ if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
- return strjoin(prefix, "/", p);
+ if (endswith(prefix, "/"))
+ return strjoin(prefix, p);
+ else
+ return strjoin(prefix, "/", p);
+}
+
+int safe_getcwd(char **ret) {
+ char *cwd;
+
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return negative_errno();
+
+ /* Let's make sure the directory is really absolute, to protect us from the logic behind
+ * CVE-2018-1000001 */
+ if (cwd[0] != '/') {
+ free(cwd);
+ return -ENOMEDIUM;
+ }
+
+ *ret = cwd;
+ return 0;
}
int path_make_absolute_cwd(const char *p, char **ret) {
char *c;
+ int r;
assert(p);
assert(ret);
@@ -104,11 +126,14 @@ int path_make_absolute_cwd(const char *p, char **ret) {
else {
_cleanup_free_ char *cwd = NULL;
- cwd = get_current_dir_name();
- if (!cwd)
- return negative_errno();
+ r = safe_getcwd(&cwd);
+ if (r < 0)
+ return r;
- c = strjoin(cwd, "/", p);
+ if (endswith(cwd, "/"))
+ c = strjoin(cwd, p);
+ else
+ c = strjoin(cwd, "/", p);
}
if (!c)
return -ENOMEM;
diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h
index 8225ce78a1..20178a659d 100644
--- a/src/systemd/src/basic/path-util.h
+++ b/src/systemd/src/basic/path-util.h
@@ -43,6 +43,7 @@ bool is_path(const char *p) _pure_;
int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
+int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c
index 2f1f397c74..d151ef1de0 100644
--- a/src/systemd/src/basic/process-util.c
+++ b/src/systemd/src/basic/process-util.c
@@ -692,32 +692,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
* A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true.
*/
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+ _cleanup_free_ char *buffer = NULL;
siginfo_t status;
+ int r, prio;
- assert(name);
assert(pid > 1);
+ if (!name) {
+ r = get_process_comm(pid, &buffer);
+ if (r < 0)
+ log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
+ else
+ name = buffer;
+ }
+
+ prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
+
r = wait_for_terminate(pid, &status);
if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
+ return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
+ if (status.si_status != EXIT_SUCCESS)
+ log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
+ "%s failed with exit status %i.", strna(name), status.si_status);
else
log_debug("%s succeeded.", name);
return status.si_status;
+
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
return -EPROTO;
}
- log_warning("%s failed due to unknown reason.", name);
+ log_full(prio, "%s failed due to unknown reason.", strna(name));
return -EPROTO;
}
@@ -790,6 +801,8 @@ void sigkill_wait(pid_t pid) {
}
void sigkill_waitp(pid_t *pid) {
+ PROTECT_ERRNO;
+
if (!pid)
return;
if (*pid <= 1)
@@ -798,6 +811,13 @@ void sigkill_waitp(pid_t *pid) {
sigkill_wait(*pid);
}
+void sigterm_wait(pid_t pid) {
+ assert(pid > 1);
+
+ if (kill_and_sigcont(pid, SIGTERM) > 0)
+ (void) wait_for_terminate(pid, NULL);
+}
+
int kill_and_sigcont(pid_t pid, int sig) {
int r;
@@ -943,6 +963,17 @@ noreturn void freeze(void) {
sync();
+ /* Let's not freeze right away, but keep reaping zombies. */
+ for (;;) {
+ int r;
+ siginfo_t si = {};
+
+ r = waitid(P_ALL, 0, &si, WEXITED);
+ if (r < 0 && errno != EINTR)
+ break;
+ }
+
+ /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
for (;;)
pause();
}
@@ -1091,7 +1122,7 @@ int ioprio_parse_priority(const char *s, int *ret) {
static pid_t cached_pid = CACHED_PID_UNSET;
-static void reset_cached_pid(void) {
+void reset_cached_pid(void) {
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
cached_pid = CACHED_PID_UNSET;
}
@@ -1121,7 +1152,7 @@ pid_t getpid_cached(void) {
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
pid_t new_pid;
- new_pid = getpid();
+ new_pid = raw_getpid();
if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
/* OOM? Let's try again later */
@@ -1134,7 +1165,7 @@ pid_t getpid_cached(void) {
}
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
- return getpid();
+ return raw_getpid();
default: /* Properly initialized */
return current_value;
@@ -1159,46 +1190,72 @@ int safe_fork_full(
pid_t *ret_pid) {
pid_t original_pid, pid;
- sigset_t saved_ss;
- bool block_signals;
- int r;
+ sigset_t saved_ss, ss;
+ bool block_signals = false;
+ int prio, r;
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
+ prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
+
original_pid = getpid_cached();
- block_signals = flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG);
+ if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
- if (block_signals) {
- sigset_t ss;
+ /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
+ * be sure that SIGTERMs are not lost we might send to the child. */
- /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can be sure
- * that SIGTERMs are not lost we might send to the child. */
if (sigfillset(&ss) < 0)
- return log_debug_errno(errno, "Failed to reset signal set: %m");
+ return log_full_errno(prio, errno, "Failed to reset signal set: %m");
- if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
- return log_debug_errno(errno, "Failed to reset signal mask: %m");
+ block_signals = true;
+
+ } else if (flags & FORK_WAIT) {
+
+ /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
+
+ if (sigemptyset(&ss) < 0)
+ return log_full_errno(prio, errno, "Failed to clear signal set: %m");
+
+ if (sigaddset(&ss, SIGCHLD) < 0)
+ return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
+
+ block_signals = true;
}
- pid = fork();
+ if (block_signals)
+ if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
+ return log_full_errno(prio, errno, "Failed to set signal mask: %m");
+
+ if (flags & FORK_NEW_MOUNTNS)
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ else
+ pid = fork();
if (pid < 0) {
r = -errno;
if (block_signals) /* undo what we did above */
(void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
- return log_debug_errno(r, "Failed to fork: %m");
+ return log_full_errno(prio, r, "Failed to fork: %m");
}
if (pid > 0) {
/* We are in the parent process */
+ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
+
+ if (flags & FORK_WAIT) {
+ r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
+ return -EPROTO;
+ }
+
if (block_signals) /* undo what we did above */
(void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
- log_debug("Sucessfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
-
if (ret_pid)
*ret_pid = pid;
@@ -1216,40 +1273,45 @@ int safe_fork_full(
if (name) {
r = rename_process(name);
if (r < 0)
- log_debug_errno(r, "Failed to rename process, ignoring: %m");
+ log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
+ r, "Failed to rename process, ignoring: %m");
}
if (flags & FORK_DEATHSIG)
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
- log_debug_errno(errno, "Failed to set death signal: %m");
+ log_full_errno(prio, errno, "Failed to set death signal: %m");
_exit(EXIT_FAILURE);
}
if (flags & FORK_RESET_SIGNALS) {
r = reset_all_signal_handlers();
if (r < 0) {
- log_debug_errno(r, "Failed to reset signal handlers: %m");
+ log_full_errno(prio, r, "Failed to reset signal handlers: %m");
_exit(EXIT_FAILURE);
}
/* This implicitly undoes the signal mask stuff we did before the fork()ing above */
r = reset_signal_mask();
if (r < 0) {
- log_debug_errno(r, "Failed to reset signal mask: %m");
+ log_full_errno(prio, r, "Failed to reset signal mask: %m");
_exit(EXIT_FAILURE);
}
} else if (block_signals) { /* undo what we did above */
if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
- log_debug_errno(errno, "Failed to restore signal mask: %m");
+ log_full_errno(prio, errno, "Failed to restore signal mask: %m");
_exit(EXIT_FAILURE);
}
}
if (flags & FORK_DEATHSIG) {
+ pid_t ppid;
/* Let's see if the parent PID is still the one we started from? If not, then the parent
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
- if (getppid() != original_pid) {
+ ppid = getppid();
+ if (ppid == 0)
+ /* Parent is in a differn't PID namespace. */;
+ else if (ppid != original_pid) {
log_debug("Parent died early, raising SIGTERM.");
(void) raise(SIGTERM);
_exit(EXIT_FAILURE);
@@ -1262,7 +1324,7 @@ int safe_fork_full(
r = close_all_fds(except_fds, n_except_fds);
if (r < 0) {
- log_debug_errno(r, "Failed to close all file descriptors: %m");
+ log_full_errno(prio, r, "Failed to close all file descriptors: %m");
_exit(EXIT_FAILURE);
}
}
@@ -1276,7 +1338,7 @@ int safe_fork_full(
if (flags & FORK_NULL_STDIO) {
r = make_null_stdio();
if (r < 0) {
- log_debug_errno(r, "Failed to connect stdin/stdout to /dev/null: %m");
+ log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
_exit(EXIT_FAILURE);
}
}
diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h
index 545a4c400f..7b0fba8ad4 100644
--- a/src/systemd/src/basic/process-util.h
+++ b/src/systemd/src/basic/process-util.h
@@ -21,6 +21,7 @@
***/
#include <alloca.h>
+#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
@@ -61,11 +62,21 @@ int get_process_environ(pid_t pid, char **environ);
int get_process_ppid(pid_t pid, pid_t *ppid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
+
+typedef enum WaitFlags {
+ WAIT_LOG_ABNORMAL = 1U << 0,
+ WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1,
+
+ /* A shortcut for requesting the most complete logging */
+ WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
+} WaitFlags;
+
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
void sigkill_waitp(pid_t *pid);
+void sigterm_wait(pid_t pid);
int kill_and_sigcont(pid_t pid, int sig);
@@ -106,8 +117,13 @@ int sigchld_code_from_string(const char *s) _pure_;
int sched_policy_to_string_alloc(int i, char **s);
int sched_policy_from_string(const char *s);
-#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
-#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+static inline pid_t PTR_TO_PID(const void *p) {
+ return (pid_t) ((uintptr_t) p);
+}
+
+static inline void* PID_TO_PTR(pid_t pid) {
+ return (void*) ((uintptr_t) pid);
+}
void valgrind_summary_hack(void);
@@ -137,11 +153,19 @@ static inline bool ioprio_priority_is_valid(int i) {
static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
+
+static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
+ if (!sched_policy_is_valid(n))
+ return -EINVAL;
+
+ return sched_policy_to_string_alloc(n, s);
+}
#endif /* NM_IGNORED */
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
+void reset_cached_pid(void);
int must_be_root(void);
@@ -151,6 +175,9 @@ typedef enum ForkFlags {
FORK_DEATHSIG = 1U << 2,
FORK_NULL_STDIO = 1U << 3,
FORK_REOPEN_LOG = 1U << 4,
+ FORK_LOG = 1U << 5,
+ FORK_WAIT = 1U << 6,
+ FORK_NEW_MOUNTNS = 1U << 7,
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
@@ -160,3 +187,22 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
}
int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...);
+
+#if SIZEOF_PID_T == 4
+/* The highest possibly (theoretic) pid_t value on this architecture. */
+#define PID_T_MAX ((pid_t) INT32_MAX)
+/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
+ * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
+ * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
+ * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
+ * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
+ * least to define them here too. */
+#define TASKS_MAX 4194303U
+#elif SIZEOF_PID_T == 2
+#define PID_T_MAX ((pid_t) INT16_MAX)
+#define TASKS_MAX 32767U
+#else
+#error "Unknown pid_t size"
+#endif
+
+assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c
index 0335730c13..29ee3b459c 100644
--- a/src/systemd/src/basic/random-util.c
+++ b/src/systemd/src/basic/random-util.c
@@ -23,11 +23,12 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/random.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/time.h>
-#include <linux/random.h>
-#include <stdint.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h
index 76b239b1fc..f6c3396ebe 100644
--- a/src/systemd/src/basic/signal-util.h
+++ b/src/systemd/src/basic/signal-util.h
@@ -55,3 +55,10 @@ static inline void block_signals_reset(sigset_t *ss) {
static inline bool SIGNAL_VALID(int signo) {
return signo > 0 && signo < _NSIG;
}
+
+static inline const char* signal_to_string_with_check(int n) {
+ if (!SIGNAL_VALID(n))
+ return NULL;
+
+ return signal_to_string(n);
+}
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c
index cdea5c643e..8bddef0318 100644
--- a/src/systemd/src/basic/socket-util.c
+++ b/src/systemd/src/basic/socket-util.c
@@ -43,6 +43,7 @@
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -53,7 +54,7 @@
#if 0 /* NM_IGNORED */
#if ENABLE_IDN
-# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES)
+# define IDN_FLAGS NI_IDN
#else
# define IDN_FLAGS 0
#endif
@@ -71,7 +72,6 @@ DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
- unsigned u;
int r;
assert(a);
@@ -81,6 +81,8 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->type = SOCK_STREAM;
if (*s == '[') {
+ uint16_t port;
+
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
@@ -98,15 +100,12 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
e++;
- r = safe_atou(e, &u);
+ r = parse_ip_port(e, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
@@ -137,12 +136,13 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:");
+ unsigned port;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
- r = safe_atou(e+1, &u);
+ r = safe_atou(e+1, &port);
if (r < 0)
return r;
@@ -155,19 +155,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK;
- a->sockaddr.vm.svm_port = u;
+ a->sockaddr.vm.svm_port = port;
a->size = sizeof(struct sockaddr_vm);
} else {
+ uint16_t port;
+
e = strchr(s, ':');
if (e) {
- r = safe_atou(e+1, &u);
+ r = parse_ip_port(e + 1, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
n = strndupa(s, e-s);
/* IPv4 in w.x.y.z:p notation? */
@@ -178,7 +177,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
@@ -192,7 +191,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
@@ -200,21 +199,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else {
/* Just a port */
- r = safe_atou(s, &u);
+ r = parse_ip_port(s, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
@@ -765,19 +761,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
return 0;
}
-int getnameinfo_pretty(int fd, char **ret) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa);
-
- assert(fd >= 0);
- assert(ret);
-
- if (getsockname(fd, &sa.sa, &salen) < 0)
- return -errno;
-
- return socknameinfo_pretty(&sa, salen, ret);
-}
-
int socket_address_unlink(SocketAddress *a) {
assert(a);
@@ -824,6 +807,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) {
+ int r;
+
+ r = parse_boolean(n);
+ if (r > 0)
+ return SOCKET_ADDRESS_IPV6_ONLY;
+ if (r == 0)
+ return SOCKET_ADDRESS_BOTH;
+
+ return socket_address_bind_ipv6_only_from_string(n);
+}
+
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
assert(a);
assert(b);
@@ -958,58 +953,80 @@ int getpeercred(int fd, struct ucred *ucred) {
if (n != sizeof(struct ucred))
return -EIO;
- /* Check if the data is actually useful and not suppressed due
- * to namespacing issues */
- if (u.pid <= 0)
- return -ENODATA;
- if (u.uid == UID_INVALID)
- return -ENODATA;
- if (u.gid == GID_INVALID)
+ /* Check if the data is actually useful and not suppressed due to namespacing issues */
+ if (!pid_is_valid(u.pid))
return -ENODATA;
+ /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of
+ * receiving in "invalid" user/group we get the overflow UID/GID. */
+
*ucred = u;
return 0;
}
int getpeersec(int fd, char **ret) {
+ _cleanup_free_ char *s = NULL;
socklen_t n = 64;
- char *s;
- int r;
assert(fd >= 0);
assert(ret);
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
+ for (;;) {
+ s = new0(char, n+1);
+ if (!s)
+ return -ENOMEM;
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ break;
if (errno != ERANGE)
return -errno;
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
- return -errno;
- }
+ s = mfree(s);
}
- if (isempty(s)) {
- free(s);
+ if (isempty(s))
return -EOPNOTSUPP;
- }
*ret = s;
+ s = NULL;
+
return 0;
}
+int getpeergroups(int fd, gid_t **ret) {
+ socklen_t n = sizeof(gid_t) * 64;
+ _cleanup_free_ gid_t *d = NULL;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ d = malloc(n);
+ if (!d)
+ return -ENOMEM;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
+ break;
+
+ if (errno != ERANGE)
+ return -errno;
+
+ d = mfree(d);
+ }
+
+ assert_se(n % sizeof(gid_t) == 0);
+ n /= sizeof(gid_t);
+
+ if ((socklen_t) (int) n != n)
+ return -E2BIG;
+
+ *ret = d;
+ d = NULL;
+
+ return (int) n;
+}
+
int send_one_fd_sa(
int transport_fd,
int fd,
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
index 3d9518ab86..ac957a7600 100644
--- a/src/systemd/src/basic/socket-util.h
+++ b/src/systemd/src/basic/socket-util.h
@@ -36,18 +36,28 @@
#include "util.h"
union sockaddr_union {
+ /* The minimal, abstract version */
struct sockaddr sa;
+
+ /* The libc provided version that allocates "enough room" for every protocol */
+ struct sockaddr_storage storage;
+
+ /* Protoctol-specific implementations */
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_nl nl;
- struct sockaddr_storage storage;
struct sockaddr_ll ll;
#if 0 /* NM_IGNORED */
struct sockaddr_vm vm;
#endif /* NM_IGNORED */
+
/* Ensure there is enough space to store Infiniband addresses */
uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
+
+ /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
+ * component is always followed by at least one NUL byte. */
+ uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
};
typedef struct SocketAddress {
@@ -118,10 +128,10 @@ int getpeername_pretty(int fd, bool include_port, char **ret);
int getsockname_pretty(int fd, char **ret);
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
-int getnameinfo_pretty(int fd, char **ret);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s);
int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s) _pure_;
@@ -139,6 +149,7 @@ bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
+int getpeergroups(int fd, gid_t **ret);
int send_one_fd_sa(int transport_fd,
int fd,
diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c
index f30d5cfa0d..a8e595dbf6 100644
--- a/src/systemd/src/basic/string-util.c
+++ b/src/systemd/src/basic/string-util.c
@@ -32,6 +32,7 @@
#include "gunicode.h"
#include "macro.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
@@ -654,7 +655,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
return ret;
}
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
+ if (!offsets)
+ return;
+
+ if ((size_t) diff < offsets[0])
+ shift[0] += size;
+ if ((size_t) diff < offsets[1])
+ shift[1] += size;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
const char *i, *begin = NULL;
enum {
STATE_OTHER,
@@ -662,7 +673,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
STATE_BRACKET
} state = STATE_OTHER;
char *obuf = NULL;
- size_t osz = 0, isz;
+ size_t osz = 0, isz, shift[2] = {};
FILE *f;
assert(ibuf);
@@ -690,15 +701,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
break;
else if (*i == '\x1B')
state = STATE_ESCAPE;
- else if (*i == '\t')
+ else if (*i == '\t') {
fputs(" ", f);
- else
+ advance_offsets(i - *ibuf, highlight, shift, 7);
+ } else
fputc(*i, f);
+
break;
case STATE_ESCAPE:
if (i >= *ibuf + isz) { /* EOT */
fputc('\x1B', f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
break;
} else if (*i == '[') {
state = STATE_BRACKET;
@@ -706,6 +720,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
} else {
fputc('\x1B', f);
fputc(*i, f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
state = STATE_OTHER;
}
@@ -717,6 +732,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
fputc('\x1B', f);
fputc('[', f);
+ advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
} else if (*i == 'm')
@@ -738,6 +754,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
if (_isz)
*_isz = osz;
+ if (highlight) {
+ highlight[0] += shift[0];
+ highlight[1] += shift[1];
+ }
+
return obuf;
}
diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h
index 09a737ad37..08eda4fce0 100644
--- a/src/systemd/src/basic/string-util.h
+++ b/src/systemd/src/basic/string-util.h
@@ -52,15 +52,15 @@ static inline bool streq_ptr(const char *a, const char *b) {
}
static inline const char* strempty(const char *s) {
- return s ? s : "";
+ return s ?: "";
}
static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
+ return s ?: "(null)";
}
static inline const char *strna(const char *s) {
- return s ? s : "n/a";
+ return s ?: "n/a";
}
static inline bool isempty(const char *p) {
@@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
char *strreplace(const char *text, const char *old_string, const char *new_string);
-char *strip_tab_ansi(char **p, size_t *l);
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
index 63aa4733f4..05a101c6e6 100644
--- a/src/systemd/src/basic/strv.c
+++ b/src/systemd/src/basic/strv.c
@@ -397,42 +397,6 @@ char *strv_join(char **l, const char *separator) {
return r;
}
-char *strv_join_quoted(char **l) {
- char *buf = NULL;
- char **s;
- size_t allocated = 0, len = 0;
-
- STRV_FOREACH(s, l) {
- /* assuming here that escaped string cannot be more
- * than twice as long, and reserving space for the
- * separator and quotes.
- */
- _cleanup_free_ char *esc = NULL;
- size_t needed;
-
- if (!GREEDY_REALLOC(buf, allocated,
- len + strlen(*s) * 2 + 3))
- goto oom;
-
- esc = cescape(*s);
- if (!esc)
- goto oom;
-
- needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
- len > 0 ? " " : "", esc);
- assert(needed < allocated - len);
- len += needed;
- }
-
- if (!buf)
- buf = malloc0(1);
-
- return buf;
-
- oom:
- return mfree(buf);
-}
-
int strv_push(char ***l, char *value) {
char **c;
unsigned n, m;
@@ -486,7 +450,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
return 0;
}
-int strv_push_prepend(char ***l, char *value) {
+int strv_insert(char ***l, unsigned position, char *value) {
char **c;
unsigned n, m, i;
@@ -494,6 +458,7 @@ int strv_push_prepend(char ***l, char *value) {
return 0;
n = strv_length(*l);
+ position = MIN(position, n);
/* increase and check for overflow */
m = n + 2;
@@ -504,10 +469,12 @@ int strv_push_prepend(char ***l, char *value) {
if (!c)
return -ENOMEM;
- for (i = 0; i < n; i++)
+ for (i = 0; i < position; i++)
+ c[i] = (*l)[i];
+ c[position] = value;
+ for (i = position; i < n; i++)
c[i+1] = (*l)[i];
- c[0] = value;
c[n+1] = NULL;
free(*l);
diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h
index 0ed6b74330..44fe1f279c 100644
--- a/src/systemd/src/basic/strv.h
+++ b/src/systemd/src/basic/strv.h
@@ -54,7 +54,12 @@ int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_extend_front(char ***l, const char *value);
int strv_push(char ***l, char *value);
int strv_push_pair(char ***l, char *a, char *b);
-int strv_push_prepend(char ***l, char *value);
+int strv_insert(char ***l, unsigned position, char *value);
+
+static inline int strv_push_prepend(char ***l, char *value) {
+ return strv_insert(l, 0, value);
+}
+
int strv_consume(char ***l, char *value);
int strv_consume_pair(char ***l, char *a, char *b);
int strv_consume_prepend(char ***l, char *value);
@@ -86,7 +91,6 @@ char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
char *strv_join(char **l, const char *separator);
-char *strv_join_quoted(char **l);
char **strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s);
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
index 1c9954e630..95fbbd91e6 100644
--- a/src/systemd/src/basic/time-util.c
+++ b/src/systemd/src/basic/time-util.c
@@ -40,6 +40,7 @@
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -891,7 +892,6 @@ typedef struct ParseTimestampResult {
int parse_timestamp(const char *t, usec_t *usec) {
char *last_space, *tz = NULL;
ParseTimestampResult *shared, tmp;
- pid_t pid;
int r;
last_space = strrchr(t, ' ');
@@ -905,7 +905,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (shared == MAP_FAILED)
return negative_errno();
- r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG, &pid);
+ r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
if (r < 0) {
(void) munmap(shared, sizeof *shared);
return r;
@@ -933,12 +933,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(pid, NULL);
- if (r < 0) {
- (void) munmap(shared, sizeof *shared);
- return r;
- }
-
tmp = *shared;
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index dc4a159310..a8c5a8375b 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -100,12 +100,12 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
-static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
+static inline bool dual_timestamp_is_set(const dual_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
}
-static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
+static inline bool triple_timestamp_is_set(const triple_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
(ts->boottime > 0 && ts->boottime != USEC_INFINITY));
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
index 1cd042e41b..bdaf99de76 100644
--- a/src/systemd/src/basic/util.c
+++ b/src/systemd/src/basic/util.c
@@ -54,6 +54,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
@@ -63,6 +64,7 @@
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
+#include "virt.h"
#if 0 /* NM_IGNORED */
int saved_argc = 0;
@@ -477,31 +479,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
uint64_t system_tasks_max(void) {
-#if SIZEOF_PID_T == 4
-#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
-#elif SIZEOF_PID_T == 2
-#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
-#else
-#error "Unknown pid_t size"
-#endif
-
- _cleanup_free_ char *value = NULL, *root = NULL;
uint64_t a = TASKS_MAX, b = TASKS_MAX;
+ _cleanup_free_ char *root = NULL;
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
* limit:
*
- * a) the maximum value for the pid_t type
+ * a) the maximum tasks value the kernel allows on this architecture
* b) the cgroups pids_max attribute for the system
- * c) the kernel's configure maximum PID value
+ * c) the kernel's configured maximum PID value
*
* And then pick the smallest of the three */
- if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
- (void) safe_atou64(value, &a);
+ (void) procfs_tasks_get_limit(&a);
if (cg_get_root_path(&root) >= 0) {
- value = mfree(value);
+ _cleanup_free_ char *value = NULL;
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
(void) safe_atou64(value, &b);
@@ -620,4 +613,16 @@ int str_verscmp(const char *s1, const char *s2) {
return strcmp(os1, os2);
}
+
+/* Turn off core dumps but only if we're running outside of a container. */
+void disable_coredumps(void) {
+ int r;
+
+ if (detect_container() > 0)
+ return;
+
+ r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
+}
#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index 20181ab917..9d1b10756b 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -191,3 +191,5 @@ int update_reboot_parameter_and_warn(const char *param);
int version(void);
int str_verscmp(const char *s1, const char *s2);
+
+void disable_coredumps(void);
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
index 0b5aa2bd4f..11dcda8693 100644
--- a/src/systemd/src/libsystemd-network/arp-util.c
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -26,6 +26,7 @@
#include "arp-util.h"
#include "fd-util.h"
+#include "unaligned.h"
#include "util.h"
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
@@ -50,12 +51,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Hardware Address must be different from our own */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
- BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
index ac5cc47efd..65c182f48b 100644
--- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -34,6 +34,8 @@ struct sd_dhcp_route {
struct in_addr dst_addr;
struct in_addr gw_addr;
unsigned char dst_prefixlen;
+
+ uint8_t option;
};
struct sd_dhcp_raw_option {
diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c
index 515798f8c6..7395390f78 100644
--- a/src/systemd/src/libsystemd-network/dhcp-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp-network.c
@@ -34,6 +34,7 @@
#include "dhcp-internal.h"
#include "fd-util.h"
#include "socket-util.h"
+#include "unaligned.h"
static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
@@ -72,13 +73,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index cb5b359cbe..13844a86c6 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -29,25 +29,65 @@
#include "macro.h"
#include "sparse-endian.h"
+/* Common option header */
+typedef struct DHCP6Option {
+ be16_t code;
+ be16_t len;
+ uint8_t data[];
+} _packed_ DHCP6Option;
+
+/* Address option */
+struct iaaddr {
+ struct in6_addr address;
+ be32_t lifetime_preferred;
+ be32_t lifetime_valid;
+} _packed_;
+
+/* Prefix Delegation Prefix option */
+struct iapdprefix {
+ be32_t lifetime_preferred;
+ be32_t lifetime_valid;
+ uint8_t prefixlen;
+ struct in6_addr address;
+} _packed_;
+
typedef struct DHCP6Address DHCP6Address;
struct DHCP6Address {
LIST_FIELDS(DHCP6Address, addresses);
- struct {
- struct in6_addr address;
- be32_t lifetime_preferred;
- be32_t lifetime_valid;
- } iaaddr _packed_;
+ union {
+ struct iaaddr iaaddr;
+ struct iapdprefix iapdprefix;
+ };
};
+/* Non-temporary Address option */
+struct ia_na {
+ be32_t id;
+ be32_t lifetime_t1;
+ be32_t lifetime_t2;
+} _packed_;
+
+/* Prefix Delegation option */
+struct ia_pd {
+ be32_t id;
+ be32_t lifetime_t1;
+ be32_t lifetime_t2;
+} _packed_;
+
+/* Temporary Address option */
+struct ia_ta {
+ be32_t id;
+} _packed_;
+
struct DHCP6IA {
uint16_t type;
- struct {
- be32_t id;
- be32_t lifetime_t1;
- be32_t lifetime_t2;
- } _packed_;
+ union {
+ struct ia_na ia_na;
+ struct ia_pd ia_pd;
+ struct ia_ta ia_ta;
+ };
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
@@ -62,11 +102,12 @@ typedef struct DHCP6IA DHCP6IA;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
-int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
- DHCP6IA *ia);
+int dhcp6_option_parse_status(DHCP6Option *option);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
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-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
index a3f00d4a5d..45e0e82427 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
@@ -36,8 +36,10 @@ struct sd_dhcp6_lease {
bool rapid_commit;
DHCP6IA ia;
+ DHCP6IA pd;
DHCP6Address *addr_iter;
+ DHCP6Address *prefix_iter;
struct in6_addr *dns;
size_t dns_count;
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index b4315db934..c4000088d2 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
#include "sparse-endian.h"
@@ -35,14 +36,27 @@
#include "unaligned.h"
#include "util.h"
-#define DHCP6_OPTION_IA_NA_LEN 12
-#define DHCP6_OPTION_IA_TA_LEN 4
+typedef struct DHCP6StatusOption {
+ struct DHCP6Option option;
+ be16_t status;
+ char msg[];
+} _packed_ DHCP6StatusOption;
-typedef struct DHCP6Option {
- be16_t code;
- be16_t len;
- uint8_t data[];
-} _packed_ DHCP6Option;
+typedef struct DHCP6AddressOption {
+ struct DHCP6Option option;
+ struct iaaddr iaaddr;
+ uint8_t options[];
+} _packed_ DHCP6AddressOption;
+
+typedef struct DHCP6PDPrefixOption {
+ struct DHCP6Option option;
+ struct iapdprefix iapdprefix;
+ uint8_t options[];
+} _packed_ DHCP6PDPrefixOption;
+
+#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
+#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
+#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
@@ -85,7 +99,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
- size_t ia_buflen, ia_addrlen = 0;
+ size_t iaid_offset, ia_buflen, ia_addrlen = 0;
DHCP6Address *addr;
int r;
@@ -94,10 +108,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
+ iaid_offset = offsetof(DHCP6IA, ia_na);
break;
case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
+ iaid_offset = offsetof(DHCP6IA, ia_ta);
break;
default:
@@ -113,7 +129,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buf += sizeof(DHCP6Option);
*buflen -= sizeof(DHCP6Option);
- memcpy(*buf, &ia->id, len);
+ memcpy(*buf, (char*) ia + iaid_offset, len);
*buf += len;
*buflen -= len;
@@ -166,6 +182,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
return r;
}
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
+ DHCP6Option *option = (DHCP6Option *)buf;
+ size_t i = sizeof(*option) + sizeof(pd->ia_pd);
+ DHCP6Address *prefix;
+
+ assert_return(buf, -EINVAL);
+ assert_return(pd, -EINVAL);
+ assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
+
+ if (len < i)
+ return -ENOBUFS;
+
+ option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
+
+ memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
+
+ LIST_FOREACH(addresses, prefix, pd->addresses) {
+ DHCP6PDPrefixOption *prefix_opt;
+
+ if (len < i + sizeof(*prefix_opt))
+ return -ENOBUFS;
+
+ prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
+ prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
+ prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
+
+ memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
+ sizeof(struct iapdprefix));
+
+ i += sizeof(*prefix_opt);
+ }
+
+ option->len = htobe16(i - sizeof(*option));
+
+ return i;
+}
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
@@ -212,35 +264,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
return 0;
}
-int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
- DHCP6IA *ia) {
+int dhcp6_option_parse_status(DHCP6Option *option) {
+ DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
+ return -ENOBUFS;
+
+ return be16toh(statusopt->status);
+}
+
+static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
+ uint32_t *lifetime_valid) {
+ DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
+ DHCP6Address *addr;
+ uint32_t lt_valid, lt_pref;
int r;
- uint16_t opt, status;
- size_t optlen;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
+ return -ENOBUFS;
+
+ lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
+ lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
+
+ if (lt_valid == 0 || lt_pref > lt_valid) {
+ log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
+ lt_pref, lt_valid);
+
+ return 0;
+ }
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
+ if (r != 0)
+ return r < 0 ? r: 0;
+ }
+
+ addr = new0(DHCP6Address, 1);
+ if (!addr)
+ return -ENOMEM;
+
+ LIST_INIT(addresses, addr);
+ memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
+
+ LIST_PREPEND(addresses, ia->addresses, addr);
+
+ *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
+
+ return 0;
+}
+
+static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
+ uint32_t *lifetime_valid) {
+ DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
+ DHCP6Address *prefix;
+ uint32_t lt_valid, lt_pref;
+ int r;
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
+ return -ENOBUFS;
+
+ lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
+ lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
+
+ if (lt_valid == 0 || lt_pref > lt_valid) {
+ log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
+ lt_pref, lt_valid);
+
+ return 0;
+ }
+
+ if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
+ if (r != 0)
+ return r < 0 ? r: 0;
+ }
+
+ prefix = new0(DHCP6Address, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ LIST_INIT(addresses, prefix);
+ memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
+
+ LIST_PREPEND(addresses, ia->addresses, prefix);
+
+ *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
+
+ return 0;
+}
+
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+ uint16_t iatype, optlen;
+ size_t i, len;
+ int r = 0, status;
+ uint16_t opt;
size_t iaaddr_offset;
- DHCP6Address *addr;
- uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
+ uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
assert_return(ia, -EINVAL);
assert_return(!ia->addresses, -EINVAL);
+ iatype = be16toh(iaoption->code);
+ len = be16toh(iaoption->len);
+
switch (iatype) {
case SD_DHCP6_OPTION_IA_NA:
- if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
- sizeof(addr->iaaddr)) {
+ if (len < DHCP6_OPTION_IA_NA_LEN) {
r = -ENOBUFS;
goto error;
}
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
- memcpy(&ia->id, *buf, iaaddr_offset);
+ memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
- lt_t1 = be32toh(ia->lifetime_t1);
- lt_t2 = be32toh(ia->lifetime_t2);
+ lt_t1 = be32toh(ia->ia_na.lifetime_t1);
+ lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
- log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
+ log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
+ lt_t1, lt_t2);
+ r = -EINVAL;
+ goto error;
+ }
+
+ break;
+
+ case SD_DHCP6_OPTION_IA_PD:
+
+ if (len < sizeof(ia->ia_pd)) {
+ r = -ENOBUFS;
+ goto error;
+ }
+
+ iaaddr_offset = sizeof(ia->ia_pd);
+ memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
+
+ lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
+ lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
+
+ if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
+ log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
lt_t1, lt_t2);
r = -EINVAL;
goto error;
@@ -249,17 +413,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
case SD_DHCP6_OPTION_IA_TA:
- if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
- sizeof(addr->iaaddr)) {
+ if (len < DHCP6_OPTION_IA_TA_LEN) {
r = -ENOBUFS;
goto error;
}
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
- memcpy(&ia->id, *buf, iaaddr_offset);
-
- ia->lifetime_t1 = 0;
- ia->lifetime_t2 = 0;
+ memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
break;
@@ -269,48 +429,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
}
ia->type = iatype;
+ i = iaaddr_offset;
- *buflen -= iaaddr_offset;
- *buf += iaaddr_offset;
+ while (i < len) {
+ DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
- while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
+ if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
+ r = -ENOBUFS;
+ goto error;
+ }
+
+ opt = be16toh(option->code);
+ optlen = be16toh(option->len);
switch (opt) {
case SD_DHCP6_OPTION_IAADDR:
- addr = new0(DHCP6Address, 1);
- if (!addr) {
- r = -ENOMEM;
+ if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
+ log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
+ r = -EINVAL;
goto error;
}
- LIST_INIT(addresses, addr);
+ r = dhcp6_option_parse_address(option, ia, &lt_valid);
+ if (r < 0)
+ goto error;
+
+ if (lt_valid < lt_min)
+ lt_min = lt_valid;
- memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
+ break;
- lt_valid = be32toh(addr->iaaddr.lifetime_valid);
- lt_pref = be32toh(addr->iaaddr.lifetime_valid);
+ case SD_DHCP6_OPTION_IA_PD_PREFIX:
- if (!lt_valid || lt_pref > lt_valid) {
- log_dhcp6_client(client, "IA preferred %ds > valid %ds",
- lt_pref, lt_valid);
- free(addr);
- } else {
- LIST_PREPEND(addresses, ia->addresses, addr);
- if (lt_valid < lt_min)
- lt_min = lt_valid;
+ if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
+ log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
+ r = -EINVAL;
+ goto error;
}
+ r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
+ if (r < 0)
+ goto error;
+
+ if (lt_valid < lt_min)
+ lt_min = lt_valid;
+
break;
case SD_DHCP6_OPTION_STATUS_CODE:
- if (optlen < sizeof(status))
- break;
- status = (*buf)[0] << 8 | (*buf)[1];
+ status = dhcp6_option_parse_status(option);
if (status) {
log_dhcp6_client(client, "IA status %d",
status);
+
+ dhcp6_lease_free_ia(ia);
+
r = -EINVAL;
goto error;
}
@@ -322,30 +497,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
}
- *buflen -= optlen;
- *buf += optlen;
+ i += sizeof(*option) + optlen;
}
- if (r == -ENOMSG)
- r = 0;
+ switch(iatype) {
+ case SD_DHCP6_OPTION_IA_NA:
+ if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
+ lt_t1 = lt_min / 2;
+ lt_t2 = lt_min / 10 * 8;
+ ia->ia_na.lifetime_t1 = htobe32(lt_t1);
+ ia->ia_na.lifetime_t2 = htobe32(lt_t2);
- if (!ia->lifetime_t1 && !ia->lifetime_t2) {
- lt_t1 = lt_min / 2;
- lt_t2 = lt_min / 10 * 8;
- ia->lifetime_t1 = htobe32(lt_t1);
- ia->lifetime_t2 = htobe32(lt_t2);
+ log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
+ lt_t1, lt_t2);
+ }
- log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
- lt_t1, lt_t2);
- }
+ break;
- if (*buflen)
- r = -ENOMSG;
+ case SD_DHCP6_OPTION_IA_PD:
+ if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
+ lt_t1 = lt_min / 2;
+ lt_t2 = lt_min / 10 * 8;
+ ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
+ ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
-error:
- *buf += *buflen;
- *buflen = 0;
+ log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
+ lt_t1, lt_t2);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+error:
return r;
}
diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
index 7bbf183996..5f7e809ba1 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
@@ -34,6 +34,7 @@ struct DHCP6Message {
} _packed_;
be32_t transaction_id;
};
+ uint8_t options[];
} _packed_;
typedef struct DHCP6Message DHCP6Message;
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index c7894cc205..ede345d9ed 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -24,6 +24,7 @@
#include <linux/if.h>
#include <netinet/ether.h>
+#include "sd-id128.h"
#include "sd-ndisc.h"
#include "alloc-util.h"
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index a88908e44f..4d5cace61b 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -1265,9 +1265,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
if (!lease->have_subnet_mask) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
- log_dhcp_client(client, "received lease lacks subnet "
- "mask, and a fallback one can not be "
- "generated, ignoring");
+ log_dhcp_client(client,
+ "received lease lacks subnet mask, "
+ "and a fallback one cannot be generated, ignoring");
return -ENOMSG;
}
}
@@ -1336,9 +1336,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
if (lease->subnet_mask == INADDR_ANY) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
- log_dhcp_client(client, "received lease lacks subnet "
- "mask, and a fallback one can not be "
- "generated, ignoring");
+ log_dhcp_client(client,
+ "received lease lacks subnet mask, "
+ "and a fallback one cannot be generated, ignoring");
return -ENOMSG;
}
}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index 4bed074fba..5da864fc46 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -473,6 +473,7 @@ static int lease_parse_routes(
struct sd_dhcp_route *route = *routes + *routes_size;
int r;
+ route->option = SD_DHCP_OPTION_STATIC_ROUTE;
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
if (r < 0) {
log_debug("Failed to determine destination prefix length from class based IP, ignoring");
@@ -516,6 +517,7 @@ static int lease_parse_classless_routes(
return -ENOMEM;
route = *routes + *routes_size;
+ route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
route->dst_prefixlen = *option;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 3946b791b7..cba4a34150 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -56,6 +56,8 @@ struct sd_dhcp6_client {
size_t mac_addr_len;
uint16_t arp_type;
DHCP6IA ia_na;
+ DHCP6IA ia_pd;
+ bool prefix_delegation;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
@@ -233,7 +235,8 @@ 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);
- client->ia_na.id = htobe32(iaid);
+ client->ia_na.ia_na.id = htobe32(iaid);
+ client->ia_pd.ia_pd.id = htobe32(iaid);
return 0;
}
@@ -283,6 +286,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
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:
@@ -302,6 +306,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
return 0;
}
+int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
+ assert_return(client, -EINVAL);
+
+ client->prefix_delegation = delegation;
+
+ return 0;
+}
+
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
@@ -340,8 +352,6 @@ static int client_reset(sd_dhcp6_client *client) {
client->receive_message =
sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
-
client->transaction_id = 0;
client->transaction_start = 0;
@@ -417,6 +427,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_REQUEST:
@@ -443,6 +462,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_REBIND:
@@ -458,6 +486,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
+ if (client->prefix_delegation) {
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ if (r < 0)
+ return r;
+
+ opt += r;
+ optlen -= r;
+ }
+
break;
case DHCP6_STATE_STOPPED:
@@ -713,16 +750,20 @@ error:
static int client_ensure_iaid(sd_dhcp6_client *client) {
int r;
+ be32_t iaid;
assert(client);
- if (client->ia_na.id)
+ if (client->ia_na.ia_na.id)
return 0;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
if (r < 0)
return r;
+ client->ia_na.ia_na.id = iaid;
+ client->ia_pd.ia_pd.id = iaid;
+
return 0;
}
@@ -731,23 +772,35 @@ static int client_parse_message(
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease) {
+ size_t pos = 0;
int r;
- uint8_t *optval, *option, *id = NULL;
- uint16_t optcode, status;
- size_t optlen, id_len;
bool clientid = false;
- be32_t iaid_lease;
+ uint8_t *id = NULL;
+ size_t id_len;
+ uint32_t lt_t1 = ~0, lt_t2 = ~0;
assert(client);
assert(message);
assert(len >= sizeof(DHCP6Message));
assert(lease);
- option = (uint8_t *)message + sizeof(DHCP6Message);
len -= sizeof(DHCP6Message);
- while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
- &optval)) >= 0) {
+ while (pos < len) {
+ DHCP6Option *option = (DHCP6Option *)&message->options[pos];
+ uint16_t optcode, optlen;
+ int status;
+ uint8_t *optval;
+ be32_t iaid_lease;
+
+ if (len < offsetof(DHCP6Option, data) ||
+ len < offsetof(DHCP6Option, data) + be16toh(option->len))
+ return -ENOBUFS;
+
+ optcode = be16toh(option->code);
+ optlen = be16toh(option->len);
+ optval = option->data;
+
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
@@ -785,21 +838,21 @@ static int client_parse_message(
if (optlen != 1)
return -EINVAL;
- r = dhcp6_lease_set_preference(lease, *optval);
+ r = dhcp6_lease_set_preference(lease, optval[0]);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_STATUS_CODE:
- if (optlen < 2)
- return -EINVAL;
-
- status = optval[0] << 8 | optval[1];
+ status = dhcp6_option_parse_status(option);
if (status) {
log_dhcp6_client(client, "%s Status %s",
dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status));
+ dhcp6_lease_free_ia(&lease->ia);
+ dhcp6_lease_free_ia(&lease->pd);
+
return -EINVAL;
}
@@ -812,8 +865,35 @@ static int client_parse_message(
break;
}
- r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
- &lease->ia);
+ r = dhcp6_option_parse_ia(option, &lease->ia);
+ if (r < 0 && r != -ENOMSG)
+ return r;
+
+ r = dhcp6_lease_get_iaid(lease, &iaid_lease);
+ if (r < 0)
+ return r;
+
+ if (client->ia_na.ia_na.id != iaid_lease) {
+ log_dhcp6_client(client, "%s has wrong IAID for IA NA",
+ dhcp6_message_type_to_string(message->type));
+ return -EINVAL;
+ }
+
+ if (lease->ia.addresses) {
+ lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
+ lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
+ }
+
+ break;
+
+ case SD_DHCP6_OPTION_IA_PD:
+ if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+ log_dhcp6_client(client, "Information request ignoring IA PD option");
+
+ break;
+ }
+
+ r = dhcp6_option_parse_ia(option, &lease->pd);
if (r < 0 && r != -ENOMSG)
return r;
@@ -821,12 +901,17 @@ static int client_parse_message(
if (r < 0)
return r;
- if (client->ia_na.id != iaid_lease) {
- log_dhcp6_client(client, "%s has wrong IAID",
+ if (client->ia_pd.ia_pd.id != iaid_lease) {
+ log_dhcp6_client(client, "%s has wrong IAID for IA PD",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
+ if (lease->pd.addresses) {
+ lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
+ lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
+ }
+
break;
case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -865,12 +950,10 @@ static int client_parse_message(
break;
}
+ pos += sizeof(*option) + optlen;
}
- if (r == -ENOMSG)
- r = 0;
-
- if (r < 0 || !clientid) {
+ if (!clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
@@ -881,9 +964,20 @@ static int client_parse_message(
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
+ return r;
}
- return r;
+ if (lease->ia.addresses) {
+ lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
+ lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
+ }
+
+ if (lease->pd.addresses) {
+ lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
+ lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
+ }
+
+ return 0;
}
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
@@ -1096,6 +1190,24 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
if (r < 0)
return r;
+ if (!client->receive_message) {
+ r = sd_event_add_io(client->event, &client->receive_message,
+ client->fd, EPOLLIN, client_receive_message,
+ client);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->receive_message,
+ client->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_description(client->receive_message,
+ "dhcp6-receive-message");
+ if (r < 0)
+ goto error;
+ }
+
switch (state) {
case DHCP6_STATE_STOPPED:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
@@ -1121,17 +1233,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
case DHCP6_STATE_BOUND:
- if (client->lease->ia.lifetime_t1 == 0xffffffff ||
- client->lease->ia.lifetime_t2 == 0xffffffff) {
+ if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
+ client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
- be32toh(client->lease->ia.lifetime_t1),
- be32toh(client->lease->ia.lifetime_t2));
+ be32toh(client->lease->ia.ia_na.lifetime_t1),
+ be32toh(client->lease->ia.ia_na.lifetime_t2));
return 0;
}
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1142,18 +1254,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
10 * USEC_PER_SEC, client_timeout_t1,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
if (r < 0)
- return r;
+ goto error;
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1164,16 +1276,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
10 * USEC_PER_SEC, client_timeout_t2,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
if (r < 0)
- return r;
+ goto error;
client->state = state;
@@ -1187,18 +1299,22 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
client);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_priority(client->timeout_resend,
client->event_priority);
if (r < 0)
- return r;
+ goto error;
r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
if (r < 0)
- return r;
+ goto error;
return 0;
+
+ error:
+ client_reset(client);
+ return r;
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
@@ -1206,6 +1322,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
+ client->fd = safe_close(client->fd);
+
return 0;
}
@@ -1239,32 +1357,18 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (r < 0)
return r;
- r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
- if (r < 0) {
- _cleanup_free_ char *p = NULL;
-
- (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
- return log_dhcp6_client_errno(client, r,
- "Failed to bind to UDP socket at address %s: %m", strna(p));
- }
-
- client->fd = r;
-
- r = sd_event_add_io(client->event, &client->receive_message,
- client->fd, EPOLLIN, client_receive_message,
- client);
- if (r < 0)
- goto error;
+ if (client->fd < 0) {
+ r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
+ if (r < 0) {
+ _cleanup_free_ char *p = NULL;
- r = sd_event_source_set_priority(client->receive_message,
- client->event_priority);
- if (r < 0)
- goto error;
+ (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
+ return log_dhcp6_client_errno(client, r,
+ "Failed to bind to UDP socket at address %s: %m", strna(p));
+ }
- r = sd_event_source_set_description(client->receive_message,
- "dhcp6-receive-message");
- if (r < 0)
- goto error;
+ client->fd = r;
+ }
if (client->information_request)
state = DHCP6_STATE_INFORMATION_REQUEST;
@@ -1274,10 +1378,6 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
"Managed");
return client_start(client, state);
-
-error:
- client_reset(client);
- return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
@@ -1337,6 +1437,8 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
client_reset(client);
+ client->fd = safe_close(client->fd);
+
sd_dhcp6_client_detach_event(client);
free(client->req_opts);
@@ -1356,6 +1458,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
client->n_ref = 1;
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
+ client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
client->ifindex = -1;
client->fd = -1;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
index b5660bde81..45ef0eb6e3 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -51,7 +51,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
valid = t;
}
- t = be32toh(ia->lifetime_t2);
+ t = be32toh(ia->ia_na.lifetime_t2);
if (t > valid)
return -EINVAL;
@@ -146,7 +146,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
- *iaid = lease->ia.id;
+ *iaid = lease->ia.ia_na.id;
return 0;
}
@@ -178,6 +178,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
lease->addr_iter = lease->ia.addresses;
}
+int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
+ uint8_t *prefix_len,
+ uint32_t *lifetime_preferred,
+ uint32_t *lifetime_valid) {
+ assert_return(lease, -EINVAL);
+ assert_return(prefix, -EINVAL);
+ assert_return(prefix_len, -EINVAL);
+ assert_return(lifetime_preferred, -EINVAL);
+ assert_return(lifetime_valid, -EINVAL);
+
+ if (!lease->prefix_iter)
+ return -ENOMSG;
+
+ memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
+ sizeof(struct in6_addr));
+ *prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
+ *lifetime_preferred =
+ be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
+ *lifetime_valid =
+ be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
+
+ lease->prefix_iter = lease->prefix_iter->addresses_next;
+
+ return 0;
+}
+
+void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
+ if (lease)
+ lease->prefix_iter = lease->pd.addresses;
+}
+
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int r;
@@ -384,6 +415,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
+ dhcp6_lease_free_ia(&lease->pd);
free(lease->dns);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index b6beadcbbf..f1e7b404b4 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
+#include "sd-id128.h"
#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h"
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
index 1780347cf1..4fb8ed7fb0 100644
--- a/src/systemd/src/libsystemd/sd-event/sd-event.c
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -124,6 +124,7 @@ struct sd_event_source {
uint32_t events;
uint32_t revents;
bool registered:1;
+ bool owned:1;
} io;
struct {
sd_event_time_handler_t callback;
@@ -242,8 +243,14 @@ struct sd_event {
unsigned delays[sizeof(usec_t) * 8];
};
+static thread_local sd_event *default_event = NULL;
+
static void source_disconnect(sd_event_source *s);
+static sd_event *event_resolve(sd_event *e) {
+ return e == SD_EVENT_DEFAULT ? default_event : e;
+}
+
static int pending_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
@@ -452,6 +459,8 @@ _public_ int sd_event_new(sd_event** ret) {
goto fail;
}
+ e->epoll_fd = fd_move_above_stdio(e->epoll_fd);
+
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
e->profile_delays = true;
@@ -690,7 +699,7 @@ static int event_make_signal_data(
return 0;
}
- d->fd = r;
+ d->fd = fd_move_above_stdio(r);
ev.events = EPOLLIN;
ev.data.ptr = d;
@@ -885,6 +894,10 @@ static void source_free(sd_event_source *s) {
assert(s);
source_disconnect(s);
+
+ if (s->type == SOURCE_IO && s->io.owned)
+ safe_close(s->io.fd);
+
free(s->description);
free(s);
}
@@ -969,6 +982,7 @@ _public_ int sd_event_add_io(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(fd >= 0, -EBADF);
assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
assert_return(callback, -EINVAL);
@@ -1035,6 +1049,8 @@ static int event_setup_timer_fd(
if (fd < 0)
return -errno;
+ fd = fd_move_above_stdio(fd);
+
ev.events = EPOLLIN;
ev.data.ptr = d;
@@ -1069,6 +1085,7 @@ _public_ int sd_event_add_time(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(accuracy != (uint64_t) -1, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1151,6 +1168,7 @@ _public_ int sd_event_add_signal(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(SIGNAL_VALID(sig), -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1211,6 +1229,7 @@ _public_ int sd_event_add_child(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(pid > 1, -EINVAL);
assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
assert_return(options != 0, -EINVAL);
@@ -1268,6 +1287,7 @@ _public_ int sd_event_add_defer(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1302,6 +1322,7 @@ _public_ int sd_event_add_post(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1340,6 +1361,7 @@ _public_ int sd_event_add_exit(
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1485,6 +1507,21 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
return 0;
}
+_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+
+ return s->io.owned;
+}
+
+_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
+ assert_return(s, -EINVAL);
+ assert_return(s->type == SOURCE_IO, -EDOM);
+
+ s->io.owned = own;
+ return 0;
+}
+
_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
assert_return(s, -EINVAL);
assert_return(events, -EINVAL);
@@ -2453,6 +2490,7 @@ _public_ int sd_event_prepare(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2510,6 +2548,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
int r, m, i;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
@@ -2616,6 +2655,7 @@ _public_ int sd_event_dispatch(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
@@ -2657,6 +2697,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2701,6 +2742,7 @@ _public_ int sd_event_loop(sd_event *e) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2722,6 +2764,7 @@ finish:
_public_ int sd_event_get_fd(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->epoll_fd;
@@ -2729,6 +2772,7 @@ _public_ int sd_event_get_fd(sd_event *e) {
_public_ int sd_event_get_state(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->state;
@@ -2736,6 +2780,7 @@ _public_ int sd_event_get_state(sd_event *e) {
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(code, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2748,6 +2793,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
_public_ int sd_event_exit(sd_event *e, int code) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2759,6 +2805,7 @@ _public_ int sd_event_exit(sd_event *e, int code) {
_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(usec, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2783,8 +2830,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
}
_public_ int sd_event_default(sd_event **ret) {
-
- static thread_local sd_event *default_event = NULL;
sd_event *e = NULL;
int r;
@@ -2811,6 +2856,7 @@ _public_ int sd_event_default(sd_event **ret) {
#if 0 /* NM_IGNORED */
_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(tid, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2826,6 +2872,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
int r;
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
if (e->watchdog == !!b)
@@ -2876,6 +2923,7 @@ fail:
_public_ int sd_event_get_watchdog(sd_event *e) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
return e->watchdog;
@@ -2883,6 +2931,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
*ret = e->iteration;
diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c
index fa5a64d43b..385a7e02eb 100644
--- a/src/systemd/src/libsystemd/sd-id128/id128-util.c
+++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c
@@ -20,6 +20,7 @@
#include "nm-sd-adapt.h"
+#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 37803c71d8..cadb32a051 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <net/ethernet.h>
+#include <stdbool.h>
#include <sys/types.h>
#include "sd-dhcp6-lease.h"
@@ -64,6 +65,8 @@ enum {
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
+ SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
+ SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
@@ -116,6 +119,8 @@ int sd_dhcp6_client_get_information_request(
int sd_dhcp6_client_set_request_option(
sd_dhcp6_client *client,
uint16_t option);
+int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
+ bool delegation);
int sd_dhcp6_client_get_lease(
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 5807b1836b..22a5f8ce75 100644
--- a/src/systemd/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -36,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
+void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
+ uint8_t *prefix_len,
+ 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_domains(sd_dhcp6_lease *lease, char ***domains);
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
index 9083d5fa9e..ec4b7bcf69 100644
--- a/src/systemd/src/systemd/sd-event.h
+++ b/src/systemd/src/systemd/sd-event.h
@@ -26,6 +26,7 @@
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/types.h>
+#include <time.h>
#include "_sd-common.h"
@@ -40,6 +41,8 @@
_SD_BEGIN_DECLARATIONS;
+#define SD_EVENT_DEFAULT ((sd_event *) 1)
+
typedef struct sd_event sd_event;
typedef struct sd_event_source sd_event_source;
@@ -124,6 +127,8 @@ int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
int sd_event_source_set_enabled(sd_event_source *s, int enabled);
int sd_event_source_get_io_fd(sd_event_source *s);
int sd_event_source_set_io_fd(sd_event_source *s, int fd);
+int sd_event_source_get_io_fd_own(sd_event_source *s);
+int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);