summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c24
-rw-r--r--src/analyze/analyze-verify.c8
-rw-r--r--src/analyze/analyze-verify.h2
-rw-r--r--src/analyze/analyze.c3
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h4
-rw-r--r--src/basic/c-rbtree.c674
-rw-r--r--src/basic/c-rbtree.h297
-rw-r--r--src/basic/copy.c62
-rw-r--r--src/basic/copy.h1
-rw-r--r--src/basic/def.h2
-rw-r--r--src/basic/dirent-util.c12
-rw-r--r--src/basic/dirent-util.h2
-rw-r--r--src/basic/fd-util.c18
-rw-r--r--src/basic/fd-util.h2
-rw-r--r--src/basic/fdset.c2
-rw-r--r--src/basic/fileio.c124
-rw-r--r--src/basic/fileio.h6
-rw-r--r--src/basic/fs-util.c15
-rw-r--r--src/basic/fs-util.h2
-rw-r--r--src/basic/hashmap.c10
-rw-r--r--src/basic/hostname-util.c10
-rw-r--r--src/basic/locale-util.c2
-rw-r--r--src/basic/missing.h47
-rw-r--r--src/basic/mount-util.c4
-rw-r--r--src/basic/nss-util.h43
-rw-r--r--src/basic/parse-util.h12
-rw-r--r--src/basic/path-util.c73
-rw-r--r--src/basic/path-util.h20
-rw-r--r--src/basic/process-util.c24
-rw-r--r--src/basic/process-util.h6
-rw-r--r--src/basic/rlimit-util.c52
-rw-r--r--src/basic/rm-rf.h9
-rw-r--r--src/basic/signal-util.c2
-rw-r--r--src/basic/signal-util.h4
-rw-r--r--src/basic/string-table.h54
-rw-r--r--src/basic/strv.c36
-rw-r--r--src/basic/strv.h1
-rw-r--r--src/basic/terminal-util.c19
-rw-r--r--src/basic/time-util.c44
-rw-r--r--src/basic/time-util.h1
-rw-r--r--src/basic/user-util.c1
-rw-r--r--src/basic/user-util.h5
-rw-r--r--src/basic/util.c41
-rw-r--r--src/basic/util.h2
-rw-r--r--src/cgls/cgls.c11
-rw-r--r--src/cgtop/cgtop.c2
-rw-r--r--src/core/automount.c5
-rw-r--r--src/core/busname.c17
-rw-r--r--src/core/cgroup.c4
-rw-r--r--src/core/dbus-execute.c8
-rw-r--r--src/core/dbus-kill.c4
-rw-r--r--src/core/dbus-manager.c279
-rw-r--r--src/core/dbus-socket.c2
-rw-r--r--src/core/dbus-timer.c2
-rw-r--r--src/core/dbus-unit.c155
-rw-r--r--src/core/dbus-unit.h1
-rw-r--r--src/core/dbus.c16
-rw-r--r--src/core/device.c6
-rw-r--r--src/core/failure-action.c16
-rw-r--r--src/core/ima-setup.c2
-rw-r--r--src/core/ima-setup.h2
-rw-r--r--src/core/job.c4
-rw-r--r--src/core/load-dropin.c15
-rw-r--r--src/core/load-dropin.h2
-rw-r--r--src/core/load-fragment-gperf.gperf.m45
-rw-r--r--src/core/load-fragment.c52
-rw-r--r--src/core/machine-id-setup.c49
-rw-r--r--src/core/main.c63
-rw-r--r--src/core/manager.c294
-rw-r--r--src/core/manager.h17
-rw-r--r--src/core/mount-setup.c1
-rw-r--r--src/core/mount.c43
-rw-r--r--src/core/org.freedesktop.systemd1.conf16
-rw-r--r--src/core/path.c10
-rw-r--r--src/core/scope.c33
-rw-r--r--src/core/selinux-access.c5
-rw-r--r--src/core/service.c92
-rw-r--r--src/core/service.h3
-rw-r--r--src/core/shutdown.c9
-rw-r--r--src/core/slice.c3
-rw-r--r--src/core/socket.c93
-rw-r--r--src/core/socket.h3
-rw-r--r--src/core/swap.c17
-rw-r--r--src/core/system.conf2
-rw-r--r--src/core/timer.c8
-rw-r--r--src/core/transaction.c4
-rw-r--r--src/core/triggers.systemd.in2
-rw-r--r--src/core/unit-printf.c11
-rw-r--r--src/core/unit.c218
-rw-r--r--src/core/unit.h18
-rw-r--r--src/core/user.conf2
-rw-r--r--src/coredump/coredump.c50
-rw-r--r--src/coredump/coredumpctl.c2
-rw-r--r--src/cryptsetup/cryptsetup.c8
-rw-r--r--src/import/curl-util.c4
-rw-r--r--src/import/import-common.c4
-rw-r--r--src/import/pull-common.c2
-rw-r--r--src/import/pull-job.h9
-rw-r--r--src/journal-remote/journal-gatewayd.c4
-rw-r--r--src/journal-remote/journal-remote-parse.c2
-rw-r--r--src/journal-remote/journal-upload-journal.c19
-rw-r--r--src/journal-remote/journal-upload.c3
-rw-r--r--src/journal-remote/journal-upload.h3
-rw-r--r--src/journal/compress.c10
-rw-r--r--src/journal/fsprg.c2
-rw-r--r--src/journal/journal-file.c161
-rw-r--r--src/journal/journal-file.h3
-rw-r--r--src/journal/journal-internal.h3
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/journal/journal-verify.c6
-rw-r--r--src/journal/journalctl.c212
-rw-r--r--src/journal/journald-gperf.gperf2
-rw-r--r--src/journal/journald-native.c2
-rw-r--r--src/journal/journald-server.c52
-rw-r--r--src/journal/journald.conf2
-rw-r--r--src/journal/sd-journal.c294
-rw-r--r--src/journal/test-compress-benchmark.c2
-rw-r--r--src/journal/test-journal-flush.c2
-rw-r--r--src/journal/test-journal-interleaving.c8
-rw-r--r--src/journal/test-journal-stream.c6
-rw-r--r--src/journal/test-journal-verify.c6
-rw-r--r--src/journal/test-journal.c10
-rw-r--r--src/libsystemd-network/dhcp-identifier.c2
-rw-r--r--src/libsystemd-network/dhcp-identifier.h43
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h7
-rw-r--r--src/libsystemd-network/network-internal.c30
-rw-r--r--src/libsystemd-network/network-internal.h4
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c48
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c48
-rw-r--r--src/libsystemd/libsystemd.sym6
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c2
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h2
-rw-r--r--src/libsystemd/sd-bus/bus-control.c3
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c14
-rw-r--r--src/libsystemd/sd-device/sd-device.c43
-rw-r--r--src/libsystemd/sd-event/sd-event.c15
-rw-r--r--src/libsystemd/sd-event/test-event.c12
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.c3
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c43
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c1
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c5
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c2
-rw-r--r--src/locale/language-fallback-map4
-rw-r--r--src/locale/localed.c76
-rw-r--r--src/login/.gitignore1
-rw-r--r--src/login/loginctl.c78
-rw-r--r--src/login/logind-core.c8
-rw-r--r--src/login/logind-dbus.c8
-rw-r--r--src/login/logind-session-dbus.c3
-rw-r--r--src/login/logind-session.c4
-rw-r--r--src/login/logind-user-dbus.c3
-rw-r--r--src/login/logind.c82
-rw-r--r--src/login/logind.conf.in (renamed from src/login/logind.conf)2
-rw-r--r--src/login/org.freedesktop.login1.policy.in8
-rw-r--r--src/login/systemd-user.m41
-rw-r--r--src/machine/image-dbus.c73
-rw-r--r--src/machine/machine-dbus.c161
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine.c24
-rw-r--r--src/machine/machine.h20
-rw-r--r--src/machine/machinectl.c259
-rw-r--r--src/machine/machined-dbus.c109
-rw-r--r--src/machine/machined.c8
-rw-r--r--src/machine/machined.h4
-rw-r--r--src/machine/operation.c131
-rw-r--r--src/machine/operation.h47
-rw-r--r--src/network/.gitignore1
-rw-r--r--src/network/networkd-address-pool.c6
-rw-r--r--src/network/networkd-address.c7
-rw-r--r--src/network/networkd-conf.c171
-rw-r--r--src/network/networkd-conf.h34
-rw-r--r--src/network/networkd-dhcp4.c20
-rw-r--r--src/network/networkd-dhcp6.c17
-rw-r--r--src/network/networkd-gperf.gperf18
-rw-r--r--src/network/networkd-link.c168
-rw-r--r--src/network/networkd-lldp-tx.c9
-rw-r--r--src/network/networkd-manager.c2
-rw-r--r--src/network/networkd-netdev-bridge.c17
-rw-r--r--src/network/networkd-netdev-bridge.h2
-rw-r--r--src/network/networkd-netdev-gperf.gperf1
-rw-r--r--src/network/networkd-network-gperf.gperf5
-rw-r--r--src/network/networkd-network.c15
-rw-r--r--src/network/networkd-network.h11
-rw-r--r--src/network/networkd-route.c15
-rw-r--r--src/network/networkd.c5
-rw-r--r--src/network/networkd.h8
-rw-r--r--src/network/test-network-tables.c2
-rw-r--r--src/nspawn/nspawn-gperf.gperf4
-rw-r--r--src/nspawn/nspawn-mount.c23
-rw-r--r--src/nspawn/nspawn-network.c47
-rw-r--r--src/nspawn/nspawn-network.h2
-rw-r--r--src/nspawn/nspawn-patch-uid.c469
-rw-r--r--src/nspawn/nspawn-patch-uid.h23
-rw-r--r--src/nspawn/nspawn-register.c1
-rw-r--r--src/nspawn/nspawn-settings.c86
-rw-r--r--src/nspawn/nspawn-settings.h15
-rw-r--r--src/nspawn/nspawn.c474
-rw-r--r--src/nspawn/test-patch-uid.c61
-rw-r--r--src/nss-myhostname/nss-myhostname.c35
-rw-r--r--src/nss-resolve/nss-resolve.c79
-rw-r--r--src/resolve/RFCs2
-rw-r--r--src/resolve/resolved-bus.c5
-rw-r--r--src/resolve/resolved-dns-dnssec.c2
-rw-r--r--src/resolve/resolved-dns-transaction.c4
-rw-r--r--src/resolve/resolved-link.c2
-rw-r--r--src/run/run.c30
-rw-r--r--src/shared/bus-unit-util.c1287
-rw-r--r--src/shared/bus-unit-util.h57
-rw-r--r--src/shared/bus-util.c902
-rw-r--r--src/shared/bus-util.h37
-rw-r--r--src/shared/cgroup-show.c92
-rw-r--r--src/shared/cgroup-show.h8
-rw-r--r--src/shared/conf-parser.c2
-rw-r--r--src/shared/dropin.c2
-rw-r--r--src/shared/firewall-util.c12
-rw-r--r--src/shared/install.c1090
-rw-r--r--src/shared/install.h131
-rw-r--r--src/shared/logs-show.c49
-rw-r--r--src/shared/logs-show.h3
-rw-r--r--src/shared/machine-image.c34
-rw-r--r--src/shared/machine-image.h26
-rw-r--r--src/shared/output-mode.c37
-rw-r--r--src/shared/output-mode.h11
-rw-r--r--src/shared/path-lookup.c807
-rw-r--r--src/shared/path-lookup.h78
-rw-r--r--src/shared/sleep-config.c4
-rw-r--r--src/shared/spawn-polkit-agent.c4
-rw-r--r--src/shared/tests.c33
-rw-r--r--src/shared/tests.h22
-rw-r--r--src/systemctl/systemctl.c547
-rw-r--r--src/systemd/sd-dhcp-client.h2
-rw-r--r--src/systemd/sd-dhcp6-client.h5
-rw-r--r--src/systemd/sd-journal.h13
-rw-r--r--src/systemd/sd-lldp.h75
-rw-r--r--src/sysv-generator/sysv-generator.c55
-rw-r--r--src/test/test-cgroup-mask.c8
-rw-r--r--src/test/test-copy.c31
-rw-r--r--src/test/test-engine.c7
-rw-r--r--src/test/test-execute.c8
-rw-r--r--src/test/test-install-root.c60
-rw-r--r--src/test/test-install.c44
-rw-r--r--src/test/test-namespace.c9
-rw-r--r--src/test/test-nss.c454
-rw-r--r--src/test/test-path-lookup.c40
-rw-r--r--src/test/test-path-util.c34
-rw-r--r--src/test/test-path.c10
-rw-r--r--src/test/test-rbtree.c362
-rw-r--r--src/test/test-rlimit-util.c12
-rw-r--r--src/test/test-sched-prio.c7
-rw-r--r--src/test/test-strv.c10
-rw-r--r--src/test/test-tmpfiles.c26
-rw-r--r--src/test/test-udev.c44
-rw-r--r--src/test/test-unit-file.c9
-rw-r--r--src/test/test-unit-name.c2
-rw-r--r--src/tmpfiles/tmpfiles.c88
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
-rw-r--r--src/udev/udev-builtin-net_id.c28
-rw-r--r--src/udev/udevadm-monitor.c2
-rw-r--r--src/udev/udevd.c7
260 files changed, 9312 insertions, 5069 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 8ac8dd8e72..a0cfc22000 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -316,19 +316,31 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) {
}
/* SIGCHLD handler. */
-static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
+static void sigchld_hdl(int sig) {
PROTECT_ERRNO;
- log_info("Child %d died with code %d", t->si_pid, t->si_status);
+ for (;;) {
+ siginfo_t si;
+ int r;
- /* Wait for a dead child. */
- (void) waitpid(t->si_pid, NULL, 0);
+ si.si_pid = 0;
+ r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG);
+ if (r < 0) {
+ if (errno != ECHILD)
+ log_error_errno(errno, "Failed to reap children: %m");
+ return;
+ }
+ if (si.si_pid == 0)
+ return;
+
+ log_info("Child %d died with code %d", si.si_pid, si.si_status);
+ }
}
static int install_chld_handler(void) {
static const struct sigaction act = {
- .sa_flags = SA_SIGINFO,
- .sa_sigaction = sigchld_hdl,
+ .sa_flags = SA_NOCLDSTOP,
+ .sa_handler = sigchld_hdl,
};
int r;
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index b83f559e7d..5fd3ee49eb 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -231,14 +231,12 @@ static int verify_unit(Unit *u, bool check_man) {
return r;
}
-int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) {
+int verify_units(char **filenames, UnitFileScope scope, bool check_man) {
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *var = NULL;
Manager *m = NULL;
FILE *serial = NULL;
FDSet *fdset = NULL;
-
- _cleanup_free_ char *var = NULL;
-
char **filename;
int r = 0, k;
@@ -255,7 +253,7 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man)
assert_se(set_unit_path(var) >= 0);
- r = manager_new(running_as, true, &m);
+ r = manager_new(scope, true, &m);
if (r < 0)
return log_error_errno(r, "Failed to initialize manager: %m");
diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h
index 27c253a562..d8204dc69c 100644
--- a/src/analyze/analyze-verify.h
+++ b/src/analyze/analyze-verify.h
@@ -23,4 +23,4 @@
#include "path-lookup.h"
-int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man);
+int verify_units(char **filenames, UnitFileScope scope, bool check_man);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 42754a2741..a790ccd33e 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "analyze-verify.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "glob-util.h"
#include "hashmap.h"
@@ -1443,7 +1444,7 @@ int main(int argc, char *argv[]) {
if (streq_ptr(argv[optind], "verify"))
r = verify_units(argv+optind+1,
- arg_user ? MANAGER_USER : MANAGER_SYSTEM,
+ arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
arg_man);
else {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index a9ecfc1cd6..8e2c2b02d2 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -121,6 +121,8 @@ int uname_architecture(void) {
{ "tilegx", ARCHITECTURE_TILEGX },
#elif defined(__cris__)
{ "crisv32", ARCHITECTURE_CRIS },
+#elif defined(__nios2__)
+ { "nios2", ARCHITECTURE_NIOS2 },
#else
#error "Please register your architecture here!"
#endif
@@ -171,6 +173,7 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = {
[ARCHITECTURE_M68K] = "m68k",
[ARCHITECTURE_TILEGX] = "tilegx",
[ARCHITECTURE_CRIS] = "cris",
+ [ARCHITECTURE_NIOS2] = "nios2",
};
DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index c22cbc8279..91ec108e04 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -57,6 +57,7 @@ enum {
ARCHITECTURE_M68K,
ARCHITECTURE_TILEGX,
ARCHITECTURE_CRIS,
+ ARCHITECTURE_NIOS2,
_ARCHITECTURE_MAX,
_ARCHITECTURE_INVALID = -1
};
@@ -187,6 +188,9 @@ int uname_architecture(void);
#elif defined(__cris__)
# define native_architecture() ARCHITECTURE_CRIS
# error "Missing LIB_ARCH_TUPLE for CRIS"
+#elif defined(__nios2__)
+# define native_architecture() ARCHITECTURE_NIOS2
+# define LIB_ARCH_TUPLE "nios2-linux-gnu"
#else
# error "Please register your architecture here!"
#endif
diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c
deleted file mode 100644
index cf5a7242df..0000000000
--- a/src/basic/c-rbtree.c
+++ /dev/null
@@ -1,674 +0,0 @@
-/***
- This file is part of systemd. See COPYING for details.
-
- 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/>.
-***/
-
-/*
- * RB-Tree Implementation
- * This implements the insertion/removal of elements in RB-Trees. You're highly
- * recommended to have an RB-Tree documentation at hand when reading this. Both
- * insertion and removal can be split into a handful of situations that can
- * occur. Those situations are enumerated as "Case 1" to "Case n" here, and
- * follow closely the cases described in most RB-Tree documentations. This file
- * does not explain why it is enough to handle just those cases, nor does it
- * provide a proof of correctness. Dig out your algorithm 101 handbook if
- * you're interested.
- *
- * This implementation is *not* straightforward. Usually, a handful of
- * rotation, reparent, swap and link helpers can be used to implement the
- * rebalance operations. However, those often perform unnecessary writes.
- * Therefore, this implementation hard-codes all the operations. You're highly
- * recommended to look at the two basic helpers before reading the code:
- * c_rbtree_swap_child()
- * c_rbtree_set_parent_and_color()
- * Those are the only helpers used, hence, you should really know what they do
- * before digging into the code.
- *
- * For a highlevel documentation of the API, see the header file and docbook
- * comments.
- */
-
-#include <assert.h>
-#include <stddef.h>
-#include "c-rbtree.h"
-
-enum {
- C_RBNODE_RED = 0,
- C_RBNODE_BLACK = 1,
-};
-
-static inline unsigned long c_rbnode_color(CRBNode *n) {
- return (unsigned long)n->__parent_and_color & 1UL;
-}
-
-static inline _Bool c_rbnode_is_red(CRBNode *n) {
- return c_rbnode_color(n) == C_RBNODE_RED;
-}
-
-static inline _Bool c_rbnode_is_black(CRBNode *n) {
- return c_rbnode_color(n) == C_RBNODE_BLACK;
-}
-
-/**
- * c_rbnode_leftmost() - return leftmost child
- * @n: current node, or NULL
- *
- * This returns the leftmost child of @n. If @n is NULL, this will return NULL.
- * In all other cases, this function returns a valid pointer. That is, if @n
- * does not have any left children, this returns @n.
- *
- * Worst case runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to leftmost child, or NULL.
- */
-CRBNode *c_rbnode_leftmost(CRBNode *n) {
- if (n)
- while (n->left)
- n = n->left;
- return n;
-}
-
-/**
- * c_rbnode_rightmost() - return rightmost child
- * @n: current node, or NULL
- *
- * This returns the rightmost child of @n. If @n is NULL, this will return
- * NULL. In all other cases, this function returns a valid pointer. That is, if
- * @n does not have any right children, this returns @n.
- *
- * Worst case runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to rightmost child, or NULL.
- */
-CRBNode *c_rbnode_rightmost(CRBNode *n) {
- if (n)
- while (n->right)
- n = n->right;
- return n;
-}
-
-/**
- * c_rbnode_next() - return next node
- * @n: current node, or NULL
- *
- * An RB-Tree always defines a linear order of its elements. This function
- * returns the logically next node to @n. If @n is NULL, the last node or
- * unlinked, this returns NULL.
- *
- * Worst case runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to next node, or NULL.
- */
-CRBNode *c_rbnode_next(CRBNode *n) {
- CRBNode *p;
-
- if (!c_rbnode_is_linked(n))
- return NULL;
- if (n->right)
- return c_rbnode_leftmost(n->right);
-
- while ((p = c_rbnode_parent(n)) && n == p->right)
- n = p;
-
- return p;
-}
-
-/**
- * c_rbnode_prev() - return previous node
- * @n: current node, or NULL
- *
- * An RB-Tree always defines a linear order of its elements. This function
- * returns the logically previous node to @n. If @n is NULL, the first node or
- * unlinked, this returns NULL.
- *
- * Worst case runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to previous node, or NULL.
- */
-CRBNode *c_rbnode_prev(CRBNode *n) {
- CRBNode *p;
-
- if (!c_rbnode_is_linked(n))
- return NULL;
- if (n->left)
- return c_rbnode_rightmost(n->left);
-
- while ((p = c_rbnode_parent(n)) && n == p->left)
- n = p;
-
- return p;
-}
-
-/**
- * c_rbtree_first() - return first node
- * @t: tree to operate on
- *
- * An RB-Tree always defines a linear order of its elements. This function
- * returns the logically first node in @t. If @t is empty, NULL is returned.
- *
- * Fixed runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to first node, or NULL.
- */
-CRBNode *c_rbtree_first(CRBTree *t) {
- assert(t);
- return c_rbnode_leftmost(t->root);
-}
-
-/**
- * c_rbtree_last() - return last node
- * @t: tree to operate on
- *
- * An RB-Tree always defines a linear order of its elements. This function
- * returns the logically last node in @t. If @t is empty, NULL is returned.
- *
- * Fixed runtime (n: number of elements in tree): O(log(n))
- *
- * Return: Pointer to last node, or NULL.
- */
-CRBNode *c_rbtree_last(CRBTree *t) {
- assert(t);
- return c_rbnode_rightmost(t->root);
-}
-
-/*
- * Set the color and parent of a node. This should be treated as a simple
- * assignment of the 'color' and 'parent' fields of the node. No other magic is
- * applied. But since both fields share its backing memory, this helper
- * function is provided.
- */
-static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) {
- assert(!((unsigned long)p & 1));
- assert(c < 2);
- n->__parent_and_color = (CRBNode*)((unsigned long)p | c);
-}
-
-/* same as c_rbnode_set_parent_and_color(), but keeps the current color */
-static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) {
- c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n));
-}
-
-/*
- * This function partially replaces an existing child pointer to a new one. The
- * existing child must be given as @old, the new child as @new. @p must be the
- * parent of @old (or NULL if it has no parent).
- * This function ensures that the parent of @old now points to @new. However,
- * it does *NOT* change the parent pointer of @new. The caller must ensure
- * this.
- * If @p is NULL, this function ensures that the root-pointer is adjusted
- * instead (given as @t).
- */
-static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) {
- if (p) {
- if (p->left == old)
- p->left = new;
- else
- p->right = new;
- } else {
- t->root = new;
- }
-}
-
-static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) {
- CRBNode *p, *g, *gg, *u, *x;
-
- /*
- * Paint a single node according to RB-Tree rules. The node must
- * already be linked into the tree and painted red.
- * We repaint the node or rotate the tree, if required. In case a
- * recursive repaint is required, the next node to be re-painted
- * is returned.
- * p: parent
- * g: grandparent
- * gg: grandgrandparent
- * u: uncle
- * x: temporary
- */
-
- /* node is red, so we can access the parent directly */
- p = n->__parent_and_color;
-
- if (!p) {
- /* Case 1:
- * We reached the root. Mark it black and be done. As all
- * leaf-paths share the root, the ratio of black nodes on each
- * path stays the same. */
- c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK);
- n = NULL;
- } else if (c_rbnode_is_black(p)) {
- /* Case 2:
- * The parent is already black. As our node is red, we did not
- * change the number of black nodes on any path, nor do we have
- * multiple consecutive red nodes. */
- n = NULL;
- } else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */
- g = p->__parent_and_color;
- gg = c_rbnode_parent(g);
- u = g->right;
-
- if (u && c_rbnode_is_red(u)) {
- /* Case 3:
- * Parent and uncle are both red. We know the
- * grandparent must be black then. Repaint parent and
- * uncle black, the grandparent red and recurse into
- * the grandparent. */
- c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
- n = g;
- } else {
- /* parent is red, uncle is black */
-
- if (n == p->right) {
- /* Case 4:
- * We're the right child. Rotate on parent to
- * become left child, so we can handle it the
- * same as case 5. */
- x = n->left;
- p->right = n->left;
- n->left = p;
- if (x)
- c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
- p = n;
- }
-
- /* 'n' is invalid from here on! */
- n = NULL;
-
- /* Case 5:
- * We're the red left child or a red parent, black
- * grandparent and uncle. Rotate on grandparent and
- * switch color with parent. Number of black nodes on
- * each path stays the same, but we got rid of the
- * double red path. As the grandparent is still black,
- * we're done. */
- x = p->right;
- g->left = x;
- p->right = g;
- if (x)
- c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
- c_rbtree_swap_child(t, gg, g, p);
- }
- } else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */
- g = p->__parent_and_color;
- gg = c_rbnode_parent(g);
- u = g->left;
-
- if (u && c_rbnode_is_red(u)) {
- c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
- n = g;
- } else {
- if (n == p->left) {
- x = n->right;
- p->left = n->right;
- n->right = p;
- if (x)
- c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
- p = n;
- }
-
- n = NULL;
-
- x = p->left;
- g->right = x;
- p->left = g;
- if (x)
- c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
- c_rbtree_swap_child(t, gg, g, p);
- }
- }
-
- return n;
-}
-
-static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) {
- assert(t);
- assert(n);
-
- while (n)
- n = c_rbtree_paint_one(t, n);
-}
-
-/**
- * c_rbtree_add() - add node to tree
- * @t: tree to operate one
- * @p: parent node to link under, or NULL
- * @l: left/right slot of @p (or root) to link at
- * @n: node to add
- *
- * This links @n into the tree given as @t. The caller must provide the exact
- * spot where to link the node. That is, the caller must traverse the tree
- * based on their search order. Once they hit a leaf where to insert the node,
- * call this function to link it and rebalance the tree.
- *
- * A typical insertion would look like this (@t is your tree, @n is your node):
- *
- * CRBNode **i, *p;
- *
- * i = &t->root;
- * p = NULL;
- * while (*i) {
- * p = *i;
- * if (compare(n, *i) < 0)
- * i = &(*i)->left;
- * else
- * i = &(*i)->right;
- * }
- *
- * c_rbtree_add(t, p, i, n);
- *
- * Once the node is linked into the tree, a simple lookup on the same tree can
- * be coded like this:
- *
- * CRBNode *i;
- *
- * i = t->root;
- * while (i) {
- * int v = compare(n, i);
- * if (v < 0)
- * i = (*i)->left;
- * else if (v > 0)
- * i = (*i)->right;
- * else
- * break;
- * }
- *
- * When you add nodes to a tree, the memory contents of the node do not matter.
- * That is, there is no need to initialize the node via c_rbnode_init().
- * However, if you relink nodes multiple times during their lifetime, it is
- * usually very convenient to use c_rbnode_init() and c_rbtree_remove_init().
- * In those cases, you should validate that a node is unlinked before you call
- * c_rbtree_add().
- */
-void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) {
- assert(t);
- assert(l);
- assert(n);
- assert(!p || l == &p->left || l == &p->right);
- assert(p || l == &t->root);
-
- c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED);
- n->left = n->right = NULL;
- *l = n;
-
- c_rbtree_paint(t, n);
-}
-
-static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) {
- CRBNode *s, *x, *y, *g;
-
- /*
- * Rebalance tree after a node was removed. This happens only if you
- * remove a black node and one path is now left with an unbalanced
- * number or black nodes.
- * This function assumes all paths through p and n have one black node
- * less than all other paths. If recursive fixup is required, the
- * current node is returned.
- */
-
- if (n == p->left) {
- s = p->right;
- if (c_rbnode_is_red(s)) {
- /* Case 3:
- * We have a red node as sibling. Rotate it onto our
- * side so we can later on turn it black. This way, we
- * gain the additional black node in our path. */
- g = c_rbnode_parent(p);
- x = s->left;
- p->right = x;
- s->left = p;
- c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
- c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
- c_rbtree_swap_child(t, g, p, s);
- s = x;
- }
-
- x = s->right;
- if (!x || c_rbnode_is_black(x)) {
- y = s->left;
- if (!y || c_rbnode_is_black(y)) {
- /* Case 4:
- * Our sibling is black and has only black
- * children. Flip it red and turn parent black.
- * This way we gained a black node in our path,
- * or we fix it recursively one layer up, which
- * will rotate the red sibling as parent. */
- c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
- if (c_rbnode_is_black(p))
- return p;
-
- c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
- return NULL;
- }
-
- /* Case 5:
- * Left child of our sibling is red, right one is black.
- * Rotate on parent so the right child of our sibling is
- * now red, and we can fall through to case 6. */
- x = y->right;
- s->left = y->right;
- y->right = s;
- p->right = y;
- if (x)
- c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
- x = s;
- s = y;
- }
-
- /* Case 6:
- * The right child of our sibling is red. Rotate left and flip
- * colors, which gains us an additional black node in our path,
- * that was previously on our sibling. */
- g = c_rbnode_parent(p);
- y = s->left;
- p->right = y;
- s->left = p;
- c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
- if (y)
- c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
- c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
- c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
- c_rbtree_swap_child(t, g, p, s);
- } else /* if (!n || n == p->right) */ { /* same as above, but mirrored */
- s = p->left;
- if (c_rbnode_is_red(s)) {
- g = c_rbnode_parent(p);
- x = s->right;
- p->left = x;
- s->right = p;
- c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK);
- c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
- c_rbtree_swap_child(t, g, p, s);
- s = x;
- }
-
- x = s->left;
- if (!x || c_rbnode_is_black(x)) {
- y = s->right;
- if (!y || c_rbnode_is_black(y)) {
- c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
- if (c_rbnode_is_black(p))
- return p;
-
- c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
- return NULL;
- }
-
- x = y->left;
- s->right = y->left;
- y->left = s;
- p->left = y;
- if (x)
- c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
- x = s;
- s = y;
- }
-
- g = c_rbnode_parent(p);
- y = s->right;
- p->left = y;
- s->right = p;
- c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
- if (y)
- c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
- c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
- c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
- c_rbtree_swap_child(t, g, p, s);
- }
-
- return NULL;
-}
-
-static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) {
- CRBNode *n = NULL;
-
- assert(t);
- assert(p);
-
- do {
- n = c_rbtree_rebalance_one(t, p, n);
- p = n ? c_rbnode_parent(n) : NULL;
- } while (p);
-}
-
-/**
- * c_rbtree_remove() - remove node from tree
- * @t: tree to operate one
- * @n: node to remove
- *
- * This removes the given node from its tree. Once unlinked, the tree is
- * rebalanced.
- * The caller *must* ensure that the given tree is actually the tree it is
- * linked on. Otherwise, behavior is undefined.
- *
- * This does *NOT* reset @n to being unlinked (for performance reason, this
- * function *never* modifies @n at all). If you need this, use
- * c_rbtree_remove_init().
- */
-void c_rbtree_remove(CRBTree *t, CRBNode *n) {
- CRBNode *p, *s, *gc, *x, *next = NULL;
- unsigned long c;
-
- assert(t);
- assert(n);
- assert(c_rbnode_is_linked(n));
-
- /*
- * There are three distinct cases during node removal of a tree:
- * * The node has no children, in which case it can simply be removed.
- * * The node has exactly one child, in which case the child displaces
- * its parent.
- * * The node has two children, in which case there is guaranteed to
- * be a successor to the node (successor being the node ordered
- * directly after it). This successor cannot have two children by
- * itself (two interior nodes can never be successive). Therefore,
- * we can simply swap the node with its successor (including color)
- * and have reduced this case to either of the first two.
- *
- * Whenever the node we removed was black, we have to rebalance the
- * tree. Note that this affects the actual node we _remove_, not @n (in
- * case we swap it).
- *
- * p: parent
- * s: successor
- * gc: grand-...-child
- * x: temporary
- * next: next node to rebalance on
- */
-
- if (!n->left) {
- /*
- * Case 1:
- * The node has no left child. If it neither has a right child,
- * it is a leaf-node and we can simply unlink it. If it also
- * was black, we have to rebalance, as always if we remove a
- * black node.
- * But if the node has a right child, the child *must* be red
- * (otherwise, the right path has more black nodes as the
- * non-existing left path), and the node to be removed must
- * hence be black. We simply replace the node with its child,
- * turning the red child black, and thus no rebalancing is
- * required.
- */
- p = c_rbnode_parent(n);
- c = c_rbnode_color(n);
- c_rbtree_swap_child(t, p, n, n->right);
- if (n->right)
- c_rbnode_set_parent_and_color(n->right, p, c);
- else
- next = (c == C_RBNODE_BLACK) ? p : NULL;
- } else if (!n->right) {
- /*
- * Case 1.1:
- * The node has exactly one child, and it is on the left. Treat
- * it as mirrored case of Case 1 (i.e., replace the node by its
- * child).
- */
- p = c_rbnode_parent(n);
- c = c_rbnode_color(n);
- c_rbtree_swap_child(t, p, n, n->left);
- c_rbnode_set_parent_and_color(n->left, p, c);
- } else {
- /*
- * Case 2:
- * We are dealing with a full interior node with a child not on
- * both sides. Find its successor and swap it. Then remove the
- * node similar to Case 1. For performance reasons we don't
- * perform the full swap, but skip links that are about to be
- * removed, anyway.
- */
- s = n->right;
- if (!s->left) {
- /* right child is next, no need to touch grandchild */
- p = s;
- gc = s->right;
- } else {
- /* find successor and swap partially */
- s = c_rbnode_leftmost(s);
- p = c_rbnode_parent(s);
-
- gc = s->right;
- p->left = s->right;
- s->right = n->right;
- c_rbnode_set_parent(n->right, s);
- }
-
- /* node is partially swapped, now remove as in Case 1 */
- s->left = n->left;
- c_rbnode_set_parent(n->left, s);
-
- x = c_rbnode_parent(n);
- c = c_rbnode_color(n);
- c_rbtree_swap_child(t, x, n, s);
- if (gc)
- c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK);
- else
- next = c_rbnode_is_black(s) ? p : NULL;
- c_rbnode_set_parent_and_color(s, x, c);
- }
-
- if (next)
- c_rbtree_rebalance(t, next);
-}
diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h
deleted file mode 100644
index 20c5515ca1..0000000000
--- a/src/basic/c-rbtree.h
+++ /dev/null
@@ -1,297 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd. See COPYING for details.
-
- 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/>.
-***/
-
-/*
- * Standalone Red-Black-Tree Implementation in Standard ISO-C11
- *
- * This header provides an RB-Tree API, that is fully implemented in ISO-C11
- * and has no external dependencies. Furthermore, tree traversal, memory
- * allocations, and key comparisons a fully in control of the API user. The
- * implementation only provides the RB-Tree specific rebalancing and coloring.
- *
- * A tree is represented by the "CRBTree" structure. It contains a *singly*
- * field, which is a pointer to the root node. If NULL, the tree is empty. If
- * non-NULL, there is at least a single element in the tree.
- *
- * Each node of the tree is represented by the "CRBNode" structure. It has
- * three fields. The @left and @right members can be accessed by the API user
- * directly to traverse the tree. The third member is an implementation detail
- * and encodes the parent pointer and color of the node.
- * API users are required to embed the CRBNode object into their own objects
- * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode
- * pointers into pointers to their own structure.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct CRBNode CRBNode;
-typedef struct CRBTree CRBTree;
-
-/**
- * struct CRBNode - Node of a Red-Black Tree
- * @__parent_and_color: internal state
- * @left: left child, or NULL
- * @right: right child, or NULL
- *
- * Each node in an RB-Tree must embed an CRBNode object. This object contains
- * pointers to its left and right child, which can be freely accessed by the
- * API user at any time. They are NULL, if the node does not have a left/right
- * child.
- *
- * The @__parent_and_color field must never be accessed directly. It encodes
- * the pointer to the parent node, and the color of the node. Use the accessor
- * functions instead.
- *
- * There is no reason to initialize a CRBNode object before linking it.
- * However, if you need a boolean state that tells you whether the node is
- * linked or not, you should initialize the node via c_rbnode_init() or
- * C_RBNODE_INIT.
- */
-struct CRBNode {
- CRBNode *__parent_and_color;
- CRBNode *left;
- CRBNode *right;
-};
-
-#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) }
-
-CRBNode *c_rbnode_leftmost(CRBNode *n);
-CRBNode *c_rbnode_rightmost(CRBNode *n);
-CRBNode *c_rbnode_next(CRBNode *n);
-CRBNode *c_rbnode_prev(CRBNode *n);
-
-/**
- * struct CRBTree - Red-Black Tree
- * @root: pointer to the root node, or NULL
- *
- * Each Red-Black Tree is rooted in an CRBTree object. This object contains a
- * pointer to the root node of the tree. The API user is free to access the
- * @root member at any time, and use it to traverse the tree.
- *
- * To initialize an RB-Tree, set it to NULL / all zero.
- */
-struct CRBTree {
- CRBNode *root;
-};
-
-CRBNode *c_rbtree_first(CRBTree *t);
-CRBNode *c_rbtree_last(CRBTree *t);
-
-void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n);
-void c_rbtree_remove(CRBTree *t, CRBNode *n);
-
-/**
- * c_rbnode_init() - mark a node as unlinked
- * @n: node to operate on
- *
- * This marks the node @n as unlinked. The node will be set to a valid state
- * that can never happen if the node is linked in a tree. Furthermore, this
- * state is fully known to the implementation, and as such handled gracefully
- * in all cases.
- *
- * You are *NOT* required to call this on your node. c_rbtree_add() can handle
- * uninitialized nodes just fine. However, calling this allows to use
- * c_rbnode_is_linked() to check for the state of a node. Furthermore,
- * iterators and accessors can be called on initialized (yet unlinked) nodes.
- *
- * Use the C_RBNODE_INIT macro if you want to initialize static variables.
- */
-static inline void c_rbnode_init(CRBNode *n) {
- *n = (CRBNode)C_RBNODE_INIT(*n);
-}
-
-/**
- * c_rbnode_is_linked() - check whether a node is linked
- * @n: node to check, or NULL
- *
- * This checks whether the passed node is linked. If you pass NULL, or if the
- * node is not linked into a tree, this will return false. Otherwise, this
- * returns true.
- *
- * Note that you must have either linked the node or initialized it, before
- * calling this function. Never call this function on uninitialized nodes.
- * Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node
- * as unlinked. You have to call c_rbnode_init() yourself after removal, or use
- * the c_rbtree_remove_init() helper.
- *
- * Return: true if the node is linked, false if not.
- */
-static inline _Bool c_rbnode_is_linked(CRBNode *n) {
- return n && n->__parent_and_color != n;
-}
-
-/**
- * c_rbnode_parent() - return parent pointer
- * @n node to access
- *
- * This returns a pointer to the parent of the given node @n. If @n does not
- * have a parent, NULL is returned. If @n is not linked, @n itself is returned.
- *
- * You should not call this on unlinked or uninitialized nodes! If you do, you
- * better know how its semantics.
- *
- * Return: Pointer to parent.
- */
-static inline CRBNode *c_rbnode_parent(CRBNode *n) {
- return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL);
-}
-
-/**
- * c_rbtree_remove_init() - safely remove node from tree and reinitialize it
- * @t: tree to operate on
- * @n: node to remove, or NULL
- *
- * This is almost the same as c_rbtree_remove(), but extends it slightly, to be
- * more convenient to use in many cases:
- * - if @n is unlinked or NULL, this is a no-op
- * - @n is reinitialized after being removed
- */
-static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) {
- if (c_rbnode_is_linked(n)) {
- c_rbtree_remove(t, n);
- c_rbnode_init(n);
- }
-}
-
-/**
- * CRBCompareFunc - compare a node to a key
- * @t: tree where the node is linked to
- * @k: key to compare
- * @n: node to compare
- *
- * If you use the tree-traversal helpers (which are optional), you need to
- * provide this callback so they can compare nodes in a tree to the key you
- * look for.
- *
- * The tree @t is provided as optional context to this callback. The key you
- * look for is provided as @k, the current node that should be compared to is
- * provided as @n. This function should work like strcmp(), that is, return -1
- * if @key orders before @n, 0 if both compare equal, and 1 if it orders after
- * @n.
- */
-typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n);
-
-/**
- * c_rbtree_find_node() - find node
- * @t: tree to search through
- * @f: comparison function
- * @k: key to search for
- *
- * This searches through @t for a node that compares equal to @k. The function
- * @f must be provided by the caller, which is used to compare nodes to @k. See
- * the documentation of CRBCompareFunc for details.
- *
- * If there are multiple entries that compare equal to @k, this will return a
- * pseudo-randomly picked node. If you need stable lookup functions for trees
- * where duplicate entries are allowed, you better code your own lookup.
- *
- * Return: Pointer to matching node, or NULL.
- */
-static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) {
- CRBNode *i;
-
- assert(t);
- assert(f);
-
- i = t->root;
- while (i) {
- int v = f(t, (void *)k, i);
- if (v < 0)
- i = i->left;
- else if (v > 0)
- i = i->right;
- else
- return i;
- }
-
- return NULL;
-}
-
-/**
- * c_rbtree_find_entry() - find entry
- * @_t: tree to search through
- * @_f: comparison function
- * @_k: key to search for
- * @_t: type of the structure that embeds the nodes
- * @_o: name of the node-member in type @_t
- *
- * This is very similar to c_rbtree_find_node(), but instead of returning a
- * pointer to the CRBNode, it returns a pointer to the surrounding object. This
- * object must embed the CRBNode object. The type of the surrounding object
- * must be given as @_t, and the name of the embedded CRBNode member as @_o.
- *
- * See c_rbtree_find_node() for more details.
- *
- * Return: Pointer to found entry, NULL if not found.
- */
-#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \
- ((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \
- (char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o)))
-
-/**
- * c_rbtree_find_slot() - find slot to insert new node
- * @t: tree to search through
- * @f: comparison function
- * @k: key to search for
- * @p: output storage for parent pointer
- *
- * This searches through @t just like c_rbtree_find_node() does. However,
- * instead of returning a pointer to a node that compares equal to @k, this
- * searches for a slot to insert a node with key @k. A pointer to the slot is
- * returned, and a pointer to the parent of the slot is stored in @p. Both
- * can be passed directly to c_rbtree_add(), together with your node to insert.
- *
- * If there already is a node in the tree, that compares equal to @k, this will
- * return NULL and store the conflicting node in @p. In all other cases,
- * this will return a pointer (non-NULL) to the empty slot to insert the node
- * at. @p will point to the parent node of that slot.
- *
- * If you want trees that allow duplicate nodes, you better code your own
- * insertion function.
- *
- * Return: Pointer to slot to insert node, or NULL on conflicts.
- */
-static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) {
- CRBNode **i;
-
- assert(t);
- assert(f);
- assert(p);
-
- i = &t->root;
- *p = NULL;
- while (*i) {
- int v = f(t, (void *)k, *i);
- *p = *i;
- if (v < 0)
- i = &(*i)->left;
- else if (v > 0)
- i = &(*i)->right;
- else
- return NULL;
- }
-
- return i;
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 41dc8ca79a..c3586728d0 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -71,7 +71,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
- size_t m = SSIZE_MAX; /* that the maximum that sendfile and c_f_r accept */
+ size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
assert(fdf >= 0);
assert(fdt >= 0);
@@ -94,15 +94,15 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
if (max_bytes <= 0)
return 1; /* return > 0 if we hit the max_bytes limit */
- if ((uint64_t) m > max_bytes)
- m = (size_t) max_bytes;
+ if (m > max_bytes)
+ m = max_bytes;
}
/* First try copy_file_range(), unless we already tried */
if (try_cfr) {
n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
if (n < 0) {
- if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV))
+ if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
return n;
try_cfr = false;
@@ -305,6 +305,8 @@ static int fd_copy_directory(
fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
else
fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
+ if (fdf < 0)
+ return -errno;
d = fdopendir(fdf);
if (!d)
@@ -325,22 +327,6 @@ static int fd_copy_directory(
r = 0;
- if (created) {
- struct timespec ut[2] = {
- st->st_atim,
- st->st_mtim
- };
-
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
- r = -errno;
-
- if (fchmod(fdt, st->st_mode & 07777) < 0)
- r = -errno;
-
- (void) futimens(fdt, ut);
- (void) copy_xattr(dirfd(d), fdt);
- }
-
FOREACH_DIRENT_ALL(de, d, return -errno) {
struct stat buf;
int q;
@@ -364,7 +350,7 @@ static int fd_copy_directory(
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else if (S_ISFIFO(buf.st_mode))
q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
+ else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else
q = -EOPNOTSUPP;
@@ -376,6 +362,22 @@ static int fd_copy_directory(
r = q;
}
+ if (created) {
+ struct timespec ut[2] = {
+ st->st_atim,
+ st->st_mtim
+ };
+
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+
+ (void) copy_xattr(dirfd(d), fdt);
+ (void) futimens(fdt, ut);
+ }
+
return r;
}
@@ -396,7 +398,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return fd_copy_symlink(fdf, from, &st, fdt, to);
else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(fdf, from, &st, fdt, to);
- else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
else
return -EOPNOTSUPP;
@@ -407,7 +409,6 @@ int copy_tree(const char *from, const char *to, bool merge) {
}
int copy_directory_fd(int dirfd, const char *to, bool merge) {
-
struct stat st;
assert(dirfd >= 0);
@@ -422,6 +423,21 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
}
+int copy_directory(const char *from, const char *to, bool merge) {
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ if (lstat(from, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -ENOTDIR;
+
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
+}
+
int copy_file_fd(const char *from, int fdt, bool try_reflink) {
_cleanup_close_ int fdf = -1;
int r;
diff --git a/src/basic/copy.h b/src/basic/copy.h
index 3e5eb52506..b5d08ebafe 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -30,6 +30,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
int copy_tree(const char *from, const char *to, bool merge);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
int copy_directory_fd(int dirfd, const char *to, bool merge);
+int copy_directory(const char *from, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/def.h b/src/basic/def.h
index 963343eb7d..1a7a0f4928 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -41,8 +41,6 @@
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
-#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
-
#ifdef HAVE_SPLIT_USR
#define KBD_KEYMAP_DIRS \
"/usr/share/keymaps/\0" \
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
index 5fb535cb13..59067121b7 100644
--- a/src/basic/dirent-util.c
+++ b/src/basic/dirent-util.c
@@ -52,12 +52,10 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
bool dirent_is_file(const struct dirent *de) {
assert(de);
- if (hidden_file(de->d_name))
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (hidden_or_backup_file(de->d_name))
return false;
return true;
@@ -66,12 +64,10 @@ bool dirent_is_file(const struct dirent *de) {
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
assert(de);
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (hidden_file_allow_backup(de->d_name))
+ if (de->d_name[0] == '.')
return false;
return endswith(de->d_name, suffix);
diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h
index 6bf099b46c..b91d04908f 100644
--- a/src/basic/dirent-util.h
+++ b/src/basic/dirent-util.h
@@ -38,7 +38,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu
on_error; \
} \
break; \
- } else if (hidden_file((de)->d_name)) \
+ } else if (hidden_or_backup_file((de)->d_name)) \
continue; \
else
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index ec9560cd07..8b466cff15 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -25,11 +25,13 @@
#include <unistd.h>
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
+#include "stdio-util.h"
#include "util.h"
int close_nointr(int fd) {
@@ -229,7 +231,7 @@ int close_all_fds(const int except[], unsigned n_except) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
@@ -356,3 +358,17 @@ bool fdname_is_valid(const char *s) {
return p - s < 256;
}
+
+int fd_get_path(int fd, char **ret) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ int r;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+
+ r = readlink_malloc(procfs_path, ret);
+
+ if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */
+ return -EBADF;
+
+ return r;
+}
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 44528c6e35..b86e41698a 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -72,6 +72,8 @@ void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
+int fd_get_path(int fd, char **ret);
+
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
diff --git a/src/basic/fdset.c b/src/basic/fdset.c
index 06f8ecbdbc..527f27bc67 100644
--- a/src/basic/fdset.c
+++ b/src/basic/fdset.c
@@ -151,7 +151,7 @@ int fdset_new_fill(FDSet **_s) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
r = safe_atoi(de->d_name, &fd);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 69590941e5..2a9b6e46ad 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1083,30 +1083,6 @@ int mkostemp_safe(char *pattern, int flags) {
return fd;
}
-int open_tmpfile(const char *path, int flags) {
- char *p;
- int fd;
-
- assert(path);
-
-#ifdef O_TMPFILE
- /* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
- if (fd >= 0)
- return fd;
-#endif
-
- /* Fall back to unguessable name + unlinking */
- p = strjoina(path, "/systemd-tmp-XXXXXX");
-
- fd = mkostemp_safe(p, flags);
- if (fd < 0)
- return fd;
-
- unlink(p);
- return fd;
-}
-
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn;
char *t;
@@ -1278,3 +1254,103 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
return fputs(s, f);
}
+
+int open_tmpfile_unlinkable(const char *directory, int flags) {
+ char *p;
+ int fd;
+
+ assert(directory);
+
+ /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
+
+#ifdef O_TMPFILE
+ /* Try O_TMPFILE first, if it is supported */
+ fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ return fd;
+#endif
+
+ /* Fall back to unguessable name + unlinking */
+ p = strjoina(directory, "/systemd-tmp-XXXXXX");
+
+ fd = mkostemp_safe(p, flags);
+ if (fd < 0)
+ return fd;
+
+ (void) unlink(p);
+
+ return fd;
+}
+
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
+ _cleanup_free_ char *tmp = NULL;
+ int r, fd;
+
+ assert(target);
+ assert(ret_path);
+
+ /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
+ assert((flags & O_EXCL) == 0);
+
+ /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
+ * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+ * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
+
+#ifdef O_TMPFILE
+ {
+ _cleanup_free_ char *dn = NULL;
+
+ dn = dirname_malloc(target);
+ if (!dn)
+ return -ENOMEM;
+
+ fd = open(dn, O_TMPFILE|flags, 0640);
+ if (fd >= 0) {
+ *ret_path = NULL;
+ return fd;
+ }
+
+ log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+ }
+#endif
+
+ r = tempfn_random(target, NULL, &tmp);
+ if (r < 0)
+ return r;
+
+ fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
+ if (fd < 0)
+ return -errno;
+
+ *ret_path = tmp;
+ tmp = NULL;
+
+ return fd;
+}
+
+int link_tmpfile(int fd, const char *path, const char *target) {
+
+ assert(fd >= 0);
+ assert(target);
+
+ /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
+ * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
+ * on the directory, and renameat2() is used instead.
+ *
+ * Note that in both cases we will not replace existing files. This is because linkat() dos not support this
+ * operation currently (renameat2() does), and there is no nice way to emulate this. */
+
+ if (path) {
+ if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
+ return -errno;
+ } else {
+ char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+
+ xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
+
+ if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 8084895ff3..58dbc80c24 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -72,7 +72,6 @@ int fflush_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
int mkostemp_safe(char *pattern, int flags);
-int open_tmpfile(const char *path, int flags);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
@@ -82,3 +81,8 @@ int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+
+int open_tmpfile_unlinkable(const char *directory, int flags);
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+
+int link_tmpfile(int fd, const char *path, const char *target);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 51268828af..e24e7036f7 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -38,6 +38,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -493,3 +494,17 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
+ char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ int r;
+
+ /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
+ xsprintf(path, "/proc/self/fd/%i", what);
+
+ r = inotify_add_watch(fd, path, mask);
+ if (r < 0)
+ return -errno;
+
+ return r;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 0d23f8635f..517b599d6f 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -72,3 +72,5 @@ union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask);
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
index 85b8d812b3..49a0479592 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -1773,20 +1773,18 @@ int set_consume(Set *s, void *value) {
int set_put_strdup(Set *s, const char *p) {
char *c;
- int r;
assert(s);
assert(p);
+ if (set_contains(s, (char*) p))
+ return 0;
+
c = strdup(p);
if (!c)
return -ENOMEM;
- r = set_consume(s, c);
- if (r == -EEXIST)
- return 0;
-
- return r;
+ return set_consume(s, c);
}
int set_put_strdupv(Set *s, char **l) {
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index 5a7ee87a20..13c3bb6446 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -178,16 +178,16 @@ bool is_localhost(const char *hostname) {
assert(hostname);
/* This tries to identify local host and domain names
- * described in RFC6761 plus the redhatism of .localdomain */
+ * described in RFC6761 plus the redhatism of localdomain */
return strcaseeq(hostname, "localhost") ||
strcaseeq(hostname, "localhost.") ||
- strcaseeq(hostname, "localdomain.") ||
- strcaseeq(hostname, "localdomain") ||
+ strcaseeq(hostname, "localhost.localdomain") ||
+ strcaseeq(hostname, "localhost.localdomain.") ||
endswith_no_case(hostname, ".localhost") ||
endswith_no_case(hostname, ".localhost.") ||
- endswith_no_case(hostname, ".localdomain") ||
- endswith_no_case(hostname, ".localdomain.");
+ endswith_no_case(hostname, ".localhost.localdomain") ||
+ endswith_no_case(hostname, ".localhost.localdomain.");
}
bool is_gateway_hostname(const char *hostname) {
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index cda6b2895d..eaad25e65b 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -153,6 +153,8 @@ static int add_locales_from_libdir (Set *locales) {
FOREACH_DIRENT(entry, dir, return -errno) {
char *z;
+ dirent_ensure_type(dir, entry);
+
if (entry->d_type != DT_DIR)
continue;
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 66cd5921ad..22ea8f67cc 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -445,6 +445,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define TMPFS_MAGIC 0x01021994
#endif
+#ifndef MQUEUE_MAGIC
+#define MQUEUE_MAGIC 0x19800202
+#endif
+
#ifndef MS_MOVE
#define MS_MOVE 8192
#endif
@@ -553,7 +557,7 @@ struct btrfs_ioctl_quota_ctl_args {
#define IFLA_INET6_ADDR_GEN_MODE 8
#define __IFLA_INET6_MAX 9
-#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
+#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
#define IN6_ADDR_GEN_MODE_EUI64 0
#define IN6_ADDR_GEN_MODE_NONE 1
@@ -738,7 +742,7 @@ struct btrfs_ioctl_quota_ctl_args {
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
#endif
-#if !HAVE_DECL_IFLA_BR_PRIORITY
+#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID
#define IFLA_BR_UNSPEC 0
#define IFLA_BR_FORWARD_DELAY 1
#define IFLA_BR_HELLO_TIME 2
@@ -746,7 +750,40 @@ struct btrfs_ioctl_quota_ctl_args {
#define IFLA_BR_AGEING_TIME 4
#define IFLA_BR_STP_STATE 5
#define IFLA_BR_PRIORITY 6
-#define __IFLA_BR_MAX 7
+#define IFLA_BR_VLAN_FILTERING 7
+#define IFLA_BR_VLAN_PROTOCOL 8
+#define IFLA_BR_GROUP_FWD_MASK 9
+#define IFLA_BR_ROOT_ID 10
+#define IFLA_BR_BRIDGE_ID 11
+#define IFLA_BR_ROOT_PORT 12
+#define IFLA_BR_ROOT_PATH_COST 13
+#define IFLA_BR_TOPOLOGY_CHANGE 14
+#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15
+#define IFLA_BR_HELLO_TIMER 16
+#define IFLA_BR_TCN_TIMER 17
+#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18
+#define IFLA_BR_GC_TIMER 19
+#define IFLA_BR_GROUP_ADDR 20
+#define IFLA_BR_FDB_FLUSH 21
+#define IFLA_BR_MCAST_ROUTER 22
+#define IFLA_BR_MCAST_SNOOPING 23
+#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24
+#define IFLA_BR_MCAST_QUERIER 25
+#define IFLA_BR_MCAST_HASH_ELASTICITY 26
+#define IFLA_BR_MCAST_HASH_MAX 27
+#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28
+#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29
+#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30
+#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31
+#define IFLA_BR_MCAST_QUERIER_INTVL 32
+#define IFLA_BR_MCAST_QUERY_INTVL 33
+#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34
+#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35
+#define IFLA_BR_NF_CALL_IPTABLES 36
+#define IFLA_BR_NF_CALL_IP6TABLES 37
+#define IFLA_BR_NF_CALL_ARPTABLES 38
+#define IFLA_BR_VLAN_DEFAULT_PVID 39
+#define __IFLA_BR_MAX 40
#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
#endif
@@ -795,6 +832,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define IPV6_UNICAST_IF 76
#endif
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif
+
#ifndef IFF_MULTI_QUEUE
#define IFF_MULTI_QUEUE 0x100
#endif
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 5faa2eba05..ba698959b7 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -498,7 +498,9 @@ bool fstype_is_network(const char *fstype) {
"nfs4\0"
"gfs\0"
"gfs2\0"
- "glusterfs\0";
+ "glusterfs\0"
+ "pvfs2\0" /* OrangeFS */
+ ;
const char *x;
diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h
index df565a3593..bf7c4854fc 100644
--- a/src/basic/nss-util.h
+++ b/src/basic/nss-util.h
@@ -154,3 +154,46 @@ enum nss_status _nss_##module##_getgrgid_r( \
struct group *gr, \
char *buffer, size_t buflen, \
int *errnop) _public_
+
+typedef enum nss_status (*_nss_gethostbyname4_r_t)(
+ const char *name,
+ struct gaih_addrtuple **pat,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop,
+ int32_t *ttlp);
+
+typedef enum nss_status (*_nss_gethostbyname3_r_t)(
+ const char *name,
+ int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop,
+ int32_t *ttlp,
+ char **canonp);
+
+typedef enum nss_status (*_nss_gethostbyname2_r_t)(
+ const char *name,
+ int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+
+typedef enum nss_status (*_nss_gethostbyname_r_t)(
+ const char *name,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
+
+typedef enum nss_status (*_nss_gethostbyaddr2_r_t)(
+ const void* addr, socklen_t len,
+ int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop,
+ int32_t *ttlp);
+typedef enum nss_status (*_nss_gethostbyaddr_r_t)(
+ const void* addr, socklen_t len,
+ int af,
+ struct hostent *host,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop);
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index d8dc26a36e..7dc579a159 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -90,6 +90,18 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
}
#endif
+#if SIZE_MAX == UINT_MAX
+static inline int safe_atozu(const char *s, size_t *ret_u) {
+ assert_cc(sizeof(size_t) == sizeof(unsigned));
+ return safe_atou(s, (unsigned *) ret_u);
+}
+#else
+static inline int safe_atozu(const char *s, size_t *ret_u) {
+ assert_cc(sizeof(size_t) == sizeof(long unsigned));
+ return safe_atolu(s, ret_u);
+}
+#endif
+
int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 822c09bfba..b2fa81a294 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -569,10 +569,10 @@ static int binary_is_good(const char *binary) {
if (r < 0)
return r;
- return !path_equal(d, "true") &&
- !path_equal(d, "/bin/true") &&
- !path_equal(d, "/usr/bin/true") &&
- !path_equal(d, "/dev/null");
+ return !PATH_IN_SET(d, "true"
+ "/bin/true",
+ "/usr/bin/true",
+ "/dev/null");
}
int fsck_exists(const char *fstype) {
@@ -756,34 +756,53 @@ char *file_in_same_dir(const char *path, const char *filename) {
return ret;
}
-bool hidden_file_allow_backup(const char *filename) {
- assert(filename);
-
- return
- filename[0] == '.' ||
- streq(filename, "lost+found") ||
- streq(filename, "aquota.user") ||
- streq(filename, "aquota.group") ||
- endswith(filename, ".rpmnew") ||
- endswith(filename, ".rpmsave") ||
- endswith(filename, ".rpmorig") ||
- endswith(filename, ".dpkg-old") ||
- endswith(filename, ".dpkg-new") ||
- endswith(filename, ".dpkg-tmp") ||
- endswith(filename, ".dpkg-dist") ||
- endswith(filename, ".dpkg-bak") ||
- endswith(filename, ".dpkg-backup") ||
- endswith(filename, ".dpkg-remove") ||
- endswith(filename, ".swp");
-}
+bool hidden_or_backup_file(const char *filename) {
+ const char *p;
-bool hidden_file(const char *filename) {
assert(filename);
- if (endswith(filename, "~"))
+ if (filename[0] == '.' ||
+ streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
+ endswith(filename, "~"))
return true;
- return hidden_file_allow_backup(filename);
+ p = strrchr(filename, '.');
+ if (!p)
+ return false;
+
+ /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
+ * with always new suffixes and that everybody else should just adjust to that, then it really should be on
+ * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
+ * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
+ * string. Specifically: there's now:
+ *
+ * The generic suffixes "~" and ".bak" for backup files
+ * The generic prefix "." for hidden files
+ *
+ * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
+ * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
+ */
+
+ return STR_IN_SET(p + 1,
+ "rpmnew",
+ "rpmsave",
+ "rpmorig",
+ "dpkg-old",
+ "dpkg-new",
+ "dpkg-tmp",
+ "dpkg-dist",
+ "dpkg-bak",
+ "dpkg-backup",
+ "dpkg-remove",
+ "ucf-new",
+ "ucf-old",
+ "ucf-dist",
+ "swp",
+ "bak",
+ "old",
+ "new");
}
bool is_device_path(const char *path) {
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 2c2f87a9f2..a27c13fcc3 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -48,6 +48,23 @@ bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b);
char* path_join(const char *root, const char *path, const char *rest);
+static inline bool path_equal_ptr(const char *a, const char *b) {
+ return !!a == !!b && (!a || path_equal(a, b));
+}
+
+/* Note: the search terminates on the first NULL item. */
+#define PATH_IN_SET(p, ...) \
+ ({ \
+ char **s; \
+ bool _found = false; \
+ STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
+ if (path_equal(p, *s)) { \
+ _found = true; \
+ break; \
+ } \
+ _found; \
+ })
+
int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
@@ -105,7 +122,6 @@ bool path_is_safe(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
-bool hidden_file_allow_backup(const char *filename);
-bool hidden_file(const char *filename) _pure_;
+bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index ae3f6109ad..4a7367cc92 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -528,14 +528,20 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod
return -EPROTO;
}
-void sigkill_wait(pid_t *pid) {
+void sigkill_wait(pid_t pid) {
+ assert(pid > 1);
+
+ if (kill(pid, SIGKILL) > 0)
+ (void) wait_for_terminate(pid, NULL);
+}
+
+void sigkill_waitp(pid_t *pid) {
if (!pid)
return;
if (*pid <= 1)
return;
- if (kill(*pid, SIGKILL) > 0)
- (void) wait_for_terminate(*pid, NULL);
+ sigkill_wait(*pid);
}
int kill_and_sigcont(pid_t pid, int sig) {
@@ -731,6 +737,18 @@ void valgrind_summary_hack(void) {
#endif
}
+int pid_compare_func(const void *a, const void *b) {
+ const pid_t *p = a, *q = b;
+
+ /* Suitable for usage in qsort() */
+
+ if (*p < *q)
+ return -1;
+ if (*p > *q)
+ return 1;
+ return 0;
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index f5d193e762..9f75088796 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -58,8 +58,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
-void sigkill_wait(pid_t *pid);
-#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
+void sigkill_wait(pid_t pid);
+void sigkill_waitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
@@ -101,3 +101,5 @@ int sched_policy_from_string(const char *s);
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
void valgrind_summary_hack(void);
+
+int pid_compare_func(const void *a, const void *b);
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index 7540b43215..ee063720ed 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -153,6 +153,56 @@ static int rlimit_parse_usec(const char *val, rlim_t *ret) {
return 0;
}
+static int rlimit_parse_nice(const char *val, rlim_t *ret) {
+ uint64_t rl;
+ int r;
+
+ /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
+ * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
+ * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
+ * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
+ * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
+ *
+ * Yeah, Linux is quality engineering sometimes... */
+
+ if (val[0] == '+') {
+
+ /* Prefixed with "+": Parse as positive user-friendly nice value */
+ r = safe_atou64(val + 1, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl >= PRIO_MAX)
+ return -ERANGE;
+
+ rl = 20 - rl;
+
+ } else if (val[0] == '-') {
+
+ /* Prefixed with "-": Parse as negative user-friendly nice value */
+ r = safe_atou64(val + 1, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl > (uint64_t) (-PRIO_MIN))
+ return -ERANGE;
+
+ rl = 20 + rl;
+ } else {
+
+ /* Not prefixed: parse as raw resource limit value */
+ r = safe_atou64(val, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl > (uint64_t) (20 - PRIO_MIN))
+ return -ERANGE;
+ }
+
+ *ret = (rlim_t) rl;
+ return 0;
+}
+
static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
[RLIMIT_CPU] = rlimit_parse_sec,
[RLIMIT_FSIZE] = rlimit_parse_size,
@@ -167,7 +217,7 @@ static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret
[RLIMIT_LOCKS] = rlimit_parse_u64,
[RLIMIT_SIGPENDING] = rlimit_parse_u64,
[RLIMIT_MSGQUEUE] = rlimit_parse_size,
- [RLIMIT_NICE] = rlimit_parse_u64,
+ [RLIMIT_NICE] = rlimit_parse_nice,
[RLIMIT_RTPRIO] = rlimit_parse_u64,
[RLIMIT_RTTIME] = rlimit_parse_usec,
};
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index 6d03268919..40b5b527d5 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -30,3 +30,12 @@ typedef enum RemoveFlags {
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
int rm_rf(const char *path, RemoveFlags flags);
+
+/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
+static inline void rm_rf_and_free(char *p) {
+ if (!p)
+ return;
+ (void) rm_rf(p, REMOVE_ROOT);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_and_free);
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index e3047b209b..280b5c3251 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -255,7 +255,7 @@ int signal_from_string(const char *s) {
}
if (safe_atou(s, &u) >= 0) {
signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
+ if (SIGNAL_VALID(signo))
return signo;
}
return -EINVAL;
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index a7322ff26a..dfd6eb564d 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -50,3 +50,7 @@ static inline void block_signals_reset(sigset_t *ss) {
assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \
t; \
})
+
+static inline bool SIGNAL_VALID(int signo) {
+ return signo > 0 && signo < _NSIG;
+}
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
index b180488fe8..d88625fca7 100644
--- a/src/basic/string-table.h
+++ b/src/basic/string-table.h
@@ -56,26 +56,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
-#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
-
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
-
-/* For string conversions where numbers are also acceptable */
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
- int name##_to_string_alloc(type i, char **str) { \
+#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \
+ scope int name##_to_string_alloc(type i, char **str) { \
char *s; \
if (i < 0 || i > max) \
return -ERANGE; \
@@ -89,7 +71,9 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
} \
*str = s; \
return 0; \
- } \
+ }
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
type name##_from_string(const char *s) { \
type i; \
unsigned u = 0; \
@@ -102,4 +86,32 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
return (type) u; \
return (type) -1; \
} \
+
+
+#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
+
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
+
+/* For string conversions where numbers are also acceptable */
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \
struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static)
diff --git a/src/basic/strv.c b/src/basic/strv.c
index 8282298dca..97a96e5762 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -558,6 +558,42 @@ int strv_extend(char ***l, const char *value) {
return strv_consume(l, v);
}
+int strv_extend_front(char ***l, const char *value) {
+ size_t n, m;
+ char *v, **c;
+
+ assert(l);
+
+ /* Like strv_extend(), but prepends rather than appends the new entry */
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* Increase and overflow check. */
+ m = n + 2;
+ if (m < n)
+ return -ENOMEM;
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ c = realloc_multiply(*l, sizeof(char*), m);
+ if (!c) {
+ free(v);
+ return -ENOMEM;
+ }
+
+ memmove(c+1, c, n * sizeof(char*));
+ c[0] = v;
+ c[n+1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
char **strv_uniq(char **l) {
char **i;
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 7bfa54408d..f61bbb5386 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -50,6 +50,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
int strv_extend(char ***l, const char *value);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
+int strv_extend_front(char ***l, const char *value);
int strv_push(char ***l, char *value);
int strv_push_pair(char ***l, char *a, char *b);
int strv_push_prepend(char ***l, char *value);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 0a9d2bbdef..9521b79daa 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -1135,14 +1135,19 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
}
bool colors_enabled(void) {
- const char *colors;
+ static int enabled = -1;
- colors = getenv("SYSTEMD_COLORS");
- if (!colors) {
- if (streq_ptr(getenv("TERM"), "dumb"))
- return false;
- return on_tty();
+ if (_unlikely_(enabled < 0)) {
+ const char *colors;
+
+ colors = getenv("SYSTEMD_COLORS");
+ if (colors)
+ enabled = parse_boolean(colors) != 0;
+ else if (streq_ptr(getenv("TERM"), "dumb"))
+ enabled = false;
+ else
+ enabled = on_tty();
}
- return parse_boolean(colors) != 0;
+ return enabled;
}
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index c16460a198..edd9179cb8 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -47,15 +47,12 @@ static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
* fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
* when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
- * those archs.
- *
- * Also, older kernels don't support CLOCK_BOOTTIME: fall back to CLOCK_MONOTONIC. */
+ * those archs. */
switch (c) {
- case CLOCK_BOOTTIME:
case CLOCK_BOOTTIME_ALARM:
- return clock_boottime_or_monotonic ();
+ return CLOCK_BOOTTIME;
case CLOCK_REALTIME_ALARM:
return CLOCK_REALTIME;
@@ -1083,22 +1080,31 @@ bool timezone_is_valid(const char *name) {
return true;
}
-clockid_t clock_boottime_or_monotonic(void) {
- static clockid_t clock = -1;
- int fd;
-
- if (clock != -1)
- return clock;
-
- fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
- if (fd < 0)
- clock = CLOCK_MONOTONIC;
- else {
- safe_close(fd);
- clock = CLOCK_BOOTTIME;
+bool clock_boottime_supported(void) {
+ static int supported = -1;
+
+ /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
+
+ if (supported < 0) {
+ int fd;
+
+ fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (fd < 0)
+ supported = false;
+ else {
+ safe_close(fd);
+ supported = true;
+ }
}
- return clock;
+ return supported;
+}
+
+clockid_t clock_boottime_or_monotonic(void) {
+ if (clock_boottime_supported())
+ return CLOCK_BOOTTIME;
+ else
+ return CLOCK_MONOTONIC;
}
int get_timezone(char **tz) {
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 77e3cd08d4..a5e3f567ec 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -112,6 +112,7 @@ bool ntp_synced(void);
int get_timezones(char ***l);
bool timezone_is_valid(const char *name);
+bool clock_boottime_supported(void);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) \
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 19155bce53..f65ca3edaa 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -30,6 +30,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "missing.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "formats-util.h"
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index c23f1d485d..8026eca3f4 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <sys/types.h>
+#include <unistd.h>
bool uid_is_valid(uid_t uid);
@@ -63,3 +64,7 @@ int take_etc_passwd_lock(const char *root);
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+static inline bool userns_supported(void) {
+ return access("/proc/self/uid_map", F_OK) >= 0;
+}
diff --git a/src/basic/util.c b/src/basic/util.c
index ea1bed7ceb..756c663be4 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -55,6 +55,7 @@
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
+#include "umask-util.h"
#include "user-util.h"
#include "util.h"
@@ -419,13 +420,17 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
_exit(EXIT_FAILURE);
}
- if (!stdout_is_tty)
- dup2(fd, STDOUT_FILENO);
+ if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
- if (!stderr_is_tty)
- dup2(fd, STDERR_FILENO);
+ if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
- if (fd > 2)
+ if (fd > STDERR_FILENO)
close(fd);
}
@@ -517,7 +522,7 @@ int on_ac_power(void) {
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
@@ -773,15 +778,25 @@ uint64_t physical_memory(void) {
return (uint64_t) mem * (uint64_t) page_size();
}
-int update_reboot_param_file(const char *param) {
- int r = 0;
+int update_reboot_parameter_and_warn(const char *param) {
+ int r;
+
+ if (isempty(param)) {
+ if (unlink("/run/systemd/reboot-param") < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
+ }
- if (param) {
- r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
+ return 0;
+ }
+
+ RUN_WITH_UMASK(0022) {
+ r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
if (r < 0)
- return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
- } else
- (void) unlink(REBOOT_PARAM_FILE);
+ return log_warning_errno(r, "Failed to write reboot parameter file: %m");
+ }
return 0;
}
diff --git a/src/basic/util.h b/src/basic/util.h
index 286db05159..1c032c15c9 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -184,6 +184,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
uint64_t physical_memory(void);
-int update_reboot_param_file(const char *param);
+int update_reboot_parameter_and_warn(const char *param);
int version(void);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index d6fb10cac5..dcb5912b83 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -191,7 +191,8 @@ int main(int argc, char *argv[]) {
output_flags =
arg_all * OUTPUT_SHOW_ALL |
- (arg_full > 0) * OUTPUT_FULL_WIDTH;
+ (arg_full > 0) * OUTPUT_FULL_WIDTH |
+ arg_kernel_threads * OUTPUT_KERNEL_THREADS;
if (optind < argc) {
_cleanup_free_ char *root = NULL;
@@ -209,7 +210,7 @@ int main(int argc, char *argv[]) {
printf("Directory %s:\n", argv[i]);
fflush(stdout);
- q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags);
+ q = show_cgroup_by_path(argv[i], NULL, 0, output_flags);
} else {
_cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
const char *controller, *path;
@@ -235,7 +236,7 @@ int main(int argc, char *argv[]) {
show_cg_info(controller, path);
- q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags);
+ q = show_cgroup(controller, path, NULL, 0, output_flags);
}
if (q < 0)
@@ -258,7 +259,7 @@ int main(int argc, char *argv[]) {
printf("Working directory %s:\n", cwd);
fflush(stdout);
- r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags);
+ r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
done = true;
}
}
@@ -273,7 +274,7 @@ int main(int argc, char *argv[]) {
show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
printf("-.slice\n");
- r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags);
+ r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
}
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 9c0e82ebb3..14eb46c8db 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -362,7 +362,7 @@ static int refresh_one(
Group **ret) {
_cleanup_closedir_ DIR *d = NULL;
- Group *ours;
+ Group *ours = NULL;
int r;
assert(controller);
diff --git a/src/core/automount.c b/src/core/automount.c
index 5dc6fd98e7..1239a0efc6 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -149,7 +149,7 @@ static int automount_add_default_dependencies(Automount *a) {
if (!UNIT(a)->default_dependencies)
return 0;
- if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -1050,9 +1050,6 @@ const UnitVTable automount_vtable = {
"Automount\0"
"Install\0",
- .no_alias = true,
- .no_instances = true,
-
.init = automount_init,
.load = automount_load,
.done = automount_done,
diff --git a/src/core/busname.c b/src/core/busname.c
index de2a21ccde..e7b7b5c012 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -149,7 +149,7 @@ static int busname_add_default_default_dependencies(BusName *n) {
if (r < 0)
return r;
- if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -318,7 +318,7 @@ static int busname_open_fd(BusName *n) {
if (n->starter_fd >= 0)
return 0;
- mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user";
+ mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user";
n->starter_fd = bus_kernel_open_bus_fd(mode, &path);
if (n->starter_fd < 0)
return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus");
@@ -999,6 +999,14 @@ static bool busname_supported(void) {
return supported;
}
+static int busname_control_pid(Unit *u) {
+ BusName *n = BUSNAME(u);
+
+ assert(n);
+
+ return n->control_pid;
+}
+
static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_SUCCESS] = "success",
[BUSNAME_FAILURE_RESOURCES] = "resources",
@@ -1020,9 +1028,6 @@ const UnitVTable busname_vtable = {
"Install\0",
.private_section = "BusName",
- .no_alias = true,
- .no_instances = true,
-
.init = busname_init,
.done = busname_done,
.load = busname_load,
@@ -1052,6 +1057,8 @@ const UnitVTable busname_vtable = {
.supported = busname_supported,
+ .control_pid = busname_control_pid,
+
.bus_vtable = bus_busname_vtable,
.status_message_formats = {
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 9c34928052..25cc6962f9 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1265,7 +1265,7 @@ int manager_setup_cgroup(Manager *m) {
* it. This is to support live upgrades from older systemd
* versions where PID 1 was moved there. Also see
* cg_get_root_path(). */
- if (!e && m->running_as == MANAGER_SYSTEM) {
+ if (!e && MANAGER_IS_SYSTEM(m)) {
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
if (!e)
e = endswith(m->cgroup_root, "/system"); /* even more legacy */
@@ -1318,7 +1318,7 @@ int manager_setup_cgroup(Manager *m) {
(void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
- } else if (m->running_as == MANAGER_SYSTEM) {
+ } else if (MANAGER_IS_SYSTEM(m)) {
/* On the legacy hierarchy we only get
* notifications via cgroup agents. (Which
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 973a60187d..9dfca14914 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -837,9 +837,9 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
- if (isempty(uu)) {
+ if (isempty(uu))
c->user = mfree(c->user);
- } else {
+ else {
char *t;
t = strdup(uu);
@@ -864,9 +864,9 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
- if (isempty(gg)) {
+ if (isempty(gg))
c->group = mfree(c->group);
- } else {
+ else {
char *t;
t = strdup(gg);
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index fc50fafaad..0f54c6b84b 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -58,7 +58,7 @@ int bus_kill_context_set_transient_property(
k = kill_mode_from_string(m);
if (k < 0)
- return -EINVAL;
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m);
if (mode != UNIT_CHECK) {
c->kill_mode = k;
@@ -75,7 +75,7 @@ int bus_kill_context_set_transient_property(
if (r < 0)
return r;
- if (sig <= 0 || sig >= _NSIG)
+ if (!SIGNAL_VALID(sig))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig);
if (mode != UNIT_CHECK) {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 00372b92b4..d45f511489 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -642,6 +642,30 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
return bus_unit_method_set_properties(message, u, error);
}
+static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_get_processes(message, u, error);
+}
+
static int transient_unit_from_message(
Manager *m,
sd_bus_message *message,
@@ -865,7 +889,7 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e
return sd_bus_reply_method_return(message, NULL);
}
-static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) {
+static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *k;
@@ -905,6 +929,10 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
!strv_contains(states, unit_sub_state_to_string(u)))
continue;
+ if (!strv_isempty(patterns) &&
+ !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
+ continue;
+
unit_path = unit_dbus_path(u);
if (!unit_path)
return -ENOMEM;
@@ -939,7 +967,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
}
static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return list_units_filtered(message, userdata, error, NULL);
+ return list_units_filtered(message, userdata, error, NULL, NULL);
}
static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -950,7 +978,23 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- return list_units_filtered(message, userdata, error, states);
+ return list_units_filtered(message, userdata, error, states, NULL);
+}
+
+static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_units_filtered(message, userdata, error, states, patterns);
}
static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1187,7 +1231,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
m->exit_code = MANAGER_REBOOT;
@@ -1206,7 +1250,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
m->exit_code = MANAGER_POWEROFF;
@@ -1225,7 +1269,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
m->exit_code = MANAGER_HALT;
@@ -1244,7 +1288,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
m->exit_code = MANAGER_KEXEC;
@@ -1265,7 +1309,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager.");
r = sd_bus_message_read(message, "ss", &root, &init);
@@ -1433,7 +1477,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0)
+ if (MANAGER_IS_SYSTEM(m) && 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;
@@ -1441,7 +1485,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_reply_method_return(message, NULL);
}
-static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
UnitFileList *item;
@@ -1466,7 +1510,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu
if (!h)
return -ENOMEM;
- r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
+ r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns);
if (r < 0)
goto fail;
@@ -1494,11 +1538,30 @@ fail:
return r;
}
+static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return list_unit_files_by_patterns(message, userdata, error, NULL, NULL);
+}
+
+static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_unit_files_by_patterns(message, userdata, error, states, patterns);
+}
+
static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
UnitFileState state;
- UnitFileScope scope;
int r;
assert(message);
@@ -1514,9 +1577,7 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_get_state(scope, NULL, name, &state);
+ r = unit_file_get_state(m->unit_file_scope, NULL, name, &state);
if (r < 0)
return r;
@@ -1526,7 +1587,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *default_target = NULL;
Manager *m = userdata;
- UnitFileScope scope;
int r;
assert(message);
@@ -1538,9 +1598,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_get_default(scope, NULL, &default_target);
+ r = unit_file_get_default(m->unit_file_scope, NULL, &default_target);
if (r < 0)
return r;
@@ -1571,7 +1629,7 @@ static int reply_unit_file_changes_and_free(
unsigned i;
int r;
- if (n_changes > 0) {
+ if (unit_file_changes_have_modification(changes, n_changes)) {
r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL);
if (r < 0)
log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m");
@@ -1591,15 +1649,19 @@ static int reply_unit_file_changes_and_free(
if (r < 0)
goto fail;
- for (i = 0; i < n_changes; i++) {
- r = sd_bus_message_append(
- reply, "(sss)",
- unit_file_change_type_to_string(changes[i].type),
- changes[i].path,
- changes[i].source);
- if (r < 0)
- goto fail;
- }
+ for (i = 0; i < n_changes; i++)
+ if (changes[i].type >= 0) {
+ const char *change = unit_file_change_type_to_string(changes[i].type);
+ assert(change != NULL);
+
+ r = sd_bus_message_append(
+ reply, "(sss)",
+ change,
+ changes[i].path,
+ changes[i].source);
+ if (r < 0)
+ goto fail;
+ }
r = sd_bus_message_close_container(reply);
if (r < 0)
@@ -1613,10 +1675,61 @@ fail:
return r;
}
+/* Create an error reply, using the error information from changes[]
+ * if possible, and fall back to generating an error from error code c.
+ * The error message only describes the first error.
+ *
+ * Coordinate with unit_file_dump_changes() in install.c.
+ */
+static int install_error(
+ sd_bus_error *error,
+ int c,
+ UnitFileChange *changes,
+ unsigned n_changes) {
+ int r;
+ unsigned i;
+ assert(c < 0);
+
+ for (i = 0; i < n_changes; i++)
+ switch(changes[i].type) {
+ case 0 ... INT_MAX:
+ continue;
+ case -EEXIST:
+ if (changes[i].source)
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+ "File %s already exists and is a symlink to %s.",
+ changes[i].path, changes[i].source);
+ else
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+ "File %s already exists.",
+ changes[i].path);
+ goto found;
+ case -ERFKILL:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED,
+ "Unit file %s is masked.", changes[i].path);
+ goto found;
+ case -EADDRNOTAVAIL:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED,
+ "Unit %s is transient or generated.", changes[i].path);
+ goto found;
+ case -ELOOP:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED,
+ "Refusing to operate on linked unit file %s", changes[i].path);
+ goto found;
+ default:
+ r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path);
+ goto found;
+ }
+
+ r = c;
+ found:
+ unit_file_changes_free(changes, n_changes);
+ return r;
+}
+
static int method_enable_unit_files_generic(
sd_bus_message *message,
Manager *m,
- const char *verb,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes),
bool carries_install_info,
sd_bus_error *error) {
@@ -1624,7 +1737,6 @@ static int method_enable_unit_files_generic(
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int runtime, force, r;
assert(message);
@@ -1644,27 +1756,23 @@ static int method_enable_unit_files_generic(
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
- if (r == -ESHUTDOWN)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
+ r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes);
}
static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_enable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error);
}
static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_reenable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error);
}
static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_link, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error);
}
static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
@@ -1672,11 +1780,11 @@ static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, cons
}
static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_preset_without_mode, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error);
}
static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "disable", unit_file_mask, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error);
}
static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1686,7 +1794,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
- UnitFileScope scope;
int runtime, force, r;
const char *mode;
@@ -1715,26 +1822,22 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
+ r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, r, changes, n_changes);
}
static int method_disable_unit_files_generic(
sd_bus_message *message,
- Manager *m, const
- char *verb,
+ Manager *m,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes),
sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int r, runtime;
assert(message);
@@ -1748,34 +1851,58 @@ static int method_disable_unit_files_generic(
if (r < 0)
return r;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = call(scope, runtime, NULL, l, &changes, &n_changes);
+ r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "disable", unit_file_disable, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
}
static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error);
+}
+
+static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ Manager *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_unit_files_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes);
+ if (r < 0)
+ return install_error(error, r, changes, n_changes);
+
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
- UnitFileScope scope;
const char *name;
int force, r;
@@ -1796,11 +1923,9 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes);
+ r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1810,7 +1935,6 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
- UnitFileScope scope;
const char *mode;
int force, runtime, r;
@@ -1839,13 +1963,9 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
- if (r < 0) {
- unit_file_changes_free(changes, n_changes);
- return r;
- }
+ r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes);
+ if (r < 0)
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1855,10 +1975,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
Manager *m = userdata;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int runtime, force, r;
- char *target;
- char *type;
+ char *target, *type;
UnitDependency dep;
assert(message);
@@ -1882,13 +2000,9 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
if (dep < 0)
return -EINVAL;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
- if (r == -ESHUTDOWN)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
+ r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1924,7 +2038,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0),
SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0),
@@ -1936,7 +2050,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1992,12 +2107,14 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2016,6 +2133,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2025,6 +2143,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
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),
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index d33e494f6b..bb09a515f8 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -149,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index bc121b83a2..a0e61b023e 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -156,7 +156,7 @@ static int property_get_next_elapse_monotonic(
usec_t a, b;
a = now(CLOCK_MONOTONIC);
- b = now(CLOCK_BOOTTIME);
+ b = now(clock_boottime_or_monotonic());
if (t->next_elapse_monotonic_or_boottime + a > b)
x = t->next_elapse_monotonic_or_boottime + a - b;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index b351f6a2c2..e912fe2192 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -24,9 +24,12 @@
#include "cgroup-util.h"
#include "dbus-unit.h"
#include "dbus.h"
+#include "fd-util.h"
#include "locale-util.h"
#include "log.h"
+#include "process-util.h"
#include "selinux-access.h"
+#include "signal-util.h"
#include "special.h"
#include "string-util.h"
#include "strv.h"
@@ -547,7 +550,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
}
- if (signo <= 0 || signo >= _NSIG)
+ if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
r = bus_verify_manage_units_async_full(
@@ -701,7 +704,8 @@ 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("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -840,6 +844,145 @@ static int property_get_cgroup(
return sd_bus_message_append(reply, "s", t);
}
+static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) {
+ _cleanup_free_ char *buf = NULL, *cmdline = NULL;
+ int r;
+
+ assert(reply);
+ assert(pid > 0);
+
+ r = set_put(pids, PID_TO_PTR(pid));
+ if (r == -EEXIST || r == 0)
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (!p) {
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf);
+ if (r == -ESRCH)
+ return 0;
+ if (r < 0)
+ return r;
+
+ p = buf;
+ }
+
+ (void) get_process_cmdline(pid, 0, true, &cmdline);
+
+ return sd_bus_message_append(reply,
+ "(sus)",
+ p,
+ (uint32_t) pid,
+ cmdline);
+}
+
+static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(reply);
+ assert(p);
+
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f);
+ if (r == ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t pid;
+
+ r = cg_read_pid(f, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (is_kernel_thread(pid) > 0)
+ continue;
+
+ r = append_process(reply, p, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *g = NULL, *j = NULL;
+
+ r = cg_read_subgroup(d, &g);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ j = strjoin(p, "/", g, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ r = append_cgroup(reply, j, pids);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(set_freep) Set *pids = NULL;
+ Unit *u = userdata;
+ pid_t pid;
+ int r;
+
+ assert(message);
+
+ pids = set_new(NULL);
+ if (!pids)
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(sus)");
+ if (r < 0)
+ return r;
+
+ if (u->cgroup_path) {
+ r = append_cgroup(reply, u->cgroup_path, pids);
+ if (r < 0)
+ return r;
+ }
+
+ /* The main and control pids might live outside of the cgroup, hence fetch them separately */
+ pid = unit_main_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ pid = unit_control_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
@@ -847,6 +990,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+ SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@@ -1002,7 +1146,6 @@ int bus_unit_queue_job(
type = JOB_TRY_RELOAD;
}
-
if (type == JOB_STOP &&
(u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
unit_active_state(u) == UNIT_INACTIVE)
@@ -1099,7 +1242,10 @@ static int bus_unit_set_transient_property(
if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
- r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be
+ * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare()
+ * instead of manager_load_unit() on purpose, here. */
+ r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice);
if (r < 0)
return r;
@@ -1259,6 +1405,7 @@ int bus_unit_set_properties(
}
int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
+ assert(u);
if (u->load_state == UNIT_LOADED)
return 0;
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 07948b9cd0..4db88dbebc 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -36,5 +36,6 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 413489373f..263955d874 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -112,7 +112,7 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus
manager_notify_cgroup_empty(m, cgroup);
/* if running as system-instance, forward under our name */
- if (m->running_as == MANAGER_SYSTEM && m->system_bus) {
+ if (MANAGER_IS_SYSTEM(m) && m->system_bus) {
r = sd_bus_message_rewind(message, 1);
if (r >= 0)
r = sd_bus_send(m->system_bus, message, NULL);
@@ -690,7 +690,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* When we run as system instance we get the Released
* signal via a direct connection */
@@ -864,10 +864,10 @@ static int bus_init_api(Manager *m) {
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == MANAGER_SYSTEM && m->system_bus)
+ if (MANAGER_IS_SYSTEM(m) && m->system_bus)
bus = sd_bus_ref(m->system_bus);
else {
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
r = sd_bus_open_system(&bus);
else
r = sd_bus_open_user(&bus);
@@ -907,7 +907,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
assert(bus);
/* On kdbus or if we are a user instance we get the Released message via the system bus */
- if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) {
+ if (MANAGER_IS_USER(m) || m->kdbus_fd >= 0) {
r = sd_bus_add_match(
bus,
NULL,
@@ -932,7 +932,7 @@ static int bus_init_system(Manager *m) {
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == MANAGER_SYSTEM && m->api_bus) {
+ if (MANAGER_IS_SYSTEM(m) && m->api_bus) {
m->system_bus = sd_bus_ref(m->api_bus);
return 0;
}
@@ -983,7 +983,7 @@ static int bus_init_private(Manager *m) {
if (m->kdbus_fd >= 0)
return 0;
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* We want the private bus only when running as init */
if (getpid() != 1)
@@ -1082,7 +1082,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
/* Possibly flush unwritten data, but only if we are
* unprivileged, since we don't want to sync here */
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
sd_bus_flush(*bus);
/* And destroy the object */
diff --git a/src/core/device.c b/src/core/device.c
index 28e4039da2..16e56efcc3 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -265,7 +265,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
assert(u);
assert(dev);
- property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
+ property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
wants = udev_device_get_property_value(dev, property);
if (!wants)
return 0;
@@ -322,7 +322,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
/* This unit is in plugged state: we're sure it's
* attached to a device. */
if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
- log_unit_error(u, "Dev %s appeared twice with different sysfs paths %s and %s",
+ log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s",
e, DEVICE(u)->sysfs, sysfs);
return -EEXIST;
}
@@ -841,8 +841,6 @@ const UnitVTable device_vtable = {
"Device\0"
"Install\0",
- .no_instances = true,
-
.init = device_init,
.done = device_done,
.load = unit_load_fragment_and_dropin_optional,
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
index bb2bc3f399..ddae46190f 100644
--- a/src/core/failure-action.c
+++ b/src/core/failure-action.c
@@ -47,7 +47,7 @@ int failure_action(
if (action == FAILURE_ACTION_NONE)
return -ECANCELED;
- if (m->running_as == MANAGER_USER) {
+ if (!MANAGER_IS_SYSTEM(m)) {
/* Downgrade all options to simply exiting if we run
* in user mode */
@@ -61,17 +61,17 @@ int failure_action(
case FAILURE_ACTION_REBOOT:
log_and_status(m, "Rebooting as result of failure.");
- update_reboot_param_file(reboot_arg);
- (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET,
- JOB_REPLACE_IRREVERSIBLY, NULL);
+ (void) update_reboot_parameter_and_warn(reboot_arg);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
case FAILURE_ACTION_REBOOT_FORCE:
log_and_status(m, "Forcibly rebooting as result of failure.");
- update_reboot_param_file(reboot_arg);
+ (void) update_reboot_parameter_and_warn(reboot_arg);
m->exit_code = MANAGER_REBOOT;
+
break;
case FAILURE_ACTION_REBOOT_IMMEDIATE:
@@ -79,9 +79,10 @@ int failure_action(
sync();
- if (reboot_arg) {
+ if (!isempty(reboot_arg)) {
log_info("Rebooting with argument '%s'.", reboot_arg);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg);
+ log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
}
log_info("Rebooting.");
@@ -90,8 +91,7 @@ int failure_action(
case FAILURE_ACTION_POWEROFF:
log_and_status(m, "Powering off as result of failure.");
- (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET,
- JOB_REPLACE_IRREVERSIBLY, NULL);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
case FAILURE_ACTION_POWEROFF_FORCE:
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index ff7558d500..d1b0ce76ef 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -3,7 +3,7 @@
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
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
diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h
index 3bad74b246..472b58cb00 100644
--- a/src/core/ima-setup.h
+++ b/src/core/ima-setup.h
@@ -5,7 +5,7 @@
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
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
diff --git a/src/core/job.c b/src/core/job.c
index 5557a6a942..d9c5669c9f 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -137,7 +137,7 @@ void job_uninstall(Job *j) {
/* Detach from next 'bigger' objects */
/* daemon-reload should be transparent to job observers */
- if (j->manager->n_reloading <= 0)
+ if (!MANAGER_IS_RELOADING(j->manager))
bus_job_send_removed_signal(j);
*pj = NULL;
@@ -1156,7 +1156,7 @@ void job_shutdown_magic(Job *j) {
if (j->type != JOB_START)
return;
- if (j->unit->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(j->unit->manager))
return;
if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 22b71b6f5e..f83fa09301 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -44,6 +44,7 @@ static int add_dependency_consumer(
}
int unit_load_dropin(Unit *u) {
+ _cleanup_strv_free_ char **l = NULL;
Iterator i;
char *t, **f;
int r;
@@ -55,7 +56,7 @@ int unit_load_dropin(Unit *u) {
SET_FOREACH(t, u->names, i) {
char **p;
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS,
add_dependency_consumer, u, NULL);
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES,
@@ -63,11 +64,19 @@ int unit_load_dropin(Unit *u) {
}
}
- u->dropin_paths = strv_free(u->dropin_paths);
- r = unit_find_dropin_paths(u, &u->dropin_paths);
+ r = unit_find_dropin_paths(u, &l);
if (r <= 0)
return 0;
+ if (!u->dropin_paths) {
+ u->dropin_paths = l;
+ l = NULL;
+ } else {
+ r = strv_extend_strv(&u->dropin_paths, l, true);
+ if (r < 0)
+ return log_oom();
+ }
+
STRV_FOREACH(f, u->dropin_paths) {
config_parse(u->id, *f, NULL,
UNIT_VTABLE(u)->sections,
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index d8a4aefbb3..942d26724e 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -25,7 +25,7 @@
/* Read service data supplementary drop-in directories */
static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
- return unit_file_find_dropin_paths(u->manager->lookup_paths.unit_path,
+ return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path,
u->manager->unit_path_cache,
u->names,
paths);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5568b4696f..928b913c7b 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -164,6 +164,8 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LE
Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
+m4_dnl The following is a legacy alias name for compatibility
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
@@ -220,6 +222,7 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0,
Service.TimeoutStopSec, config_parse_service_timeout, 0, 0
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
+m4_dnl The following three only exist for compatibility, they moved into Unit, see above
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
@@ -297,6 +300,8 @@ Socket.RemoveOnStop, config_parse_bool, 0,
Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
Socket.FileDescriptorName, config_parse_fdname, 0, 0
Socket.Service, config_parse_socket_service, 0, 0
+Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
+Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
m4_ifdef(`HAVE_SMACK',
`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index f1a874cfdf..1a8c03904c 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2495,7 +2495,7 @@ int config_parse_syscall_filter(
/* Turn on NNP, but only if it wasn't configured explicitly
* before, and only if we are in user mode. */
- if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
+ if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
c->no_new_privileges = true;
return 0;
@@ -2847,11 +2847,12 @@ int config_parse_device_allow(
void *data,
void *userdata) {
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *path = NULL, *t = NULL;
CGroupContext *c = data;
CGroupDeviceAllow *a;
- const char *m;
+ const char *m = NULL;
size_t n;
+ int r;
if (isempty(rvalue)) {
while (c->device_allow)
@@ -2860,8 +2861,16 @@ int config_parse_device_allow(
return 0;
}
- n = strcspn(rvalue, WHITESPACE);
- path = strndup(rvalue, n);
+ r = unit_full_printf(userdata, rvalue, &t);
+ if(r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to resolve specifiers in %s, ignoring: %m",
+ rvalue);
+ }
+
+ n = strcspn(t, WHITESPACE);
+
+ path = strndup(t, n);
if (!path)
return log_oom();
@@ -2872,7 +2881,7 @@ int config_parse_device_allow(
return 0;
}
- m = rvalue + n + strspn(rvalue + n, WHITESPACE);
+ m = t + n + strspn(t + n, WHITESPACE);
if (isempty(m))
m = "rwm";
@@ -3418,10 +3427,10 @@ int config_parse_protect_system(
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
+ char *id = NULL;
unsigned c = 0;
int fd, r;
FILE *f;
- char *id = NULL;
assert(filename);
assert(*filename);
@@ -3443,7 +3452,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
* the names of this unit, but only if it is a valid
* unit name. */
name = basename(*filename);
-
if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
id = set_get(names, name);
@@ -3483,6 +3491,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
*_f = f;
*_final = id;
+
return 0;
}
@@ -3543,13 +3552,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
}
static int load_from_path(Unit *u, const char *path) {
- int r;
_cleanup_set_free_free_ Set *symlink_names = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *filename = NULL;
char *id = NULL;
Unit *merged;
struct stat st;
+ int r;
assert(u);
assert(path);
@@ -3574,7 +3583,7 @@ static int load_from_path(Unit *u, const char *path) {
} else {
char **p;
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
/* Instead of opening the path right away, we manually
* follow all symlinks and add their name to our unit
@@ -3588,18 +3597,14 @@ static int load_from_path(Unit *u, const char *path) {
r = -ENOENT;
else
r = open_follow(&filename, &f, symlink_names, &id);
+ if (r >= 0)
+ break;
+ filename = mfree(filename);
+ if (r != -ENOENT)
+ return r;
- if (r < 0) {
- filename = mfree(filename);
- if (r != -ENOENT)
- return r;
-
- /* Empty the symlink names for the next run */
- set_clear_free(symlink_names);
- continue;
- }
-
- break;
+ /* Empty the symlink names for the next run */
+ set_clear_free(symlink_names);
}
}
@@ -3607,6 +3612,11 @@ static int load_from_path(Unit *u, const char *path) {
/* Hmm, no suitable file found? */
return 0;
+ if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
+ log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
+ return -ELOOP;
+ }
+
merged = u;
r = merge_by_names(&merged, symlink_names, id);
if (r < 0)
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 9e6b3d3292..0145fe2894 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -101,14 +101,23 @@ static int read_machine_id(int fd, char id[34]) {
return 0;
}
-static int write_machine_id(int fd, char id[34]) {
+static int write_machine_id(int fd, const char id[34]) {
+ int r;
+
assert(fd >= 0);
assert(id);
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
- return loop_write(fd, id, 33, false);
+ r = loop_write(fd, id, 33, false);
+ if (r < 0)
+ return r;
+
+ if (fsync(fd) < 0)
+ return -errno;
+
+ return 0;
}
static int generate_machine_id(char id[34], const char *root) {
@@ -120,10 +129,7 @@ static int generate_machine_id(char id[34], const char *root) {
assert(id);
- if (isempty(root))
- dbus_machine_id = "/var/lib/dbus/machine-id";
- else
- dbus_machine_id = strjoina(root, "/var/lib/dbus/machine-id");
+ dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
/* First, try reading the D-Bus machine id, unless it is a symlink */
fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
@@ -203,18 +209,8 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) {
char id[34]; /* 32 + \n + \0 */
int r;
- if (isempty(root)) {
- etc_machine_id = "/etc/machine-id";
- run_machine_id = "/run/machine-id";
- } else {
- char *x;
-
- x = strjoina(root, "/etc/machine-id");
- etc_machine_id = path_kill_slashes(x);
-
- x = strjoina(root, "/run/machine-id");
- run_machine_id = path_kill_slashes(x);
- }
+ etc_machine_id = prefix_roota(root, "/etc/machine-id");
+ run_machine_id = prefix_roota(root, "/run/machine-id");
RUN_WITH_UMASK(0000) {
/* We create this 0444, to indicate that this isn't really
@@ -274,10 +270,10 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) {
RUN_WITH_UMASK(0022) {
r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE);
- }
- if (r < 0) {
- (void) unlink(run_machine_id);
- return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
+ if (r < 0) {
+ (void) unlink(run_machine_id);
+ return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
+ }
}
/* And now, let's mount it over */
@@ -301,14 +297,7 @@ int machine_id_commit(const char *root) {
char id[34]; /* 32 + \n + \0 */
int r;
- if (isempty(root))
- etc_machine_id = "/etc/machine-id";
- else {
- char *x;
-
- x = strjoina(root, "/etc/machine-id");
- etc_machine_id = path_kill_slashes(x);
- }
+ etc_machine_id = prefix_roota(root, "/etc/machine-id");
r = path_is_mount_point(etc_machine_id, 0);
if (r < 0)
diff --git a/src/core/main.c b/src/core/main.c
index 56df32426a..ed4d42c8cc 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -81,6 +81,7 @@
#include "strv.h"
#include "switch-root.h"
#include "terminal-util.h"
+#include "umask-util.h"
#include "user-util.h"
#include "virt.h"
#include "watchdog.h"
@@ -94,7 +95,7 @@ static enum {
ACTION_DONE
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
-static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
+static bool arg_system = false;
static bool arg_dump_core = true;
static int arg_crash_chvt = -1;
static bool arg_crash_shell = false;
@@ -288,6 +289,7 @@ static int parse_crash_chvt(const char *value) {
}
static int set_machine_id(const char *m) {
+ assert(m);
if (sd_id128_from_string(m, &arg_machine_id) < 0)
return -EINVAL;
@@ -412,6 +414,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
target = runlevel_to_target(key);
if (target)
return free_and_strdup(&arg_default_unit, target);
+
+ } else if (streq(key, "systemd.default_timeout_start_sec") && value) {
+
+ r = parse_sec(value, &arg_default_timeout_start_usec);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
+
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
}
return 0;
@@ -659,7 +670,8 @@ static int parse_config_file(void) {
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
- { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval },
+ { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
+ { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval },
{ "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
{ "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
{ "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit },
@@ -688,11 +700,11 @@ static int parse_config_file(void) {
const char *fn, *conf_dirs_nulstr;
- fn = arg_running_as == MANAGER_SYSTEM ?
+ fn = arg_system ?
PKGSYSCONFDIR "/system.conf" :
PKGSYSCONFDIR "/user.conf";
- conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ?
+ conf_dirs_nulstr = arg_system ?
CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d");
@@ -866,11 +878,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SYSTEM:
- arg_running_as = MANAGER_SYSTEM;
+ arg_system = true;
break;
case ARG_USER:
- arg_running_as = MANAGER_USER;
+ arg_system = false;
break;
case ARG_TEST:
@@ -1237,7 +1249,8 @@ static int write_container_id(void) {
if (isempty(c))
return 0;
- r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
+ RUN_WITH_UMASK(0022)
+ r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
if (r < 0)
return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
@@ -1336,7 +1349,7 @@ int main(int argc, char *argv[]) {
saved_argv = argv;
saved_argc = argc;
- log_show_color(isatty(STDERR_FILENO) > 0);
+ log_show_color(colors_enabled());
log_set_upgrade_syslog_to_journal(true);
/* Disable the umask logic */
@@ -1346,7 +1359,7 @@ int main(int argc, char *argv[]) {
if (getpid() == 1 && detect_container() <= 0) {
/* Running outside of a container as PID 1 */
- arg_running_as = MANAGER_SYSTEM;
+ arg_system = true;
make_null_stdio();
log_set_target(LOG_TARGET_KMSG);
log_open();
@@ -1430,7 +1443,7 @@ int main(int argc, char *argv[]) {
} else if (getpid() == 1) {
/* Running inside a container, as PID 1 */
- arg_running_as = MANAGER_SYSTEM;
+ arg_system = true;
log_set_target(LOG_TARGET_CONSOLE);
log_close_console(); /* force reopen of /dev/console */
log_open();
@@ -1443,7 +1456,7 @@ int main(int argc, char *argv[]) {
kernel_timestamp = DUAL_TIMESTAMP_NULL;
} else {
/* Running as user instance */
- arg_running_as = MANAGER_USER;
+ arg_system = false;
log_set_target(LOG_TARGET_AUTO);
log_open();
@@ -1501,7 +1514,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_system) {
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -1522,14 +1535,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_USER &&
+ if (!arg_system &&
arg_action == ACTION_RUN &&
sd_booted() <= 0) {
log_error("Trying to run as user instance, but the system has not been booted with systemd.");
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM &&
+ if (arg_system &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
@@ -1557,7 +1570,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_USER &&
+ if (!arg_system &&
!getenv("XDG_RUNTIME_DIR")) {
log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
goto finish;
@@ -1580,7 +1593,7 @@ int main(int argc, char *argv[]) {
if (arg_serialization)
assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
- if (arg_running_as == MANAGER_SYSTEM)
+ if (arg_system)
/* Become a session leader if we aren't one yet. */
setsid();
@@ -1589,7 +1602,7 @@ int main(int argc, char *argv[]) {
/* Reset the console, but only if this is really init and we
* are freshly booted */
- if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
+ if (arg_system && arg_action == ACTION_RUN) {
/* If we are init, we connect stdin/stdout/stderr to
* /dev/null and make sure we don't have a controlling
@@ -1616,7 +1629,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_system) {
int v;
log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
@@ -1652,7 +1665,7 @@ int main(int argc, char *argv[]) {
arg_action == ACTION_TEST ? " test" : "", getuid(), t);
}
- if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
+ if (arg_system && !skip_setup) {
if (arg_show_status > 0)
status_welcome();
@@ -1664,7 +1677,7 @@ int main(int argc, char *argv[]) {
test_usr();
}
- if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
+ if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
watchdog_set_timeout(&arg_runtime_watchdog);
if (arg_timer_slack_nsec != NSEC_INFINITY)
@@ -1694,12 +1707,12 @@ int main(int argc, char *argv[]) {
}
}
- if (arg_running_as == MANAGER_USER)
+ if (!arg_system)
/* Become reaper of our children */
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
log_warning_errno(errno, "Failed to make us a subreaper: %m");
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_system) {
bump_rlimit_nofile(&saved_rlimit_nofile);
if (empty_etc) {
@@ -1711,7 +1724,7 @@ int main(int argc, char *argv[]) {
}
}
- r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m);
+ r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m);
if (r < 0) {
log_emergency_errno(r, "Failed to allocate manager object: %m");
error_message = "Failed to allocate manager object";
@@ -1874,7 +1887,7 @@ int main(int argc, char *argv[]) {
case MANAGER_EXIT:
retval = m->return_value;
- if (m->running_as == MANAGER_USER) {
+ if (MANAGER_IS_USER(m)) {
log_debug("Exit.");
goto finish;
}
@@ -1970,7 +1983,7 @@ finish:
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
- args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
+ args[i++] = arg_system ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index e739795e70..bd00c224f4 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -49,6 +49,7 @@
#include "dbus-manager.h"
#include "dbus-unit.h"
#include "dbus.h"
+#include "dirent-util.h"
#include "env-util.h"
#include "escape.h"
#include "exit-status.h"
@@ -63,6 +64,7 @@
#include "manager.h"
#include "missing.h"
#include "mkdir.h"
+#include "mkdir.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
@@ -98,7 +100,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m);
-static void manager_undo_generators(Manager *m);
static void manager_watch_jobs_in_progress(Manager *m) {
usec_t next;
@@ -491,7 +492,7 @@ static int manager_setup_signals(Manager *m) {
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
return enable_special_signals(m);
return 0;
@@ -518,7 +519,7 @@ static void manager_clean_environment(Manager *m) {
static int manager_default_environment(Manager *m) {
assert(m);
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* The system manager always starts with a clean
* environment for its children. It does not import
* the kernel or the parents exported variables.
@@ -547,43 +548,36 @@ static int manager_default_environment(Manager *m) {
}
-int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
-
- static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
- [MANAGER_SYSTEM] = "UNIT=",
- [MANAGER_USER] = "USER_UNIT=",
- };
-
- static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
- [MANAGER_SYSTEM] = "UNIT=%s",
- [MANAGER_USER] = "USER_UNIT=%s",
- };
-
+int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
Manager *m;
int r;
assert(_m);
- assert(running_as >= 0);
- assert(running_as < _MANAGER_RUNNING_AS_MAX);
+ assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER));
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
-#ifdef ENABLE_EFI
- if (running_as == MANAGER_SYSTEM && detect_container() <= 0)
- boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
-#endif
-
- m->running_as = running_as;
+ m->unit_file_scope = scope;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
m->default_tasks_accounting = true;
m->default_tasks_max = UINT64_C(512);
+#ifdef ENABLE_EFI
+ if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
+ boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
+#endif
+
/* Prepare log fields we can use for structured logging */
- m->unit_log_field = unit_log_fields[running_as];
- m->unit_log_format_string = unit_log_format_strings[running_as];
+ if (MANAGER_IS_SYSTEM(m)) {
+ m->unit_log_field = "UNIT=";
+ m->unit_log_format_string = "UNIT=%s";
+ } else {
+ m->unit_log_field = "USER_UNIT=";
+ m->unit_log_format_string = "USER_UNIT=%s";
+ }
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
@@ -683,6 +677,7 @@ static int manager_setup_notify(Manager *m) {
.sa.sa_family = AF_UNIX,
};
static const int one = 1;
+ const char *e;
/* First free all secondary fields */
m->notify_socket = mfree(m->notify_socket);
@@ -694,19 +689,13 @@ static int manager_setup_notify(Manager *m) {
fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
- if (m->running_as == MANAGER_SYSTEM)
- m->notify_socket = strdup("/run/systemd/notify");
- else {
- const char *e;
-
- e = getenv("XDG_RUNTIME_DIR");
- if (!e) {
- log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m");
- return -EINVAL;
- }
-
- m->notify_socket = strappend(e, "/systemd/notify");
+ e = manager_get_runtime_prefix(m);
+ if (!e) {
+ log_error("Failed to determine runtime prefix.");
+ return -EINVAL;
}
+
+ m->notify_socket = strappend(e, "/systemd/notify");
if (!m->notify_socket)
return log_oom();
@@ -756,8 +745,8 @@ static int manager_setup_kdbus(Manager *m) {
return -ESOCKTNOSUPPORT;
m->kdbus_fd = bus_kernel_create_bus(
- m->running_as == MANAGER_SYSTEM ? "system" : "user",
- m->running_as == MANAGER_SYSTEM, &p);
+ MANAGER_IS_SYSTEM(m) ? "system" : "user",
+ MANAGER_IS_SYSTEM(m), &p);
if (m->kdbus_fd < 0)
return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
@@ -778,7 +767,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
try_bus_connect =
m->kdbus_fd >= 0 ||
reexecuting ||
- (m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
+ (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"));
/* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
@@ -940,7 +929,7 @@ Manager* manager_free(Manager *m) {
* around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
- manager_undo_generators(m);
+ lookup_paths_flush_generator(&m->lookup_paths);
bus_done(m);
@@ -1037,7 +1026,6 @@ static void manager_coldplug(Manager *m) {
static void manager_build_unit_path_cache(Manager *m) {
char **i;
- _cleanup_closedir_ DIR *d = NULL;
int r;
assert(m);
@@ -1046,29 +1034,27 @@ static void manager_build_unit_path_cache(Manager *m) {
m->unit_path_cache = set_new(&string_hash_ops);
if (!m->unit_path_cache) {
- log_error("Failed to allocate unit path cache.");
- return;
+ r = -ENOMEM;
+ goto fail;
}
/* This simply builds a list of files we know exist, so that
* we don't always have to go to disk */
- STRV_FOREACH(i, m->lookup_paths.unit_path) {
+ STRV_FOREACH(i, m->lookup_paths.search_path) {
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
d = opendir(*i);
if (!d) {
if (errno != ENOENT)
- log_error_errno(errno, "Failed to open directory %s: %m", *i);
+ log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i);
continue;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, r = -errno; goto fail) {
char *p;
- if (hidden_file(de->d_name))
- continue;
-
p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
if (!p) {
r = -ENOMEM;
@@ -1079,20 +1065,15 @@ static void manager_build_unit_path_cache(Manager *m) {
if (r < 0)
goto fail;
}
-
- d = safe_closedir(d);
}
return;
fail:
- log_error_errno(r, "Failed to build unit path cache: %m");
-
- set_free_free(m->unit_path_cache);
- m->unit_path_cache = NULL;
+ log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m");
+ m->unit_path_cache = set_free_free(m->unit_path_cache);
}
-
static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
Unit *u;
@@ -1116,21 +1097,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
assert(m);
- dual_timestamp_get(&m->generators_start_timestamp);
- r = manager_run_generators(m);
- dual_timestamp_get(&m->generators_finish_timestamp);
+ r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
+ if (r < 0)
+ return r;
+
+ /* Make sure the transient directory always exists, so that it remains in the search path */
+ r = mkdir_p_label(m->lookup_paths.transient, 0755);
if (r < 0)
return r;
- r = lookup_paths_init(
- &m->lookup_paths, m->running_as, true,
- NULL,
- m->generator_unit_path,
- m->generator_unit_path_early,
- m->generator_unit_path_late);
+ dual_timestamp_get(&m->generators_start_timestamp);
+ r = manager_run_generators(m);
+ dual_timestamp_get(&m->generators_finish_timestamp);
if (r < 0)
return r;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
@@ -1744,7 +1726,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
}
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
+ (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
? LOG_DEBUG : LOG_INFO,
&sfsi);
@@ -1755,7 +1737,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGTERM:
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
@@ -1765,7 +1747,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
/* Fall through */
case SIGINT:
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* If the user presses C-A-D more than
* 7 times within 2s, we reboot
@@ -1791,14 +1773,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGWINCH:
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGPWR:
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
@@ -1906,7 +1888,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case 24:
- if (m->running_as == MANAGER_USER) {
+ if (MANAGER_IS_USER(m)) {
m->exit_code = MANAGER_EXIT;
return 0;
}
@@ -2022,7 +2004,7 @@ int manager_loop(Manager *m) {
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM)
+ if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
watchdog_ping();
if (!ratelimit_test(&rl)) {
@@ -2047,7 +2029,7 @@ int manager_loop(Manager *m) {
continue;
/* Sleep for half the watchdog time */
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM) {
+ if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
@@ -2118,7 +2100,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
const char *msg;
int audit_fd, r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
audit_fd = get_audit_fd();
@@ -2127,7 +2109,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
/* Don't generate audit events if the service was already
* started and we're just deserializing */
- if (m->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(m))
return;
if (u->type != UNIT_SERVICE)
@@ -2161,10 +2143,10 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
/* Don't generate plymouth events if the service was already
* started and we're just deserializing */
- if (m->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(m))
return;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
if (detect_container() > 0)
@@ -2208,8 +2190,8 @@ int manager_open_serialization(Manager *m, FILE **_f) {
assert(_f);
- path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
- fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
+ path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -2539,23 +2521,19 @@ int manager_reload(Manager *m) {
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
- manager_undo_generators(m);
+ lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths);
- /* Find new unit paths */
- q = manager_run_generators(m);
+ q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
if (q < 0 && r >= 0)
r = q;
- q = lookup_paths_init(
- &m->lookup_paths, m->running_as, true,
- NULL,
- m->generator_unit_path,
- m->generator_unit_path_early,
- m->generator_unit_path_late);
+ /* Find new unit paths */
+ q = manager_run_generators(m);
if (q < 0 && r >= 0)
r = q;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
@@ -2589,12 +2567,6 @@ int manager_reload(Manager *m) {
return r;
}
-bool manager_is_reloading_or_reexecuting(Manager *m) {
- assert(m);
-
- return m->n_reloading != 0;
-}
-
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
@@ -2626,7 +2598,7 @@ static void manager_notify_finished(Manager *m) {
if (m->test_run)
return;
- if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) {
+ if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
/* Note that m->kernel_usec.monotonic is always at 0,
* and m->firmware_usec.monotonic and
@@ -2691,7 +2663,7 @@ static void manager_notify_finished(Manager *m) {
void manager_check_finished(Manager *m) {
assert(m);
- if (m->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(m))
return;
/* Verify that we are actually running currently. Initially
@@ -2732,77 +2704,6 @@ void manager_check_finished(Manager *m) {
manager_invalidate_startup_units(m);
}
-static int create_generator_dir(Manager *m, char **generator, const char *name) {
- char *p;
- int r;
-
- assert(m);
- assert(generator);
- assert(name);
-
- if (*generator)
- return 0;
-
- if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
- /* systemd --system, not running --test */
-
- p = strappend("/run/systemd/", name);
- if (!p)
- return log_oom();
-
- r = mkdir_p_label(p, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create generator directory %s: %m", p);
- free(p);
- return r;
- }
- } else if (m->running_as == MANAGER_USER) {
- const char *s = NULL;
-
- s = getenv("XDG_RUNTIME_DIR");
- if (!s)
- return -EINVAL;
- p = strjoin(s, "/systemd/", name, NULL);
- if (!p)
- return log_oom();
-
- r = mkdir_p_label(p, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create generator directory %s: %m", p);
- free(p);
- return r;
- }
- } else {
- /* systemd --system --test */
-
- p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
- if (!p)
- return log_oom();
-
- if (!mkdtemp(p)) {
- log_error_errno(errno, "Failed to create generator directory %s: %m", p);
- free(p);
- return -errno;
- }
- }
-
- *generator = p;
- return 0;
-}
-
-static void trim_generator_dir(Manager *m, char **generator) {
- assert(m);
- assert(generator);
-
- if (!*generator)
- return;
-
- if (rmdir(*generator) >= 0)
- *generator = mfree(*generator);
-
- return;
-}
-
static int manager_run_generators(Manager *m) {
_cleanup_strv_free_ char **paths = NULL;
const char *argv[5];
@@ -2814,71 +2715,40 @@ static int manager_run_generators(Manager *m) {
if (m->test_run)
return 0;
- paths = generator_paths(m->running_as);
+ paths = generator_binary_paths(m->unit_file_scope);
if (!paths)
return log_oom();
/* Optimize by skipping the whole process by not creating output directories
* if no generators are found. */
STRV_FOREACH(path, paths) {
- r = access(*path, F_OK);
- if (r == 0)
+ if (access(*path, F_OK) >= 0)
goto found;
if (errno != ENOENT)
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
}
+
return 0;
found:
- r = create_generator_dir(m, &m->generator_unit_path, "generator");
- if (r < 0)
- goto finish;
-
- r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
- if (r < 0)
- goto finish;
-
- r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
+ r = lookup_paths_mkdir_generator(&m->lookup_paths);
if (r < 0)
goto finish;
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
- argv[1] = m->generator_unit_path;
- argv[2] = m->generator_unit_path_early;
- argv[3] = m->generator_unit_path_late;
+ argv[1] = m->lookup_paths.generator;
+ argv[2] = m->lookup_paths.generator_early;
+ argv[3] = m->lookup_paths.generator_late;
argv[4] = NULL;
RUN_WITH_UMASK(0022)
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
finish:
- trim_generator_dir(m, &m->generator_unit_path);
- trim_generator_dir(m, &m->generator_unit_path_early);
- trim_generator_dir(m, &m->generator_unit_path_late);
+ lookup_paths_trim_generator(&m->lookup_paths);
return r;
}
-static void remove_generator_dir(Manager *m, char **generator) {
- assert(m);
- assert(generator);
-
- if (!*generator)
- return;
-
- strv_remove(m->lookup_paths.unit_path, *generator);
- (void) rm_rf(*generator, REMOVE_ROOT);
-
- *generator = mfree(*generator);
-}
-
-static void manager_undo_generators(Manager *m) {
- assert(m);
-
- remove_generator_dir(m, &m->generator_unit_path);
- remove_generator_dir(m, &m->generator_unit_path_early);
- remove_generator_dir(m, &m->generator_unit_path_late);
-}
-
int manager_environment_add(Manager *m, char **minus, char **plus) {
char **a = NULL, **b = NULL, **l;
assert(m);
@@ -2941,7 +2811,7 @@ void manager_recheck_journal(Manager *m) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
@@ -2965,7 +2835,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
assert(m);
assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
if (m->show_status != mode)
@@ -2982,7 +2852,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
static bool manager_get_show_status(Manager *m, StatusType type) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return false;
if (m->no_console_output)
@@ -3004,7 +2874,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
void manager_set_first_boot(Manager *m, bool b) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
if (m->first_boot != (int) b) {
@@ -3050,7 +2920,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
const char *manager_get_runtime_prefix(Manager *m) {
assert(m);
- return m->running_as == MANAGER_SYSTEM ?
+ return MANAGER_IS_SYSTEM(m) ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}
diff --git a/src/core/manager.h b/src/core/manager.h
index 9803f73129..17f84e6963 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -140,6 +140,7 @@ struct Manager {
sd_event_source *jobs_in_progress_event_source;
+ UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Set *unit_path_cache;
@@ -162,10 +163,6 @@ struct Manager {
dual_timestamp units_load_start_timestamp;
dual_timestamp units_load_finish_timestamp;
- char *generator_unit_path;
- char *generator_unit_path_early;
- char *generator_unit_path_late;
-
struct udev* udev;
/* Data specific to the device subsystem */
@@ -228,7 +225,6 @@ struct Manager {
unsigned n_in_gc_queue;
/* Flags */
- ManagerRunningAs running_as;
ManagerExitCode exit_code:5;
bool dispatching_load_queue:1;
@@ -304,10 +300,15 @@ struct Manager {
const char *unit_log_field;
const char *unit_log_format_string;
- int first_boot;
+ int first_boot; /* tri-state */
};
-int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
+#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM)
+#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM)
+
+#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0)
+
+int manager_new(UnitFileScope scope, bool test_run, Manager **m);
Manager* manager_free(Manager *m);
void manager_enumerate(Manager *m);
@@ -345,8 +346,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
int manager_reload(Manager *m);
-bool manager_is_reloading_or_reexecuting(Manager *m) _pure_;
-
void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 32fe51c67e..40fc548b42 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -375,6 +375,7 @@ int mount_setup(bool loaded_policy) {
before_relabel = now(CLOCK_MONOTONIC);
nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+ nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
after_relabel = now(CLOCK_MONOTONIC);
diff --git a/src/core/mount.c b/src/core/mount.c
index 0fd880df5d..c8a898e4dc 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -86,6 +86,15 @@ static bool mount_is_network(const MountParameters *p) {
return mount_needs_network(p->options, p->fstype);
}
+static bool mount_is_loop(const MountParameters *p) {
+ assert(p);
+
+ if (fstab_test_option(p->options, "loop\0"))
+ return true;
+
+ return false;
+}
+
static bool mount_is_bind(const MountParameters *p) {
assert(p);
@@ -269,12 +278,12 @@ static int mount_add_mount_links(Mount *m) {
}
/* Adds in links to other mount points that might be needed
- * for the source path (if this is a bind mount) to be
+ * for the source path (if this is a bind mount or a loop mount) to be
* available. */
pm = get_mount_parameters_fragment(m);
if (pm && pm->what &&
path_is_absolute(pm->what) &&
- !mount_is_network(pm)) {
+ (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
r = unit_require_mounts_for(UNIT(m), pm->what);
if (r < 0)
@@ -336,8 +345,7 @@ static int mount_add_device_links(Mount *m) {
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_auto(p) && !mount_is_automount(p) &&
- UNIT(m)->manager->running_as == MANAGER_SYSTEM)
+ if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
device_wants_mount = true;
r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
@@ -353,7 +361,7 @@ static int mount_add_quota_links(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
return 0;
p = get_mount_parameters_fragment(m);
@@ -377,8 +385,7 @@ static int mount_add_quota_links(Mount *m) {
static bool should_umount(Mount *m) {
MountParameters *p;
- if (path_equal(m->where, "/") ||
- path_equal(m->where, "/usr") ||
+ if (PATH_IN_SET(m->where, "/", "/usr") ||
path_startswith(m->where, "/run/initramfs"))
return false;
@@ -400,7 +407,7 @@ static int mount_add_default_dependencies(Mount *m) {
if (!UNIT(m)->default_dependencies)
return 0;
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
return 0;
/* We do not add any default dependencies to /, /usr or
@@ -409,8 +416,7 @@ static int mount_add_default_dependencies(Mount *m) {
* Also, don't bother with anything mounted below virtual
* file systems, it's also going to be virtual, and hence
* not worth the effort. */
- if (path_equal(m->where, "/") ||
- path_equal(m->where, "/usr") ||
+ if (PATH_IN_SET(m->where, "/", "/usr") ||
path_startswith(m->where, "/run/initramfs") ||
path_startswith(m->where, "/proc") ||
path_startswith(m->where, "/sys") ||
@@ -1396,7 +1402,7 @@ static int mount_setup_unit(
goto fail;
}
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
const char* target;
target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
@@ -1424,7 +1430,7 @@ static int mount_setup_unit(
}
}
- if (m->running_as == MANAGER_SYSTEM &&
+ if (MANAGER_IS_SYSTEM(m) &&
mount_needs_network(options, fstype)) {
/* _netdev option may have shown up late, or on a
* remount. Add remote-fs dependencies, even though
@@ -1793,6 +1799,14 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
+static int mount_control_pid(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ return m->control_pid;
+}
+
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
@@ -1825,9 +1839,6 @@ const UnitVTable mount_vtable = {
"Install\0",
.private_section = "Mount",
- .no_alias = true,
- .no_instances = true,
-
.init = mount_init,
.load = mount_load,
.done = mount_done,
@@ -1854,6 +1865,8 @@ const UnitVTable mount_vtable = {
.reset_failed = mount_reset_failed,
+ .control_pid = mount_control_pid,
+
.bus_vtable = bus_mount_vtable,
.bus_set_property = bus_mount_set_property,
.bus_commit_properties = bus_mount_commit_properties,
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 6a7a37ee92..6c504a5e69 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -70,14 +70,26 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitsByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitFilesByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="GetUnitFileState"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitProcesses"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListJobs"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -176,6 +188,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RevertUnitFiles"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="PresetUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/path.c b/src/core/path.c
index 6ac9b8b90d..5e7b3eb234 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -110,16 +110,14 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
} else {
exists = true;
- /* Path exists, we don't need to watch parent
- too closely. */
+ /* Path exists, we don't need to watch parent too closely. */
if (oldslash) {
char *cut2 = oldslash + (oldslash == s->path);
char tmp2 = *cut2;
*cut2 = '\0';
- inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
- /* Error is ignored, the worst can happen is
- we get spurious events. */
+ (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
+ /* Error is ignored, the worst can happen is we get spurious events. */
*cut2 = tmp2;
}
@@ -320,7 +318,7 @@ static int path_add_default_dependencies(Path *p) {
if (r < 0)
return r;
- if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
diff --git a/src/core/scope.c b/src/core/scope.c
index 361695c3f9..238f63a729 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -138,7 +138,7 @@ static int scope_verify(Scope *s) {
return 0;
if (set_isempty(UNIT(s)->pids) &&
- !manager_is_reloading_or_reexecuting(UNIT(s)->manager) &&
+ !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
!unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) {
log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
return -EINVAL;
@@ -154,26 +154,27 @@ static int scope_load(Unit *u) {
assert(s);
assert(u->load_state == UNIT_STUB);
- if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
+ if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
+ /* Refuse to load non-transient scope units, but allow them while reloading. */
return -ENOENT;
- u->load_state = UNIT_LOADED;
-
- r = unit_load_dropin(u);
+ r = unit_load_fragment_and_dropin_optional(u);
if (r < 0)
return r;
- r = unit_patch_contexts(u);
- if (r < 0)
- return r;
+ if (u->load_state == UNIT_LOADED) {
+ r = unit_patch_contexts(u);
+ if (r < 0)
+ return r;
- r = unit_set_default_slice(u);
- if (r < 0)
- return r;
+ r = unit_set_default_slice(u);
+ if (r < 0)
+ return r;
- r = scope_add_default_dependencies(s);
- if (r < 0)
- return r;
+ r = scope_add_default_dependencies(s);
+ if (r < 0)
+ return r;
+ }
return scope_verify(s);
}
@@ -292,7 +293,7 @@ static int scope_start(Unit *u) {
assert(s->state == SCOPE_DEAD);
- if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
+ if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
return -ENOENT;
(void) unit_realize_cgroup(u);
@@ -568,8 +569,6 @@ const UnitVTable scope_vtable = {
"Install\0",
.private_section = "Scope",
- .no_alias = true,
- .no_instances = true,
.can_transient = true,
.init = scope_init,
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 2cdfcf7b5d..cc287d602d 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -110,6 +110,7 @@ static int callback_type_to_priority(int type) {
*/
_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
+ const char *fmt2;
#ifdef HAVE_AUDIT
int fd;
@@ -131,8 +132,10 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
}
#endif
+ fmt2 = strjoina("selinux: ", fmt);
+
va_start(ap, fmt);
- log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
+ log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap);
va_end(ap);
return 0;
diff --git a/src/core/service.c b/src/core/service.c
index 5d58b0b752..f7a3fcf2b9 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -180,20 +180,17 @@ static int service_set_main_pid(Service *s, pid_t pid) {
return 0;
}
-static void service_close_socket_fd(Service *s) {
+void service_close_socket_fd(Service *s) {
assert(s);
- s->socket_fd = asynchronous_close(s->socket_fd);
-}
-
-static void service_connection_unref(Service *s) {
- assert(s);
+ /* Undo the effect of service_set_socket_fd(). */
- if (!UNIT_ISSET(s->accept_socket))
- return;
+ s->socket_fd = asynchronous_close(s->socket_fd);
- socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
- unit_ref_unset(&s->accept_socket);
+ if (UNIT_ISSET(s->accept_socket)) {
+ socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
+ unit_ref_unset(&s->accept_socket);
+ }
}
static void service_stop_watchdog(Service *s) {
@@ -321,7 +318,6 @@ static void service_done(Unit *u) {
s->bus_name_owner = mfree(s->bus_name_owner);
service_close_socket_fd(s);
- service_connection_unref(s);
unit_ref_unset(&s->accept_socket);
@@ -523,7 +519,7 @@ static int service_add_default_dependencies(Service *s) {
/* Add a number of automatic dependencies useful for the
* majority of services. */
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
/* First, pull in the really early boot stuff, and
* require it, so that we fail if we can't acquire
* it. */
@@ -832,7 +828,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
return 0;
}
-static int service_search_main_pid(Service *s) {
+static void service_search_main_pid(Service *s) {
pid_t pid = 0;
int r;
@@ -841,30 +837,24 @@ static int service_search_main_pid(Service *s) {
/* If we know it anyway, don't ever fallback to unreliable
* heuristics */
if (s->main_pid_known)
- return 0;
+ return;
if (!s->guess_main_pid)
- return 0;
+ return;
assert(s->main_pid <= 0);
- r = unit_search_main_pid(UNIT(s), &pid);
- if (r < 0)
- return r;
+ if (unit_search_main_pid(UNIT(s), &pid) < 0)
+ return;
log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid);
- r = service_set_main_pid(s, pid);
- if (r < 0)
- return r;
+ if (service_set_main_pid(s, pid) < 0)
+ return;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0) {
+ if (r < 0)
/* FIXME: we need to do something here */
log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid);
- return r;
- }
-
- return 0;
}
static void service_set_state(Service *s, ServiceState state) {
@@ -916,17 +906,15 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_RUNNING, SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
- !(state == SERVICE_DEAD && UNIT(s)->job)) {
+ !(state == SERVICE_DEAD && UNIT(s)->job))
service_close_socket_fd(s);
- service_connection_unref(s);
- }
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
/* For the inactive states unit_notify() will trim the cgroup,
* but for exit we have to do that ourselves... */
- if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
+ if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
unit_prune_cgroup(UNIT(s));
/* For remain_after_exit services, let's see if we can "release" the
@@ -1217,7 +1205,7 @@ static int service_spawn(
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
return -ENOMEM;
- if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
return -ENOMEM;
@@ -2729,7 +2717,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
}
} else
- (void) service_search_main_pid(s);
+ service_search_main_pid(s);
service_enter_start_post(s);
break;
@@ -2751,16 +2739,15 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
}
} else
- (void) service_search_main_pid(s);
+ service_search_main_pid(s);
service_enter_running(s, SERVICE_SUCCESS);
break;
case SERVICE_RELOAD:
- if (f == SERVICE_SUCCESS) {
- service_load_pid_file(s, true);
- (void) service_search_main_pid(s);
- }
+ if (f == SERVICE_SUCCESS)
+ if (service_load_pid_file(s, true) < 0)
+ service_search_main_pid(s);
s->reload_result = f;
service_enter_running(s, SERVICE_SUCCESS);
@@ -3146,9 +3133,8 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
assert(s);
assert(fd >= 0);
- /* This is called by the socket code when instantiating a new
- * service for a stream socket and the socket needs to be
- * configured. */
+ /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs
+ * to be configured. We take ownership of the passed fd on success. */
if (UNIT(s)->load_state != UNIT_LOADED)
return -EINVAL;
@@ -3176,12 +3162,15 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
return r;
}
+ r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ if (r < 0)
+ return r;
+
s->socket_fd = fd;
s->socket_fd_selinux_context_net = selinux_context_net;
unit_ref_set(&s->accept_socket, UNIT(sock));
-
- return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ return 0;
}
static void service_reset_failed(Unit *u) {
@@ -3202,6 +3191,22 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
+static int service_main_pid(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return s->main_pid;
+}
+
+static int service_control_pid(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -3310,6 +3315,9 @@ const UnitVTable service_vtable = {
.notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_message = service_notify_message,
+ .main_pid = service_main_pid,
+ .control_pid = service_control_pid,
+
.bus_name_owner_change = service_bus_name_owner_change,
.bus_vtable = bus_service_vtable,
diff --git a/src/core/service.h b/src/core/service.h
index a5ced215e4..c7f1e81bdb 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -80,7 +80,7 @@ typedef enum NotifyState {
typedef enum ServiceResult {
SERVICE_SUCCESS,
- SERVICE_FAILURE_RESOURCES,
+ SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
SERVICE_FAILURE_TIMEOUT,
SERVICE_FAILURE_EXIT_CODE,
SERVICE_FAILURE_SIGNAL,
@@ -198,6 +198,7 @@ struct Service {
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
+void service_close_socket_fd(Service *s);
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 6296b4c94a..e14755d84e 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -202,7 +202,7 @@ int main(int argc, char *argv[]) {
goto error;
}
- cg_get_root_path(&cgroup);
+ (void) cg_get_root_path(&cgroup);
use_watchdog = !!getenv("WATCHDOG_USEC");
@@ -397,9 +397,14 @@ int main(int argc, char *argv[]) {
if (!in_container) {
_cleanup_free_ char *param = NULL;
- if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
+ r = read_one_line_file("/run/systemd/reboot-param", &param);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read reboot parameter file: %m");
+
+ if (!isempty(param)) {
log_info("Rebooting with argument '%s'.", param);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
+ log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
}
}
diff --git a/src/core/slice.c b/src/core/slice.c
index 667f61bde5..c7700b8857 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -135,6 +135,7 @@ static int slice_load(Unit *u) {
int r;
assert(s);
+ assert(u->load_state == UNIT_STUB);
r = unit_load_fragment_and_dropin_optional(u);
if (r < 0)
@@ -308,8 +309,6 @@ const UnitVTable slice_vtable = {
"Install\0",
.private_section = "Slice",
- .no_alias = true,
- .no_instances = true,
.can_transient = true,
.init = slice_init,
diff --git a/src/core/socket.c b/src/core/socket.c
index dd515a17a5..7eeed068bd 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -99,6 +99,8 @@ static void socket_init(Unit *u) {
s->exec_context.std_error = u->manager->default_std_error;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+
+ RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500);
}
static void socket_unwatch_control_pid(Socket *s) {
@@ -227,7 +229,6 @@ int socket_instantiate_service(Socket *s) {
if (r < 0)
return r;
- u->no_gc = true;
unit_ref_set(&s->service, u);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
@@ -301,7 +302,7 @@ static int socket_add_default_dependencies(Socket *s) {
if (r < 0)
return r;
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -792,47 +793,45 @@ static void socket_close_fds(Socket *s) {
assert(s);
LIST_FOREACH(port, p, s->ports) {
+ bool was_open;
- p->event_source = sd_event_source_unref(p->event_source);
-
- if (p->fd < 0)
- continue;
+ was_open = p->fd >= 0;
+ p->event_source = sd_event_source_unref(p->event_source);
p->fd = safe_close(p->fd);
socket_cleanup_fd_list(p);
- /* One little note: we should normally not delete any
- * sockets in the file system here! After all some
- * other process we spawned might still have a
- * reference of this fd and wants to continue to use
- * it. Therefore we delete sockets in the file system
- * before we create a new one, not after we stopped
- * using one! */
+ /* One little note: we should normally not delete any sockets in the file system here! After all some
+ * other process we spawned might still have a reference of this fd and wants to continue to use
+ * it. Therefore we normally delete sockets in the file system before we create a new one, not after we
+ * stopped using one! That all said, if the user explicitly requested this, we'll delete them here
+ * anyway, but only then. */
- if (s->remove_on_stop) {
- switch (p->type) {
+ if (!was_open || !s->remove_on_stop)
+ continue;
- case SOCKET_FIFO:
- unlink(p->path);
- break;
+ switch (p->type) {
- case SOCKET_MQUEUE:
- mq_unlink(p->path);
- break;
+ case SOCKET_FIFO:
+ (void) unlink(p->path);
+ break;
- case SOCKET_SOCKET:
- socket_address_unlink(&p->address);
- break;
+ case SOCKET_MQUEUE:
+ (void) mq_unlink(p->path);
+ break;
- default:
- break;
- }
+ case SOCKET_SOCKET:
+ (void) socket_address_unlink(&p->address);
+ break;
+
+ default:
+ break;
}
}
if (s->remove_on_stop)
STRV_FOREACH(i, s->symlinks)
- unlink(*i);
+ (void) unlink(*i);
}
static void socket_apply_socket_options(Socket *s, int fd) {
@@ -1887,6 +1886,9 @@ static void socket_enter_running(Socket *s, int cfd) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
+ /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or
+ * close it. */
+
assert(s);
/* We don't take connections anymore if we are supposed to
@@ -1896,7 +1898,7 @@ static void socket_enter_running(Socket *s, int cfd) {
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
- safe_close(cfd);
+ cfd = safe_close(cfd);
else {
/* Flush all sockets by closing and reopening them */
socket_close_fds(s);
@@ -1918,6 +1920,13 @@ static void socket_enter_running(Socket *s, int cfd) {
return;
}
+ if (!ratelimit_test(&s->trigger_limit)) {
+ safe_close(cfd);
+ log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation.");
+ socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT);
+ return;
+ }
+
if (cfd < 0) {
Iterator i;
Unit *other;
@@ -1949,7 +1958,7 @@ static void socket_enter_running(Socket *s, int cfd) {
Service *service;
if (s->n_connections >= s->max_connections) {
- log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections);
+ log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections);
safe_close(cfd);
return;
}
@@ -1965,6 +1974,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* ENOTCONN is legitimate if TCP RST was received.
* This connection is over, but the socket unit lives on. */
+ log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
safe_close(cfd);
return;
}
@@ -1983,22 +1993,24 @@ static void socket_enter_running(Socket *s, int cfd) {
service = SERVICE(UNIT_DEREF(s->service));
unit_ref_unset(&s->service);
- s->n_accepted++;
-
- UNIT(service)->no_gc = false;
+ s->n_accepted++;
unit_choose_id(UNIT(service), name);
r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
if (r < 0)
goto fail;
- cfd = -1;
+ cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
s->n_connections++;
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
- if (r < 0)
+ if (r < 0) {
+ /* We failed to activate the new service, but it still exists. Let's make sure the service
+ * closes and forgets the connection fd again, immediately. */
+ service_close_socket_fd(service);
goto fail;
+ }
/* Notify clients about changed counters */
unit_add_to_dbus_queue(UNIT(s));
@@ -2781,6 +2793,14 @@ char *socket_fdname(Socket *s) {
return UNIT(s)->id;
}
+static int socket_control_pid(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_CHOWN] = "StartChown",
@@ -2798,6 +2818,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
+ [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
@@ -2846,6 +2867,8 @@ const UnitVTable socket_vtable = {
.reset_failed = socket_reset_failed,
+ .control_pid = socket_control_pid,
+
.bus_vtable = bus_socket_vtable,
.bus_set_property = bus_socket_set_property,
.bus_commit_properties = bus_socket_commit_properties,
diff --git a/src/core/socket.h b/src/core/socket.h
index b537b026a7..2a4b1bb674 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -52,6 +52,7 @@ typedef enum SocketResult {
SOCKET_FAILURE_EXIT_CODE,
SOCKET_FAILURE_SIGNAL,
SOCKET_FAILURE_CORE_DUMP,
+ SOCKET_FAILURE_TRIGGER_LIMIT_HIT,
SOCKET_FAILURE_SERVICE_START_LIMIT_HIT,
_SOCKET_RESULT_MAX,
_SOCKET_RESULT_INVALID = -1
@@ -156,6 +157,8 @@ struct Socket {
bool reset_cpu_usage:1;
char *fdname;
+
+ RateLimit trigger_limit;
};
/* Called from the service code when collecting fds */
diff --git a/src/core/swap.c b/src/core/swap.c
index 11506d9ecb..c018648d87 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -198,7 +198,7 @@ static int swap_add_device_links(Swap *s) {
return 0;
if (is_device_path(s->what))
- return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO);
+ return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
@@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) {
if (!UNIT(s)->default_dependencies)
return 0;
- if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
return 0;
if (detect_container() > 0)
@@ -1426,6 +1426,14 @@ static bool swap_supported(void) {
return supported;
}
+static int swap_control_pid(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
@@ -1457,9 +1465,6 @@ const UnitVTable swap_vtable = {
"Install\0",
.private_section = "Swap",
- .no_alias = true,
- .no_instances = true,
-
.init = swap_init,
.load = swap_load,
.done = swap_done,
@@ -1487,6 +1492,8 @@ const UnitVTable swap_vtable = {
.reset_failed = swap_reset_failed,
+ .control_pid = swap_control_pid,
+
.bus_vtable = bus_swap_vtable,
.bus_set_property = bus_swap_set_property,
.bus_commit_properties = bus_swap_commit_properties,
diff --git a/src/core/system.conf b/src/core/system.conf
index e2ded27333..eacd7ee282 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -34,7 +34,7 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultCPUAccounting=no
diff --git a/src/core/timer.c b/src/core/timer.c
index 3d0bae16e5..f8f5f4b2e4 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -109,7 +109,7 @@ static int timer_add_default_dependencies(Timer *t) {
if (r < 0)
return r;
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -135,7 +135,7 @@ static int timer_setup_persistent(Timer *t) {
if (!t->persistent)
return 0;
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
if (r < 0)
@@ -320,7 +320,7 @@ static usec_t monotonic_to_boottime(usec_t t) {
if (t <= 0)
return 0;
- a = now(CLOCK_BOOTTIME);
+ a = now(clock_boottime_or_monotonic());
b = now(CLOCK_MONOTONIC);
if (t + a > b)
@@ -373,7 +373,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
* rather than the monotonic clock. */
ts_realtime = now(CLOCK_REALTIME);
- ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
+ ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC);
t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
LIST_FOREACH(value, v, t->values) {
diff --git a/src/core/transaction.c b/src/core/transaction.c
index c894001cf9..d5370b2a14 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -855,7 +855,7 @@ int transaction_add_job_and_dependencies(
* This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
* This way, we "recursively" coldplug units, ensuring that we do not look at state of
* not-yet-coldplugged units. */
- if (unit->manager->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(unit->manager))
unit_coldplug(unit);
/* log_debug("Pulling in %s/%s from %s/%s", */
@@ -939,7 +939,7 @@ int transaction_add_job_and_dependencies(
if (r < 0) {
/* unit masked, job type not applicable and unit not found are not considered as errors. */
log_unit_full(dep,
- IN_SET(r, -ESHUTDOWN, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
+ IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
sd_bus_error_free(e);
diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in
index 9e18a39a67..0d8c303136 100644
--- a/src/core/triggers.systemd.in
+++ b/src/core/triggers.systemd.in
@@ -18,6 +18,8 @@
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
# The contents of this are an example to be copied into systemd.spec.
+#
+# Minimum rpm version supported: 4.13.0
%transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system
-- This script will run after any package is initially installed or
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index fc057d965c..f11df42af3 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -140,14 +140,9 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
assert(u);
- if (u->manager->running_as == MANAGER_SYSTEM)
- e = "/run";
- else {
- e = getenv("XDG_RUNTIME_DIR");
- if (!e)
- return -EOPNOTSUPP;
- }
-
+ e = manager_get_runtime_prefix(u->manager);
+ if (!e)
+ return -EOPNOTSUPP;
n = strdup(e);
if (!n)
return -ENOMEM;
diff --git a/src/core/unit.c b/src/core/unit.c
index 70175557f7..4ace6b075b 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -47,11 +47,13 @@
#include "path-util.h"
#include "process-util.h"
#include "set.h"
+#include "signal-util.h"
#include "special.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "umask-util.h"
#include "unit-name.h"
#include "unit.h"
#include "user-util.h"
@@ -191,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) {
if (r < 0)
return r;
- if (i && unit_vtable[t]->no_instances)
+ if (i && !unit_type_may_template(t))
return -EINVAL;
/* Ensure that this unit is either instanced or not instanced,
@@ -200,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) {
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
return -EINVAL;
- if (unit_vtable[t]->no_alias && !set_isempty(u->names))
+ if (!unit_type_may_alias(t) && !set_isempty(u->names))
return -EEXIST;
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
@@ -418,13 +420,22 @@ static void unit_remove_transient(Unit *u) {
(void) unlink(u->fragment_path);
STRV_FOREACH(i, u->dropin_paths) {
- _cleanup_free_ char *p = NULL;
+ _cleanup_free_ char *p = NULL, *pp = NULL;
- (void) unlink(*i);
+ p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */
+ if (!p)
+ continue;
+
+ pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */
+ if (!pp)
+ continue;
+
+ /* Only drop transient drop-ins */
+ if (!path_equal(u->manager->lookup_paths.transient, pp))
+ continue;
- p = dirname_malloc(*i);
- if (p)
- (void) rmdir(p);
+ (void) unlink(*i);
+ (void) rmdir(p);
}
}
@@ -483,7 +494,10 @@ void unit_free(Unit *u) {
assert(u);
- if (u->manager->n_reloading <= 0)
+ if (u->transient_file)
+ fclose(u->transient_file);
+
+ if (!MANAGER_IS_RELOADING(u->manager))
unit_remove_transient(u);
bus_unit_send_removed_signal(u);
@@ -706,6 +720,9 @@ int unit_merge(Unit *u, Unit *other) {
if (!u->instance != !other->instance)
return -EINVAL;
+ if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */
+ return -EEXIST;
+
if (other->load_state != UNIT_STUB &&
other->load_state != UNIT_NOT_FOUND)
return -EEXIST;
@@ -762,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) {
}
int unit_merge_by_name(Unit *u, const char *name) {
+ _cleanup_free_ char *s = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name);
@@ -814,7 +831,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (u->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(u->manager))
return 0;
if (c->private_tmp) {
@@ -1222,6 +1239,17 @@ int unit_load(Unit *u) {
if (u->load_state != UNIT_STUB)
return 0;
+ if (u->transient_file) {
+ r = fflush_and_check(u->transient_file);
+ if (r < 0)
+ goto fail;
+
+ fclose(u->transient_file);
+ u->transient_file = NULL;
+
+ u->dropin_mtime = now(CLOCK_REALTIME);
+ }
+
if (UNIT_VTABLE(u)->load) {
r = UNIT_VTABLE(u)->load(u);
if (r < 0)
@@ -1472,11 +1500,6 @@ int unit_start(Unit *u) {
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
- /* Make sure we don't enter a busy loop of some kind. */
- r = unit_start_limit_test(u);
- if (r < 0)
- return r;
-
/* Units that aren't loaded cannot be started */
if (u->load_state != UNIT_LOADED)
return -EINVAL;
@@ -1518,6 +1541,11 @@ int unit_start(Unit *u) {
if (!UNIT_VTABLE(u)->start)
return -EBADR;
+ /* Make sure we don't enter a busy loop of some kind. */
+ r = unit_start_limit_test(u);
+ if (r < 0)
+ return r;
+
/* We don't suppress calls to ->start() here when we are
* already starting, to allow this request to be used as a
* "hurry up" call, for example when the unit is in some "auto
@@ -1834,7 +1862,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
m = u->manager;
/* Update timestamps for state changes */
- if (m->n_reloading <= 0) {
+ if (!MANAGER_IS_RELOADING(m)) {
dual_timestamp_get(&u->state_change_timestamp);
if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
@@ -1941,7 +1969,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} else
unexpected = true;
- if (m->n_reloading <= 0) {
+ if (!MANAGER_IS_RELOADING(m)) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
@@ -1978,7 +2006,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- m->n_reloading <= 0) {
+ !MANAGER_IS_RELOADING(m)) {
/* Write audit record if we have just finished starting up */
manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
@@ -1995,7 +2023,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
- m->n_reloading <= 0) {
+ !MANAGER_IS_RELOADING(m)) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
@@ -2016,7 +2044,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
manager_recheck_journal(m);
unit_trigger_notify(u);
- if (u->manager->n_reloading <= 0) {
+ if (!MANAGER_IS_RELOADING(u->manager)) {
/* Maybe we finished startup and are now ready for
* being stopped because unneeded? */
unit_check_unneeded(u);
@@ -2413,7 +2441,7 @@ int unit_set_default_slice(Unit *u) {
if (!escaped)
return -ENOMEM;
- if (u->manager->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(u->manager))
b = strjoin("system-", escaped, ".slice", NULL);
else
b = strappend(escaped, ".slice");
@@ -2423,7 +2451,7 @@ int unit_set_default_slice(Unit *u) {
slice_name = b;
} else
slice_name =
- u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
+ MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
? SPECIAL_SYSTEM_SLICE
: SPECIAL_ROOT_SLICE;
@@ -2493,12 +2521,11 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
return -EBUSY;
match = strjoina("type='signal',"
- "sender='org.freedesktop.DBus',"
- "path='/org/freedesktop/DBus',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',"
- "arg0='", name, "'",
- NULL);
+ "sender='org.freedesktop.DBus',"
+ "path='/org/freedesktop/DBus',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "arg0='", name, "'");
return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
}
@@ -2884,7 +2911,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
return r;
r = unit_add_two_dependencies(u, UNIT_AFTER,
- u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS,
+ MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
device, true);
if (r < 0)
return r;
@@ -3040,8 +3067,7 @@ bool unit_active_or_pending(Unit *u) {
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
assert(u);
assert(w >= 0 && w < _KILL_WHO_MAX);
- assert(signo > 0);
- assert(signo < _NSIG);
+ assert(SIGNAL_VALID(signo));
if (!UNIT_VTABLE(u)->kill)
return -EOPNOTSUPP;
@@ -3158,7 +3184,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
if (u->unit_file_state < 0 && u->fragment_path) {
r = unit_file_get_state(
- u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ u->manager->unit_file_scope,
NULL,
basename(u->fragment_path),
&u->unit_file_state);
@@ -3174,7 +3200,7 @@ int unit_get_unit_file_preset(Unit *u) {
if (u->unit_file_preset < 0 && u->fragment_path)
u->unit_file_preset = unit_file_query_preset(
- u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ u->manager->unit_file_scope,
NULL,
basename(u->fragment_path));
@@ -3199,6 +3225,10 @@ void unit_ref_unset(UnitRef *ref) {
if (!ref->unit)
return;
+ /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might
+ * be unreferenced now. */
+ unit_add_to_gc_queue(ref->unit);
+
LIST_REMOVE(refs, ref->unit->refs, ref);
ref->unit = NULL;
}
@@ -3225,7 +3255,7 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
- if (u->manager->running_as == MANAGER_USER &&
+ if (MANAGER_IS_USER(u->manager) &&
!ec->working_directory) {
r = get_home_dir(&ec->working_directory);
@@ -3237,7 +3267,7 @@ int unit_patch_contexts(Unit *u) {
ec->working_directory_missing_ok = true;
}
- if (u->manager->running_as == MANAGER_USER &&
+ if (MANAGER_IS_USER(u->manager) &&
(ec->syscall_whitelist ||
!set_isempty(ec->syscall_filter) ||
!set_isempty(ec->syscall_archs) ||
@@ -3315,59 +3345,62 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
return *(ExecRuntime**) ((uint8_t*) u + offset);
}
-static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
+static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) {
assert(u);
- if (u->manager->running_as == MANAGER_USER) {
- int r;
+ if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT))
+ return NULL;
- if (mode == UNIT_PERSISTENT && !transient)
- r = user_config_home(dir);
- else
- r = user_runtime_dir(dir);
- if (r == 0)
- return -ENOENT;
+ if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */
+ return u->manager->lookup_paths.transient;
- return r;
- }
+ if (mode == UNIT_RUNTIME)
+ return u->manager->lookup_paths.runtime_control;
- if (mode == UNIT_PERSISTENT && !transient)
- *dir = strdup("/etc/systemd/system");
- else
- *dir = strdup("/run/systemd/system");
- if (!*dir)
- return -ENOMEM;
+ if (mode == UNIT_PERSISTENT)
+ return u->manager->lookup_paths.persistent_control;
- return 0;
+ return NULL;
}
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
-
- _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ const char *dir, *prefixed;
int r;
assert(u);
+ if (u->transient_file) {
+ /* When this is a transient unit file in creation, then let's not create a new drop-in but instead
+ * write to the transient unit file. */
+ fputs(data, u->transient_file);
+ return 0;
+ }
+
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
return 0;
- r = unit_drop_in_dir(u, mode, u->transient, &dir);
- if (r < 0)
- return r;
+ dir = unit_drop_in_dir(u, mode);
+ if (!dir)
+ return -EINVAL;
- r = write_drop_in(dir, u->id, 50, name, data);
+ prefixed = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\" or an equivalent operation. Do not edit.\n",
+ data);
+
+ r = drop_in_file(dir, u->id, 50, name, &p, &q);
if (r < 0)
return r;
- r = drop_in_file(dir, u->id, 50, name, &p, &q);
+ (void) mkdir_p(p, 0755);
+ r = write_string_file_atomic_label(q, prefixed);
if (r < 0)
return r;
- r = strv_extend(&u->dropin_paths, q);
+ r = strv_push(&u->dropin_paths, q);
if (r < 0)
return r;
+ q = NULL;
- strv_sort(u->dropin_paths);
strv_uniq(u->dropin_paths);
u->dropin_mtime = now(CLOCK_REALTIME);
@@ -3398,7 +3431,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
}
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
- _cleanup_free_ char *ndata = NULL;
+ const char *ndata;
assert(u);
assert(name);
@@ -3410,9 +3443,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
return 0;
- ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
- if (!ndata)
- return -ENOMEM;
+ ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data);
return unit_write_drop_in(u, mode, name, ndata);
}
@@ -3440,24 +3471,51 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const
}
int unit_make_transient(Unit *u) {
+ FILE *f;
+ char *path;
+
assert(u);
if (!UNIT_VTABLE(u)->can_transient)
return -EOPNOTSUPP;
- u->load_state = UNIT_STUB;
- u->load_error = 0;
- u->transient = true;
+ path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL);
+ if (!path)
+ return -ENOMEM;
+
+ /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
+ * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
+
+ RUN_WITH_UMASK(0022) {
+ f = fopen(path, "we");
+ if (!f) {
+ free(path);
+ return -errno;
+ }
+ }
+
+ if (u->transient_file)
+ fclose(u->transient_file);
+ u->transient_file = f;
+
+ free(u->fragment_path);
+ u->fragment_path = path;
- u->fragment_path = mfree(u->fragment_path);
u->source_path = mfree(u->source_path);
u->dropin_paths = strv_free(u->dropin_paths);
u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
+ u->load_state = UNIT_STUB;
+ u->load_error = 0;
+ u->transient = true;
+
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
unit_add_to_load_queue(u);
+ fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n",
+ u->transient_file);
+
return 0;
}
@@ -3549,7 +3607,7 @@ int unit_kill_context(
* cases. It doesn't work at all in
* containers, and outside of containers it
* can be confused easily by left-over
- * directories in the cgroup -- which however
+ * directories in the cgroup — which however
* should not exist in non-delegated units. On
* the unified hierarchy that's different,
* there we get proper events. Hence rely on
@@ -3750,3 +3808,21 @@ bool unit_is_pristine(Unit *u) {
u->job ||
u->merged_into);
}
+
+pid_t unit_control_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->control_pid)
+ return UNIT_VTABLE(u)->control_pid(u);
+
+ return 0;
+}
+
+pid_t unit_main_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->main_pid)
+ return UNIT_VTABLE(u)->main_pid(u);
+
+ return 0;
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index 601e763ce2..be62e88421 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -95,6 +95,9 @@ struct Unit {
usec_t source_mtime;
usec_t dropin_mtime;
+ /* If this is a transient unit we are currently writing, this is where we are writing it to */
+ FILE *transient_file;
+
/* If there is something to do with this unit, then this is the installed job for it */
Job *job;
@@ -387,6 +390,12 @@ struct UnitVTable {
/* Returns the next timeout of a unit */
int (*get_timeout)(Unit *u, usec_t *timeout);
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*main_pid)(Unit *u);
+
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*control_pid)(Unit *u);
+
/* This is called for each unit type and should be used to
* enumerate existing devices and load them. However,
* everything that is loaded here should still stay in
@@ -407,12 +416,6 @@ struct UnitVTable {
/* The strings to print in status messages */
UnitStatusMessageFormats status_message_formats;
- /* Can units of this type have multiple names? */
- bool no_alias:1;
-
- /* Instances make no sense for this type */
- bool no_instances:1;
-
/* True if transient units of this type are OK */
bool can_transient:1;
};
@@ -598,6 +601,9 @@ bool unit_type_supported(UnitType t);
bool unit_is_pristine(Unit *u);
+pid_t unit_control_pid(Unit *u);
+pid_t unit_main_pid(Unit *u);
+
static inline bool unit_supported(Unit *u) {
return unit_type_supported(u->type);
}
diff --git a/src/core/user.conf b/src/core/user.conf
index 87c8164378..b427f1ef6d 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -23,7 +23,7 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultLimitCPU=
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 085909c20c..41fc1993d5 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -49,6 +49,7 @@
#include "journald-native.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "process-util.h"
@@ -212,6 +213,10 @@ static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) {
#define filename_escape(s) xescape((s), "./ ")
+static inline const char *coredump_tmpfile_name(const char *s) {
+ return s ? s : "(unnamed temporary file)";
+}
+
static int fix_permissions(
int fd,
const char *filename,
@@ -219,8 +224,9 @@ static int fix_permissions(
const char *context[_CONTEXT_MAX],
uid_t uid) {
+ int r;
+
assert(fd >= 0);
- assert(filename);
assert(target);
assert(context);
@@ -230,10 +236,11 @@ static int fix_permissions(
(void) fix_xattr(fd, context);
if (fsync(fd) < 0)
- return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
+ return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename));
- if (rename(filename, target) < 0)
- return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
+ r = link_tmpfile(fd, filename, target);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move coredump %s into place: %m", target);
return 0;
}
@@ -335,15 +342,11 @@ static int save_external_coredump(
if (r < 0)
return log_error_errno(r, "Failed to determine coredump file name: %m");
- r = tempfn_random(fn, NULL, &tmp);
- if (r < 0)
- return log_error_errno(r, "Failed to determine temporary file name: %m");
-
mkdir_p_label("/var/lib/systemd/coredump", 0755);
- fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
+ fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
if (fd < 0)
- return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
+ return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
r = copy_bytes(input_fd, fd, max_size, false);
if (r == -EFBIG) {
@@ -358,12 +361,12 @@ static int save_external_coredump(
}
if (fstat(fd, &st) < 0) {
- log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
+ log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp));
goto fail;
}
if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
- log_error_errno(errno, "Failed to seek on %s: %m", tmp);
+ log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
goto fail;
}
@@ -381,21 +384,15 @@ static int save_external_coredump(
goto uncompressed;
}
- r = tempfn_random(fn_compressed, NULL, &tmp_compressed);
- if (r < 0) {
- log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed);
- goto uncompressed;
- }
-
- fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
+ fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
if (fd_compressed < 0) {
- log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
+ log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
goto uncompressed;
}
r = compress_stream(fd, fd_compressed, -1);
if (r < 0) {
- log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
+ log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
goto fail_compressed;
}
@@ -404,7 +401,8 @@ static int save_external_coredump(
goto fail_compressed;
/* OK, this worked, we can get rid of the uncompressed version now */
- unlink_noerrno(tmp);
+ if (tmp)
+ unlink_noerrno(tmp);
*ret_filename = fn_compressed; /* compressed */
*ret_node_fd = fd_compressed; /* compressed */
@@ -417,7 +415,8 @@ static int save_external_coredump(
return 0;
fail_compressed:
- (void) unlink(tmp_compressed);
+ if (tmp_compressed)
+ (void) unlink(tmp_compressed);
}
uncompressed:
@@ -438,7 +437,8 @@ uncompressed:
return 0;
fail:
- (void) unlink(tmp);
+ if (tmp)
+ (void) unlink(tmp);
return r;
}
@@ -1095,7 +1095,7 @@ static int process_kernel(int argc, char* argv[]) {
IOVEC_SET_STRING(iovec[n_iovec++], core_environ);
}
- core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL);
+ core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000");
IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp);
IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index dac800ebef..27b1e0fb3f 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -664,7 +664,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
#endif
} else {
if (r == -ENOENT)
- log_error("Cannot retrieve coredump from journal nor disk.");
+ log_error("Cannot retrieve coredump from journal or disk.");
else
log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
goto error;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 2ef966257a..9927621ea0 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -719,8 +719,12 @@ int main(int argc, char *argv[]) {
int k;
k = crypt_init_by_name(&cd, argv[2]);
- if (k) {
- log_error_errno(k, "crypt_init() failed: %m");
+ if (k == -ENODEV) {
+ log_info("Volume %s already inactive.", argv[2]);
+ r = EXIT_SUCCESS;
+ goto finish;
+ } else if (k) {
+ log_error_errno(k, "crypt_init_by_name() failed: %m");
goto finish;
}
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index a04c8c49ff..6990c47f48 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -137,7 +137,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0)
return -1;
- sd_event_source_set_description(io, "curl-io");
+ (void) sd_event_source_set_description(io, "curl-io");
r = hashmap_put(g->ios, FD_TO_PTR(s), io);
if (r < 0) {
@@ -204,7 +204,7 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata
if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0)
return -1;
- sd_event_source_set_description(g->timer, "curl-timer");
+ (void) sd_event_source_set_description(g->timer, "curl-timer");
}
return 0;
diff --git a/src/import/import-common.c b/src/import/import-common.c
index 18a30be36d..287a3382a1 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -136,7 +136,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
+ execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL);
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
}
@@ -210,7 +210,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- execlp("tar", "tar", "-C", path, "-c", ".", NULL);
+ execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL);
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
}
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index d301d4d79e..dc4e4667a9 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -330,7 +330,7 @@ int pull_verify(PullJob *main_job,
_cleanup_close_ int sig_file = -1;
const char *p, *line;
char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
- _cleanup_sigkill_wait_ pid_t pid = 0;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
bool gpg_home_created = false;
int r;
diff --git a/src/import/pull-job.h b/src/import/pull-job.h
index 998857035a..3a152a50e3 100644
--- a/src/import/pull-job.h
+++ b/src/import/pull-job.h
@@ -44,15 +44,6 @@ typedef enum PullJobState {
#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED))
-typedef enum PullJobCompression {
- PULL_JOB_UNCOMPRESSED,
- PULL_JOB_XZ,
- PULL_JOB_GZIP,
- PULL_JOB_BZIP2,
- _PULL_JOB_COMPRESSION_MAX,
- _PULL_JOB_COMPRESSION_INVALID = -1,
-} PullJobCompression;
-
struct PullJob {
PullJobState state;
int error;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 60d897758b..4ad9184993 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -122,12 +122,14 @@ static int open_journal(RequestMeta *m) {
}
static int request_meta_ensure_tmp(RequestMeta *m) {
+ assert(m);
+
if (m->tmp)
rewind(m->tmp);
else {
int fd;
- fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC);
+ fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
if (fd < 0)
return fd;
diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
index 3864647eb7..9ba9ee3fc0 100644
--- a/src/journal-remote/journal-remote-parse.c
+++ b/src/journal-remote/journal-remote-parse.c
@@ -485,7 +485,7 @@ int process_source(RemoteSource *source, bool compress, bool seal) {
}
target = source->size;
- while (target > 16 * LINE_CHUNK && remain < target / 2)
+ while (target > 16 * LINE_CHUNK && source->filled < target / 2)
target /= 2;
if (target < source->size) {
char *tmp;
diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c
index e61b6bc68f..8ce8e1895e 100644
--- a/src/journal-remote/journal-upload-journal.c
+++ b/src/journal-remote/journal-upload-journal.c
@@ -25,6 +25,7 @@
#include "log.h"
#include "utf8.h"
#include "util.h"
+#include "sd-daemon.h"
/**
* Write up to size bytes to buf. Return negative on error, and number of
@@ -242,6 +243,22 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
assert_not_reached("WTF?");
}
+static inline void check_update_watchdog(Uploader *u) {
+ usec_t after;
+ usec_t elapsed_time;
+
+ if (u->watchdog_usec <= 0)
+ return;
+
+ after = now(CLOCK_MONOTONIC);
+ elapsed_time = usec_sub(after, u->watchdog_timestamp);
+ if (elapsed_time > u->watchdog_usec / 2) {
+ log_debug("Update watchdog timer");
+ sd_notify(false, "WATCHDOG=1");
+ u->watchdog_timestamp = after;
+ }
+}
+
static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
Uploader *u = userp;
int r;
@@ -252,6 +269,8 @@ static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void
assert(u);
assert(nmemb <= SSIZE_MAX / size);
+ check_update_watchdog(u);
+
j = u->journal;
while (j && filled < size * nmemb) {
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 6e1c3bb9ef..4647cfdeb3 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -463,6 +463,8 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
if (r < 0)
return log_error_errno(r, "Failed to set up signals: %m");
+ (void) sd_watchdog_enabled(false, &u->watchdog_usec);
+
return load_cursor_state(u);
}
@@ -494,6 +496,7 @@ static int perform_upload(Uploader *u) {
assert(u);
+ u->watchdog_timestamp = now(CLOCK_MONOTONIC);
code = curl_easy_perform(u->easy);
if (code) {
if (u->error[0])
diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h
index b8cd04d527..5711905f86 100644
--- a/src/journal-remote/journal-upload.h
+++ b/src/journal-remote/journal-upload.h
@@ -4,6 +4,7 @@
#include "sd-event.h"
#include "sd-journal.h"
+#include "time-util.h"
typedef enum {
ENTRY_CURSOR = 0, /* Nothing actually written yet. */
@@ -48,6 +49,8 @@ typedef struct Uploader {
size_t entries_sent;
char *last_cursor, *current_cursor;
+ usec_t watchdog_timestamp;
+ usec_t watchdog_usec;
} Uploader;
#define JOURNAL_UPLOAD_POLL_TIMEOUT (10 * USEC_PER_SEC)
diff --git a/src/journal/compress.c b/src/journal/compress.c
index c43849c46a..ba734b5561 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -112,7 +112,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
if (src_size < 9)
return -ENOBUFS;
- r = LZ4_compress_limitedOutput(src, dst + 8, src_size, (int) dst_alloc_size - 8);
+ r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
if (r <= 0)
return -ENOBUFS;
@@ -176,7 +176,7 @@ int decompress_blob_xz(const void *src, uint64_t src_size,
return -ENOMEM;
s.avail_out = space - used;
- s.next_out = *dst + used;
+ s.next_out = *(uint8_t**)dst + used;
}
*dst_size = space - s.avail_out;
@@ -215,7 +215,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size,
} else
out = *dst;
- r = LZ4_decompress_safe(src + 8, out, src_size - 8, size);
+ r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
if (r < 0 || r != size)
return -EBADMSG;
@@ -291,7 +291,7 @@ int decompress_startswith_xz(const void *src, uint64_t src_size,
if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
return -ENOMEM;
- s.next_out = *buffer + *buffer_size - s.avail_out;
+ s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out;
}
#else
@@ -324,7 +324,7 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
return -ENOMEM;
- r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8,
+ r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
prefix_len + 1, *buffer_size);
if (r >= 0)
size = (unsigned) r;
diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c
index 8956eb1d58..612b10f3a9 100644
--- a/src/journal/fsprg.c
+++ b/src/journal/fsprg.c
@@ -58,7 +58,7 @@ static gcry_mpi_t mpi_import(const void *buf, size_t buflen) {
gcry_mpi_t h;
unsigned len;
- gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL);
+ assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
len = (gcry_mpi_get_nbits(h) + 7) / 8;
assert(len <= buflen);
assert(gcry_mpi_cmp_ui(h, 0) >= 0);
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index bed825cdc3..ec50333c2c 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -37,6 +37,7 @@
#include "journal-file.h"
#include "lookup3.h"
#include "parse-util.h"
+#include "path-util.h"
#include "random-util.h"
#include "sd-event.h"
#include "set.h"
@@ -119,7 +120,7 @@ static void journal_file_set_offline_internal(JournalFile *f) {
if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING))
continue;
- f->header->state = STATE_OFFLINE;
+ f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE;
(void) fsync(f->fd);
break;
@@ -217,8 +218,10 @@ int journal_file_set_offline(JournalFile *f, bool wait) {
if (!(f->fd >= 0 && f->header))
return -EINVAL;
- if (f->header->state != STATE_ONLINE)
- return 0;
+ /* An offlining journal is implicitly online and may modify f->header->state,
+ * we must also join any potentially lingering offline thread when not online. */
+ if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE)
+ return journal_file_set_offline_thread_join(f);
/* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */
restarted = journal_file_set_offline_try_restart(f);
@@ -362,7 +365,8 @@ JournalFile* journal_file_close(JournalFile *f) {
(void) btrfs_defrag_fd(f->fd);
}
- safe_close(f->fd);
+ if (f->close_fd)
+ safe_close(f->fd);
free(f->path);
mmap_cache_unref(f->mmap);
@@ -435,6 +439,39 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) {
return 0;
}
+static int fsync_directory_of_file(int fd) {
+ _cleanup_free_ char *path = NULL, *dn = NULL;
+ _cleanup_close_ int dfd = -1;
+ struct stat st;
+ int r;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EBADFD;
+
+ r = fd_get_path(fd, &path);
+ if (r < 0)
+ return r;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ dn = dirname_malloc(path);
+ if (!dn)
+ return -ENOMEM;
+
+ dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0)
+ return -errno;
+
+ if (fsync(dfd) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int journal_file_refresh_header(JournalFile *f) {
sd_id128_t boot_id;
int r;
@@ -460,6 +497,9 @@ static int journal_file_refresh_header(JournalFile *f) {
/* Sync the online state to disk */
(void) fsync(f->fd);
+ /* We likely just created a new file, also sync the directory this file is located in. */
+ (void) fsync_directory_of_file(f->fd);
+
return r;
}
@@ -703,7 +743,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
/* Objects may only be located at multiple of 64 bit */
if (!VALID64(offset))
- return -EFAULT;
+ return -EBADMSG;
+
+ /* Object may not be located in the file header */
+ if (offset < le64toh(f->header->header_size))
+ return -EBADMSG;
r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t);
if (r < 0)
@@ -1976,9 +2020,14 @@ static int generic_array_bisect(
i = right - 1;
lp = p = le64toh(array->entry_array.items[i]);
if (p <= 0)
- return -EBADMSG;
-
- r = test_object(f, p, needle);
+ r = -EBADMSG;
+ else
+ r = test_object(f, p, needle);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)");
+ n = i;
+ continue;
+ }
if (r < 0)
return r;
@@ -2054,9 +2103,14 @@ static int generic_array_bisect(
p = le64toh(array->entry_array.items[i]);
if (p <= 0)
- return -EBADMSG;
-
- r = test_object(f, p, needle);
+ r = -EBADMSG;
+ else
+ r = test_object(f, p, needle);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)");
+ right = n = i;
+ continue;
+ }
if (r < 0)
return r;
@@ -2461,13 +2515,18 @@ int journal_file_next_entry(
le64toh(f->header->entry_array_offset),
i,
ret, &ofs);
+ if (r == -EBADMSG && direction == DIRECTION_DOWN) {
+ /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read,
+ * consider this the end of the journal file. */
+ log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file.");
+ return 0;
+ }
if (r <= 0)
return r;
if (p > 0 &&
(direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) {
- log_debug("%s: entry array corrupted at entry %"PRIu64,
- f->path, i);
+ log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i);
return -EBADMSG;
}
@@ -2806,11 +2865,11 @@ void journal_file_print_header(JournalFile *f) {
"Data Hash Table Size: %"PRIu64"\n"
"Field Hash Table Size: %"PRIu64"\n"
"Rotate Suggested: %s\n"
- "Head Sequential Number: %"PRIu64"\n"
- "Tail Sequential Number: %"PRIu64"\n"
- "Head Realtime Timestamp: %s\n"
- "Tail Realtime Timestamp: %s\n"
- "Tail Monotonic Timestamp: %s\n"
+ "Head Sequential Number: %"PRIu64" (%"PRIx64")\n"
+ "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n"
+ "Head Realtime Timestamp: %s (%"PRIx64")\n"
+ "Tail Realtime Timestamp: %s (%"PRIx64")\n"
+ "Tail Monotonic Timestamp: %s (%"PRIx64")\n"
"Objects: %"PRIu64"\n"
"Entry Objects: %"PRIu64"\n",
f->path,
@@ -2831,11 +2890,11 @@ void journal_file_print_header(JournalFile *f) {
le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
yes_no(journal_file_rotate_suggested(f, 0)),
- le64toh(f->header->head_entry_seqnum),
- le64toh(f->header->tail_entry_seqnum),
- format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
- format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
- format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC),
+ le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum),
+ le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum),
+ format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime),
+ format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime),
+ format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic),
le64toh(f->header->n_objects),
le64toh(f->header->n_entries));
@@ -2898,6 +2957,7 @@ static int journal_file_warn_btrfs(JournalFile *f) {
}
int journal_file_open(
+ int fd,
const char *fname,
int flags,
mode_t mode,
@@ -2914,22 +2974,24 @@ int journal_file_open(
void *h;
int r;
- assert(fname);
assert(ret);
+ assert(fd >= 0 || fname);
if ((flags & O_ACCMODE) != O_RDONLY &&
(flags & O_ACCMODE) != O_RDWR)
return -EINVAL;
- if (!endswith(fname, ".journal") &&
- !endswith(fname, ".journal~"))
- return -EINVAL;
+ if (fname) {
+ if (!endswith(fname, ".journal") &&
+ !endswith(fname, ".journal~"))
+ return -EINVAL;
+ }
f = new0(JournalFile, 1);
if (!f)
return -ENOMEM;
- f->fd = -1;
+ f->fd = fd;
f->mode = mode;
f->flags = flags;
@@ -2954,7 +3016,10 @@ int journal_file_open(
}
}
- f->path = strdup(fname);
+ if (fname)
+ f->path = strdup(fname);
+ else /* If we don't know the path, fill in something explanatory and vaguely useful */
+ asprintf(&f->path, "/proc/self/%i", fd);
if (!f->path) {
r = -ENOMEM;
goto fail;
@@ -2966,10 +3031,15 @@ int journal_file_open(
goto fail;
}
- f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
if (f->fd < 0) {
- r = -errno;
- goto fail;
+ f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
+ if (f->fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* fds we opened here by us should also be closed by us. */
+ f->close_fd = true;
}
r = journal_file_fstat(f);
@@ -3090,6 +3160,9 @@ int journal_file_open(
goto fail;
}
+ /* The file is opened now successfully, thus we take possesion of any passed in fd. */
+ f->close_fd = true;
+
*ret = f;
return 0;
@@ -3116,6 +3189,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
if (!old_file->writable)
return -EINVAL;
+ /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
+ * rotation, since we don't know the actual path, and couldn't rename the file hence.*/
+ if (path_startswith(old_file->path, "/proc/self/fd"))
+ return -EINVAL;
+
if (!endswith(old_file->path, ".journal"))
return -EINVAL;
@@ -3135,14 +3213,23 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
if (r < 0 && errno != ENOENT)
return -errno;
- old_file->header->state = STATE_ARCHIVED;
+ /* Sync the rename to disk */
+ (void) fsync_directory_of_file(old_file->fd);
+
+ /* Set as archive so offlining commits w/state=STATE_ARCHIVED.
+ * Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
+ * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
+ * would result in the rotated journal never getting fsync() called before closing.
+ * Now we simply queue the archive state by setting an archive bit, leaving the state
+ * as STATE_ONLINE so proper offlining occurs. */
+ old_file->archive = true;
/* Currently, btrfs is not very good with out write patterns
* and fragments heavily. Let's defrag our journal files when
* we archive them */
old_file->defrag_on_close = true;
- r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file);
+ r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file);
if (deferred_closes &&
set_put(deferred_closes, old_file) >= 0)
@@ -3170,7 +3257,7 @@ int journal_file_open_reliably(
size_t l;
_cleanup_free_ char *p = NULL;
- r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
+ r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
if (!IN_SET(r,
-EBADMSG, /* corrupted */
-ENODATA, /* truncated */
@@ -3206,12 +3293,12 @@ int journal_file_open_reliably(
/* btrfs doesn't cope well with our write pattern and
* fragments heavily. Let's defrag all files we rotate */
- (void) chattr_path(p, false, FS_NOCOW_FL);
+ (void) chattr_path(p, 0, FS_NOCOW_FL);
(void) btrfs_defrag(p);
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
- return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
+ return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
}
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 9ad6013359..564e1a8179 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -85,6 +85,8 @@ typedef struct JournalFile {
bool compress_lz4:1;
bool seal:1;
bool defrag_on_close:1;
+ bool close_fd:1;
+ bool archive:1;
bool tail_entry_monotonic_valid:1;
@@ -142,6 +144,7 @@ typedef struct JournalFile {
} JournalFile;
int journal_file_open(
+ int fd,
const char *fname,
int flags,
mode_t mode,
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index 7639325acf..34a48141f5 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -82,6 +82,8 @@ struct Directory {
};
struct sd_journal {
+ int toplevel_fd;
+
char *path;
char *prefix;
@@ -117,6 +119,7 @@ struct sd_journal {
bool on_network:1;
bool no_new_files:1;
+ bool no_inotify:1;
bool unique_file_lost:1; /* File we were iterating over got
removed, and there were no more
files, so sd_j_enumerate_unique
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a79846146a..f0959b6237 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -316,7 +316,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
buffer_fd = memfd_new(NULL);
if (buffer_fd < 0) {
if (buffer_fd == -ENOSYS) {
- buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC);
+ buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
if (buffer_fd < 0)
return buffer_fd;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index a1241c9bcf..26572ddd76 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -838,19 +838,19 @@ int journal_file_verify(
} else if (f->seal)
return -ENOKEY;
- data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (data_fd < 0) {
r = log_error_errno(data_fd, "Failed to create data file: %m");
goto fail;
}
- entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_fd < 0) {
r = log_error_errno(entry_fd, "Failed to create entry file: %m");
goto fail;
}
- entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_array_fd < 0) {
r = log_error_errno(entry_array_fd,
"Failed to create entry array file: %m");
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index fd2cb99410..f67c556783 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -95,11 +95,13 @@ static bool arg_boot = false;
static sd_id128_t arg_boot_id = {};
static int arg_boot_offset = 0;
static bool arg_dmesg = false;
+static bool arg_no_hostname = false;
static const char *arg_cursor = NULL;
static const char *arg_after_cursor = NULL;
static bool arg_show_cursor = false;
static const char *arg_directory = NULL;
static char **arg_file = NULL;
+static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
static const char *arg_verify_key = NULL;
#ifdef HAVE_GCRYPT
@@ -304,6 +306,7 @@ static void help(void) {
" -a --all Show all fields, including long and unprintable\n"
" -q --quiet Do not show info messages and privilege warning\n"
" --no-pager Do not pipe output into a pager\n"
+ " --no-hostname Suppress output of hostname field\n"
" -m --merge Show entries from all available journals\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
@@ -370,6 +373,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VACUUM_SIZE,
ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
+ ARG_NO_HOSTNAME,
};
static const struct option options[] = {
@@ -427,6 +431,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
+ { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
{}
};
@@ -588,9 +593,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_FILE:
- r = glob_extend(&arg_file, optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to add paths: %m");
+ if (streq(optarg, "-"))
+ /* An undocumented feature: we can read journal files from STDIN. We don't document
+ * this though, since after all we only support this for mmap-able, seekable files, and
+ * not for example pipes which are probably the primary usecase for reading things from
+ * STDIN. To avoid confusion we hence don't document this feature. */
+ arg_file_stdin = true;
+ else {
+ r = glob_extend(&arg_file, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add paths: %m");
+ }
break;
case ARG_ROOT:
@@ -780,6 +793,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_LIST_FIELD_NAMES;
break;
+ case ARG_NO_HOSTNAME:
+ arg_no_hostname = true;
+ break;
+
case 'x':
arg_catalog = true;
break;
@@ -856,6 +873,18 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) {
+
+ /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user
+ * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice
+ * to users, and automatically turn --unit= into --user-unit= if combined with --user. */
+ r = strv_extend_strv(&arg_user_units, arg_system_units, true);
+ if (r < 0)
+ return -ENOMEM;
+
+ arg_system_units = strv_free(arg_system_units);
+ }
+
return 1;
}
@@ -980,18 +1009,18 @@ static void boot_id_free_all(BootId *l) {
}
}
-static int discover_next_boot(
- sd_journal *j,
- BootId **boot,
+static int discover_next_boot(sd_journal *j,
+ sd_id128_t previous_boot_id,
bool advance_older,
- bool read_realtime) {
+ BootId **ret) {
- int r;
- char match[9+32+1] = "_BOOT_ID=";
_cleanup_free_ BootId *next_boot = NULL;
+ char match[9+32+1] = "_BOOT_ID=";
+ sd_id128_t boot_id;
+ int r;
assert(j);
- assert(boot);
+ assert(ret);
/* We expect the journal to be on the last position of a boot
* (in relation to the direction we are going), so that the next
@@ -1004,29 +1033,40 @@ static int discover_next_boot(
* we can actually advance to a *different* boot. */
sd_journal_flush_matches(j);
- if (advance_older)
- r = sd_journal_previous(j);
- else
- r = sd_journal_next(j);
- if (r < 0)
- return r;
- else if (r == 0)
- return 0; /* End of journal, yay. */
+ do {
+ if (advance_older)
+ r = sd_journal_previous(j);
+ else
+ r = sd_journal_next(j);
+ if (r < 0)
+ return r;
+ else if (r == 0)
+ return 0; /* End of journal, yay. */
+
+ r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
+ if (r < 0)
+ return r;
+
+ /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
+ * normally, this will only require a single iteration, as we seeked to the last entry of the previous
+ * boot entry already. However, it might happen that the per-journal-field entry arrays are less
+ * complete than the main entry array, and hence might reference an entry that's not actually the last
+ * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
+ * speed things up, but let's not trust that it is complete, and hence, manually advance as
+ * necessary. */
+
+ } while (sd_id128_equal(boot_id, previous_boot_id));
next_boot = new0(BootId, 1);
if (!next_boot)
return -ENOMEM;
- r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id);
+ next_boot->id = boot_id;
+
+ r = sd_journal_get_realtime_usec(j, &next_boot->first);
if (r < 0)
return r;
- if (read_realtime) {
- r = sd_journal_get_realtime_usec(j, &next_boot->first);
- if (r < 0)
- return r;
- }
-
/* Now seek to the last occurrence of this boot ID. */
sd_id128_to_string(next_boot->id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
@@ -1049,13 +1089,11 @@ static int discover_next_boot(
else if (r == 0)
return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- if (read_realtime) {
- r = sd_journal_get_realtime_usec(j, &next_boot->last);
- if (r < 0)
- return r;
- }
+ r = sd_journal_get_realtime_usec(j, &next_boot->last);
+ if (r < 0)
+ return r;
- *boot = next_boot;
+ *ret = next_boot;
next_boot = NULL;
return 0;
@@ -1064,47 +1102,48 @@ static int discover_next_boot(
static int get_boots(
sd_journal *j,
BootId **boots,
- BootId *query_ref_boot,
+ sd_id128_t *query_ref_boot,
int ref_boot_offset) {
bool skip_once;
int r, count = 0;
BootId *head = NULL, *tail = NULL;
const bool advance_older = query_ref_boot && ref_boot_offset <= 0;
+ sd_id128_t previous_boot_id;
assert(j);
/* Adjust for the asymmetry that offset 0 is
* the last (and current) boot, while 1 is considered the
* (chronological) first boot in the journal. */
- skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0;
+ skip_once = query_ref_boot && sd_id128_is_null(*query_ref_boot) && ref_boot_offset < 0;
/* Advance to the earliest/latest occurrence of our reference
* boot ID (taking our lookup direction into account), so that
* discover_next_boot() can do its job.
* If no reference is given, the journal head/tail will do,
* they're "virtual" boots after all. */
- if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) {
+ if (query_ref_boot && !sd_id128_is_null(*query_ref_boot)) {
char match[9+32+1] = "_BOOT_ID=";
sd_journal_flush_matches(j);
- sd_id128_to_string(query_ref_boot->id, match + 9);
+ sd_id128_to_string(*query_ref_boot, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
return r;
if (advance_older)
- r = sd_journal_seek_head(j);
+ r = sd_journal_seek_head(j); /* seek to oldest */
else
- r = sd_journal_seek_tail(j);
+ r = sd_journal_seek_tail(j); /* seek to newest */
if (r < 0)
return r;
if (advance_older)
- r = sd_journal_next(j);
+ r = sd_journal_next(j); /* read the oldest entry */
else
- r = sd_journal_previous(j);
+ r = sd_journal_previous(j); /* read the most recently added entry */
if (r < 0)
return r;
else if (r == 0)
@@ -1113,21 +1152,31 @@ static int get_boots(
count = 1;
goto finish;
}
+
+ /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot
+ * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at
+ * the following entry, which must then have an older/newer boot ID */
} else {
+
if (advance_older)
- r = sd_journal_seek_tail(j);
+ r = sd_journal_seek_tail(j); /* seek to newest */
else
- r = sd_journal_seek_head(j);
+ r = sd_journal_seek_head(j); /* seek to oldest */
if (r < 0)
return r;
- /* No sd_journal_next/previous here. */
+ /* No sd_journal_next()/_previous() here.
+ *
+ * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
+ * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
+ * entry we have. */
}
+ previous_boot_id = SD_ID128_NULL;
for (;;) {
_cleanup_free_ BootId *current = NULL;
- r = discover_next_boot(j, &current, advance_older, !query_ref_boot);
+ r = discover_next_boot(j, previous_boot_id, advance_older, &current);
if (r < 0) {
boot_id_free_all(head);
return r;
@@ -1136,6 +1185,8 @@ static int get_boots(
if (!current)
break;
+ previous_boot_id = current->id;
+
if (query_ref_boot) {
if (!skip_once)
ref_boot_offset += advance_older ? 1 : -1;
@@ -1143,7 +1194,7 @@ static int get_boots(
if (ref_boot_offset == 0) {
count = 1;
- query_ref_boot->id = current->id;
+ *query_ref_boot = current->id;
break;
}
} else {
@@ -1199,8 +1250,8 @@ static int list_boots(sd_journal *j) {
static int add_boot(sd_journal *j) {
char match[9+32+1] = "_BOOT_ID=";
+ sd_id128_t ref_boot_id;
int r;
- BootId ref_boot_id = {};
assert(j);
@@ -1210,7 +1261,7 @@ static int add_boot(sd_journal *j) {
if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL))
return add_match_this_boot(j, arg_machine);
- ref_boot_id.id = arg_boot_id;
+ ref_boot_id = arg_boot_id;
r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset);
assert(r <= 1);
if (r <= 0) {
@@ -1226,7 +1277,7 @@ static int add_boot(sd_journal *j) {
return r == 0 ? -ENODATA : r;
}
- sd_id128_to_string(ref_boot_id.id, match + 9);
+ sd_id128_to_string(ref_boot_id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
@@ -1839,7 +1890,7 @@ static int access_check(sd_journal *j) {
break;
default:
- log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path);
+ log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path);
break;
}
}
@@ -2095,11 +2146,61 @@ int main(int argc, char *argv[]) {
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
- else if (arg_file)
+ else if (arg_file_stdin) {
+ int ifd = STDIN_FILENO;
+ r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
+ } else if (arg_file)
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
- else if (arg_machine)
- r = sd_journal_open_container(&j, arg_machine, 0);
- else
+ else if (arg_machine) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int fd;
+
+ if (geteuid() != 0) {
+ /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
+ * the container, thus we need root privileges to override them. */
+ log_error("Using the --machine= switch requires root privileges.");
+ r = -EPERM;
+ goto finish;
+ }
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open system bus: %m");
+ goto finish;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "OpenMachineRootDirectory",
+ &error,
+ &reply,
+ "s", arg_machine);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "h", &fd);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0) {
+ r = log_error_errno(errno, "Failed to duplicate file descriptor: %m");
+ goto finish;
+ }
+
+ r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT);
+ if (r < 0)
+ safe_close(fd);
+ } else
r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
@@ -2275,6 +2376,10 @@ int main(int argc, char *argv[]) {
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
r = sd_journal_get_fd(j);
+ if (r == -EMEDIUMTYPE) {
+ log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN.");
+ goto finish;
+ }
if (r < 0) {
log_error_errno(r, "Failed to get journal fd: %m");
goto finish;
@@ -2444,7 +2549,8 @@ int main(int argc, char *argv[]) {
arg_full * OUTPUT_FULL_WIDTH |
colors_enabled() * OUTPUT_COLOR |
arg_catalog * OUTPUT_CATALOG |
- arg_utc * OUTPUT_UTC;
+ arg_utc * OUTPUT_UTC |
+ arg_no_hostname * OUTPUT_NO_HOSTNAME;
r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
need_seek = true;
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index c154610c54..7fecd7a964 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -19,7 +19,9 @@ Journal.Storage, config_parse_storage, 0, offsetof(Server, storage
Journal.Compress, config_parse_bool, 0, offsetof(Server, compress)
Journal.Seal, config_parse_bool, 0, offsetof(Server, seal)
Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec)
+# The following is a legacy name for compatibility
Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval)
+Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval)
Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst)
Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use)
Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size)
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 3d8f05996b..a445291a5e 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -206,7 +206,7 @@ void server_process_native_message(
allow_object_pid(ucred)) {
char buf[DECIMAL_STR_MAX(pid_t)];
memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
- char_array_0(buf);
+ buf[l-strlen("OBJECT_PID=")] = '\0';
/* ignore error */
parse_pid(buf, &object_pid);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 2939322925..e14d0ad980 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -253,7 +253,7 @@ static int open_journal(
if (reliably)
r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
else
- r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
+ r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
if (r < 0)
return r;
@@ -492,38 +492,36 @@ static void server_cache_hostname(Server *s) {
}
static bool shall_try_append_again(JournalFile *f, int r) {
-
- /* -E2BIG Hit configured limit
- -EFBIG Hit fs limit
- -EDQUOT Quota limit hit
- -ENOSPC Disk full
- -EIO I/O error of some kind (mmap)
- -EHOSTDOWN Other machine
- -EBUSY Unclean shutdown
- -EPROTONOSUPPORT Unsupported feature
- -EBADMSG Corrupted
- -ENODATA Truncated
- -ESHUTDOWN Already archived
- -EIDRM Journal file has been deleted */
-
- if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC)
+ switch(r) {
+ case -E2BIG: /* Hit configured limit */
+ case -EFBIG: /* Hit fs limit */
+ case -EDQUOT: /* Quota limit hit */
+ case -ENOSPC: /* Disk full */
log_debug("%s: Allocation limit reached, rotating.", f->path);
- else if (r == -EHOSTDOWN)
+ return true;
+ case -EIO: /* I/O error of some kind (mmap) */
+ log_warning("%s: IO error, rotating.", f->path);
+ return true;
+ case -EHOSTDOWN: /* Other machine */
log_info("%s: Journal file from other machine, rotating.", f->path);
- else if (r == -EBUSY)
+ return true;
+ case -EBUSY: /* Unclean shutdown */
log_info("%s: Unclean shutdown, rotating.", f->path);
- else if (r == -EPROTONOSUPPORT)
+ return true;
+ case -EPROTONOSUPPORT: /* Unsupported feature */
log_info("%s: Unsupported feature, rotating.", f->path);
- else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN)
+ return true;
+ case -EBADMSG: /* Corrupted */
+ case -ENODATA: /* Truncated */
+ case -ESHUTDOWN: /* Already archived */
log_warning("%s: Journal file corrupted, rotating.", f->path);
- else if (r == -EIO)
- log_warning("%s: IO error, rotating.", f->path);
- else if (r == -EIDRM)
+ return true;
+ case -EIDRM: /* Journal file has been deleted */
log_warning("%s: Journal file has been deleted, rotating.", f->path);
- else
+ return true;
+ default:
return false;
-
- return true;
+ }
}
static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n, int priority) {
@@ -1662,7 +1660,7 @@ static int server_connect_notify(Server *s) {
it. Specifically: given that PID 1 might block on
dbus-daemon during IPC, and dbus-daemon is logging to us,
and might hence block on us, we might end up in a deadlock
- if we block on sending PID 1 notification messages -- by
+ if we block on sending PID 1 notification messages — by
generating a full blocking circle. To avoid this, let's
create a non-blocking socket, and connect it to the
notification socket, and then wait for POLLOUT before we
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 7beb96c671..2541b949be 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -17,7 +17,7 @@
#Seal=yes
#SplitMode=uid
#SyncIntervalSec=5m
-#RateLimitInterval=30s
+#RateLimitIntervalSec=30s
#RateLimitBurst=1000
#SystemMaxUse=
#SystemKeepFree=
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 3c21d4129e..27c1dd346f 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1233,14 +1233,37 @@ static bool file_type_wanted(int flags, const char *filename) {
return false;
}
-static int add_any_file(sd_journal *j, const char *path) {
+static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
+ assert(j);
+ assert(path);
+ assert(prefix);
+
+ if (j->toplevel_fd >= 0)
+ return false;
+
+ return path_startswith(path, prefix);
+}
+
+static const char *skip_slash(const char *p) {
+
+ if (!p)
+ return NULL;
+
+ while (*p == '/')
+ p++;
+
+ return p;
+}
+
+static int add_any_file(sd_journal *j, int fd, const char *path) {
JournalFile *f = NULL;
+ bool close_fd = false;
int r, k;
assert(j);
- assert(path);
+ assert(fd >= 0 || path);
- if (ordered_hashmap_get(j->files, path))
+ if (path && ordered_hashmap_get(j->files, path))
return 0;
if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
@@ -1249,8 +1272,24 @@ static int add_any_file(sd_journal *j, const char *path) {
goto fail;
}
- r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
+ if (fd < 0 && j->toplevel_fd >= 0) {
+
+ /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
+ * explicitly, since otherwise openat() ignores the first argument.) */
+
+ fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
+ goto fail;
+ }
+
+ close_fd = true;
+ }
+
+ r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
if (r < 0) {
+ if (close_fd)
+ safe_close(fd);
log_debug_errno(r, "Failed to open journal file %s: %m", path);
goto fail;
}
@@ -1259,10 +1298,16 @@ static int add_any_file(sd_journal *j, const char *path) {
r = ordered_hashmap_put(j->files, f->path, f);
if (r < 0) {
+ f->close_fd = close_fd;
(void) journal_file_close(f);
goto fail;
}
+ if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
+ j->has_runtime_files = true;
+ else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
+ j->has_persistent_files = true;
+
log_debug("File %s added.", f->path);
check_network(j, f->fd);
@@ -1286,18 +1331,14 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
assert(prefix);
assert(filename);
- if (j->no_new_files ||
- !file_type_wanted(j->flags, filename))
+ if (j->no_new_files)
+ return 0;
+
+ if (!file_type_wanted(j->flags, filename))
return 0;
path = strjoina(prefix, "/", filename);
-
- if (!j->has_runtime_files && path_startswith(path, "/run/log/journal"))
- j->has_runtime_files = true;
- else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal"))
- j->has_persistent_files = true;
-
- return add_any_file(j, path);
+ return add_any_file(j, -1, path);
}
static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
@@ -1373,21 +1414,33 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
assert(j);
assert(prefix);
- assert(dirname);
-
- log_debug("Considering %s/%s.", prefix, dirname);
- if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
- !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run")))
- return 0;
+ /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
+ * and reenumerates directory contents */
- path = strjoin(prefix, "/", dirname, NULL);
+ if (dirname)
+ path = strjoin(prefix, "/", dirname, NULL);
+ else
+ path = strdup(prefix);
if (!path) {
r = -ENOMEM;
goto fail;
}
- d = opendir(path);
+ log_debug("Considering directory %s.", path);
+
+ /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
+ if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
+ !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
+ return 0;
+
+
+ if (j->toplevel_fd < 0)
+ d = opendir(path);
+ else
+ /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is
+ * relative, by dropping the initial slash */
+ d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
if (!d) {
r = log_debug_errno(errno, "Failed to open directory %s: %m", path);
goto fail;
@@ -1419,17 +1472,18 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
return 0;
if (m->wd <= 0 && j->inotify_fd >= 0) {
+ /* Watch this directory, if it not being watched yet. */
- m->wd = inotify_add_watch(j->inotify_fd, m->path,
- IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
- IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
- IN_ONLYDIR);
+ m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
+ IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
+ IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
+ IN_ONLYDIR);
if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
inotify_rm_watch(j->inotify_fd, m->wd);
}
- FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
+ FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~"))
@@ -1441,7 +1495,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
return 0;
fail:
- k = journal_put_error(j, r, path ?: dirname);
+ k = journal_put_error(j, r, path ?: prefix);
if (k < 0)
return k;
@@ -1449,28 +1503,62 @@ fail:
}
static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
+
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
Directory *m;
int r, k;
assert(j);
- assert(p);
- if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
- !path_startswith(p, "/run"))
- return -EINVAL;
+ /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
+ * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
+ * populate the set, as well as to update it later. */
- if (j->prefix)
- p = strjoina(j->prefix, p);
+ if (p) {
+ /* If there's a path specified, use it. */
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT && missing_ok)
- return 0;
+ if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
+ !path_has_prefix(j, p, "/run"))
+ return -EINVAL;
- r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
- goto fail;
+ if (j->prefix)
+ p = strjoina(j->prefix, p);
+
+ if (j->toplevel_fd < 0)
+ d = opendir(p);
+ else
+ d = xopendirat(j->toplevel_fd, skip_slash(p), 0);
+
+ if (!d) {
+ if (errno == ENOENT && missing_ok)
+ return 0;
+
+ r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
+ goto fail;
+ }
+ } else {
+ int dfd;
+
+ /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
+ * opendir() will take possession of the fd, and close it, which we don't want. */
+
+ p = "."; /* store this as "." in the directories hashmap */
+
+ dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
+ if (dfd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ d = fdopendir(dfd);
+ if (!d) {
+ r = -errno;
+ safe_close(dfd);
+ goto fail;
+ }
+
+ rewinddir(d);
}
m = hashmap_get(j->directories_by_path, p);
@@ -1482,6 +1570,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
}
m->is_root = true;
+
m->path = strdup(p);
if (!m->path) {
free(m);
@@ -1505,7 +1594,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
if (m->wd <= 0 && j->inotify_fd >= 0) {
- m->wd = inotify_add_watch(j->inotify_fd, m->path,
+ m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_ONLYDIR);
@@ -1516,7 +1605,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
if (j->no_new_files)
return 0;
- FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
+ FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
sd_id128_t id;
if (dirent_is_file_with_suffix(de, ".journal") ||
@@ -1585,8 +1674,7 @@ static int add_current_paths(sd_journal *j) {
assert(j);
assert(j->no_new_files);
- /* Simply adds all directories for files we have open as
- * "root" directories. We don't expect errors here, so we
+ /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
* treat them as fatal. */
ORDERED_HASHMAP_FOREACH(f, j->files, i) {
@@ -1597,7 +1685,7 @@ static int add_current_paths(sd_journal *j) {
if (!dir)
return -ENOMEM;
- r = add_root_directory(j, dir, true);
+ r = add_directory(j, dir, NULL);
if (r < 0)
return r;
}
@@ -1614,13 +1702,7 @@ static int allocate_inotify(sd_journal *j) {
return -errno;
}
- if (!j->directories_by_wd) {
- j->directories_by_wd = hashmap_new(NULL);
- if (!j->directories_by_wd)
- return -ENOMEM;
- }
-
- return 0;
+ return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
}
static sd_journal *journal_new(int flags, const char *path) {
@@ -1631,6 +1713,7 @@ static sd_journal *journal_new(int flags, const char *path) {
return NULL;
j->original_pid = getpid();
+ j->toplevel_fd = -1;
j->inotify_fd = -1;
j->flags = flags;
j->data_threshold = DEFAULT_DATA_THRESHOLD;
@@ -1684,6 +1767,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
char *p;
int r;
+ /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
+ * combination with sd_journal_open_directory_fd(). */
+
assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL);
assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
@@ -1726,13 +1812,16 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
assert_return(ret, -EINVAL);
assert_return(path, -EINVAL);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
j = journal_new(flags, path);
if (!j)
return -ENOMEM;
- r = add_root_directory(j, path, false);
+ if (flags & SD_JOURNAL_OS_ROOT)
+ r = add_search_paths(j);
+ else
+ r = add_root_directory(j, path, false);
if (r < 0)
goto fail;
@@ -1741,7 +1830,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
fail:
sd_journal_close(j);
-
return r;
}
@@ -1758,7 +1846,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
return -ENOMEM;
STRV_FOREACH(path, paths) {
- r = add_any_file(j, *path);
+ r = add_any_file(j, -1, *path);
if (r < 0)
goto fail;
}
@@ -1770,7 +1858,96 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
fail:
sd_journal_close(j);
+ return r;
+}
+
+_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
+ sd_journal *j;
+ struct stat st;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(fd >= 0, -EBADF);
+ assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -EBADFD;
+
+ j = journal_new(flags, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ j->toplevel_fd = fd;
+
+ if (flags & SD_JOURNAL_OS_ROOT)
+ r = add_search_paths(j);
+ else
+ r = add_root_directory(j, NULL, false);
+ if (r < 0)
+ goto fail;
+ *ret = j;
+ return 0;
+
+fail:
+ sd_journal_close(j);
+ return r;
+}
+
+_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
+ Iterator iterator;
+ JournalFile *f;
+ sd_journal *j;
+ unsigned i;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(n_fds > 0, -EBADF);
+ assert_return(flags == 0, -EINVAL);
+
+ j = journal_new(flags, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ for (i = 0; i < n_fds; i++) {
+ struct stat st;
+
+ if (fds[i] < 0) {
+ r = -EBADF;
+ goto fail;
+ }
+
+ if (fstat(fds[i], &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ r = -EBADFD;
+ goto fail;
+ }
+
+ r = add_any_file(j, fds[i], NULL);
+ if (r < 0)
+ goto fail;
+ }
+
+ j->no_new_files = true;
+ j->no_inotify = true;
+
+ *ret = j;
+ return 0;
+
+fail:
+ /* If we fail, make sure we don't take possession of the files we managed to make use of successfuly, and they
+ * remain open */
+ ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
+ f->close_fd = false;
+
+ sd_journal_close(j);
return r;
}
@@ -2097,6 +2274,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
+ if (j->no_inotify)
+ return -EMEDIUMTYPE;
+
if (j->inotify_fd >= 0)
return j->inotify_fd;
@@ -2104,10 +2284,14 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
if (r < 0)
return r;
+ log_debug("Reiterating files to get inotify watches established");
+
/* Iterate through all dirs again, to add them to the
* inotify */
if (j->no_new_files)
r = add_current_paths(j);
+ else if (j->toplevel_fd >= 0)
+ r = add_root_directory(j, NULL, false);
else if (j->path)
r = add_root_directory(j, j->path, true);
else
diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c
index 0ef6d36a50..6f6d71435d 100644
--- a/src/journal/test-compress-benchmark.c
+++ b/src/journal/test-compress-benchmark.c
@@ -164,7 +164,7 @@ int main(int argc, char *argv[]) {
arg_duration = x * USEC_PER_SEC;
}
if (argc == 3)
- (void) safe_atolu(argv[2], &arg_start);
+ (void) safe_atozu(argv[2], &arg_start);
else
arg_start = getpid();
diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c
index 93dc0e0d81..ba8b20b228 100644
--- a/src/journal/test-journal-flush.c
+++ b/src/journal/test-journal-flush.c
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(dn));
fn = strappend(dn, "/test.journal");
- r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal);
+ r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal);
assert_se(r >= 0);
r = sd_journal_open(&j, 0);
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index f887f43f0d..5e063f4d04 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -52,7 +52,7 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil
static JournalFile *test_open(const char *name) {
JournalFile *f;
- assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f));
+ assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f));
return f;
}
@@ -216,7 +216,7 @@ static void test_sequence_numbers(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644,
+ assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644,
true, false, NULL, NULL, NULL, NULL, &one) == 0);
append_number(one, 1, &seqnum);
@@ -233,7 +233,7 @@ static void test_sequence_numbers(void) {
memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644,
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644,
true, false, NULL, NULL, NULL, one, &two) == 0);
assert_se(two->header->state == STATE_ONLINE);
@@ -264,7 +264,7 @@ static void test_sequence_numbers(void) {
/* restart server */
seqnum = 0;
- assert_se(journal_file_open("two.journal", O_RDWR, 0,
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0,
true, false, NULL, NULL, NULL, NULL, &two) == 0);
assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index 839ea5a9a5..7e5a980719 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -92,9 +92,9 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0);
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0);
- assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0);
+ assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0);
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0);
+ assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0);
for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index 6b4643cd25..3d2312fc55 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -55,7 +55,7 @@ static int raw_verify(const char *fn, const char *verification_key) {
JournalFile *f;
int r;
- r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f);
+ r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f);
if (r < 0)
return r;
@@ -88,7 +88,7 @@ int main(int argc, char *argv[]) {
log_info("Generating...");
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
for (n = 0; n < N_ENTRIES; n++) {
struct iovec iovec;
@@ -111,7 +111,7 @@ int main(int argc, char *argv[]) {
log_info("Verifying...");
- assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
/* journal_file_print_header(f); */
journal_file_dump(f);
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index ea685af782..2543d64b5b 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -42,7 +42,7 @@ static void test_non_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0);
dual_timestamp_get(&ts);
@@ -131,13 +131,13 @@ static void test_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0);
- assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0);
+ assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0);
- assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0);
+ assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0);
- assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0);
+ assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0);
journal_file_print_header(f1);
puts("");
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 1d9ec7be82..4f7d4d8bf2 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -43,7 +43,7 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
if (r < 0)
return r;
- unaligned_write_be16(&duid->type, DHCP6_DUID_EN);
+ unaligned_write_be16(&duid->type, DUID_TYPE_EN);
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
*len = sizeof(duid->type) + sizeof(duid->en);
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index 93f06f5938..e6486b78f8 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -25,13 +25,23 @@
#include "sparse-endian.h"
#include "unaligned.h"
+typedef enum DUIDType {
+ DUID_TYPE_RAW = 0,
+ DUID_TYPE_LLT = 1,
+ DUID_TYPE_EN = 2,
+ DUID_TYPE_LL = 3,
+ DUID_TYPE_UUID = 4,
+ _DUID_TYPE_MAX,
+ _DUID_TYPE_INVALID = -1,
+} DUIDType;
+
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
struct duid {
- uint16_t type;
+ be16_t type;
union {
struct {
/* DHCP6_DUID_LLT */
@@ -61,3 +71,34 @@ struct duid {
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
+
+static inline int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
+ struct duid d;
+
+ assert(duid_len > 0);
+
+ switch (duid_type) {
+ case DUID_TYPE_LLT:
+ if (duid_len <= sizeof(d.llt))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_EN:
+ if (duid_len != sizeof(d.en))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_LL:
+ if (duid_len <= sizeof(d.ll))
+ return -EINVAL;
+ break;
+ case DUID_TYPE_UUID:
+ if (duid_len != sizeof(d.uuid))
+ return -EINVAL;
+ break;
+ default:
+ if (duid_len > sizeof(d.raw))
+ return -EINVAL;
+ /* accept unknown type in order to be forward compatible */
+ break;
+ }
+ return 0;
+}
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index ee4bdfb07f..2487c470ab 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -62,13 +62,6 @@ enum {
#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC
#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC
-enum {
- DHCP6_DUID_LLT = 1,
- DHCP6_DUID_EN = 2,
- DHCP6_DUID_LL = 3,
- DHCP6_DUID_UUID = 4,
-};
-
enum DHCP6State {
DHCP6_STATE_STOPPED = 0,
DHCP6_STATE_INFORMATION_REQUEST = 1,
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index cb7252bbeb..99b3a1d01f 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -335,6 +335,36 @@ int config_parse_hwaddr(const char *unit,
return 0;
}
+int config_parse_iaid(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) {
+ uint32_t iaid;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou32(rvalue, &iaid);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Unable to read IAID, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ *((uint32_t *)data) = iaid;
+
+ return 0;
+}
+
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) {
unsigned i;
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index c8a531ab0f..72432774d7 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -62,6 +62,10 @@ int config_parse_ifalias(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_iaid(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 net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
const char *net_get_name(struct udev_device *device);
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 1188b31500..287b6e26fa 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -82,7 +82,7 @@ struct sd_dhcp_client {
} _packed_ ll;
struct {
/* 255: Node-specific (RFC 4361) */
- uint32_t iaid;
+ be32_t iaid;
struct duid duid;
} _packed_ ns;
struct {
@@ -298,6 +298,52 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
return 0;
}
+int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
+ uint16_t duid_type, uint8_t *duid, size_t duid_len) {
+ DHCP_CLIENT_DONT_DESTROY(client);
+ int r;
+ assert_return(client, -EINVAL);
+ zero(client->client_id);
+
+ client->client_id.type = 255;
+
+ /* If IAID is not configured, generate it. */
+ if (iaid == 0) {
+ r = dhcp_identifier_set_iaid(client->index, client->mac_addr,
+ client->mac_addr_len,
+ &client->client_id.ns.iaid);
+ if (r < 0)
+ return r;
+ } else
+ client->client_id.ns.iaid = htobe32(iaid);
+
+ /* If DUID is not configured, generate DUID-EN. */
+ if (duid_len == 0) {
+ r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid,
+ &duid_len);
+ if (r < 0)
+ return r;
+ } else {
+ r = dhcp_validate_duid_len(client->client_id.type, duid_len);
+ if (r < 0)
+ return r;
+ client->client_id.ns.duid.type = htobe16(duid_type);
+ memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
+ duid_len += sizeof(client->client_id.ns.duid.type);
+ }
+
+ client->client_id_len = sizeof(client->client_id.type) + duid_len +
+ sizeof(client->client_id.ns.iaid);
+
+ if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
+ log_dhcp_client(client, "Configured IAID+DUID, restarting.");
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
+ sd_dhcp_client_start(client);
+ }
+
+ return 0;
+}
+
int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
const char *hostname) {
char *new_hostname = NULL;
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index af4709d788..ee4fb4fc1e 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -180,41 +180,29 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
}
-int sd_dhcp6_client_set_duid(
- sd_dhcp6_client *client,
- uint16_t type,
- uint8_t *duid, size_t duid_len) {
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t duid_type,
+ uint8_t *duid, size_t duid_len) {
+ int r;
assert_return(client, -EINVAL);
- assert_return(duid, -EINVAL);
- assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
-
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- switch (type) {
- case DHCP6_DUID_LLT:
- if (duid_len <= sizeof(client->duid.llt))
- return -EINVAL;
- break;
- case DHCP6_DUID_EN:
- if (duid_len != sizeof(client->duid.en))
- return -EINVAL;
- break;
- case DHCP6_DUID_LL:
- if (duid_len <= sizeof(client->duid.ll))
- return -EINVAL;
- break;
- case DHCP6_DUID_UUID:
- if (duid_len != sizeof(client->duid.uuid))
- return -EINVAL;
- break;
- default:
- /* accept unknown type in order to be forward compatible */
- break;
+ if (duid_len > 0) {
+ r = dhcp_validate_duid_len(duid_type, duid_len);
+ if (r < 0)
+ return r;
+ client->duid.type = htobe16(duid_type);
+ memcpy(&client->duid.raw.data, duid, duid_len);
+ client->duid_len = duid_len + sizeof(client->duid.type);
}
- client->duid.type = htobe16(type);
- memcpy(&client->duid.raw.data, duid, duid_len);
- client->duid_len = duid_len + sizeof(client->duid.type);
+ return 0;
+}
+
+int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
+ assert_return(client, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->ia_na.id = htobe32(iaid);
return 0;
}
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 4ab637b686..0b3a1708dc 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -489,3 +489,9 @@ global:
sd_journal_enumerate_fields;
sd_journal_restart_fields;
} LIBSYSTEMD_227;
+
+LIBSYSTEMD_230 {
+global:
+ sd_journal_open_directory_fd;
+ sd_journal_open_files_fd;
+} LIBSYSTEMD_229;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 6370061daf..02e3bf904c 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -38,6 +38,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL),
+ SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM),
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 464834979a..c8f369cb78 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -34,6 +34,8 @@
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive"
#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked"
+#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated"
+#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked"
#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable"
#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index e860876c12..52128e7b5c 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -678,6 +678,7 @@ int bus_get_name_creds_kdbus(
(mask & (SD_BUS_CREDS_PPID|
SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
@@ -795,6 +796,7 @@ static int bus_get_name_creds_dbus1(
((mask & SD_BUS_CREDS_AUGMENT) &&
(mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
@@ -947,6 +949,7 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **
(mask & (SD_BUS_CREDS_PPID|
SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 862f26aad7..04da94e7e3 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -1778,7 +1778,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie,
r = bus_write_message(bus, m, hint_sync_call, &idx);
if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
return -ECONNRESET;
}
@@ -2083,7 +2083,7 @@ _public_ int sd_bus_call(
r = bus_read_message(bus, false, 0);
if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
r = -ECONNRESET;
}
@@ -2116,7 +2116,7 @@ _public_ int sd_bus_call(
r = dispatch_wqueue(bus);
if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
r = -ECONNRESET;
}
@@ -2766,7 +2766,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
case BUS_OPENING:
r = bus_socket_process_opening(bus);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
r = 1;
} else if (r < 0)
@@ -2777,7 +2777,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
case BUS_AUTHENTICATING:
r = bus_socket_process_authenticating(bus);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
r = 1;
} else if (r < 0)
@@ -2791,7 +2791,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
case BUS_RUNNING:
case BUS_HELLO:
r = process_running(bus, hint_priority, priority, ret);
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
r = 1;
@@ -2914,7 +2914,7 @@ _public_ int sd_bus_flush(sd_bus *bus) {
for (;;) {
r = dispatch_wqueue(bus);
if (r < 0) {
- if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
return -ECONNRESET;
}
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 8657e61cd9..b1c3d5f228 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -1212,19 +1212,19 @@ int device_get_id_filename(sd_device *device, const char **ret) {
if (major(devnum) > 0) {
assert(subsystem);
- /* use dev_t -- b259:131072, c254:0 */
+ /* use dev_t — b259:131072, c254:0 */
r = asprintf(&id, "%c%u:%u",
streq(subsystem, "block") ? 'b' : 'c',
major(devnum), minor(devnum));
if (r < 0)
return -ENOMEM;
} else if (ifindex > 0) {
- /* use netdev ifindex -- n3 */
+ /* use netdev ifindex — n3 */
r = asprintf(&id, "n%u", ifindex);
if (r < 0)
return -ENOMEM;
} else {
- /* use $subsys:$sysname -- pci:0000:00:1f.2
+ /* use $subsys:$sysname — pci:0000:00:1f.2
* sysname() has '!' translated, get it from devpath
*/
const char *sysname;
@@ -1457,15 +1457,20 @@ static int device_properties_prepare(sd_device *device) {
return r;
if (device->property_devlinks_outdated) {
- char *devlinks = NULL;
+ _cleanup_free_ char *devlinks = NULL;
+ size_t devlinks_allocated = 0, devlinks_len = 0;
const char *devlink;
- devlink = sd_device_get_devlink_first(device);
- if (devlink)
- devlinks = strdupa(devlink);
+ for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) {
+ char *e;
- while ((devlink = sd_device_get_devlink_next(device)))
- devlinks = strjoina(devlinks, " ", devlink);
+ if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2))
+ return -ENOMEM;
+ if (devlinks_len > 0)
+ stpcpy(devlinks + devlinks_len++, " ");
+ e = stpcpy(devlinks + devlinks_len, devlink);
+ devlinks_len = e - devlinks;
+ }
r = device_add_property_internal(device, "DEVLINKS", devlinks);
if (r < 0)
@@ -1475,17 +1480,23 @@ static int device_properties_prepare(sd_device *device) {
}
if (device->property_tags_outdated) {
- char *tags = NULL;
+ _cleanup_free_ char *tags = NULL;
+ size_t tags_allocated = 0, tags_len = 0;
const char *tag;
- tag = sd_device_get_tag_first(device);
- if (tag)
- tags = strjoina(":", tag);
+ if (!GREEDY_REALLOC(tags, tags_allocated, 2))
+ return -ENOMEM;
+ stpcpy(tags, ":");
+ tags_len++;
- while ((tag = sd_device_get_tag_next(device)))
- tags = strjoina(tags, ":", tag);
+ for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) {
+ char *e;
- tags = strjoina(tags, ":");
+ if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2))
+ return -ENOMEM;
+ e = stpcpy(stpcpy(tags + tags_len, tag), ":");
+ tags_len = e - tags;
+ }
r = device_add_property_internal(device, "TAGS", tags);
if (r < 0)
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 841358ed03..7ba6527f63 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1072,6 +1072,10 @@ _public_ int sd_event_add_time(
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
+ if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) &&
+ !clock_boottime_supported())
+ return -EOPNOTSUPP;
+
if (!callback)
callback = time_exit_callback;
@@ -1145,8 +1149,7 @@ _public_ int sd_event_add_signal(
int r;
assert_return(e, -EINVAL);
- assert_return(sig > 0, -EINVAL);
- assert_return(sig < _NSIG, -EINVAL);
+ assert_return(SIGNAL_VALID(sig), -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -2200,7 +2203,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
if (_unlikely_(n != sizeof(si)))
return -EIO;
- assert(si.ssi_signo < _NSIG);
+ assert(SIGNAL_VALID(si.ssi_signo));
read_one = true;
@@ -2528,7 +2531,8 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
}
dual_timestamp_get(&e->timestamp);
- e->timestamp_boottime = now(CLOCK_BOOTTIME);
+ if (clock_boottime_supported())
+ e->timestamp_boottime = now(CLOCK_BOOTTIME);
for (i = 0; i < m; i++) {
@@ -2762,6 +2766,9 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
CLOCK_BOOTTIME,
CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
+ if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
+ return -EOPNOTSUPP;
+
if (!dual_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran
* before and thus have no cached time. */
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index fd31588b8f..289114490c 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -270,8 +270,10 @@ static void test_sd_event_now(void) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ if (clock_boottime_supported()) {
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ }
assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
@@ -280,8 +282,10 @@ static void test_sd_event_now(void) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ if (clock_boottime_supported()) {
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ }
assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
}
diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c
index 6abd8fd0cc..ed9ee041ab 100644
--- a/src/libsystemd/sd-netlink/local-addresses.c
+++ b/src/libsystemd/sd-netlink/local-addresses.c
@@ -155,8 +155,7 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre
n_list++;
};
- if (n_list > 0)
- qsort(list, n_list, sizeof(struct local_address), address_compare);
+ qsort_safe(list, n_list, sizeof(struct local_address), address_compare);
*ret = list;
list = NULL;
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index a5758bb516..3a4bac2ced 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -95,12 +95,43 @@ static const NLType rtnl_link_info_data_macvlan_types[] = {
};
static const NLType rtnl_link_info_data_bridge_types[] = {
- [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_vlan_types[] = {
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index 255526bf32..f251536a89 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -402,7 +402,6 @@ int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret,
int r;
assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL);
- assert_return(nlmsg_type != RTM_DELLINK || index > 0, -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index 37585048b8..d8303e2e69 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -579,9 +579,10 @@ static void resolve_free(sd_resolve *resolve) {
(void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL);
}
- /* Now terminate them and wait until they are gone. */
+ /* Now terminate them and wait until they are gone.
+ If we get an error than most likely the thread already exited. */
for (i = 0; i < resolve->n_valid_workers; i++)
- pthread_join(resolve->workers[i], NULL);
+ (void) pthread_join(resolve->workers[i], NULL);
/* Close all communication channels */
for (i = 0; i < _FD_MAX; i++)
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index 33ef6fc0f7..1be1a7f8a7 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -63,7 +63,7 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c
return 0;
}
- printf("Host: %s -- Serv: %s\n", strna(host), strna(serv));
+ printf("Host: %s — Serv: %s\n", strna(host), strna(serv));
return 0;
}
diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map
index 6aadda091a..d0b02a6b98 100644
--- a/src/locale/language-fallback-map
+++ b/src/locale/language-fallback-map
@@ -3,6 +3,10 @@ en_AU en_AU:en_GB
en_IE en_IE:en_GB
en_NZ en_NZ:en_GB
en_ZA en_ZA:en_GB
+fr_BE fr_BE:fr_FR
+fr_CA fr_CA:fr_FR
+fr_CH fr_CH:fr_FR
+fr_LU fr_LU:fr_FR
it_CH it_CH:it_IT
mai_IN mai:hi
nds_DE nds:de
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 46405ca68a..3b22a582ac 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -24,6 +24,7 @@
#ifdef HAVE_XKBCOMMON
#include <xkbcommon/xkbcommon.h>
+#include <dlfcn.h>
#endif
#include "sd-bus.h"
@@ -1101,6 +1102,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
}
#ifdef HAVE_XKBCOMMON
+
_printf_(3, 0)
static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
const char *fmt;
@@ -1109,7 +1111,24 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char
log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
}
+#define LOAD_SYMBOL(symbol, dl, name) \
+ ({ \
+ (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \
+ (symbol) ? 0 : -EOPNOTSUPP; \
+ })
+
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
+
+ /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge
+ * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function
+ * pointers to the shared library are below: */
+
+ struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL;
+ void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL;
+ void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL;
+ struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL;
+ void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL;
+
const struct xkb_rule_names rmlvo = {
.model = model,
.layout = layout,
@@ -1118,35 +1137,68 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v
};
struct xkb_context *ctx = NULL;
struct xkb_keymap *km = NULL;
+ void *dl;
int r;
- /* compile keymap from RMLVO information to check out its validity */
+ /* Compile keymap from RMLVO information to check out its validity */
+
+ dl = dlopen("libxkbcommon.so.0", RTLD_LAZY);
+ if (!dl)
+ return -EOPNOTSUPP;
+
+ r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new");
+ if (r < 0)
+ goto finish;
+
+ r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref");
+ if (r < 0)
+ goto finish;
+
+ r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn");
+ if (r < 0)
+ goto finish;
- ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
+ r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names");
+ if (r < 0)
+ goto finish;
+
+ r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref");
+ if (r < 0)
+ goto finish;
+
+ ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
if (!ctx) {
r = -ENOMEM;
- goto exit;
+ goto finish;
}
- xkb_context_set_log_fn(ctx, log_xkb);
+ symbol_xkb_context_set_log_fn(ctx, log_xkb);
- km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!km) {
r = -EINVAL;
- goto exit;
+ goto finish;
}
r = 0;
-exit:
- xkb_keymap_unref(km);
- xkb_context_unref(ctx);
+finish:
+ if (symbol_xkb_keymap_unref && km)
+ symbol_xkb_keymap_unref(km);
+
+ if (symbol_xkb_context_unref && ctx)
+ symbol_xkb_context_unref(ctx);
+
+ (void) dlclose(dl);
return r;
}
+
#else
+
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
return 0;
}
+
#endif
static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
@@ -1203,7 +1255,11 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
if (r < 0) {
log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
strempty(model), strempty(layout), strempty(variant), strempty(options));
- return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing");
+
+ if (r == -EOPNOTSUPP)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
+
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
}
if (free_and_strdup(&c->x11_layout, layout) < 0 ||
diff --git a/src/login/.gitignore b/src/login/.gitignore
index 39088ec252..3a8ba497c1 100644
--- a/src/login/.gitignore
+++ b/src/login/.gitignore
@@ -1,4 +1,5 @@
/logind-gperf.c
+/logind.conf
/org.freedesktop.login1.policy
/71-seat.rules
/73-seat-late.rules
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index c9a5cd796b..1c75565636 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -48,6 +49,7 @@
static char **arg_property = NULL;
static bool arg_all = false;
+static bool arg_value = false;
static bool arg_full = false;
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -226,18 +228,15 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
- int r;
unsigned c;
+ int r;
assert(bus);
assert(unit);
- if (arg_transport != BUS_TRANSPORT_LOCAL)
- return 0;
-
path = unit_dbus_path_from_name(unit);
if (!path)
- return -ENOMEM;
+ return log_oom();
r = sd_bus_get_property(
bus,
@@ -245,27 +244,40 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
path,
interface,
"ControlGroup",
- &error, &reply, "s");
+ &error,
+ &reply,
+ "s");
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &cgroup);
if (r < 0)
- return r;
+ return bus_log_parse_error(r);
if (isempty(cgroup))
return 0;
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
- return 0;
-
c = columns();
if (c > 18)
c -= 18;
else
c = 0;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
+ r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
+ if (r == -EBADR) {
+
+ if (arg_transport == BUS_TRANSPORT_REMOTE)
+ return 0;
+
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
+
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ return 0;
+
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
+
return 0;
}
@@ -292,6 +304,7 @@ typedef struct SessionStatusInfo {
typedef struct UserStatusInfo {
uid_t uid;
+ bool linger;
char *name;
struct dual_timestamp timestamp;
char *state;
@@ -550,6 +563,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) },
+ { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) },
{ "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
{ "State", "s", NULL, offsetof(UserStatusInfo, state) },
{ "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
@@ -594,16 +608,16 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
char **l;
printf("\tSessions:");
- STRV_FOREACH(l, i.sessions) {
- if (streq_ptr(*l, i.display))
- printf(" *%s", *l);
- else
- printf(" %s", *l);
- }
+ STRV_FOREACH(l, i.sessions)
+ printf(" %s%s",
+ streq_ptr(*l, i.display) ? "*" : "",
+ *l);
printf("\n");
}
+ printf("\t Linger: %s\n", yes_no(i.linger));
+
if (i.slice) {
printf("\t Unit: %s\n", i.slice);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
@@ -679,6 +693,14 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
return 0;
}
+#define property(name, fmt, ...) \
+ do { \
+ if (arg_value) \
+ printf(fmt "\n", __VA_ARGS__); \
+ else \
+ printf("%s=" fmt "\n", name, __VA_ARGS__); \
+ } while(0)
+
static int print_property(const char *name, sd_bus_message *m, const char *contents) {
int r;
@@ -702,7 +724,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
if (arg_all || !isempty(s))
- printf("%s=%s\n", name, s);
+ property(name, "%s", s);
return 0;
@@ -718,8 +740,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return -EINVAL;
}
- printf("%s=" UID_FMT "\n", name, uid);
-
+ property(name, UID_FMT, uid);
return 0;
}
@@ -735,14 +756,16 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
if (r < 0)
return bus_log_parse_error(r);
- printf("%s=", name);
+ if (!arg_value)
+ printf("%s=", name);
while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
printf("%s%s", space ? " " : "", s);
space = true;
}
- printf("\n");
+ if (space || !arg_value)
+ printf("\n");
if (r < 0)
return bus_log_parse_error(r);
@@ -757,7 +780,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
break;
}
- r = bus_print_property(name, m, arg_all);
+ r = bus_print_property(name, m, arg_value, arg_all);
if (r < 0)
return bus_log_parse_error(r);
@@ -1330,6 +1353,7 @@ static int help(int argc, char *argv[], void *userdata) {
" -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME Show only properties by this name\n"
" -a --all Show all properties, including empty ones\n"
+ " --value When showing properties, only print the value\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
@@ -1371,6 +1395,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
+ ARG_VALUE,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_KILL_WHO,
@@ -1382,6 +1407,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "property", required_argument, NULL, 'p' },
{ "all", no_argument, NULL, 'a' },
+ { "value", no_argument, NULL, ARG_VALUE },
{ "full", no_argument, NULL, 'l' },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
@@ -1427,6 +1453,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
+ case ARG_VALUE:
+ arg_value = true;
+ break;
+
case 'l':
arg_full = true;
break;
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 8bdb3a9a38..cbf8d757fe 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -364,16 +364,16 @@ bool manager_shall_kill(Manager *m, const char *user) {
assert(m);
assert(user);
- if (!m->kill_user_processes)
+ if (!m->kill_exclude_users && streq(user, "root"))
return false;
if (strv_contains(m->kill_exclude_users, user))
return false;
- if (strv_isempty(m->kill_only_users))
- return true;
+ if (!strv_isempty(m->kill_only_users))
+ return strv_contains(m->kill_only_users, user);
- return strv_contains(m->kill_only_users, user);
+ return m->kill_user_processes;
}
static int vt_is_busy(unsigned int vtnr) {
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 1d3133ee25..a281f99a34 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1077,11 +1077,11 @@ static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus
static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *cc = NULL;
Manager *m = userdata;
- int b, r;
+ int r, b, interactive;
struct passwd *pw;
const char *path;
uint32_t uid;
- int interactive;
+ bool self = false;
assert(message);
assert(m);
@@ -1102,6 +1102,8 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
+ self = true;
+
} else if (!uid_is_valid(uid))
return -EINVAL;
@@ -1113,7 +1115,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-user-linger",
+ self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger",
NULL,
interactive,
UID_INVALID,
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index ff9170683b..0f8862c0d9 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -28,6 +28,7 @@
#include "logind-session-device.h"
#include "logind-session.h"
#include "logind.h"
+#include "signal-util.h"
#include "strv.h"
#include "util.h"
@@ -300,7 +301,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
}
- if (signo <= 0 || signo >= _NSIG)
+ if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
r = bus_verify_polkit_async(
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index e088225beb..a8b1d5943d 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -513,7 +513,7 @@ static int session_start_scope(Session *s) {
if (!scope)
return log_oom();
- description = strjoina("Session ", s->id, " of user ", s->user->name, NULL);
+ description = strjoina("Session ", s->id, " of user ", s->user->name);
r = manager_start_scope(
s->manager,
@@ -797,7 +797,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
/* Graphical sessions should really implement a real
* idle hint logic */
- if (s->display)
+ if (SESSION_TYPE_IS_GRAPHICAL(s->type))
goto dont_know;
/* For sessions with an explicitly configured tty, let's check
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index fd98c7beca..b73f9ea69e 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -25,6 +25,7 @@
#include "formats-util.h"
#include "logind-user.h"
#include "logind.h"
+#include "signal-util.h"
#include "strv.h"
#include "user-util.h"
@@ -222,7 +223,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
if (r < 0)
return r;
- if (signo <= 0 || signo >= _NSIG)
+ if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
r = user_kill(u, signo);
diff --git a/src/login/logind.c b/src/login/logind.c
index d5f6757bd3..a48e2fc61e 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -41,17 +41,7 @@
static void manager_free(Manager *m);
-static Manager *manager_new(void) {
- Manager *m;
- int r;
-
- m = new0(Manager, 1);
- if (!m)
- return NULL;
-
- m->console_active_fd = -1;
- m->reserve_vt_fd = -1;
-
+static void manager_reset_config(Manager *m) {
m->n_autovts = 6;
m->reserve_vt = 6;
m->remove_ipc = true;
@@ -61,16 +51,38 @@ static Manager *manager_new(void) {
m->handle_hibernate_key = HANDLE_HIBERNATE;
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_docked = HANDLE_IGNORE;
+ m->power_key_ignore_inhibited = false;
+ m->suspend_key_ignore_inhibited = false;
+ m->hibernate_key_ignore_inhibited = false;
m->lid_switch_ignore_inhibited = true;
+
m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
- m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
m->user_tasks_max = UINT64_C(12288);
+ m->kill_user_processes = KILL_USER_PROCESSES;
+
+ m->kill_only_users = strv_free(m->kill_only_users);
+ m->kill_exclude_users = strv_free(m->kill_exclude_users);
+}
+
+static Manager *manager_new(void) {
+ Manager *m;
+ int r;
+
+ m = new0(Manager, 1);
+ if (!m)
+ return NULL;
+
+ m->console_active_fd = -1;
+ m->reserve_vt_fd = -1;
+
+ m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
+
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
m->sessions = hashmap_new(&string_hash_ops);
@@ -84,10 +96,6 @@ static Manager *manager_new(void) {
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
goto fail;
- m->kill_exclude_users = strv_new("root", NULL);
- if (!m->kill_exclude_users)
- goto fail;
-
m->udev = udev_new();
if (!m->udev)
goto fail;
@@ -98,6 +106,8 @@ static Manager *manager_new(void) {
sd_event_set_watchdog(m->event, true);
+ manager_reset_config(m);
+
return m;
fail:
@@ -986,6 +996,30 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
return 0;
}
+static int manager_parse_config_file(Manager *m) {
+ assert(m);
+
+ return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ CONF_PATHS_NULSTR("systemd/logind.conf.d"),
+ "Login\0",
+ config_item_perf_lookup, logind_gperf_lookup,
+ false, m);
+}
+
+static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+ int r;
+
+ manager_reset_config(m);
+ r = manager_parse_config_file(m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse config file, using defaults: %m");
+ else
+ log_info("Config file reloaded.");
+
+ return 0;
+}
+
static int manager_startup(Manager *m) {
int r;
Seat *seat;
@@ -997,6 +1031,12 @@ static int manager_startup(Manager *m) {
assert(m);
+ assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGHUP, -1) >= 0);
+
+ r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register SIGHUP handler: %m");
+
/* Connect to console */
r = manager_connect_console(m);
if (r < 0)
@@ -1099,16 +1139,6 @@ static int manager_run(Manager *m) {
}
}
-static int manager_parse_config_file(Manager *m) {
- assert(m);
-
- return config_parse_many(PKGSYSCONFDIR "/logind.conf",
- CONF_PATHS_NULSTR("systemd/logind.conf.d"),
- "Login\0",
- config_item_perf_lookup, logind_gperf_lookup,
- false, m);
-}
-
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r;
diff --git a/src/login/logind.conf b/src/login/logind.conf.in
index 6095e482ac..3c96def45d 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf.in
@@ -14,7 +14,7 @@
[Login]
#NAutoVTs=6
#ReserveVT=6
-#KillUserProcesses=no
+#KillUserProcesses=@KILL_USER_PROCESSES@
#KillOnlyUsers=
#KillExcludeUsers=root
#InhibitDelayMaxSec=5
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 23326bb79f..1fa6441629 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -111,6 +111,14 @@
</defaults>
</action>
+ <action id="org.freedesktop.login1.set-self-linger">
+ <_description>Allow non-logged-in user to run programs</_description>
+ <_message>Explicit request is required to run programs as a non-logged-in user.</_message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.login1.set-user-linger">
<_description>Allow non-logged-in users to run programs</_description>
<_message>Authentication is required to run programs as a non-logged-in user.</_message>
diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4
index 7933508f2b..f188a8e548 100644
--- a/src/login/systemd-user.m4
+++ b/src/login/systemd-user.m4
@@ -8,4 +8,5 @@ m4_ifdef(`HAVE_SELINUX',
session required pam_selinux.so close
session required pam_selinux.so nottys open
)m4_dnl
+session required pam_loginuid.so
session include system-auth
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 73f5112c4d..0eed9b81bb 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -20,9 +20,11 @@
#include "alloc-util.h"
#include "bus-label.h"
#include "bus-util.h"
+#include "fd-util.h"
#include "image-dbus.h"
#include "io-util.h"
#include "machine-image.h"
+#include "process-util.h"
#include "strv.h"
#include "user-util.h"
@@ -33,13 +35,18 @@ int bus_image_method_remove(
void *userdata,
sd_bus_error *error) {
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
Image *image = userdata;
Manager *m = image->userdata;
+ pid_t child;
int r;
assert(message);
assert(image);
+ if (m->n_operations >= OPERATIONS_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
@@ -54,11 +61,35 @@ int bus_image_method_remove(
if (r == 0)
return 1; /* Will call us back */
- r = image_remove(image);
- if (r < 0)
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ if (child == 0) {
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ r = image_remove(image);
+ if (r < 0) {
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ r = operation_new(m, NULL, child, message, errno_pipe_fd[0]);
+ if (r < 0) {
+ (void) sigkill_wait(child);
return r;
+ }
- return sd_bus_reply_method_return(message, NULL);
+ errno_pipe_fd[0] = -1;
+
+ return 1;
}
int bus_image_method_rename(
@@ -107,13 +138,19 @@ int bus_image_method_clone(
void *userdata,
sd_bus_error *error) {
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
Image *image = userdata;
Manager *m = image->userdata;
const char *new_name;
int r, read_only;
+ pid_t child;
assert(message);
assert(image);
+ assert(m);
+
+ if (m->n_operations >= OPERATIONS_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "sb", &new_name, &read_only);
if (r < 0)
@@ -136,11 +173,35 @@ int bus_image_method_clone(
if (r == 0)
return 1; /* Will call us back */
- r = image_clone(image, new_name, read_only);
- if (r < 0)
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ if (child == 0) {
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ r = image_clone(image, new_name, read_only);
+ if (r < 0) {
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ r = operation_new(m, NULL, child, message, errno_pipe_fd[0]);
+ if (r < 0) {
+ (void) sigkill_wait(child);
return r;
+ }
- return sd_bus_reply_method_return(message, NULL);
+ errno_pipe_fd[0] = -1;
+
+ return 1;
}
int bus_image_method_mark_read_only(
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index c5bbf2fbde..7b9aa66d63 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -46,6 +46,7 @@
#include "mkdir.h"
#include "path-util.h"
#include "process-util.h"
+#include "signal-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "user-util.h"
@@ -166,7 +167,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
}
- if (signo <= 0 || signo >= _NSIG)
+ if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
r = bus_verify_polkit_async(
@@ -729,7 +730,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
return r;
/* Name and mode */
- unit = strjoina("container-shell@", p, ".service", NULL);
+ unit = strjoina("container-shell@", p, ".service");
r = sd_bus_message_append(tm, "ss", unit, "fail");
if (r < 0)
return r;
@@ -1084,52 +1085,11 @@ finish:
return r;
}
-static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- MachineOperation *o = userdata;
- int r;
-
- assert(o);
- assert(si);
-
- o->pid = 0;
-
- if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
- goto fail;
- }
-
- if (si->si_status != EXIT_SUCCESS) {
- if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
- r = sd_bus_error_set_errnof(&error, r, "%m");
- else
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
-
- goto fail;
- }
-
- r = sd_bus_reply_method_return(o->message, NULL);
- if (r < 0)
- log_error_errno(r, "Failed to reply to message: %m");
-
- machine_operation_unref(o);
- return 0;
-
-fail:
- r = sd_bus_reply_method_error(o->message, &error);
- if (r < 0)
- log_error_errno(r, "Failed to reply to message: %m");
-
- machine_operation_unref(o);
- return 0;
-}
-
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
_cleanup_close_ int hostfd = -1;
Machine *m = userdata;
- MachineOperation *o;
bool copy_from;
pid_t child;
char *t;
@@ -1138,7 +1098,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
assert(message);
assert(m);
- if (m->n_operations >= MACHINE_OPERATIONS_MAX)
+ if (m->manager->n_operations >= OPERATIONS_MAX)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
if (m->class != MACHINE_CONTAINER)
@@ -1248,29 +1208,107 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
- /* Copying might take a while, hence install a watch the
- * child, and return */
+ /* Copying might take a while, hence install a watch on the child, and return */
- o = new0(MachineOperation, 1);
- if (!o)
- return log_oom();
-
- o->pid = child;
- o->message = sd_bus_message_ref(message);
- o->errno_fd = errno_pipe_fd[0];
+ r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]);
+ if (r < 0) {
+ (void) sigkill_wait(child);
+ return r;
+ }
errno_pipe_fd[0] = -1;
- r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o);
- if (r < 0) {
- machine_operation_unref(o);
- return log_oom();
+ return 1;
+}
+
+int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_close_ int fd = -1;
+ Machine *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ break;
+
+ case MACHINE_CONTAINER: {
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ siginfo_t si;
+ pid_t child;
+
+ r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_close_ int dfd = -1;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0)
+ _exit(EXIT_FAILURE);
+
+ r = send_one_fd(pair[1], dfd, 0);
+ dfd = safe_close(dfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+
+ fd = receive_one_fd(pair[0], MSG_DONTWAIT);
+ if (fd < 0)
+ return fd;
+
+ break;
}
- LIST_PREPEND(operations, m->operations, o);
- m->n_operations++;
- o->machine = m;
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
+ }
- return 1;
+ return sd_bus_reply_method_return(message, "h", fd);
}
const sd_bus_vtable machine_vtable[] = {
@@ -1296,6 +1334,7 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index 3a8162b171..241b23c7ec 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -38,6 +38,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error);
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 7d4270a8ff..c1fae57084 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -89,7 +89,7 @@ void machine_free(Machine *m) {
assert(m);
while (m->operations)
- machine_operation_unref(m->operations);
+ operation_free(m->operations);
if (m->in_gc_queue)
LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
@@ -596,28 +596,6 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {
}
}
-MachineOperation *machine_operation_unref(MachineOperation *o) {
- if (!o)
- return NULL;
-
- sd_event_source_unref(o->event_source);
-
- safe_close(o->errno_fd);
-
- if (o->pid > 1)
- (void) kill(o->pid, SIGKILL);
-
- sd_bus_message_unref(o->message);
-
- if (o->machine) {
- LIST_REMOVE(operations, o->machine->operations, o);
- o->machine->n_operations--;
- }
-
- free(o);
- return NULL;
-}
-
void machine_release_unit(Machine *m) {
assert(m);
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 1d8cc5911a..e5d75361a9 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -20,11 +20,11 @@
***/
typedef struct Machine Machine;
-typedef struct MachineOperation MachineOperation;
typedef enum KillWho KillWho;
#include "list.h"
#include "machined.h"
+#include "operation.h"
typedef enum MachineState {
MACHINE_OPENING, /* Machine is being registered */
@@ -49,17 +49,6 @@ enum KillWho {
_KILL_WHO_INVALID = -1
};
-#define MACHINE_OPERATIONS_MAX 64
-
-struct MachineOperation {
- Machine *machine;
- pid_t pid;
- sd_bus_message *message;
- int errno_fd;
- sd_event_source *event_source;
- LIST_FIELDS(MachineOperation, operations);
-};
-
struct Machine {
Manager *manager;
@@ -88,10 +77,9 @@ struct Machine {
int *netif;
unsigned n_netif;
- LIST_FIELDS(Machine, gc_queue);
+ LIST_HEAD(Operation, operations);
- MachineOperation *operations;
- unsigned n_operations;
+ LIST_FIELDS(Machine, gc_queue);
};
Machine* machine_new(Manager *manager, MachineClass class, const char *name);
@@ -109,8 +97,6 @@ void machine_release_unit(Machine *m);
MachineState machine_get_state(Machine *u);
-MachineOperation *machine_operation_unref(MachineOperation *o);
-
const char* machine_class_to_string(MachineClass t) _const_;
MachineClass machine_class_from_string(const char *s) _pure_;
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index e49c90fd1b..1165ab5afa 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -33,6 +33,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -61,6 +62,7 @@
static char **arg_property = NULL;
static bool arg_all = false;
+static bool arg_value = false;
static bool arg_full = false;
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -129,15 +131,14 @@ static int list_machines(int argc, char *argv[], void *userdata) {
pager_open(arg_no_pager, false);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "ListMachines",
- &error,
- &reply,
- NULL);
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "ListMachines",
+ &error,
+ &reply,
+ NULL);
if (r < 0) {
log_error("Could not get machines: %s", bus_error_message(&error, -r));
return r;
@@ -232,15 +233,14 @@ static int list_images(int argc, char *argv[], void *userdata) {
pager_open(arg_no_pager, false);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "ListImages",
- &error,
- &reply,
- "");
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "ListImages",
+ &error,
+ &reply,
+ "");
if (r < 0) {
log_error("Could not get images: %s", bus_error_message(&error, -r));
return r;
@@ -332,8 +332,8 @@ static int list_images(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
int r;
@@ -342,9 +342,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
assert(bus);
assert(unit);
- if (arg_transport == BUS_TRANSPORT_REMOTE)
- return 0;
-
path = unit_dbus_path_from_name(unit);
if (!path)
return log_oom();
@@ -358,16 +355,14 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
&error,
&reply,
"s");
- if (r < 0) {
- log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &cgroup);
if (r < 0)
return bus_log_parse_error(r);
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ if (isempty(cgroup))
return 0;
c = columns();
@@ -376,7 +371,21 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
else
c = 0;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
+ r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
+ if (r == -EBADR) {
+
+ if (arg_transport == BUS_TRANSPORT_REMOTE)
+ return 0;
+
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
+
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ return 0;
+
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
+
return 0;
}
@@ -680,7 +689,7 @@ static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line
*new_line = true;
- r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
+ r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
if (r < 0)
log_error_errno(r, "Could not get properties: %m");
@@ -713,15 +722,14 @@ static int show_machine(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
const char *path = NULL;
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "GetMachine",
- &error,
- &reply,
- "s", argv[i]);
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetMachine",
+ &error,
+ &reply,
+ "s", argv[i]);
if (r < 0) {
log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
return r;
@@ -929,7 +937,7 @@ static int show_image_properties(sd_bus *bus, const char *path, bool *new_line)
*new_line = true;
- r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
+ r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all);
if (r < 0)
log_error_errno(r, "Could not get properties: %m");
@@ -1068,6 +1076,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *abs_host_path = NULL;
char *dest, *host_path, *container_path;
sd_bus *bus = userdata;
@@ -1091,19 +1100,28 @@ static int copy_files(int argc, char *argv[], void *userdata) {
host_path = abs_host_path;
}
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- copy_from ? "CopyFromMachine" : "CopyToMachine",
- &error,
- NULL,
+ copy_from ? "CopyFromMachine" : "CopyToMachine");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
"sss",
argv[1],
copy_from ? container_path : host_path,
copy_from ? host_path : container_path);
if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
return 0;
@@ -1385,7 +1403,6 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
}
static int remove_image(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1394,19 +1411,27 @@ static int remove_image(int argc, char *argv[], void *userdata) {
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
- r = sd_bus_call_method(
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "RemoveImage",
- &error,
- NULL,
- "s", argv[i]);
- if (r < 0) {
- log_error("Could not remove image: %s", bus_error_message(&error, -r));
- return r;
- }
+ "RemoveImage");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", argv[i]);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
}
return 0;
@@ -1438,24 +1463,30 @@ static int rename_image(int argc, char *argv[], void *userdata) {
static int clone_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
sd_bus *bus = userdata;
int r;
polkit_agent_open_if_enabled();
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "CloneImage",
- &error,
- NULL,
- "ssb", argv[1], argv[2], arg_read_only);
- if (r < 0) {
- log_error("Could not clone image: %s", bus_error_message(&error, -r));
- return r;
- }
+ "CloneImage");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
return 0;
}
@@ -2183,15 +2214,14 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
pager_open(arg_no_pager, false);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.import1",
- "/org/freedesktop/import1",
- "org.freedesktop.import1.Manager",
- "ListTransfers",
- &error,
- &reply,
- NULL);
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ListTransfers",
+ &error,
+ &reply,
+ NULL);
if (r < 0) {
log_error("Could not get transfers: %s", bus_error_message(&error, -r));
return r;
@@ -2341,6 +2371,50 @@ static int set_limit(int argc, char *argv[], void *userdata) {
return 0;
}
+static int clean_images(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ uint64_t usage, total = 0;
+ char fb[FORMAT_BYTES_MAX];
+ sd_bus *bus = userdata;
+ const char *name;
+ unsigned c = 0;
+ int r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "CleanPool",
+ &error,
+ &reply,
+ "s", arg_all ? "all" : "hidden");
+ if (r < 0)
+ return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_enter_container(reply, 'a', "(st)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
+ log_info("Removed image '%s'. Freed exclusive disk space: %s",
+ name, format_bytes(fb, sizeof(fb), usage));
+
+ total += usage;
+ c++;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ log_info("Removed %u images in total. Total freed exclusive disk space %s.",
+ c, format_bytes(fb, sizeof(fb), total));
+
+ return 0;
+}
+
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -2356,11 +2430,12 @@ static int help(int argc, char *argv[], void *userdata) {
" -p --property=NAME Show only properties by this name\n"
" -q --quiet Suppress output\n"
" -a --all Show all properties, including empty ones\n"
+ " --value When showing properties, only print the value\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
- " --setenv=VAR=VALUE Add an environment variable for shell\n"
+ " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
@@ -2397,7 +2472,8 @@ static int help(int argc, char *argv[], void *userdata) {
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
" remove NAME... Remove an image\n"
- " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
+ " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
+ " clean Remove hidden (or all) images\n\n"
"Image Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
@@ -2418,6 +2494,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
+ ARG_VALUE,
ARG_KILL_WHO,
ARG_READ_ONLY,
ARG_MKDIR,
@@ -2426,7 +2503,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORCE,
ARG_FORMAT,
ARG_UID,
- ARG_SETENV,
};
static const struct option options[] = {
@@ -2434,6 +2510,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "property", required_argument, NULL, 'p' },
{ "all", no_argument, NULL, 'a' },
+ { "value", no_argument, NULL, ARG_VALUE },
{ "full", no_argument, NULL, 'l' },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
@@ -2451,16 +2528,38 @@ static int parse_argv(int argc, char *argv[]) {
{ "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT },
{ "uid", required_argument, NULL, ARG_UID },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{}
};
+ bool reorder = false;
int c, r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
+ for (;;) {
+ const char * const option_string = "+hp:als:H:M:qn:o:";
+
+ c = getopt_long(argc, argv, option_string + reorder, options, NULL);
+ if (c < 0) {
+ /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
+ * for switches after the main verb. However, for "shell" we really don't want that, since we
+ * want that switches passed after that are passed to the program to execute, and not processed
+ * by us. To make this possible, we'll first invoke getopt_long() with reordering disabled
+ * (i.e. with the "+" prefix in the option string), and as soon as we hit the end (i.e. the
+ * verb) we check if that's "shell". If it is, we exit the loop, since we don't want any
+ * further options processed. However, if it is anything else, we process the same argument
+ * again, but this time allow reordering. */
+
+ if (!reorder && optind < argc && !streq(argv[optind], "shell")) {
+ reorder = true;
+ optind--;
+ continue;
+ }
+
+ break;
+ }
switch (c) {
@@ -2485,6 +2584,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
+ case ARG_VALUE:
+ arg_value = true;
+ break;
+
case 'l':
arg_full = true;
break;
@@ -2575,7 +2678,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_uid = optarg;
break;
- case ARG_SETENV:
+ case 'E':
if (!env_assignment_is_valid(optarg)) {
log_error("Environment assignment invalid: %s", optarg);
return -EINVAL;
@@ -2592,6 +2695,7 @@ static int parse_argv(int argc, char *argv[]) {
default:
assert_not_reached("Unhandled option");
}
+ }
return 1;
}
@@ -2631,6 +2735,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
{ "set-limit", 2, 3, 0, set_limit },
+ { "clean", VERB_ANY, 1, 0, clean_images },
{}
};
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 20894433e7..31efa3695b 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -706,6 +706,26 @@ static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_e
return bus_machine_method_copy(message, machine, error);
}
+static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_open_root_directory(message, machine, error);
+}
+
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
@@ -802,6 +822,93 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
return bus_image_method_mark_read_only(message, i, error);
}
+static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ enum {
+ REMOVE_ALL,
+ REMOVE_HIDDEN,
+ } mode;
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ Manager *m = userdata;
+ Image *image;
+ const char *mm;
+ Iterator i;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "s", &mm);
+ if (r < 0)
+ return r;
+
+ if (streq(mm, "all"))
+ mode = REMOVE_ALL;
+ else if (streq(mm, "hidden"))
+ mode = REMOVE_HIDDEN;
+ else
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ images = hashmap_new(&string_hash_ops);
+ if (!images)
+ return -ENOMEM;
+
+ r = image_discover(images);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(image, images, i) {
+
+ /* We can't remove vendor images (i.e. those in /usr) */
+ if (IMAGE_IS_VENDOR(image))
+ continue;
+
+ if (IMAGE_IS_HOST(image))
+ continue;
+
+ if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image))
+ continue;
+
+ r = image_remove(image);
+ if (r == -EBUSY) /* keep images that are currently being used. */
+ continue;
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name);
+
+ r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
uint64_t limit;
@@ -1138,12 +1245,14 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/machine/machined.c b/src/machine/machined.c
index f2c1966a6b..f7ceb5e603 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -70,6 +70,11 @@ void manager_free(Manager *m) {
assert(m);
+ while (m->operations)
+ operation_free(m->operations);
+
+ assert(m->n_operations == 0);
+
while ((machine = hashmap_first(m->machines)))
machine_free(machine);
@@ -336,6 +341,9 @@ int manager_startup(Manager *m) {
static bool check_idle(void *userdata) {
Manager *m = userdata;
+ if (m->operations)
+ return false;
+
manager_gc(m, true);
return hashmap_isempty(m->machines);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index e7d7dfdceb..7b9b148044 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -32,6 +32,7 @@ typedef struct Manager Manager;
#include "image-dbus.h"
#include "machine-dbus.h"
#include "machine.h"
+#include "operation.h"
struct Manager {
sd_event *event;
@@ -49,6 +50,9 @@ struct Manager {
LIST_HEAD(Machine, machine_gc_queue);
Machine *host_machine;
+
+ LIST_HEAD(Operation, operations);
+ unsigned n_operations;
};
Manager *manager_new(void);
diff --git a/src/machine/operation.c b/src/machine/operation.c
new file mode 100644
index 0000000000..e6ddc41a55
--- /dev/null
+++ b/src/machine/operation.c
@@ -0,0 +1,131 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 "alloc-util.h"
+#include "fd-util.h"
+#include "operation.h"
+#include "process-util.h"
+
+static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Operation *o = userdata;
+ int r;
+
+ assert(o);
+ assert(si);
+
+ log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i",
+ o->pid,
+ sigchld_code_to_string(si->si_code), si->si_status);
+
+ o->pid = 0;
+
+ if (si->si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ goto fail;
+ }
+
+ if (si->si_status != EXIT_SUCCESS) {
+ if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
+ r = sd_bus_error_set_errnof(&error, r, "%m");
+ else
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+
+ goto fail;
+ }
+
+ r = sd_bus_reply_method_return(o->message, NULL);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ operation_free(o);
+ return 0;
+
+fail:
+ r = sd_bus_reply_method_error(o->message, &error);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ operation_free(o);
+ return 0;
+}
+
+int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) {
+ Operation *o;
+ int r;
+
+ assert(manager);
+ assert(child > 1);
+ assert(message);
+ assert(errno_fd >= 0);
+
+ o = new0(Operation, 1);
+ if (!o)
+ return -ENOMEM;
+
+ r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o);
+ if (r < 0) {
+ free(o);
+ return r;
+ }
+
+ o->pid = child;
+ o->message = sd_bus_message_ref(message);
+ o->errno_fd = errno_fd;
+
+ LIST_PREPEND(operations, manager->operations, o);
+ manager->n_operations++;
+ o->manager = manager;
+
+ if (machine) {
+ LIST_PREPEND(operations_by_machine, machine->operations, o);
+ o->machine = machine;
+ }
+
+ log_debug("Started new operation " PID_FMT ".", child);
+
+ /* At this point we took ownership of both the child and the errno file descriptor! */
+
+ return 0;
+}
+
+Operation *operation_free(Operation *o) {
+ if (!o)
+ return NULL;
+
+ sd_event_source_unref(o->event_source);
+
+ safe_close(o->errno_fd);
+
+ if (o->pid > 1)
+ (void) sigkill_wait(o->pid);
+
+ sd_bus_message_unref(o->message);
+
+ if (o->manager) {
+ LIST_REMOVE(operations, o->manager->operations, o);
+ o->manager->n_operations--;
+ }
+
+ if (o->machine)
+ LIST_REMOVE(operations_by_machine, o->machine->operations, o);
+
+ free(o);
+ return NULL;
+}
diff --git a/src/machine/operation.h b/src/machine/operation.h
new file mode 100644
index 0000000000..7ca47bc3af
--- /dev/null
+++ b/src/machine/operation.h
@@ -0,0 +1,47 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 <sys/types.h>
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+#include "list.h"
+
+typedef struct Operation Operation;
+
+#include "machined.h"
+
+#define OPERATIONS_MAX 64
+
+struct Operation {
+ Manager *manager;
+ Machine *machine;
+ pid_t pid;
+ sd_bus_message *message;
+ int errno_fd;
+ sd_event_source *event_source;
+ LIST_FIELDS(Operation, operations);
+ LIST_FIELDS(Operation, operations_by_machine);
+};
+
+int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd);
+Operation *operation_free(Operation *o);
diff --git a/src/network/.gitignore b/src/network/.gitignore
index 8858596489..aca55206b7 100644
--- a/src/network/.gitignore
+++ b/src/network/.gitignore
@@ -1,2 +1,3 @@
/networkd-network-gperf.c
/networkd-netdev-gperf.c
+/networkd-gperf.c
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index d9d487d805..ebc6c9eb9e 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -148,8 +148,12 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union
for (;;) {
if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
_cleanup_free_ char *s = NULL;
+ int r;
+
+ r = in_addr_to_string(p->family, &u, &s);
+ if (r < 0)
+ return r;
- in_addr_to_string(p->family, &u, &s);
log_debug("Found range %s/%u", strna(s), prefixlen);
*found = u;
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 7f9a7268cc..429319da6b 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -67,16 +67,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
if (r < 0)
return r;
- address->network = network;
-
- LIST_APPEND(addresses, network->static_addresses, address);
-
if (section) {
address->section = section;
hashmap_put(network->addresses_by_section,
UINT_TO_PTR(address->section), address);
}
+ address->network = network;
+ LIST_APPEND(addresses, network->static_addresses, address);
+
*ret = address;
address = NULL;
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
new file mode 100644
index 0000000000..70f0121d6d
--- /dev/null
+++ b/src/network/networkd-conf.c
@@ -0,0 +1,171 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.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 <ctype.h>
+
+#include "conf-parser.h"
+#include "def.h"
+#include "dhcp-identifier.h"
+#include "hexdecoct.h"
+#include "networkd-conf.h"
+#include "string-table.h"
+
+int manager_parse_config_file(Manager *m) {
+ assert(m);
+
+ return config_parse_many(PKGSYSCONFDIR "/networkd.conf",
+ CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
+ "DHCP\0",
+ config_item_perf_lookup, networkd_gperf_lookup,
+ false, m);
+}
+
+static const char* const duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_RAW] = "raw",
+ [DUID_TYPE_LLT] = "link-layer-time",
+ [DUID_TYPE_EN] = "vendor",
+ [DUID_TYPE_LL] = "link-layer",
+ [DUID_TYPE_UUID] = "uuid"
+};
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type");
+
+int config_parse_duid_rawdata(
+ 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 r;
+ char *cbyte;
+ const char *pduid = rvalue;
+ Manager *m = userdata;
+ Network *n = userdata;
+ DUIDType duidtype;
+ uint16_t dhcp_duid_type = 0;
+ uint8_t dhcp_duid[MAX_DUID_LEN];
+ size_t len, count = 0, duid_start_offset = 0, dhcp_duid_len = 0;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(userdata);
+
+ duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type : n->duid_type;
+
+ if (duidtype == _DUID_TYPE_INVALID)
+ duidtype = DUID_TYPE_RAW;
+
+ switch (duidtype) {
+
+ case DUID_TYPE_LLT:
+ /* RawData contains DUID-LLT link-layer address (offset 6) */
+ duid_start_offset = 6;
+ break;
+
+ case DUID_TYPE_EN:
+ /* RawData contains DUID-EN identifier (offset 4) */
+ duid_start_offset = 4;
+ break;
+
+ case DUID_TYPE_LL:
+ /* RawData contains DUID-LL link-layer address (offset 2) */
+ duid_start_offset = 2;
+ break;
+
+ case DUID_TYPE_UUID:
+ /* RawData specifies UUID (offset 0) - fall thru */
+
+ case DUID_TYPE_RAW:
+ /* First two bytes of RawData is DUID Type - fall thru */
+
+ default:
+ break;
+ }
+
+ if (duidtype != DUID_TYPE_RAW)
+ dhcp_duid_type = (uint16_t) duidtype;
+
+ /* RawData contains DUID in format " NN:NN:NN... " */
+ for (;;) {
+ int n1, n2;
+ uint32_t byte;
+
+ r = extract_first_word(&pduid, &cbyte, ":", 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+ if (duid_start_offset + dhcp_duid_len >= MAX_DUID_LEN) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
+ return 0;
+ }
+
+ len = strlen(cbyte);
+ if (len != 1 && len != 2) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
+ }
+ n1 = unhexchar(cbyte[0]);
+ if (len == 2)
+ n2 = unhexchar(cbyte[1]);
+ else
+ n2 = 0;
+
+ if (n1 < 0 || n2 < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
+ }
+
+ byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
+
+ /* If DUID_TYPE_RAW, first two bytes hold DHCP DUID type code */
+ if (duidtype == DUID_TYPE_RAW && count < 2) {
+ dhcp_duid_type |= (byte << (8 * (1 - count)));
+ count++;
+ continue;
+ }
+
+ dhcp_duid[duid_start_offset + dhcp_duid_len] = byte;
+ dhcp_duid_len++;
+ }
+
+ if (ltype == DUID_CONFIG_SOURCE_GLOBAL) {
+ m->duid_type = duidtype;
+ m->dhcp_duid_type = dhcp_duid_type;
+ m->dhcp_duid_len = dhcp_duid_len;
+ memcpy(&m->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len);
+ } else {
+ /* DUID_CONFIG_SOURCE_NETWORK */
+ n->duid_type = duidtype;
+ n->dhcp_duid_type = dhcp_duid_type;
+ n->dhcp_duid_len = dhcp_duid_len;
+ memcpy(&n->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len);
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h
new file mode 100644
index 0000000000..671e656d7b
--- /dev/null
+++ b/src/network/networkd-conf.h
@@ -0,0 +1,34 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.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 "networkd.h"
+
+typedef enum DuidConfigSource {
+ DUID_CONFIG_SOURCE_GLOBAL = 0,
+ DUID_CONFIG_SOURCE_NETWORK,
+} DuidConfigSource;
+
+int manager_parse_config_file(Manager *m);
+
+const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length);
+
+int config_parse_duid_type(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_duid_rawdata(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/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 68998eabf2..c5b61abc9e 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -57,6 +57,10 @@ static int link_set_dhcp_routes(Link *link) {
assert(link);
assert(link->dhcp_lease);
+ assert(link->network);
+
+ if (!link->network->dhcp_use_routes)
+ return 0;
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
if (r < 0 && r != -ENODATA)
@@ -625,7 +629,21 @@ int dhcp4_configure(Link *link) {
switch (link->network->dhcp_client_identifier) {
case DHCP_CLIENT_ID_DUID:
- /* Library defaults to this. */
+ /* If configured, apply user specified DUID and/or IAID */
+ if (link->network->duid_type != _DUID_TYPE_INVALID)
+ r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+ link->network->iaid,
+ link->network->dhcp_duid_type,
+ link->network->dhcp_duid,
+ link->network->dhcp_duid_len);
+ else
+ r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+ link->network->iaid,
+ link->manager->dhcp_duid_type,
+ link->manager->dhcp_duid,
+ link->manager->dhcp_duid_len);
+ if (r < 0)
+ return r;
break;
case DHCP_CLIENT_ID_MAC:
r = sd_dhcp_client_set_client_id(link->dhcp_client,
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 5f7a005c36..d4b2fbfc57 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -230,6 +230,23 @@ int dhcp6_configure(Link *link) {
if (r < 0)
goto error;
+ r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
+ if (r < 0)
+ goto error;
+
+ if (link->network->duid_type != _DUID_TYPE_INVALID)
+ r = sd_dhcp6_client_set_duid(client,
+ link->network->dhcp_duid_type,
+ link->network->dhcp_duid,
+ link->network->dhcp_duid_len);
+ else
+ r = sd_dhcp6_client_set_duid(client,
+ link->manager->dhcp_duid_type,
+ link->manager->dhcp_duid,
+ link->manager->dhcp_duid_len);
+ if (r < 0)
+ goto error;
+
r = sd_dhcp6_client_set_index(client, link->ifindex);
if (r < 0)
goto error;
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
new file mode 100644
index 0000000000..afc71b4cb8
--- /dev/null
+++ b/src/network/networkd-gperf.gperf
@@ -0,0 +1,18 @@
+%{
+#include <stddef.h>
+#include "conf-parser.h"
+#include "networkd-conf.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name networkd_gperf_hash
+%define lookup-function-name networkd_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid_type)
+DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_GLOBAL, offsetof(Manager, dhcp_duid)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index ae3bac217b..5fc513bfda 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -42,6 +42,9 @@
static bool link_dhcp6_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -90,6 +93,9 @@ static bool link_ipv4ll_enabled(Link *link) {
static bool link_ipv6ll_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -99,6 +105,15 @@ static bool link_ipv6ll_enabled(Link *link) {
return link->network->link_local & ADDRESS_FAMILY_IPV6;
}
+static bool link_ipv6_enabled(Link *link) {
+ assert(link);
+
+ if (!socket_ipv6_is_supported())
+ return false;
+
+ return link_dhcp6_enabled(link) || link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network);
+}
+
static bool link_lldp_rx_enabled(Link *link) {
assert(link);
@@ -165,9 +180,27 @@ static bool link_ipv6_forward_enabled(Link *link) {
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
}
+static bool link_proxy_arp_enabled(Link *link) {
+ assert(link);
+
+ if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ if (!link->network)
+ return false;
+
+ if (link->network->proxy_arp < 0)
+ return false;
+
+ return true;
+}
+
static bool link_ipv6_accept_ra_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -190,6 +223,7 @@ static bool link_ipv6_accept_ra_enabled(Link *link) {
}
static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
+ assert(link);
if (!socket_ipv6_is_supported())
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
@@ -203,6 +237,31 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
return link->network->ipv6_privacy_extensions;
}
+static int link_enable_ipv6(Link *link) {
+ const char *p = NULL;
+ bool disabled;
+ int r;
+
+ if (link->flags & IFF_LOOPBACK)
+ return 0;
+
+ disabled = !link_ipv6_enabled(link);
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6");
+
+ r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname);
+ else {
+ if (disabled)
+ log_link_info(link, "IPv6 disabled for interface: %m");
+ else
+ log_link_info(link, "IPv6 enabled for interface: %m");
+ }
+
+ return 0;
+}
+
void link_update_operstate(Link *link) {
LinkOperationalState operstate;
assert(link);
@@ -250,7 +309,6 @@ void link_update_operstate(Link *link) {
link->operstate = operstate;
link_send_changed(link, "OperationalState", NULL);
link_dirty(link);
- manager_dirty(link->manager);
}
}
@@ -518,8 +576,6 @@ static void link_set_state(Link *link, LinkState state) {
link->state = state;
link_send_changed(link, "AdministrativeState", NULL);
-
- return;
}
static void link_enter_unmanaged(Link *link) {
@@ -1039,6 +1095,22 @@ static int link_set_bridge_fdb(Link *const link) {
return r;
}
+static int link_set_proxy_arp(Link *const link) {
+ const char *p = NULL;
+ int r;
+
+ if (!link_proxy_arp_enabled(link))
+ return 0;
+
+ p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp");
+
+ r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m");
+
+ return 0;
+}
+
static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -1384,7 +1456,7 @@ static int link_acquire_ipv6_conf(Link *link) {
return 0;
}
-static int link_acquire_conf(Link *link) {
+static int link_acquire_ipv4_conf(Link *link) {
int r;
assert(link);
@@ -1412,6 +1484,24 @@ static int link_acquire_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
}
+ return 0;
+}
+
+static int link_acquire_conf(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = link_acquire_ipv4_conf(link);
+ if (r < 0)
+ return r;
+
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
+ r = link_acquire_ipv6_conf(link);
+ if (r < 0)
+ return r;
+ }
+
if (link_lldp_tx_enabled(link)) {
r = link_lldp_tx_start(link);
if (r < 0)
@@ -1479,7 +1569,21 @@ static int link_up(Link *link) {
return log_link_error_errno(link, r, "Could not set MAC address: %m");
}
+ /* If IPv6 not configured (no static IPv6 address and neither DHCPv6 nor IPv6LL is enabled)
+ for this interface then disable IPv6 else enable it. */
+ (void) link_enable_ipv6(link);
+
if (link->network->mtu) {
+ /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
+ on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */
+ if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) {
+
+ log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as "
+ "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m");
+
+ link->network->mtu = IPV6_MIN_MTU;
+ }
+
r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu);
if (r < 0)
return log_link_error_errno(link, r, "Could not set MTU: %m");
@@ -1489,7 +1593,7 @@ static int link_up(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
- if (socket_ipv6_is_supported()) {
+ if (link_ipv6_enabled(link)) {
/* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */
r = sd_netlink_message_open_container(req, AF_INET6);
if (r < 0)
@@ -2167,6 +2271,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = link_set_proxy_arp(link);
+ if (r < 0)
+ return r;
+
r = link_set_ipv4_forward(link);
if (r < 0)
return r;
@@ -2259,12 +2367,6 @@ static int link_configure(Link *link) {
r = link_acquire_conf(link);
if (r < 0)
return r;
-
- if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
- r = link_acquire_ipv6_conf(link);
- if (r < 0)
- return r;
- }
}
return link_enter_join_netdev(link);
@@ -2647,6 +2749,10 @@ static int link_carrier_gained(Link *link) {
link_enter_failed(link);
return r;
}
+
+ r = link_enter_set_addresses(link);
+ if (r < 0)
+ return r;
}
r = link_handle_bound_by_list(link);
@@ -2780,6 +2886,21 @@ int link_update(Link *link, sd_netlink_message *m) {
ARPHRD_ETHER);
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
+
+ if (link->network->duid_type != _DUID_TYPE_INVALID)
+ r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+ link->network->iaid,
+ link->network->dhcp_duid_type,
+ link->network->dhcp_duid,
+ link->network->dhcp_duid_len);
+ else
+ r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+ link->network->iaid,
+ link->manager->dhcp_duid_type,
+ link->manager->dhcp_duid,
+ link->manager->dhcp_duid_len);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m");
}
if (link->dhcp6_client) {
@@ -2789,6 +2910,24 @@ int link_update(Link *link, sd_netlink_message *m) {
ARPHRD_ETHER);
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
+
+ r = sd_dhcp6_client_set_iaid(link->dhcp6_client,
+ link->network->iaid);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
+
+ if (link->network->duid_type != _DUID_TYPE_INVALID)
+ r = sd_dhcp6_client_set_duid(link->dhcp6_client,
+ link->network->dhcp_duid_type,
+ link->network->dhcp_duid,
+ link->network->dhcp_duid_len);
+ else
+ r = sd_dhcp6_client_set_duid(link->dhcp6_client,
+ link->manager->dhcp_duid_type,
+ link->manager->dhcp_duid,
+ link->manager->dhcp_duid_len);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
}
}
}
@@ -3112,14 +3251,17 @@ void link_dirty(Link *link) {
assert(link);
+ /* mark manager dirty as link is dirty */
+ manager_dirty(link->manager);
+
r = set_ensure_allocated(&link->manager->dirty_links, NULL);
if (r < 0)
/* allocation errors are ignored */
return;
r = set_put(link->manager->dirty_links, link);
- if (r < 0)
- /* allocation errors are ignored */
+ if (r <= 0)
+ /* don't take another ref if the link was already dirty */
return;
link_ref(link);
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index 5af2a31ea7..6bde04bc32 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -30,6 +30,8 @@
#include "string-util.h"
#include "unaligned.h"
+#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+
/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
#define LLDP_TX_FAST_INIT 4U
@@ -127,7 +129,7 @@ static int lldp_make_packet(
h = (struct ether_header*) packet;
h->ether_type = htobe16(ETHERTYPE_LLDP);
- memcpy(h->ether_dhost, &(struct ether_addr) { SD_LLDP_MULTICAST_ADDR }, ETH_ALEN);
+ memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN);
memcpy(h->ether_shost, hwaddr, ETH_ALEN);
p = (uint8_t*) packet + sizeof(struct ether_header);
@@ -199,7 +201,7 @@ static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size)
.ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
- .ll.sll_addr = SD_LLDP_MULTICAST_ADDR,
+ .ll.sll_addr = LLDP_MULTICAST_ADDR,
};
_cleanup_close_ int fd = -1;
@@ -239,9 +241,8 @@ static int link_send_lldp(Link *link) {
(void) gethostname_strict(&hostname);
(void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL);
+ assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
- if (ttl > (usec_t) UINT16_MAX)
- ttl = (usec_t) UINT16_MAX;
caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ?
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index b8cb7f875d..d355aaa19c 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -1037,6 +1037,8 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
+ m->duid_type = _DUID_TYPE_INVALID;
+
*ret = m;
m = NULL;
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index cdcd08f057..3f91b2eaea 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -89,6 +89,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m");
}
+ if (b->mcast_querier >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m");
+ }
+
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
@@ -106,8 +112,19 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
return r;
}
+static void bridge_init(NetDev *n) {
+ Bridge *b;
+
+ b = BRIDGE(n);
+
+ assert(b);
+
+ b->mcast_querier = -1;
+}
+
const NetDevVTable bridge_vtable = {
.object_size = sizeof(Bridge),
+ .init = bridge_init,
.sections = "Match\0NetDev\0Bridge\0",
.post_create = netdev_bridge_post_create,
.create_type = NETDEV_CREATE_MASTER,
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h
index 27f26f7870..3f6f1d0502 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/networkd-netdev-bridge.h
@@ -26,6 +26,8 @@ typedef struct Bridge Bridge;
struct Bridge {
NetDev meta;
+ int mcast_querier;
+
usec_t forward_delay;
usec_t hello_time;
usec_t max_age;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 8f506af092..15a787a9e3 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -92,3 +92,4 @@ Bond.LearnPacketIntervalSec, config_parse_sec, 0,
Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
+Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index a5d1714293..654d6a0316 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -2,6 +2,7 @@
#include <stddef.h>
#include "conf-parser.h"
#include "networkd.h"
+#include "networkd-conf.h"
#include "network-internal.h"
%}
struct ConfigPerfItem;
@@ -26,6 +27,7 @@ Match.KernelCommandLine, config_parse_net_condition,
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)
+Link.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
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)
@@ -57,6 +59,7 @@ Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions,
Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit)
+Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
@@ -80,6 +83,8 @@ DHCP.Hostname, config_parse_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.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid_type)
+DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_NETWORK, offsetof(Network, dhcp_duid)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 491b9a3efa..2ebcdfa744 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -131,6 +131,8 @@ static int network_load_one(Manager *manager, const char *filename) {
network->ipv6_accept_ra = -1;
network->ipv6_dad_transmits = -1;
network->ipv6_hop_limit = -1;
+ network->duid_type = _DUID_TYPE_INVALID;
+ network->proxy_arp = -1;
r = config_parse(NULL, filename, file,
"Match\0"
@@ -396,6 +398,19 @@ int network_apply(Manager *manager, Network *network, Link *link) {
return 0;
}
+bool network_has_static_ipv6_addresses(Network *network) {
+ Address *address;
+
+ assert(network);
+
+ LIST_FOREACH(addresses, address, network->static_addresses) {
+ if (address->family == AF_INET6)
+ return true;
+ }
+
+ return false;
+}
+
int config_parse_netdev(const char *unit,
const char *filename,
unsigned line,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 4a13e2b574..15417f4828 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -24,6 +24,7 @@
typedef struct Network Network;
+#include "dhcp-identifier.h"
#include "networkd-address.h"
#include "networkd-fdb.h"
#include "networkd-netdev.h"
@@ -138,12 +139,20 @@ struct Network {
int ipv6_accept_ra;
int ipv6_dad_transmits;
int ipv6_hop_limit;
+ int proxy_arp;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
struct ether_addr *mac;
unsigned mtu;
+ uint32_t iaid;
+ /* Value of Type in [DUID] section */
+ DUIDType duid_type;
+ /* DUID type code - RFC 3315 */
+ uint16_t dhcp_duid_type;
+ size_t dhcp_duid_len;
+ uint8_t dhcp_duid[MAX_DUID_LEN];
LLDPMode lldp_mode; /* LLDP reception */
bool lldp_emit; /* LLDP transmission */
@@ -177,6 +186,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
int network_apply(Manager *manager, Network *network, Link *link);
+bool network_has_static_ipv6_addresses(Network *network);
+
int config_parse_netdev(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_domains(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_tunnel(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/network/networkd-route.c b/src/network/networkd-route.c
index e065a5a5a9..bda2707e6d 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -52,8 +52,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
int r;
if (section) {
- route = hashmap_get(network->routes_by_section,
- UINT_TO_PTR(section));
+ route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section));
if (route) {
*ret = route;
route = NULL;
@@ -67,16 +66,18 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
return r;
route->protocol = RTPROT_STATIC;
- route->network = network;
-
- LIST_PREPEND(routes, network->static_routes, route);
if (section) {
route->section = section;
- hashmap_put(network->routes_by_section,
- UINT_TO_PTR(route->section), route);
+
+ r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
+ if (r < 0)
+ return r;
}
+ route->network = network;
+ LIST_PREPEND(routes, network->static_routes, route);
+
*ret = route;
route = NULL;
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 3a2615e6fd..c8f81a2ca6 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -21,6 +21,7 @@
#include "capability-util.h"
#include "networkd.h"
+#include "networkd-conf.h"
#include "signal-util.h"
#include "user-util.h"
@@ -89,6 +90,10 @@ int main(int argc, char *argv[]) {
goto out;
}
+ r = manager_parse_config_file(m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse configuration file: %m");
+
r = manager_load_config(m);
if (r < 0) {
log_error_errno(r, "Could not load configuration files: %m");
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 6bdd8302a0..72a2438ac8 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -31,6 +31,7 @@
typedef struct Manager Manager;
+#include "dhcp-identifier.h"
#include "networkd-address-pool.h"
#include "networkd-link.h"
#include "networkd-network.h"
@@ -61,6 +62,13 @@ struct Manager {
LIST_HEAD(AddressPool, address_pools);
usec_t network_dirs_ts_usec;
+
+ /* Value of Type in [DUID] section */
+ DUIDType duid_type;
+ /* DUID type code - RFC 3315 */
+ uint16_t dhcp_duid_type;
+ size_t dhcp_duid_len;
+ uint8_t dhcp_duid[MAX_DUID_LEN];
};
extern const char* const network_dirs[];
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index ecbbe6c3c9..adbe09a5e1 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -9,7 +9,7 @@
int main(int argc, char **argv) {
test_table(bond_mode, NETDEV_BOND_MODE);
- /* test_table(link_state, LINK_STATE); -- not a reversible mapping */
+ /* test_table(link_state, LINK_STATE); — not a reversible mapping */
test_table(link_operstate, LINK_OPERSTATE);
test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN);
test_table(netdev_kind, NETDEV_KIND);
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 116655cdd2..34e1310e29 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -16,7 +16,7 @@ struct ConfigPerfItem;
%includes
%%
Exec.Boot, config_parse_boot, 0, 0
-Exec.ProcessTwo, config_parse_pid2, 0, 0,
+Exec.ProcessTwo, config_parse_pid2, 0, 0
Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
Exec.User, config_parse_string, 0, offsetof(Settings, user)
@@ -26,11 +26,13 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings,
Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
+Exec.PrivateUsers, config_parse_private_users, 0, 0
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
Files.Bind, config_parse_bind, 0, 0
Files.BindReadOnly, config_parse_bind, 1, 0
Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
+Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown)
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 64cb6b3ce3..8e2d2d543c 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -438,21 +438,22 @@ static int mount_bind(const char *dest, CustomMount *m) {
r = mkdir_parents_label(where, 0755);
if (r < 0)
return log_error_errno(r, "Failed to make parents of %s: %m", where);
+
+ /* Create the mount point. Any non-directory file can be
+ * mounted on any non-directory file (regular, fifo, socket,
+ * char, block).
+ */
+ if (S_ISDIR(source_st.st_mode))
+ r = mkdir_label(where, 0755);
+ else
+ r = touch(where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create mount point %s: %m", where);
+
} else {
return log_error_errno(errno, "Failed to stat %s: %m", where);
}
- /* Create the mount point. Any non-directory file can be
- * mounted on any non-directory file (regular, fifo, socket,
- * char, block).
- */
- if (S_ISDIR(source_st.st_mode))
- r = mkdir_label(where, 0755);
- else
- r = touch(where);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Failed to create mount point %s: %m", where);
-
if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0)
return log_error_errno(errno, "mount(%s) failed: %m", where);
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index 74a0ae865b..f2b7e4dd79 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -538,3 +538,50 @@ int veth_extra_parse(char ***l, const char *p) {
a = b = NULL;
return 0;
}
+
+static int remove_one_veth_link(sd_netlink *rtnl, const char *name) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ if (isempty(name))
+ return 0;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r == -ENODEV) /* Already gone */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to remove veth interface %s: %m", name);
+
+ return 1;
+}
+
+int remove_veth_links(const char *primary, char **pairs) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ char **a, **b;
+ int r;
+
+ /* In some cases the kernel might pin the veth links between host and container even after the namespace
+ * died. Hence, let's better remove them explicitly too. */
+
+ if (isempty(primary) && strv_isempty(pairs))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ remove_one_veth_link(rtnl, primary);
+
+ STRV_FOREACH_PAIR(a, b, pairs)
+ remove_one_veth_link(rtnl, *a);
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
index 9ab1606d1c..c5036ab470 100644
--- a/src/nspawn/nspawn-network.h
+++ b/src/nspawn/nspawn-network.h
@@ -34,3 +34,5 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
int move_network_interfaces(pid_t pid, char **ifaces);
int veth_extra_parse(char ***l, const char *p);
+
+int remove_veth_links(const char *primary, char **pairs);
diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c
new file mode 100644
index 0000000000..c7382d412d
--- /dev/null
+++ b/src/nspawn/nspawn-patch-uid.c
@@ -0,0 +1,469 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 <fcntl.h>
+#include <linux/magic.h>
+#ifdef HAVE_ACL
+#include <sys/acl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include "acl-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "missing.h"
+#include "nspawn-patch-uid.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+#ifdef HAVE_ACL
+
+static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ acl_t acl;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (name) {
+ _cleanup_close_ int child_fd = -1;
+
+ child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (child_fd < 0)
+ return -errno;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
+ acl = acl_get_file(procfs_path, type);
+ } else if (type == ACL_TYPE_ACCESS)
+ acl = acl_get_fd(fd);
+ else {
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ acl = acl_get_file(procfs_path, type);
+ }
+ if (!acl)
+ return -errno;
+
+ *ret = acl;
+ return 0;
+}
+
+static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ int r;
+
+ assert(fd >= 0);
+ assert(acl);
+
+ if (name) {
+ _cleanup_close_ int child_fd = -1;
+
+ child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (child_fd < 0)
+ return -errno;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
+ r = acl_set_file(procfs_path, type, acl);
+ } else if (type == ACL_TYPE_ACCESS)
+ r = acl_set_fd(fd, acl);
+ else {
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ r = acl_set_file(procfs_path, type, acl);
+ }
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) {
+ _cleanup_(acl_freep) acl_t copy = NULL;
+ acl_entry_t i;
+ int r;
+
+ assert(acl);
+ assert(ret);
+
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ while (r > 0) {
+ uid_t *old_uid, new_uid;
+ bool modify = false;
+ acl_tag_t tag;
+
+ if (acl_get_tag_type(i, &tag) < 0)
+ return -errno;
+
+ if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
+
+ /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that
+ * this is actually OK */
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+ old_uid = acl_get_qualifier(i);
+ if (!old_uid)
+ return -errno;
+
+ new_uid = shift | (*old_uid & UINT32_C(0xFFFF));
+ if (!uid_is_valid(new_uid))
+ return -EINVAL;
+
+ modify = new_uid != *old_uid;
+ if (modify && !copy) {
+ int n;
+
+ /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the
+ * beginning, so that we copy all entries, starting from the first, this time. */
+
+ n = acl_entries(acl);
+ if (n < 0)
+ return -errno;
+
+ copy = acl_init(n);
+ if (!copy)
+ return -errno;
+
+ /* Seek back to the beginning */
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ continue;
+ }
+ }
+
+ if (copy) {
+ acl_entry_t new_entry;
+
+ if (acl_create_entry(&copy, &new_entry) < 0)
+ return -errno;
+
+ if (acl_copy_entry(new_entry, i) < 0)
+ return -errno;
+
+ if (modify)
+ if (acl_set_qualifier(new_entry, &new_uid) < 0)
+ return -errno;
+ }
+
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ }
+
+ *ret = copy;
+ copy = NULL;
+
+ return !!*ret;
+}
+
+static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) {
+ _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL;
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(st);
+
+ /* ACLs are not supported on symlinks, there's no point in trying */
+ if (S_ISLNK(st->st_mode))
+ return 0;
+
+ r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl);
+ if (r == -EOPNOTSUPP)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = shift_acl(acl, shift, &shifted);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted);
+ if (r < 0)
+ return r;
+
+ changed = true;
+ }
+
+ if (S_ISDIR(st->st_mode)) {
+ acl_free(acl);
+ acl_free(shifted);
+
+ acl = shifted = NULL;
+
+ r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl);
+ if (r < 0)
+ return r;
+
+ r = shift_acl(acl, shift, &shifted);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted);
+ if (r < 0)
+ return r;
+
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+#else
+
+static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) {
+ return 0;
+}
+
+#endif
+
+static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) {
+ uid_t new_uid;
+ gid_t new_gid;
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(st);
+
+ new_uid = shift | (st->st_uid & UINT32_C(0xFFFF));
+ new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF));
+
+ if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid))
+ return -EINVAL;
+
+ if (st->st_uid != new_uid || st->st_gid != new_gid) {
+ if (name)
+ r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW);
+ else
+ r = fchown(fd, new_uid, new_gid);
+ if (r < 0)
+ return -errno;
+
+ /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */
+ if (name && !S_ISLNK(st->st_mode))
+ r = fchmodat(fd, name, st->st_mode, 0);
+ else
+ r = fchmod(fd, st->st_mode);
+ if (r < 0)
+ return -errno;
+
+ changed = true;
+ }
+
+ r = patch_acls(fd, name, st, shift);
+ if (r < 0)
+ return r;
+
+ return r > 0 || changed;
+}
+
+static int is_procfs_sysfs_or_suchlike(int fd) {
+ struct statfs sfs;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &sfs) < 0)
+ return -errno;
+
+ return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC);
+}
+
+static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) {
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+
+ /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we
+ * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's
+ * stop the recursion when we hit a procfs or sysfs file system. */
+ r = is_procfs_sysfs_or_suchlike(fd);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ r = 0; /* don't recurse */
+ goto finish;
+ }
+
+ r = patch_fd(fd, NULL, st, shift);
+ if (r == -EROFS) {
+ _cleanup_free_ char *name = NULL;
+
+ if (!is_toplevel) {
+ /* When we hit a ready-only subtree we simply skip it, but log about it. */
+ (void) fd_get_path(fd, &name);
+ log_debug("Skippping read-only file or directory %s.", strna(name));
+ r = 0;
+ }
+
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+
+ if (S_ISDIR(st->st_mode)) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+
+ if (!donate_fd) {
+ int copy;
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ fd = copy;
+ donate_fd = true;
+ }
+
+ d = fdopendir(fd);
+ if (!d) {
+ r = -errno;
+ goto finish;
+ }
+ fd = -1;
+
+ FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
+ struct stat fst;
+
+ if (STR_IN_SET(de->d_name, ".", ".."))
+ continue;
+
+ if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (S_ISDIR(fst.st_mode)) {
+ int subdir_fd;
+
+ subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0) {
+ r = -errno;
+ goto finish;
+
+ }
+
+ r = recurse_fd(subdir_fd, true, &fst, shift, false);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ changed = true;
+
+ } else {
+ r = patch_fd(dirfd(d), de->d_name, &fst, shift);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ changed = true;
+ }
+ }
+ }
+
+ r = changed;
+
+finish:
+ if (donate_fd)
+ safe_close(fd);
+
+ return r;
+}
+
+static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) {
+ struct stat st;
+ int r;
+
+ assert(fd >= 0);
+
+ /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an
+ * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges
+ * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual
+ * UID within the container. */
+
+ if ((shift & 0xFFFF) != 0) {
+ /* We only support containers where the shift starts at a 2^16 boundary */
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ if (range != 0x10000) {
+ /* We only support containers with 16bit UID ranges for the patching logic */
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) {
+ /* We only support containers where the uid/gid container ID match */
+ r = -EBADE;
+ goto finish;
+ }
+
+ /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume
+ * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */
+ if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0)
+ return 0;
+
+ return recurse_fd(fd, donate_fd, &st, shift, true);
+
+finish:
+ if (donate_fd)
+ safe_close(fd);
+
+ return r;
+}
+
+int fd_patch_uid(int fd, uid_t shift, uid_t range) {
+ return fd_patch_uid_internal(fd, false, shift, range);
+}
+
+int path_patch_uid(const char *path, uid_t shift, uid_t range) {
+ int fd;
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (fd < 0)
+ return -errno;
+
+ return fd_patch_uid_internal(fd, true, shift, range);
+}
diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h
new file mode 100644
index 0000000000..55d0990016
--- /dev/null
+++ b/src/nspawn/nspawn-patch-uid.h
@@ -0,0 +1,23 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 <sys/types.h>
+
+int fd_patch_uid(int fd, uid_t shift, uid_t range);
+int path_patch_uid(const char *path, uid_t shift, uid_t range);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 760861089d..20103c5e88 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -20,6 +20,7 @@
#include "sd-bus.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "nspawn-register.h"
#include "stat-util.h"
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 4fb0054698..b98a79fd09 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -25,7 +25,9 @@
#include "parse-util.h"
#include "process-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
+#include "string-util.h"
int settings_load(FILE *f, const char *path, Settings **ret) {
_cleanup_(settings_freep) Settings *s = NULL;
@@ -40,9 +42,13 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
s->start_mode = _START_MODE_INVALID;
s->personality = PERSONALITY_INVALID;
+ s->userns_mode = _USER_NAMESPACE_MODE_INVALID;
+ s->uid_shift = UID_INVALID;
+ s->uid_range = UID_INVALID;
s->read_only = -1;
s->volatile_mode = _VOLATILE_MODE_INVALID;
+ s->userns_chown = -1;
s->private_network = -1;
s->network_veth = -1;
@@ -59,6 +65,16 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
if (r < 0)
return r;
+ /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
+ * both fields shall be initialized or neither. */
+ if (s->userns_mode == USER_NAMESPACE_PICK)
+ s->userns_chown = true;
+ else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
+ s->userns_chown = false;
+
+ if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
+ s->userns_mode = USER_NAMESPACE_NO;
+
*ret = s;
s = NULL;
@@ -392,3 +408,73 @@ conflict:
log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
return 0;
}
+
+int config_parse_private_users(
+ 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) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = parse_boolean(rvalue);
+ if (r == 0) {
+ /* no: User namespacing off */
+ settings->userns_mode = USER_NAMESPACE_NO;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else if (r > 0) {
+ /* yes: User namespacing on, UID range is read from root dir */
+ settings->userns_mode = USER_NAMESPACE_FIXED;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else if (streq(rvalue, "pick")) {
+ /* pick: User namespacing on, UID range is picked randomly */
+ settings->userns_mode = USER_NAMESPACE_PICK;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else {
+ const char *range, *shift;
+ uid_t sh, rn;
+
+ /* anything else: User namespacing on, UID range is explicitly configured */
+
+ range = strchr(rvalue, ':');
+ if (range) {
+ shift = strndupa(rvalue, range - rvalue);
+ range++;
+
+ r = safe_atou32(range, &rn);
+ if (r < 0 || rn <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
+ return 0;
+ }
+ } else {
+ shift = rvalue;
+ rn = UINT32_C(0x10000);
+ }
+
+ r = parse_uid(shift, &sh);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
+ return 0;
+ }
+
+ settings->userns_mode = USER_NAMESPACE_FIXED;
+ settings->uid_shift = sh;
+ settings->uid_range = rn;
+ }
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index a017405cd9..e12e91b886 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -33,6 +33,14 @@ typedef enum StartMode {
_START_MODE_INVALID = -1
} StartMode;
+typedef enum UserNamespaceMode {
+ USER_NAMESPACE_NO,
+ USER_NAMESPACE_FIXED,
+ USER_NAMESPACE_PICK,
+ _USER_NAMESPACE_MODE_MAX,
+ _USER_NAMESPACE_MODE_INVALID = -1,
+} UserNamespaceMode;
+
typedef enum SettingsMask {
SETTING_START_MODE = 1 << 0,
SETTING_ENVIRONMENT = 1 << 1,
@@ -47,7 +55,8 @@ typedef enum SettingsMask {
SETTING_VOLATILE_MODE = 1 << 10,
SETTING_CUSTOM_MOUNTS = 1 << 11,
SETTING_WORKING_DIRECTORY = 1 << 12,
- _SETTINGS_MASK_ALL = (1 << 13) -1
+ SETTING_USERNS = 1 << 13,
+ _SETTINGS_MASK_ALL = (1 << 14) -1
} SettingsMask;
typedef struct Settings {
@@ -62,12 +71,15 @@ typedef struct Settings {
unsigned long personality;
sd_id128_t machine_id;
char *working_directory;
+ UserNamespaceMode userns_mode;
+ uid_t uid_shift, uid_range;
/* [Image] */
int read_only;
VolatileMode volatile_mode;
CustomMount *custom_mounts;
unsigned n_custom_mounts;
+ int userns_chown;
/* [Network] */
int private_network;
@@ -99,3 +111,4 @@ int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, co
int config_parse_veth_extra(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_boot(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_pid2(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_private_users(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/nspawn/nspawn.c b/src/nspawn/nspawn.c
index eb89916b7e..3fc6cc955c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -22,7 +22,9 @@
#endif
#include <errno.h>
#include <getopt.h>
+#include <grp.h>
#include <linux/loop.h>
+#include <pwd.h>
#include <sched.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
@@ -64,6 +66,7 @@
#include "hostname-util.h"
#include "log.h"
#include "loopback-setup.h"
+#include "machine-id-setup.h"
#include "machine-image.h"
#include "macro.h"
#include "missing.h"
@@ -74,6 +77,7 @@
#include "nspawn-expose-ports.h"
#include "nspawn-mount.h"
#include "nspawn-network.h"
+#include "nspawn-patch-uid.h"
#include "nspawn-register.h"
#include "nspawn-settings.h"
#include "nspawn-setuid.h"
@@ -100,6 +104,11 @@
#include "user-util.h"
#include "util.h"
+/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit
+ * UID range here */
+#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000))
+#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000))
+
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
CONTAINER_REBOOTED
@@ -172,8 +181,9 @@ static char *arg_image = NULL;
static VolatileMode arg_volatile_mode = VOLATILE_NO;
static ExposePort *arg_expose_ports = NULL;
static char **arg_property = NULL;
+static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
-static bool arg_userns = false;
+static bool arg_userns_chown = false;
static int arg_kill_signal = 0;
static bool arg_unified_cgroup_hierarchy = false;
static SettingsMask arg_settings_mask = 0;
@@ -201,8 +211,10 @@ static void help(void) {
" --uuid=UUID Set a specific machine UUID for the container\n"
" -S --slice=SLICE Place the container in the specified slice\n"
" --property=NAME=VALUE Set scope unit property\n"
+ " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n"
" --private-users[=UIDBASE[:NUIDS]]\n"
- " Run within user namespace\n"
+ " Run within user namespace, user configured UID/GID range\n"
+ " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
@@ -249,7 +261,7 @@ static void help(void) {
" the container\n"
" --overlay-ro=PATH[:PATH...]:PATH\n"
" Similar, but creates a read-only overlay mount\n"
- " --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
+ " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
" --share-system Share system namespaces with host\n"
" --register=BOOLEAN Register container as machine\n"
" --keep-unit Do not register a scope for the machine, reuse\n"
@@ -271,9 +283,15 @@ static int custom_mounts_prepare(void) {
for (i = 0; i < arg_n_custom_mounts; i++) {
CustomMount *m = &arg_custom_mounts[i];
- if (arg_userns && arg_uid_shift == UID_INVALID && path_equal(m->destination, "/")) {
- log_error("--private-users with automatic UID shift may not be combined with custom root mounts.");
- return -EINVAL;
+ if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
+
+ if (arg_userns_chown) {
+ log_error("--private-users-chown may not be combined with custom root mounts.");
+ return -EINVAL;
+ } else if (arg_uid_shift == UID_INVALID) {
+ log_error("--private-users with automatic UID shift may not be combined with custom root mounts.");
+ return -EINVAL;
+ }
}
if (m->type != CUSTOM_MOUNT_OVERLAY)
@@ -332,7 +350,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TMPFS,
ARG_OVERLAY,
ARG_OVERLAY_RO,
- ARG_SETENV,
ARG_SHARE_SYSTEM,
ARG_REGISTER,
ARG_KEEP_UNIT,
@@ -349,6 +366,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_KILL_SIGNAL,
ARG_SETTINGS,
ARG_CHDIR,
+ ARG_PRIVATE_USERS_CHOWN,
};
static const struct option options[] = {
@@ -373,7 +391,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
{ "machine", required_argument, NULL, 'M' },
{ "slice", required_argument, NULL, 'S' },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{ "selinux-context", required_argument, NULL, 'Z' },
{ "selinux-apifs-context", required_argument, NULL, 'L' },
{ "quiet", no_argument, NULL, 'q' },
@@ -392,6 +410,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "port", required_argument, NULL, 'p' },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
+ { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN},
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR },
@@ -406,7 +425,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0)
switch (c) {
@@ -710,7 +729,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
- case ARG_SETENV: {
+ case 'E': {
char **n;
if (!env_assignment_is_valid(optarg)) {
@@ -797,10 +816,29 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_PRIVATE_USERS:
- if (optarg) {
+
+ r = optarg ? parse_boolean(optarg) : 1;
+ if (r == 0) {
+ /* no: User namespacing off */
+ arg_userns_mode = USER_NAMESPACE_NO;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else if (r > 0) {
+ /* yes: User namespacing on, UID range is read from root dir */
+ arg_userns_mode = USER_NAMESPACE_FIXED;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else if (streq(optarg, "pick")) {
+ /* pick: User namespacing on, UID range is picked randomly */
+ arg_userns_mode = USER_NAMESPACE_PICK;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else {
_cleanup_free_ char *buffer = NULL;
const char *range, *shift;
+ /* anything else: User namespacing on, UID range is explicitly configured */
+
range = strchr(optarg, ':');
if (range) {
buffer = strndup(optarg, range - optarg);
@@ -820,9 +858,28 @@ static int parse_argv(int argc, char *argv[]) {
log_error("Failed to parse UID: %s", optarg);
return -EINVAL;
}
+
+ arg_userns_mode = USER_NAMESPACE_FIXED;
}
- arg_userns = true;
+ arg_settings_mask |= SETTING_USERNS;
+ break;
+
+ case 'U':
+ if (userns_supported()) {
+ arg_userns_mode = USER_NAMESPACE_PICK;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+
+ arg_settings_mask |= SETTING_USERNS;
+ }
+
+ break;
+
+ case ARG_PRIVATE_USERS_CHOWN:
+ arg_userns_chown = true;
+
+ arg_settings_mask |= SETTING_USERNS;
break;
case ARG_KILL_SIGNAL:
@@ -893,6 +950,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_share_system)
arg_register = false;
+ if (arg_userns_mode == USER_NAMESPACE_PICK)
+ arg_userns_chown = true;
+
if (arg_start_mode != START_PID1 && arg_share_system) {
log_error("--boot and --share-system may not be combined.");
return -EINVAL;
@@ -933,8 +993,15 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_userns && access("/proc/self/uid_map", F_OK) < 0)
- return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support.");
+ if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) {
+ log_error("--private-users= is not supported, kernel compiled without user namespace support.");
+ return -EOPNOTSUPP;
+ }
+
+ if (arg_userns_chown && arg_read_only) {
+ log_error("--read-only and --private-users-chown may not be combined.");
+ return -EINVAL;
+ }
if (argc > optind) {
arg_parameters = strv_copy(argv + optind);
@@ -993,7 +1060,7 @@ static int verify_arguments(void) {
static int userns_lchown(const char *p, uid_t uid, gid_t gid) {
assert(p);
- if (!arg_userns)
+ if (arg_userns_mode == USER_NAMESPACE_NO)
return 0;
if (uid == UID_INVALID && gid == GID_INVALID)
@@ -1375,11 +1442,11 @@ static int setup_hostname(void) {
}
static int setup_journal(const char *directory) {
- sd_id128_t machine_id, this_id;
- _cleanup_free_ char *b = NULL, *d = NULL;
- const char *etc_machine_id, *p, *q;
+ sd_id128_t this_id;
+ _cleanup_free_ char *d = NULL;
+ const char *p, *q;
bool try;
- char *id;
+ char id[33];
int r;
/* Don't link journals in ephemeral mode */
@@ -1391,30 +1458,13 @@ static int setup_journal(const char *directory) {
try = arg_link_journal_try || arg_link_journal == LINK_AUTO;
- etc_machine_id = prefix_roota(directory, "/etc/machine-id");
-
- r = read_one_line_file(etc_machine_id, &b);
- if (r == -ENOENT && try)
- return 0;
- else if (r < 0)
- return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id);
-
- id = strstrip(b);
- if (isempty(id) && try)
- return 0;
-
- /* Verify validity */
- r = sd_id128_from_string(id, &machine_id);
- if (r < 0)
- return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id);
-
r = sd_id128_get_machine(&this_id);
if (r < 0)
return log_error_errno(r, "Failed to retrieve machine ID: %m");
- if (sd_id128_equal(machine_id, this_id)) {
+ if (sd_id128_equal(arg_uuid, this_id)) {
log_full(try ? LOG_WARNING : LOG_ERR,
- "Host and machine ids are equal (%s): refusing to link journals", id);
+ "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id));
if (try)
return 0;
return -EEXIST;
@@ -1432,6 +1482,8 @@ static int setup_journal(const char *directory) {
if (r < 0)
return log_error_errno(r, "Failed to create /var/log/journal: %m");
+ (void) sd_id128_to_string(arg_uuid, id);
+
p = strjoina("/var/log/journal/", id);
q = prefix_roota(directory, p);
@@ -1496,7 +1548,7 @@ static int setup_journal(const char *directory) {
}
if (arg_link_journal == LINK_HOST) {
- /* don't create parents here -- if the host doesn't have
+ /* don't create parents here — if the host doesn't have
* permanent journal set up, don't force it here */
if (mkdir(p, 0755) < 0 && errno != EEXIST) {
@@ -2201,6 +2253,61 @@ static int mount_device(const char *what, const char *where, const char *directo
#endif
}
+static int setup_machine_id(const char *directory) {
+ int r;
+ const char *etc_machine_id, *t;
+ _cleanup_free_ char *s = NULL;
+
+ etc_machine_id = prefix_roota(directory, "/etc/machine-id");
+
+ r = read_one_line_file(etc_machine_id, &s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id);
+
+ t = strstrip(s);
+
+ if (!isempty(t)) {
+ r = sd_id128_from_string(t, &arg_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id);
+ } else {
+ if (sd_id128_is_null(arg_uuid)) {
+ r = sd_id128_randomize(&arg_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate random machine ID: %m");
+ }
+ }
+
+ r = machine_id_setup(directory, arg_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to setup machine ID: %m");
+
+ return 0;
+}
+
+static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
+ int r;
+
+ assert(directory);
+
+ if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown)
+ return 0;
+
+ r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
+ if (r == -EOPNOTSUPP)
+ return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16.");
+ if (r == -EBADE)
+ return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m");
+ if (r == 0)
+ log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation.");
+ else
+ log_debug("Patched directory tree to match UID/GID range.");
+
+ return r;
+}
+
static int mount_devices(
const char *where,
const char *root_device, bool root_device_rw,
@@ -2418,7 +2525,7 @@ static int determine_names(void) {
static int determine_uid_shift(const char *directory) {
int r;
- if (!arg_userns) {
+ if (arg_userns_mode == USER_NAMESPACE_NO) {
arg_uid_shift = 0;
return 0;
}
@@ -2445,7 +2552,6 @@ static int determine_uid_shift(const char *directory) {
return -EINVAL;
}
- log_info("Using user namespaces with base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
return 0;
}
@@ -2458,6 +2564,7 @@ static int inner_child(
FDSet *fds) {
_cleanup_free_ char *home = NULL;
+ char as_uuid[37];
unsigned n_env = 1;
const char *envp[] = {
"PATH=" DEFAULT_PATH_SPLIT_USR,
@@ -2481,7 +2588,7 @@ static int inner_child(
cg_unified_flush();
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Tell the parent, that it now can write the UID map. */
(void) barrier_place(barrier); /* #1 */
@@ -2492,7 +2599,14 @@ static int inner_child(
}
}
- r = mount_all(NULL, arg_userns, true, arg_uid_shift, arg_private_network, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(NULL,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ true,
+ arg_private_network,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
+
if (r < 0)
return r;
@@ -2575,12 +2689,10 @@ static int inner_child(
(asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0))
return log_oom();
- if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
- char as_uuid[37];
+ assert(!sd_id128_equal(arg_uuid, SD_ID128_NULL));
- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0)
- return log_oom();
- }
+ if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0)
+ return log_oom();
if (fdset_size(fds) > 0) {
r = fdset_cloexec(fds, false);
@@ -2648,7 +2760,8 @@ static int inner_child(
execvpe(arg_parameters[0], arg_parameters, env_use);
else {
if (!arg_chdir)
- chdir(home ?: "/root");
+ /* If we cannot change the directory, we'll end up in /, that is expected. */
+ (void) chdir(home ?: "/root");
execle("/bin/bash", "-bash", NULL, env_use);
execle("/bin/sh", "-sh", NULL, env_use);
@@ -2669,6 +2782,7 @@ static int outer_child(
bool interactive,
bool secondary,
int pid_socket,
+ int uuid_socket,
int kmsg_socket,
int rtnl_socket,
int uid_shift_socket,
@@ -2682,6 +2796,7 @@ static int outer_child(
assert(directory);
assert(console);
assert(pid_socket >= 0);
+ assert(uuid_socket >= 0);
assert(kmsg_socket >= 0);
cg_unified_flush();
@@ -2730,7 +2845,8 @@ static int outer_child(
if (r < 0)
return r;
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* Let the parent know which UID shift we read from the image */
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send UID shift: %m");
@@ -2738,17 +2854,49 @@ static int outer_child(
log_error("Short write while sending UID shift.");
return -EIO;
}
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift
+ * we just read from the image is available. If yes, it will send the UID shift back to us, if
+ * not it will pick a different one, and send it back to us. */
+
+ l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to recv UID shift: %m");
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short read while recieving UID shift.");
+ return -EIO;
+ }
+ }
+
+ log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
}
/* Turn directory into bind mount */
if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
return log_error_errno(errno, "Failed to make bind mount: %m");
- r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
+ r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
+ if (r < 0)
+ return r;
+
+ r = setup_volatile(
+ directory,
+ arg_volatile_mode,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_context);
if (r < 0)
return r;
- r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
+ r = setup_volatile_state(
+ directory,
+ arg_volatile_mode,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_context);
if (r < 0)
return r;
@@ -2762,7 +2910,13 @@ static int outer_child(
return log_error_errno(r, "Failed to make tree read-only: %m");
}
- r = mount_all(directory, arg_userns, false, arg_private_network, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(directory,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ false,
+ arg_private_network,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2796,15 +2950,32 @@ static int outer_child(
if (r < 0)
return r;
+ r = setup_machine_id(directory);
+ if (r < 0)
+ return r;
+
r = setup_journal(directory);
if (r < 0)
return r;
- r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_custom(
+ directory,
+ arg_custom_mounts,
+ arg_n_custom_mounts,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
- r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_cgroups(
+ directory,
+ arg_unified_cgroup_hierarchy,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2815,12 +2986,13 @@ static int outer_child(
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
(arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
(arg_private_network ? CLONE_NEWNET : 0) |
- (arg_userns ? CLONE_NEWUSER : 0),
+ (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0),
NULL);
if (pid < 0)
return log_error_errno(errno, "Failed to fork inner child: %m");
if (pid == 0) {
pid_socket = safe_close(pid_socket);
+ uuid_socket = safe_close(uuid_socket);
uid_shift_socket = safe_close(uid_shift_socket);
/* The inner child has all namespaces that are
@@ -2842,13 +3014,77 @@ static int outer_child(
return -EIO;
}
+ l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send machine ID: %m");
+ if (l != sizeof(arg_uuid)) {
+ log_error("Short write while sending machine ID.");
+ return -EIO;
+ }
+
pid_socket = safe_close(pid_socket);
+ uuid_socket = safe_close(uuid_socket);
kmsg_socket = safe_close(kmsg_socket);
rtnl_socket = safe_close(rtnl_socket);
return 0;
}
+static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) {
+ unsigned n_tries = 100;
+ uid_t candidate;
+ int r;
+
+ assert(shift);
+ assert(ret_lock_file);
+ assert(arg_userns_mode == USER_NAMESPACE_PICK);
+ assert(arg_uid_range == 0x10000U);
+
+ candidate = *shift;
+
+ (void) mkdir("/run/systemd/nspawn-uid", 0755);
+
+ for (;;) {
+ char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
+ _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT;
+
+ if (--n_tries <= 0)
+ return -EBUSY;
+
+ if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX)
+ goto next;
+ if ((candidate & UINT32_C(0xFFFF)) != 0)
+ goto next;
+
+ xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate);
+ r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf);
+ if (r == -EBUSY) /* Range already taken by another nspawn instance */
+ goto next;
+ if (r < 0)
+ return r;
+
+ /* Make some superficial checks whether the range is currently known in the user database */
+ if (getpwuid(candidate))
+ goto next;
+ if (getpwuid(candidate + UINT32_C(0xFFFE)))
+ goto next;
+ if (getgrgid(candidate))
+ goto next;
+ if (getgrgid(candidate + UINT32_C(0xFFFE)))
+ goto next;
+
+ *ret_lock_file = lf;
+ lf = (struct LockFile) LOCK_FILE_INIT;
+ *shift = candidate;
+ return 0;
+
+ next:
+ random_bytes(&candidate, sizeof(candidate));
+ candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN;
+ candidate &= (uid_t) UINT32_C(0xFFFF0000);
+ }
+}
+
static int setup_uid_map(pid_t pid) {
char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
int r;
@@ -3080,6 +3316,19 @@ static int load_settings(void) {
}
}
+ if ((arg_settings_mask & SETTING_USERNS) == 0 &&
+ settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p);
+ else {
+ arg_userns_mode = settings->userns_mode;
+ arg_uid_shift = settings->uid_shift;
+ arg_uid_range = settings->uid_range;
+ arg_userns_chown = settings->userns_chown;
+ }
+ }
+
return 0;
}
@@ -3090,7 +3339,7 @@ int main(int argc, char *argv[]) {
_cleanup_close_ int master = -1, image_fd = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
int r, n_fd_passed, loop_nr = -1;
- char veth_name[IFNAMSIZ];
+ char veth_name[IFNAMSIZ] = "";
bool secondary = false, remove_subvol = false;
sigset_t mask_chld;
pid_t pid = 0;
@@ -3318,19 +3567,42 @@ int main(int argc, char *argv[]) {
}
for (;;) {
- _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 };
- ContainerStatus container_status;
- _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
static const struct sigaction sa = {
.sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP,
};
- int ifi = 0;
- ssize_t l;
+
+ _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
+ _cleanup_close_ int etc_passwd_lock = -1;
+ _cleanup_close_pair_ int
+ kmsg_socket_pair[2] = { -1, -1 },
+ rtnl_socket_pair[2] = { -1, -1 },
+ pid_socket_pair[2] = { -1, -1 },
+ uuid_socket_pair[2] = { -1, -1 },
+ uid_shift_socket_pair[2] = { -1, -1 };
+ _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ ContainerStatus container_status;
char last_char = 0;
+ int ifi = 0;
+ ssize_t l;
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
+ * check with getpwuid() if the specific user already exists. Note that /etc might be
+ * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
+ * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
+ * really just an extra safety net. We kinda assume that the UID range we allocate from is
+ * really ours. */
+
+ etc_passwd_lock = take_etc_passwd_lock(NULL);
+ if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) {
+ log_error_errno(r, "Failed to take /etc/passwd lock: %m");
+ goto finish;
+ }
+ }
r = barrier_create(&barrier);
if (r < 0) {
@@ -3353,7 +3625,12 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_userns)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) {
+ r = log_error_errno(errno, "Failed to create id socket pair: %m");
+ goto finish;
+ }
+
+ if (arg_userns_mode != USER_NAMESPACE_NO)
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create uid shift socket pair: %m");
goto finish;
@@ -3393,6 +3670,7 @@ int main(int argc, char *argv[]) {
kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
+ uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
(void) reset_all_signal_handlers();
@@ -3407,6 +3685,7 @@ int main(int argc, char *argv[]) {
interactive,
secondary,
pid_socket_pair[1],
+ uuid_socket_pair[1],
kmsg_socket_pair[1],
rtnl_socket_pair[1],
uid_shift_socket_pair[1],
@@ -3424,8 +3703,46 @@ int main(int argc, char *argv[]) {
kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
+ uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* The child just let us know the UID shift it might have read from the image. */
+ l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read UID shift: %m");
+ goto finish;
+ }
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short read while reading UID shift.");
+ r = EIO;
+ goto finish;
+ }
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* If we are supposed to pick the UID shift, let's try to use the shift read from the
+ * image, but if that's already in use, pick a new one, and report back to the child,
+ * which one we now picked. */
+
+ r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
+ if (r < 0) {
+ log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
+ goto finish;
+ }
+
+ l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to send UID shift: %m");
+ goto finish;
+ }
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short write while writing UID shift.");
+ r = -EIO;
+ goto finish;
+ }
+ }
+ }
+
/* Wait for the outer child. */
r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
if (r < 0)
@@ -3448,26 +3765,27 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* We also retrieve container UUID in case it was generated by outer child */
+ l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read container machine ID: %m");
+ goto finish;
+ }
+ if (l != sizeof(arg_uuid)) {
+ log_error("Short read while reading container machined ID.");
+ r = EIO;
+ goto finish;
+ }
+
log_debug("Init process invoked as PID " PID_FMT, pid);
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
if (!barrier_place_and_sync(&barrier)) { /* #1 */
log_error("Child died too early.");
r = -ESRCH;
goto finish;
}
- l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while reading UID shift.");
- r = EIO;
- goto finish;
- }
-
r = setup_uid_map(pid);
if (r < 0)
goto finish;
@@ -3565,6 +3883,10 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear
+ * in getpwuid(), thus we can release the /etc/passwd lock. */
+ etc_passwd_lock = safe_close(etc_passwd_lock);
+
sd_notifyf(false,
"READY=1\n"
"STATUS=Container running.\n"
@@ -3659,6 +3981,7 @@ int main(int argc, char *argv[]) {
}
expose_port_flush(arg_expose_ports, &exposed);
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
}
finish:
@@ -3691,6 +4014,7 @@ finish:
}
expose_port_flush(arg_expose_ports, &exposed);
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
free(arg_directory);
free(arg_template);
diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c
new file mode 100644
index 0000000000..11c5321788
--- /dev/null
+++ b/src/nspawn/test-patch-uid.c
@@ -0,0 +1,61 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 "log.h"
+#include "nspawn-patch-uid.h"
+#include "user-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ uid_t shift, range;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc != 4) {
+ log_error("Expected PATH SHIFT RANGE parameters.");
+ return EXIT_FAILURE;
+ }
+
+ r = parse_uid(argv[2], &shift);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse UID shift %s.", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ r = parse_gid(argv[3], &range);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse UID range %s.", argv[3]);
+ return EXIT_FAILURE;
+ }
+
+ r = path_patch_uid(argv[1], shift, range);
+ if (r < 0) {
+ log_error_errno(r, "Failed to patch directory tree: %m");
+ return EXIT_FAILURE;
+ }
+
+ log_info("Changed: %s", yes_no(r));
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 2536ad2898..2b83d127b7 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -127,7 +127,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
memcpy(r_name, canonical, l+1);
idx = ALIGN(l+1);
- if (n_addresses <= 0) {
+ assert(n_addresses >= 0);
+ if (n_addresses == 0) {
/* Second, fill in IPv6 tuple */
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
@@ -453,38 +454,33 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
}
n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
- if (n_addresses > 0) {
- for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
- if (af != a->family)
- continue;
+ for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
+ if (af != a->family)
+ continue;
- if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
- goto found;
- }
+ if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
+ goto found;
}
addresses = mfree(addresses);
n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
- if (n_addresses > 0) {
- for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
- if (af != a->family)
- continue;
+ for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
+ if (af != a->family)
+ continue;
- if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
- canonical = "gateway";
- goto found;
- }
+ if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
+ canonical = "gateway";
+ goto found;
}
}
*errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
-
return NSS_STATUS_NOTFOUND;
found:
- if (!canonical || (!additional && additional_from_hostname)) {
+ if (!canonical || additional_from_hostname) {
hn = gethostname_malloc();
if (!hn) {
*errnop = ENOMEM;
@@ -494,8 +490,7 @@ found:
if (!canonical)
canonical = hn;
-
- if (!additional && additional_from_hostname)
+ else
additional = hn;
}
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index 0de6bd2241..5ce10f1cbd 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -117,13 +117,6 @@ enum nss_status _nss_resolve_gethostbyname4_r(
int *errnop, int *h_errnop,
int32_t *ttlp) {
- enum nss_status (*fallback)(
- const char *name,
- struct gaih_addrtuple **pat,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp);
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
@@ -275,15 +268,15 @@ enum nss_status _nss_resolve_gethostbyname4_r(
return NSS_STATUS_SUCCESS;
fallback:
- fallback = (enum nss_status (*)(const char *name,
- struct gaih_addrtuple **pat,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp))
- find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
+ {
+ _nss_gethostbyname4_r_t fallback;
+
+ fallback = (_nss_gethostbyname4_r_t)
+ find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
- if (fallback)
- return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
+ if (fallback)
+ return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
+ }
fail:
*errnop = -r;
@@ -300,15 +293,6 @@ enum nss_status _nss_resolve_gethostbyname3_r(
int32_t *ttlp,
char **canonp) {
- enum nss_status (*fallback)(
- const char *name,
- int af,
- struct hostent *result,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp,
- char **canonp);
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
@@ -480,16 +464,14 @@ enum nss_status _nss_resolve_gethostbyname3_r(
return NSS_STATUS_SUCCESS;
fallback:
- fallback = (enum nss_status (*)(const char *name,
- int af,
- struct hostent *result,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp,
- char **canonp))
- find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
- if (fallback)
- return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
+ {
+ _nss_gethostbyname3_r_t fallback;
+
+ fallback = (_nss_gethostbyname3_r_t)
+ find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
+ if (fallback)
+ return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
+ }
fail:
*errnop = -r;
@@ -505,15 +487,6 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
int *errnop, int *h_errnop,
int32_t *ttlp) {
- enum nss_status (*fallback)(
- const void* addr, socklen_t len,
- int af,
- struct hostent *result,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp);
-
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
@@ -682,17 +655,15 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
return NSS_STATUS_SUCCESS;
fallback:
- fallback = (enum nss_status (*)(
- const void* addr, socklen_t len,
- int af,
- struct hostent *result,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp))
- find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
-
- if (fallback)
- return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
+ {
+ _nss_gethostbyaddr2_r_t fallback;
+
+ fallback = (_nss_gethostbyaddr2_r_t)
+ find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
+
+ if (fallback)
+ return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
+ }
fail:
*errnop = -r;
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
index 22004a00cd..09c85f9518 100644
--- a/src/resolve/RFCs
+++ b/src/resolve/RFCs
@@ -8,7 +8,7 @@ D = Comprehensively Implemented, by a dependency of resolved
Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
-Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support
+Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts — Application and Support
~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes
Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 16cae8c1e5..33f7c61557 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -424,8 +424,9 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (added <= 0) {
_cleanup_free_ char *ip = NULL;
- in_addr_to_string(q->request_family, &q->request_address, &ip);
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip));
+ (void) in_addr_to_string(q->request_family, &q->request_address, &ip);
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR,
+ "Address '%s' does not have any RR of requested type", strnull(ip));
goto finish;
}
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 0af7551425..a54aed3a63 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -1734,7 +1734,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
if (r <= 0)
return r;
- wc = strjoina("*.", common_suffix, NULL);
+ wc = strjoina("*.", common_suffix);
return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
}
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index a5129c201e..081131ede0 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -262,7 +262,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
if (manager_our_packet(t->scope->manager, p) != 0)
return;
- in_addr_to_string(p->family, &p->sender, &pretty);
+ (void) in_addr_to_string(p->family, &p->sender, &pretty);
log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
t->id,
@@ -270,7 +270,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
af_to_name_short(t->scope->family),
- pretty);
+ strnull(pretty));
/* RFC 4795, Section 4.1 says that the peer with the
* lexicographically smaller IP address loses */
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index c5863b3aa2..b0dc65036d 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -468,7 +468,7 @@ static void link_read_settings(Link *l) {
}
if (r > 0) {
- /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */
+ /* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */
if (l->is_managed)
link_flush_settings(l);
diff --git a/src/run/run.c b/src/run/run.c
index 540a612fdf..1d0f74ad21 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "calendarspec.h"
#include "env-util.h"
@@ -103,7 +104,7 @@ static void help(void) {
" --uid=USER Run as system user\n"
" --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
- " --setenv=NAME=VALUE Set environment\n"
+ " -E --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo tty\n"
" -q --quiet Suppress information messages during runtime\n\n"
"Timer options:\n\n"
@@ -125,7 +126,6 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
- ARG_NO_ASK_PASSWORD,
ARG_USER,
ARG_SYSTEM,
ARG_SCOPE,
@@ -133,12 +133,10 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DESCRIPTION,
ARG_SLICE,
ARG_SEND_SIGHUP,
+ ARG_SERVICE_TYPE,
ARG_EXEC_USER,
ARG_EXEC_GROUP,
- ARG_SERVICE_TYPE,
ARG_NICE,
- ARG_SETENV,
- ARG_TTY,
ARG_ON_ACTIVE,
ARG_ON_BOOT,
ARG_ON_STARTUP,
@@ -147,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ON_CALENDAR,
ARG_TIMER_PROPERTY,
ARG_NO_BLOCK,
+ ARG_NO_ASK_PASSWORD,
};
static const struct option options[] = {
@@ -166,9 +165,10 @@ static int parse_argv(int argc, char *argv[]) {
{ "uid", required_argument, NULL, ARG_EXEC_USER },
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
{ "nice", required_argument, NULL, ARG_NICE },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{ "property", required_argument, NULL, 'p' },
- { "tty", no_argument, NULL, 't' },
+ { "tty", no_argument, NULL, 't' }, /* deprecated */
+ { "pty", no_argument, NULL, 't' },
{ "quiet", no_argument, NULL, 'q' },
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
@@ -266,7 +266,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_nice_set = true;
break;
- case ARG_SETENV:
+ case 'E':
if (strv_extend(&arg_environment, optarg) < 0)
return log_oom();
@@ -621,6 +621,10 @@ static int transient_scope_set_properties(sd_bus_message *m) {
if (r < 0)
return r;
+ r = transient_cgroup_set_properties(m);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
if (r < 0)
return r;
@@ -878,7 +882,7 @@ static int start_transient_service(
(void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
if (!arg_quiet)
- log_info("Running as unit %s\nPress ^] three times within 1s to disconnect TTY.", service);
+ log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service);
r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
if (r < 0)
@@ -896,7 +900,7 @@ static int start_transient_service(
fputc('\n', stdout);
} else if (!arg_quiet)
- log_info("Running as unit %s", service);
+ log_info("Running as unit: %s", service);
return 0;
}
@@ -1038,7 +1042,7 @@ static int start_transient_scope(
return r;
if (!arg_quiet)
- log_info("Running scope as unit %s", scope);
+ log_info("Running scope as unit: %s", scope);
execvpe(argv[0], argv, env);
@@ -1189,9 +1193,9 @@ static int start_transient_timer(
if (r < 0)
return r;
- log_info("Running timer as unit %s", timer);
+ log_info("Running timer as unit: %s", timer);
if (argv[0])
- log_info("Will run service as unit %s", service);
+ log_info("Will run service as unit: %s", service);
return 0;
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
new file mode 100644
index 0000000000..2b755cea28
--- /dev/null
+++ b/src/shared/bus-unit-util.c
@@ -0,0 +1,1287 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 "alloc-util.h"
+#include "bus-internal.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "env-util.h"
+#include "escape.h"
+#include "hashmap.h"
+#include "list.h"
+#include "locale-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "utf8.h"
+#include "util.h"
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
+ assert(message);
+ assert(u);
+
+ u->machine = NULL;
+
+ return sd_bus_message_read(
+ message,
+ "(ssssssouso)",
+ &u->id,
+ &u->description,
+ &u->load_state,
+ &u->active_state,
+ &u->sub_state,
+ &u->following,
+ &u->unit_path,
+ &u->job_id,
+ &u->job_type,
+ &u->job_path);
+}
+
+int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
+ const char *eq, *field;
+ int r, rl;
+
+ assert(m);
+ assert(assignment);
+
+ eq = strchr(assignment, '=');
+ if (!eq) {
+ log_error("Not an assignment: %s", assignment);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ field = strndupa(assignment, eq - assignment);
+ eq++;
+
+ if (streq(field, "CPUQuota")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
+ else if (endswith(eq, "%")) {
+ double percent;
+
+ if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
+ log_error("CPU quota '%s' invalid.", eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
+ } else {
+ log_error("CPU quota needs to be in percent.");
+ return -EINVAL;
+ }
+
+ goto finish;
+
+ } else if (streq(field, "EnvironmentFile")) {
+
+ r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
+ eq[0] == '-' ? eq + 1 : eq,
+ eq[0] == '-');
+ goto finish;
+
+ } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
+ char *n;
+ usec_t t;
+ size_t l;
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
+
+ l = strlen(field);
+ n = newa(char, l + 2);
+ if (!n)
+ return log_oom();
+
+ /* Change suffix Sec → USec */
+ strcpy(mempcpy(n, field, l - 3), "USec");
+ r = sd_bus_message_append(m, "sv", n, "t", t);
+ goto finish;
+ }
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ rl = rlimit_from_string(field);
+ if (rl >= 0) {
+ const char *sn;
+ struct rlimit l;
+
+ r = rlimit_parse(rl, eq, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resource limit: %s", eq);
+
+ r = sd_bus_message_append(m, "v", "t", l.rlim_max);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ sn = strjoina(field, "Soft");
+ r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
+
+ } else if (STR_IN_SET(field,
+ "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
+ "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
+ "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
+
+ r = parse_boolean(eq);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
+
+ r = sd_bus_message_append(m, "v", "b", r);
+
+ } else if (streq(field, "MemoryLimit")) {
+ uint64_t bytes;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ bytes = (uint64_t) -1;
+ else {
+ r = parse_size(eq, 1024, &bytes);
+ if (r < 0) {
+ log_error("Failed to parse bytes specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", bytes);
+
+ } else if (streq(field, "TasksMax")) {
+ uint64_t n;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ n = (uint64_t) -1;
+ else {
+ r = safe_atou64(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse maximum tasks specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+
+ } 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 = 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,
+ "User", "Group", "DevicePolicy", "KillMode",
+ "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
+ "StandardInput", "StandardOutput", "StandardError",
+ "Description", "Slice", "Type", "WorkingDirectory",
+ "RootDirectory", "SyslogIdentifier", "ProtectSystem",
+ "ProtectHome"))
+ r = sd_bus_message_append(m, "v", "s", eq);
+
+ else if (streq(field, "SyslogLevel")) {
+ int level;
+
+ level = log_level_from_string(eq);
+ if (level < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", level);
+
+ } else if (streq(field, "SyslogFacility")) {
+ int facility;
+
+ facility = log_facility_unshifted_from_string(eq);
+ if (facility < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", facility);
+
+ } else if (streq(field, "DeviceAllow")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(ss)", 0);
+ else {
+ const char *path, *rwm, *e;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ rwm = e+1;
+ } else {
+ path = eq;
+ rwm = "";
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
+ }
+
+ } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(st)", 0);
+ else {
+ const char *path, *bandwidth, *e;
+ uint64_t bytes;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ bandwidth = e+1;
+ } else {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = parse_size(bandwidth, 1000, &bytes);
+ if (r < 0) {
+ log_error("Failed to parse byte value %s.", bandwidth);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
+ }
+
+ } else if (streq(field, "BlockIODeviceWeight")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(st)", 0);
+ else {
+ const char *path, *weight, *e;
+ uint64_t u;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ weight = e+1;
+ } else {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = safe_atou64(weight, &u);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, weight);
+ return -EINVAL;
+ }
+ r = sd_bus_message_append(m, "v", "a(st)", path, u);
+ }
+
+ } else if (streq(field, "Nice")) {
+ int32_t i;
+
+ r = safe_atoi32(eq, &i);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", i);
+
+ } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ if (r < 0) {
+ log_error("Failed to parse Environment value %s", eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (streq(field, "Environment")) {
+ if (!env_assignment_is_valid(word)) {
+ log_error("Invalid environment assignment: %s", word);
+ return -EINVAL;
+ }
+ } else { /* PassEnvironment */
+ if (!env_name_is_valid(word)) {
+ log_error("Invalid environment variable name: %s", word);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else if (streq(field, "KillSignal")) {
+ int sig;
+
+ sig = signal_from_string_try_harder(eq);
+ if (sig < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", sig);
+
+ } else if (streq(field, "TimerSlackNSec")) {
+ nsec_t n;
+
+ r = parse_nsec(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+ } else if (streq(field, "OOMScoreAdjust")) {
+ int oa;
+
+ r = safe_atoi(eq, &oa);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ if (!oom_score_adjust_is_valid(oa)) {
+ log_error("OOM score adjust value out of range");
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", oa);
+ } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int offset;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (!utf8_is_valid(word)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ offset = word[0] == '-';
+ if (!path_is_absolute(word + offset)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ path_kill_slashes(word + offset);
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else if (streq(field, "RuntimeDirectory")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value %s", field, eq);
+
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else {
+ log_error("Unknown assignment %s.", assignment);
+ return -EINVAL;
+ }
+
+finish:
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 0;
+}
+
+typedef struct BusWaitForJobs {
+ sd_bus *bus;
+ Set *jobs;
+
+ char *name;
+ char *result;
+
+ sd_bus_slot *slot_job_removed;
+ sd_bus_slot *slot_disconnected;
+} BusWaitForJobs;
+
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ assert(m);
+
+ log_error("Warning! D-Bus connection terminated.");
+ sd_bus_close(sd_bus_message_get_bus(m));
+
+ return 0;
+}
+
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ const char *path, *unit, *result;
+ BusWaitForJobs *d = userdata;
+ uint32_t id;
+ char *found;
+ int r;
+
+ assert(m);
+ assert(d);
+
+ r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ found = set_remove(d->jobs, (char*) path);
+ if (!found)
+ return 0;
+
+ free(found);
+
+ if (!isempty(result))
+ d->result = strdup(result);
+
+ if (!isempty(unit))
+ d->name = strdup(unit);
+
+ return 0;
+}
+
+void bus_wait_for_jobs_free(BusWaitForJobs *d) {
+ if (!d)
+ return;
+
+ set_free_free(d->jobs);
+
+ sd_bus_slot_unref(d->slot_disconnected);
+ sd_bus_slot_unref(d->slot_job_removed);
+
+ sd_bus_unref(d->bus);
+
+ free(d->name);
+ free(d->result);
+
+ free(d);
+}
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
+ int r;
+
+ assert(bus);
+ assert(ret);
+
+ d = new0(BusWaitForJobs, 1);
+ if (!d)
+ return -ENOMEM;
+
+ d->bus = sd_bus_ref(bus);
+
+ /* When we are a bus client we match by sender. Direct
+ * connections OTOH have no initialized sender field, and
+ * hence we ignore the sender then */
+ r = sd_bus_add_match(
+ bus,
+ &d->slot_job_removed,
+ bus->bus_client ?
+ "type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='JobRemoved',"
+ "path='/org/freedesktop/systemd1'" :
+ "type='signal',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='JobRemoved',"
+ "path='/org/freedesktop/systemd1'",
+ match_job_removed, d);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(
+ bus,
+ &d->slot_disconnected,
+ "type='signal',"
+ "sender='org.freedesktop.DBus.Local',"
+ "interface='org.freedesktop.DBus.Local',"
+ "member='Disconnected'",
+ match_disconnected, d);
+ if (r < 0)
+ return r;
+
+ *ret = d;
+ d = NULL;
+
+ return 0;
+}
+
+static int bus_process_wait(sd_bus *bus) {
+ int r;
+
+ for (;;) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
+ _cleanup_free_ char *dbus_path = NULL;
+
+ assert(d);
+ assert(d->name);
+ assert(result);
+
+ dbus_path = unit_dbus_path_from_name(d->name);
+ if (!dbus_path)
+ return -ENOMEM;
+
+ return sd_bus_get_property_string(d->bus,
+ "org.freedesktop.systemd1",
+ dbus_path,
+ "org.freedesktop.systemd1.Service",
+ "Result",
+ NULL,
+ result);
+}
+
+static const struct {
+ const char *result, *explanation;
+} explanations [] = {
+ { "resources", "of unavailable resources or another system error" },
+ { "timeout", "a timeout was exceeded" },
+ { "exit-code", "the control process exited with error code" },
+ { "signal", "a fatal signal was delivered to the control process" },
+ { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
+ { "watchdog", "the service failed to send watchdog ping" },
+ { "start-limit", "start of the service was attempted too often" }
+};
+
+static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
+ _cleanup_free_ char *service_shell_quoted = NULL;
+ const char *systemctl = "systemctl", *journalctl = "journalctl";
+
+ assert(service);
+
+ service_shell_quoted = shell_maybe_quote(service);
+
+ if (extra_args && extra_args[1]) {
+ _cleanup_free_ char *t;
+
+ t = strv_join((char**) extra_args, " ");
+ systemctl = strjoina("systemctl ", t ? : "<args>");
+ journalctl = strjoina("journalctl ", t ? : "<args>");
+ }
+
+ if (!isempty(result)) {
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(explanations); ++i)
+ if (streq(result, explanations[i].result))
+ break;
+
+ if (i < ELEMENTSOF(explanations)) {
+ log_error("Job for %s failed because %s.\n"
+ "See \"%s status %s\" and \"%s -xe\" for details.\n",
+ service,
+ explanations[i].explanation,
+ systemctl,
+ service_shell_quoted ?: "<service>",
+ journalctl);
+ goto finish;
+ }
+ }
+
+ log_error("Job for %s failed.\n"
+ "See \"%s status %s\" and \"%s -xe\" for details.\n",
+ service,
+ systemctl,
+ service_shell_quoted ?: "<service>",
+ journalctl);
+
+finish:
+ /* For some results maybe additional explanation is required */
+ if (streq_ptr(result, "start-limit"))
+ log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
+ "followed by \"%1$s start %2$s\" again.",
+ systemctl,
+ service_shell_quoted ?: "<service>");
+}
+
+static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+ int r = 0;
+
+ assert(d->result);
+
+ if (!quiet) {
+ if (streq(d->result, "canceled"))
+ log_error("Job for %s canceled.", strna(d->name));
+ else if (streq(d->result, "timeout"))
+ log_error("Job for %s timed out.", strna(d->name));
+ else if (streq(d->result, "dependency"))
+ log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+ else if (streq(d->result, "invalid"))
+ log_error("%s is not active, cannot reload.", strna(d->name));
+ else if (streq(d->result, "assert"))
+ log_error("Assertion failed on job for %s.", strna(d->name));
+ else if (streq(d->result, "unsupported"))
+ log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+ else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
+ if (d->name) {
+ int q;
+ _cleanup_free_ char *result = NULL;
+
+ q = bus_job_get_service_result(d, &result);
+ if (q < 0)
+ log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
+
+ log_job_error_with_service_result(d->name, result, extra_args);
+ } else
+ log_error("Job failed. See \"journalctl -xe\" for details.");
+ }
+ }
+
+ if (streq(d->result, "canceled"))
+ r = -ECANCELED;
+ else if (streq(d->result, "timeout"))
+ r = -ETIME;
+ else if (streq(d->result, "dependency"))
+ r = -EIO;
+ else if (streq(d->result, "invalid"))
+ r = -ENOEXEC;
+ else if (streq(d->result, "assert"))
+ r = -EPROTO;
+ else if (streq(d->result, "unsupported"))
+ r = -EOPNOTSUPP;
+ else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+ r = -EIO;
+
+ return r;
+}
+
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+ int r = 0;
+
+ assert(d);
+
+ while (!set_isempty(d->jobs)) {
+ int q;
+
+ q = bus_process_wait(d->bus);
+ if (q < 0)
+ return log_error_errno(q, "Failed to wait for response: %m");
+
+ if (d->result) {
+ q = check_wait_response(d, quiet, extra_args);
+ /* Return the first error as it is most likely to be
+ * meaningful. */
+ if (q < 0 && r == 0)
+ r = q;
+
+ log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
+ }
+
+ d->name = mfree(d->name);
+ d->result = mfree(d->result);
+ }
+
+ return r;
+}
+
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
+ int r;
+
+ assert(d);
+
+ r = set_ensure_allocated(&d->jobs, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ return set_put_strdup(d->jobs, path);
+}
+
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
+ int r;
+
+ r = bus_wait_for_jobs_add(d, path);
+ if (r < 0)
+ return log_oom();
+
+ return bus_wait_for_jobs(d, quiet, NULL);
+}
+
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
+ const char *type, *path, *source;
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
+ /* We expect only "success" changes to be sent over the bus.
+ Hence, reject anything negative. */
+ UnitFileChangeType ch = unit_file_change_type_from_string(type);
+
+ if (ch < 0) {
+ log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
+ continue;
+ }
+
+ r = unit_file_changes_add(changes, n_changes, ch, path, source);
+ if (r < 0)
+ return r;
+ }
+ 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);
+
+ unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
+ return 0;
+}
+
+struct CGroupInfo {
+ char *cgroup_path;
+ bool is_const; /* If false, cgroup_path should be free()'d */
+
+ Hashmap *pids; /* PID → process name */
+ bool done;
+
+ struct CGroupInfo *parent;
+ LIST_FIELDS(struct CGroupInfo, siblings);
+ LIST_HEAD(struct CGroupInfo, children);
+ size_t n_children;
+};
+
+static bool IS_ROOT(const char *p) {
+ return isempty(p) || streq(p, "/");
+}
+
+static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
+ struct CGroupInfo *parent = NULL, *cg;
+ int r;
+
+ assert(cgroups);
+ assert(ret);
+
+ if (IS_ROOT(path))
+ path = "/";
+
+ cg = hashmap_get(cgroups, path);
+ if (cg) {
+ *ret = cg;
+ return 0;
+ }
+
+ if (!IS_ROOT(path)) {
+ const char *e, *pp;
+
+ e = strrchr(path, '/');
+ if (!e)
+ return -EINVAL;
+
+ pp = strndupa(path, e - path);
+ if (!pp)
+ return -ENOMEM;
+
+ r = add_cgroup(cgroups, pp, false, &parent);
+ if (r < 0)
+ return r;
+ }
+
+ cg = new0(struct CGroupInfo, 1);
+ if (!cg)
+ return -ENOMEM;
+
+ if (is_const)
+ cg->cgroup_path = (char*) path;
+ else {
+ cg->cgroup_path = strdup(path);
+ if (!cg->cgroup_path) {
+ free(cg);
+ return -ENOMEM;
+ }
+ }
+
+ cg->is_const = is_const;
+ cg->parent = parent;
+
+ r = hashmap_put(cgroups, cg->cgroup_path, cg);
+ if (r < 0) {
+ if (!is_const)
+ free(cg->cgroup_path);
+ free(cg);
+ return r;
+ }
+
+ if (parent) {
+ LIST_PREPEND(siblings, parent->children, cg);
+ parent->n_children++;
+ }
+
+ *ret = cg;
+ return 1;
+}
+
+static int add_process(
+ Hashmap *cgroups,
+ const char *path,
+ pid_t pid,
+ const char *name) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(cgroups);
+ assert(name);
+ assert(pid > 0);
+
+ r = add_cgroup(cgroups, path, true, &cg);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
+}
+
+static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
+ assert(cgroups);
+ assert(cg);
+
+ while (cg->children)
+ remove_cgroup(cgroups, cg->children);
+
+ hashmap_remove(cgroups, cg->cgroup_path);
+
+ if (!cg->is_const)
+ free(cg->cgroup_path);
+
+ hashmap_free(cg->pids);
+
+ if (cg->parent)
+ LIST_REMOVE(siblings, cg->parent->children, cg);
+
+ free(cg);
+}
+
+static int cgroup_info_compare_func(const void *a, const void *b) {
+ const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
+
+ assert(x);
+ assert(y);
+
+ return strcmp(x->cgroup_path, y->cgroup_path);
+}
+
+static int dump_processes(
+ Hashmap *cgroups,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(prefix);
+
+ if (IS_ROOT(cgroup_path))
+ cgroup_path = "/";
+
+ cg = hashmap_get(cgroups, cgroup_path);
+ if (!cg)
+ return 0;
+
+ if (!hashmap_isempty(cg->pids)) {
+ const char *name;
+ size_t n = 0, i;
+ pid_t *pids;
+ void *pidp;
+ Iterator j;
+ int width;
+
+ /* Order processes by their PID */
+ pids = newa(pid_t, hashmap_size(cg->pids));
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
+ pids[n++] = PTR_TO_PID(pidp);
+
+ assert(n == hashmap_size(cg->pids));
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *e = NULL;
+ const char *special;
+ bool more;
+
+ name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned k;
+
+ k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, k, 100);
+ if (e)
+ name = e;
+ }
+
+ more = i+1 < n || cg->children;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
+ prefix,
+ special,
+ width, pids[i],
+ name);
+ }
+ }
+
+ if (cg->children) {
+ struct CGroupInfo **children, *child;
+ size_t n = 0, i;
+
+ /* Order subcgroups by their name */
+ children = newa(struct CGroupInfo*, cg->n_children);
+ LIST_FOREACH(siblings, child, cg->children)
+ children[n++] = child;
+ assert(n == cg->n_children);
+ qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
+
+ n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *pp = NULL;
+ const char *name, *special;
+ bool more;
+
+ child = children[i];
+
+ name = strrchr(child->cgroup_path, '/');
+ if (!name)
+ return -EINVAL;
+ name++;
+
+ more = i+1 < n;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fputs(prefix, stdout);
+ fputs(special, stdout);
+ fputs(name, stdout);
+ fputc('\n', stdout);
+
+ special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
+
+ pp = strappend(prefix, special);
+ if (!pp)
+ return -ENOMEM;
+
+ r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ cg->done = true;
+ return 0;
+}
+
+static int dump_extra_processes(
+ Hashmap *cgroups,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ _cleanup_free_ pid_t *pids = NULL;
+ _cleanup_hashmap_free_ Hashmap *names = NULL;
+ struct CGroupInfo *cg;
+ size_t n_allocated = 0, n = 0, k;
+ Iterator i;
+ int width, r;
+
+ /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
+ * combined, sorted, linear list. */
+
+ HASHMAP_FOREACH(cg, cgroups, i) {
+ const char *name;
+ void *pidp;
+ Iterator j;
+
+ if (cg->done)
+ continue;
+
+ if (hashmap_isempty(cg->pids))
+ continue;
+
+ r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
+ return -ENOMEM;
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
+ pids[n++] = PTR_TO_PID(pidp);
+
+ r = hashmap_put(names, pidp, (void*) name);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (n == 0)
+ return 0;
+
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (k = 0; k < n; k++) {
+ _cleanup_free_ char *e = NULL;
+ const char *name;
+
+ name = hashmap_get(names, PID_TO_PTR(pids[k]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned z;
+
+ z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, z, 100);
+ if (e)
+ name = e;
+ }
+
+ fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
+ prefix,
+ draw_special_char(DRAW_TRIANGULAR_BULLET),
+ width, pids[k],
+ name);
+ }
+
+ return 0;
+}
+
+int unit_show_processes(
+ sd_bus *bus,
+ const char *unit,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags,
+ sd_bus_error *error) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Hashmap *cgroups = NULL;
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(bus);
+ assert(unit);
+
+ if (flags & OUTPUT_FULL_WIDTH)
+ n_columns = 0;
+ else if (n_columns <= 0)
+ n_columns = columns();
+
+ prefix = strempty(prefix);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitProcesses",
+ error,
+ &reply,
+ "s",
+ unit);
+ if (r < 0)
+ return r;
+
+ cgroups = hashmap_new(&string_hash_ops);
+ if (!cgroups)
+ return -ENOMEM;
+
+ r = sd_bus_message_enter_container(reply, 'a', "(sus)");
+ if (r < 0)
+ goto finish;
+
+ for (;;) {
+ const char *path = NULL, *name = NULL;
+ uint32_t pid;
+
+ r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ break;
+
+ r = add_process(cgroups, path, pid, name);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ goto finish;
+
+ r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
+ if (r < 0)
+ goto finish;
+
+ r = dump_extra_processes(cgroups, prefix, n_columns, flags);
+
+finish:
+ while ((cg = hashmap_first(cgroups)))
+ remove_cgroup(cgroups, cg);
+
+ hashmap_free(cgroups);
+
+ return r;
+}
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
new file mode 100644
index 0000000000..c0c172f336
--- /dev/null
+++ b/src/shared/bus-unit-util.h
@@ -0,0 +1,57 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 "sd-bus.h"
+
+#include "output-mode.h"
+#include "install.h"
+
+typedef struct UnitInfo {
+ const char *machine;
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+} UnitInfo;
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
+
+int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
+
+typedef struct BusWaitForJobs BusWaitForJobs;
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
+void bus_wait_for_jobs_free(BusWaitForJobs *d);
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
+
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
+
+int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error);
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 90b312a1a7..4efbf3710f 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -39,34 +39,16 @@
#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "cgroup-util.h"
#include "def.h"
-#include "env-util.h"
#include "escape.h"
-#include "extract-word.h"
#include "fd-util.h"
-#include "hashmap.h"
-#include "install.h"
-#include "kdbus.h"
-#include "log.h"
-#include "macro.h"
#include "missing.h"
#include "parse-util.h"
-#include "path-util.h"
#include "proc-cmdline.h"
-#include "process-util.h"
#include "rlimit-util.h"
-#include "set.h"
-#include "signal-util.h"
#include "stdio-util.h"
-#include "string-util.h"
#include "strv.h"
-#include "syslog-util.h"
-#include "time-util.h"
-#include "unit-name.h"
#include "user-util.h"
-#include "utf8.h"
-#include "util.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -712,7 +694,15 @@ int bus_connect_user_systemd(sd_bus **_bus) {
return 0;
}
-int bus_print_property(const char *name, sd_bus_message *property, bool all) {
+#define print_property(name, fmt, ...) \
+ do { \
+ if (value) \
+ printf(fmt "\n", __VA_ARGS__); \
+ else \
+ printf("%s=" fmt "\n", name, __VA_ARGS__); \
+ } while(0)
+
+int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) {
char type;
const char *contents;
int r;
@@ -740,7 +730,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (!escaped)
return -ENOMEM;
- printf("%s=%s\n", name, escaped);
+ print_property(name, "%s", escaped);
}
return 1;
@@ -753,7 +743,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- printf("%s=%s\n", name, yes_no(b));
+ print_property(name, "%s", yes_no(b));
return 1;
}
@@ -773,14 +763,14 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
t = format_timestamp(timestamp, sizeof(timestamp), u);
if (t || all)
- printf("%s=%s\n", name, strempty(t));
+ print_property(name, "%s", strempty(t));
} else if (strstr(name, "USec")) {
char timespan[FORMAT_TIMESPAN_MAX];
- printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
+ print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
} else
- printf("%s=%llu\n", name, (unsigned long long) u);
+ print_property(name, "%"PRIu64, u);
return 1;
}
@@ -792,7 +782,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- printf("%s=%lld\n", name, (long long) i);
+ print_property(name, "%"PRIi64, i);
return 1;
}
@@ -805,9 +795,9 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
return r;
if (strstr(name, "UMask") || strstr(name, "Mode"))
- printf("%s=%04o\n", name, u);
+ print_property(name, "%04o", u);
else
- printf("%s=%u\n", name, (unsigned) u);
+ print_property(name, "%"PRIu32, u);
return 1;
}
@@ -819,7 +809,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- printf("%s=%i\n", name, (int) i);
+ print_property(name, "%"PRIi32, i);
return 1;
}
@@ -830,7 +820,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- printf("%s=%g\n", name, d);
+ print_property(name, "%g", d);
return 1;
}
@@ -846,7 +836,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
_cleanup_free_ char *escaped = NULL;
- if (first)
+ if (first && !value)
printf("%s=", name);
escaped = xescape(str, "\n ");
@@ -860,7 +850,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- if (first && all)
+ if (first && all && !value)
printf("%s=", name);
if (!first || all)
puts("");
@@ -882,7 +872,8 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (all || n > 0) {
unsigned int i;
- printf("%s=", name);
+ if (!value)
+ printf("%s=", name);
for (i = 0; i < n; i++)
printf("%02x", u[i]);
@@ -903,7 +894,8 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (all || n > 0) {
unsigned int i;
- printf("%s=", name);
+ if (!value)
+ printf("%s=", name);
for (i = 0; i < n; i++)
printf("%08x", u[i]);
@@ -920,7 +912,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
return 0;
}
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
@@ -960,7 +952,7 @@ int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, ch
if (r < 0)
return r;
- r = bus_print_property(name, reply, all);
+ r = bus_print_property(name, reply, value, all);
if (r < 0)
return r;
if (r == 0) {
@@ -1068,7 +1060,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
}
case SD_BUS_TYPE_UINT32: {
- uint64_t u;
+ uint32_t u;
uint32_t *p = userdata;
r = sd_bus_message_read_basic(m, type, &u);
@@ -1373,844 +1365,6 @@ int bus_log_create_error(int r) {
return log_error_errno(r, "Failed to create bus message: %m");
}
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
- assert(message);
- assert(u);
-
- u->machine = NULL;
-
- return sd_bus_message_read(
- message,
- "(ssssssouso)",
- &u->id,
- &u->description,
- &u->load_state,
- &u->active_state,
- &u->sub_state,
- &u->following,
- &u->unit_path,
- &u->job_id,
- &u->job_type,
- &u->job_path);
-}
-
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
- const char *eq, *field;
- int r, rl;
-
- assert(m);
- assert(assignment);
-
- eq = strchr(assignment, '=');
- if (!eq) {
- log_error("Not an assignment: %s", assignment);
- return -EINVAL;
- }
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
- field = strndupa(assignment, eq - assignment);
- eq++;
-
- if (streq(field, "CPUQuota")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
- else if (endswith(eq, "%")) {
- double percent;
-
- if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
- log_error("CPU quota '%s' invalid.", eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
- } else {
- log_error("CPU quota needs to be in percent.");
- return -EINVAL;
- }
-
- goto finish;
-
- } else if (streq(field, "EnvironmentFile")) {
-
- r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
- eq[0] == '-' ? eq + 1 : eq,
- eq[0] == '-');
- goto finish;
-
- } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
- char *n;
- usec_t t;
- size_t l;
- r = parse_sec(eq, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
-
- l = strlen(field);
- n = newa(char, l + 2);
- if (!n)
- return log_oom();
-
- /* Change suffix Sec → USec */
- strcpy(mempcpy(n, field, l - 3), "USec");
- r = sd_bus_message_append(m, "sv", n, "t", t);
- goto finish;
- }
-
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
- if (r < 0)
- return bus_log_create_error(r);
-
- rl = rlimit_from_string(field);
- if (rl >= 0) {
- const char *sn;
- struct rlimit l;
-
- r = rlimit_parse(rl, eq, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resource limit: %s", eq);
-
- r = sd_bus_message_append(m, "v", "t", l.rlim_max);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
- sn = strjoina(field, "Soft");
- r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
-
- } else if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
- "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
- "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
-
- r = parse_boolean(eq);
- if (r < 0)
- return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
-
- r = sd_bus_message_append(m, "v", "b", r);
-
- } else if (streq(field, "MemoryLimit")) {
- uint64_t bytes;
-
- if (isempty(eq) || streq(eq, "infinity"))
- bytes = (uint64_t) -1;
- else {
- r = parse_size(eq, 1024, &bytes);
- if (r < 0) {
- log_error("Failed to parse bytes specification %s", assignment);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", bytes);
-
- } else if (streq(field, "TasksMax")) {
- uint64_t n;
-
- if (isempty(eq) || streq(eq, "infinity"))
- n = (uint64_t) -1;
- else {
- r = safe_atou64(eq, &n);
- if (r < 0) {
- log_error("Failed to parse maximum tasks specification %s", assignment);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", n);
-
- } 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 = 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,
- "User", "Group", "DevicePolicy", "KillMode",
- "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
- "StandardInput", "StandardOutput", "StandardError",
- "Description", "Slice", "Type", "WorkingDirectory",
- "RootDirectory", "SyslogIdentifier", "ProtectSystem",
- "ProtectHome"))
- r = sd_bus_message_append(m, "v", "s", eq);
-
- else if (streq(field, "SyslogLevel")) {
- int level;
-
- level = log_level_from_string(eq);
- if (level < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", level);
-
- } else if (streq(field, "SyslogFacility")) {
- int facility;
-
- facility = log_facility_unshifted_from_string(eq);
- if (facility < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", facility);
-
- } else if (streq(field, "DeviceAllow")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(ss)", 0);
- else {
- const char *path, *rwm, *e;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- rwm = e+1;
- } else {
- path = eq;
- rwm = "";
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
- }
-
- } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
- else {
- const char *path, *bandwidth, *e;
- uint64_t bytes;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- bandwidth = e+1;
- } else {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = parse_size(bandwidth, 1000, &bytes);
- if (r < 0) {
- log_error("Failed to parse byte value %s.", bandwidth);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
- }
-
- } else if (streq(field, "BlockIODeviceWeight")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
- else {
- const char *path, *weight, *e;
- uint64_t u;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- weight = e+1;
- } else {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = safe_atou64(weight, &u);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, weight);
- return -EINVAL;
- }
- r = sd_bus_message_append(m, "v", "a(st)", path, u);
- }
-
- } else if (streq(field, "Nice")) {
- int32_t i;
-
- r = safe_atoi32(eq, &i);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", i);
-
- } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
- if (r < 0) {
- log_error("Failed to parse Environment value %s", eq);
- return -EINVAL;
- }
- if (r == 0)
- break;
-
- if (streq(field, "Environment")) {
- if (!env_assignment_is_valid(word)) {
- log_error("Invalid environment assignment: %s", word);
- return -EINVAL;
- }
- } else { /* PassEnvironment */
- if (!env_name_is_valid(word)) {
- log_error("Invalid environment variable name: %s", word);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else if (streq(field, "KillSignal")) {
- int sig;
-
- sig = signal_from_string_try_harder(eq);
- if (sig < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", sig);
-
- } else if (streq(field, "TimerSlackNSec")) {
- nsec_t n;
-
- r = parse_nsec(eq, &n);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "t", n);
- } else if (streq(field, "OOMScoreAdjust")) {
- int oa;
-
- r = safe_atoi(eq, &oa);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- if (!oom_score_adjust_is_valid(oa)) {
- log_error("OOM score adjust value out of range");
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", oa);
- } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
- int offset;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
- if (r == 0)
- break;
-
- if (!utf8_is_valid(word)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- offset = word[0] == '-';
- if (!path_is_absolute(word + offset)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- path_kill_slashes(word + offset);
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else if (streq(field, "RuntimeDirectory")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s", field, eq);
-
- if (r == 0)
- break;
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else {
- log_error("Unknown assignment %s.", assignment);
- return -EINVAL;
- }
-
-finish:
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- return 0;
-}
-
-typedef struct BusWaitForJobs {
- sd_bus *bus;
- Set *jobs;
-
- char *name;
- char *result;
-
- sd_bus_slot *slot_job_removed;
- sd_bus_slot *slot_disconnected;
-} BusWaitForJobs;
-
-static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- assert(m);
-
- log_error("Warning! D-Bus connection terminated.");
- sd_bus_close(sd_bus_message_get_bus(m));
-
- return 0;
-}
-
-static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char *path, *unit, *result;
- BusWaitForJobs *d = userdata;
- uint32_t id;
- char *found;
- int r;
-
- assert(m);
- assert(d);
-
- r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
-
- found = set_remove(d->jobs, (char*) path);
- if (!found)
- return 0;
-
- free(found);
-
- if (!isempty(result))
- d->result = strdup(result);
-
- if (!isempty(unit))
- d->name = strdup(unit);
-
- return 0;
-}
-
-void bus_wait_for_jobs_free(BusWaitForJobs *d) {
- if (!d)
- return;
-
- set_free_free(d->jobs);
-
- sd_bus_slot_unref(d->slot_disconnected);
- sd_bus_slot_unref(d->slot_job_removed);
-
- sd_bus_unref(d->bus);
-
- free(d->name);
- free(d->result);
-
- free(d);
-}
-
-int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
- _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
- int r;
-
- assert(bus);
- assert(ret);
-
- d = new0(BusWaitForJobs, 1);
- if (!d)
- return -ENOMEM;
-
- d->bus = sd_bus_ref(bus);
-
- /* When we are a bus client we match by sender. Direct
- * connections OTOH have no initialized sender field, and
- * hence we ignore the sender then */
- r = sd_bus_add_match(
- bus,
- &d->slot_job_removed,
- bus->bus_client ?
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'" :
- "type='signal',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_job_removed, d);
- if (r < 0)
- return r;
-
- r = sd_bus_add_match(
- bus,
- &d->slot_disconnected,
- "type='signal',"
- "sender='org.freedesktop.DBus.Local',"
- "interface='org.freedesktop.DBus.Local',"
- "member='Disconnected'",
- match_disconnected, d);
- if (r < 0)
- return r;
-
- *ret = d;
- d = NULL;
-
- return 0;
-}
-
-static int bus_process_wait(sd_bus *bus) {
- int r;
-
- for (;;) {
- r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
- _cleanup_free_ char *dbus_path = NULL;
-
- assert(d);
- assert(d->name);
- assert(result);
-
- dbus_path = unit_dbus_path_from_name(d->name);
- if (!dbus_path)
- return -ENOMEM;
-
- return sd_bus_get_property_string(d->bus,
- "org.freedesktop.systemd1",
- dbus_path,
- "org.freedesktop.systemd1.Service",
- "Result",
- NULL,
- result);
-}
-
-static const struct {
- const char *result, *explanation;
-} explanations [] = {
- { "resources", "a configured resource limit was exceeded" },
- { "timeout", "a timeout was exceeded" },
- { "exit-code", "the control process exited with error code" },
- { "signal", "a fatal signal was delivered to the control process" },
- { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
- { "watchdog", "the service failed to send watchdog ping" },
- { "start-limit", "start of the service was attempted too often" }
-};
-
-static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
- _cleanup_free_ char *service_shell_quoted = NULL;
- const char *systemctl = "systemctl", *journalctl = "journalctl";
-
- assert(service);
-
- service_shell_quoted = shell_maybe_quote(service);
-
- if (extra_args && extra_args[1]) {
- _cleanup_free_ char *t;
-
- t = strv_join((char**) extra_args, " ");
- systemctl = strjoina("systemctl ", t ?: "<args>", NULL);
- journalctl = strjoina("journalctl ", t ?: "<args>", NULL);
- }
-
- if (!isempty(result)) {
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(explanations); ++i)
- if (streq(result, explanations[i].result))
- break;
-
- if (i < ELEMENTSOF(explanations)) {
- log_error("Job for %s failed because %s.\n"
- "See \"%s status %s\" and \"%s -xe\" for details.\n",
- service,
- explanations[i].explanation,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl);
- goto finish;
- }
- }
-
- log_error("Job for %s failed.\n"
- "See \"%s status %s\" and \"%s -xe\" for details.\n",
- service,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl);
-
-finish:
- /* For some results maybe additional explanation is required */
- if (streq_ptr(result, "start-limit"))
- log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
- "followed by \"%1$s start %2$s\" again.",
- systemctl,
- service_shell_quoted ?: "<service>");
-}
-
-static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
- int r = 0;
-
- assert(d->result);
-
- if (!quiet) {
- if (streq(d->result, "canceled"))
- log_error("Job for %s canceled.", strna(d->name));
- else if (streq(d->result, "timeout"))
- log_error("Job for %s timed out.", strna(d->name));
- else if (streq(d->result, "dependency"))
- log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
- else if (streq(d->result, "invalid"))
- log_error("%s is not active, cannot reload.", strna(d->name));
- else if (streq(d->result, "assert"))
- log_error("Assertion failed on job for %s.", strna(d->name));
- else if (streq(d->result, "unsupported"))
- log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
- else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
- if (d->name) {
- int q;
- _cleanup_free_ char *result = NULL;
-
- q = bus_job_get_service_result(d, &result);
- if (q < 0)
- log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
-
- log_job_error_with_service_result(d->name, result, extra_args);
- } else
- log_error("Job failed. See \"journalctl -xe\" for details.");
- }
- }
-
- if (streq(d->result, "canceled"))
- r = -ECANCELED;
- else if (streq(d->result, "timeout"))
- r = -ETIME;
- else if (streq(d->result, "dependency"))
- r = -EIO;
- else if (streq(d->result, "invalid"))
- r = -ENOEXEC;
- else if (streq(d->result, "assert"))
- r = -EPROTO;
- else if (streq(d->result, "unsupported"))
- r = -EOPNOTSUPP;
- else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
- r = -EIO;
-
- return r;
-}
-
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
- int r = 0;
-
- assert(d);
-
- while (!set_isempty(d->jobs)) {
- int q;
-
- q = bus_process_wait(d->bus);
- if (q < 0)
- return log_error_errno(q, "Failed to wait for response: %m");
-
- if (d->result) {
- q = check_wait_response(d, quiet, extra_args);
- /* Return the first error as it is most likely to be
- * meaningful. */
- if (q < 0 && r == 0)
- r = q;
-
- log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
- }
-
- d->name = mfree(d->name);
- d->result = mfree(d->result);
- }
-
- return r;
-}
-
-int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
- int r;
-
- assert(d);
-
- r = set_ensure_allocated(&d->jobs, &string_hash_ops);
- if (r < 0)
- return r;
-
- return set_put_strdup(d->jobs, path);
-}
-
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
- int r;
-
- r = bus_wait_for_jobs_add(d, path);
- if (r < 0)
- return log_oom();
-
- return bus_wait_for_jobs(d, quiet, NULL);
-}
-
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
- const char *type, *path, *source;
- int r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
- if (!quiet) {
- if (streq(type, "symlink"))
- log_info("Created symlink from %s to %s.", path, source);
- else
- log_info("Removed symlink %s.", path);
- }
-
- r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source);
- if (r < 0)
- return r;
- }
- 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;
-}
-
/**
* bus_path_encode_unique() - encode unique object path
* @b: bus connection or NULL
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index fcda1b2c6c..d792258ecd 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -24,15 +24,12 @@
#include <stdint.h>
#include <sys/types.h>
-#include "sd-bus-vtable.h"
#include "sd-bus.h"
#include "sd-event.h"
#include "hashmap.h"
-#include "install.h"
#include "macro.h"
#include "string-util.h"
-#include "time-util.h"
typedef enum BusTransport {
BUS_TRANSPORT_LOCAL,
@@ -78,8 +75,8 @@ int bus_connect_user_systemd(sd_bus **_bus);
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
-int bus_print_property(const char *name, sd_bus_message *property, bool all);
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
+int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all);
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all);
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
@@ -126,22 +123,6 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t));
int bus_log_parse_error(int r);
int bus_log_create_error(int r);
-typedef struct UnitInfo {
- const char *machine;
- const char *id;
- const char *description;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *following;
- const char *unit_path;
- uint32_t job_id;
- const char *job_type;
- const char *job_path;
-} UnitInfo;
-
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
-
#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
int function(sd_bus *bus, \
const char *path, \
@@ -173,20 +154,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
-
-typedef struct BusWaitForJobs BusWaitForJobs;
-
-int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
-void bus_wait_for_jobs_free(BusWaitForJobs *d);
-int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
-
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
-
int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);
int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index f3039b23f7..7539891bf2 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -37,23 +37,21 @@
#include "string-util.h"
#include "terminal-util.h"
-static int compare(const void *a, const void *b) {
- const pid_t *p = a, *q = b;
+static void show_pid_array(
+ pid_t pids[],
+ unsigned n_pids,
+ const char *prefix,
+ unsigned n_columns,
+ bool extra,
+ bool more,
+ OutputFlags flags) {
- if (*p < *q)
- return -1;
- if (*p > *q)
- return 1;
- return 0;
-}
-
-static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
unsigned i, j, pid_width;
if (n_pids == 0)
return;
- qsort(pids, n_pids, sizeof(pid_t), compare);
+ qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);
/* Filter duplicates */
for (j = 0, i = 1; i < n_pids; i++) {
@@ -86,8 +84,13 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un
}
}
+static int show_cgroup_one_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool more,
+ OutputFlags flags) {
-static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
char *fn;
_cleanup_fclose_ FILE *f = NULL;
size_t n = 0, n_allocated = 0;
@@ -107,7 +110,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
while ((r = cg_read_pid(f, &pid)) > 0) {
- if (!kernel_threads && is_kernel_thread(pid) > 0)
+ if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0)
continue;
if (!GREEDY_REALLOC(pids, n_allocated, n + 1))
@@ -120,12 +123,17 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
if (r < 0)
return r;
- show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
+ show_pid_array(pids, n, prefix, n_columns, false, more, flags);
return 0;
}
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
_cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
_cleanup_closedir_ DIR *d = NULL;
char *gn = NULL;
@@ -137,8 +145,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (n_columns <= 0)
n_columns = columns();
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
r = cg_mangle_path(path, &fn);
if (r < 0)
@@ -160,7 +167,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
continue;
if (!shown_pids) {
- show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
+ show_cgroup_one_by_path(path, prefix, n_columns, true, flags);
shown_pids = true;
}
@@ -173,7 +180,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return -ENOMEM;
}
- show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
+ show_cgroup_by_path(last, p1, n_columns-2, flags);
free(last);
}
@@ -185,7 +192,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return r;
if (!shown_pids)
- show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
+ show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
if (last) {
printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last)));
@@ -196,13 +203,17 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return -ENOMEM;
}
- show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
+ show_cgroup_by_path(last, p2, n_columns-2, flags);
}
return 0;
}
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup(const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
_cleanup_free_ char *p = NULL;
int r;
@@ -212,10 +223,18 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un
if (r < 0)
return r;
- return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
+ return show_cgroup_by_path(p, prefix, n_columns, flags);
}
-static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
+static int show_extra_pids(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t pids[],
+ unsigned n_pids,
+ OutputFlags flags) {
+
_cleanup_free_ pid_t *copy = NULL;
unsigned i, j;
int r;
@@ -247,24 +266,39 @@ static int show_extra_pids(const char *controller, const char *path, const char
copy[j++] = pids[i];
}
- show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
+ show_pid_array(copy, j, prefix, n_columns, true, false, flags);
return 0;
}
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
int r;
assert(path);
- r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
+ r = show_cgroup(controller, path, prefix, n_columns, flags);
if (r < 0)
return r;
return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra_by_spec(
+ const char *spec,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
_cleanup_free_ char *controller = NULL, *path = NULL;
int r;
@@ -274,5 +308,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned
if (r < 0)
return r;
- return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
+ return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 3ab7dfb33c..5c1d6e6d98 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -25,8 +25,8 @@
#include "logs-show.h"
#include "output-mode.h"
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
+int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags);
+int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags);
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index bd0a1f483b..1141f9964f 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -732,7 +732,7 @@ int config_parse_strv(const char *unit,
for (;;) {
char *word = NULL;
int r;
- r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
if (r == 0)
break;
if (r == -ENOMEM)
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index cc1acd6f23..b9cd952ac8 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -160,7 +160,7 @@ static int iterate_dir(
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
f = strjoin(path, "/", de->d_name, NULL);
diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c
index 0d3da2e6d2..ade2de7727 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -17,14 +17,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#warning "Temporary work-around for broken glibc vs. linux kernel header definitions"
+#warning "This really should be removed sooner rather than later, when this is fixed upstream"
+#define _NET_IF_H 1
+
#include <alloca.h>
#include <arpa/inet.h>
#include <endian.h>
#include <errno.h>
-#include <net/if.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
+#include <net/if.h>
+#include <linux/if.h>
+#ifndef IFNAMSIZ
+#undef _NET_IF_H
+/* Let's make sure to include this one, too, if IFNAMSIZ isn't defined yet, as it is for kernels <= 4.2 */
+#include <net/if.h>
+#endif
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter/xt_addrtype.h>
diff --git a/src/shared/install.c b/src/shared/install.c
index ef8f485cae..f02d81504f 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -45,6 +45,7 @@
#include "mkdir.h"
#include "path-lookup.h"
#include "path-util.h"
+#include "rm-rf.h"
#include "set.h"
#include "special.h"
#include "stat-util.h"
@@ -65,7 +66,28 @@ typedef struct {
OrderedHashmap *have_processed;
} InstallContext;
-static int in_search_path(const char *path, char **search) {
+static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
+
+bool unit_type_may_alias(UnitType type) {
+ return IN_SET(type,
+ UNIT_SERVICE,
+ UNIT_SOCKET,
+ UNIT_TARGET,
+ UNIT_DEVICE,
+ UNIT_TIMER,
+ UNIT_PATH);
+}
+
+bool unit_type_may_template(UnitType type) {
+ return IN_SET(type,
+ UNIT_SERVICE,
+ UNIT_SOCKET,
+ UNIT_TARGET,
+ UNIT_TIMER,
+ UNIT_PATH);
+}
+
+static int in_search_path(const LookupPaths *p, const char *path) {
_cleanup_free_ char *parent = NULL;
char **i;
@@ -75,141 +97,141 @@ static int in_search_path(const char *path, char **search) {
if (!parent)
return -ENOMEM;
- STRV_FOREACH(i, search)
+ STRV_FOREACH(i, p->search_path)
if (path_equal(parent, *i))
return true;
return false;
}
-static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
- char *p = NULL;
- int r;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
- assert(ret);
+static const char* skip_root(const LookupPaths *p, const char *path) {
+ char *e;
- /* This determines where we shall create or remove our
- * installation ("configuration") symlinks */
+ assert(p);
+ assert(path);
- switch (scope) {
+ if (!p->root_dir)
+ return path;
- case UNIT_FILE_SYSTEM:
+ e = path_startswith(path, p->root_dir);
+ if (!e)
+ return NULL;
- if (runtime)
- p = path_join(root_dir, "/run/systemd/system", NULL);
- else
- p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL);
- break;
+ /* Make sure the returned path starts with a slash */
+ if (e[0] != '/') {
+ if (e == path || e[-1] != '/')
+ return NULL;
- case UNIT_FILE_GLOBAL:
+ e--;
+ }
- if (root_dir)
- return -EINVAL;
+ return e;
+}
- if (runtime)
- p = strdup("/run/systemd/user");
- else
- p = strdup(USER_CONFIG_UNIT_PATH);
- break;
+static int path_is_generator(const LookupPaths *p, const char *path) {
+ _cleanup_free_ char *parent = NULL;
- case UNIT_FILE_USER:
+ assert(p);
+ assert(path);
- if (root_dir)
- return -EINVAL;
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- if (runtime)
- r = user_runtime_dir(&p);
- else
- r = user_config_home(&p);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
+ return path_equal_ptr(parent, p->generator) ||
+ path_equal_ptr(parent, p->generator_early) ||
+ path_equal_ptr(parent, p->generator_late);
+}
- break;
+static int path_is_transient(const LookupPaths *p, const char *path) {
+ _cleanup_free_ char *parent = NULL;
- default:
- assert_not_reached("Bad scope");
- }
+ assert(p);
+ assert(path);
- if (!p)
+ parent = dirname_malloc(path);
+ if (!parent)
return -ENOMEM;
- *ret = p;
- return 0;
+ return path_equal_ptr(parent, p->transient);
}
-static bool is_config_path(UnitFileScope scope, const char *path) {
- int r;
+static int path_is_control(const LookupPaths *p, const char *path) {
+ _cleanup_free_ char *parent = NULL;
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(p);
assert(path);
- /* Checks whether the specified path is intended for
- * configuration or is outside of it */
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- switch (scope) {
+ return path_equal_ptr(parent, p->persistent_control) ||
+ path_equal_ptr(parent, p->runtime_control);
+}
- case UNIT_FILE_SYSTEM:
- case UNIT_FILE_GLOBAL:
- return path_startswith(path, "/etc") ||
- path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) ||
- path_startswith(path, "/run");
+static int path_is_config(const LookupPaths *p, const char *path) {
+ _cleanup_free_ char *parent = NULL;
+ assert(p);
+ assert(path);
- case UNIT_FILE_USER: {
- _cleanup_free_ char *p = NULL;
+ /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern
+ * configuration from transient or generated units */
- r = user_config_home(&p);
- if (r < 0)
- return r;
- if (r > 0 && path_startswith(path, p))
- return true;
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- p = mfree(p);
+ return path_equal_ptr(parent, p->persistent_config) ||
+ path_equal_ptr(parent, p->runtime_config);
+}
- r = user_runtime_dir(&p);
- if (r < 0)
- return r;
- if (r > 0 && path_startswith(path, p))
- return true;
+static int path_is_runtime(const LookupPaths *p, const char *path) {
+ _cleanup_free_ char *parent = NULL;
+ const char *rpath;
- return false;
- }
+ assert(p);
+ assert(path);
- default:
- assert_not_reached("Bad scope");
- }
-}
+ /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime
+ * directories, as safety net. */
+ rpath = skip_root(p, path);
+ if (rpath && path_startswith(rpath, "/run"))
+ return true;
-static int verify_root_dir(UnitFileScope scope, const char **root_dir) {
- int r;
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- assert(root_dir);
+ return path_equal_ptr(parent, p->runtime_config) ||
+ path_equal_ptr(parent, p->generator) ||
+ path_equal_ptr(parent, p->generator_early) ||
+ path_equal_ptr(parent, p->generator_late) ||
+ path_equal_ptr(parent, p->transient) ||
+ path_equal_ptr(parent, p->runtime_control);
+}
- /* Verifies that the specified root directory to operate on
- * makes sense. Reset it to NULL if it is the root directory
- * or set to empty */
+static int path_is_vendor(const LookupPaths *p, const char *path) {
+ const char *rpath;
- if (isempty(*root_dir) || path_equal(*root_dir, "/")) {
- *root_dir = NULL;
+ assert(p);
+ assert(path);
+
+ rpath = skip_root(p, path);
+ if (!rpath)
return 0;
- }
- if (scope != UNIT_FILE_SYSTEM)
- return -EINVAL;
+ if (path_startswith(rpath, "/usr"))
+ return true;
- r = is_dir(*root_dir, true);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOTDIR;
+#ifdef HAVE_SPLIT_USR
+ if (path_startswith(rpath, "/lib"))
+ return true;
+#endif
- return 0;
+ return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
}
int unit_file_changes_add(
@@ -219,8 +241,8 @@ int unit_file_changes_add(
const char *path,
const char *source) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
UnitFileChange *c;
- unsigned i;
assert(path);
assert(!changes == !n_changes);
@@ -231,29 +253,22 @@ int unit_file_changes_add(
c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
if (!c)
return -ENOMEM;
-
*changes = c;
- i = *n_changes;
-
- c[i].type = type;
- c[i].path = strdup(path);
- if (!c[i].path)
- return -ENOMEM;
- path_kill_slashes(c[i].path);
+ p = strdup(path);
+ if (source)
+ s = strdup(source);
- if (source) {
- c[i].source = strdup(source);
- if (!c[i].source) {
- free(c[i].path);
- return -ENOMEM;
- }
+ if (!p || (source && !s))
+ return -ENOMEM;
- path_kill_slashes(c[i].path);
- } else
- c[i].source = NULL;
+ path_kill_slashes(p);
+ if (s)
+ path_kill_slashes(s);
- *n_changes = i+1;
+ c[*n_changes] = (UnitFileChange) { type, p, s };
+ p = s = NULL;
+ (*n_changes) ++;
return 0;
}
@@ -262,9 +277,6 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
assert(changes || n_changes == 0);
- if (!changes)
- return;
-
for (i = 0; i < n_changes; i++) {
free(changes[i].path);
free(changes[i].source);
@@ -273,6 +285,70 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
free(changes);
}
+void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) {
+ unsigned i;
+ bool logged = false;
+
+ assert(changes || n_changes == 0);
+ /* If verb is not specified, errors are not allowed! */
+ assert(verb || r >= 0);
+
+ for (i = 0; i < n_changes; i++) {
+ assert(verb || changes[i].type >= 0);
+
+ switch(changes[i].type) {
+ case UNIT_FILE_SYMLINK:
+ if (!quiet)
+ log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
+ break;
+ case UNIT_FILE_UNLINK:
+ if (!quiet)
+ log_info("Removed %s.", changes[i].path);
+ break;
+ case UNIT_FILE_IS_MASKED:
+ if (!quiet)
+ log_info("Unit %s is masked, ignoring.", changes[i].path);
+ break;
+ case -EEXIST:
+ if (changes[i].source)
+ log_error_errno(changes[i].type,
+ "Failed to %s unit, file %s already exists and is a symlink to %s.",
+ verb, changes[i].path, changes[i].source);
+ else
+ log_error_errno(changes[i].type,
+ "Failed to %s unit, file %s already exists.",
+ verb, changes[i].path);
+ logged = true;
+ break;
+ case -ERFKILL:
+ log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
+ verb, changes[i].path);
+ logged = true;
+ break;
+ case -EADDRNOTAVAIL:
+ log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
+ verb, changes[i].path);
+ logged = true;
+ break;
+ case -ELOOP:
+ log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s",
+ verb, changes[i].path);
+ logged = true;
+ break;
+ default:
+ assert(changes[i].type < 0);
+ log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
+ verb, changes[i].path);
+ logged = true;
+ }
+ }
+
+ if (r < 0 && !logged)
+ log_error_errno(r, "Failed to %s: %m.", verb);
+}
+
+
+
static int create_symlink(
const char *old_path,
const char *new_path,
@@ -294,30 +370,42 @@ static int create_symlink(
if (symlink(old_path, new_path) >= 0) {
unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
- return 0;
+ return 1;
}
- if (errno != EEXIST)
+ if (errno != EEXIST) {
+ unit_file_changes_add(changes, n_changes, -errno, new_path, NULL);
return -errno;
+ }
r = readlink_malloc(new_path, &dest);
- if (r < 0)
+ if (r < 0) {
+ /* translate EINVAL (non-symlink exists) to EEXIST */
+ if (r == -EINVAL)
+ r = -EEXIST;
+
+ unit_file_changes_add(changes, n_changes, r, new_path, NULL);
return r;
+ }
if (path_equal(dest, old_path))
return 0;
- if (!force)
+ if (!force) {
+ unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
return -EEXIST;
+ }
r = symlink_atomic(old_path, new_path);
- if (r < 0)
+ if (r < 0) {
+ unit_file_changes_add(changes, n_changes, r, new_path, NULL);
return r;
+ }
unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
- return 0;
+ return 1;
}
static int mark_symlink_for_removal(
@@ -353,6 +441,7 @@ static int remove_marked_symlinks_fd(
int fd,
const char *path,
const char *config_path,
+ const LookupPaths *lp,
bool *restart,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -365,6 +454,7 @@ static int remove_marked_symlinks_fd(
assert(fd >= 0);
assert(path);
assert(config_path);
+ assert(lp);
assert(restart);
d = fdopendir(fd);
@@ -400,12 +490,13 @@ static int remove_marked_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes);
+ q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, restart, changes, n_changes);
if (q < 0 && r == 0)
r = q;
} else if (de->d_type == DT_LNK) {
_cleanup_free_ char *p = NULL, *dest = NULL;
+ const char *rp;
bool found;
int q;
@@ -415,42 +506,43 @@ static int remove_marked_symlinks_fd(
p = path_make_absolute(de->d_name, path);
if (!p)
return -ENOMEM;
+ path_kill_slashes(p);
q = readlink_malloc(p, &dest);
+ if (q == -ENOENT)
+ continue;
if (q < 0) {
- if (q == -ENOENT)
- continue;
-
if (r == 0)
r = q;
continue;
}
- /* We remove all links pointing to a file or
- * path that is marked, as well as all files
- * sharing the same name as a file that is
- * marked. */
+ /* We remove all links pointing to a file or path that is marked, as well as all files sharing
+ * the same name as a file that is marked. */
- found =
- set_contains(remove_symlinks_to, dest) ||
+ found = set_contains(remove_symlinks_to, dest) ||
set_contains(remove_symlinks_to, basename(dest)) ||
set_contains(remove_symlinks_to, de->d_name);
if (!found)
continue;
- if (unlink(p) < 0 && errno != ENOENT) {
+ if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
if (r == 0)
r = -errno;
+ unit_file_changes_add(changes, n_changes, -errno, p, NULL);
continue;
}
- path_kill_slashes(p);
(void) rmdir_parents(p, config_path);
unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
- q = mark_symlink_for_removal(&remove_symlinks_to, p);
+ /* Now, remember the full path (but with the root prefix removed) of
+ * the symlink we just removed, and remove any symlinks to it, too. */
+
+ rp = skip_root(lp, p);
+ q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
if (q < 0)
return q;
if (q > 0)
@@ -464,6 +556,7 @@ static int remove_marked_symlinks_fd(
static int remove_marked_symlinks(
Set *remove_symlinks_to,
const char *config_path,
+ const LookupPaths *lp,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -472,6 +565,7 @@ static int remove_marked_symlinks(
int r = 0;
assert(config_path);
+ assert(lp);
if (set_size(remove_symlinks_to) <= 0)
return 0;
@@ -489,7 +583,7 @@ static int remove_marked_symlinks(
return -errno;
/* This takes possession of cfd and closes it */
- q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes);
+ q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, &restart, changes, n_changes);
if (r == 0)
r = q;
} while (restart);
@@ -503,6 +597,7 @@ static int find_symlinks_fd(
int fd,
const char *path,
const char *config_path,
+ const LookupPaths *lp,
bool *same_name_link) {
_cleanup_closedir_ DIR *d = NULL;
@@ -513,6 +608,7 @@ static int find_symlinks_fd(
assert(fd >= 0);
assert(path);
assert(config_path);
+ assert(lp);
assert(same_name_link);
d = fdopendir(fd);
@@ -546,7 +642,7 @@ static int find_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link);
+ q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link);
if (q > 0)
return 1;
if (r == 0)
@@ -624,6 +720,7 @@ static int find_symlinks(
const char *root_dir,
const char *name,
const char *config_path,
+ const LookupPaths *lp,
bool *same_name_link) {
int fd;
@@ -640,29 +737,25 @@ static int find_symlinks(
}
/* This takes possession of fd and closes it */
- return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link);
+ return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link);
}
static int find_symlinks_in_scope(
UnitFileScope scope,
- const char *root_dir,
+ const LookupPaths *paths,
const char *name,
UnitFileState *state) {
- _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
bool same_name_link_runtime = false, same_name_link = false;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(paths);
assert(name);
- /* First look in the normal config path */
- r = get_config_path(scope, false, root_dir, &normal_path);
- if (r < 0)
- return r;
-
- r = find_symlinks(root_dir, name, normal_path, &same_name_link);
+ /* First look in the persistent config path */
+ r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link);
if (r < 0)
return r;
if (r > 0) {
@@ -671,11 +764,7 @@ static int find_symlinks_in_scope(
}
/* Then look in runtime config path */
- r = get_config_path(scope, true, root_dir, &runtime_path);
- if (r < 0)
- return r;
-
- r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime);
+ r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime);
if (r < 0)
return r;
if (r > 0) {
@@ -742,6 +831,30 @@ static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *nam
return ordered_hashmap_get(c->will_process, name);
}
+static int install_info_may_process(
+ UnitFileInstallInfo *i,
+ const LookupPaths *paths,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+ assert(i);
+ assert(paths);
+
+ /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus
+ * not subject to enable/disable operations. */
+
+ if (i->type == UNIT_FILE_TYPE_MASKED) {
+ unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
+ return -ERFKILL;
+ }
+ if (path_is_generator(paths, i->path) ||
+ path_is_transient(paths, i->path)) {
+ unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
+ return -EADDRNOTAVAIL;
+ }
+
+ return 0;
+}
+
static int install_info_add(
InstallContext *c,
const char *name,
@@ -804,18 +917,34 @@ fail:
return r;
}
-static int install_info_add_auto(
- InstallContext *c,
- const char *name_or_path,
- UnitFileInstallInfo **ret) {
+static int config_parse_alias(
+ 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) {
- assert(c);
- assert(name_or_path);
+ const char *name;
+ UnitType type;
- if (path_is_absolute(name_or_path))
- return install_info_add(c, NULL, name_or_path, ret);
- else
- return install_info_add(c, name_or_path, NULL, ret);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ name = basename(filename);
+ type = unit_name_to_type(name);
+ if (!unit_type_may_alias(type))
+ return log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Aliases are not allowed for %s units, ignoring.",
+ unit_type_to_string(type));
+
+ return config_parse_strv(unit, filename, line, section, section_line,
+ lvalue, ltype, rvalue, data, userdata);
}
static int config_parse_also(
@@ -874,6 +1003,7 @@ static int config_parse_default_instance(
void *userdata) {
UnitFileInstallInfo *i = data;
+ const char *name;
char *printed;
int r;
@@ -881,6 +1011,15 @@ static int config_parse_default_instance(
assert(lvalue);
assert(rvalue);
+ name = basename(filename);
+ if (unit_name_is_valid(name, UNIT_NAME_INSTANCE))
+ /* When enabling an instance, we might be using a template unit file,
+ * but we should ignore DefaultInstance silently. */
+ return 0;
+ if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
+ return log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "DefaultInstance only makes sense for template units, ignoring.");
+
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
return r;
@@ -900,11 +1039,10 @@ static int unit_file_load(
InstallContext *c,
UnitFileInstallInfo *info,
const char *path,
- const char *root_dir,
SearchFlags flags) {
const ConfigTableItem items[] = {
- { "Install", "Alias", config_parse_strv, 0, &info->aliases },
+ { "Install", "Alias", config_parse_alias, 0, &info->aliases },
{ "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "DefaultInstance", config_parse_default_instance, 0, info },
@@ -912,6 +1050,8 @@ static int unit_file_load(
{}
};
+ const char *name;
+ UnitType type;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
struct stat st;
@@ -921,7 +1061,11 @@ static int unit_file_load(
assert(info);
assert(path);
- path = prefix_roota(root_dir, path);
+ name = basename(path);
+ type = unit_name_to_type(name);
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
+ !unit_type_may_template(type))
+ return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
if (!(flags & SEARCH_LOAD)) {
r = lstat(path, &st);
@@ -983,26 +1127,26 @@ static int unit_file_load_or_readlink(
const char *root_dir,
SearchFlags flags) {
- _cleanup_free_ char *np = NULL;
+ _cleanup_free_ char *target = NULL;
int r;
- r = unit_file_load(c, info, path, root_dir, flags);
+ r = unit_file_load(c, info, path, flags);
if (r != -ELOOP)
return r;
/* This is a symlink, let's read it. */
- r = readlink_and_make_absolute_root(root_dir, path, &np);
+ r = readlink_malloc(path, &target);
if (r < 0)
return r;
- if (path_equal(np, "/dev/null"))
+ if (path_equal(target, "/dev/null"))
info->type = UNIT_FILE_TYPE_MASKED;
else {
const char *bn;
UnitType a, b;
- bn = basename(np);
+ bn = basename(target);
if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
@@ -1029,9 +1173,16 @@ static int unit_file_load_or_readlink(
if (a < 0 || b < 0 || a != b)
return -EINVAL;
+ if (path_is_absolute(target))
+ /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
+ info->symlink_target = prefix_root(root_dir, target);
+ else
+ /* This is a relative path, take it relative to the dir the symlink is located in. */
+ info->symlink_target = file_in_same_dir(path, target);
+ if (!info->symlink_target)
+ return -ENOMEM;
+
info->type = UNIT_FILE_TYPE_SYMLINK;
- info->symlink_target = np;
- np = NULL;
}
return 0;
@@ -1041,7 +1192,6 @@ static int unit_file_search(
InstallContext *c,
UnitFileInstallInfo *info,
const LookupPaths *paths,
- const char *root_dir,
SearchFlags flags) {
char **p;
@@ -1056,18 +1206,18 @@ static int unit_file_search(
return 0;
if (info->path)
- return unit_file_load_or_readlink(c, info, info->path, root_dir, flags);
+ return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
assert(info->name);
- STRV_FOREACH(p, paths->unit_path) {
+ STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
path = strjoin(*p, "/", info->name, NULL);
if (!path)
return -ENOMEM;
- r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
+ r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
if (r < 0) {
if (r != -ENOENT)
return r;
@@ -1090,14 +1240,14 @@ static int unit_file_search(
if (r < 0)
return r;
- STRV_FOREACH(p, paths->unit_path) {
+ STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
path = strjoin(*p, "/", template, NULL);
if (!path)
return -ENOMEM;
- r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
+ r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
if (r < 0) {
if (r != -ENOENT)
return r;
@@ -1143,7 +1293,6 @@ static int install_info_follow(
static int install_info_traverse(
UnitFileScope scope,
InstallContext *c,
- const char *root_dir,
const LookupPaths *paths,
UnitFileInstallInfo *start,
SearchFlags flags,
@@ -1157,7 +1306,7 @@ static int install_info_traverse(
assert(start);
assert(c);
- r = unit_file_search(c, start, paths, root_dir, flags);
+ r = unit_file_search(c, start, paths, flags);
if (r < 0)
return r;
@@ -1168,10 +1317,15 @@ static int install_info_traverse(
if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
return -ELOOP;
- if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path))
- return -ELOOP;
+ if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
+ r = path_is_config(paths, i->path);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -ELOOP;
+ }
- r = install_info_follow(c, i, root_dir, flags);
+ r = install_info_follow(c, i, paths->root_dir, flags);
if (r < 0) {
_cleanup_free_ char *buffer = NULL;
const char *bn;
@@ -1205,7 +1359,7 @@ static int install_info_traverse(
if (r < 0)
return r;
- r = unit_file_search(c, i, paths, root_dir, flags);
+ r = unit_file_search(c, i, paths, flags);
if (r < 0)
return r;
}
@@ -1219,10 +1373,28 @@ static int install_info_traverse(
return 0;
}
+static int install_info_add_auto(
+ InstallContext *c,
+ const LookupPaths *paths,
+ const char *name_or_path,
+ UnitFileInstallInfo **ret) {
+
+ assert(c);
+ assert(name_or_path);
+
+ if (path_is_absolute(name_or_path)) {
+ const char *pp;
+
+ pp = prefix_roota(paths->root_dir, name_or_path);
+
+ return install_info_add(c, NULL, pp, ret);
+ } else
+ return install_info_add(c, name_or_path, NULL, ret);
+}
+
static int install_info_discover(
UnitFileScope scope,
InstallContext *c,
- const char *root_dir,
const LookupPaths *paths,
const char *name,
SearchFlags flags,
@@ -1235,15 +1407,16 @@ static int install_info_discover(
assert(paths);
assert(name);
- r = install_info_add_auto(c, name, &i);
+ r = install_info_add_auto(c, paths, name, &i);
if (r < 0)
return r;
- return install_info_traverse(scope, c, root_dir, paths, i, flags, ret);
+ return install_info_traverse(scope, c, paths, i, flags, ret);
}
static int install_info_symlink_alias(
UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
bool force,
UnitFileChange **changes,
@@ -1253,10 +1426,12 @@ static int install_info_symlink_alias(
int r = 0, q;
assert(i);
+ assert(paths);
assert(config_path);
STRV_FOREACH(s, i->aliases) {
_cleanup_free_ char *alias_path = NULL, *dst = NULL;
+ const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1266,7 +1441,9 @@ static int install_info_symlink_alias(
if (!alias_path)
return -ENOMEM;
- q = create_symlink(i->path, alias_path, force, changes, n_changes);
+ rp = skip_root(paths, i->path);
+
+ q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1276,10 +1453,10 @@ static int install_info_symlink_alias(
static int install_info_symlink_wants(
UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
char **list,
const char *suffix,
- bool force,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -1289,6 +1466,7 @@ static int install_info_symlink_wants(
int r = 0, q;
assert(i);
+ assert(paths);
assert(config_path);
if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
@@ -1309,6 +1487,7 @@ static int install_info_symlink_wants(
STRV_FOREACH(s, list) {
_cleanup_free_ char *path = NULL, *dst = NULL;
+ const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1323,7 +1502,9 @@ static int install_info_symlink_wants(
if (!path)
return -ENOMEM;
- q = create_symlink(i->path, path, force, changes, n_changes);
+ rp = skip_root(paths, i->path);
+
+ q = create_symlink(rp ?: i->path, path, true, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1335,12 +1516,12 @@ static int install_info_symlink_link(
UnitFileInstallInfo *i,
const LookupPaths *paths,
const char *config_path,
- const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_free_ char *path = NULL;
+ const char *rp;
int r;
assert(i);
@@ -1348,22 +1529,25 @@ static int install_info_symlink_link(
assert(config_path);
assert(i->path);
- r = in_search_path(i->path, paths->unit_path);
- if (r != 0)
+ r = in_search_path(paths, i->path);
+ if (r < 0)
return r;
+ if (r > 0)
+ return 0;
path = strjoin(config_path, "/", i->name, NULL);
if (!path)
return -ENOMEM;
- return create_symlink(i->path, path, force, changes, n_changes);
+ rp = skip_root(paths, i->path);
+
+ return create_symlink(rp ?: i->path, path, force, changes, n_changes);
}
static int install_info_apply(
UnitFileInstallInfo *i,
const LookupPaths *paths,
const char *config_path,
- const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -1377,18 +1561,19 @@ static int install_info_apply(
if (i->type != UNIT_FILE_TYPE_REGULAR)
return 0;
- r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
+ r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
- q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
+ q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
if (r == 0)
r = q;
- q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
+ q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
if (r == 0)
r = q;
- q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes);
- if (r == 0)
+ q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
+ /* Do not count links to the unit file towards the "carries_install_info" count */
+ if (r == 0 && q < 0)
r = q;
return r;
@@ -1399,7 +1584,6 @@ static int install_context_apply(
InstallContext *c,
const LookupPaths *paths,
const char *config_path,
- const char *root_dir,
bool force,
SearchFlags flags,
UnitFileChange **changes,
@@ -1427,19 +1611,19 @@ static int install_context_apply(
if (q < 0)
return q;
- r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL);
+ r = install_info_traverse(scope, c, paths, i, flags, NULL);
if (r < 0)
return r;
if (i->type != UNIT_FILE_TYPE_REGULAR)
continue;
- q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
+ q = install_info_apply(i, paths, config_path, force, changes, n_changes);
if (r >= 0) {
if (q < 0)
r = q;
else
- r+= q;
+ r += q;
}
}
@@ -1451,8 +1635,7 @@ static int install_context_mark_for_removal(
InstallContext *c,
const LookupPaths *paths,
Set **remove_symlinks_to,
- const char *config_path,
- const char *root_dir) {
+ const char *config_path) {
UnitFileInstallInfo *i;
int r;
@@ -1476,7 +1659,7 @@ static int install_context_mark_for_removal(
if (r < 0)
return r;
- r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
+ r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
if (r < 0)
return r;
@@ -1500,20 +1683,19 @@ int unit_file_mask(
UnitFileChange **changes,
unsigned *n_changes) {
- _cleanup_free_ char *prefix = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ const char *config_path;
char **i;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = get_config_path(scope, runtime, root_dir, &prefix);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
@@ -1525,7 +1707,7 @@ int unit_file_mask(
continue;
}
- path = path_make_absolute(*i, prefix);
+ path = path_make_absolute(*i, config_path);
if (!path)
return -ENOMEM;
@@ -1545,23 +1727,22 @@ int unit_file_unmask(
UnitFileChange **changes,
unsigned *n_changes) {
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- _cleanup_free_ char *config_path = NULL;
_cleanup_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
+ const char *config_path;
char **i;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
@@ -1592,24 +1773,31 @@ int unit_file_unmask(
r = 0;
STRV_FOREACH(i, todo) {
_cleanup_free_ char *path = NULL;
+ const char *rp;
path = path_make_absolute(*i, config_path);
if (!path)
return -ENOMEM;
if (unlink(path) < 0) {
- if (errno != -ENOENT && r >= 0)
- r = -errno;
- } else {
- q = mark_symlink_for_removal(&remove_symlinks_to, path);
- if (q < 0)
- return q;
+ if (errno != ENOENT) {
+ if (r >= 0)
+ r = -errno;
+ unit_file_changes_add(changes, n_changes, -errno, path, NULL);
+ }
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ continue;
}
+
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+
+ rp = skip_root(&paths, path);
+ q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
+ if (q < 0)
+ return q;
}
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes);
if (r >= 0)
r = q;
@@ -1626,26 +1814,20 @@ int unit_file_link(
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char *config_path = NULL;
_cleanup_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
+ const char *config_path;
char **i;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(i, files) {
_cleanup_free_ char *full = NULL;
@@ -1659,7 +1841,7 @@ int unit_file_link(
if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
return -EINVAL;
- full = prefix_root(root_dir, *i);
+ full = prefix_root(paths.root_dir, *i);
if (!full)
return -ENOMEM;
@@ -1672,7 +1854,7 @@ int unit_file_link(
if (!S_ISREG(st.st_mode))
return -ENOTTY;
- q = in_search_path(*i, paths.unit_path);
+ q = in_search_path(&paths, *i);
if (q < 0)
return q;
if (q > 0)
@@ -1688,13 +1870,15 @@ int unit_file_link(
r = 0;
STRV_FOREACH(i, todo) {
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *new_path = NULL;
+ const char *old_path;
- path = path_make_absolute(basename(*i), config_path);
- if (!path)
+ old_path = skip_root(&paths, *i);
+ new_path = path_make_absolute(basename(*i), config_path);
+ if (!new_path)
return -ENOMEM;
- q = create_symlink(*i, path, force, changes, n_changes);
+ q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes);
if (q < 0 && r >= 0)
r = q;
}
@@ -1702,6 +1886,182 @@ int unit_file_link(
return r;
}
+static int path_shall_revert(const LookupPaths *paths, const char *path) {
+ int r;
+
+ assert(paths);
+ assert(path);
+
+ /* Checks whether the path is one where the drop-in directories shall be removed. */
+
+ r = path_is_config(paths, path);
+ if (r != 0)
+ return r;
+
+ r = path_is_control(paths, path);
+ if (r != 0)
+ return r;
+
+ return path_is_transient(paths, path);
+}
+
+int unit_file_revert(
+ UnitFileScope scope,
+ const char *root_dir,
+ char **files,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+ /* _cleanup_(install_context_done) InstallContext c = {}; */
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_strv_free_ char **todo = NULL;
+ size_t n_todo = 0, n_allocated = 0;
+ char **i;
+ int r, q;
+
+ /* Puts a unit file back into vendor state. This means:
+ *
+ * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
+ * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
+ *
+ * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
+ * "config", but not in "transient" or "control" or even "generated").
+ *
+ * We remove all that in both the runtime and the persistent directories, if that applies.
+ */
+
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, files) {
+ bool has_vendor = false;
+ char **p;
+
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ STRV_FOREACH(p, paths.search_path) {
+ _cleanup_free_ char *path = NULL, *dropin = NULL;
+ struct stat st;
+
+ path = path_make_absolute(*i, *p);
+ if (!path)
+ return -ENOMEM;
+
+ r = lstat(path, &st);
+ if (r < 0) {
+ if (errno != ENOENT)
+ return -errno;
+ } else if (S_ISREG(st.st_mode)) {
+ /* Check if there's a vendor version */
+ r = path_is_vendor(&paths, path);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ has_vendor = true;
+ }
+
+ dropin = strappend(path, ".d");
+ if (!dropin)
+ return -ENOMEM;
+
+ r = lstat(dropin, &st);
+ if (r < 0) {
+ if (errno != ENOENT)
+ return -errno;
+ } else if (S_ISDIR(st.st_mode)) {
+ /* Remove the drop-ins */
+ r = path_shall_revert(&paths, dropin);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+ return -ENOMEM;
+
+ todo[n_todo++] = dropin;
+ dropin = NULL;
+ }
+ }
+ }
+
+ if (!has_vendor)
+ continue;
+
+ /* OK, there's a vendor version, hence drop all configuration versions */
+ STRV_FOREACH(p, paths.search_path) {
+ _cleanup_free_ char *path = NULL;
+ struct stat st;
+
+ path = path_make_absolute(*i, *p);
+ if (!path)
+ return -ENOMEM;
+
+ r = lstat(path, &st);
+ if (r < 0) {
+ if (errno != ENOENT)
+ return -errno;
+ } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+ r = path_is_config(&paths, path);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+ return -ENOMEM;
+
+ todo[n_todo++] = path;
+ path = NULL;
+ }
+ }
+ }
+ }
+
+ strv_uniq(todo);
+
+ r = 0;
+ STRV_FOREACH(i, todo) {
+ _cleanup_strv_free_ char **fs = NULL;
+ const char *rp;
+ char **j;
+
+ (void) get_files_in_directory(*i, &fs);
+
+ q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
+ if (q < 0 && q != -ENOENT && r >= 0) {
+ r = q;
+ continue;
+ }
+
+ STRV_FOREACH(j, fs) {
+ _cleanup_free_ char *t = NULL;
+
+ t = strjoin(*i, "/", *j, NULL);
+ if (!t)
+ return -ENOMEM;
+
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
+ }
+
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
+
+ rp = skip_root(&paths, *i);
+ q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
+ if (q < 0)
+ return q;
+ }
+
+ q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes);
+ if (r >= 0)
+ r = q;
+
+ q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes);
+ if (r >= 0)
+ r = q;
+
+ return r;
+}
+
int unit_file_add_dependency(
UnitFileScope scope,
bool runtime,
@@ -1715,8 +2075,8 @@ int unit_file_add_dependency(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- _cleanup_free_ char *config_path = NULL;
UnitFileInstallInfo *i, *target_info;
+ const char *config_path;
char **f;
int r;
@@ -1730,34 +2090,30 @@ int unit_file_add_dependency(
if (!unit_name_is_valid(target, UNIT_NAME_ANY))
return -EINVAL;
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
- r = get_config_path(scope, runtime, root_dir, &config_path);
+ r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
if (r < 0)
return r;
-
- r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
+ r = install_info_may_process(target_info, &paths, changes, n_changes);
if (r < 0)
return r;
- if (target_info->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
STRV_FOREACH(f, files) {
char ***l;
- r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
+ r = install_info_may_process(i, &paths, changes, n_changes);
if (r < 0)
return r;
- if (i->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
assert(i->type == UNIT_FILE_TYPE_REGULAR);
@@ -1776,7 +2132,7 @@ int unit_file_add_dependency(
return -ENOMEM;
}
- return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
+ return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
}
int unit_file_enable(
@@ -1790,7 +2146,7 @@ int unit_file_enable(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- _cleanup_free_ char *config_path = NULL;
+ const char *config_path;
UnitFileInstallInfo *i;
char **f;
int r;
@@ -1798,24 +2154,19 @@ int unit_file_enable(
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(f, files) {
- r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i);
+ r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD, &i);
+ if (r < 0)
+ return r;
+ r = install_info_may_process(i, &paths, changes, n_changes);
if (r < 0)
return r;
- if (i->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
assert(i->type == UNIT_FILE_TYPE_REGULAR);
}
@@ -1825,7 +2176,7 @@ int unit_file_enable(
is useful to determine whether the passed files had any
installation data at all. */
- return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
+ return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes);
}
int unit_file_disable(
@@ -1838,25 +2189,19 @@ int unit_file_disable(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- _cleanup_free_ char *config_path = NULL;
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+ const char *config_path;
char **i;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
@@ -1867,11 +2212,11 @@ int unit_file_disable(
return r;
}
- r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir);
+ r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path);
if (r < 0)
return r;
- return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes);
}
int unit_file_reenable(
@@ -1912,41 +2257,34 @@ int unit_file_set_default(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- _cleanup_free_ char *config_path = NULL;
UnitFileInstallInfo *i;
- const char *path;
+ const char *new_path, *old_path;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- if (unit_name_to_type(name) != UNIT_TARGET)
+ if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
return -EINVAL;
if (streq(name, SPECIAL_DEFAULT_TARGET))
return -EINVAL;
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = install_info_discover(scope, &c, &paths, name, 0, &i);
if (r < 0)
return r;
-
- r = get_config_path(scope, false, root_dir, &config_path);
- if (r < 0)
- return r;
-
- r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i);
+ r = install_info_may_process(i, &paths, changes, n_changes);
if (r < 0)
return r;
- if (i->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
- path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET);
+ old_path = skip_root(&paths, i->path);
+ new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
- return create_symlink(i->path, path, force, changes, n_changes);
+ return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes);
}
int unit_file_get_default(
@@ -1964,19 +2302,16 @@ int unit_file_get_default(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
-
- r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_may_process(i, &paths, NULL, 0);
if (r < 0)
return r;
- if (i->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
n = strdup(i->name);
if (!n)
@@ -1986,9 +2321,8 @@ int unit_file_get_default(
return 0;
}
-int unit_file_lookup_state(
+static int unit_file_lookup_state(
UnitFileScope scope,
- const char *root_dir,
const LookupPaths *paths,
const char *name,
UnitFileState *ret) {
@@ -2004,11 +2338,7 @@ int unit_file_lookup_state(
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- r = verify_root_dir(scope, &root_dir);
- if (r < 0)
- return r;
-
- r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
@@ -2019,11 +2349,31 @@ int unit_file_lookup_state(
switch (i->type) {
case UNIT_FILE_TYPE_MASKED:
- state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+ r = path_is_runtime(paths, i->path);
+ if (r < 0)
+ return r;
+
+ state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
break;
case UNIT_FILE_TYPE_REGULAR:
- r = find_symlinks_in_scope(scope, root_dir, i->name, &state);
+ r = path_is_generator(paths, i->path);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ state = UNIT_FILE_GENERATED;
+ break;
+ }
+
+ r = path_is_transient(paths, i->path);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ state = UNIT_FILE_TRANSIENT;
+ break;
+ }
+
+ r = find_symlinks_in_scope(scope, paths, i->name, &state);
if (r < 0)
return r;
if (r == 0) {
@@ -2058,15 +2408,30 @@ int unit_file_get_state(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ return unit_file_lookup_state(scope, &paths, name, ret);
+}
+
+int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
+ _cleanup_(install_context_done) InstallContext c = {};
+ int r;
+
+ assert(paths);
+ assert(name);
+
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ r = install_info_discover(scope, &c, paths, name, 0, NULL);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
- return unit_file_lookup_state(scope, root_dir, &paths, name, ret);
+ return 1;
}
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
@@ -2078,10 +2443,6 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- r = verify_root_dir(scope, &root_dir);
- if (r < 0)
- return r;
-
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
@@ -2164,7 +2525,6 @@ static int execute_preset(
InstallContext *minus,
const LookupPaths *paths,
const char *config_path,
- const char *root_dir,
char **files,
UnitFilePresetMode mode,
bool force,
@@ -2181,11 +2541,11 @@ static int execute_preset(
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir);
+ r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path);
if (r < 0)
return r;
- r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, changes, n_changes);
} else
r = 0;
@@ -2193,12 +2553,12 @@ static int execute_preset(
int q;
/* Returns number of symlinks that where supposed to be installed. */
- q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
+ q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
if (r >= 0) {
if (q < 0)
r = q;
else
- r+= q;
+ r += q;
}
}
@@ -2210,9 +2570,10 @@ static int preset_prepare_one(
InstallContext *plus,
InstallContext *minus,
LookupPaths *paths,
- const char *root_dir,
UnitFilePresetMode mode,
- const char *name) {
+ const char *name,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
UnitFileInstallInfo *i;
int r;
@@ -2221,19 +2582,20 @@ static int preset_prepare_one(
install_info_find(minus, name))
return 0;
- r = unit_file_query_preset(scope, root_dir, name);
+ r = unit_file_query_preset(scope, paths->root_dir, name);
if (r < 0)
return r;
if (r > 0) {
- r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
- if (i->type == UNIT_FILE_TYPE_MASKED)
- return -ESHUTDOWN;
+ r = install_info_may_process(i, paths, changes, n_changes);
+ if (r < 0)
+ return r;
} else
- r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
return r;
}
@@ -2250,7 +2612,7 @@ int unit_file_preset(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char *config_path = NULL;
+ const char *config_path;
char **i;
int r;
@@ -2258,28 +2620,22 @@ int unit_file_preset(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
- r = verify_root_dir(scope, &root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
- r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, changes, n_changes);
if (r < 0)
return r;
}
- return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes);
+ return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes);
}
int unit_file_preset_all(
@@ -2293,7 +2649,7 @@ int unit_file_preset_all(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char *config_path = NULL;
+ const char *config_path = NULL;
char **i;
int r;
@@ -2301,28 +2657,17 @@ int unit_file_preset_all(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
- r = verify_root_dir(scope, &root_dir);
- if (r < 0)
- return r;
-
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
- STRV_FOREACH(i, paths.unit_path) {
+ STRV_FOREACH(i, paths.search_path) {
_cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *units_dir;
struct dirent *de;
- units_dir = path_join(root_dir, *i, NULL);
- if (!units_dir)
- return -ENOMEM;
-
- d = opendir(units_dir);
+ d = opendir(*i);
if (!d) {
if (errno == ENOENT)
continue;
@@ -2340,13 +2685,17 @@ int unit_file_preset_all(
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
continue;
- r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name);
+ /* we don't pass changes[] in, because we want to handle errors on our own */
+ r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, NULL, 0);
+ if (r == -ERFKILL)
+ r = unit_file_changes_add(changes, n_changes,
+ UNIT_FILE_IS_MASKED, de->d_name, NULL);
if (r < 0)
return r;
}
}
- return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes);
+ return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes);
}
static void unit_file_list_free_one(UnitFileList *f) {
@@ -2371,7 +2720,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
int unit_file_get_list(
UnitFileScope scope,
const char *root_dir,
- Hashmap *h) {
+ Hashmap *h,
+ char **states,
+ char **patterns) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
char **i;
@@ -2381,24 +2732,15 @@ int unit_file_get_list(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(h);
- r = verify_root_dir(scope, &root_dir);
- if (r < 0)
- return r;
-
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = lookup_paths_init(&paths, scope, 0, root_dir);
if (r < 0)
return r;
- STRV_FOREACH(i, paths.unit_path) {
+ STRV_FOREACH(i, paths.search_path) {
_cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *units_dir;
struct dirent *de;
- units_dir = path_join(root_dir, *i, NULL);
- if (!units_dir)
- return -ENOMEM;
-
- d = opendir(units_dir);
+ d = opendir(*i);
if (!d) {
if (errno == ENOENT)
continue;
@@ -2412,6 +2754,9 @@ int unit_file_get_list(
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
+ if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
+ continue;
+
if (hashmap_get(h, de->d_name))
continue;
@@ -2424,14 +2769,18 @@ int unit_file_get_list(
if (!f)
return -ENOMEM;
- f->path = path_make_absolute(de->d_name, units_dir);
+ f->path = path_make_absolute(de->d_name, *i);
if (!f->path)
return -ENOMEM;
- r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state);
+ r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
if (r < 0)
f->state = UNIT_FILE_BAD;
+ if (!strv_isempty(states) &&
+ !strv_contains(states, unit_file_state_to_string(f->state)))
+ continue;
+
r = hashmap_put(h, basename(f->path), f);
if (r < 0)
return r;
@@ -2453,6 +2802,8 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
[UNIT_FILE_STATIC] = "static",
[UNIT_FILE_DISABLED] = "disabled",
[UNIT_FILE_INDIRECT] = "indirect",
+ [UNIT_FILE_GENERATED] = "generated",
+ [UNIT_FILE_TRANSIENT] = "transient",
[UNIT_FILE_BAD] = "bad",
};
@@ -2461,6 +2812,7 @@ DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
[UNIT_FILE_SYMLINK] = "symlink",
[UNIT_FILE_UNLINK] = "unlink",
+ [UNIT_FILE_IS_MASKED] = "masked",
};
DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
diff --git a/src/shared/install.h b/src/shared/install.h
index c1a43e23e7..5812447c5b 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -54,6 +54,8 @@ enum UnitFileState {
UNIT_FILE_STATIC,
UNIT_FILE_DISABLED,
UNIT_FILE_INDIRECT,
+ UNIT_FILE_GENERATED,
+ UNIT_FILE_TRANSIENT,
UNIT_FILE_BAD,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
@@ -70,16 +72,29 @@ enum UnitFilePresetMode {
enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
+ UNIT_FILE_IS_MASKED,
_UNIT_FILE_CHANGE_TYPE_MAX,
- _UNIT_FILE_CHANGE_TYPE_INVALID = -1
+ _UNIT_FILE_CHANGE_INVALID = INT_MIN
};
+/* type can either one of the UnitFileChangeTypes listed above, or a negative error.
+ * If source is specified, it should be the contents of the path symlink.
+ * In case of an error, source should be the existing symlink contents or NULL
+ */
struct UnitFileChange {
- UnitFileChangeType type;
+ int type; /* UnitFileChangeType or bust */
char *path;
char *source;
};
+static inline bool unit_file_changes_have_modification(const UnitFileChange* changes, unsigned n_changes) {
+ unsigned i;
+ for (i = 0; i < n_changes; i++)
+ if (IN_SET(changes[i].type, UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK))
+ return true;
+ return false;
+}
+
struct UnitFileList {
char *path;
UnitFileState state;
@@ -123,31 +138,115 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) {
return !strv_isempty(i->also);
}
-int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
-int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
-int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
-
-int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret);
+bool unit_type_may_alias(UnitType type) _const_;
+bool unit_type_may_template(UnitType type) _const_;
+
+int unit_file_enable(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_disable(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_reenable(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_preset(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ UnitFilePresetMode mode,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_preset_all(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ UnitFilePresetMode mode,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_mask(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_unmask(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_link(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_revert(
+ UnitFileScope scope,
+ const char *root_dir,
+ char **files,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_set_default(
+ UnitFileScope scope,
+ const char *root_dir,
+ const char *file,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+int unit_file_get_default(
+ UnitFileScope scope,
+ const char *root_dir,
+ char **name);
+int unit_file_add_dependency(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ const char *target,
+ UnitDependency dep,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes);
+
int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
+int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name);
-int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
+int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
Hashmap* unit_file_list_free(Hashmap *h);
int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
+void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet);
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
const char *unit_file_state_to_string(UnitFileState s) _const_;
UnitFileState unit_file_state_from_string(const char *s) _pure_;
+/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */
const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_;
UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index e2d2931c51..9351b85eed 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -287,7 +287,10 @@ static int output_short(
if (r < 0)
return r;
}
-
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
if (r < 0)
return log_error_errno(r, "Failed to get journal fields: %m");
@@ -344,16 +347,22 @@ static int output_short(
t = (time_t) (x / USEC_PER_SEC);
- switch(mode) {
+ switch (mode) {
+
+ case OUTPUT_SHORT_UNIX:
+ r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
+ break;
+
case OUTPUT_SHORT_ISO:
r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));
break;
+
case OUTPUT_SHORT_PRECISE:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
if (r > 0)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
break;
+
default:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
}
@@ -367,6 +376,12 @@ static int output_short(
n += strlen(buf);
}
+ if (hostname && (flags & OUTPUT_NO_HOSTNAME)) {
+ /* Suppress display of the hostname if this is requested. */
+ hostname = NULL;
+ hostname_len = 0;
+ }
+
if (hostname && shall_print(hostname, hostname_len, flags)) {
fprintf(f, " %.*s", (int) hostname_len, hostname);
n += hostname_len + 1;
@@ -894,6 +909,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_SHORT_ISO] = output_short,
[OUTPUT_SHORT_PRECISE] = output_short,
[OUTPUT_SHORT_MONOTONIC] = output_short,
+ [OUTPUT_SHORT_UNIX] = output_short,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,
@@ -1040,8 +1056,8 @@ static int show_journal(FILE *f,
}
int add_matches_for_unit(sd_journal *j, const char *unit) {
+ const char *m1, *m2, *m3, *m4;
int r;
- char *m1, *m2, *m3, *m4;
assert(j);
assert(unit);
@@ -1073,7 +1089,9 @@ int add_matches_for_unit(sd_journal *j, const char *unit) {
);
if (r == 0 && endswith(unit, ".slice")) {
- char *m5 = strappend("_SYSTEMD_SLICE=", unit);
+ const char *m5;
+
+ m5 = strjoina("_SYSTEMD_SLICE=", unit);
/* Show all messages belonging to a slice */
(void)(
@@ -1123,7 +1141,9 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
);
if (r == 0 && endswith(unit, ".slice")) {
- char *m5 = strappend("_SYSTEMD_SLICE=", unit);
+ const char *m5;
+
+ m5 = strjoina("_SYSTEMD_SLICE=", unit);
/* Show all messages belonging to a slice */
(void)(
@@ -1288,18 +1308,3 @@ int show_journal_by_unit(
return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
}
-
-static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
- [OUTPUT_SHORT] = "short",
- [OUTPUT_SHORT_ISO] = "short-iso",
- [OUTPUT_SHORT_PRECISE] = "short-precise",
- [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
- [OUTPUT_VERBOSE] = "verbose",
- [OUTPUT_EXPORT] = "export",
- [OUTPUT_JSON] = "json",
- [OUTPUT_JSON_PRETTY] = "json-pretty",
- [OUTPUT_JSON_SSE] = "json-sse",
- [OUTPUT_CAT] = "cat"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 9765a24ff2..6643440881 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -68,6 +68,3 @@ void json_escape(
const char* p,
size_t l,
OutputFlags flags);
-
-const char* output_mode_to_string(OutputMode m) _const_;
-OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index d2f1c4a40c..66f58ecd92 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -401,8 +401,7 @@ int image_remove(Image *i) {
assert(i);
- if (path_equal(i->path, "/") ||
- path_startswith(i->path, "/usr"))
+ if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
settings = image_settings_path(i);
@@ -424,7 +423,7 @@ int image_remove(Image *i) {
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
- (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (r < 0)
return r;
@@ -474,8 +473,7 @@ int image_rename(Image *i, const char *new_name) {
if (!image_name_is_valid(new_name))
return -EINVAL;
- if (path_equal(i->path, "/") ||
- path_startswith(i->path, "/usr"))
+ if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
settings = image_settings_path(i);
@@ -507,7 +505,7 @@ int image_rename(Image *i, const char *new_name) {
(void) read_attr_path(i->path, &file_attr);
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
/* fall through */
@@ -540,7 +538,7 @@ int image_rename(Image *i, const char *new_name) {
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(new_path, true, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
free(i->path);
i->path = new_path;
@@ -605,13 +603,21 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_SUBVOLUME:
case IMAGE_DIRECTORY:
+ /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain
+ * directory.*/
+
new_path = strjoina("/var/lib/machines/", new_name);
r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
+ if (r == -EOPNOTSUPP) {
+ /* No btrfs snapshots supported, create a normal directory then. */
- /* Enable "subtree" quotas for the copy, if we didn't
- * copy any quota from the source. */
- (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
+ r = copy_directory(i->path, new_path, false);
+ if (r >= 0)
+ (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
+ } else if (r >= 0)
+ /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */
+ (void) btrfs_subvol_auto_qgroup(new_path, 0, true);
break;
@@ -642,8 +648,7 @@ int image_read_only(Image *i, bool b) {
int r;
assert(i);
- if (path_equal(i->path, "/") ||
- path_startswith(i->path, "/usr"))
+ if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
/* Make sure we don't interfere with a running nspawn */
@@ -673,7 +678,7 @@ int image_read_only(Image *i, bool b) {
a read-only subvolume, but at least something, and
we can read the value back.*/
- r = chattr_path(i->path, b, FS_IMMUTABLE_FL);
+ r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
if (r < 0)
return r;
@@ -751,8 +756,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
int image_set_limit(Image *i, uint64_t referenced_max) {
assert(i);
- if (path_equal(i->path, "/") ||
- path_startswith(i->path, "/usr"))
+ if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
return -EROFS;
if (i->type != IMAGE_SUBVOLUME)
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 31b720d50c..7410168c4f 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -25,6 +25,8 @@
#include "hashmap.h"
#include "lockfile-util.h"
#include "macro.h"
+#include "path-util.h"
+#include "string-util.h"
#include "time-util.h"
typedef enum ImageType {
@@ -75,3 +77,27 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
int image_name_lock(const char *name, int operation, LockFile *ret);
int image_set_limit(Image *i, uint64_t referenced_max);
+
+static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
+ assert(i);
+
+ return i->name && i->name[0] == '.';
+}
+
+static inline bool IMAGE_IS_VENDOR(const struct Image *i) {
+ assert(i);
+
+ return i->path && path_startswith(i->path, "/usr");
+}
+
+static inline bool IMAGE_IS_HOST(const struct Image *i) {
+ assert(i);
+
+ if (i->name && streq(i->name, ".host"))
+ return true;
+
+ if (i->path && path_equal(i->path, "/"))
+ return true;
+
+ return false;
+}
diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c
new file mode 100644
index 0000000000..bec53ee0ae
--- /dev/null
+++ b/src/shared/output-mode.c
@@ -0,0 +1,37 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ 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 "output-mode.h"
+#include "string-table.h"
+
+static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
+ [OUTPUT_SHORT] = "short",
+ [OUTPUT_SHORT_ISO] = "short-iso",
+ [OUTPUT_SHORT_PRECISE] = "short-precise",
+ [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
+ [OUTPUT_SHORT_UNIX] = "short-unix",
+ [OUTPUT_VERBOSE] = "verbose",
+ [OUTPUT_EXPORT] = "export",
+ [OUTPUT_JSON] = "json",
+ [OUTPUT_JSON_PRETTY] = "json-pretty",
+ [OUTPUT_JSON_SSE] = "json-sse",
+ [OUTPUT_CAT] = "cat"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h
index c5470e7c1b..f37189e57f 100644
--- a/src/shared/output-mode.h
+++ b/src/shared/output-mode.h
@@ -19,11 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "macro.h"
+
typedef enum OutputMode {
OUTPUT_SHORT,
OUTPUT_SHORT_ISO,
OUTPUT_SHORT_PRECISE,
OUTPUT_SHORT_MONOTONIC,
+ OUTPUT_SHORT_UNIX,
OUTPUT_VERBOSE,
OUTPUT_EXPORT,
OUTPUT_JSON,
@@ -34,6 +37,9 @@ typedef enum OutputMode {
_OUTPUT_MODE_INVALID = -1
} OutputMode;
+/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the
+ * logs output, others only to the process tree output. */
+
typedef enum OutputFlags {
OUTPUT_SHOW_ALL = 1 << 0,
OUTPUT_FOLLOW = 1 << 1,
@@ -43,4 +49,9 @@ typedef enum OutputFlags {
OUTPUT_CATALOG = 1 << 5,
OUTPUT_BEGIN_NEWLINE = 1 << 6,
OUTPUT_UTC = 1 << 7,
+ OUTPUT_KERNEL_THREADS = 1 << 8,
+ OUTPUT_NO_HOSTNAME = 1 << 9,
} OutputFlags;
+
+const char* output_mode_to_string(OutputMode m) _const_;
+OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 5410620725..ca593b6963 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -26,61 +26,66 @@
#include "install.h"
#include "log.h"
#include "macro.h"
+#include "mkdir.h"
#include "path-lookup.h"
#include "path-util.h"
+#include "rm-rf.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
-int user_config_home(char **config_home) {
+static int user_runtime_dir(char **ret, const char *suffix) {
const char *e;
- char *r;
+ char *j;
- e = getenv("XDG_CONFIG_HOME");
- if (e) {
- r = strappend(e, "/systemd/user");
- if (!r)
- return -ENOMEM;
-
- *config_home = r;
- return 1;
- } else {
- const char *home;
+ assert(ret);
+ assert(suffix);
- home = getenv("HOME");
- if (home) {
- r = strappend(home, "/.config/systemd/user");
- if (!r)
- return -ENOMEM;
+ e = getenv("XDG_RUNTIME_DIR");
+ if (!e)
+ return -ENXIO;
- *config_home = r;
- return 1;
- }
- }
+ j = strappend(e, suffix);
+ if (!j)
+ return -ENOMEM;
+ *ret = j;
return 0;
}
-int user_runtime_dir(char **runtime_dir) {
+static int user_config_dir(char **ret, const char *suffix) {
const char *e;
- char *r;
+ char *j;
- e = getenv("XDG_RUNTIME_DIR");
- if (e) {
- r = strappend(e, "/systemd/user");
- if (!r)
- return -ENOMEM;
+ assert(ret);
+
+ e = getenv("XDG_CONFIG_HOME");
+ if (e)
+ j = strappend(e, suffix);
+ else {
+ const char *home;
- *runtime_dir = r;
- return 1;
+ home = getenv("HOME");
+ if (!home)
+ return -ENXIO;
+
+ j = strjoin(home, "/.config", suffix, NULL);
}
+ if (!j)
+ return -ENOMEM;
+
+ *ret = j;
return 0;
}
-static int user_data_home_dir(char **dir, const char *suffix) {
+static int user_data_dir(char **ret, const char *suffix) {
const char *e;
- char *res;
+ char *j;
+
+ assert(ret);
+ assert(suffix);
/* We don't treat /etc/xdg/systemd here as the spec
* suggests because we assume that that is a link to
@@ -88,27 +93,33 @@ static int user_data_home_dir(char **dir, const char *suffix) {
e = getenv("XDG_DATA_HOME");
if (e)
- res = strappend(e, suffix);
+ j = strappend(e, suffix);
else {
const char *home;
home = getenv("HOME");
- if (home)
- res = strjoin(home, "/.local/share", suffix, NULL);
- else
- return 0;
+ if (!home)
+ return -ENXIO;
+
+
+ j = strjoin(home, "/.local/share", suffix, NULL);
}
- if (!res)
+ if (!j)
return -ENOMEM;
- *dir = res;
- return 0;
+ *ret = j;
+ return 1;
}
static char** user_dirs(
+ const char *persistent_config,
+ const char *runtime_config,
const char *generator,
const char *generator_early,
- const char *generator_late) {
+ const char *generator_late,
+ const char *transient,
+ const char *persistent_control,
+ const char *runtime_control) {
const char * const config_unit_paths[] = {
USER_CONFIG_UNIT_PATH,
@@ -116,8 +127,6 @@ static char** user_dirs(
NULL
};
- const char * const runtime_unit_path = "/run/systemd/user";
-
const char * const data_unit_paths[] = {
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
@@ -128,8 +137,8 @@ static char** user_dirs(
};
const char *e;
- _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
+ _cleanup_free_ char *data_home = NULL;
_cleanup_free_ char **res = NULL;
char **tmp;
int r;
@@ -143,12 +152,6 @@ static char** user_dirs(
* as data, and allow overriding as configuration.
*/
- if (user_config_home(&config_home) < 0)
- return NULL;
-
- if (user_runtime_dir(&runtime_dir) < 0)
- return NULL;
-
e = getenv("XDG_CONFIG_DIRS");
if (e) {
config_dirs = strv_split(e, ":");
@@ -156,8 +159,8 @@ static char** user_dirs(
return NULL;
}
- r = user_data_home_dir(&data_home, "/systemd/user");
- if (r < 0)
+ r = user_data_dir(&data_home, "/systemd/user");
+ if (r < 0 && r != -ENXIO)
return NULL;
e = getenv("XDG_DATA_DIRS");
@@ -171,35 +174,36 @@ static char** user_dirs(
return NULL;
/* Now merge everything we found. */
- if (generator_early)
- if (strv_extend(&res, generator_early) < 0)
- return NULL;
+ if (strv_extend(&res, persistent_control) < 0)
+ return NULL;
- if (config_home)
- if (strv_extend(&res, config_home) < 0)
- return NULL;
+ if (strv_extend(&res, runtime_control) < 0)
+ return NULL;
+
+ if (strv_extend(&res, transient) < 0)
+ return NULL;
+
+ if (strv_extend(&res, generator_early) < 0)
+ return NULL;
if (!strv_isempty(config_dirs))
if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
return NULL;
- if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
+ if (strv_extend(&res, persistent_config) < 0)
return NULL;
- if (runtime_dir)
- if (strv_extend(&res, runtime_dir) < 0)
- return NULL;
+ if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
+ return NULL;
- if (strv_extend(&res, runtime_unit_path) < 0)
+ if (strv_extend(&res, runtime_config) < 0)
return NULL;
- if (generator)
- if (strv_extend(&res, generator) < 0)
- return NULL;
+ if (strv_extend(&res, generator) < 0)
+ return NULL;
- if (data_home)
- if (strv_extend(&res, data_home) < 0)
- return NULL;
+ if (strv_extend(&res, data_home) < 0)
+ return NULL;
if (!strv_isempty(data_dirs))
if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
@@ -208,9 +212,8 @@ static char** user_dirs(
if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
return NULL;
- if (generator_late)
- if (strv_extend(&res, generator_late) < 0)
- return NULL;
+ if (strv_extend(&res, generator_late) < 0)
+ return NULL;
if (path_strv_make_absolute_cwd(res) < 0)
return NULL;
@@ -220,58 +223,298 @@ static char** user_dirs(
return tmp;
}
-char **generator_paths(ManagerRunningAs running_as) {
- if (running_as == MANAGER_USER)
- return strv_new("/run/systemd/user-generators",
- "/etc/systemd/user-generators",
- "/usr/local/lib/systemd/user-generators",
- USER_GENERATOR_PATH,
- NULL);
- else
- return strv_new("/run/systemd/system-generators",
- "/etc/systemd/system-generators",
- "/usr/local/lib/systemd/system-generators",
- SYSTEM_GENERATOR_PATH,
- NULL);
+static int acquire_generator_dirs(
+ UnitFileScope scope,
+ char **generator,
+ char **generator_early,
+ char **generator_late) {
+
+ _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
+ const char *prefix;
+
+ assert(generator);
+ assert(generator_early);
+ assert(generator_late);
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM:
+ prefix = "/run/systemd/";
+ break;
+
+ case UNIT_FILE_USER: {
+ const char *e;
+
+ e = getenv("XDG_RUNTIME_DIR");
+ if (!e)
+ return -ENXIO;
+
+ prefix = strjoina(e, "/systemd/");
+ break;
+ }
+
+ case UNIT_FILE_GLOBAL:
+ return -EOPNOTSUPP;
+
+ default:
+ assert_not_reached("Hmm, unexpected scope value.");
+ }
+
+ x = strappend(prefix, "generator");
+ if (!x)
+ return -ENOMEM;
+
+ y = strappend(prefix, "generator.early");
+ if (!y)
+ return -ENOMEM;
+
+ z = strappend(prefix, "generator.late");
+ if (!z)
+ return -ENOMEM;
+
+ *generator = x;
+ *generator_early = y;
+ *generator_late = z;
+
+ x = y = z = NULL;
+ return 0;
+}
+
+static int acquire_transient_dir(UnitFileScope scope, char **ret) {
+ assert(ret);
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM: {
+ char *transient;
+
+ transient = strdup("/run/systemd/transient");
+ if (!transient)
+ return -ENOMEM;
+
+ *ret = transient;
+ return 0;
+ }
+
+ case UNIT_FILE_USER:
+ return user_runtime_dir(ret, "/systemd/transient");
+
+ case UNIT_FILE_GLOBAL:
+ return -EOPNOTSUPP;
+
+ default:
+ assert_not_reached("Hmm, unexpected scope value.");
+ }
+}
+
+static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ int r;
+
+ assert(persistent);
+ assert(runtime);
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM:
+ a = strdup(SYSTEM_CONFIG_UNIT_PATH);
+ b = strdup("/run/systemd/system");
+ break;
+
+ case UNIT_FILE_GLOBAL:
+ a = strdup(USER_CONFIG_UNIT_PATH);
+ b = strdup("/run/systemd/user");
+ break;
+
+ case UNIT_FILE_USER:
+ r = user_config_dir(&a, "/systemd/user");
+ if (r < 0)
+ return r;
+
+ r = user_runtime_dir(runtime, "/systemd/user");
+ if (r < 0)
+ return r;
+
+ *persistent = a;
+ a = NULL;
+
+ return 0;
+
+ default:
+ assert_not_reached("Hmm, unexpected scope value.");
+ }
+
+ if (!a || !b)
+ return -ENOMEM;
+
+ *persistent = a;
+ *runtime = b;
+ a = b = NULL;
+
+ return 0;
+}
+
+static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
+ _cleanup_free_ char *a = NULL;
+ int r;
+
+ assert(persistent);
+ assert(runtime);
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM: {
+ _cleanup_free_ char *b = NULL;
+
+ a = strdup("/etc/systemd/system.control");
+ if (!a)
+ return -ENOMEM;
+
+ b = strdup("/run/systemd/system.control");
+ if (!b)
+ return -ENOMEM;
+
+ *runtime = b;
+ b = NULL;
+
+ break;
+ }
+
+ case UNIT_FILE_USER:
+ r = user_config_dir(&a, "/systemd/system.control");
+ if (r < 0)
+ return r;
+
+ r = user_runtime_dir(runtime, "/systemd/system.control");
+ if (r < 0)
+ return r;
+
+ break;
+
+ case UNIT_FILE_GLOBAL:
+ return -EOPNOTSUPP;
+
+ default:
+ assert_not_reached("Hmm, unexpected scope value.");
+ }
+
+ *persistent = a;
+ a = NULL;
+
+ return 0;
+}
+
+static int patch_root_prefix(char **p, const char *root_dir) {
+ char *c;
+
+ assert(p);
+
+ if (!*p)
+ return 0;
+
+ c = prefix_root(root_dir, *p);
+ if (!c)
+ return -ENOMEM;
+
+ free(*p);
+ *p = c;
+
+ return 0;
+}
+
+static int patch_root_prefix_strv(char **l, const char *root_dir) {
+ char **i;
+ int r;
+
+ if (!root_dir)
+ return 0;
+
+ STRV_FOREACH(i, l) {
+ r = patch_root_prefix(i, root_dir);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
int lookup_paths_init(
LookupPaths *p,
- ManagerRunningAs running_as,
- bool personal,
- const char *root_dir,
- const char *generator,
- const char *generator_early,
- const char *generator_late) {
-
- const char *e;
+ UnitFileScope scope,
+ LookupPathsFlags flags,
+ const char *root_dir) {
+
+ _cleanup_free_ char
+ *root = NULL,
+ *persistent_config = NULL, *runtime_config = NULL,
+ *generator = NULL, *generator_early = NULL, *generator_late = NULL,
+ *transient = NULL,
+ *persistent_control = NULL, *runtime_control = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
+ _cleanup_strv_free_ char **paths = NULL;
+ const char *e;
int r;
assert(p);
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+ if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
+ if (scope == UNIT_FILE_USER)
+ return -EINVAL;
+
+ r = is_dir(root_dir, true);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTDIR;
- /* First priority is whatever has been passed to us via env
- * vars */
+ root = strdup(root_dir);
+ if (!root)
+ return -ENOMEM;
+ }
+
+ r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
+ if (r < 0 && r != -ENXIO)
+ return r;
+
+ if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
+ r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late);
+ if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
+ return r;
+ }
+
+ r = acquire_transient_dir(scope, &transient);
+ if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
+ return r;
+
+ r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
+ if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
+ return r;
+
+ /* First priority is whatever has been passed to us via env vars */
e = getenv("SYSTEMD_UNIT_PATH");
if (e) {
- if (endswith(e, ":")) {
- e = strndupa(e, strlen(e) - 1);
+ const char *k;
+
+ k = endswith(e, ":");
+ if (k) {
+ e = strndupa(e, k - e);
append = true;
}
/* FIXME: empty components in other places should be
* rejected. */
- r = path_split_and_make_absolute(e, &p->unit_path);
+ r = path_split_and_make_absolute(e, &paths);
if (r < 0)
return r;
- } else
- p->unit_path = NULL;
+ }
- if (!p->unit_path || append) {
+ if (!paths || append) {
/* Let's figure something out. */
- _cleanup_strv_free_ char **unit_path;
+ _cleanup_strv_free_ char **add = NULL;
/* For the user units we include share/ in the search
* path in order to comply with the XDG basedir spec.
@@ -279,17 +522,45 @@ int lookup_paths_init(
* we include /lib in the search path for the system
* stuff but avoid it for user stuff. */
- if (running_as == MANAGER_USER) {
- if (personal)
- unit_path = user_dirs(generator, generator_early, generator_late);
- else
- unit_path = strv_new(
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM:
+ add = strv_new(
+ /* If you modify this you also want to modify
+ * systemdsystemunitpath= in systemd.pc.in! */
+ STRV_IFNOTNULL(persistent_control),
+ STRV_IFNOTNULL(runtime_control),
+ STRV_IFNOTNULL(transient),
+ STRV_IFNOTNULL(generator_early),
+ persistent_config,
+ SYSTEM_CONFIG_UNIT_PATH,
+ "/etc/systemd/system",
+ runtime_config,
+ "/run/systemd/system",
+ STRV_IFNOTNULL(generator),
+ "/usr/local/lib/systemd/system",
+ SYSTEM_DATA_UNIT_PATH,
+ "/usr/lib/systemd/system",
+#ifdef HAVE_SPLIT_USR
+ "/lib/systemd/system",
+#endif
+ STRV_IFNOTNULL(generator_late),
+ NULL);
+ break;
+
+ case UNIT_FILE_GLOBAL:
+ add = strv_new(
/* If you modify this you also want to modify
* systemduserunitpath= in systemd.pc.in, and
* the arrays in user_dirs() above! */
+ STRV_IFNOTNULL(persistent_control),
+ STRV_IFNOTNULL(runtime_control),
+ STRV_IFNOTNULL(transient),
STRV_IFNOTNULL(generator_early),
+ persistent_config,
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
+ runtime_config,
"/run/systemd/user",
STRV_IFNOTNULL(generator),
"/usr/local/lib/systemd/user",
@@ -299,143 +570,253 @@ int lookup_paths_init(
"/usr/share/systemd/user",
STRV_IFNOTNULL(generator_late),
NULL);
- } else
- unit_path = strv_new(
- /* If you modify this you also want to modify
- * systemdsystemunitpath= in systemd.pc.in! */
- STRV_IFNOTNULL(generator_early),
- SYSTEM_CONFIG_UNIT_PATH,
- "/etc/systemd/system",
- "/run/systemd/system",
- STRV_IFNOTNULL(generator),
- "/usr/local/lib/systemd/system",
- SYSTEM_DATA_UNIT_PATH,
- "/usr/lib/systemd/system",
-#ifdef HAVE_SPLIT_USR
- "/lib/systemd/system",
-#endif
- STRV_IFNOTNULL(generator_late),
- NULL);
+ break;
+
+ case UNIT_FILE_USER:
+ add = user_dirs(persistent_config, runtime_config,
+ generator, generator_early, generator_late,
+ transient,
+ persistent_config, runtime_control);
+ break;
- if (!unit_path)
+ default:
+ assert_not_reached("Hmm, unexpected scope?");
+ }
+
+ if (!add)
return -ENOMEM;
- r = strv_extend_strv(&p->unit_path, unit_path, false);
- if (r < 0)
- return r;
+ if (paths) {
+ r = strv_extend_strv(&paths, add, true);
+ if (r < 0)
+ return r;
+ } else {
+ /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+ * and don't have to copy anything */
+ paths = add;
+ add = NULL;
+ }
}
- if (!path_strv_resolve_uniq(p->unit_path, root_dir))
+ r = patch_root_prefix(&persistent_config, root);
+ if (r < 0)
+ return r;
+ r = patch_root_prefix(&runtime_config, root);
+ if (r < 0)
+ return r;
+
+ r = patch_root_prefix(&generator, root);
+ if (r < 0)
+ return r;
+ r = patch_root_prefix(&generator_early, root);
+ if (r < 0)
+ return r;
+ r = patch_root_prefix(&generator_late, root);
+ if (r < 0)
+ return r;
+
+ r = patch_root_prefix(&transient, root);
+ if (r < 0)
+ return r;
+
+ r = patch_root_prefix(&persistent_control, root);
+ if (r < 0)
+ return r;
+
+ r = patch_root_prefix(&runtime_control, root);
+ if (r < 0)
+ return r;
+
+ r = patch_root_prefix_strv(paths, root);
+ if (r < 0)
return -ENOMEM;
- if (!strv_isempty(p->unit_path)) {
- _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
- if (!t)
- return -ENOMEM;
- log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
- } else {
- log_debug("Ignoring unit files.");
- p->unit_path = strv_free(p->unit_path);
- }
+ p->search_path = strv_uniq(paths);
+ paths = NULL;
- if (running_as == MANAGER_SYSTEM) {
-#ifdef HAVE_SYSV_COMPAT
- /* /etc/init.d/ compatibility does not matter to users */
+ p->persistent_config = persistent_config;
+ p->runtime_config = runtime_config;
+ persistent_config = runtime_config = NULL;
- e = getenv("SYSTEMD_SYSVINIT_PATH");
- if (e) {
- r = path_split_and_make_absolute(e, &p->sysvinit_path);
- if (r < 0)
- return r;
- } else
- p->sysvinit_path = NULL;
+ p->generator = generator;
+ p->generator_early = generator_early;
+ p->generator_late = generator_late;
+ generator = generator_early = generator_late = NULL;
- if (strv_isempty(p->sysvinit_path)) {
- strv_free(p->sysvinit_path);
+ p->transient = transient;
+ transient = NULL;
- p->sysvinit_path = strv_new(
- SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
- NULL);
- if (!p->sysvinit_path)
- return -ENOMEM;
- }
+ p->persistent_control = persistent_control;
+ p->runtime_control = runtime_control;
+ persistent_control = runtime_control = NULL;
- e = getenv("SYSTEMD_SYSVRCND_PATH");
- if (e) {
- r = path_split_and_make_absolute(e, &p->sysvrcnd_path);
- if (r < 0)
- return r;
- } else
- p->sysvrcnd_path = NULL;
+ p->root_dir = root;
+ root = NULL;
- if (strv_isempty(p->sysvrcnd_path)) {
- strv_free(p->sysvrcnd_path);
+ return 0;
+}
- p->sysvrcnd_path = strv_new(
- SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
- NULL);
- if (!p->sysvrcnd_path)
- return -ENOMEM;
+void lookup_paths_free(LookupPaths *p) {
+ if (!p)
+ return;
+
+ p->search_path = strv_free(p->search_path);
+
+ p->persistent_config = mfree(p->persistent_config);
+ p->runtime_config = mfree(p->runtime_config);
+
+ p->generator = mfree(p->generator);
+ p->generator_early = mfree(p->generator_early);
+ p->generator_late = mfree(p->generator_late);
+
+ p->transient = mfree(p->transient);
+
+ p->persistent_control = mfree(p->persistent_control);
+ p->runtime_control = mfree(p->runtime_control);
+
+ p->root_dir = mfree(p->root_dir);
+}
+
+int lookup_paths_reduce(LookupPaths *p) {
+ _cleanup_free_ struct stat *stats = NULL;
+ size_t n_stats = 0, allocated = 0;
+ unsigned c = 0;
+ int r;
+
+ assert(p);
+
+ /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
+ * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
+ * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
+ * account when following symlinks. When we have no root path set this restriction does not apply however. */
+
+ if (!p->search_path)
+ return 0;
+
+ while (p->search_path[c]) {
+ struct stat st;
+ unsigned k;
+
+ if (p->root_dir)
+ r = lstat(p->search_path[c], &st);
+ else
+ r = stat(p->search_path[c], &st);
+ if (r < 0) {
+ if (errno == ENOENT)
+ goto remove_item;
+
+ /* If something we don't grok happened, let's better leave it in. */
+ log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
+ c++;
+ continue;
}
- if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
- return -ENOMEM;
+ for (k = 0; k < n_stats; k++) {
+ if (stats[k].st_dev == st.st_dev &&
+ stats[k].st_ino == st.st_ino)
+ break;
+ }
+
+ if (k < n_stats) /* Is there already an entry with the same device/inode? */
+ goto remove_item;
- if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
+ if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
return -ENOMEM;
- if (!strv_isempty(p->sysvinit_path)) {
- _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
- if (!t)
- return -ENOMEM;
- log_debug("Looking for SysV init scripts in:\n\t%s", t);
- } else {
- log_debug("Ignoring SysV init scripts.");
- p->sysvinit_path = strv_free(p->sysvinit_path);
- }
+ stats[n_stats++] = st;
+ c++;
+ continue;
- if (!strv_isempty(p->sysvrcnd_path)) {
- _cleanup_free_ char *t =
- strv_join(p->sysvrcnd_path, "\n\t");
- if (!t)
- return -ENOMEM;
+ remove_item:
+ free(p->search_path[c]);
+ memmove(p->search_path + c,
+ p->search_path + c + 1,
+ (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
+ }
- log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
- } else {
- log_debug("Ignoring SysV rcN.d links.");
- p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
- }
-#else
- log_debug("SysV init scripts and rcN.d links support disabled");
-#endif
+ if (strv_isempty(p->search_path)) {
+ log_debug("Ignoring unit files.");
+ p->search_path = strv_free(p->search_path);
+ } else {
+ _cleanup_free_ char *t;
+
+ t = strv_join(p->search_path, "\n\t");
+ if (!t)
+ return -ENOMEM;
+
+ log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
}
return 0;
}
-void lookup_paths_free(LookupPaths *p) {
+int lookup_paths_mkdir_generator(LookupPaths *p) {
+ int r, q;
+
assert(p);
- p->unit_path = strv_free(p->unit_path);
+ if (!p->generator || !p->generator_early || !p->generator_late)
+ return -EINVAL;
-#ifdef HAVE_SYSV_COMPAT
- p->sysvinit_path = strv_free(p->sysvinit_path);
- p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
-#endif
+ r = mkdir_p_label(p->generator, 0755);
+
+ q = mkdir_p_label(p->generator_early, 0755);
+ if (q < 0 && r >= 0)
+ r = q;
+
+ q = mkdir_p_label(p->generator_late, 0755);
+ if (q < 0 && r >= 0)
+ r = q;
+
+ return r;
}
-int lookup_paths_init_from_scope(LookupPaths *paths,
- UnitFileScope scope,
- const char *root_dir) {
- assert(paths);
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+void lookup_paths_trim_generator(LookupPaths *p) {
+ assert(p);
- zero(*paths);
+ /* Trim empty dirs */
+
+ if (p->generator)
+ (void) rmdir(p->generator);
+ if (p->generator_early)
+ (void) rmdir(p->generator_early);
+ if (p->generator_late)
+ (void) rmdir(p->generator_late);
+}
+
+void lookup_paths_flush_generator(LookupPaths *p) {
+ assert(p);
- return lookup_paths_init(paths,
- scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
- scope == UNIT_FILE_USER,
- root_dir,
- NULL, NULL, NULL);
+ /* Flush the generated unit files in full */
+
+ if (p->generator)
+ (void) rm_rf(p->generator, REMOVE_ROOT);
+ if (p->generator_early)
+ (void) rm_rf(p->generator_early, REMOVE_ROOT);
+ if (p->generator_late)
+ (void) rm_rf(p->generator_late, REMOVE_ROOT);
+}
+
+char **generator_binary_paths(UnitFileScope scope) {
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM:
+ return strv_new("/run/systemd/system-generators",
+ "/etc/systemd/system-generators",
+ "/usr/local/lib/systemd/system-generators",
+ SYSTEM_GENERATOR_PATH,
+ NULL);
+
+ case UNIT_FILE_GLOBAL:
+ case UNIT_FILE_USER:
+ return strv_new("/run/systemd/user-generators",
+ "/etc/systemd/user-generators",
+ "/usr/local/lib/systemd/user-generators",
+ USER_GENERATOR_PATH,
+ NULL);
+
+ default:
+ assert_not_reached("Hmm, unexpected scope.");
+ }
}
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 26c83d6111..f9bb2fe237 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -20,41 +20,57 @@
***/
#include <stdbool.h>
-#include "macro.h"
-typedef struct LookupPaths {
- char **unit_path;
-#ifdef HAVE_SYSV_COMPAT
- char **sysvinit_path;
- char **sysvrcnd_path;
-#endif
-} LookupPaths;
-
-typedef enum ManagerRunningAs {
- MANAGER_SYSTEM,
- MANAGER_USER,
- _MANAGER_RUNNING_AS_MAX,
- _MANAGER_RUNNING_AS_INVALID = -1
-} ManagerRunningAs;
-
-int user_config_home(char **config_home);
-int user_runtime_dir(char **runtime_dir);
-
-char **generator_paths(ManagerRunningAs running_as);
-
-int lookup_paths_init(LookupPaths *p,
- ManagerRunningAs running_as,
- bool personal,
- const char *root_dir,
- const char *generator,
- const char *generator_early,
- const char *generator_late);
+typedef struct LookupPaths LookupPaths;
#include "install.h"
+#include "macro.h"
+
+typedef enum LookupPathsFlags {
+ LOOKUP_PATHS_EXCLUDE_GENERATED = 1,
+} LookupPathsFlags;
+
+struct LookupPaths {
+ /* Where we look for unit files. This includes the individual special paths below, but also any vendor
+ * supplied, static unit file paths. */
+ char **search_path;
+
+ /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin
+ * shall place his own unit files. */
+ char *persistent_config;
+ char *runtime_config;
+
+ /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
+ * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
+ * not alter these directories directly. */
+ char *generator;
+ char *generator_early;
+ char *generator_late;
-int lookup_paths_init_from_scope(LookupPaths *paths,
- UnitFileScope scope,
- const char *root_dir);
+ /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special
+ * semantics of this directory: all units created transiently have their unit files removed as the transient
+ * unit is unloaded. The user should not alter this directory directly. */
+ char *transient;
+
+ /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the
+ * snippets are placed in the transient directory though (see above). The user should not alter this directory
+ * directly. */
+ char *persistent_control;
+ char *runtime_control;
+
+ /* The root directory prepended to all items above, or NULL */
+ char *root_dir;
+};
+
+int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
+
+int lookup_paths_reduce(LookupPaths *p);
+
+int lookup_paths_mkdir_generator(LookupPaths *p);
+void lookup_paths_trim_generator(LookupPaths *p);
+void lookup_paths_flush_generator(LookupPaths *p);
void lookup_paths_free(LookupPaths *p);
#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
+
+char **generator_binary_paths(UnitFileScope scope);
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index 35aa60101f..f00624d0f2 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
@@ -231,6 +232,9 @@ static bool enough_memory_for_hibernation(void) {
size_t size = 0, used = 0;
int r;
+ if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
+ return true;
+
r = hibernation_partition_size(&size, &used);
if (r < 0)
return false;
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index cf3c8ad5a3..7dae4d14fe 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -44,6 +44,10 @@ int polkit_agent_open(void) {
if (agent_pid > 0)
return 0;
+ /* Clients that run as root don't need to activate/query polkit */
+ if (geteuid() == 0)
+ return 0;
+
/* We check STDIN here, not STDOUT, since this is about input,
* not output */
if (!isatty(STDIN_FILENO))
diff --git a/src/shared/tests.c b/src/shared/tests.c
new file mode 100644
index 0000000000..409116290d
--- /dev/null
+++ b/src/shared/tests.c
@@ -0,0 +1,33 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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 <util.h>
+
+#include "tests.h"
+
+char* setup_fake_runtime_dir(void) {
+ char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
+
+ assert_se(mkdtemp(t));
+ assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0);
+ assert_se(p = strdup(t));
+
+ return p;
+}
diff --git a/src/shared/tests.h b/src/shared/tests.h
new file mode 100644
index 0000000000..93f09013a1
--- /dev/null
+++ b/src/shared/tests.h
@@ -0,0 +1,22 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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/>.
+***/
+
+char* setup_fake_runtime_dir(void);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 2afb7bad1a..9af25e22a4 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -39,6 +39,7 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-message.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -103,6 +104,7 @@ static bool arg_no_pager = false;
static bool arg_no_wtmp = false;
static bool arg_no_wall = false;
static bool arg_no_reload = false;
+static bool arg_value = false;
static bool arg_show_types = false;
static bool arg_ignore_inhibitors = false;
static bool arg_dry = false;
@@ -323,6 +325,8 @@ static int compare_unit_info(const void *a, const void *b) {
}
static bool output_show_unit(const UnitInfo *u, char **patterns) {
+ assert(u);
+
if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
return false;
@@ -340,6 +344,12 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
if (arg_all)
return true;
+ /* Note that '--all' is not purely a state filter, but also a
+ * filter that hides units that "follow" other units (which is
+ * used for device units that appear under different names). */
+ if (!isempty(u->following))
+ return false;
+
if (!strv_isempty(arg_states))
return true;
@@ -348,7 +358,7 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
if (u->job_id > 0)
return true;
- if (streq(u->active_state, "inactive") || u->following[0])
+ if (streq(u->active_state, "inactive"))
return false;
return true;
@@ -531,6 +541,7 @@ static int get_unit_list(
size_t size = c;
int r;
UnitInfo u;
+ bool fallback = false;
assert(bus);
assert(unit_infos);
@@ -542,8 +553,7 @@ static int get_unit_list(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ListUnitsFiltered");
-
+ "ListUnitsByPatterns");
if (r < 0)
return bus_log_create_error(r);
@@ -551,7 +561,34 @@ static int get_unit_list(
if (r < 0)
return bus_log_create_error(r);
+ r = sd_bus_message_append_strv(m, patterns);
+ if (r < 0)
+ return bus_log_create_error(r);
+
r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ /* Fallback to legacy ListUnitsFiltered method */
+ fallback = true;
+ log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r));
+ m = sd_bus_message_unref(m);
+ sd_bus_error_free(&error);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitsFiltered");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_states);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ }
if (r < 0)
return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
@@ -562,7 +599,7 @@ static int get_unit_list(
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
u.machine = machine;
- if (!output_show_unit(&u, patterns))
+ if (!output_show_unit(&u, fallback ? patterns : NULL))
continue;
if (!GREEDY_REALLOC(*unit_infos, size, c+1))
@@ -1272,7 +1309,9 @@ static int compare_unit_file_list(const void *a, const void *b) {
return strcasecmp(basename(u->path), basename(v->path));
}
-static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
+static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) {
+ assert(u);
+
if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE))
return false;
@@ -1287,8 +1326,8 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
return false;
}
- if (!strv_isempty(arg_states) &&
- !strv_find(arg_states, unit_file_state_to_string(u->state)))
+ if (!strv_isempty(states) &&
+ !strv_find(states, unit_file_state_to_string(u->state)))
return false;
return true;
@@ -1361,6 +1400,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
const char *state;
char *path;
int r;
+ bool fallback = false;
pager_open(arg_no_pager, false);
@@ -1374,7 +1414,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
if (!h)
return log_oom();
- r = unit_file_get_list(arg_scope, arg_root, h);
+ r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1));
if (r < 0) {
unit_file_list_free(h);
return log_error_errno(r, "Failed to get unit file list: %m");
@@ -1389,7 +1429,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
}
HASHMAP_FOREACH(u, h, i) {
- if (!output_show_unit_file(u, strv_skip(argv, 1)))
+ if (!output_show_unit_file(u, NULL, NULL))
continue;
units[c++] = *u;
@@ -1399,6 +1439,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
assert(c <= n_units);
hashmap_free(h);
} else {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus;
@@ -1406,15 +1447,44 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ListUnitFiles",
- &error,
- &reply,
- NULL);
+ "ListUnitFilesByPatterns");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_states);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ /* Fallback to legacy ListUnitFiles method */
+ fallback = true;
+ log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r));
+ m = sd_bus_message_unref(m);
+ sd_bus_error_free(&error);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitFiles");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ }
if (r < 0)
return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
@@ -1432,7 +1502,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
unit_file_state_from_string(state)
};
- if (output_show_unit_file(&units[c], strv_skip(argv, 1)))
+ if (output_show_unit_file(&units[c],
+ fallback ? arg_states : NULL,
+ fallback ? strv_skip(argv, 1) : NULL))
c++;
}
@@ -1896,13 +1968,13 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
printf("%s%s%s ", on_state, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_state);
if (m->is_host)
- printf("%-*s (host) %s%-*s%s %s%*u%s %*u\n",
+ printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
(int) (namelen - (sizeof(" (host)")-1)), strna(m->name),
on_state, statelen, strna(m->state), off_state,
on_failed, failedlen, m->n_failed_units, off_failed,
jobslen, m->n_jobs);
else
- printf("%-*s %s%-*s%s %s%*u%s %*u\n",
+ printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
namelen, strna(m->name),
on_state, statelen, strna(m->state), off_state,
on_failed, failedlen, m->n_failed_units, off_failed,
@@ -1983,19 +2055,6 @@ static int get_default(int argc, char *argv[], void *userdata) {
return 0;
}
-static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) {
- unsigned i;
-
- assert(changes || n_changes == 0);
-
- for (i = 0; i < n_changes; i++) {
- if (changes[i].type == UNIT_FILE_SYMLINK)
- log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
- else
- log_info("Removed symlink %s.", changes[i].path);
- }
-}
-
static int set_default(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *unit = NULL;
int r;
@@ -2012,14 +2071,9 @@ static int set_default(int argc, char *argv[], void *userdata) {
unsigned n_changes = 0;
r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes);
- if (r < 0)
- return log_error_errno(r, "Failed to set default target: %m");
-
- if (!arg_quiet)
- dump_unit_file_changes(changes, n_changes);
-
+ unit_file_dump_changes(r, "set default", changes, n_changes, arg_quiet);
unit_file_changes_free(changes, n_changes);
- r = 0;
+ return r;
} else {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -2294,7 +2348,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
assert(unit_name);
assert(unit_path);
- STRV_FOREACH(p, lp->unit_path) {
+ STRV_FOREACH(p, lp->search_path) {
_cleanup_free_ char *path;
path = path_join(arg_root, *p, unit_name);
@@ -2394,7 +2448,7 @@ static int unit_find_paths(
}
if (dropin_paths) {
- r = unit_file_find_dropin_paths(lp->unit_path, NULL, names, &dropins);
+ r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins);
if (r < 0)
return r;
}
@@ -3108,7 +3162,7 @@ static int set_exit_code(uint8_t code) {
NULL,
"y", code);
if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r));
return 0;
}
@@ -3135,7 +3189,7 @@ static int start_special(int argc, char *argv[], void *userdata) {
return r;
if (a == ACTION_REBOOT && argc > 1) {
- r = update_reboot_param_file(argv[1]);
+ r = update_reboot_parameter_and_warn(argv[1]);
if (r < 0)
return r;
@@ -3253,7 +3307,7 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
/* --fail was specified */
if (streq(arg_job_mode, "fail"))
- kill_who = strjoina(arg_kill_who, "-fail", NULL);
+ kill_who = strjoina(arg_kill_who, "-fail");
r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
if (r < 0)
@@ -3445,6 +3499,7 @@ typedef struct UnitStatusInfo {
} UnitStatusInfo;
static void print_status_info(
+ sd_bus *bus,
UnitStatusInfo *i,
bool *ellipsized) {
@@ -3455,6 +3510,7 @@ static void print_status_info(
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
char **t, **t2;
+ int r;
assert(i);
@@ -3727,25 +3783,26 @@ static void print_status_info(
printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
}
- if (i->control_group &&
- (i->main_pid > 0 || i->control_pid > 0 ||
- (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) {
+ if (i->control_group)
+ printf(" CGroup: %s\n", i->control_group);
+
+ {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ static const char prefix[] = " ";
unsigned c;
- printf(" CGroup: %s\n", i->control_group);
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
- if (IN_SET(arg_transport,
- BUS_TRANSPORT_LOCAL,
- BUS_TRANSPORT_MACHINE)) {
+ r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error);
+ if (r == -EBADR) {
unsigned k = 0;
pid_t extra[2];
- static const char prefix[] = " ";
- c = columns();
- if (c > sizeof(prefix) - 1)
- c -= sizeof(prefix) - 1;
- else
- c = 0;
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
if (i->main_pid > 0)
extra[k++] = i->main_pid;
@@ -3753,8 +3810,9 @@ static void print_status_info(
if (i->control_pid > 0)
extra[k++] = i->control_pid;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags());
- }
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags());
+ } else if (r < 0)
+ log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r));
}
if (i->id && arg_transport == BUS_TRANSPORT_LOCAL)
@@ -4116,6 +4174,14 @@ skip:
return 0;
}
+#define print_prop(name, fmt, ...) \
+ do { \
+ if (arg_value) \
+ printf(fmt "\n", __VA_ARGS__); \
+ else \
+ printf("%s=" fmt "\n", name, __VA_ARGS__); \
+ } while(0)
+
static int print_property(const char *name, sd_bus_message *m, const char *contents) {
int r;
@@ -4143,9 +4209,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
if (u > 0)
- printf("%s=%"PRIu32"\n", name, u);
+ print_prop(name, "%"PRIu32, u);
else if (arg_all)
- printf("%s=\n", name);
+ print_prop(name, "%s", "");
return 0;
@@ -4157,7 +4223,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
if (arg_all || !isempty(s))
- printf("%s=%s\n", name, s);
+ print_prop(name, "%s", s);
return 0;
@@ -4169,7 +4235,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
if (arg_all || !isempty(a) || !isempty(b))
- printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+ print_prop(name, "%s \"%s\"", strempty(a), strempty(b));
return 0;
} else if (streq_ptr(name, "SystemCallFilter")) {
@@ -4196,8 +4262,10 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
bool first = true;
char **i;
- fputs(name, stdout);
- fputc('=', stdout);
+ if (!arg_value) {
+ fputs(name, stdout);
+ fputc('=', stdout);
+ }
if (!whitelist)
fputc('~', stdout);
@@ -4229,7 +4297,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0)
- printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
+ print_prop("EnvironmentFile", "%s (ignore_errors=%s)\n", path, yes_no(ignore));
if (r < 0)
return bus_log_parse_error(r);
@@ -4248,7 +4316,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
- printf("%s=%s\n", type, path);
+ print_prop(type, "%s", path);
if (r < 0)
return bus_log_parse_error(r);
@@ -4266,7 +4334,10 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
- printf("Listen%s=%s\n", type, path);
+ if (arg_value)
+ puts(path);
+ else
+ printf("Listen%s=%s\n", type, path);
if (r < 0)
return bus_log_parse_error(r);
@@ -4287,10 +4358,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) {
char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
- printf("%s={ value=%s ; next_elapse=%s }\n",
- base,
- format_timespan(timespan1, sizeof(timespan1), value, 0),
- format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
+ print_prop(base, "{ value=%s ; next_elapse=%s }",
+ format_timespan(timespan1, sizeof(timespan1), value, 0),
+ format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
}
if (r < 0)
return bus_log_parse_error(r);
@@ -4314,18 +4384,18 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
tt = strv_join(info.argv, " ");
- printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }\n",
- name,
- strna(info.path),
- strna(tt),
- yes_no(info.ignore),
- strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
- strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
- info.pid,
- sigchld_code_to_string(info.code),
- info.status,
- info.code == CLD_EXITED ? "" : "/",
- strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ print_prop(name,
+ "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ yes_no(info.ignore),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
free(info.path);
strv_free(info.argv);
@@ -4346,7 +4416,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0)
- printf("%s=%s %s\n", name, strna(path), strna(rwm));
+ print_prop(name, "%s %s", strna(path), strna(rwm));
if (r < 0)
return bus_log_parse_error(r);
@@ -4365,7 +4435,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0)
- printf("%s=%s %" PRIu64 "\n", name, strna(path), weight);
+ print_prop(name, "%s %"PRIu64, strna(path), weight);
if (r < 0)
return bus_log_parse_error(r);
@@ -4384,7 +4454,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0)
- printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth);
+ print_prop(name, "%s %"PRIu64, strna(path), bandwidth);
if (r < 0)
return bus_log_parse_error(r);
@@ -4398,7 +4468,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
break;
}
- r = bus_print_property(name, m, arg_all);
+ r = bus_print_property(name, m, arg_value, arg_all);
if (r < 0)
return bus_log_parse_error(r);
@@ -4503,7 +4573,7 @@ static int show_one(
if (streq(verb, "help"))
show_unit_help(&info);
else
- print_status_info(&info, ellipsized);
+ print_status_info(bus, &info, ellipsized);
}
strv_free(info.documentation);
@@ -4637,8 +4707,8 @@ static int show_system_status(sd_bus *bus) {
printf(" State: %s%s%s\n",
on, strna(mi.state), off);
- printf(" Jobs: %u queued\n", mi.n_jobs);
- printf(" Failed: %u units\n", mi.n_failed_units);
+ printf(" Jobs: %" PRIu32 " queued\n", mi.n_jobs);
+ printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units);
printf(" Since: %s; %s\n",
format_timestamp(since2, sizeof(since2), mi.timestamp),
@@ -4657,7 +4727,7 @@ static int show_system_status(sd_bus *bus) {
else
c = 0;
- show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, get_output_flags());
+ show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags());
}
return 0;
@@ -4767,34 +4837,6 @@ static int show(int argc, char *argv[], void *userdata) {
return ret;
}
-static int init_home_and_lookup_paths(char **user_home, char **user_runtime, LookupPaths *lp) {
- int r;
-
- assert(user_home);
- assert(user_runtime);
- assert(lp);
-
- if (arg_scope == UNIT_FILE_USER) {
- r = user_config_home(user_home);
- if (r < 0)
- return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
- else if (r == 0)
- return log_error_errno(ENOTDIR, "Cannot find units: $XDG_CONFIG_HOME and $HOME are not set.");
-
- r = user_runtime_dir(user_runtime);
- if (r < 0)
- return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
- else if (r == 0)
- return log_error_errno(ENOTDIR, "Cannot find units: $XDG_RUNTIME_DIR is not set.");
- }
-
- r = lookup_paths_init_from_scope(lp, arg_scope, arg_root);
- if (r < 0)
- return log_error_errno(r, "Failed to query unit lookup paths: %m");
-
- return 0;
-}
-
static int cat_file(const char *filename, bool newline) {
_cleanup_close_ int fd;
@@ -4813,8 +4855,6 @@ static int cat_file(const char *filename, bool newline) {
}
static int cat(int argc, char *argv[], void *userdata) {
- _cleanup_free_ char *user_home = NULL;
- _cleanup_free_ char *user_runtime = NULL;
_cleanup_lookup_paths_free_ LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
char **name;
@@ -4827,9 +4867,9 @@ static int cat(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
- r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
+ r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to determine unit paths: %m");
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
@@ -4976,7 +5016,7 @@ static int daemon_reload(int argc, char *argv[], void *userdata) {
* reply */
r = 0;
else if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r));
return r < 0 ? r : 0;
}
@@ -5240,8 +5280,10 @@ static int enable_sysv_units(const char *verb, char **args) {
int r = 0;
#if defined(HAVE_SYSV_COMPAT)
- unsigned f = 0;
_cleanup_lookup_paths_free_ LookupPaths paths = {};
+ unsigned f = 0;
+
+ /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
if (arg_scope != UNIT_FILE_SYSTEM)
return 0;
@@ -5255,24 +5297,28 @@ static int enable_sysv_units(const char *verb, char **args) {
"is-enabled"))
return 0;
- /* Processes all SysV units, and reshuffles the array so that
- * afterwards only the native units remain */
-
- r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, arg_root, NULL, NULL, NULL);
+ r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root);
if (r < 0)
return r;
r = 0;
while (args[f]) {
- const char *name;
+
+ const char *argv[] = {
+ ROOTLIBEXECDIR "/systemd-sysv-install",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ };
+
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
bool found_native = false, found_sysv;
+ siginfo_t status;
+ const char *name;
unsigned c = 1;
- const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL };
- char **k;
- int j;
pid_t pid;
- siginfo_t status;
+ int j;
name = args[f++];
@@ -5282,21 +5328,13 @@ static int enable_sysv_units(const char *verb, char **args) {
if (path_is_absolute(name))
continue;
- STRV_FOREACH(k, paths.unit_path) {
- _cleanup_free_ char *path = NULL;
-
- path = path_join(arg_root, *k, name);
- if (!path)
- return log_oom();
-
- found_native = access(path, F_OK) >= 0;
- if (found_native)
- break;
- }
+ j = unit_file_exists(arg_scope, &paths, name);
+ if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL))
+ return log_error_errno(j, "Failed to lookup unit file state: %m");
+ found_native = j != 0;
- /* If we have both a native unit and a SysV script,
- * enable/disable them both (below); for is-enabled, prefer the
- * native unit */
+ /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled,
+ * prefer the native unit */
if (found_native && streq(verb, "is-enabled"))
continue;
@@ -5310,9 +5348,9 @@ static int enable_sysv_units(const char *verb, char **args) {
continue;
if (found_native)
- log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]);
+ log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]);
else
- log_info("%s is not a native service, redirecting to systemd-sysv-install", name);
+ log_info("%s is not a native service, redirecting to systemd-sysv-install.", name);
if (!isempty(arg_root))
argv[c++] = q = strappend("--root=", arg_root);
@@ -5325,7 +5363,7 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!l)
return log_oom();
- log_info("Executing %s", l);
+ log_info("Executing: %s", l);
pid = fork();
if (pid < 0)
@@ -5337,7 +5375,7 @@ static int enable_sysv_units(const char *verb, char **args) {
(void) reset_signal_mask();
execv(argv[0], (char**) argv);
- log_error_errno(r, "Failed to execute %s: %m", argv[0]);
+ log_error_errno(errno, "Failed to execute %s: %m", argv[0]);
_exit(EXIT_FAILURE);
}
@@ -5359,9 +5397,11 @@ static int enable_sysv_units(const char *verb, char **args) {
}
} else if (status.si_status != 0)
- return -EINVAL;
- } else
+ return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
+ } else {
+ log_error("Unexpected waitid() result.");
return -EPROTO;
+ }
if (found_native)
continue;
@@ -5419,6 +5459,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
int carries_install_info = -1;
+ bool ignore_carries_install_info = false;
int r;
if (!argv[1])
@@ -5432,8 +5473,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- /* If the operation was fully executed by the SysV compat,
- * let's finish early */
+ /* If the operation was fully executed by the SysV compat, let's finish early */
if (strv_isempty(names))
return 0;
@@ -5450,28 +5490,24 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
else if (streq(verb, "preset")) {
r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
- carries_install_info = r;
} else if (streq(verb, "mask"))
r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
else if (streq(verb, "unmask"))
r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
+ else if (streq(verb, "revert"))
+ r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes);
else
assert_not_reached("Unknown verb");
- if (r == -ESHUTDOWN)
- return log_error_errno(r, "Unit file is masked.");
+ unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet);
if (r < 0)
- return log_error_errno(r, "Operation failed: %m");
-
- if (!arg_quiet)
- dump_unit_file_changes(changes, n_changes);
-
+ return r;
r = 0;
} else {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int expect_carries_install_info = false;
- bool send_force = true, send_preset_mode = false;
+ bool expect_carries_install_info = false;
+ bool send_runtime = true, send_force = true, send_preset_mode = false;
const char *method;
sd_bus *bus;
@@ -5501,11 +5537,15 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
method = "PresetUnitFiles";
expect_carries_install_info = true;
+ ignore_carries_install_info = true;
} else if (streq(verb, "mask"))
method = "MaskUnitFiles";
else if (streq(verb, "unmask")) {
method = "UnmaskUnitFiles";
send_force = false;
+ } else if (streq(verb, "revert")) {
+ method = "RevertUnitFiles";
+ send_runtime = send_force = false;
} else
assert_not_reached("Unknown verb");
@@ -5529,9 +5569,11 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
return bus_log_create_error(r);
}
- r = sd_bus_message_append(m, "b", arg_runtime);
- if (r < 0)
- return bus_log_create_error(r);
+ if (send_runtime) {
+ r = sd_bus_message_append(m, "b", arg_runtime);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
if (send_force) {
r = sd_bus_message_append(m, "b", arg_force);
@@ -5541,7 +5583,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r));
if (expect_carries_install_info) {
r = sd_bus_message_read(reply, "b", &carries_install_info);
@@ -5560,16 +5602,19 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = 0;
}
- if (carries_install_info == 0)
- log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
- "using systemctl.\n"
+ if (carries_install_info == 0 && !ignore_carries_install_info)
+ log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n"
+ "settings in the [Install] section, and DefaultInstance for template units).\n"
+ "This means they are not meant to be enabled using systemctl.\n"
"Possible reasons for having this kind of units are:\n"
"1) A unit may be statically enabled by being symlinked from another unit's\n"
" .wants/ or .requires/ directory.\n"
"2) A unit's purpose may be to act as a helper for some other unit which has\n"
" a requirement dependency on it.\n"
"3) A unit may be started when needed via activation (socket, path, timer,\n"
- " D-Bus, udev, scripted systemctl call, ...).\n");
+ " D-Bus, udev, scripted systemctl call, ...).\n"
+ "4) In case of template units, the unit is meant to be enabled with some\n"
+ " instance name specified.");
if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
char *new_args[n_changes + 2];
@@ -5624,16 +5669,9 @@ static int add_dependency(int argc, char *argv[], void *userdata) {
unsigned n_changes = 0;
r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
- if (r == -ESHUTDOWN)
- return log_error_errno(r, "Unit file is masked.");
- if (r < 0)
- return log_error_errno(r, "Can't add dependency: %m");
-
- if (!arg_quiet)
- dump_unit_file_changes(changes, n_changes);
-
+ unit_file_dump_changes(r, "add dependency on", changes, n_changes, arg_quiet);
unit_file_changes_free(changes, n_changes);
-
+ return r;
} else {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -5665,39 +5703,29 @@ static int add_dependency(int argc, char *argv[], void *userdata) {
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to add dependency: %s", bus_error_message(&error, r));
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
- if (!arg_no_reload)
- r = daemon_reload(argc, argv, userdata);
- else
- r = 0;
+ if (arg_no_reload)
+ return 0;
+ return daemon_reload(argc, argv, userdata);
}
-
- return r;
}
static int preset_all(int argc, char *argv[], void *userdata) {
- UnitFileChange *changes = NULL;
- unsigned n_changes = 0;
int r;
if (install_client_side()) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
- if (r < 0) {
- log_error_errno(r, "Operation failed: %m");
- goto finish;
- }
-
- if (!arg_quiet)
- dump_unit_file_changes(changes, n_changes);
-
- r = 0;
-
+ unit_file_dump_changes(r, "preset", changes, n_changes, arg_quiet);
+ unit_file_changes_free(changes, n_changes);
+ return r;
} else {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -5722,22 +5750,16 @@ static int preset_all(int argc, char *argv[], void *userdata) {
arg_runtime,
arg_force);
if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to preset all units: %s", bus_error_message(&error, r));
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
- if (!arg_no_reload)
- r = daemon_reload(argc, argv, userdata);
- else
- r = 0;
+ if (arg_no_reload)
+ return 0;
+ return daemon_reload(argc, argv, userdata);
}
-
-finish:
- unit_file_changes_free(changes, n_changes);
-
- return r;
}
static int unit_is_enabled(int argc, char *argv[], void *userdata) {
@@ -5770,7 +5792,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
UNIT_FILE_ENABLED,
UNIT_FILE_ENABLED_RUNTIME,
UNIT_FILE_STATIC,
- UNIT_FILE_INDIRECT))
+ UNIT_FILE_INDIRECT,
+ UNIT_FILE_GENERATED))
enabled = true;
if (!arg_quiet)
@@ -5805,7 +5828,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect"))
+ if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated"))
enabled = true;
if (!arg_quiet)
@@ -5813,7 +5836,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
}
}
- return !enabled;
+ return enabled ? EXIT_SUCCESS : EXIT_FAILURE;
}
static int is_system_running(int argc, char *argv[], void *userdata) {
@@ -5883,52 +5906,32 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
return 0;
}
-static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) {
- _cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL;
+static int get_file_to_edit(
+ const LookupPaths *paths,
+ const char *name,
+ char **ret_path) {
+
+ _cleanup_free_ char *path = NULL, *run = NULL;
assert(name);
assert(ret_path);
- switch (arg_scope) {
- case UNIT_FILE_SYSTEM:
- path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name);
- if (arg_runtime)
- run = path_join(arg_root, "/run/systemd/system/", name);
- break;
- case UNIT_FILE_GLOBAL:
- path = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
- if (arg_runtime)
- run = path_join(arg_root, "/run/systemd/user/", name);
- break;
- case UNIT_FILE_USER:
- assert(user_home);
- assert(user_runtime);
-
- path = path_join(arg_root, user_home, name);
- if (arg_runtime) {
- path2 = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
- if (!path2)
- return log_oom();
- run = path_join(arg_root, user_runtime, name);
- }
- break;
- default:
- assert_not_reached("Invalid scope");
- }
- if (!path || (arg_runtime && !run))
+ path = strjoin(paths->persistent_config, "/", name, NULL);
+ if (!path)
return log_oom();
if (arg_runtime) {
+ run = strjoin(paths->runtime_config, name, NULL);
+ if (!run)
+ return log_oom();
+ }
+
+ if (arg_runtime) {
if (access(path, F_OK) >= 0) {
log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path);
return -EEXIST;
}
- if (path2 && access(path2, F_OK) >= 0) {
- log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2);
- return -EEXIST;
- }
-
*ret_path = run;
run = NULL;
} else {
@@ -5939,7 +5942,12 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
return 0;
}
-static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
+static int unit_file_create_dropin(
+ const LookupPaths *paths,
+ const char *unit_name,
+ char **ret_new_path,
+ char **ret_tmp_path) {
+
char *tmp_new_path, *tmp_tmp_path, *ending;
int r;
@@ -5948,7 +5956,7 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home,
assert(ret_tmp_path);
ending = strjoina(unit_name, ".d/override.conf");
- r = get_file_to_edit(ending, user_home, user_runtime, &tmp_new_path);
+ r = get_file_to_edit(paths, ending, &tmp_new_path);
if (r < 0)
return r;
@@ -5965,10 +5973,9 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home,
}
static int unit_file_create_copy(
+ const LookupPaths *paths,
const char *unit_name,
const char *fragment_path,
- const char *user_home,
- const char *user_runtime,
char **ret_new_path,
char **ret_tmp_path) {
@@ -5980,7 +5987,7 @@ static int unit_file_create_copy(
assert(ret_new_path);
assert(ret_tmp_path);
- r = get_file_to_edit(unit_name, user_home, user_runtime, &tmp_new_path);
+ r = get_file_to_edit(paths, unit_name, &tmp_new_path);
if (r < 0)
return r;
@@ -6095,8 +6102,6 @@ static int run_editor(char **paths) {
}
static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
- _cleanup_free_ char *user_home = NULL;
- _cleanup_free_ char *user_runtime = NULL;
_cleanup_lookup_paths_free_ LookupPaths lp = {};
char **name;
int r;
@@ -6104,13 +6109,12 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
assert(names);
assert(paths);
- r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
+ r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
if (r < 0)
return r;
STRV_FOREACH(name, names) {
- _cleanup_free_ char *path = NULL;
- char *new_path, *tmp_path;
+ _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL;
r = unit_find_paths(bus, *name, &lp, &path, NULL);
if (r < 0)
@@ -6124,15 +6128,16 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
}
if (arg_full)
- r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path);
+ r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path);
else
- r = unit_file_create_dropin(*name, user_home, user_runtime, &new_path, &tmp_path);
+ r = unit_file_create_dropin(&lp, *name, &new_path, &tmp_path);
if (r < 0)
return r;
r = strv_push_pair(paths, new_path, tmp_path);
if (r < 0)
return log_oom();
+ new_path = tmp_path = NULL;
}
return 0;
@@ -6243,6 +6248,7 @@ static void systemctl_help(void) {
" --job-mode=MODE Specify how to deal with already queued jobs, when\n"
" queueing a new job\n"
" --show-types When showing sockets, explicitly show their type\n"
+ " --value When showing properties, only print the value\n"
" -i --ignore-inhibitors\n"
" When shutting down or sleeping, ignore inhibitors\n"
" --kill-who=WHO Who to send signal to\n"
@@ -6310,6 +6316,8 @@ static void systemctl_help(void) {
" unmask NAME... Unmask one or more units\n"
" link PATH... Link one or more units files into\n"
" the search path\n"
+ " revert NAME... Revert one or more unit files to vendor\n"
+ " version\n"
" add-wants TARGET NAME... Add 'Wants' dependency for the target\n"
" on specified one or more units\n"
" add-requires TARGET NAME... Add 'Requires' dependency for the target\n"
@@ -6494,6 +6502,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_SHOW_TYPES,
ARG_IRREVERSIBLE,
ARG_IGNORE_DEPENDENCIES,
+ ARG_VALUE,
ARG_VERSION,
ARG_USER,
ARG_SYSTEM,
@@ -6535,6 +6544,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */
{ "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */
{ "ignore-inhibitors", no_argument, NULL, 'i' },
+ { "value", no_argument, NULL, ARG_VALUE },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
@@ -6686,6 +6696,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_show_types = true;
break;
+ case ARG_VALUE:
+ arg_value = true;
+ break;
+
case ARG_JOB_MODE:
arg_job_mode = optarg;
break;
@@ -6731,7 +6745,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ r = parse_path_argument_and_warn(optarg, false, &arg_root);
if (r < 0)
return r;
break;
@@ -6972,7 +6986,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
}
if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
- r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL);
+ r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL);
if (r < 0)
return r;
} else if (optind < argc) {
@@ -7427,6 +7441,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "mask", 2, VERB_ANY, 0, enable_unit },
{ "unmask", 2, VERB_ANY, 0, enable_unit },
{ "link", 2, VERB_ANY, 0, enable_unit },
+ { "revert", 2, VERB_ANY, 0, enable_unit },
{ "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root },
{ "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies },
{ "set-default", 2, 2, 0, set_default },
@@ -7473,6 +7488,7 @@ static int start_with_fallback(void) {
}
static int halt_now(enum action a) {
+ int r;
/* The kernel will automaticall flush ATA disks and suchlike
* on reboot(), but the file systems need to be synce'd
@@ -7499,9 +7515,14 @@ static int halt_now(enum action a) {
case ACTION_REBOOT: {
_cleanup_free_ char *param = NULL;
- if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
+ r = read_one_line_file("/run/systemd/reboot-param", &param);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read reboot parameter file: %m");
+
+ if (!isempty(param)) {
log_info("Rebooting with argument '%s'.", param);
(void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
+ log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
}
log_info("Rebooting.");
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index ef45370505..374ff8774e 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -98,6 +98,8 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
const uint8_t *data, size_t data_len);
+int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
+ uint16_t duid_type, uint8_t *duid, size_t duid_len);
int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
const uint8_t **data, size_t *data_len);
int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu);
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 1bedc941aa..4604cb6382 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -85,8 +85,9 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
- size_t duid_len);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t duid_type,
+ uint8_t *duid, size_t duid_len);
+int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid);
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled);
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled);
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index d4c6f409cd..9c36b27157 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -67,10 +67,11 @@ typedef struct sd_journal sd_journal;
/* Open flags */
enum {
- SD_JOURNAL_LOCAL_ONLY = 1,
- SD_JOURNAL_RUNTIME_ONLY = 2,
- SD_JOURNAL_SYSTEM = 4,
- SD_JOURNAL_CURRENT_USER = 8,
+ SD_JOURNAL_LOCAL_ONLY = 1 << 0,
+ SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
+ SD_JOURNAL_SYSTEM = 1 << 2,
+ SD_JOURNAL_CURRENT_USER = 1 << 3,
+ SD_JOURNAL_OS_ROOT = 1 << 4,
SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */
};
@@ -84,8 +85,10 @@ enum {
int sd_journal_open(sd_journal **ret, int flags);
int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
+int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
-int sd_journal_open_container(sd_journal **ret, const char *machine, int flags);
+int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
+int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */
void sd_journal_close(sd_journal *j);
int sd_journal_previous(sd_journal *j);
diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h
index 4f2a3b50c0..5772d5794a 100644
--- a/src/systemd/sd-lldp.h
+++ b/src/systemd/sd-lldp.h
@@ -33,20 +33,18 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_lldp sd_lldp;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
-#define SD_LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
-
/* IEEE 802.3AB Clause 9: TLV Types */
enum {
- SD_LLDP_TYPE_END = 0,
- SD_LLDP_TYPE_CHASSIS_ID = 1,
- SD_LLDP_TYPE_PORT_ID = 2,
- SD_LLDP_TYPE_TTL = 3,
- SD_LLDP_TYPE_PORT_DESCRIPTION = 4,
- SD_LLDP_TYPE_SYSTEM_NAME = 5,
- SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
- SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
- SD_LLDP_TYPE_MGMT_ADDRESS = 8,
- SD_LLDP_TYPE_PRIVATE = 127,
+ SD_LLDP_TYPE_END = 0,
+ SD_LLDP_TYPE_CHASSIS_ID = 1,
+ SD_LLDP_TYPE_PORT_ID = 2,
+ SD_LLDP_TYPE_TTL = 3,
+ SD_LLDP_TYPE_PORT_DESCRIPTION = 4,
+ SD_LLDP_TYPE_SYSTEM_NAME = 5,
+ SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
+ SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
+ SD_LLDP_TYPE_MGMT_ADDRESS = 8,
+ SD_LLDP_TYPE_PRIVATE = 127,
};
/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
@@ -63,28 +61,28 @@ enum {
/* IEEE 802.3AB Clause 9.5.3: Port subtype */
enum {
- SD_LLDP_PORT_SUBTYPE_RESERVED = 0,
- SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
- SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
- SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
- SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
- SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
- SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
- SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
+ SD_LLDP_PORT_SUBTYPE_RESERVED = 0,
+ SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
+ SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
+ SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
+ SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
+ SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
+ SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
+ SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
};
enum {
- SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
- SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
- SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
- SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
- SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
- SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
- SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
- SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
- SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
- SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
- SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
+ SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
+ SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
+ SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
+ SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
+ SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
+ SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
+ SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
+ SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
+ SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
+ SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
+ SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
};
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
@@ -100,18 +98,17 @@ enum {
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \
SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
-
#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
enum {
- SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
- SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
- SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
- SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
- SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
- SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
- SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
+ SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
+ SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
+ SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
+ SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
+ SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
+ SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
+ SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
};
typedef enum sd_lldp_event {
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 59e1a3e921..fe4bbeeb75 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -729,14 +729,50 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
return 0;
}
+static int acquire_search_path(const char *def, const char *envvar, char ***ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char *e;
+ int r;
+
+ assert(def);
+ assert(envvar);
+
+ e = getenv(envvar);
+ if (e) {
+ r = path_split_and_make_absolute(e, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar);
+ }
+
+ if (strv_isempty(l)) {
+ strv_free(l);
+
+ l = strv_new(def, NULL);
+ if (!l)
+ return log_oom();
+ }
+
+ if (!path_strv_resolve_uniq(l, NULL))
+ return log_oom();
+
+ *ret = l;
+ l = NULL;
+
+ return 0;
+}
+
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
+ _cleanup_strv_free_ char **sysvinit_path = NULL;
char **path;
int r;
assert(lp);
- assert(all_services);
- STRV_FOREACH(path, lp->sysvinit_path) {
+ r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(path, sysvinit_path) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -770,11 +806,11 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
if (hashmap_contains(all_services, name))
continue;
- r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
- if (r < 0 && r != -ENOENT) {
+ r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name);
+ if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
continue;
- } else if (r >= 0) {
+ } else if (r != 0) {
log_debug("Native unit for %s already exists, skipping.", name);
continue;
}
@@ -806,6 +842,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
_cleanup_set_free_ Set *shutdown_services = NULL;
+ _cleanup_strv_free_ char **sysvrcnd_path = NULL;
SysvStub *service;
unsigned i;
Iterator j;
@@ -814,7 +851,11 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
assert(lp);
- STRV_FOREACH(p, lp->sysvrcnd_path) {
+ r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, sysvrcnd_path) {
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
_cleanup_closedir_ DIR *d = NULL;
@@ -963,7 +1004,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
+ r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
if (r < 0) {
log_error_errno(r, "Failed to find lookup paths: %m");
goto finish;
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index ad15075a5b..4eb8fcd773 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -21,7 +21,9 @@
#include "macro.h"
#include "manager.h"
+#include "rm-rf.h"
#include "test-helper.h"
+#include "tests.h"
#include "unit.h"
static int test_cgroup_mask(void) {
@@ -33,7 +35,7 @@ static int test_cgroup_mask(void) {
/* Prepare the manager. */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(MANAGER_USER, true, &m);
+ r = manager_new(UNIT_FILE_USER, true, &m);
if (r == -EPERM || r == -EACCES) {
puts("manager_new: Permission denied. Skipping test.");
return EXIT_TEST_SKIP;
@@ -107,7 +109,11 @@ static int test_cgroup_mask(void) {
}
int main(int argc, char* argv[]) {
+ _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
int rc = 0;
+
+ assert_se(runtime_dir = setup_fake_runtime_dir());
TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask());
+
return rc;
}
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index cb437754b4..68154fc4e8 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -95,6 +95,8 @@ static void test_copy_tree(void) {
char **links = STRV_MAKE("link", "file",
"link2", "dir1/file");
char **p, **link;
+ const char *unixsockp;
+ struct stat st;
log_info("%s", __func__);
@@ -102,26 +104,34 @@ static void test_copy_tree(void) {
(void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
STRV_FOREACH(p, files) {
- char *f = strjoina(original_dir, *p);
+ _cleanup_free_ char *f;
+
+ assert_se(f = strappend(original_dir, *p));
assert_se(mkdir_parents(f, 0755) >= 0);
assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
}
STRV_FOREACH_PAIR(link, p, links) {
- char *f = strjoina(original_dir, *p);
- char *l = strjoina(original_dir, *link);
+ _cleanup_free_ char *f, *l;
+
+ assert_se(f = strappend(original_dir, *p));
+ assert_se(l = strappend(original_dir, *link));
assert_se(mkdir_parents(l, 0755) >= 0);
assert_se(symlink(f, l) == 0);
}
+ unixsockp = strjoina(original_dir, "unixsock");
+ assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
+
assert_se(copy_tree(original_dir, copy_dir, true) == 0);
STRV_FOREACH(p, files) {
- _cleanup_free_ char *buf = NULL;
+ _cleanup_free_ char *buf = NULL, *f;
size_t sz = 0;
- char *f = strjoina(copy_dir, *p);
+
+ assert_se(f = strappend(copy_dir, *p));
assert_se(access(f, F_OK) == 0);
assert_se(read_full_file(f, &buf, &sz) == 0);
@@ -129,14 +139,19 @@ static void test_copy_tree(void) {
}
STRV_FOREACH_PAIR(link, p, links) {
- _cleanup_free_ char *target = NULL;
- char *f = strjoina(original_dir, *p);
- char *l = strjoina(copy_dir, *link);
+ _cleanup_free_ char *target = NULL, *f, *l;
+
+ assert_se(f = strjoin(original_dir, *p, NULL));
+ assert_se(l = strjoin(copy_dir, *link, NULL));
assert_se(readlink_and_canonicalize(l, &target) == 0);
assert_se(path_equal(f, target));
}
+ unixsockp = strjoina(copy_dir, "unixsock");
+ assert_se(stat(unixsockp, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+
assert_se(copy_tree(original_dir, copy_dir, false) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index ca66f5b684..361d1e7b0b 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -23,9 +23,12 @@
#include "bus-util.h"
#include "manager.h"
+#include "rm-rf.h"
#include "test-helper.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
Manager *m = NULL;
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
@@ -34,9 +37,11 @@ int main(int argc, char *argv[]) {
Job *j;
int r;
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(MANAGER_USER, true, &m);
+ r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 901cc44af6..77ef4e8b2a 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -291,14 +291,14 @@ static void test_exec_spec_interpolation(Manager *m) {
test(m, "exec-spec-interpolation.service", 0, CLD_EXITED);
}
-static int run_tests(ManagerRunningAs running_as, test_function_t *tests) {
+static int run_tests(UnitFileScope scope, test_function_t *tests) {
test_function_t *test = NULL;
Manager *m = NULL;
int r;
assert_se(tests);
- r = manager_new(running_as, true, &m);
+ r = manager_new(scope, true, &m);
if (MANAGER_SKIP_TEST(r)) {
printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
@@ -366,9 +366,9 @@ int main(int argc, char *argv[]) {
assert_se(unsetenv("VAR2") == 0);
assert_se(unsetenv("VAR3") == 0);
- r = run_tests(MANAGER_USER, user_tests);
+ r = run_tests(UNIT_FILE_USER, user_tests);
if (r != 0)
return r;
- return run_tests(MANAGER_SYSTEM, system_tests);
+ return run_tests(UNIT_FILE_SYSTEM, system_tests);
}
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index cd250ca7b8..4680b0336d 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -30,6 +30,8 @@ static void test_basic_mask_and_enable(const char *root) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
+ log_set_max_level(LOG_DEBUG);
+
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
@@ -78,7 +80,7 @@ static void test_basic_mask_and_enable(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED);
/* Enabling a masked unit should fail! */
- assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN);
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ERFKILL);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
@@ -105,7 +107,7 @@ static void test_basic_mask_and_enable(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
/* Enabling it again should succeed but be a NOP */
- assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 0);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
@@ -604,7 +606,7 @@ static void test_preset_and_list(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(h = hashmap_new(&string_hash_ops));
- assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0);
+ assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
@@ -628,6 +630,57 @@ static void test_preset_and_list(const char *root) {
assert_se(got_yes && got_no);
}
+static void test_revert(const char *root) {
+ const char *p;
+ UnitFileState state;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+
+ assert(root);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/xx.service");
+ assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC);
+
+ /* Initially there's nothing to revert */
+ assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 0);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service");
+ assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ /* Revert the override file */
+ assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf");
+ assert_se(mkdir_parents(p, 0755) >= 0);
+ assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ /* Revert the dropin file */
+ assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ assert_se(streq(changes[0].path, p));
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d");
+ assert_se(changes[1].type == UNIT_FILE_UNLINK);
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -656,6 +709,7 @@ int main(int argc, char *argv[]) {
test_template_enable(root);
test_indirect(root);
test_preset_and_list(root);
+ test_revert(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 874d617621..0ac85f040a 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -46,8 +46,11 @@ int main(int argc, char* argv[]) {
unsigned n_changes = 0;
UnitFileState state = 0;
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+
h = hashmap_new(&string_hash_ops);
- r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
+ r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
assert_se(r == 0);
HASHMAP_FOREACH(p, h, i) {
@@ -65,12 +68,12 @@ int main(int argc, char* argv[]) {
unit_file_list_free(h);
- log_error("enable");
+ log_info("/*** enable **/");
r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
assert_se(r >= 0);
- log_error("enable2");
+ log_info("/*** enable2 **/");
r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
assert_se(r >= 0);
@@ -82,8 +85,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
- log_error("disable");
-
+ log_info("/*** disable ***/");
changes = NULL;
n_changes = 0;
@@ -97,13 +99,13 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
- log_error("mask");
+ log_info("/*** mask ***/");
changes = NULL;
n_changes = 0;
r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
assert_se(r >= 0);
- log_error("mask2");
+ log_info("/*** mask2 ***/");
r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
assert_se(r >= 0);
@@ -114,13 +116,13 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
- log_error("unmask");
+ log_info("/*** unmask ***/");
changes = NULL;
n_changes = 0;
r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
assert_se(r >= 0);
- log_error("unmask2");
+ log_info("/*** unmask2 ***/");
r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
assert_se(r >= 0);
@@ -131,7 +133,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
- log_error("mask");
+ log_info("/*** mask ***/");
changes = NULL;
n_changes = 0;
@@ -145,13 +147,13 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
- log_error("disable");
+ log_info("/*** disable ***/");
changes = NULL;
n_changes = 0;
r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
assert_se(r >= 0);
- log_error("disable2");
+ log_info("/*** disable2 ***/");
r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
assert_se(r >= 0);
@@ -162,7 +164,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
- log_error("umask");
+ log_info("/*** umask ***/");
changes = NULL;
n_changes = 0;
@@ -176,7 +178,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
- log_error("enable files2");
+ log_info("/*** enable files2 ***/");
changes = NULL;
n_changes = 0;
@@ -190,7 +192,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
- log_error("disable files2");
+ log_info("/*** disable files2 ***/");
changes = NULL;
n_changes = 0;
@@ -203,7 +205,7 @@ int main(int argc, char* argv[]) {
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
- log_error("link files2");
+ log_info("/*** link files2 ***/");
changes = NULL;
n_changes = 0;
@@ -217,7 +219,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_LINKED);
- log_error("disable files2");
+ log_info("/*** disable files2 ***/");
changes = NULL;
n_changes = 0;
@@ -230,7 +232,7 @@ int main(int argc, char* argv[]) {
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
- log_error("link files2");
+ log_info("/*** link files2 ***/");
changes = NULL;
n_changes = 0;
@@ -244,7 +246,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_LINKED);
- log_error("reenable files2");
+ log_info("/*** reenable files2 ***/");
changes = NULL;
n_changes = 0;
@@ -258,7 +260,7 @@ int main(int argc, char* argv[]) {
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
- log_error("disable files2");
+ log_info("/*** disable files2 ***/");
changes = NULL;
n_changes = 0;
@@ -270,7 +272,7 @@ int main(int argc, char* argv[]) {
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
- log_error("preset files");
+ log_info("/*** preset files ***/");
changes = NULL;
n_changes = 0;
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index 0b2f9e9173..ff9f35cecd 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -69,8 +69,10 @@ static void test_netns(void) {
int r, n = 0;
siginfo_t si;
- if (geteuid() > 0)
- return;
+ if (geteuid() > 0) {
+ log_info("Skipping test: not root");
+ exit(EXIT_TEST_SKIP);
+ }
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, s) >= 0);
@@ -124,6 +126,9 @@ int main(int argc, char *argv[]) {
char boot_id[SD_ID128_STRING_MAX];
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
+ log_parse_environment();
+ log_open();
+
assert_se(sd_id128_get_boot(&bid) >= 0);
sd_id128_to_string(bid, boot_id);
diff --git a/src/test/test-nss.c b/src/test/test-nss.c
new file mode 100644
index 0000000000..55af592287
--- /dev/null
+++ b/src/test/test-nss.c
@@ -0,0 +1,454 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ 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 <dlfcn.h>
+#include <stdlib.h>
+#include <net/if.h>
+
+#include "log.h"
+#include "nss-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "hexdecoct.h"
+#include "af-list.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "errno-list.h"
+#include "hostname-util.h"
+#include "local-addresses.h"
+
+static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) {
+ switch (status) {
+ case NSS_STATUS_TRYAGAIN:
+ return "NSS_STATUS_TRYAGAIN";
+ case NSS_STATUS_UNAVAIL:
+ return "NSS_STATUS_UNAVAIL";
+ case NSS_STATUS_NOTFOUND:
+ return "NSS_STATUS_NOTFOUND";
+ case NSS_STATUS_SUCCESS:
+ return "NSS_STATUS_SUCCESS";
+ case NSS_STATUS_RETURN:
+ return "NSS_STATUS_RETURN";
+ default:
+ snprintf(buf, buf_len, "%i", status);
+ return buf;
+ }
+};
+
+static const char* af_to_string(int family, char *buf, size_t buf_len) {
+ const char *name;
+
+ if (family == AF_UNSPEC)
+ return "*";
+
+ name = af_to_name(family);
+ if (name)
+ return name;
+
+ snprintf(buf, buf_len, "%i", family);
+ return buf;
+}
+
+static void* open_handle(const char* dir, const char* module, int flags) {
+ const char *path;
+ void *handle;
+
+ if (dir)
+ path = strjoina(dir, "/.libs/libnss_", module, ".so.2");
+ else
+ path = strjoina("libnss_", module, ".so.2");
+
+ handle = dlopen(path, flags);
+ assert_se(handle);
+ return handle;
+}
+
+static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
+ const struct gaih_addrtuple *it;
+ int n = 0;
+
+ for (it = tuples; it; it = it->next) {
+ _cleanup_free_ char *a = NULL;
+ union in_addr_union u;
+ int r;
+ char family_name[DECIMAL_STR_MAX(int)];
+ char ifname[IF_NAMESIZE];
+
+ memcpy(&u, it->addr, 16);
+ r = in_addr_to_string(it->family, &u, &a);
+ assert_se(r == 0 || r == -EAFNOSUPPORT);
+ if (r == -EAFNOSUPPORT)
+ assert_se((a = hexmem(it->addr, 16)));
+
+ if (it->scopeid == 0)
+ goto numerical_index;
+
+ if (if_indextoname(it->scopeid, ifname) == NULL) {
+ log_warning("if_indextoname(%d) failed: %m", it->scopeid);
+ numerical_index:
+ xsprintf(ifname, "%i", it->scopeid);
+ };
+
+ log_info(" \"%s\" %s %s %%%s",
+ it->name,
+ af_to_string(it->family, family_name, sizeof family_name),
+ a,
+ ifname);
+ n ++;
+ }
+ return n;
+}
+
+static void print_struct_hostent(struct hostent *host, const char *canon) {
+ char **s;
+
+ log_info(" \"%s\"", host->h_name);
+ STRV_FOREACH(s, host->h_aliases)
+ log_info(" alias \"%s\"", *s);
+ STRV_FOREACH(s, host->h_addr_list) {
+ union in_addr_union u;
+ _cleanup_free_ char *a = NULL;
+ char family_name[DECIMAL_STR_MAX(int)];
+ int r;
+
+ assert_se((unsigned) host->h_length == FAMILY_ADDRESS_SIZE(host->h_addrtype));
+ memcpy(&u, *s, host->h_length);
+ r = in_addr_to_string(host->h_addrtype, &u, &a);
+ assert_se(r == 0);
+ log_info(" %s %s",
+ af_to_string(host->h_addrtype, family_name, sizeof family_name),
+ a);
+ }
+ if (canon)
+ log_info(" canonical: \"%s\"", canon);
+}
+
+static void test_gethostbyname4_r(void *handle, const char *module, const char *name) {
+ const char *fname;
+ _nss_gethostbyname4_r_t f;
+ char buffer[2000];
+ struct gaih_addrtuple *pat = NULL;
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
+ and will access this variable through *ttlp,
+ so we need to set it to something.
+ I'm not sure if this is a bug in nss-dns
+ or not. */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ int n;
+
+ fname = strjoina("_nss_", module, "_gethostbyname4_r");
+ f = dlsym(handle, fname);
+ log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
+ assert_se(f);
+
+ status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
+ if (status == NSS_STATUS_SUCCESS) {
+ log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
+ fname, name,
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ pat ? (char*) pat - buffer : 0,
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2),
+ ttl);
+ n = print_gaih_addrtuples(pat);
+ } else {
+ log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
+ fname, name,
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ pat,
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2));
+ n = 0;
+ }
+
+ if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL)
+ return;
+
+ if (STR_IN_SET(module, "myhostname", "resolve") && streq(name, "localhost")) {
+ assert_se(status == NSS_STATUS_SUCCESS);
+ assert_se(n == 2);
+ }
+}
+
+
+static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) {
+ const char *fname;
+ _nss_gethostbyname3_r_t f;
+ char buffer[2000];
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl,
+ and will access this variable through *ttlp,
+ so we need to set it to something.
+ I'm not sure if this is a bug in nss-dns
+ or not. */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ struct hostent host;
+ char *canon;
+ char family_name[DECIMAL_STR_MAX(int)];
+
+ fname = strjoina("_nss_", module, "_gethostbyname3_r");
+ f = dlsym(handle, fname);
+ log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
+ assert_se(f);
+
+ status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon);
+ log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
+ fname, name, af_to_string(af, family_name, sizeof family_name),
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2),
+ ttl);
+ if (status == NSS_STATUS_SUCCESS)
+ print_struct_hostent(&host, canon);
+}
+
+static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) {
+ const char *fname;
+ _nss_gethostbyname2_r_t f;
+ char buffer[2000];
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ struct hostent host;
+ char family_name[DECIMAL_STR_MAX(int)];
+
+ fname = strjoina("_nss_", module, "_gethostbyname2_r");
+ f = dlsym(handle, fname);
+ log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
+ assert_se(f);
+
+ status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2);
+ log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
+ fname, name, af_to_string(af, family_name, sizeof family_name),
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2));
+ if (status == NSS_STATUS_SUCCESS)
+ print_struct_hostent(&host, NULL);
+}
+
+static void test_gethostbyname_r(void *handle, const char *module, const char *name) {
+ const char *fname;
+ _nss_gethostbyname_r_t f;
+ char buffer[2000];
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ struct hostent host;
+
+ fname = strjoina("_nss_", module, "_gethostbyname_r");
+ f = dlsym(handle, fname);
+ log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
+ assert_se(f);
+
+ status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2);
+ log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
+ fname, name,
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2));
+ if (status == NSS_STATUS_SUCCESS)
+ print_struct_hostent(&host, NULL);
+}
+
+static void test_gethostbyaddr2_r(void *handle,
+ const char *module,
+ const void* addr, socklen_t len,
+ int af) {
+
+ const char *fname;
+ _nss_gethostbyaddr2_r_t f;
+ char buffer[2000];
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ struct hostent host;
+ int32_t ttl = INT32_MAX;
+ _cleanup_free_ char *addr_pretty = NULL;
+
+ fname = strjoina("_nss_", module, "_gethostbyaddr2_r");
+ f = dlsym(handle, fname);
+
+ log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
+ "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
+ if (!f)
+ return;
+
+ assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
+
+ status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl);
+ log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
+ fname, addr_pretty,
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2),
+ ttl);
+ if (status == NSS_STATUS_SUCCESS)
+ print_struct_hostent(&host, NULL);
+}
+
+static void test_gethostbyaddr_r(void *handle,
+ const char *module,
+ const void* addr, socklen_t len,
+ int af) {
+
+ const char *fname;
+ _nss_gethostbyaddr_r_t f;
+ char buffer[2000];
+ int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */
+ enum nss_status status;
+ char pretty_status[DECIMAL_STR_MAX(enum nss_status)];
+ struct hostent host;
+ _cleanup_free_ char *addr_pretty = NULL;
+
+ fname = strjoina("_nss_", module, "_gethostbyaddr_r");
+ f = dlsym(handle, fname);
+
+ log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
+ "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
+ if (!f)
+ return;
+
+ assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
+
+ status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2);
+ log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
+ fname, addr_pretty,
+ nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n",
+ errno1, errno_to_name(errno1) ?: "---",
+ errno2, hstrerror(errno2));
+ if (status == NSS_STATUS_SUCCESS)
+ print_struct_hostent(&host, NULL);
+}
+
+static void test_byname(void *handle, const char *module, const char *name) {
+ test_gethostbyname4_r(handle, module, name);
+ puts("");
+
+ test_gethostbyname3_r(handle, module, name, AF_INET);
+ puts("");
+ test_gethostbyname3_r(handle, module, name, AF_INET6);
+ puts("");
+ test_gethostbyname3_r(handle, module, name, AF_UNSPEC);
+ puts("");
+ test_gethostbyname3_r(handle, module, name, AF_LOCAL);
+ puts("");
+
+ test_gethostbyname2_r(handle, module, name, AF_INET);
+ puts("");
+ test_gethostbyname2_r(handle, module, name, AF_INET6);
+ puts("");
+ test_gethostbyname2_r(handle, module, name, AF_UNSPEC);
+ puts("");
+ test_gethostbyname2_r(handle, module, name, AF_LOCAL);
+ puts("");
+
+ test_gethostbyname_r(handle, module, name);
+ puts("");
+}
+
+static void test_byaddr(void *handle,
+ const char *module,
+ const void* addr, socklen_t len,
+ int af) {
+ test_gethostbyaddr2_r(handle, module, addr, len, af);
+ puts("");
+
+ test_gethostbyaddr_r(handle, module, addr, len, af);
+ puts("");
+}
+
+#ifdef HAVE_MYHOSTNAME
+# define MODULE1 "myhostname\0"
+#else
+# define MODULE1
+#endif
+#ifdef HAVE_RESOLVED
+# define MODULE2 "resolve\0"
+#else
+# define MODULE2
+#endif
+#ifdef HAVE_MACHINED
+# define MODULE3 "mymachines\0"
+#else
+# define MODULE3
+#endif
+#define MODULE4 "dns\0"
+
+int main(int argc, char **argv) {
+ _cleanup_free_ char *dir = NULL, *hostname = NULL;
+ const char *module;
+
+ const uint32_t local_address_ipv4 = htonl(0x7F000001);
+ const uint32_t local_address_ipv4_2 = htonl(0x7F000002);
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n_addresses;
+
+ log_set_max_level(LOG_INFO);
+ log_parse_environment();
+
+ dir = dirname_malloc(argv[0]);
+ assert_se(dir);
+
+ hostname = gethostname_malloc();
+ assert_se(hostname);
+
+ n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
+ if (n_addresses < 0) {
+ log_info_errno(n_addresses, "Failed to query local addresses: %m");
+ n_addresses = 0;
+ }
+
+ NULSTR_FOREACH(module, MODULE1 MODULE2 MODULE3 MODULE4) {
+ void *handle;
+ const char *name;
+ int i;
+
+ log_info("======== %s ========", module);
+
+ handle = open_handle(streq(module, "dns") ? NULL : dir,
+ module,
+ RTLD_LAZY|RTLD_NODELETE);
+ NULSTR_FOREACH(name, "localhost\0" "gateway\0" "foo_no_such_host\0")
+ test_byname(handle, module, name);
+
+ test_byname(handle, module, hostname);
+
+ test_byaddr(handle, module, &local_address_ipv4, sizeof local_address_ipv4, AF_INET);
+ test_byaddr(handle, module, &local_address_ipv4_2, sizeof local_address_ipv4_2, AF_INET);
+ test_byaddr(handle, module, &in6addr_loopback, sizeof in6addr_loopback, AF_INET6);
+
+ for (i = 0; i < n_addresses; i++)
+ test_byaddr(handle, module,
+ &addresses[i].address,
+ FAMILY_ADDRESS_SIZE(addresses[i].family),
+ addresses[i].family);
+
+ dlclose(handle);
+
+ log_info(" ");
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
index 268da002a9..096326d176 100644
--- a/src/test/test-path-lookup.c
+++ b/src/test/test-path-lookup.c
@@ -26,41 +26,38 @@
#include "string-util.h"
#include "strv.h"
-static void test_paths(ManagerRunningAs running_as, bool personal) {
+static void test_paths(UnitFileScope scope) {
char template[] = "/tmp/test-path-lookup.XXXXXXX";
_cleanup_lookup_paths_free_ LookupPaths lp_without_env = {};
_cleanup_lookup_paths_free_ LookupPaths lp_with_env = {};
- char *exists, *not, *systemd_unit_path;
+ char *systemd_unit_path;
assert_se(mkdtemp(template));
- exists = strjoina(template, "/exists");
- assert_se(mkdir(exists, 0755) == 0);
- not = strjoina(template, "/not");
assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
- assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0);
-
- assert_se(!strv_isempty(lp_without_env.unit_path));
- assert_se(strv_contains(lp_without_env.unit_path, exists));
- assert_se(strv_contains(lp_without_env.unit_path, not));
+ assert_se(lookup_paths_init(&lp_without_env, scope, 0, NULL) >= 0);
+ assert_se(!strv_isempty(lp_without_env.search_path));
+ assert_se(lookup_paths_reduce(&lp_without_env) >= 0);
systemd_unit_path = strjoina(template, "/systemd-unit-path");
assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0);
- assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0);
- assert_se(strv_length(lp_with_env.unit_path) == 1);
- assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path));
+ assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0);
+ assert_se(strv_length(lp_with_env.search_path) == 1);
+ assert_se(streq(lp_with_env.search_path[0], systemd_unit_path));
+ assert_se(lookup_paths_reduce(&lp_with_env) >= 0);
+ assert_se(strv_length(lp_with_env.search_path) == 0);
assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
-static void print_generator_paths(ManagerRunningAs running_as) {
+static void print_generator_binary_paths(UnitFileScope scope) {
_cleanup_strv_free_ char **paths;
char **dir;
- log_info("Generators dirs (%s):", running_as == MANAGER_SYSTEM ? "system" : "user");
+ log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
- paths = generator_paths(running_as);
+ paths = generator_binary_paths(scope);
STRV_FOREACH(dir, paths)
log_info(" %s", *dir);
}
@@ -70,13 +67,12 @@ int main(int argc, char **argv) {
log_parse_environment();
log_open();
- test_paths(MANAGER_SYSTEM, false);
- test_paths(MANAGER_SYSTEM, true);
- test_paths(MANAGER_USER, false);
- test_paths(MANAGER_USER, true);
+ test_paths(UNIT_FILE_SYSTEM);
+ test_paths(UNIT_FILE_USER);
+ test_paths(UNIT_FILE_GLOBAL);
- print_generator_paths(MANAGER_SYSTEM);
- print_generator_paths(MANAGER_USER);
+ print_generator_binary_paths(UNIT_FILE_SYSTEM);
+ print_generator_binary_paths(UNIT_FILE_USER);
return EXIT_SUCCESS;
}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index d376dd56c5..b53324b5e6 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -90,6 +90,18 @@ static void test_path(void) {
assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
assert_se(path_equal(path_kill_slashes(p3), "/./"));
}
+
+ assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo"));
+ assert_se(PATH_IN_SET("/bin", "/bin"));
+ assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin"));
+ assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar"));
+ assert_se(!PATH_IN_SET("/", "/abc", "/def"));
+
+ assert_se(path_equal_ptr(NULL, NULL));
+ assert_se(path_equal_ptr("/a", "/a"));
+ assert_se(!path_equal_ptr("/a", "/b"));
+ assert_se(!path_equal_ptr("/a", NULL));
+ assert_se(!path_equal_ptr(NULL, "/a"));
}
static void test_find_binary(const char *self) {
@@ -477,6 +489,27 @@ static void test_filename_is_valid(void) {
assert_se(filename_is_valid("o.o"));
}
+static void test_hidden_or_backup_file(void) {
+ assert_se(hidden_or_backup_file(".hidden"));
+ assert_se(hidden_or_backup_file("..hidden"));
+ assert_se(!hidden_or_backup_file("hidden."));
+
+ assert_se(hidden_or_backup_file("backup~"));
+ assert_se(hidden_or_backup_file(".backup~"));
+
+ assert_se(hidden_or_backup_file("lost+found"));
+ assert_se(hidden_or_backup_file("aquota.user"));
+ assert_se(hidden_or_backup_file("aquota.group"));
+
+ assert_se(hidden_or_backup_file("test.rpmnew"));
+ assert_se(hidden_or_backup_file("test.dpkg-old"));
+ assert_se(hidden_or_backup_file("test.dpkg-remove"));
+ assert_se(hidden_or_backup_file("test.swp"));
+
+ assert_se(!hidden_or_backup_file("test.rpmnew."));
+ assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
+}
+
int main(int argc, char **argv) {
test_path();
test_find_binary(argv[0]);
@@ -490,6 +523,7 @@ int main(int argc, char **argv) {
test_path_is_mount_point();
test_file_in_same_dir();
test_filename_is_valid();
+ test_hidden_or_backup_file();
return 0;
}
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 1e704a03dc..435cafd83a 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -30,6 +30,7 @@
#include "string-util.h"
#include "strv.h"
#include "test-helper.h"
+#include "tests.h"
#include "unit.h"
#include "util.h"
@@ -44,7 +45,7 @@ static int setup_test(Manager **m) {
assert_se(m);
- r = manager_new(MANAGER_USER, true, &tmp);
+ r = manager_new(UNIT_FILE_USER, true, &tmp);
if (MANAGER_SKIP_TEST(r)) {
printf("Skipping test: manager_new: %s\n", strerror(-r));
return -EXIT_TEST_SKIP;
@@ -243,7 +244,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
}
int main(int argc, char *argv[]) {
- test_function_t tests[] = {
+ static const test_function_t tests[] = {
test_path_exists,
test_path_existsglob,
test_path_changed,
@@ -253,12 +254,15 @@ int main(int argc, char *argv[]) {
test_path_makedirectory_directorymode,
NULL,
};
- test_function_t *test = NULL;
+
+ _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
+ const test_function_t *test = NULL;
Manager *m = NULL;
log_parse_environment();
log_open();
+ assert_se(runtime_dir = setup_fake_runtime_dir());
assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0);
for (test = tests; test && *test; test++) {
diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c
deleted file mode 100644
index 8ae416c557..0000000000
--- a/src/test/test-rbtree.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/***
- This file is part of systemd. See COPYING for details.
-
- 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/>.
-***/
-
-/*
- * Tests for RB-Tree
- */
-
-#undef NDEBUG
-#include <assert.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include "c-rbtree.h"
-
-/* verify that all API calls are exported */
-static void test_api(void) {
- CRBTree t = {};
- CRBNode n = C_RBNODE_INIT(n);
-
- assert(!c_rbnode_is_linked(&n));
-
- /* init, is_linked, add, remove, remove_init */
-
- c_rbtree_add(&t, NULL, &t.root, &n);
- assert(c_rbnode_is_linked(&n));
-
- c_rbtree_remove_init(&t, &n);
- assert(!c_rbnode_is_linked(&n));
-
- c_rbtree_add(&t, NULL, &t.root, &n);
- assert(c_rbnode_is_linked(&n));
-
- c_rbtree_remove(&t, &n);
- assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */
-
- c_rbnode_init(&n);
- assert(!c_rbnode_is_linked(&n));
-
- /* first, last, leftmost, rightmost, next, prev */
-
- assert(!c_rbtree_first(&t));
- assert(!c_rbtree_last(&t));
- assert(&n == c_rbnode_leftmost(&n));
- assert(&n == c_rbnode_rightmost(&n));
- assert(!c_rbnode_next(&n));
- assert(!c_rbnode_prev(&n));
-}
-
-/* copied from c-rbtree.c, relies on internal representation */
-static inline _Bool c_rbnode_is_red(CRBNode *n) {
- return !((unsigned long)n->__parent_and_color & 1UL);
-}
-
-/* copied from c-rbtree.c, relies on internal representation */
-static inline _Bool c_rbnode_is_black(CRBNode *n) {
- return !!((unsigned long)n->__parent_and_color & 1UL);
-}
-
-static size_t validate(CRBTree *t) {
- unsigned int i_black, n_black;
- CRBNode *n, *p, *o;
- size_t count = 0;
-
- assert(t);
- assert(!t->root || c_rbnode_is_black(t->root));
-
- /* traverse to left-most child, count black nodes */
- i_black = 0;
- n = t->root;
- while (n && n->left) {
- if (c_rbnode_is_black(n))
- ++i_black;
- n = n->left;
- }
- n_black = i_black;
-
- /*
- * Traverse tree and verify correctness:
- * 1) A node is either red or black
- * 2) The root is black
- * 3) All leaves are black
- * 4) Every red node must have two black child nodes
- * 5) Every path to a leaf contains the same number of black nodes
- *
- * Note that NULL nodes are considered black, which is why we don't
- * check for 3).
- */
- o = NULL;
- while (n) {
- ++count;
-
- /* verify natural order */
- assert(n > o);
- o = n;
-
- /* verify consistency */
- assert(!n->right || c_rbnode_parent(n->right) == n);
- assert(!n->left || c_rbnode_parent(n->left) == n);
-
- /* verify 2) */
- if (!c_rbnode_parent(n))
- assert(c_rbnode_is_black(n));
-
- if (c_rbnode_is_red(n)) {
- /* verify 4) */
- assert(!n->left || c_rbnode_is_black(n->left));
- assert(!n->right || c_rbnode_is_black(n->right));
- } else {
- /* verify 1) */
- assert(c_rbnode_is_black(n));
- }
-
- /* verify 5) */
- if (!n->left && !n->right)
- assert(i_black == n_black);
-
- /* get next node */
- if (n->right) {
- n = n->right;
- if (c_rbnode_is_black(n))
- ++i_black;
-
- while (n->left) {
- n = n->left;
- if (c_rbnode_is_black(n))
- ++i_black;
- }
- } else {
- while ((p = c_rbnode_parent(n)) && n == p->right) {
- n = p;
- if (c_rbnode_is_black(p->right))
- --i_black;
- }
-
- n = p;
- if (p && c_rbnode_is_black(p->left))
- --i_black;
- }
- }
-
- return count;
-}
-
-static void insert(CRBTree *t, CRBNode *n) {
- CRBNode **i, *p;
-
- assert(t);
- assert(n);
- assert(!c_rbnode_is_linked(n));
-
- i = &t->root;
- p = NULL;
- while (*i) {
- p = *i;
- if (n < *i) {
- i = &(*i)->left;
- } else {
- assert(n > *i);
- i = &(*i)->right;
- }
- }
-
- c_rbtree_add(t, p, i, n);
-}
-
-static void shuffle(void **nodes, size_t n_memb) {
- unsigned int i, j;
- void *t;
-
- for (i = 0; i < n_memb; ++i) {
- j = rand() % n_memb;
- t = nodes[j];
- nodes[j] = nodes[i];
- nodes[i] = t;
- }
-}
-
-/* run some pseudo-random tests on the tree */
-static void test_shuffle(void) {
- CRBNode *nodes[256];
- CRBTree t = {};
- unsigned int i, j;
- size_t n;
-
- /* allocate and initialize all nodes */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- nodes[i] = malloc(sizeof(*nodes[i]));
- assert(nodes[i]);
- c_rbnode_init(nodes[i]);
- }
-
- /* shuffle nodes and validate *empty* tree */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
- n = validate(&t);
- assert(n == 0);
-
- /* add all nodes and validate after each insertion */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- insert(&t, nodes[i]);
- n = validate(&t);
- assert(n == i + 1);
- }
-
- /* shuffle nodes again */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
-
- /* remove all nodes (in different order) and validate on each round */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- c_rbtree_remove(&t, nodes[i]);
- n = validate(&t);
- assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
- c_rbnode_init(nodes[i]);
- }
-
- /* shuffle nodes and validate *empty* tree again */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
- n = validate(&t);
- assert(n == 0);
-
- /* add all nodes again */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- insert(&t, nodes[i]);
- n = validate(&t);
- assert(n == i + 1);
- }
-
- /* 4 times, remove half of the nodes and add them again */
- for (j = 0; j < 4; ++j) {
- /* shuffle nodes again */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
-
- /* remove half of the nodes */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
- c_rbtree_remove(&t, nodes[i]);
- n = validate(&t);
- assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
- c_rbnode_init(nodes[i]);
- }
-
- /* shuffle the removed half */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2);
-
- /* add the removed half again */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
- insert(&t, nodes[i]);
- n = validate(&t);
- assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1);
- }
- }
-
- /* shuffle nodes again */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
-
- /* remove all */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- c_rbtree_remove(&t, nodes[i]);
- n = validate(&t);
- assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
- c_rbnode_init(nodes[i]);
- }
-
- /* free nodes again */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
- free(nodes[i]);
-}
-
-typedef struct {
- unsigned long key;
- CRBNode rb;
-} Node;
-
-#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
-
-static int compare(CRBTree *t, void *k, CRBNode *n) {
- unsigned long key = (unsigned long)k;
- Node *node = node_from_rb(n);
-
- return (key < node->key) ? -1 : (key > node->key) ? 1 : 0;
-}
-
-/* run tests against the c_rbtree_find*() helpers */
-static void test_map(void) {
- CRBNode **slot, *p;
- CRBTree t = {};
- Node *nodes[2048];
- unsigned long i;
-
- /* allocate and initialize all nodes */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- nodes[i] = malloc(sizeof(*nodes[i]));
- assert(nodes[i]);
- nodes[i]->key = i;
- c_rbnode_init(&nodes[i]->rb);
- }
-
- /* shuffle nodes */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
-
- /* add all nodes, and verify that each node is linked */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- assert(!c_rbnode_is_linked(&nodes[i]->rb));
- assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
-
- slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p);
- assert(slot);
- c_rbtree_add(&t, p, slot, &nodes[i]->rb);
-
- assert(c_rbnode_is_linked(&nodes[i]->rb));
- assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
- }
-
- /* shuffle nodes again */
- shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
-
- /* remove all nodes (in different order) */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
- assert(c_rbnode_is_linked(&nodes[i]->rb));
- assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
-
- c_rbtree_remove_init(&t, &nodes[i]->rb);
-
- assert(!c_rbnode_is_linked(&nodes[i]->rb));
- assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
- }
-
- /* free nodes again */
- for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
- free(nodes[i]);
-}
-
-int main(int argc, char **argv) {
- unsigned int i;
-
- /* we want stable tests, so use fixed seed */
- srand(0xdeadbeef);
-
- test_api();
-
- /*
- * The tests are pseudo random; run them multiple times, each run will
- * have different orders and thus different results.
- */
- for (i = 0; i < 4; ++i) {
- test_shuffle();
- test_map();
- }
-
- return 0;
-}
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
index d9ac9368cd..62afd2de5e 100644
--- a/src/test/test-rlimit-util.c
+++ b/src/test/test-rlimit-util.c
@@ -99,6 +99,18 @@ int main(int argc, char *argv[]) {
test_rlimit_parse_format(RLIMIT_NOFILE, "", 0, 0, -EINVAL, NULL);
test_rlimit_parse_format(RLIMIT_NOFILE, "5:4", 0, 0, -EILSEQ, NULL);
test_rlimit_parse_format(RLIMIT_NOFILE, "5:4:3", 0, 0, -EINVAL, NULL);
+ test_rlimit_parse_format(RLIMIT_NICE, "20", 20, 20, 0, "20");
+ test_rlimit_parse_format(RLIMIT_NICE, "40", 40, 40, 0, "40");
+ test_rlimit_parse_format(RLIMIT_NICE, "41", 41, 41, -ERANGE, "41");
+ test_rlimit_parse_format(RLIMIT_NICE, "0", 0, 0, 0, "0");
+ test_rlimit_parse_format(RLIMIT_NICE, "-7", 27, 27, 0, "27");
+ test_rlimit_parse_format(RLIMIT_NICE, "-20", 40, 40, 0, "40");
+ test_rlimit_parse_format(RLIMIT_NICE, "-21", 41, 41, -ERANGE, "41");
+ test_rlimit_parse_format(RLIMIT_NICE, "-0", 20, 20, 0, "20");
+ test_rlimit_parse_format(RLIMIT_NICE, "+7", 13, 13, 0, "13");
+ test_rlimit_parse_format(RLIMIT_NICE, "+19", 1, 1, 0, "1");
+ test_rlimit_parse_format(RLIMIT_NICE, "+20", 0, 0, -ERANGE, "0");
+ test_rlimit_parse_format(RLIMIT_NICE, "+0", 20, 20, 0, "20");
return 0;
}
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index 7f515b53d8..3e9caafc71 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -21,9 +21,12 @@
#include "macro.h"
#include "manager.h"
+#include "rm-rf.h"
#include "test-helper.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
Manager *m = NULL;
Unit *idle_ok, *idle_bad, *rr_ok, *rr_bad, *rr_sched;
Service *ser;
@@ -31,9 +34,11 @@ int main(int argc, char *argv[]) {
FDSet *fdset = NULL;
int r;
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
- r = manager_new(MANAGER_USER, true, &m);
+ r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index fea1f848cd..fc01dcfaf1 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -358,7 +358,7 @@ static void test_strv_extend_strv_concat(void) {
}
static void test_strv_extend_strv(void) {
- _cleanup_strv_free_ char **a = NULL, **b = NULL;
+ _cleanup_strv_free_ char **a = NULL, **b = NULL, **n = NULL;
a = strv_new("abc", "def", "ghi", NULL);
b = strv_new("jkl", "mno", "abc", "pqr", NULL);
@@ -373,8 +373,14 @@ static void test_strv_extend_strv(void) {
assert_se(streq(a[3], "jkl"));
assert_se(streq(a[4], "mno"));
assert_se(streq(a[5], "pqr"));
-
assert_se(strv_length(a) == 6);
+
+ assert_se(strv_extend_strv(&n, b, false) >= 0);
+ assert_se(streq(n[0], "jkl"));
+ assert_se(streq(n[1], "mno"));
+ assert_se(streq(n[2], "abc"));
+ assert_se(streq(n[3], "pqr"));
+ assert_se(strv_length(n) == 4);
}
static void test_strv_extend(void) {
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index d7223dd2bf..b34ebeefb2 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -32,15 +32,17 @@
#include "util.h"
int main(int argc, char** argv) {
+ _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL;
+ _cleanup_close_ int fd = -1, fd2 = -1;
const char *p = argv[1] ?: "/tmp";
- char *pattern = strjoina(p, "/systemd-test-XXXXXX");
- _cleanup_close_ int fd, fd2;
- _cleanup_free_ char *cmd, *cmd2, *ans, *ans2;
+ char *pattern;
log_set_max_level(LOG_DEBUG);
log_parse_environment();
- fd = open_tmpfile(p, O_RDWR|O_CLOEXEC);
+ pattern = strjoina(p, "/systemd-test-XXXXXX");
+
+ fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
@@ -59,5 +61,21 @@ int main(int argc, char** argv) {
log_debug("link2: %s", ans2);
assert_se(endswith(ans2, " (deleted)"));
+ pattern = strjoina(p, "/tmpfiles-test");
+ assert_se(tempfn_random(pattern, NULL, &d) >= 0);
+
+ fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp);
+ assert_se(fd >= 0);
+ assert_se(write(fd, "foobar\n", 7) == 7);
+
+ assert_se(touch(d) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d) == -EEXIST);
+ assert_se(unlink(d) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d) >= 0);
+
+ assert_se(read_one_line_file(d, &line) >= 0);
+ assert_se(streq(line, "foobar"));
+ assert_se(unlink(d) >= 0);
+
return 0;
}
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index d01789fe08..e965b4494a 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -27,6 +27,7 @@
#include <unistd.h>
#include "fs-util.h"
+#include "log.h"
#include "missing.h"
#include "selinux-util.h"
#include "signal-util.h"
@@ -39,39 +40,31 @@ static int fake_filesystems(void) {
const char *src;
const char *target;
const char *error;
+ bool ignore_mount_error;
} fakefss[] = {
- { "test/sys", "/sys", "failed to mount test /sys" },
- { "test/dev", "/dev", "failed to mount test /dev" },
- { "test/run", "/run", "failed to mount test /run" },
- { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d" },
- { "test/run", UDEVLIBEXECDIR "/rules.d","failed to mount empty " UDEVLIBEXECDIR "/rules.d" },
+ { "test/tmpfs/sys", "/sys", "failed to mount test /sys", false },
+ { "test/tmpfs/dev", "/dev", "failed to mount test /dev", false },
+ { "test/run", "/run", "failed to mount test /run", false },
+ { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d", true },
+ { "test/run", UDEVLIBEXECDIR "/rules.d", "failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
};
unsigned int i;
- int err;
- err = unshare(CLONE_NEWNS);
- if (err < 0) {
- err = -errno;
- fprintf(stderr, "failed to call unshare(): %m\n");
- goto out;
- }
+ if (unshare(CLONE_NEWNS) < 0)
+ return log_error_errno(errno, "failed to call unshare(): %m");
- if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) {
- err = -errno;
- fprintf(stderr, "failed to mount / as private: %m\n");
- goto out;
- }
+ if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "failed to mount / as private: %m");
for (i = 0; i < ELEMENTSOF(fakefss); i++) {
- err = mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL);
- if (err < 0) {
- err = -errno;
- fprintf(stderr, "%s %m\n", fakefss[i].error);
- return err;
+ if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
+ log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error);
+ if (!fakefss[i].ignore_mount_error)
+ return -errno;
}
}
-out:
- return err;
+
+ return 0;
}
int main(int argc, char *argv[]) {
@@ -84,6 +77,9 @@ int main(int argc, char *argv[]) {
const char *action;
int err;
+ log_parse_environment();
+ log_open();
+
err = fake_filesystems();
if (err < 0)
return EXIT_FAILURE;
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index cc6c61ba63..c340673c6c 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -35,10 +35,12 @@
#include "install.h"
#include "load-fragment.h"
#include "macro.h"
+#include "rm-rf.h"
#include "specifier.h"
#include "string-util.h"
#include "strv.h"
#include "test-helper.h"
+#include "tests.h"
#include "user-util.h"
#include "util.h"
@@ -51,7 +53,7 @@ static int test_unit_file_get_set(void) {
h = hashmap_new(&string_hash_ops);
assert_se(h);
- r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
+ r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
if (r == -EPERM || r == -EACCES) {
printf("Skipping test: unit_file_get_list: %s", strerror(-r));
@@ -113,7 +115,7 @@ static void test_config_parse_exec(void) {
Manager *m = NULL;
Unit *u = NULL;
- r = manager_new(MANAGER_USER, true, &m);
+ r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
printf("Skipping test: manager_new: %s\n", strerror(-r));
return;
@@ -840,11 +842,14 @@ static void test_config_parse_pass_environ(void) {
}
int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
int r;
log_parse_environment();
log_open();
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
r = test_unit_file_get_set();
test_config_parse_exec();
test_config_parse_capability_set();
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index 3de94ef425..2fd83f321c 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -209,7 +209,7 @@ static int test_unit_printf(void) {
assert_se(get_home_dir(&home) >= 0);
assert_se(get_shell(&shell) >= 0);
- r = manager_new(MANAGER_USER, true, &m);
+ r = manager_new(UNIT_FILE_USER, true, &m);
if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
puts("manager_new: Permission denied. Skipping test.");
return EXIT_TEST_SKIP;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index efd264b34d..2053d35a67 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -94,6 +94,7 @@ typedef enum ItemType {
/* These ones take globs */
WRITE_FILE = 'w',
+ EMPTY_DIRECTORY = 'e',
SET_XATTR = 't',
RECURSIVE_SET_XATTR = 'T',
SET_ACL = 'a',
@@ -179,6 +180,7 @@ static bool needs_glob(ItemType t) {
IGNORE_DIRECTORY_PATH,
REMOVE_PATH,
RECURSIVE_REMOVE_PATH,
+ EMPTY_DIRECTORY,
ADJUST_MODE,
RELABEL_PATH,
RECURSIVE_RELABEL_PATH,
@@ -195,6 +197,7 @@ static bool takes_ownership(ItemType t) {
CREATE_FILE,
TRUNCATE_FILE,
CREATE_DIRECTORY,
+ EMPTY_DIRECTORY,
TRUNCATE_DIRECTORY,
CREATE_SUBVOLUME,
CREATE_SUBVOLUME_INHERIT_QUOTA,
@@ -1217,7 +1220,6 @@ static int create_item(Item *i) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
-
RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
@@ -1276,11 +1278,11 @@ static int create_item(Item *i) {
if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
if (r == -ENOTTY)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
else if (r == -EROFS)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
else if (r == -ENOPROTOOPT)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
else if (r < 0)
q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
else if (r > 0)
@@ -1289,6 +1291,9 @@ static int create_item(Item *i) {
log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
}
+ /* fall through */
+
+ case EMPTY_DIRECTORY:
r = path_set_perms(i, i->path);
if (q < 0)
return q;
@@ -1298,7 +1303,6 @@ static int create_item(Item *i) {
break;
case CREATE_FIFO:
-
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
@@ -1535,47 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) {
}
static int remove_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case CREATE_SUBVOLUME:
- case CREATE_SUBVOLUME_INHERIT_QUOTA:
- case CREATE_SUBVOLUME_NEW_QUOTA:
- case CREATE_FIFO:
- case CREATE_SYMLINK:
- case CREATE_CHAR_DEVICE:
- case CREATE_BLOCK_DEVICE:
- case IGNORE_PATH:
- case IGNORE_DIRECTORY_PATH:
- case ADJUST_MODE:
- case RELABEL_PATH:
- case RECURSIVE_RELABEL_PATH:
- case WRITE_FILE:
- case COPY_FILES:
- case SET_XATTR:
- case RECURSIVE_SET_XATTR:
- case SET_ACL:
- case RECURSIVE_SET_ACL:
- case SET_ATTRIBUTE:
- case RECURSIVE_SET_ATTRIBUTE:
- break;
-
case REMOVE_PATH:
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
- r = glob_item(i, remove_item_instance, false);
- break;
- }
+ return glob_item(i, remove_item_instance, false);
- return r;
+ default:
+ return 0;
+ }
}
static int clean_item_instance(Item *i, const char* instance) {
@@ -1630,8 +1607,6 @@ static int clean_item_instance(Item *i, const char* instance) {
}
static int clean_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
@@ -1641,19 +1616,17 @@ static int clean_item(Item *i) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
clean_item_instance(i, i->path);
- break;
+ return 0;
case IGNORE_DIRECTORY_PATH:
- r = glob_item(i, clean_item_instance, false);
- break;
+ return glob_item(i, clean_item_instance, false);
default:
- break;
+ return 0;
}
-
- return r;
}
static int process_item_array(ItemArray *array);
@@ -1879,6 +1852,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
@@ -2198,7 +2172,8 @@ static int parse_argv(int argc, char *argv[]) {
}
static int read_config_file(const char *fn, bool ignore_enoent) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_fclose_ FILE *_f = NULL;
+ FILE *f;
char line[LINE_MAX];
Iterator iterator;
unsigned v = 0;
@@ -2207,16 +2182,23 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
assert(fn);
- r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
- if (r < 0) {
- if (ignore_enoent && r == -ENOENT) {
- log_debug_errno(r, "Failed to open \"%s\": %m", fn);
- return 0;
- }
+ if (streq(fn, "-")) {
+ log_debug("Reading config from stdin.");
+ fn = "<stdin>";
+ f = stdin;
+ } else {
+ r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT) {
+ log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
+ return 0;
+ }
- return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
+ return log_error_errno(r, "Failed to open '%s': %m", fn);
+ }
+ log_debug("Reading config file \"%s\".", fn);
+ f = _f;
}
- log_debug("Reading config file \"%s\".", fn);
FOREACH_LINE(line, f, break) {
char *l;
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 7b67831e54..c7ded451a2 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -481,7 +481,7 @@ static int show_passwords(void) {
if (de->d_type != DT_REG)
continue;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (!startswith(de->d_name, "ask."))
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 8b1bcefe2d..a7be2a4eed 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -27,21 +27,21 @@
* http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
*
* Two character prefixes based on the type of interface:
- * en -- Ethernet
- * sl -- serial line IP (slip)
- * wl -- wlan
- * ww -- wwan
+ * en — Ethernet
+ * sl — serial line IP (slip)
+ * wl — wlan
+ * ww — wwan
*
* Type of names:
- * b<number> -- BCMA bus core number
- * c<bus_id> -- CCW bus group name, without leading zeros [s390]
- * o<index>[d<dev_port>] -- on-board device index number
- * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
- * x<MAC> -- MAC address
+ * b<number> — BCMA bus core number
+ * c<bus_id> — CCW bus group name, without leading zeros [s390]
+ * o<index>[d<dev_port>] — on-board device index number
+ * s<slot>[f<function>][d<dev_port>] — hotplug slot index number
+ * x<MAC> — MAC address
* [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
- * -- PCI geographical location
+ * — PCI geographical location
* [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
- * -- USB port number chain
+ * — USB port number chain
*
* All multi-function PCI devices will carry the [f<function>] number in the
* device name, including the function 0 device.
@@ -140,9 +140,9 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
const char *attr;
int idx;
- /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
+ /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
- /* SMBIOS type 41 -- Onboard Devices Extended Information */
+ /* SMBIOS type 41 — Onboard Devices Extended Information */
if (!attr)
attr = udev_device_get_sysattr_value(names->pcidev, "index");
if (!attr)
@@ -230,7 +230,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (l == 0)
names->pci_path[0] = '\0';
- /* ACPI _SUN -- slot user number */
+ /* ACPI _SUN — slot user number */
pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
if (!pci) {
err = -ENOENT;
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index b5f7f0d512..c0ef073476 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -40,7 +40,7 @@ static void sig_handler(int signum) {
static void print_device(struct udev_device *device, const char *source, int prop) {
struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
+ assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n",
source,
ts.tv_sec, ts.tv_nsec/1000,
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 243df7386f..e9dd2f47c7 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -400,10 +400,11 @@ static void worker_spawn(Manager *manager, struct event *event) {
goto out;
}
- /* request TERM signal if parent exits */
- prctl(PR_SET_PDEATHSIG, SIGTERM);
+ /* Request TERM signal if parent exits.
+ Ignore error, not much we can do in that case. */
+ (void) prctl(PR_SET_PDEATHSIG, SIGTERM);
- /* reset OOM score, we only protect the main daemon */
+ /* Reset OOM score, we only protect the main daemon. */
write_string_file("/proc/self/oom_score_adj", "0", 0);
for (;;) {