summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/cgroup-util.c41
-rw-r--r--src/basic/cgroup-util.h29
-rw-r--r--src/basic/macro.h27
-rw-r--r--src/basic/ring.c209
-rw-r--r--src/basic/ring.h55
-rw-r--r--src/basic/time-util.h4
-rw-r--r--src/basic/util.h4
-rw-r--r--src/cgls/cgls.c16
-rw-r--r--src/cgtop/cgtop.c57
-rw-r--r--src/core/cgroup.c195
-rw-r--r--src/core/cgroup.h40
-rw-r--r--src/core/dbus-cgroup.c155
-rw-r--r--src/core/dbus-execute.c54
-rw-r--r--src/core/dbus-manager.c32
-rw-r--r--src/core/dbus-unit.c1
-rw-r--r--src/core/execute.c34
-rw-r--r--src/core/kill.c5
-rw-r--r--src/core/kill.h3
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c110
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c21
-rw-r--r--src/core/manager.c41
-rw-r--r--src/core/manager.h11
-rw-r--r--src/core/service.c2
-rw-r--r--src/core/shutdown.c22
-rw-r--r--src/core/transaction.c4
-rw-r--r--src/core/unit.c66
-rw-r--r--src/core/unit.h5
-rw-r--r--src/libsystemd-network/arp-util.c153
-rw-r--r--src/libsystemd-network/arp-util.h (renamed from src/libsystemd-network/ipv4ll-internal.h)14
-rw-r--r--src/libsystemd-network/ipv4ll-network.c91
-rw-r--r--src/libsystemd-network/ipv4ll-packet.c71
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c529
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c641
-rw-r--r--src/libsystemd-network/test-acd.c117
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c129
-rw-r--r--src/libsystemd-network/test-ipv4ll.c82
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h2
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c2
-rw-r--r--src/login/loginctl.c152
-rw-r--r--src/network/networkd-ipv4ll.c9
-rw-r--r--src/network/networkd-link.c43
-rw-r--r--src/network/networkd-network-gperf.gperf169
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/notify/notify.c2
-rw-r--r--src/resolve/resolved-bus.c4
-rw-r--r--src/resolve/resolved-dns-answer.c21
-rw-r--r--src/resolve/resolved-dns-answer.h1
-rw-r--r--src/resolve/resolved-dns-cache.c119
-rw-r--r--src/resolve/resolved-dns-cache.h2
-rw-r--r--src/resolve/resolved-dns-query.c4
-rw-r--r--src/resolve/resolved-dns-query.h2
-rw-r--r--src/resolve/resolved-dns-question.c8
-rw-r--r--src/resolve/resolved-dns-question.h2
-rw-r--r--src/resolve/resolved-dns-rr.c13
-rw-r--r--src/resolve/resolved-dns-rr.h2
-rw-r--r--src/resolve/resolved-dns-transaction.c2
-rw-r--r--src/shared/bus-util.c38
-rw-r--r--src/shared/pty.c633
-rw-r--r--src/shared/pty.h72
-rw-r--r--src/systemctl/systemctl.c38
-rw-r--r--src/systemd/sd-ipv4acd.h55
-rw-r--r--src/systemd/sd-ipv4ll.h2
-rw-r--r--src/test/test-execute.c8
-rw-r--r--src/test/test-pty.c142
-rw-r--r--src/test/test-ring.c130
-rw-r--r--src/timesync/timesyncd.c2
69 files changed, 2365 insertions, 2391 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 6099dc8721..95fc2b9e5d 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -2207,6 +2207,46 @@ bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();
}
+int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
+ uint64_t u;
+ int r;
+
+ if (isempty(s)) {
+ *ret = CGROUP_CPU_SHARES_INVALID;
+ return 0;
+ }
+
+ r = safe_atou64(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
+ return -ERANGE;
+
+ *ret = u;
+ return 0;
+}
+
+int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
+ uint64_t u;
+ int r;
+
+ if (isempty(s)) {
+ *ret = CGROUP_BLKIO_WEIGHT_INVALID;
+ return 0;
+ }
+
+ r = safe_atou64(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
+ return -ERANGE;
+
+ *ret = u;
+ return 0;
+}
+
static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_CPU] = "cpu",
[CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
@@ -2214,6 +2254,7 @@ static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_MEMORY] = "memory",
[CGROUP_CONTROLLER_DEVICES] = "devices",
[CGROUP_CONTROLLER_PIDS] = "pids",
+ [CGROUP_CONTROLLER_NET_CLS] = "net_cls",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 7e1b4f9c72..01359fa7cb 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -36,6 +36,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_MEMORY,
CGROUP_CONTROLLER_DEVICES,
CGROUP_CONTROLLER_PIDS,
+ CGROUP_CONTROLLER_NET_CLS,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -1,
} CGroupController;
@@ -50,9 +51,34 @@ typedef enum CGroupMask {
CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES),
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
+ CGROUP_MASK_NET_CLS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_NET_CLS),
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
+/* Special values for the cpu.shares attribute */
+#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1)
+#define CGROUP_CPU_SHARES_MIN UINT64_C(2)
+#define CGROUP_CPU_SHARES_MAX UINT64_C(262144)
+#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024)
+
+static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) {
+ return
+ x == CGROUP_CPU_SHARES_INVALID ||
+ (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX);
+}
+
+/* Special values for the blkio.weight attribute */
+#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1)
+#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
+#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000)
+#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500)
+
+static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
+ return
+ x == CGROUP_BLKIO_WEIGHT_INVALID ||
+ (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX);
+}
+
/*
* General rules:
*
@@ -161,3 +187,6 @@ bool cg_is_legacy_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
+
+int cg_cpu_shares_parse(const char *s, uint64_t *ret);
+int cg_blkio_weight_parse(const char *s, uint64_t *ret);
diff --git a/src/basic/macro.h b/src/basic/macro.h
index cbc3ca97b8..248f7a86dd 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -123,8 +123,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
-#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-
+#define ELEMENTSOF(x) \
+ __extension__ (__builtin_choose_expr( \
+ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
+ sizeof(x)/sizeof((x)[0]), \
+ (void)0))
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
@@ -213,18 +216,20 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
(__x / __y + !!(__x % __y)); \
})
-#define assert_se(expr) \
+#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
- log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- } while (false) \
+ log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false)
+
+#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while(false)
#else
-#define assert(expr) assert_se(expr)
+#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached(t) \
@@ -249,19 +254,19 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
REENABLE_WARNING
#endif
-#define assert_log(expr) ((_likely_(expr)) \
- ? (true) \
- : (log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
+#define assert_log(expr, message) ((_likely_(expr)) \
+ ? (true) \
+ : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
#define assert_return(expr, r) \
do { \
- if (!assert_log(expr)) \
+ if (!assert_log(expr, #expr)) \
return (r); \
} while (false)
#define assert_return_errno(expr, r, err) \
do { \
- if (!assert_log(expr)) { \
+ if (!assert_log(expr, #expr)) { \
errno = err; \
return (r); \
} \
diff --git a/src/basic/ring.c b/src/basic/ring.c
deleted file mode 100644
index 6814918464..0000000000
--- a/src/basic/ring.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include "macro.h"
-#include "ring.h"
-
-#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
-
-void ring_flush(Ring *r) {
- assert(r);
-
- r->start = 0;
- r->used = 0;
-}
-
-void ring_clear(Ring *r) {
- assert(r);
-
- free(r->buf);
- zero(*r);
-}
-
-/*
- * Get data pointers for current ring-buffer data. @vec must be an array of 2
- * iovec objects. They are filled according to the data available in the
- * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
- * that were filled (0 meaning buffer is empty).
- *
- * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
- * struct iovec {
- * void *iov_base;
- * size_t iov_len;
- * };
- */
-size_t ring_peek(Ring *r, struct iovec *vec) {
- assert(r);
-
- if (r->used == 0) {
- return 0;
- } else if (r->start + r->used <= r->size) {
- if (vec) {
- vec[0].iov_base = &r->buf[r->start];
- vec[0].iov_len = r->used;
- }
- return 1;
- } else {
- if (vec) {
- vec[0].iov_base = &r->buf[r->start];
- vec[0].iov_len = r->size - r->start;
- vec[1].iov_base = r->buf;
- vec[1].iov_len = r->used - (r->size - r->start);
- }
- return 2;
- }
-}
-
-/*
- * Copy data from the ring buffer into the linear external buffer @buf. Copy
- * at most @size bytes. If the ring buffer size is smaller, copy less bytes and
- * return the number of bytes copied.
- */
-size_t ring_copy(Ring *r, void *buf, size_t size) {
- size_t l;
-
- assert(r);
- assert(buf);
-
- if (size > r->used)
- size = r->used;
-
- if (size > 0) {
- l = r->size - r->start;
- if (size <= l) {
- memcpy(buf, &r->buf[r->start], size);
- } else {
- memcpy(buf, &r->buf[r->start], l);
- memcpy((uint8_t*)buf + l, r->buf, size - l);
- }
- }
-
- return size;
-}
-
-/*
- * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
- * ring operations will behave incorrectly.
- */
-static int ring_resize(Ring *r, size_t nsize) {
- uint8_t *buf;
- size_t l;
-
- assert(r);
- assert(nsize > 0);
-
- buf = malloc(nsize);
- if (!buf)
- return -ENOMEM;
-
- if (r->used > 0) {
- l = r->size - r->start;
- if (r->used <= l) {
- memcpy(buf, &r->buf[r->start], r->used);
- } else {
- memcpy(buf, &r->buf[r->start], l);
- memcpy(&buf[l], r->buf, r->used - l);
- }
- }
-
- free(r->buf);
- r->buf = buf;
- r->size = nsize;
- r->start = 0;
-
- return 0;
-}
-
-/*
- * Resize ring-buffer to provide enough room for @add bytes of new data. This
- * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
- * success.
- */
-static int ring_grow(Ring *r, size_t add) {
- size_t need;
-
- assert(r);
-
- if (r->size - r->used >= add)
- return 0;
-
- need = r->used + add;
- if (need <= r->used)
- return -ENOMEM;
- else if (need < 4096)
- need = 4096;
-
- need = ALIGN_POWER2(need);
- if (need == 0)
- return -ENOMEM;
-
- return ring_resize(r, need);
-}
-
-/*
- * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
- * is too small. -ENOMEM is returned on OOM, 0 on success.
- */
-int ring_push(Ring *r, const void *u8, size_t size) {
- int err;
- size_t pos, l;
-
- assert(r);
- assert(u8);
-
- if (size == 0)
- return 0;
-
- err = ring_grow(r, size);
- if (err < 0)
- return err;
-
- pos = RING_MASK(r, r->start + r->used);
- l = r->size - pos;
- if (l >= size) {
- memcpy(&r->buf[pos], u8, size);
- } else {
- memcpy(&r->buf[pos], u8, l);
- memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
- }
-
- r->used += size;
-
- return 0;
-}
-
-/*
- * Remove @len bytes from the start of the ring-buffer. Note that we protect
- * against overflows so removing more bytes than available is safe.
- */
-void ring_pull(Ring *r, size_t size) {
- assert(r);
-
- if (size > r->used)
- size = r->used;
-
- r->start = RING_MASK(r, r->start + size);
- r->used -= size;
-}
diff --git a/src/basic/ring.h b/src/basic/ring.h
deleted file mode 100644
index dbd6296384..0000000000
--- a/src/basic/ring.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-
-typedef struct Ring Ring;
-
-struct Ring {
- uint8_t *buf; /* buffer or NULL */
- size_t size; /* actual size of @buf */
- size_t start; /* start position of ring */
- size_t used; /* number of actually used bytes */
-};
-
-/* flush buffer so it is empty again */
-void ring_flush(Ring *r);
-
-/* flush buffer, free allocated data and reset to initial state */
-void ring_clear(Ring *r);
-
-/* get pointers to buffer data and their length */
-size_t ring_peek(Ring *r, struct iovec *vec);
-
-/* copy data into external linear buffer */
-size_t ring_copy(Ring *r, void *buf, size_t size);
-
-/* push data to the end of the buffer */
-int ring_push(Ring *r, const void *u8, size_t size);
-
-/* pull data from the front of the buffer */
-void ring_pull(Ring *r, size_t size);
-
-/* return size of occupied buffer in bytes */
-static inline size_t ring_get_size(Ring *r) {
- return r->used;
-}
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index de881e8fe1..1af01541fc 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -112,6 +112,8 @@ bool timezone_is_valid(const char *name);
clockid_t clock_boottime_or_monotonic(void);
-#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
+#define xstrftime(buf, fmt, tm) \
+ assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \
+ "xstrftime: " #buf "[] must be big enough")
int get_timezone(char **timezone);
diff --git a/src/basic/util.h b/src/basic/util.h
index c7dff9a86d..8abaa740b2 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -374,7 +374,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
+#define xsprintf(buf, fmt, ...) \
+ assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \
+ "xsprintf: " #buf "[] must be big enough")
int files_same(const char *filea, const char *fileb);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 4fb642e7b3..ec4215f741 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -165,6 +165,13 @@ static int get_cgroup_root(char **ret) {
return 0;
}
+static void show_cg_info(const char *controller, const char *path) {
+ if (cg_unified() <= 0)
+ printf("Controller %s; ", controller);
+ printf("Control group %s:\n", isempty(path) ? "/" : path);
+ fflush(stdout);
+}
+
int main(int argc, char *argv[]) {
int r, output_flags;
@@ -225,11 +232,7 @@ int main(int argc, char *argv[]) {
} else
path = root;
- if (cg_unified() > 0)
- printf("Control group %s:\n", path);
- else
- printf("Controller %s; control group %s:\n", controller, path);
- fflush(stdout);
+ show_cg_info(controller, path);
q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags);
}
@@ -266,8 +269,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- printf("Control group %s:\n", isempty(root) ? "/" : root);
- fflush(stdout);
+ show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags);
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 4786a155da..1307a34ab7 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -36,6 +36,10 @@
#include "cgroup-util.h"
#include "build.h"
#include "fileio.h"
+#include "sd-bus.h"
+#include "bus-util.h"
+#include "bus-error.h"
+#include "unit-name.h"
typedef struct Group {
char *path;
@@ -65,6 +69,7 @@ static unsigned arg_iterations = (unsigned) -1;
static bool arg_batch = false;
static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
+static char* arg_machine = NULL;
enum {
COUNT_PIDS,
@@ -645,6 +650,7 @@ static void help(void) {
" -n --iterations=N Run for N iterations before exiting\n"
" -b --batch Run in batch mode, accepting no input\n"
" --depth=DEPTH Maximum traversal depth (default: %u)\n"
+ " -M --machine= Show container\n"
, program_invocation_short_name, arg_depth);
}
@@ -669,6 +675,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "cpu", optional_argument, NULL, ARG_CPU_TYPE },
{ "order", required_argument, NULL, ARG_ORDER },
{ "recursive", required_argument, NULL, ARG_RECURSIVE },
+ { "machine", required_argument, NULL, 'M' },
{}
};
@@ -678,7 +685,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0)
switch (c) {
@@ -797,6 +804,10 @@ static int parse_argv(int argc, char *argv[]) {
recursive_unset = r == 0;
break;
+ case 'M':
+ arg_machine = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -826,6 +837,48 @@ static const char* counting_what(void) {
return "userspace processes (excl. kernel)";
}
+static int get_cgroup_root(char **ret) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *unit = NULL, *path = NULL;
+ const char *m;
+ int r;
+
+ if (!arg_machine) {
+ r = cg_get_root_path(ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get root control group path: %m");
+
+ return 0;
+ }
+
+ m = strjoina("/run/systemd/machines/", arg_machine);
+ r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load machine data: %m");
+
+ path = unit_dbus_path_from_name(unit);
+ if (!path)
+ return log_oom();
+
+ r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ unit_dbus_interface_from_name(unit),
+ "ControlGroup",
+ &error,
+ ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
int r;
Hashmap *a = NULL, *b = NULL;
@@ -850,7 +903,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = cg_get_root_path(&root);
+ r = get_cgroup_root(&root);
if (r < 0) {
log_error_errno(r, "Failed to get root control group path: %m");
goto finish;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 48000d4e6e..0c790c33da 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -37,14 +37,18 @@ void cgroup_context_init(CGroupContext *c) {
/* Initialize everything to the kernel defaults, assuming the
* structure is preinitialized to 0 */
- c->cpu_shares = (unsigned long) -1;
- c->startup_cpu_shares = (unsigned long) -1;
+ c->cpu_shares = CGROUP_CPU_SHARES_INVALID;
+ c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID;
+ c->cpu_quota_per_sec_usec = USEC_INFINITY;
+
c->memory_limit = (uint64_t) -1;
- c->blockio_weight = (unsigned long) -1;
- c->startup_blockio_weight = (unsigned long) -1;
+
+ c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+ c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+
c->tasks_max = (uint64_t) -1;
- c->cpu_quota_per_sec_usec = USEC_INFINITY;
+ c->netclass_type = CGROUP_NETCLASS_TYPE_NONE;
}
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
@@ -102,11 +106,12 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
"%sCPUAccounting=%s\n"
"%sBlockIOAccounting=%s\n"
"%sMemoryAccounting=%s\n"
- "%sCPUShares=%lu\n"
- "%sStartupCPUShares=%lu\n"
+ "%sTasksAccounting=%s\n"
+ "%sCPUShares=%" PRIu64 "\n"
+ "%sStartupCPUShares=%" PRIu64 "\n"
"%sCPUQuotaPerSecSec=%s\n"
- "%sBlockIOWeight=%lu\n"
- "%sStartupBlockIOWeight=%lu\n"
+ "%sBlockIOWeight=%" PRIu64 "\n"
+ "%sStartupBlockIOWeight=%" PRIu64 "\n"
"%sMemoryLimit=%" PRIu64 "\n"
"%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
@@ -114,6 +119,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->cpu_accounting),
prefix, yes_no(c->blockio_accounting),
prefix, yes_no(c->memory_accounting),
+ prefix, yes_no(c->tasks_accounting),
prefix, c->cpu_shares,
prefix, c->startup_cpu_shares,
prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
@@ -133,7 +139,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
LIST_FOREACH(device_weights, w, c->blockio_device_weights)
fprintf(f,
- "%sBlockIODeviceWeight=%s %lu",
+ "%sBlockIODeviceWeight=%s %" PRIu64,
prefix,
w->path,
w->weight);
@@ -287,7 +293,7 @@ fail:
return -errno;
}
-void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) {
+void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass, ManagerState state) {
bool is_root;
int r;
@@ -309,11 +315,11 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M
* and missing cgroups, i.e. EROFS and ENOENT. */
if ((mask & CGROUP_MASK_CPU) && !is_root) {
- char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1];
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
- sprintf(buf, "%lu\n",
- IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares :
- c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024);
+ sprintf(buf, "%" PRIu64 "\n",
+ IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares :
+ c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT);
r = cg_set_attribute("cpu", path, "cpu.shares", buf);
if (r < 0)
log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
@@ -336,15 +342,15 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M
}
if (mask & CGROUP_MASK_BLKIO) {
- char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
- DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
- DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t)+1,
+ DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
CGroupBlockIODeviceWeight *w;
CGroupBlockIODeviceBandwidth *b;
if (!is_root) {
- sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight :
- c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000);
+ sprintf(buf, "%" PRIu64 "\n",
+ IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->startup_blockio_weight :
+ c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT);
r = cg_set_attribute("blkio", path, "blkio.weight", buf);
if (r < 0)
log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
@@ -358,7 +364,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M
if (r < 0)
continue;
- sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight);
+ sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight);
r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
if (r < 0)
log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
@@ -485,6 +491,17 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M
log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set pids.max on %s: %m", path);
}
+
+ if (mask & CGROUP_MASK_NET_CLS) {
+ char buf[DECIMAL_STR_MAX(uint32_t)];
+
+ sprintf(buf, "%" PRIu32, netclass);
+
+ r = cg_set_attribute("net_cls", path, "net_cls.classid", buf);
+ if (r < 0)
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set net_cls.classid on %s: %m", path);
+ }
}
CGroupMask cgroup_context_get_mask(CGroupContext *c) {
@@ -493,14 +510,14 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
/* Figure out which controllers we need */
if (c->cpu_accounting ||
- c->cpu_shares != (unsigned long) -1 ||
- c->startup_cpu_shares != (unsigned long) -1 ||
+ c->cpu_shares != CGROUP_CPU_SHARES_INVALID ||
+ c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ||
c->cpu_quota_per_sec_usec != USEC_INFINITY)
mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU;
if (c->blockio_accounting ||
- c->blockio_weight != (unsigned long) -1 ||
- c->startup_blockio_weight != (unsigned long) -1 ||
+ c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ||
+ c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ||
c->blockio_device_weights ||
c->blockio_device_bandwidths)
mask |= CGROUP_MASK_BLKIO;
@@ -517,6 +534,9 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
c->tasks_max != (uint64_t) -1)
mask |= CGROUP_MASK_PIDS;
+ if (c->netclass_type != CGROUP_NETCLASS_TYPE_NONE)
+ mask |= CGROUP_MASK_NET_CLS;
+
return mask;
}
@@ -884,6 +904,103 @@ static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) {
return u->cgroup_realized && u->cgroup_realized_mask == target_mask;
}
+static int unit_find_free_netclass_cgroup(Unit *u, uint32_t *ret) {
+
+ uint32_t start, i;
+ Manager *m;
+
+ assert(u);
+
+ m = u->manager;
+
+ i = start = m->cgroup_netclass_registry_last;
+
+ do {
+ i++;
+
+ if (!hashmap_get(m->cgroup_netclass_registry, UINT_TO_PTR(i))) {
+ m->cgroup_netclass_registry_last = i;
+ *ret = i;
+ return 0;
+ }
+
+ if (i == UINT32_MAX)
+ i = CGROUP_NETCLASS_FIXED_MAX;
+
+ } while (i != start);
+
+ return -ENOBUFS;
+}
+
+int unit_add_to_netclass_cgroup(Unit *u) {
+
+ CGroupContext *cc;
+ Unit *first;
+ void *key;
+ int r;
+
+ assert(u);
+
+ cc = unit_get_cgroup_context(u);
+ if (!cc)
+ return 0;
+
+ switch (cc->netclass_type) {
+ case CGROUP_NETCLASS_TYPE_NONE:
+ return 0;
+
+ case CGROUP_NETCLASS_TYPE_FIXED:
+ u->cgroup_netclass_id = cc->netclass_id;
+ break;
+
+ case CGROUP_NETCLASS_TYPE_AUTO:
+ /* Allocate a new ID in case it was requested and not done yet */
+ if (u->cgroup_netclass_id == 0) {
+ r = unit_find_free_netclass_cgroup(u, &u->cgroup_netclass_id);
+ if (r < 0)
+ return r;
+
+ log_debug("Dynamically assigned netclass cgroup id %" PRIu32 " to %s", u->cgroup_netclass_id, u->id);
+ }
+
+ break;
+ }
+
+ r = hashmap_ensure_allocated(&u->manager->cgroup_netclass_registry, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ key = UINT32_TO_PTR(u->cgroup_netclass_id);
+ first = hashmap_get(u->manager->cgroup_netclass_registry, key);
+
+ if (first) {
+ LIST_PREPEND(cgroup_netclass, first, u);
+ return hashmap_replace(u->manager->cgroup_netclass_registry, key, u);
+ }
+
+ return hashmap_put(u->manager->cgroup_netclass_registry, key, u);
+}
+
+int unit_remove_from_netclass_cgroup(Unit *u) {
+
+ Unit *head;
+ void *key;
+
+ assert(u);
+
+ key = UINT32_TO_PTR(u->cgroup_netclass_id);
+
+ LIST_FIND_HEAD(cgroup_netclass, u, head);
+ LIST_REMOVE(cgroup_netclass, head, u);
+
+ if (head)
+ return hashmap_replace(u->manager->cgroup_netclass_registry, key, head);
+
+ hashmap_remove(u->manager->cgroup_netclass_registry, key);
+
+ return 0;
+}
+
/* Check if necessary controllers and attributes for a unit are in place.
*
* If so, do nothing.
@@ -919,7 +1036,7 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
return r;
/* Finally, apply the necessary attributes. */
- cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state);
+ cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, u->cgroup_netclass_id, state);
return 0;
}
@@ -1577,6 +1694,32 @@ bool unit_cgroup_delegate(Unit *u) {
return c->delegate;
}
+void unit_invalidate_cgroup(Unit *u, CGroupMask m) {
+ assert(u);
+
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return;
+
+ if (m == 0)
+ return;
+
+ if ((u->cgroup_realized_mask & m) == 0)
+ return;
+
+ u->cgroup_realized_mask &= ~m;
+ unit_add_to_cgroup_queue(u);
+}
+
+void manager_invalidate_startup_units(Manager *m) {
+ Iterator i;
+ Unit *u;
+
+ assert(m);
+
+ SET_FOREACH(u, m->startup_units, i)
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_BLKIO);
+}
+
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
[CGROUP_AUTO] = "auto",
[CGROUP_CLOSED] = "closed",
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 3ba09d56a4..457544b49f 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -26,6 +26,11 @@
#include "list.h"
#include "time-util.h"
+/* Maximum value for fixed (manual) net class ID assignment,
+ * and also the value at which the range of automatic assignments starts
+ */
+#define CGROUP_NETCLASS_FIXED_MAX UINT32_C(65535)
+
typedef struct CGroupContext CGroupContext;
typedef struct CGroupDeviceAllow CGroupDeviceAllow;
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
@@ -47,6 +52,17 @@ typedef enum CGroupDevicePolicy {
_CGROUP_DEVICE_POLICY_INVALID = -1
} CGroupDevicePolicy;
+typedef enum CGroupNetClassType {
+ /* Default - do not assign a net class */
+ CGROUP_NETCLASS_TYPE_NONE,
+
+ /* Automatically assign a net class */
+ CGROUP_NETCLASS_TYPE_AUTO,
+
+ /* Assign the net class that was provided by the user */
+ CGROUP_NETCLASS_TYPE_FIXED,
+} CGroupNetClassType;
+
struct CGroupDeviceAllow {
LIST_FIELDS(CGroupDeviceAllow, device_allow);
char *path;
@@ -58,7 +74,7 @@ struct CGroupDeviceAllow {
struct CGroupBlockIODeviceWeight {
LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights);
char *path;
- unsigned long weight;
+ uint64_t weight;
};
struct CGroupBlockIODeviceBandwidth {
@@ -74,12 +90,12 @@ struct CGroupContext {
bool memory_accounting;
bool tasks_accounting;
- unsigned long cpu_shares;
- unsigned long startup_cpu_shares;
+ uint64_t cpu_shares;
+ uint64_t startup_cpu_shares;
usec_t cpu_quota_per_sec_usec;
- unsigned long blockio_weight;
- unsigned long startup_blockio_weight;
+ uint64_t blockio_weight;
+ uint64_t startup_blockio_weight;
LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights);
LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths);
@@ -88,9 +104,12 @@ struct CGroupContext {
CGroupDevicePolicy device_policy;
LIST_HEAD(CGroupDeviceAllow, device_allow);
- bool delegate;
+ CGroupNetClassType netclass_type;
+ uint32_t netclass_id;
uint64_t tasks_max;
+
+ bool delegate;
};
#include "unit.h"
@@ -99,7 +118,7 @@ struct CGroupContext {
void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
-void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state);
+void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass_id, ManagerState state);
CGroupMask cgroup_context_get_mask(CGroupContext *c);
@@ -127,6 +146,9 @@ int unit_watch_cgroup(Unit *u);
int unit_attach_pids_to_cgroup(Unit *u);
+int unit_add_to_netclass_cgroup(Unit *u);
+int unit_remove_from_netclass_cgroup(Unit *u);
+
int manager_setup_cgroup(Manager *m);
void manager_shutdown_cgroup(Manager *m, bool delete);
@@ -149,5 +171,9 @@ bool unit_cgroup_delegate(Unit *u);
int unit_notify_cgroup_empty(Unit *u);
int manager_notify_cgroup_empty(Manager *m, const char *group);
+void unit_invalidate_cgroup(Unit *u, CGroupMask m);
+
+void manager_invalidate_startup_units(Manager *m);
+
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 94de92c3dd..f334dc928d 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -133,34 +133,16 @@ static int property_get_device_allow(
return sd_bus_message_close_container(reply);
}
-static int property_get_ulong_as_u64(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- unsigned long *ul = userdata;
-
- assert(bus);
- assert(reply);
- assert(ul);
-
- return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
-}
-
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
- SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
- SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
+ SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0),
+ SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0),
SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
- SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
- SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
+ SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0),
+ SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0),
SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
@@ -230,56 +212,52 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU);
unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
}
return 1;
} else if (streq(name, "CPUShares")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t shares;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &shares);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul <= 0 || (uint64_t) ul != u64)
- return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
- }
+ if (!CGROUP_CPU_SHARES_IS_OK(shares))
+ return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
if (mode != UNIT_CHECK) {
- c->cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
- unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
+ c->cpu_shares = shares;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (shares == CGROUP_CPU_SHARES_INVALID)
+ unit_write_drop_in_private(u, mode, name, "CPUShares=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares);
}
return 1;
} else if (streq(name, "StartupCPUShares")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t shares;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &shares);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul <= 0 || (uint64_t) ul != u64)
- return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
- }
+ if (!CGROUP_CPU_SHARES_IS_OK(shares))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
if (mode != UNIT_CHECK) {
- c->startup_cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
- unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
+ c->startup_cpu_shares = shares;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (shares == CGROUP_CPU_SHARES_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupCPUShares=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares);
}
return 1;
@@ -296,7 +274,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_quota_per_sec_usec = u64;
- u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
}
@@ -311,56 +289,52 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->blockio_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
}
return 1;
} else if (streq(name, "BlockIOWeight")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t weight;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &weight);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul < 10 || ul > 1000)
- return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
- }
+ if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
if (mode != UNIT_CHECK) {
- c->blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
- unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
+ c->blockio_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
+
+ if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "BlockIOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight);
}
return 1;
} else if (streq(name, "StartupBlockIOWeight")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t weight;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &weight);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul < 10 || ul > 1000)
- return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
- }
+ if (CGROUP_BLKIO_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
if (mode != UNIT_CHECK) {
- c->startup_blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
- unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
+ c->startup_blockio_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
+
+ if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight);
}
return 1;
@@ -429,7 +403,7 @@ int bus_cgroup_set_property(
cgroup_context_free_blockio_device_bandwidth(c, a);
}
- u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
f = open_memstream(&buf, &size);
if (!f)
@@ -455,17 +429,16 @@ int bus_cgroup_set_property(
} else if (streq(name, "BlockIODeviceWeight")) {
const char *path;
- uint64_t u64;
+ uint64_t weight;
unsigned n = 0;
r = sd_bus_message_enter_container(message, 'a', "(st)");
if (r < 0)
return r;
- while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
- unsigned long ul = u64;
+ while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
- if (ul < 10 || ul > 1000)
+ if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
if (mode != UNIT_CHECK) {
@@ -491,7 +464,7 @@ int bus_cgroup_set_property(
LIST_PREPEND(device_weights,c->blockio_device_weights, a);
}
- a->weight = ul;
+ a->weight = weight;
}
n++;
@@ -512,7 +485,7 @@ int bus_cgroup_set_property(
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
}
- u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
f = open_memstream(&buf, &size);
if (!f)
@@ -520,7 +493,7 @@ int bus_cgroup_set_property(
fputs("BlockIODeviceWeight=\n", f);
LIST_FOREACH(device_weights, a, c->blockio_device_weights)
- fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
+ fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
fflush(f);
unit_write_drop_in_private(u, mode, name, buf);
@@ -537,7 +510,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
}
@@ -552,7 +525,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_limit = limit;
- u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
if (limit == (uint64_t) -1)
unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity");
@@ -578,7 +551,7 @@ int bus_cgroup_set_property(
char *buf;
c->device_policy = p;
- u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES;
+ unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
buf = strjoina("DevicePolicy=", policy);
unit_write_drop_in_private(u, mode, name, buf);
@@ -657,7 +630,7 @@ int bus_cgroup_set_property(
cgroup_context_free_device_allow(c, c->device_allow);
}
- u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES;
+ unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
f = open_memstream(&buf, &size);
if (!f)
@@ -682,7 +655,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->tasks_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
+ unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no");
}
@@ -697,7 +670,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->tasks_max = limit;
- u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS;
+ unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
if (limit == (uint64_t) -1)
unit_write_drop_in_private(u, mode, name, "TasksMax=infinity");
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index ed55fcfca2..fd13c6d019 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -933,7 +933,10 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (streq(name, "IgnoreSIGPIPE")) {
+ } else if (STR_IN_SET(name,
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork",
+ "NoNewPrivileges")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -941,39 +944,22 @@ int bus_exec_context_set_transient_property(
return r;
if (mode != UNIT_CHECK) {
- c->ignore_sigpipe = b;
-
- unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "TTYVHangup")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (mode != UNIT_CHECK) {
- c->tty_vhangup = b;
-
- unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b));
- }
-
- return 1;
-
- } else if (streq(name, "TTYReset")) {
- int b;
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0)
- return r;
-
- if (mode != UNIT_CHECK) {
- c->tty_reset = b;
-
- unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b));
+ if (streq(name, "IgnoreSIGPIPE"))
+ c->ignore_sigpipe = b;
+ else if (streq(name, "TTYVHangup"))
+ c->tty_vhangup = b;
+ else if (streq(name, "TTYReset"))
+ c->tty_reset = b;
+ else if (streq(name, "PrivateTmp"))
+ c->private_tmp = b;
+ else if (streq(name, "PrivateDevices"))
+ c->private_devices = b;
+ else if (streq(name, "PrivateNetwork"))
+ c->private_network = b;
+ else if (streq(name, "NoNewPrivileges"))
+ c->no_new_privileges = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, yes_no(b));
}
return 1;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 4e5d67fc19..561b6f8bfa 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1201,8 +1201,10 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
+ /* Exit() (in contrast to SetExitCode()) is actually allowed even if
+ * we are running on the host. It will fall back on reboot() in
+ * systemd-shutdown if it cannot do the exit() because it isn't a
+ * container. */
m->exit_code = MANAGER_EXIT;
@@ -1450,6 +1452,30 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
return sd_bus_reply_method_return(message, NULL);
}
+static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ uint8_t code;
+ Manager *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = mac_selinux_access_check(message, "exit", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(message, 'y', &code);
+ if (r < 0)
+ return r;
+
+ if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
+
+ m->return_value = code;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
@@ -1933,6 +1959,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
+ SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0),
SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1986,6 +2013,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("UnitNew", "so", 0),
SD_BUS_SIGNAL("UnitRemoved", "so", 0),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index f7e9795928..cd88a87340 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -679,6 +679,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NetClass", "u", bus_property_get_unsigned, offsetof(Unit, cgroup_netclass_id), 0),
SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/execute.c b/src/core/execute.c
index d1acda6682..6e14848cd4 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -629,15 +629,6 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
* we avoid NSS lookups for gid=0. */
if (context->group || username) {
-
- if (context->group) {
- const char *g = context->group;
-
- r = get_group_creds(&g, &gid);
- if (r < 0)
- return r;
- }
-
/* First step, initialize groups from /etc/groups */
if (username && gid != 0) {
if (initgroups(username, gid) < 0)
@@ -1160,8 +1151,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
- safe_close(idle_pipe[1]);
- safe_close(idle_pipe[2]);
+ idle_pipe[1] = safe_close(idle_pipe[1]);
+ idle_pipe[2] = safe_close(idle_pipe[2]);
if (idle_pipe[0] >= 0) {
int r;
@@ -1169,18 +1160,20 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
+ ssize_t n;
+
/* Signal systemd that we are bored and want to continue. */
- r = write(idle_pipe[3], "x", 1);
- if (r > 0)
+ n = write(idle_pipe[3], "x", 1);
+ if (n > 0)
/* Wait for systemd to react to the signal above. */
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
- safe_close(idle_pipe[0]);
+ idle_pipe[0] = safe_close(idle_pipe[0]);
}
- safe_close(idle_pipe[3]);
+ idle_pipe[3] = safe_close(idle_pipe[3]);
}
static int build_environment(
@@ -1412,6 +1405,17 @@ static int exec_child(
}
}
+ if (context->group) {
+ const char *g = context->group;
+
+ r = get_group_creds(&g, &gid);
+ if (r < 0) {
+ *exit_status = EXIT_GROUP;
+ return r;
+ }
+ }
+
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
diff --git a/src/core/kill.c b/src/core/kill.c
index 2de71c6bf9..bddfa4460f 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -60,7 +60,10 @@ DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
static const char* const kill_who_table[_KILL_WHO_MAX] = {
[KILL_MAIN] = "main",
[KILL_CONTROL] = "control",
- [KILL_ALL] = "all"
+ [KILL_ALL] = "all",
+ [KILL_MAIN_FAIL] = "main-fail",
+ [KILL_CONTROL_FAIL] = "control-fail",
+ [KILL_ALL_FAIL] = "all-fail"
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
diff --git a/src/core/kill.h b/src/core/kill.h
index d5f125fa41..5d97abb104 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -50,6 +50,9 @@ typedef enum KillWho {
KILL_MAIN,
KILL_CONTROL,
KILL_ALL,
+ KILL_MAIN_FAIL,
+ KILL_CONTROL_FAIL,
+ KILL_ALL_FAIL,
_KILL_WHO_MAX,
_KILL_WHO_INVALID = -1
} KillWho;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index e056fd863c..ec744214c1 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -126,7 +126,8 @@ $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0,
$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context)
-$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)'
+$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)
+$1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index f7a8539910..fcf863c5c7 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2605,26 +2605,19 @@ int config_parse_cpu_shares(
void *data,
void *userdata) {
- unsigned long *shares = data, lu;
+ uint64_t *shares = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- if (isempty(rvalue)) {
- *shares = (unsigned long) -1;
- return 0;
- }
-
- r = safe_atolu(rvalue, &lu);
- if (r < 0 || lu <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU shares '%s' invalid. Ignoring.", rvalue);
+ r = cg_cpu_shares_parse(rvalue, shares);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
return 0;
}
- *shares = lu;
return 0;
}
@@ -2805,26 +2798,19 @@ int config_parse_blockio_weight(
void *data,
void *userdata) {
- unsigned long *weight = data, lu;
+ uint64_t *weight = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- if (isempty(rvalue)) {
- *weight = (unsigned long) -1;
- return 0;
- }
-
- r = safe_atolu(rvalue, &lu);
- if (r < 0 || lu < 10 || lu > 1000) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO weight '%s' invalid. Ignoring.", rvalue);
+ r = cg_blkio_weight_parse(rvalue, weight);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
return 0;
}
- *weight = lu;
return 0;
}
@@ -2843,8 +2829,8 @@ int config_parse_blockio_device_weight(
_cleanup_free_ char *path = NULL;
CGroupBlockIODeviceWeight *w;
CGroupContext *c = data;
- unsigned long lu;
const char *weight;
+ uint64_t u;
size_t n;
int r;
@@ -2861,9 +2847,10 @@ int config_parse_blockio_device_weight(
n = strcspn(rvalue, WHITESPACE);
weight = rvalue + n;
- if (!*weight) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Expected block device and device weight. Ignoring.");
+ weight += strspn(weight, WHITESPACE);
+
+ if (isempty(weight)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Expected block device and device weight. Ignoring.");
return 0;
}
@@ -2872,19 +2859,18 @@ int config_parse_blockio_device_weight(
return log_oom();
if (!path_startswith(path, "/dev")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
- weight += strspn(weight, WHITESPACE);
- r = safe_atolu(weight, &lu);
- if (r < 0 || lu < 10 || lu > 1000) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO weight '%s' invalid. Ignoring.", rvalue);
+ r = cg_blkio_weight_parse(weight, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
return 0;
}
+ assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
+
w = new0(CGroupBlockIODeviceWeight, 1);
if (!w)
return log_oom();
@@ -2892,7 +2878,7 @@ int config_parse_blockio_device_weight(
w->path = path;
path = NULL;
- w->weight = lu;
+ w->weight = u;
LIST_PREPEND(device_weights, c->blockio_device_weights, w);
return 0;
@@ -2976,6 +2962,48 @@ int config_parse_blockio_bandwidth(
return 0;
}
+int config_parse_netclass(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CGroupContext *c = data;
+ unsigned v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (streq(rvalue, "auto")) {
+ c->netclass_type = CGROUP_NETCLASS_TYPE_AUTO;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Netclass '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ if (v > CGROUP_NETCLASS_FIXED_MAX)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX);
+
+ c->netclass_id = v;
+ c->netclass_type = CGROUP_NETCLASS_TYPE_FIXED;
+
+ return 0;
+}
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
int config_parse_job_mode_isolate(
@@ -3021,6 +3049,7 @@ int config_parse_runtime_directory(
void *userdata) {
char***rt = data;
+ Unit *u = userdata;
const char *word, *state;
size_t l;
int r;
@@ -3037,12 +3066,19 @@ int config_parse_runtime_directory(
}
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
+ _cleanup_free_ char *t = NULL, *n = NULL;
- n = strndup(word, l);
- if (!n)
+ t = strndup(word, l);
+ if (!t)
return log_oom();
+ r = unit_name_printf(u, t, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ continue;
+ }
+
if (!filename_is_valid(n)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Runtime directory is not valid, ignoring assignment: %s", rvalue);
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 638b343a6e..5d0a09249f 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -90,6 +90,7 @@ int config_parse_device_allow(const char *unit, const char *filename, unsigned l
int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_netclass(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/core/main.c b/src/core/main.c
index 200fe740da..9b59648279 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1254,6 +1254,7 @@ int main(int argc, char *argv[]) {
char *switch_root_dir = NULL, *switch_root_init = NULL;
struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0);
const char *error_message = NULL;
+ uint8_t shutdown_exit_code = 0;
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
@@ -1764,11 +1765,6 @@ int main(int argc, char *argv[]) {
switch (m->exit_code) {
- case MANAGER_EXIT:
- retval = EXIT_SUCCESS;
- log_debug("Exit.");
- goto finish;
-
case MANAGER_RELOAD:
log_info("Reloading.");
@@ -1810,11 +1806,13 @@ int main(int argc, char *argv[]) {
log_notice("Switching root.");
goto finish;
+ case MANAGER_EXIT:
case MANAGER_REBOOT:
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
+ [MANAGER_EXIT] = "exit",
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
@@ -1836,8 +1834,10 @@ int main(int argc, char *argv[]) {
finish:
pager_close();
- if (m)
+ if (m) {
arg_shutdown_watchdog = m->shutdown_watchdog;
+ shutdown_exit_code = m->return_value;
+ }
m = manager_free(m);
for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
@@ -1978,7 +1978,8 @@ finish:
if (shutdown_verb) {
char log_level[DECIMAL_STR_MAX(int) + 1];
- const char* command_line[9] = {
+ char exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
+ const char* command_line[11] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
"--log-level", log_level,
@@ -2015,6 +2016,12 @@ finish:
if (log_get_show_location())
command_line[pos++] = "--log-location";
+ if (streq(shutdown_verb, "exit")) {
+ command_line[pos++] = "--exit-code";
+ command_line[pos++] = exit_code;
+ xsprintf(exit_code, "%d", shutdown_exit_code);
+ }
+
assert(pos < ELEMENTSOF(command_line));
if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
diff --git a/src/core/manager.c b/src/core/manager.c
index d918007bb8..4e672a8c48 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -317,6 +317,8 @@ static int manager_watch_idle_pipe(Manager *m) {
static void manager_close_idle_pipe(Manager *m) {
assert(m);
+ m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
+
safe_close_pair(m->idle_pipe);
safe_close_pair(m->idle_pipe + 2);
}
@@ -577,6 +579,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
m->have_ask_password = -EINVAL; /* we don't know */
m->first_boot = -1;
+ m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX;
+
m->test_run = test_run;
/* Reboot immediately if the user hits C-A-D more often than 7x per 2s */
@@ -602,14 +606,6 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
if (r < 0)
goto fail;
- r = set_ensure_allocated(&m->startup_units, NULL);
- if (r < 0)
- goto fail;
-
- r = set_ensure_allocated(&m->failed_units, NULL);
- if (r < 0)
- goto fail;
-
r = sd_event_default(&m->event);
if (r < 0)
goto fail;
@@ -944,7 +940,6 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->notify_event_source);
sd_event_source_unref(m->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_event_source);
- sd_event_source_unref(m->idle_pipe_event_source);
sd_event_source_unref(m->run_queue_event_source);
safe_close(m->signal_fd);
@@ -967,6 +962,8 @@ Manager* manager_free(Manager *m) {
hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
+ hashmap_free(m->cgroup_netclass_registry);
+
free(m->switch_root);
free(m->switch_root_init);
@@ -1962,7 +1959,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
m->no_console_output = m->n_on_console > 0;
- m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
manager_close_idle_pipe(m);
return 0;
@@ -2675,9 +2671,6 @@ static void manager_notify_finished(Manager *m) {
}
void manager_check_finished(Manager *m) {
- Unit *u = NULL;
- Iterator i;
-
assert(m);
if (m->n_reloading > 0)
@@ -2690,11 +2683,9 @@ void manager_check_finished(Manager *m) {
return;
if (hashmap_size(m->jobs) > 0) {
-
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
- (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
- now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
+ (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
return;
}
@@ -2702,7 +2693,6 @@ void manager_check_finished(Manager *m) {
manager_flip_auto_status(m, false);
/* Notify Type=idle units that we are done now */
- m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
manager_close_idle_pipe(m);
/* Turn off confirm spawn now */
@@ -2721,9 +2711,7 @@ void manager_check_finished(Manager *m) {
manager_notify_finished(m);
- SET_FOREACH(u, m->startup_units, i)
- if (u->cgroup_path)
- cgroup_context_apply(unit_get_cgroup_context(u), unit_get_own_mask(u), u->cgroup_path, manager_state(m));
+ manager_invalidate_startup_units(m);
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
@@ -3069,8 +3057,9 @@ const char *manager_get_runtime_prefix(Manager *m) {
getenv("XDG_RUNTIME_DIR");
}
-void manager_update_failed_units(Manager *m, Unit *u, bool failed) {
+int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
unsigned size;
+ int r;
assert(m);
assert(u->manager == m);
@@ -3078,13 +3067,19 @@ void manager_update_failed_units(Manager *m, Unit *u, bool failed) {
size = set_size(m->failed_units);
if (failed) {
+ r = set_ensure_allocated(&m->failed_units, NULL);
+ if (r < 0)
+ return log_oom();
+
if (set_put(m->failed_units, u) < 0)
- log_oom();
+ return log_oom();
} else
- set_remove(m->failed_units, u);
+ (void) set_remove(m->failed_units, u);
if (set_size(m->failed_units) != size)
bus_manager_send_change_signal(m);
+
+ return 0;
}
ManagerState manager_state(Manager *m) {
diff --git a/src/core/manager.h b/src/core/manager.h
index 78a0e50a33..1384eb33a4 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -242,6 +242,11 @@ struct Manager {
bool test_run:1;
+ /* If non-zero, exit with the following value when the systemd
+ * process terminate. Useful for containers: systemd-nspawn could get
+ * the return value. */
+ uint8_t return_value;
+
ShowStatus show_status;
bool confirm_spawn;
bool no_console_output;
@@ -303,6 +308,10 @@ struct Manager {
const char *unit_log_format_string;
int first_boot;
+
+ /* Used for NetClass=auto units */
+ Hashmap *cgroup_netclass_registry;
+ uint32_t cgroup_netclass_registry_last;
};
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
@@ -369,7 +378,7 @@ const char *manager_get_runtime_prefix(Manager *m);
ManagerState manager_state(Manager *m);
-void manager_update_failed_units(Manager *m, Unit *u, bool failed);
+int manager_update_failed_units(Manager *m, Unit *u, bool failed);
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
diff --git a/src/core/service.c b/src/core/service.c
index 248a9e8c62..fc28ba4d07 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -2737,6 +2737,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
case SERVICE_RELOAD:
log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
+ service_unwatch_control_pid(s);
+ service_kill_control_processes(s);
s->reload_result = SERVICE_FAILURE_TIMEOUT;
service_enter_running(s, SERVICE_SUCCESS);
break;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 8cc6efc5b8..5296efce1d 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -48,6 +48,7 @@
#define FINALIZE_ATTEMPTS 50
static char* arg_verb;
+static uint8_t arg_exit_code;
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -55,6 +56,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
+ ARG_EXIT_CODE,
};
static const struct option options[] = {
@@ -62,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
+ { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
{}
};
@@ -110,6 +113,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_EXIT_CODE:
+ r = safe_atou8(optarg, &arg_exit_code);
+ if (r < 0)
+ log_error("Failed to parse exit code %s, ignoring", optarg);
+
+ break;
+
case '\001':
if (!arg_verb)
arg_verb = optarg;
@@ -183,6 +193,8 @@ int main(int argc, char *argv[]) {
cmd = RB_HALT_SYSTEM;
else if (streq(arg_verb, "kexec"))
cmd = LINUX_REBOOT_CMD_KEXEC;
+ else if (streq(arg_verb, "exit"))
+ cmd = 0; /* ignored, just checking that arg_verb is valid */
else {
r = -EINVAL;
log_error("Unknown action '%s'.", arg_verb);
@@ -339,6 +351,16 @@ int main(int argc, char *argv[]) {
if (!in_container)
sync();
+ if (streq(arg_verb, "exit")) {
+ if (in_container)
+ exit(arg_exit_code);
+ else {
+ /* We cannot exit() on the host, fallback on another
+ * method. */
+ cmd = RB_POWER_OFF;
+ }
+ }
+
switch (cmd) {
case LINUX_REBOOT_CMD_KEXEC:
diff --git a/src/core/transaction.c b/src/core/transaction.c
index b8f69ec6f3..2d120af4b5 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -736,8 +736,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
- pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
- pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
+ (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
+ (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
}
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 2ebfb09a7a..3a6313e4a2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -452,6 +452,7 @@ static void unit_free_requires_mounts_for(Unit *u) {
static void unit_done(Unit *u) {
ExecContext *ec;
CGroupContext *cc;
+ int r;
assert(u);
@@ -468,6 +469,10 @@ static void unit_done(Unit *u) {
cc = unit_get_cgroup_context(u);
if (cc)
cgroup_context_done(cc);
+
+ r = unit_remove_from_netclass_cgroup(u);
+ if (r < 0)
+ log_warning_errno(r, "Unable to remove unit from netclass group: %m");
}
void unit_free(Unit *u) {
@@ -528,7 +533,7 @@ void unit_free(Unit *u) {
unit_release_cgroup(u);
- manager_update_failed_units(u->manager, u, false);
+ (void) manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
free(u->description);
@@ -1172,15 +1177,20 @@ static int unit_add_mount_dependencies(Unit *u) {
static int unit_add_startup_units(Unit *u) {
CGroupContext *c;
+ int r;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
- if (c->startup_cpu_shares == (unsigned long) -1 &&
- c->startup_blockio_weight == (unsigned long) -1)
+ if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID &&
+ c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID)
return 0;
+ r = set_ensure_allocated(&u->manager->startup_units, NULL);
+ if (r < 0)
+ return r;
+
return set_put(u->manager->startup_units, u);
}
@@ -1236,6 +1246,14 @@ int unit_load(Unit *u) {
}
unit_update_cgroup_members_masks(u);
+
+ /* If we are reloading, we need to wait for the deserializer
+ * to restore the net_cls ids that have been set previously */
+ if (u->manager->n_reloading <= 0) {
+ r = unit_add_to_netclass_cgroup(u);
+ if (r < 0)
+ return r;
+ }
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
@@ -1807,7 +1825,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
/* Keep track of failed units */
- manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
+ (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
/* Make sure the cgroup is always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
@@ -2586,6 +2604,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
+ if (u->cgroup_netclass_id)
+ unit_serialize_item_format(u, f, "netclass-id", "%" PRIu32, u->cgroup_netclass_id);
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
@@ -2773,6 +2794,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
u->cgroup_realized = b;
continue;
+ } else if (streq(l, "netclass-id")) {
+ r = safe_atou32(v, &u->cgroup_netclass_id);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse netclass ID %s, ignoring.", v);
+ else {
+ r = unit_add_to_netclass_cgroup(u);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to add unit to netclass cgroup, ignoring: %m");
+ }
+
+ continue;
}
if (unit_can_serialize(u)) {
@@ -3032,32 +3064,39 @@ int unit_kill_common(
sd_bus_error *error) {
int r = 0;
+ bool killed = false;
- if (who == KILL_MAIN) {
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
if (main_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
else if (main_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
- if (who == KILL_CONTROL) {
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) {
if (control_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
else if (control_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (control_pid > 0)
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (control_pid > 0) {
if (kill(control_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_MAIN || who == KILL_ALL)
- if (main_pid > 0)
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (main_pid > 0) {
if (kill(main_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_ALL && u->cgroup_path) {
+ if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
int q;
@@ -3069,8 +3108,13 @@ int unit_kill_common(
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
+ else
+ killed = true;
}
+ if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+ return -ESRCH;
+
return r;
}
diff --git a/src/core/unit.h b/src/core/unit.h
index 3c7684411b..c868d75c79 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -161,6 +161,9 @@ struct Unit {
/* CGroup realize members queue */
LIST_FIELDS(Unit, cgroup_queue);
+ /* Units with the same CGroup netclass */
+ LIST_FIELDS(Unit, cgroup_netclass);
+
/* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to
* process SIGCHLD for */
@@ -189,6 +192,8 @@ struct Unit {
CGroupMask cgroup_members_mask;
int cgroup_inotify_wd;
+ uint32_t cgroup_netclass_id;
+
/* How to start OnFailure units */
JobMode on_failure_job_mode;
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
new file mode 100644
index 0000000000..2f5b9b3731
--- /dev/null
+++ b/src/libsystemd-network/arp-util.c
@@ -0,0 +1,153 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/filter.h>
+#include <arpa/inet.h>
+
+#include "util.h"
+#include "arp-util.h"
+
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */
+ 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_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_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 */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ };
+ struct sock_fprog fprog = {
+ .len = ELEMENTSOF(filter),
+ .filter = (struct sock_filter*) filter
+ };
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ _cleanup_close_ int s = -1;
+ int r;
+
+ assert(ifindex > 0);
+
+ s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (s < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ if (r < 0)
+ return -errno;
+
+ r = bind(s, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+
+ return r;
+}
+
+static int arp_send_packet(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha,
+ bool announce) {
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ struct ether_arp arp = {
+ .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
+ .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
+ .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+ .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
+ .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
+ };
+ int r;
+
+ assert(fd >= 0);
+ assert(pa != 0);
+ assert(ha);
+
+ memcpy(&arp.arp_sha, ha, ETH_ALEN);
+ memcpy(&arp.arp_tpa, &pa, sizeof(pa));
+
+ if (announce)
+ memcpy(&arp.arp_spa, &pa, sizeof(pa));
+
+ r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, false);
+}
+
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, true);
+}
diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/arp-util.h
index ae0ce43985..44e5c893a7 100644
--- a/src/libsystemd-network/ipv4ll-internal.h
+++ b/src/libsystemd-network/arp-util.h
@@ -26,13 +26,9 @@
#include "sparse-endian.h"
#include "socket-util.h"
-int arp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
-void arp_packet_init(struct ether_arp *arp);
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-int arp_packet_verify_headers(struct ether_arp *arp);
-
-#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
diff --git a/src/libsystemd-network/ipv4ll-network.c b/src/libsystemd-network/ipv4ll-network.c
deleted file mode 100644
index 93ffed408f..0000000000
--- a/src/libsystemd-network/ipv4ll-network.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/filter.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp) {
- int r;
-
- assert(arp);
- assert(link);
- assert(fd >= 0);
-
- r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) {
-
- static const struct sock_filter filter[] = {
- BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
- BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- };
- struct sock_fprog fprog = {
- .len = ELEMENTSOF(filter),
- .filter = (struct sock_filter*) filter
- };
- _cleanup_close_ int s = -1;
- int r;
-
- assert(ifindex > 0);
- assert(link);
-
- s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
- if (r < 0)
- return -errno;
-
- link->ll.sll_family = AF_PACKET;
- link->ll.sll_protocol = htons(ETH_P_ARP);
- link->ll.sll_ifindex = ifindex;
- link->ll.sll_halen = ETH_ALEN;
- memset(link->ll.sll_addr, 0xff, ETH_ALEN);
-
- r = bind(s, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- r = s;
- s = -1;
-
- return r;
-}
diff --git a/src/libsystemd-network/ipv4ll-packet.c b/src/libsystemd-network/ipv4ll-packet.c
deleted file mode 100644
index 2b6c73ab4b..0000000000
--- a/src/libsystemd-network/ipv4ll-packet.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-#include <arpa/inet.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-void arp_packet_init(struct ether_arp *arp) {
- assert(arp);
-
- memzero(arp, sizeof(struct ether_arp));
- /* Header */
- arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */
- arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */
- arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */
- arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */
- arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */
-}
-
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
-}
-
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
- memcpy(arp->arp_spa, &pa, sizeof(pa));
-}
-
-int arp_packet_verify_headers(struct ether_arp *arp) {
- assert(arp);
-
- if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
- log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
- log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
- arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
- log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
- return -EINVAL;
- }
-
- return 0;
-}
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644
index 0000000000..ee5d13c030
--- /dev/null
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -0,0 +1,529 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "event-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "refcnt.h"
+#include "random-util.h"
+#include "siphash24.h"
+#include "util.h"
+
+#include "arp-util.h"
+#include "sd-ipv4acd.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4ACD_NETWORK 0xA9FE0000L
+#define IPV4ACD_NETMASK 0xFFFF0000L
+
+#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
+#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+
+typedef enum IPv4ACDState {
+ IPV4ACD_STATE_INIT,
+ IPV4ACD_STATE_WAITING_PROBE,
+ IPV4ACD_STATE_PROBING,
+ IPV4ACD_STATE_WAITING_ANNOUNCE,
+ IPV4ACD_STATE_ANNOUNCING,
+ IPV4ACD_STATE_RUNNING,
+ _IPV4ACD_STATE_MAX,
+ _IPV4ACD_STATE_INVALID = -1
+} IPv4ACDState;
+
+struct sd_ipv4acd {
+ RefCount n_ref;
+
+ IPv4ACDState state;
+ int index;
+ int fd;
+ int iteration;
+ int conflict;
+ sd_event_source *receive_message;
+ sd_event_source *timer;
+ usec_t defend_window;
+ be32_t address;
+ /* External */
+ struct ether_addr mac_addr;
+ sd_event *event;
+ int event_priority;
+ sd_ipv4acd_cb_t cb;
+ void* userdata;
+};
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
+ if (ll)
+ assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+ return ll;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
+ if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+ return NULL;
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ sd_ipv4acd_detach_event(ll);
+
+ free(ll);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
+#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+ _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ ll = new0(sd_ipv4acd, 1);
+ if (!ll)
+ return -ENOMEM;
+
+ ll->n_ref = REFCNT_INIT;
+ ll->state = IPV4ACD_STATE_INIT;
+ ll->index = -1;
+ ll->fd = -1;
+
+ *ret = ll;
+ ll = NULL;
+
+ return 0;
+}
+
+static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
+
+ assert(ll);
+ assert(st < _IPV4ACD_STATE_MAX);
+
+ if (st == ll->state && !reset_counter)
+ ll->iteration++;
+ else {
+ ll->state = st;
+ ll->iteration = 0;
+ }
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
+ assert(ll);
+
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
+}
+
+static void ipv4acd_stop(sd_ipv4acd *ll) {
+ assert(ll);
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ log_ipv4acd_debug(ll, "STOPPED");
+
+ ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_STOP);
+
+ return 0;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+ _cleanup_event_source_unref_ sd_event_source *timer = NULL;
+ usec_t next_timeout;
+ usec_t time_now;
+ int r;
+
+ assert(sec >= 0);
+ assert(random_sec >= 0);
+ assert(ll);
+
+ next_timeout = sec * USEC_PER_SEC;
+
+ if (random_sec)
+ next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
+ time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(timer, ll->event_priority);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_description(timer, "ipv4acd-timer");
+ if (r < 0)
+ return r;
+
+ ll->timer = sd_event_source_unref(ll->timer);
+ ll->timer = timer;
+ timer = NULL;
+
+ return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
+ assert(ll);
+ assert(arp);
+
+ /* see the BPF */
+ if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+ return true;
+
+ /* the TPA matched instead of the SPA, this is not a conflict */
+ return false;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ int r = 0;
+
+ assert(ll);
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_INIT:
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+
+ if (ll->conflict >= MAX_CONFLICTS) {
+ log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
+ r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+
+ ll->conflict = 0;
+ } else {
+ r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ /* Send a probe */
+ r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
+ goto out;
+ } else {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Probing %s", address);
+ }
+
+ if (ll->iteration < PROBE_NUM - 2) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+ if (r < 0)
+ goto out;
+ } else {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+
+ case IPV4ACD_STATE_ANNOUNCING:
+ if (ll->iteration >= ANNOUNCE_NUM - 1) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
+
+ break;
+ }
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* Send announcement packet */
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "ANNOUNCE");
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+ if (r < 0)
+ goto out;
+
+ if (ll->iteration == 0) {
+ ll->conflict = 0;
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_BIND);
+ }
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+ int r;
+
+ assert(ll);
+
+ ll->conflict++;
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ struct ether_arp packet;
+ int r;
+
+ assert(ll);
+ assert(fd >= 0);
+
+ r = read(fd, &packet, sizeof(struct ether_arp));
+ if (r < (int) sizeof(struct ether_arp))
+ goto out;
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_ANNOUNCING:
+ case IPV4ACD_STATE_RUNNING:
+ if (ipv4acd_arp_conflict(ll, &packet)) {
+ usec_t ts;
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* Defend address */
+ if (ts > ll->defend_window) {
+ ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "DEFEND");
+
+ } else
+ ipv4acd_on_conflict(ll);
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* BPF ensures this packet indicates a conflict */
+ ipv4acd_on_conflict(ll);
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
+ assert_return(ll, -EINVAL);
+ assert_return(interface_index > 0, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->index = interface_index;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
+ assert_return(ll, -EINVAL);
+ assert_return(addr, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+ return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ll->event = sd_event_unref(ll->event);
+
+ return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(!ll->event, -EBUSY);
+
+ if (event)
+ ll->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&ll->event);
+ if (r < 0)
+ return r;
+ }
+
+ ll->event_priority = priority;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+ assert_return(ll, -EINVAL);
+
+ ll->cb = cb;
+ ll->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+ assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->address = address->s_addr;
+
+ return 0;
+}
+
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+ assert_return(ll, false);
+
+ return ll->state != IPV4ACD_STATE_INIT;
+}
+
+static bool ether_addr_is_nul(const struct ether_addr *addr) {
+ const struct ether_addr nul_addr = {};
+
+ assert(addr);
+
+ return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4acd_start(sd_ipv4acd *ll) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(ll->event, -EINVAL);
+ assert_return(ll->index > 0, -EINVAL);
+ assert_return(ll->address != 0, -EINVAL);
+ assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->defend_window = 0;
+
+ r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+ if (r < 0)
+ goto out;
+
+ ll->fd = safe_close(ll->fd);
+ ll->fd = r;
+
+ r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
+ EPOLLIN, ipv4acd_on_packet, ll);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
+ if (r < 0)
+ goto out;
+
+ r = ipv4acd_set_next_wakeup(ll, 0, 0);
+ if (r < 0)
+ goto out;
+out:
+ if (r < 0) {
+ ipv4acd_stop(ll);
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 14b9444dab..f0230b919c 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -23,429 +24,153 @@
#include <stdio.h>
#include <arpa/inet.h>
-#include "util.h"
-#include "siphash24.h"
+#include "event-util.h"
#include "list.h"
#include "random-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "util.h"
-#include "ipv4ll-internal.h"
+#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h"
-/* Constants from the RFC */
-#define PROBE_WAIT 1
-#define PROBE_NUM 3
-#define PROBE_MIN 1
-#define PROBE_MAX 2
-#define ANNOUNCE_WAIT 2
-#define ANNOUNCE_NUM 2
-#define ANNOUNCE_INTERVAL 2
-#define MAX_CONFLICTS 10
-#define RATE_LIMIT_INTERVAL 60
-#define DEFEND_INTERVAL 10
-
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
-typedef enum IPv4LLTrigger{
- IPV4LL_TRIGGER_NULL,
- IPV4LL_TRIGGER_PACKET,
- IPV4LL_TRIGGER_TIMEOUT,
- _IPV4LL_TRIGGER_MAX,
- _IPV4LL_TRIGGER_INVALID = -1
-} IPv4LLTrigger;
-
-typedef enum IPv4LLState {
- IPV4LL_STATE_INIT,
- IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE,
- IPV4LL_STATE_ANNOUNCING,
- IPV4LL_STATE_RUNNING,
- IPV4LL_STATE_STOPPED,
- _IPV4LL_STATE_MAX,
- _IPV4LL_STATE_INVALID = -1
-} IPv4LLState;
+#define IPV4LL_DONT_DESTROY(ll) \
+ _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
struct sd_ipv4ll {
unsigned n_ref;
- IPv4LLState state;
- int index;
- int fd;
- union sockaddr_union link;
- int iteration;
- int conflict;
- sd_event_source *receive_message;
- sd_event_source *timer;
- usec_t next_wakeup;
- usec_t defend_window;
- int next_wakeup_valid;
- be32_t address;
+ sd_ipv4acd *acd;
+ be32_t address; /* the address pushed to ACD */
struct random_data *random_data;
char *random_data_state;
+
/* External */
be32_t claimed_address;
- struct ether_addr mac_addr;
- sd_event *event;
- int event_priority;
sd_ipv4ll_cb_t cb;
void* userdata;
};
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
-
-static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
-
- assert(ll);
- assert(st < _IPV4LL_STATE_MAX);
-
- if (st == ll->state && !reset_counter) {
- ll->iteration++;
- } else {
- ll->state = st;
- ll->iteration = 0;
- }
-}
-
-static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- if (ll->cb) {
- ll = sd_ipv4ll_ref(ll);
- ll->cb(ll, event, ll->userdata);
- ll = sd_ipv4ll_unref(ll);
- }
-
- return ll;
-}
-
-static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
-
- log_ipv4ll(ll, "STOPPED");
-
- ll = ipv4ll_client_notify(ll, event);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- if (ll) {
- ll->claimed_address = 0;
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
- }
+ assert(ll->n_ref >= 1);
+ ll->n_ref++;
return ll;
}
-static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
- be32_t addr;
- int r;
- int32_t random;
-
- assert(ll);
- assert(address);
- assert(ll->random_data);
-
- do {
- r = random_r(ll->random_data, &random);
- if (r < 0)
- return r;
- addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
- } while (addr == ll->address ||
- (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
- (ntohl(addr) & 0x0000FF00) == 0x0000 ||
- (ntohl(addr) & 0x0000FF00) == 0xFF00);
-
- *address = addr;
- return 0;
-}
-
-static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
-
- assert(ll);
-
- ll->next_wakeup_valid = 0;
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
-
- return 0;
-}
-
-static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
- usec_t next_timeout = 0;
- usec_t time_now = 0;
-
- assert(sec >= 0);
- assert(random_sec >= 0);
- assert(ll);
-
- next_timeout = sec * USEC_PER_SEC;
-
- if (random_sec)
- next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
-
- assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
- ll->next_wakeup = time_now + next_timeout;
- ll->next_wakeup_valid = 1;
-}
-
-static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
-
- if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
- return true;
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- return false;
-}
+ assert(ll->n_ref >= 1);
+ ll->n_ref--;
-static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
+ if (ll->n_ref > 0)
+ return NULL;
- if (ipv4ll_arp_conflict(ll, arp))
- return true;
+ sd_ipv4acd_unref(ll->acd);
- if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
- return true;
+ free(ll->random_data);
+ free(ll->random_data_state);
+ free(ll);
- return false;
+ return NULL;
}
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
- struct ether_arp out_packet;
- int out_packet_ready = 0;
- int r = 0;
-
- assert(ll);
- assert(trigger < _IPV4LL_TRIGGER_MAX);
-
- if (ll->state == IPV4LL_STATE_INIT) {
-
- log_ipv4ll(ll, "PROBE");
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
-
- /* Send a probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
-
- ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
-
- } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
-
- /* Send the last probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
-
- /* Send announcement packet */
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
-
- if (ll->iteration == 0) {
- log_ipv4ll(ll, "ANNOUNCE");
- ll->claimed_address = ll->address;
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
-
- ll->conflict = 0;
- }
-
- } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
- ll->iteration >= ANNOUNCE_NUM-1)) {
-
- ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
- ll->next_wakeup_valid = 0;
-
- } else if (trigger == IPV4LL_TRIGGER_PACKET) {
-
- int conflicted = 0;
- usec_t time_now;
- struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
-
- assert(in_packet);
-
- if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
-
- if (ipv4ll_arp_conflict(ll, in_packet)) {
-
- r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- goto out;
-
- /* Defend address */
- if (time_now > ll->defend_window) {
- ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- } else
- conflicted = 1;
- }
-
- } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE)) {
-
- conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
- }
-
- if (conflicted) {
- log_ipv4ll(ll, "CONFLICT");
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
+#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
- ll->claimed_address = 0;
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
- /* Pick a new address */
- r = ipv4ll_pick_address(ll, &ll->address);
- if (r < 0)
- goto out;
- ll->conflict++;
- ll->defend_window = 0;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+ _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
+ int r;
- if (ll->conflict >= MAX_CONFLICTS) {
- log_ipv4ll(ll, "MAX_CONFLICTS");
- ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
- } else
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
+ assert_return(ret, -EINVAL);
- }
- }
+ ll = new0(sd_ipv4ll, 1);
+ if (!ll)
+ return -ENOMEM;
- if (out_packet_ready) {
- r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
- if (r < 0) {
- log_ipv4ll(ll, "failed to send arp packet out");
- goto out;
- }
- }
+ r = sd_ipv4acd_new(&ll->acd);
+ if (r < 0)
+ return r;
- if (ll->next_wakeup_valid) {
- ll->timer = sd_event_source_unref(ll->timer);
- r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(),
- ll->next_wakeup, 0, ipv4ll_timer, ll);
- if (r < 0)
- goto out;
+ r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+ if (r < 0)
+ return r;
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
+ ll->n_ref = 1;
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
- if (r < 0)
- goto out;
- }
+ *ret = ll;
+ ll = NULL;
-out:
- if (r < 0 && ll)
- ipv4ll_stop(ll, r);
+ return 0;
}
-static int ipv4ll_receive_message(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
+int sd_ipv4ll_stop(sd_ipv4ll *ll) {
int r;
- struct ether_arp arp;
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
- assert(ll);
-
- r = read(fd, &arp, sizeof(struct ether_arp));
- if (r < (int) sizeof(struct ether_arp))
- return 0;
+ assert_return(ll, -EINVAL);
- r = arp_packet_verify_headers(&arp);
+ r = sd_ipv4acd_stop(ll->acd);
if (r < 0)
- return 0;
-
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
+ return r;
return 0;
}
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
assert_return(ll, -EINVAL);
- assert_return(interface_index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
- ll->index = interface_index;
-
- return 0;
+ return sd_ipv4acd_set_index(ll->acd, interface_index);
}
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
- bool need_restart = false;
+ int r;
assert_return(ll, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
- return 0;
- if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
- log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
- "client, restarting");
- ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- need_restart = true;
- }
+ if (!ll->random_data) {
+ uint8_t seed[8];
- if (!ll)
- return 0;
+ /* If no random data is set, generate some from the MAC */
+ siphash24(seed, &addr->ether_addr_octet,
+ ETH_ALEN, HASH_KEY.bytes);
- memcpy(&ll->mac_addr, addr, ETH_ALEN);
+ assert_cc(sizeof(unsigned) <= 8);
- if (need_restart)
- sd_ipv4ll_start(ll);
+ r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
+ if (r < 0)
+ return r;
+ }
- return 0;
+ return sd_ipv4acd_set_mac(ll->acd, addr);
}
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
assert_return(ll, -EINVAL);
- ll->event = sd_event_unref(ll->event);
-
- return 0;
+ return sd_ipv4acd_detach_event(ll->acd);
}
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
int r;
assert_return(ll, -EINVAL);
- assert_return(!ll->event, -EBUSY);
-
- if (event)
- ll->event = sd_event_ref(event);
- else {
- r = sd_event_default(&ll->event);
- if (r < 0) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- return r;
- }
- }
- ll->event_priority = priority;
+ r = sd_ipv4acd_attach_event(ll->acd, event, priority);
+ if (r < 0)
+ return r;
return 0;
}
@@ -467,189 +192,147 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
return -ENOENT;
address->s_addr = ll->claimed_address;
+
return 0;
}
-int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
- unsigned int entropy;
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
+ _cleanup_free_ struct random_data *random_data = NULL;
+ _cleanup_free_ char *random_data_state = NULL;
int r;
assert_return(ll, -EINVAL);
assert_return(seed, -EINVAL);
- entropy = *seed;
+ random_data = new0(struct random_data, 1);
+ if (!random_data)
+ return -ENOMEM;
- free(ll->random_data);
- free(ll->random_data_state);
+ random_data_state = new0(char, 128);
+ if (!random_data_state)
+ return -ENOMEM;
- ll->random_data = new0(struct random_data, 1);
- ll->random_data_state = new0(char, 128);
+ r = initstate_r(seed, random_data_state, 128, random_data);
+ if (r < 0)
+ return r;
- if (!ll->random_data || !ll->random_data_state) {
- r = -ENOMEM;
- goto error;
- }
+ free(ll->random_data);
+ ll->random_data = random_data;
+ random_data = NULL;
- r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
- if (r < 0)
- goto error;
+ free(ll->random_data_state);
+ ll->random_data_state = random_data_state;
+ random_data_state = NULL;
-error:
- if (r < 0){
- free(ll->random_data);
- free(ll->random_data_state);
- ll->random_data = NULL;
- ll->random_data_state = NULL;
- }
- return r;
+ return 0;
}
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
assert_return(ll, false);
- return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
+ return sd_ipv4acd_is_running(ll->acd);
}
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-int sd_ipv4ll_start (sd_ipv4ll *ll) {
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+ struct in_addr in_addr;
+ be32_t addr;
int r;
+ int32_t random;
- assert_return(ll, -EINVAL);
- assert_return(ll->event, -EINVAL);
- assert_return(ll->index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
+ assert(ll);
+ assert(ll->random_data);
- ll->state = IPV4LL_STATE_INIT;
+ do {
+ r = random_r(ll->random_data, &random);
+ if (r < 0)
+ return r;
+ addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
+ } while (addr == ll->address ||
+ (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
+ (ntohl(addr) & 0x0000FF00) == 0x0000 ||
+ (ntohl(addr) & 0x0000FF00) == 0xFF00);
- r = arp_network_bind_raw_socket(ll->index, &ll->link);
+ in_addr.s_addr = addr;
+ r = sd_ipv4acd_set_address(ll->acd, &in_addr);
if (r < 0)
- goto out;
+ return r;
- ll->fd = r;
- ll->conflict = 0;
- ll->defend_window = 0;
- ll->claimed_address = 0;
+ ll->address = addr;
- if (!ll->random_data) {
- uint8_t seed[8];
+ return 0;
+}
- /* Fallback to mac */
- siphash24(seed, &ll->mac_addr.ether_addr_octet,
- ETH_ALEN, HASH_KEY.bytes);
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+ int r;
- r = sd_ipv4ll_set_address_seed(ll, seed);
- if (r < 0)
- goto out;
- }
+ assert_return(ll, -EINVAL);
+ assert_return(ll->random_data, -EINVAL);
if (ll->address == 0) {
- r = ipv4ll_pick_address(ll, &ll->address);
+ r = ipv4ll_pick_address(ll);
if (r < 0)
- goto out;
+ return r;
}
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
-
- r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
- EPOLLIN, ipv4ll_receive_message, ll);
+ r = sd_ipv4acd_start(ll->acd);
if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
- if (r < 0)
- goto out;
-
- r = sd_event_add_time(ll->event,
- &ll->timer,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()), 0,
- ipv4ll_timer, ll);
-
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
-out:
- if (r < 0)
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+ return r;
return 0;
}
-int sd_ipv4ll_stop(sd_ipv4ll *ll) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- if (ll)
- ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
-
- return 0;
-}
-
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref++;
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+ assert(ll);
- return ll;
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
}
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref--;
-
- if (ll->n_ref > 0)
- return ll;
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
+void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+ sd_ipv4ll *ll = userdata;
+ IPV4LL_DONT_DESTROY(ll);
+ int r;
- sd_ipv4ll_detach_event(ll);
+ assert(acd);
+ assert(ll);
- free(ll->random_data);
- free(ll->random_data_state);
- free(ll);
+ switch (event) {
+ case IPV4ACD_EVENT_STOP:
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
- return NULL;
-}
+ ll->claimed_address = 0;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
-#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
+ break;
+ case IPV4ACD_EVENT_BIND:
+ ll->claimed_address = ll->address;
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
- _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
+ break;
+ case IPV4ACD_EVENT_CONFLICT:
+ /* if an address was already bound we must call up to the
+ user to handle this, otherwise we just try again */
+ if (ll->claimed_address != 0) {
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
- assert_return(ret, -EINVAL);
+ ll->claimed_address = 0;
+ } else {
+ r = ipv4ll_pick_address(ll);
+ if (r < 0)
+ goto error;
- ll = new0(sd_ipv4ll, 1);
- if (!ll)
- return -ENOMEM;
+ r = sd_ipv4acd_start(ll->acd);
+ if (r < 0)
+ goto error;
+ }
- ll->n_ref = 1;
- ll->state = IPV4LL_STATE_INIT;
- ll->index = -1;
- ll->fd = -1;
+ break;
+ default:
+ assert_not_reached("Invalid IPv4ACD event.");
+ }
- *ret = ll;
- ll = NULL;
+ return;
- return 0;
+error:
+ ipv4ll_client_notify(ll, IPV4LL_EVENT_STOP);
}
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
new file mode 100644
index 0000000000..f1f14ee119
--- /dev/null
+++ b/src/libsystemd-network/test-acd.c
@@ -0,0 +1,117 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+#include "sd-ipv4acd.h"
+
+#include "util.h"
+#include "event-util.h"
+#include "netlink-util.h"
+#include "in-addr-util.h"
+
+static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
+ assert_se(acd);
+
+ switch (event) {
+ case IPV4ACD_EVENT_BIND:
+ log_info("bound");
+ break;
+ case IPV4ACD_EVENT_CONFLICT:
+ log_info("conflict");
+ break;
+ case IPV4ACD_EVENT_STOP:
+ log_error("the client was stopped");
+ break;
+ default:
+ assert_not_reached("invalid ACD event");
+ }
+}
+
+static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) {
+ sd_ipv4acd *acd;
+
+ assert_se(sd_ipv4acd_new(&acd) >= 0);
+ assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0);
+
+ assert_se(sd_ipv4acd_set_index(acd, ifindex) >= 0);
+ assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0);
+ assert_se(sd_ipv4acd_set_address(acd, pa) >= 0);
+ assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0);
+
+ log_info("starting IPv4ACD client");
+
+ assert_se(sd_ipv4acd_start(acd) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ assert_se(!sd_ipv4acd_unref(acd));
+
+ return EXIT_SUCCESS;
+}
+
+static int test_acd(const char *ifname, const char *address) {
+ _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ union in_addr_union pa;
+ struct ether_addr ha;
+ int ifindex;
+
+ assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0);
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_netlink_open(&rtnl) >= 0);
+ assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0);
+
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0);
+ assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0);
+ assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0);
+
+ assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
+ assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
+
+ client_run(ifindex, &pa.in, &ha, e);
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc == 3)
+ return test_acd(argv[1], argv[2]);
+ else {
+ log_error("This program takes two arguments.\n"
+ "\t %s <ifname> <IPv4 address>", program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
new file mode 100644
index 0000000000..ad664cba51
--- /dev/null
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -0,0 +1,129 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+#include "sd-ipv4ll.h"
+
+#include "util.h"
+#include "event-util.h"
+#include "netlink-util.h"
+#include "in-addr-util.h"
+
+static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
+ _cleanup_free_ char *address = NULL;
+ struct in_addr addr = {};
+
+ assert_se(ll);
+
+ if (sd_ipv4ll_get_address(ll, &addr) >= 0)
+ assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0);
+
+ switch (event) {
+ case IPV4LL_EVENT_BIND:
+ log_info("bound %s", strna(address));
+ break;
+ case IPV4LL_EVENT_CONFLICT:
+ log_info("conflict on %s", strna(address));
+ break;
+ case IPV4LL_EVENT_STOP:
+ log_error("the client was stopped with address %s", strna(address));
+ break;
+ default:
+ assert_not_reached("invalid LL event");
+ }
+}
+
+static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) {
+ sd_ipv4ll *ll;
+
+ assert_se(sd_ipv4ll_new(&ll) >= 0);
+ assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0);
+
+ assert_se(sd_ipv4ll_set_index(ll, ifindex) >= 0);
+ assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0);
+ assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0);
+
+ if (seed_str) {
+ unsigned seed;
+
+ assert_se(safe_atou(seed_str, &seed) >= 0);
+
+ assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0);
+ }
+
+ log_info("starting IPv4LL client");
+
+ assert_se(sd_ipv4ll_start(ll) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ assert_se(!sd_ipv4ll_unref(ll));
+
+ return EXIT_SUCCESS;
+}
+
+static int test_ll(const char *ifname, const char *seed) {
+ _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ struct ether_addr ha;
+ int ifindex;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_netlink_open(&rtnl) >= 0);
+ assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0);
+
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0);
+ assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0);
+ assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0);
+
+ assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
+ assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
+
+ client_run(ifindex, seed, &ha, e);
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc == 2)
+ return test_ll(argv[1], NULL);
+ else if (argc == 3)
+ return test_ll(argv[1], argv[2]);
+ else {
+ log_error("This program takes one or two arguments.\n"
+ "\t %s <ifname> [<seed>]", program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index d60ee98b25..f0e26bda07 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -31,7 +31,7 @@
#include "event-util.h"
#include "sd-ipv4ll.h"
-#include "ipv4ll-internal.h"
+#include "arp-util.h"
static bool verbose = false;
static bool extended = false;
@@ -56,10 +56,10 @@ static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) {
}
}
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp) {
+static int arp_network_send_raw_socket(int fd, int ifindex,
+ const struct ether_arp *arp) {
assert_se(arp);
- assert_se(link);
+ assert_se(ifindex > 0);
assert_se(fd >= 0);
if (send(fd, arp, sizeof(struct ether_arp), 0) < 0)
@@ -68,51 +68,35 @@ int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
return 0;
}
-int arp_network_bind_raw_socket(int index, union sockaddr_union *link) {
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
- return -errno;
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ struct ether_arp ea = {};
- return test_fd[0];
-}
+ assert(fd >= 0);
+ assert(ifindex > 0);
+ assert(pa != 0);
+ assert(ha);
-static void test_arp_header(struct ether_arp *arp) {
- assert_se(arp);
- assert_se(arp->ea_hdr.ar_hrd == htons(ARPHRD_ETHER)); /* HTYPE */
- assert_se(arp->ea_hdr.ar_pro == htons(ETHERTYPE_IP)); /* PTYPE */
- assert_se(arp->ea_hdr.ar_hln == ETH_ALEN); /* HLEN */
- assert_se(arp->ea_hdr.ar_pln == sizeof arp->arp_spa); /* PLEN */
- assert_se(arp->ea_hdr.ar_op == htons(ARPOP_REQUEST)); /* REQUEST */
+ return arp_network_send_raw_socket(fd, ifindex, &ea);
}
-static void test_arp_probe(void) {
- struct ether_arp arp;
- struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
- be32_t pa = 0x3030;
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ struct ether_arp ea = {};
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ assert(fd >= 0);
+ assert(ifindex > 0);
+ assert(pa != 0);
+ assert(ha);
- arp_packet_probe(&arp, pa, &mac_addr);
- test_arp_header(&arp);
- assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0);
- assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0);
+ return arp_network_send_raw_socket(fd, ifindex, &ea);
}
-static void test_arp_announce(void) {
- struct ether_arp arp;
- struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
- be32_t pa = 0x3131;
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
- arp_packet_announcement(&arp, pa, &mac_addr);
- test_arp_header(&arp);
- assert_se(memcmp(arp.arp_sha, &mac_addr, ETH_ALEN) == 0);
- assert_se(memcmp(arp.arp_tpa, &pa, sizeof(pa)) == 0);
- assert_se(memcmp(arp.arp_spa, &pa, sizeof(pa)) == 0);
+ return test_fd[0];
}
static void test_public_api_setters(sd_event *e) {
@@ -134,9 +118,8 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0);
- assert_se(sd_ipv4ll_set_address_seed(NULL, NULL) == -EINVAL);
- assert_se(sd_ipv4ll_set_address_seed(ll, NULL) == -EINVAL);
- assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
+ assert_se(sd_ipv4ll_set_address_seed(NULL, *(unsigned *) seed) == -EINVAL);
+ assert_se(sd_ipv4ll_set_address_seed(ll, *(unsigned *) seed) == 0);
assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
@@ -149,7 +132,7 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_set_index(ll, 99) == 0);
assert_se(sd_ipv4ll_ref(ll) == ll);
- assert_se(sd_ipv4ll_unref(ll) == ll);
+ assert_se(sd_ipv4ll_unref(ll) == NULL);
/* Cleanup */
assert_se(sd_ipv4ll_unref(ll) == NULL);
@@ -184,21 +167,20 @@ static void test_basic_request(sd_event *e) {
sd_event_run(e, (uint64_t) -1);
assert_se(sd_ipv4ll_start(ll) == -EBUSY);
+ assert_se(sd_ipv4ll_is_running(ll));
+
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
if (extended) {
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
/* PROBE */
sd_event_run(e, (uint64_t) -1);
assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp));
- test_arp_header(&arp);
sd_event_run(e, (uint64_t) -1);
assert_se(basic_request_handler_bind == 1);
@@ -215,11 +197,13 @@ static void test_basic_request(sd_event *e) {
int main(int argc, char *argv[]) {
_cleanup_event_unref_ sd_event *e = NULL;
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
assert_se(sd_event_new(&e) >= 0);
test_public_api_setters(e);
- test_arp_probe();
- test_arp_announce();
test_basic_request(e);
return 0;
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 7af61a9433..e399701beb 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -396,6 +396,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
#define bus_assert_return(expr, r, error) \
do { \
- if (!assert_log(expr)) \
+ if (!assert_log(expr, #expr)) \
return sd_bus_error_set_errno(error, r); \
} while (false)
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 735a775cb4..d0b1e3d7dc 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -985,7 +985,7 @@ int bus_socket_read_message(sd_bus *bus) {
return -EIO;
}
- f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n));
+ f = realloc(bus->fds, sizeof(int) * (bus->n_fds + n));
if (!f) {
close_many((int*) CMSG_DATA(cmsg), n);
return -ENOMEM;
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index a7e64071cf..405df49a7c 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -686,19 +686,165 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
return 0;
}
+static int print_property(const char *name, sd_bus_message *m, const char *contents) {
+ int r;
+
+ assert(name);
+ assert(m);
+ assert(contents);
+
+ if (arg_property && !strv_find(arg_property, name))
+ /* skip what we didn't read */
+ return sd_bus_message_skip(m, contents);
+
+ switch (contents[0]) {
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+
+ if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
+ const char *s;
+
+ r = sd_bus_message_read(m, "(so)", &s, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_all || !isempty(s))
+ printf("%s=%s\n", name, s);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
+ uint32_t uid;
+
+ r = sd_bus_message_read(m, "(uo)", &uid, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (UID_IS_INVALID(uid)) {
+ log_error("Invalid user ID: " UID_FMT, uid);
+ return -EINVAL;
+ }
+
+ printf("%s=" UID_FMT "\n", name, uid);
+
+ return 0;
+ }
+
+ break;
+
+ case SD_BUS_TYPE_ARRAY:
+
+ if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
+ const char *s;
+ bool space = false;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ printf("%s=", name);
+
+ while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
+ printf("%s%s", space ? " " : "", s);
+ space = true;
+ }
+
+ printf("\n");
+
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+ }
+
+ break;
+ }
+
+ r = bus_print_property(name, m, arg_all);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (r == 0) {
+ r = sd_bus_message_skip(m, contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_all)
+ printf("%s=[unprintable]\n", name);
+ }
+
+ return 0;
+}
+
static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
+ assert(bus);
+ assert(path);
+ assert(new_line);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &error,
+ &reply,
+ "s", "");
+ if (r < 0)
+ return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
if (*new_line)
printf("\n");
*new_line = true;
- r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
+ while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const char *name, *contents;
+
+ r = sd_bus_message_read(reply, "s", &name);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_peek_type(reply, NULL, &contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = print_property(name, reply, contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
if (r < 0)
- log_error_errno(r, "Could not get properties: %m");
+ return bus_log_parse_error(r);
- return r;
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
}
static int show_session(int argc, char *argv[], void *userdata) {
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 0a27a30278..1c34f55b4b 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -195,10 +195,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
}
break;
default:
- if (event < 0)
- log_link_warning(link, "IPv4 link-local error: %s", strerror(-event));
- else
- log_link_warning(link, "IPv4 link-local unknown event: %d", event);
+ log_link_warning(link, "IPv4 link-local unknown event: %d", event);
break;
}
}
@@ -218,7 +215,9 @@ int ipv4ll_configure(Link *link) {
if (link->udev_device) {
r = net_get_unique_predictable_data(link->udev_device, seed);
if (r >= 0) {
- r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+ assert_cc(sizeof(unsigned) <= 8);
+
+ r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed);
if (r < 0)
return r;
}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 9d4a69b0db..215c47b8af 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1789,6 +1789,45 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
return 0;
}
+static int link_set_ipv6_accept_ra(Link *link) {
+ const char *p = NULL, *v = NULL;
+ bool b;
+ int r;
+
+ /* Make this a NOP if IPv6 is not available */
+ if (!socket_ipv6_is_supported())
+ return 0;
+
+ if (link->flags & IFF_LOOPBACK)
+ return 0;
+
+ /* if unset check the ip forwarding setting maintained for the interface
+ * and then set it to depending on that. enabled if local forwarding
+ * is disabled. disabled if local forwarding is enabled.
+ */
+ if (link->network->ipv6_accept_ra < 0) {
+ if (IN_SET(link->network->ip_forward, ADDRESS_FAMILY_YES, ADDRESS_FAMILY_IPV6))
+ b = false;
+ else
+ b = true;
+ } else
+ b = link->network->ipv6_accept_ra;
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
+ v = one_zero(b);
+
+ r = write_string_file(p, v, 0);
+ if (r < 0) {
+ /* If the right value is set anyway, don't complain */
+ if (verify_one_line_file(p, v) > 0)
+ return 0;
+
+ log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m");
+ }
+
+ return 0;
+}
+
static int link_configure(Link *link) {
int r;
@@ -1812,6 +1851,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = link_set_ipv6_accept_ra(link);
+ if (r < 0)
+ return r;
+
if (link_ipv4ll_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 10ca9dae35..8257ab45da 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -15,88 +15,89 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac)
-Match.Path, config_parse_strv, 0, offsetof(Network, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(Network, match_type)
-Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
-Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
-Network.Description, config_parse_string, 0, offsetof(Network, description)
-Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
-Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
-Network.VLAN, config_parse_netdev, 0, 0
-Network.MACVLAN, config_parse_netdev, 0, 0
-Network.MACVTAP, config_parse_netdev, 0, 0
-Network.IPVLAN, config_parse_netdev, 0, 0
-Network.VXLAN, config_parse_netdev, 0, 0
-Network.Tunnel, config_parse_tunnel, 0, 0
-Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
-Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
-Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local)
-Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
-Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
-Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
-Network.Address, config_parse_address, 0, 0
-Network.Gateway, config_parse_gateway, 0, 0
-Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
-Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
-Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr)
-Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
-Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
-Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
-Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
-Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
-Address.Address, config_parse_address, 0, 0
-Address.Peer, config_parse_address, 0, 0
-Address.Broadcast, config_parse_broadcast, 0, 0
-Address.Label, config_parse_label, 0, 0
-Route.Gateway, config_parse_gateway, 0, 0
-Route.Destination, config_parse_destination, 0, 0
-Route.Source, config_parse_destination, 0, 0
-Route.Metric, config_parse_route_priority, 0, 0
-Route.Scope, config_parse_route_scope, 0, 0
-DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
-DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, hostname)
-DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
-DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
-DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone)
-DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
-DHCPServer.DefaultLeaseTimeSec,config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
-DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
-DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0
-DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp)
-DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0
-DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
-DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
-DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
-DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
-Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
-Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu)
-Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin)
-Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave)
-Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root)
-Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood)
-BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
-BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
+Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac)
+Match.Path, config_parse_strv, 0, offsetof(Network, match_path)
+Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver)
+Match.Type, config_parse_strv, 0, offsetof(Network, match_type)
+Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
+Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
+Network.Description, config_parse_string, 0, offsetof(Network, description)
+Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
+Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
+Network.VLAN, config_parse_netdev, 0, 0
+Network.MACVLAN, config_parse_netdev, 0, 0
+Network.MACVTAP, config_parse_netdev, 0, 0
+Network.IPVLAN, config_parse_netdev, 0, 0
+Network.VXLAN, config_parse_netdev, 0, 0
+Network.Tunnel, config_parse_tunnel, 0, 0
+Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
+Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
+Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local)
+Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
+Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
+Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
+Network.Address, config_parse_address, 0, 0
+Network.Gateway, config_parse_gateway, 0, 0
+Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
+Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
+Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr)
+Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
+Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
+Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
+Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
+Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
+Address.Address, config_parse_address, 0, 0
+Address.Peer, config_parse_address, 0, 0
+Address.Broadcast, config_parse_broadcast, 0, 0
+Address.Label, config_parse_label, 0, 0
+Route.Gateway, config_parse_gateway, 0, 0
+Route.Destination, config_parse_destination, 0, 0
+Route.Source, config_parse_destination, 0, 0
+Route.Metric, config_parse_route_priority, 0, 0
+Route.Scope, config_parse_route_scope, 0, 0
+DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
+DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
+DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
+DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, hostname)
+DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
+DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone)
+DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
+DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
+DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
+DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0
+DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp)
+DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0
+DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
+DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
+DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
+DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
+Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
+Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu)
+Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin)
+Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave)
+Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root)
+Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood)
+BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
+BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
/* backwards compatibility: do not add new entries to this section */
-Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
+Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
+DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index ee14401982..57495b58e0 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -120,6 +120,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->link_local = ADDRESS_FAMILY_IPV6;
network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
+ network->ipv6_accept_ra = -1;
r = config_parse(NULL, filename, file,
"Match\0"
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index c3439a70ba..2a43b6b347 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -120,6 +120,8 @@ struct Network {
AddressFamilyBoolean ip_forward;
bool ip_masquerade;
+ int ipv6_accept_ra;
+
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
diff --git a/src/notify/notify.c b/src/notify/notify.c
index c303bcf718..772e3db69d 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -191,7 +191,7 @@ int main(int argc, char* argv[]) {
goto finish;
}
- r = sd_pid_notify(arg_pid, false, n);
+ r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
if (r < 0) {
log_error_errno(r, "Failed to notify init system: %m");
goto finish;
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 12c17003e9..bf1b7c8ab4 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -191,7 +191,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
/* This has a cname? Then update the query with the
* new cname. */
- r = dns_query_cname_redirect(q, cname->cname.name);
+ r = dns_query_cname_redirect(q, cname);
if (r < 0) {
if (r == -ELOOP)
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
@@ -220,8 +220,6 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
added++;
}
- // what about the cache?
-
/* If we didn't find anything, then let's restart the
* query, this time with the cname */
if (added <= 0) {
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 13ad4ca6bd..89b9b0e1ea 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -149,6 +149,19 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
return 0;
}
+int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa) {
+ if (soa->class != DNS_CLASS_IN)
+ return 0;
+
+ if (soa->type != DNS_TYPE_SOA)
+ return 0;
+
+ if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
+ return 0;
+
+ return 1;
+}
+
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
unsigned i;
@@ -164,13 +177,7 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r
for (i = 0; i < a->n_rrs; i++) {
- if (a->items[i].rr->key->class != DNS_CLASS_IN)
- continue;
-
- if (a->items[i].rr->key->type != DNS_TYPE_SOA)
- continue;
-
- if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) {
+ if (dns_answer_match_soa(key, a->items[i].rr->key)) {
*ret = a->items[i].rr;
return 1;
}
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 0757dd60d0..044d73b19c 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -49,6 +49,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a);
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl);
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
+int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa);
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index cbbbed8c8a..ab13636bc1 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -277,13 +277,14 @@ static int dns_cache_put_positive(
/* New TTL is 0? Delete the entry... */
if (rr->ttl <= 0) {
- if (dns_cache_remove(c, rr->key)) {
- r = dns_resource_key_to_string(rr->key, &key_str);
- if (r < 0)
- return r;
+ r = dns_resource_key_to_string(rr->key, &key_str);
+ if (r < 0)
+ return r;
+ if (dns_cache_remove(c, rr->key))
log_debug("Removed zero TTL entry from cache: %s", key_str);
- }
+ else
+ log_debug("Not caching zero TTL cache entry: %s", key_str);
return 0;
}
@@ -361,7 +362,7 @@ static int dns_cache_put_negative(
if (r < 0)
return r;
- log_debug("Ignored negative cache entry with zero SOA TTL: %s", key_str);
+ log_debug("Not caching negative entry with zero SOA TTL: %s", key_str);
return 0;
}
@@ -402,7 +403,7 @@ static int dns_cache_put_negative(
int dns_cache_put(
DnsCache *c,
- DnsQuestion *q,
+ DnsResourceKey *key,
int rcode,
DnsAnswer *answer,
unsigned max_rrs,
@@ -410,16 +411,16 @@ int dns_cache_put(
int owner_family,
const union in_addr_union *owner_address) {
+ DnsResourceRecord *soa = NULL;
unsigned cache_keys, i;
int r;
assert(c);
- if (q) {
- /* First, if we were passed a question, delete all matching old RRs,
+ if (key) {
+ /* First, if we were passed a key, delete all matching old RRs,
* so that we only keep complete by_key in place. */
- for (i = 0; i < q->n_keys; i++)
- dns_cache_remove(c, q->keys[i]);
+ dns_cache_remove(c, key);
}
if (!answer)
@@ -437,8 +438,8 @@ int dns_cache_put(
cache_keys = answer->n_rrs;
- if (q)
- cache_keys += q->n_keys;
+ if (key)
+ cache_keys ++;
/* Make some space for our new entries */
dns_cache_make_space(c, cache_keys);
@@ -453,44 +454,63 @@ int dns_cache_put(
goto fail;
}
- if (!q)
+ if (!key)
return 0;
- /* Third, add in negative entries for all keys with no RR */
- for (i = 0; i < q->n_keys; i++) {
- DnsResourceRecord *soa = NULL;
+ /* Third, add in negative entries if the key has no RR */
+ r = dns_answer_contains(answer, key);
+ if (r < 0)
+ goto fail;
+ if (r > 0)
+ return 0;
- r = dns_answer_contains(answer, q->keys[i]);
- if (r < 0)
- goto fail;
- if (r > 0)
- continue;
+ /* See https://tools.ietf.org/html/rfc2308, which
+ * say that a matching SOA record in the packet
+ * is used to to enable negative caching. */
- /* See https://tools.ietf.org/html/rfc2308, which
- * say that a matching SOA record in the packet
- * is used to to enable negative caching. */
+ r = dns_answer_find_soa(answer, key, &soa);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
+ return 0;
- r = dns_answer_find_soa(answer, q->keys[i], &soa);
- if (r < 0)
- goto fail;
- if (r == 0)
- continue;
+ /* Also, if the requested key is an alias, the negative response should
+ be cached for each name in the redirect chain. Any CNAME record in
+ the response is from the redirection chain, though only the final one
+ is guaranteed to be included. This means that we cannot verify the
+ chain and that we need to cache them all as it may be incomplete. */
+ for (i = 0; i < answer->n_rrs; i++) {
+ DnsResourceRecord *answer_rr = answer->items[i].rr;
- r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
- if (r < 0)
- goto fail;
+ if (answer_rr->key->type == DNS_TYPE_CNAME) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *canonical_key = NULL;
+
+ canonical_key = dns_resource_key_new_redirect(key, answer_rr);
+ if (!canonical_key)
+ goto fail;
+
+ /* Let's not add negative cache entries for records outside the current zone. */
+ if (!dns_answer_match_soa(canonical_key, soa->key))
+ continue;
+
+ r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+ if (r < 0)
+ goto fail;
+ }
}
+ r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+ if (r < 0)
+ goto fail;
+
return 0;
fail:
/* Adding all RRs failed. Let's clean up what we already
* added, just in case */
- if (q) {
- for (i = 0; i < q->n_keys; i++)
- dns_cache_remove(c, q->keys[i]);
- }
+ if (key)
+ dns_cache_remove(c, key);
for (i = 0; i < answer->n_rrs; i++)
dns_cache_remove(c, answer->items[i].rr->key);
@@ -498,6 +518,29 @@ fail:
return r;
}
+static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL;
+ DnsCacheItem *i, *j;
+
+ assert(c);
+ assert(k);
+
+ i = hashmap_get(c->by_key, k);
+ if (i || k->type == DNS_TYPE_CNAME)
+ return i;
+
+ /* check if we have a CNAME record instead */
+ cname_key = dns_resource_key_new_cname(k);
+ if (!cname_key)
+ return NULL;
+
+ j = hashmap_get(c->by_key, cname_key);
+ if (j)
+ return j;
+
+ return i;
+}
+
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
unsigned n = 0;
@@ -527,7 +570,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
return 0;
}
- first = hashmap_get(c->by_key, key);
+ first = dns_cache_get_by_key_follow_cname(c, key);
if (!first) {
/* If one question cannot be answered we need to refresh */
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
index 1225e58de4..60cf6a4784 100644
--- a/src/resolve/resolved-dns-cache.h
+++ b/src/resolve/resolved-dns-cache.h
@@ -39,7 +39,7 @@ typedef struct DnsCache {
void dns_cache_flush(DnsCache *c);
void dns_cache_prune(DnsCache *c);
-int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
+int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer);
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index c0b4c8ba81..4b1d18b2ef 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -831,7 +831,7 @@ void dns_query_ready(DnsQuery *q) {
dns_query_complete(q, state);
}
-int dns_query_cname_redirect(DnsQuery *q, const char *name) {
+int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
_cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
int r;
@@ -840,7 +840,7 @@ int dns_query_cname_redirect(DnsQuery *q, const char *name) {
if (q->n_cname_redirects > CNAME_MAX)
return -ELOOP;
- r = dns_question_cname_redirect(q->question, name, &nq);
+ r = dns_question_cname_redirect(q->question, cname, &nq);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 93d49301fa..e7063d9678 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -72,7 +72,7 @@ DnsQuery *dns_query_free(DnsQuery *q);
int dns_query_go(DnsQuery *q);
void dns_query_ready(DnsQuery *q);
-int dns_query_cname_redirect(DnsQuery *q, const char *name);
+int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname);
int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index c94928d725..1507f22da0 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -242,13 +242,13 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
return 1;
}
-int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) {
+int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
bool same = true;
unsigned i;
int r;
- assert(name);
+ assert(cname);
assert(ret);
if (!q) {
@@ -262,7 +262,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
}
for (i = 0; i < q->n_keys; i++) {
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name);
if (r < 0)
return r;
@@ -286,7 +286,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
for (i = 0; i < q->n_keys; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
- k = dns_resource_key_new(q->keys[i]->class, q->keys[i]->type, name);
+ k = dns_resource_key_new_redirect(q->keys[i], cname);
if (!k)
return -ENOMEM;
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 77de0c7a2c..13cd1f20f3 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -46,6 +46,6 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
-int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret);
+int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index f31644eebc..fd2f53f40b 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -48,6 +48,19 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *
return k;
}
+DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) {
+ assert(key);
+
+ return dns_resource_key_new(key->class, DNS_TYPE_CNAME, DNS_RESOURCE_KEY_NAME(key));
+}
+
+DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
+ assert(key);
+ assert(cname);
+
+ return dns_resource_key_new(key->class, key->type, cname->cname.name);
+}
+
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
DnsResourceKey *k;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 8986a298af..9e2207c0aa 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -177,6 +177,8 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
}
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
+DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
+DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 8092bb514d..b30473dd7e 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -458,7 +458,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
/* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
- dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
+ dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 4dc4ca581d..fdf41cec19 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -23,22 +23,24 @@
#include "sd-daemon.h"
#include "sd-event.h"
-#include "util.h"
-#include "strv.h"
-#include "macro.h"
+#include "sd-bus.h"
+
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-label.h"
+#include "bus-message.h"
+#include "cgroup-util.h"
#include "def.h"
-#include "path-util.h"
+#include "macro.h"
#include "missing.h"
+#include "path-util.h"
#include "set.h"
#include "signal-util.h"
+#include "strv.h"
#include "unit-name.h"
+#include "util.h"
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-label.h"
-#include "bus-message.h"
#include "bus-util.h"
-#include "bus-internal.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -1423,7 +1425,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (STR_IN_SET(field,
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) {
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) {
r = parse_boolean(eq);
if (r < 0) {
@@ -1463,10 +1466,21 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "t", n);
- } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
+ } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
+ uint64_t u;
+
+ r = cg_cpu_shares_parse(eq, &u);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", u);
+
+ } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
uint64_t u;
- r = safe_atou64(eq, &u);
+ r = cg_cpu_shares_parse(eq, &u);
if (r < 0) {
log_error("Failed to parse %s value %s.", field, eq);
return -EINVAL;
diff --git a/src/shared/pty.c b/src/shared/pty.c
deleted file mode 100644
index 35d9ff5f4d..0000000000
--- a/src/shared/pty.c
+++ /dev/null
@@ -1,633 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * PTY
- * A PTY object represents a single PTY connection between a master and a
- * child. The child process is fork()ed so the caller controls what program
- * will be run.
- *
- * Programs like /bin/login tend to perform a vhangup() on their TTY
- * before running the login procedure. This also causes the pty master
- * to get a EPOLLHUP event as long as no client has the TTY opened.
- * This means, we cannot use the TTY connection as reliable way to track
- * the client. Instead, we _must_ rely on the PID of the client to track
- * them.
- * However, this has the side effect that if the client forks and the
- * parent exits, we loose them and restart the client. But this seems to
- * be the expected behavior so we implement it here.
- *
- * Unfortunately, epoll always polls for EPOLLHUP so as long as the
- * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
- * This gets worse if the client closes the TTY but doesn't exit.
- * Therefore, the fd must be edge-triggered in the epoll-set so we
- * only get the events once they change.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "barrier.h"
-#include "macro.h"
-#include "ring.h"
-#include "util.h"
-#include "signal-util.h"
-#include "pty.h"
-
-#define PTY_BUFSIZE 4096
-
-enum {
- PTY_ROLE_UNKNOWN,
- PTY_ROLE_PARENT,
- PTY_ROLE_CHILD,
-};
-
-struct Pty {
- unsigned long ref;
- Barrier barrier;
- int fd;
- pid_t child;
- sd_event_source *fd_source;
- sd_event_source *child_source;
-
- char in_buf[PTY_BUFSIZE];
- Ring out_buf;
-
- pty_event_t event_fn;
- void *event_fn_userdata;
-
- bool needs_requeue : 1;
- unsigned int role : 2;
-};
-
-int pty_new(Pty **out) {
- _pty_unref_ Pty *pty = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- pty = new0(Pty, 1);
- if (!pty)
- return -ENOMEM;
-
- pty->ref = 1;
- pty->fd = -1;
- pty->barrier = (Barrier) BARRIER_NULL;
-
- pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
- if (pty->fd < 0)
- return -errno;
-
- /*
- * The slave-node is initialized to uid/gid of the caller of
- * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is
- * skipped. In that case, grantpt() can overwrite these, but then you
- * have to be root to use chown() (or a pt_chown helper has to be
- * present). In those cases grantpt() really does something,
- * otherwise it's a no-op. We call grantpt() here to try supporting
- * those cases, even though no-one uses that, I guess. If you need other
- * access-rights, set them yourself after this call returns (no, this is
- * not racy, it looks racy, but races regarding your own UID are never
- * important as an attacker could ptrace you; and the slave-pty is also
- * still locked).
- */
- r = grantpt(pty->fd);
- if (r < 0)
- return -errno;
-
- r = barrier_create(&pty->barrier);
- if (r < 0)
- return r;
-
- *out = pty;
- pty = NULL;
- return 0;
-}
-
-Pty *pty_ref(Pty *pty) {
- if (!pty || pty->ref < 1)
- return NULL;
-
- ++pty->ref;
- return pty;
-}
-
-Pty *pty_unref(Pty *pty) {
- if (!pty || pty->ref < 1 || --pty->ref > 0)
- return NULL;
-
- pty_close(pty);
- pty->child_source = sd_event_source_unref(pty->child_source);
- barrier_destroy(&pty->barrier);
- ring_clear(&pty->out_buf);
- free(pty);
-
- return NULL;
-}
-
-Barrier *pty_get_barrier(Pty *pty) {
- assert(pty);
- return &pty->barrier;
-}
-
-bool pty_is_unknown(Pty *pty) {
- return pty && pty->role == PTY_ROLE_UNKNOWN;
-}
-
-bool pty_is_parent(Pty *pty) {
- return pty && pty->role == PTY_ROLE_PARENT;
-}
-
-bool pty_is_child(Pty *pty) {
- return pty && pty->role == PTY_ROLE_CHILD;
-}
-
-bool pty_has_child(Pty *pty) {
- return pty_is_parent(pty) && pty->child > 0;
-}
-
-pid_t pty_get_child(Pty *pty) {
- return pty_has_child(pty) ? pty->child : -ECHILD;
-}
-
-bool pty_is_open(Pty *pty) {
- return pty && pty->fd >= 0;
-}
-
-int pty_get_fd(Pty *pty) {
- assert_return(pty, -EINVAL);
-
- return pty_is_open(pty) ? pty->fd : -EPIPE;
-}
-
-int pty_make_child(Pty *pty) {
- _cleanup_free_ char *slave_name = NULL;
- int r, fd;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty), -EALREADY);
-
- r = ptsname_malloc(pty->fd, &slave_name);
- if (r < 0)
- return -errno;
-
- fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
- if (fd < 0)
- return -errno;
-
- safe_close(pty->fd);
- pty->fd = fd;
- pty->child = getpid();
- pty->role = PTY_ROLE_CHILD;
- barrier_set_role(&pty->barrier, BARRIER_CHILD);
-
- return 0;
-}
-
-int pty_make_parent(Pty *pty, pid_t child) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty), -EALREADY);
-
- pty->child = child;
- pty->role = PTY_ROLE_PARENT;
-
- return 0;
-}
-
-int pty_unlock(Pty *pty) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
-
- return unlockpt(pty->fd) < 0 ? -errno : 0;
-}
-
-int pty_setup_child(Pty *pty) {
- struct termios attr;
- pid_t pid;
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_child(pty), -EINVAL);
- assert_return(pty_is_open(pty), -EALREADY);
-
- r = reset_signal_mask();
- if (r < 0)
- return r;
-
- r = reset_all_signal_handlers();
- if (r < 0)
- return r;
-
- pid = setsid();
- if (pid < 0 && errno != EPERM)
- return -errno;
-
- r = ioctl(pty->fd, TIOCSCTTY, 0);
- if (r < 0)
- return -errno;
-
- r = tcgetattr(pty->fd, &attr);
- if (r < 0)
- return -errno;
-
- /* erase character should be normal backspace, PLEASEEE! */
- attr.c_cc[VERASE] = 010;
- /* always set UTF8 flag */
- attr.c_iflag |= IUTF8;
-
- r = tcsetattr(pty->fd, TCSANOW, &attr);
- if (r < 0)
- return -errno;
-
- if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO ||
- dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO ||
- dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO)
- return -errno;
-
- /* only close FD if it's not a std-fd */
- pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1;
-
- return 0;
-}
-
-void pty_close(Pty *pty) {
- if (!pty_is_open(pty))
- return;
-
- pty->fd_source = sd_event_source_unref(pty->fd_source);
- pty->fd = safe_close(pty->fd);
-}
-
-/*
- * Drain input-queue and dispatch data via the event-handler. Returns <0 on
- * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast
- * enough and there's still data left.
- */
-static int pty_dispatch_read(Pty *pty) {
- unsigned int i;
- ssize_t len;
- int r;
-
- /*
- * We're edge-triggered, means we need to read the whole queue. This,
- * however, might cause us to stall if the writer is faster than we
- * are. Therefore, try reading as much as 8 times (32KiB) and only
- * bail out then.
- */
-
- for (i = 0; i < 8; ++i) {
- len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1);
- if (len < 0) {
- if (errno == EINTR)
- continue;
-
- return (errno == EAGAIN) ? 0 : -errno;
- } else if (len == 0)
- continue;
-
- /* set terminating zero for debugging safety */
- pty->in_buf[len] = 0;
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len);
- if (r < 0)
- return r;
- }
-
- /* still data left, make sure we're queued again */
- pty->needs_requeue = true;
-
- return 1;
-}
-
-/*
- * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the
- * output queue is empty now and 1 if we couldn't empty the output queue fast
- * enough and there's still data left.
- */
-static int pty_dispatch_write(Pty *pty) {
- struct iovec vec[2];
- unsigned int i;
- ssize_t len;
- size_t num;
-
- /*
- * Same as pty_dispatch_read(), we're edge-triggered so we need to call
- * write() until either all data is written or it returns EAGAIN. We
- * call it twice and if it still writes successfully, we reschedule.
- */
-
- for (i = 0; i < 2; ++i) {
- num = ring_peek(&pty->out_buf, vec);
- if (num < 1)
- return 0;
-
- len = writev(pty->fd, vec, (int)num);
- if (len < 0) {
- if (errno == EINTR)
- continue;
-
- return (errno == EAGAIN) ? 1 : -errno;
- } else if (len == 0)
- continue;
-
- ring_pull(&pty->out_buf, (size_t)len);
- }
-
- /* still data left, make sure we're queued again */
- if (ring_get_size(&pty->out_buf) > 0) {
- pty->needs_requeue = true;
- return 1;
- }
-
- return 0;
-}
-
-static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- Pty *pty = userdata;
- int r_hup = 0, r_write = 0, r_read = 0, r;
-
- /*
- * Whenever we encounter I/O errors, we have to make sure to drain the
- * input queue first, before we handle any HUP. A child might send us
- * a message and immediately close the queue. We must not handle the
- * HUP first or we loose data.
- * Therefore, if we read a message successfully, we always return
- * success and wait for the next event-loop iteration. Furthermore,
- * whenever there is a write-error, we must try reading from the input
- * queue even if EPOLLIN is not set. The input might have arrived in
- * between epoll_wait() and write(). Therefore, write-errors are only
- * ever handled if the input-queue is empty. In all other cases they
- * are ignored until either reading fails or the input queue is empty.
- */
-
- if (revents & (EPOLLHUP | EPOLLERR))
- r_hup = -EPIPE;
-
- if (revents & EPOLLOUT)
- r_write = pty_dispatch_write(pty);
-
- /* Awesome! Kernel signals HUP without IN but queues are not empty.. */
- if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) {
- r_read = pty_dispatch_read(pty);
- if (r_read > 0)
- return 0; /* still data left to fetch next round */
- }
-
- if (r_hup < 0 || r_write < 0 || r_read < 0) {
- /* PTY closed and input-queue drained */
- pty_close(pty);
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) {
- Pty *pty = userdata;
- int r;
-
- if (pty->needs_requeue) {
- /*
- * We're edge-triggered. In case we couldn't handle all events
- * or in case new write-data is queued, we set needs_requeue.
- * Before going asleep, we set the io-events *again*. sd-event
- * notices that we're edge-triggered and forwards the call to
- * the kernel even if the events didn't change. The kernel will
- * check the events and re-queue us on the ready queue in case
- * an event is pending.
- */
- r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET);
- if (r >= 0)
- pty->needs_requeue = false;
- }
-
- return 0;
-}
-
-static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) {
- Pty *pty = userdata;
- int r;
-
- pty->child = 0;
-
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si));
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) {
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(event, -EINVAL);
- assert_return(event_fn, -EINVAL);
- assert_return(pty_is_parent(pty), -EINVAL);
-
- pty_detach_event(pty);
-
- if (pty_is_open(pty)) {
- r = sd_event_add_io(event,
- &pty->fd_source,
- pty->fd,
- EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET,
- pty_fd_fn,
- pty);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn);
- if (r < 0)
- goto error;
- }
-
- if (pty_has_child(pty)) {
- r = sd_event_add_child(event,
- &pty->child_source,
- pty->child,
- WEXITED,
- pty_child_fn,
- pty);
- if (r < 0)
- goto error;
- }
-
- pty->event_fn = event_fn;
- pty->event_fn_userdata = event_fn_userdata;
-
- return 0;
-
-error:
- pty_detach_event(pty);
- return r;
-}
-
-void pty_detach_event(Pty *pty) {
- if (!pty)
- return;
-
- pty->child_source = sd_event_source_unref(pty->child_source);
- pty->fd_source = sd_event_source_unref(pty->fd_source);
- pty->event_fn = NULL;
- pty->event_fn_userdata = NULL;
-}
-
-int pty_write(Pty *pty, const void *buf, size_t size) {
- bool was_empty;
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- if (size < 1)
- return 0;
-
- /*
- * Push @buf[0..@size] into the output ring-buffer. In case the
- * ring-buffer wasn't empty beforehand, we're already waiting for
- * EPOLLOUT and we're done. If it was empty, we have to re-queue the
- * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new
- * EPOLLOUT event.
- */
-
- was_empty = ring_get_size(&pty->out_buf) < 1;
-
- r = ring_push(&pty->out_buf, buf, size);
- if (r < 0)
- return r;
-
- if (was_empty)
- pty->needs_requeue = true;
-
- return 0;
-}
-
-int pty_signal(Pty *pty, int sig) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0;
-}
-
-int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) {
- struct winsize ws = {
- .ws_col = term_width,
- .ws_row = term_height,
- };
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- /*
- * This will send SIGWINCH to the pty slave foreground process group.
- * We will also get one, but we don't need it.
- */
- return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0;
-}
-
-pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) {
- _pty_unref_ Pty *pty = NULL;
- int r;
- pid_t pid;
-
- assert_return(out, -EINVAL);
- assert_return((event && event_fn) || (!event && !event_fn), -EINVAL);
-
- r = pty_new(&pty);
- if (r < 0)
- return r;
-
- r = pty_unlock(pty);
- if (r < 0)
- return r;
-
- pid = fork();
- if (pid < 0)
- return -errno;
-
- if (pid == 0) {
- /* child */
-
- r = pty_make_child(pty);
- if (r < 0)
- _exit(-r);
-
- r = pty_setup_child(pty);
- if (r < 0)
- _exit(-r);
-
- /* sync with parent */
- if (!barrier_place_and_sync(&pty->barrier))
- _exit(1);
-
- /* fallthrough and return the child's PTY object */
- } else {
- /* parent */
-
- r = pty_make_parent(pty, pid);
- if (r < 0)
- goto parent_error;
-
- r = pty_resize(pty, initial_term_width, initial_term_height);
- if (r < 0)
- goto parent_error;
-
- if (event) {
- r = pty_attach_event(pty, event, event_fn, event_fn_userdata);
- if (r < 0)
- goto parent_error;
- }
-
- /* sync with child */
- if (!barrier_place_and_sync(&pty->barrier)) {
- r = -ECHILD;
- goto parent_error;
- }
-
- /* fallthrough and return the parent's PTY object */
- }
-
- *out = pty;
- pty = NULL;
- return pid;
-
-parent_error:
- barrier_abort(&pty->barrier);
- waitpid(pty->child, NULL, 0);
- pty->child = 0;
- return r;
-}
diff --git a/src/shared/pty.h b/src/shared/pty.h
deleted file mode 100644
index 63c7db2833..0000000000
--- a/src/shared/pty.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <unistd.h>
-
-#include "barrier.h"
-#include "macro.h"
-#include "sd-event.h"
-
-typedef struct Pty Pty;
-
-enum {
- PTY_CHILD,
- PTY_HUP,
- PTY_DATA,
-};
-
-typedef int (*pty_event_t) (Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size);
-
-int pty_new(Pty **out);
-Pty *pty_ref(Pty *pty);
-Pty *pty_unref(Pty *pty);
-
-#define _pty_unref_ _cleanup_(pty_unrefp)
-DEFINE_TRIVIAL_CLEANUP_FUNC(Pty*, pty_unref);
-
-Barrier *pty_get_barrier(Pty *pty);
-
-bool pty_is_unknown(Pty *pty);
-bool pty_is_parent(Pty *pty);
-bool pty_is_child(Pty *pty);
-bool pty_has_child(Pty *pty);
-pid_t pty_get_child(Pty *pty);
-
-bool pty_is_open(Pty *pty);
-int pty_get_fd(Pty *pty);
-
-int pty_make_child(Pty *pty);
-int pty_make_parent(Pty *pty, pid_t child);
-int pty_unlock(Pty *pty);
-int pty_setup_child(Pty *pty);
-void pty_close(Pty *pty);
-
-int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata);
-void pty_detach_event(Pty *pty);
-
-int pty_write(Pty *pty, const void *buf, size_t size);
-int pty_signal(Pty *pty, int sig);
-int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height);
-
-pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index d21ba9a566..e20fc1bbbb 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3005,6 +3005,7 @@ static int prepare_firmware_setup(sd_bus *bus) {
}
static int start_special(sd_bus *bus, char **args) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
enum action a;
int r;
@@ -3029,6 +3030,31 @@ static int start_special(sd_bus *bus, char **args) {
r = update_reboot_param_file(args[1]);
if (r < 0)
return r;
+ } else if (a == ACTION_EXIT && strv_length(args) > 1) {
+ /* If the exit code is not given on the command line, don't
+ * reset it to zero: just keep it as it might have been set
+ * previously. */
+ uint8_t code = 0;
+
+ r = safe_atou8(args[1], &code);
+ if (r < 0) {
+ log_error("Invalid exit code.");
+ return -EINVAL;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetExitCode",
+ &error,
+ NULL,
+ "y", code);
+ if (r < 0) {
+ log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+ return r;
+ }
}
if (arg_force >= 2 &&
@@ -3107,7 +3133,7 @@ static int check_unit_failed(sd_bus *bus, char **args) {
static int kill_unit(sd_bus *bus, char **args) {
_cleanup_strv_free_ char **names = NULL;
- char **name;
+ char *kill_who = NULL, **name;
int r, q;
assert(bus);
@@ -3118,6 +3144,10 @@ static int kill_unit(sd_bus *bus, char **args) {
if (!arg_kill_who)
arg_kill_who = "all";
+ /* --fail was specified */
+ if (streq(arg_job_mode, "fail"))
+ kill_who = strjoina(arg_kill_who, "-fail", NULL);
+
r = expand_names(bus, args + 1, NULL, &names);
if (r < 0)
log_error_errno(r, "Failed to expand names: %m");
@@ -3133,7 +3163,7 @@ static int kill_unit(sd_bus *bus, char **args) {
"KillUnit",
&error,
NULL,
- "ssi", *names, arg_kill_who, arg_signal);
+ "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal);
if (q < 0) {
log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
if (r == 0)
@@ -6220,7 +6250,7 @@ static void systemctl_help(void) {
" poweroff Shut down and power-off the system\n"
" reboot [ARG] Shut down and reboot the system\n"
" kexec Shut down and reboot the system with kexec\n"
- " exit Request user instance exit\n"
+ " exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root ROOT [INIT] Change to a different root file system\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
@@ -7207,7 +7237,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
{ "default", EQUAL, 1, start_special },
{ "rescue", EQUAL, 1, start_special },
{ "emergency", EQUAL, 1, start_special },
- { "exit", EQUAL, 1, start_special },
+ { "exit", LESS, 2, start_special },
{ "reset-failed", MORE, 1, reset_failed },
{ "enable", MORE, 2, enable_unit, NOBUS },
{ "disable", MORE, 2, enable_unit, NOBUS },
diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h
new file mode 100644
index 0000000000..8844ae848d
--- /dev/null
+++ b/src/systemd/sd-ipv4acd.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4acdfoo
+#define foosdipv4acdfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+ IPV4ACD_EVENT_STOP = 0,
+ IPV4ACD_EVENT_BIND = 1,
+ IPV4ACD_EVENT_CONFLICT = 2,
+};
+
+typedef struct sd_ipv4acd sd_ipv4acd;
+typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_start(sd_ipv4acd *ll);
+int sd_ipv4acd_stop(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+int sd_ipv4acd_new (sd_ipv4acd **ret);
+
+#endif
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index d017158154..9581e99d31 100644
--- a/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/sd-ipv4ll.h
@@ -43,7 +43,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
int sd_ipv4ll_start(sd_ipv4ll *ll);
int sd_ipv4ll_stop(sd_ipv4ll *ll);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 0f4172e722..dd8ab7dcb8 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -137,6 +137,12 @@ static void test_exec_umask(Manager *m) {
test(m, "exec-umask-0177.service", 0, CLD_EXITED);
}
+static void test_exec_runtimedirectory(Manager *m) {
+ test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
+ test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
+ test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
+}
+
int main(int argc, char *argv[]) {
test_function_t tests[] = {
test_exec_workingdirectory,
@@ -150,6 +156,7 @@ int main(int argc, char *argv[]) {
test_exec_group,
test_exec_environment,
test_exec_umask,
+ test_exec_runtimedirectory,
NULL,
};
test_function_t *test = NULL;
@@ -165,6 +172,7 @@ int main(int argc, char *argv[]) {
return EXIT_TEST_SKIP;
}
+ assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(MANAGER_USER, true, &m);
diff --git a/src/test/test-pty.c b/src/test/test-pty.c
deleted file mode 100644
index fbab3d4ebe..0000000000
--- a/src/test/test-pty.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <locale.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "pty.h"
-#include "util.h"
-#include "signal-util.h"
-
-static const char sndmsg[] = "message\n";
-static const char rcvmsg[] = "message\r\n";
-static char rcvbuf[128];
-static size_t rcvsiz = 0;
-static sd_event *event;
-
-static void run_child(Pty *pty) {
- ssize_t r, l;
- char buf[512];
-
- r = read(0, buf, sizeof(buf));
- assert_se((size_t)r == strlen(sndmsg));
- assert_se(!strncmp(buf, sndmsg, r));
-
- l = write(1, buf, r);
- assert_se(l == r);
-}
-
-static int pty_fn(Pty *pty, void *userdata, unsigned int ev, const void *ptr, size_t size) {
- switch (ev) {
- case PTY_DATA:
- assert_se(rcvsiz < strlen(rcvmsg) * 2);
- assert_se(rcvsiz + size < sizeof(rcvbuf));
-
- memcpy(&rcvbuf[rcvsiz], ptr, size);
- rcvsiz += size;
-
- if (rcvsiz >= strlen(rcvmsg) * 2) {
- assert_se(rcvsiz == strlen(rcvmsg) * 2);
- assert_se(!memcmp(rcvbuf, rcvmsg, strlen(rcvmsg)));
- assert_se(!memcmp(&rcvbuf[strlen(rcvmsg)], rcvmsg, strlen(rcvmsg)));
- }
-
- break;
- case PTY_HUP:
- /* This is guaranteed to appear _after_ the input queues are
- * drained! */
- assert_se(rcvsiz == strlen(rcvmsg) * 2);
- break;
- case PTY_CHILD:
- /* this may appear at any time */
- break;
- default:
- assert_se(0);
- break;
- }
-
- /* if we got HUP _and_ CHILD, exit */
- if (pty_get_fd(pty) < 0 && pty_get_child(pty) < 0)
- sd_event_exit(event, 0);
-
- return 0;
-}
-
-static void run_parent(Pty *pty) {
- int r;
-
- /* write message to pty, ECHO mode guarantees that we get it back
- * twice: once via ECHO, once from the run_child() fn */
- assert_se(pty_write(pty, sndmsg, strlen(sndmsg)) >= 0);
-
- r = sd_event_loop(event);
- assert_se(r >= 0);
-}
-
-static void test_pty(void) {
- pid_t pid;
- Pty *pty = NULL;
-
- rcvsiz = 0;
- zero(rcvbuf);
-
- assert_se(sd_event_default(&event) >= 0);
-
- pid = pty_fork(&pty, event, pty_fn, NULL, 80, 25);
- assert_se(pid >= 0);
-
- if (pid == 0) {
- /* child */
- run_child(pty);
- exit(0);
- }
-
- /* parent */
- run_parent(pty);
-
- /* Make sure the PTY recycled the child; yeah, this is racy if the
- * PID was already reused; but that seems fine for a test. */
- assert_se(waitpid(pid, NULL, WNOHANG) < 0 && errno == ECHILD);
-
- pty_unref(pty);
- sd_event_unref(event);
-}
-
-int main(int argc, char *argv[]) {
- unsigned int i;
-
- log_parse_environment();
- log_open();
-
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
-
- /* Oh, there're ugly races in the TTY layer regarding HUP vs IN. Turns
- * out they appear only 10% of the time. I fixed all of them and
- * don't see them, anymore. But let's be safe and run this 1000 times
- * so we catch any new ones, in case they appear again. */
- for (i = 0; i < 1000; ++i)
- test_pty();
-
- return 0;
-}
diff --git a/src/test/test-ring.c b/src/test/test-ring.c
deleted file mode 100644
index cb8a5d4e9e..0000000000
--- a/src/test/test-ring.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-
-#include "def.h"
-#include "ring.h"
-
-static void test_ring(void) {
- static const char buf[8192];
- Ring r;
- size_t l;
- struct iovec vec[2];
- int s;
-
- zero(r);
-
- l = ring_peek(&r, vec);
- assert_se(l == 0);
-
- s = ring_push(&r, buf, 2048);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 2048);
-
- l = ring_peek(&r, vec);
- assert_se(l == 1);
- assert_se(vec[0].iov_len == 2048);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(ring_get_size(&r) == 2048);
-
- ring_pull(&r, 2048);
- assert_se(ring_get_size(&r) == 0);
-
- l = ring_peek(&r, vec);
- assert_se(l == 0);
- assert_se(ring_get_size(&r) == 0);
-
- s = ring_push(&r, buf, 2048);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 2048);
-
- l = ring_peek(&r, vec);
- assert_se(l == 1);
- assert_se(vec[0].iov_len == 2048);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(ring_get_size(&r) == 2048);
-
- s = ring_push(&r, buf, 1);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 2049);
-
- l = ring_peek(&r, vec);
- assert_se(l == 2);
- assert_se(vec[0].iov_len == 2048);
- assert_se(vec[1].iov_len == 1);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(!memcmp(vec[1].iov_base, buf, vec[1].iov_len));
- assert_se(ring_get_size(&r) == 2049);
-
- ring_pull(&r, 2048);
- assert_se(ring_get_size(&r) == 1);
-
- l = ring_peek(&r, vec);
- assert_se(l == 1);
- assert_se(vec[0].iov_len == 1);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(ring_get_size(&r) == 1);
-
- ring_pull(&r, 1);
- assert_se(ring_get_size(&r) == 0);
-
- s = ring_push(&r, buf, 2048);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 2048);
-
- s = ring_push(&r, buf, 2049);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 4097);
-
- l = ring_peek(&r, vec);
- assert_se(l == 1);
- assert_se(vec[0].iov_len == 4097);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(ring_get_size(&r) == 4097);
-
- ring_pull(&r, 1);
- assert_se(ring_get_size(&r) == 4096);
-
- s = ring_push(&r, buf, 4096);
- assert_se(!s);
- assert_se(ring_get_size(&r) == 8192);
-
- l = ring_peek(&r, vec);
- assert_se(l == 2);
- assert_se(vec[0].iov_len == 8191);
- assert_se(vec[1].iov_len == 1);
- assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len));
- assert_se(!memcmp(vec[1].iov_base, buf, vec[1].iov_len));
- assert_se(ring_get_size(&r) == 8192);
-
- ring_clear(&r);
- assert_se(ring_get_size(&r) == 0);
-}
-
-int main(int argc, char *argv[]) {
- log_parse_environment();
- log_open();
-
- test_ring();
-
- return 0;
-}
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 7b4178c993..3cb7d435cd 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -131,7 +131,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_warning_errno(r, "Failed to parse configuration file: %m");
- log_debug("systemd-timesyncd running as pid %lu", (unsigned long) getpid());
+ log_debug("systemd-timesyncd running as pid " PID_FMT, getpid());
sd_notify(false,
"READY=1\n"
"STATUS=Daemon is running");