summaryrefslogtreecommitdiff
path: root/src/libshared
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-06-12 08:43:34 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-06-12 08:43:34 -0400
commit670b77ddfab0f4eddbe539964aba83d446d48129 (patch)
tree5b159fe9bd52169e05cdc60db5a48a5c5ac9602a /src/libshared
parent23708daf3ba69ba9880102b4f720a3842883332e (diff)
parent34dbdee3b2f122d2ef903a368b172e75f962b66a (diff)
Merge branch 'lukeshu/postmove' into 'lukeshu/master'
Diffstat (limited to 'src/libshared')
-rw-r--r--src/libshared/Makefile9
-rw-r--r--src/libshared/acpi-fpdt.c6
-rw-r--r--src/libshared/architecture.c176
-rw-r--r--src/libshared/architecture.h207
-rw-r--r--src/libshared/ask-password-api.c2
-rw-r--r--src/libshared/bus-unit-util.c1307
-rw-r--r--src/libshared/bus-unit-util.h57
-rw-r--r--src/libshared/bus-util.c899
-rw-r--r--src/libshared/bus-util.h37
-rw-r--r--src/libshared/cgroup-show.c102
-rw-r--r--src/libshared/cgroup-show.h8
-rw-r--r--src/libshared/condition.c2
-rw-r--r--src/libshared/conf-parser.c53
-rw-r--r--src/libshared/conf-parser.h3
-rw-r--r--src/libshared/dns-domain.c8
-rw-r--r--src/libshared/dns-domain.h5
-rw-r--r--src/libshared/dropin.c2
-rw-r--r--src/libshared/gcrypt-util.c71
-rw-r--r--src/libshared/gcrypt-util.h39
-rw-r--r--src/libshared/generator.c16
-rw-r--r--src/libshared/generator.h4
-rw-r--r--src/libshared/gpt.h4
-rw-r--r--src/libshared/install-printf.h4
-rw-r--r--src/libshared/install.c1299
-rw-r--r--src/libshared/install.h132
-rw-r--r--src/libshared/local-addresses.c3
-rw-r--r--src/libshared/logs-show.c51
-rw-r--r--src/libshared/logs-show.h3
-rw-r--r--src/libshared/machine-image.c39
-rw-r--r--src/libshared/machine-image.h26
-rw-r--r--src/libshared/machine-pool.c9
-rw-r--r--src/libshared/output-mode.c37
-rw-r--r--src/libshared/output-mode.h11
-rw-r--r--src/libshared/pager.c5
-rw-r--r--src/libshared/pager.h2
-rw-r--r--src/libshared/path-lookup.c807
-rw-r--r--src/libshared/path-lookup.h78
-rw-r--r--src/libshared/ptyfwd.c5
-rw-r--r--src/libshared/sleep-config.c6
-rw-r--r--src/libshared/sleep-config.h4
-rw-r--r--src/libshared/spawn-polkit-agent.c4
-rw-r--r--src/libshared/tests.c33
-rw-r--r--src/libshared/tests.h22
-rw-r--r--src/libshared/uid-range.c2
44 files changed, 3518 insertions, 2081 deletions
diff --git a/src/libshared/Makefile b/src/libshared/Makefile
index f6d3c87e42..778a6ec0ec 100644
--- a/src/libshared/Makefile
+++ b/src/libshared/Makefile
@@ -30,14 +30,13 @@ libshared_la_SOURCES = \
src/libsystemd/sd-netlink/local-addresses.h \
src/libsystemd/sd-netlink/local-addresses.c \
src/shared/output-mode.h \
+ src/shared/output-mode.c \
src/shared/gpt.h \
src/shared/udev-util.h \
src/shared/linux/auto_dev-ioctl.h \
src/shared/initreq.h \
src/shared/dns-domain.c \
src/shared/dns-domain.h \
- src/shared/architecture.c \
- src/shared/architecture.h \
src/shared/efivars.c \
src/shared/efivars.h \
src/shared/fstab-util.c \
@@ -106,7 +105,11 @@ libshared_la_SOURCES = \
src/shared/machine-pool.c \
src/shared/machine-pool.h \
src/shared/resolve-util.c \
- src/shared/resolve-util.h
+ src/shared/resolve-util.h \
+ src/shared/bus-unit-util.c \
+ src/shared/bus-unit-util.h \
+ src/shared/tests.h \
+ src/shared/tests.c
ifneq ($(HAVE_UTMP),)
libshared_la_SOURCES += \
diff --git a/src/libshared/acpi-fpdt.c b/src/libshared/acpi-fpdt.c
index 3cb9e781fd..6779691c28 100644
--- a/src/libshared/acpi-fpdt.c
+++ b/src/libshared/acpi-fpdt.c
@@ -119,7 +119,7 @@ int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
}
if (ptr == 0)
- return -EINVAL;
+ return -ENODATA;
/* read Firmware Basic Boot Performance Data Record */
fd = open("/dev/mem", O_CLOEXEC|O_RDONLY);
@@ -146,6 +146,10 @@ int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
if (brec.type != ACPI_FPDT_BOOT_REC)
return -EINVAL;
+ if (brec.exit_services_exit == 0)
+ /* Non-UEFI compatible boot. */
+ return -ENODATA;
+
if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start)
return -EINVAL;
if (brec.exit_services_exit > NSEC_PER_HOUR)
diff --git a/src/libshared/architecture.c b/src/libshared/architecture.c
deleted file mode 100644
index a9ecfc1cd6..0000000000
--- a/src/libshared/architecture.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 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/utsname.h>
-
-#include "architecture.h"
-#include "macro.h"
-#include "string-table.h"
-#include "string-util.h"
-
-int uname_architecture(void) {
-
- /* Return a sanitized enum identifying the architecture we are
- * running on. This is based on uname(), and the user may
- * hence control what this returns by using
- * personality(). This puts the user in control on systems
- * that can run binaries of multiple architectures.
- *
- * We do not translate the string returned by uname()
- * 1:1. Instead we try to clean it up and break down the
- * confusion on x86 and arm in particular.
- *
- * We do not try to distinguish CPUs not CPU features, but
- * actual architectures, i.e. that have genuinely different
- * code. */
-
- static const struct {
- const char *machine;
- int arch;
- } arch_map[] = {
-#if defined(__x86_64__) || defined(__i386__)
- { "x86_64", ARCHITECTURE_X86_64 },
- { "i686", ARCHITECTURE_X86 },
- { "i586", ARCHITECTURE_X86 },
- { "i486", ARCHITECTURE_X86 },
- { "i386", ARCHITECTURE_X86 },
-#elif defined(__powerpc__) || defined(__powerpc64__)
- { "ppc64", ARCHITECTURE_PPC64 },
- { "ppc64le", ARCHITECTURE_PPC64_LE },
- { "ppc", ARCHITECTURE_PPC },
- { "ppcle", ARCHITECTURE_PPC_LE },
-#elif defined(__ia64__)
- { "ia64", ARCHITECTURE_IA64 },
-#elif defined(__hppa__) || defined(__hppa64__)
- { "parisc64", ARCHITECTURE_PARISC64 },
- { "parisc", ARCHITECTURE_PARISC },
-#elif defined(__s390__) || defined(__s390x__)
- { "s390x", ARCHITECTURE_S390X },
- { "s390", ARCHITECTURE_S390 },
-#elif defined(__sparc__) || defined(__sparc64__)
- { "sparc64", ARCHITECTURE_SPARC64 },
- { "sparc", ARCHITECTURE_SPARC },
-#elif defined(__mips__) || defined(__mips64__)
- { "mips64", ARCHITECTURE_MIPS64 },
- { "mips", ARCHITECTURE_MIPS },
-#elif defined(__alpha__)
- { "alpha" , ARCHITECTURE_ALPHA },
-#elif defined(__arm__) || defined(__aarch64__)
- { "aarch64", ARCHITECTURE_ARM64 },
- { "aarch64_be", ARCHITECTURE_ARM64_BE },
- { "armv4l", ARCHITECTURE_ARM },
- { "armv4b", ARCHITECTURE_ARM_BE },
- { "armv4tl", ARCHITECTURE_ARM },
- { "armv4tb", ARCHITECTURE_ARM_BE },
- { "armv5tl", ARCHITECTURE_ARM },
- { "armv5tb", ARCHITECTURE_ARM_BE },
- { "armv5tel", ARCHITECTURE_ARM },
- { "armv5teb" , ARCHITECTURE_ARM_BE },
- { "armv5tejl", ARCHITECTURE_ARM },
- { "armv5tejb", ARCHITECTURE_ARM_BE },
- { "armv6l", ARCHITECTURE_ARM },
- { "armv6b", ARCHITECTURE_ARM_BE },
- { "armv7l", ARCHITECTURE_ARM },
- { "armv7b", ARCHITECTURE_ARM_BE },
- { "armv7ml", ARCHITECTURE_ARM },
- { "armv7mb", ARCHITECTURE_ARM_BE },
- { "armv4l", ARCHITECTURE_ARM },
- { "armv4b", ARCHITECTURE_ARM_BE },
- { "armv4tl", ARCHITECTURE_ARM },
- { "armv4tb", ARCHITECTURE_ARM_BE },
- { "armv5tl", ARCHITECTURE_ARM },
- { "armv5tb", ARCHITECTURE_ARM_BE },
- { "armv5tel", ARCHITECTURE_ARM },
- { "armv5teb", ARCHITECTURE_ARM_BE },
- { "armv5tejl", ARCHITECTURE_ARM },
- { "armv5tejb", ARCHITECTURE_ARM_BE },
- { "armv6l", ARCHITECTURE_ARM },
- { "armv6b", ARCHITECTURE_ARM_BE },
- { "armv7l", ARCHITECTURE_ARM },
- { "armv7b", ARCHITECTURE_ARM_BE },
- { "armv7ml", ARCHITECTURE_ARM },
- { "armv7mb", ARCHITECTURE_ARM_BE },
- { "armv8l", ARCHITECTURE_ARM },
- { "armv8b", ARCHITECTURE_ARM_BE },
-#elif defined(__sh__) || defined(__sh64__)
- { "sh5", ARCHITECTURE_SH64 },
- { "sh2", ARCHITECTURE_SH },
- { "sh2a", ARCHITECTURE_SH },
- { "sh3", ARCHITECTURE_SH },
- { "sh4", ARCHITECTURE_SH },
- { "sh4a", ARCHITECTURE_SH },
-#elif defined(__m68k__)
- { "m68k", ARCHITECTURE_M68K },
-#elif defined(__tilegx__)
- { "tilegx", ARCHITECTURE_TILEGX },
-#elif defined(__cris__)
- { "crisv32", ARCHITECTURE_CRIS },
-#else
-#error "Please register your architecture here!"
-#endif
- };
-
- static int cached = _ARCHITECTURE_INVALID;
- struct utsname u;
- unsigned i;
-
- if (cached != _ARCHITECTURE_INVALID)
- return cached;
-
- assert_se(uname(&u) >= 0);
-
- for (i = 0; i < ELEMENTSOF(arch_map); i++)
- if (streq(arch_map[i].machine, u.machine))
- return cached = arch_map[i].arch;
-
- assert_not_reached("Couldn't identify architecture. You need to patch systemd.");
- return _ARCHITECTURE_INVALID;
-}
-
-static const char *const architecture_table[_ARCHITECTURE_MAX] = {
- [ARCHITECTURE_X86] = "x86",
- [ARCHITECTURE_X86_64] = "x86-64",
- [ARCHITECTURE_PPC] = "ppc",
- [ARCHITECTURE_PPC_LE] = "ppc-le",
- [ARCHITECTURE_PPC64] = "ppc64",
- [ARCHITECTURE_PPC64_LE] = "ppc64-le",
- [ARCHITECTURE_IA64] = "ia64",
- [ARCHITECTURE_PARISC] = "parisc",
- [ARCHITECTURE_PARISC64] = "parisc64",
- [ARCHITECTURE_S390] = "s390",
- [ARCHITECTURE_S390X] = "s390x",
- [ARCHITECTURE_SPARC] = "sparc",
- [ARCHITECTURE_SPARC64] = "sparc64",
- [ARCHITECTURE_MIPS] = "mips",
- [ARCHITECTURE_MIPS_LE] = "mips-le",
- [ARCHITECTURE_MIPS64] = "mips64",
- [ARCHITECTURE_MIPS64_LE] = "mips64-le",
- [ARCHITECTURE_ALPHA] = "alpha",
- [ARCHITECTURE_ARM] = "arm",
- [ARCHITECTURE_ARM_BE] = "arm-be",
- [ARCHITECTURE_ARM64] = "arm64",
- [ARCHITECTURE_ARM64_BE] = "arm64-be",
- [ARCHITECTURE_SH] = "sh",
- [ARCHITECTURE_SH64] = "sh64",
- [ARCHITECTURE_M68K] = "m68k",
- [ARCHITECTURE_TILEGX] = "tilegx",
- [ARCHITECTURE_CRIS] = "cris",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/libshared/architecture.h b/src/libshared/architecture.h
deleted file mode 100644
index 26679e28c6..0000000000
--- a/src/libshared/architecture.h
+++ /dev/null
@@ -1,207 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <endian.h>
-
-#include "macro.h"
-#include "util.h"
-
-/* A cleaned up architecture definition. We don't want to get lost in
- * processor features, models, generations or even ABIs. Hence we
- * focus on general family, and distinguish word width and
- * endianness. */
-
-enum {
- ARCHITECTURE_X86 = 0,
- ARCHITECTURE_X86_64,
- ARCHITECTURE_PPC,
- ARCHITECTURE_PPC_LE,
- ARCHITECTURE_PPC64,
- ARCHITECTURE_PPC64_LE,
- ARCHITECTURE_IA64,
- ARCHITECTURE_PARISC,
- ARCHITECTURE_PARISC64,
- ARCHITECTURE_S390,
- ARCHITECTURE_S390X,
- ARCHITECTURE_SPARC,
- ARCHITECTURE_SPARC64,
- ARCHITECTURE_MIPS,
- ARCHITECTURE_MIPS_LE,
- ARCHITECTURE_MIPS64,
- ARCHITECTURE_MIPS64_LE,
- ARCHITECTURE_ALPHA,
- ARCHITECTURE_ARM,
- ARCHITECTURE_ARM_BE,
- ARCHITECTURE_ARM64,
- ARCHITECTURE_ARM64_BE,
- ARCHITECTURE_SH,
- ARCHITECTURE_SH64,
- ARCHITECTURE_M68K,
- ARCHITECTURE_TILEGX,
- ARCHITECTURE_CRIS,
- _ARCHITECTURE_MAX,
- _ARCHITECTURE_INVALID = -1
-};
-
-int uname_architecture(void);
-
-/*
- * LIB_ARCH_TUPLE should resolve to the local library path
- * architecture tuple systemd is built for, according to the Debian
- * tuple list:
- *
- * https://wiki.debian.org/Multiarch/Tuples
- *
- * This is used in library search paths that should understand
- * Debian's paths on all distributions.
- */
-
-#if defined(__x86_64__)
-# define native_architecture() ARCHITECTURE_X86_64
-# define LIB_ARCH_TUPLE "x86_64-linux-gnu"
-# define PROC_CPUINFO_MODEL "model name"
-#elif defined(__i386__)
-# define native_architecture() ARCHITECTURE_X86
-# define LIB_ARCH_TUPLE "i386-linux-gnu"
-# define PROC_CPUINFO_MODEL "model name"
-#elif defined(__powerpc64__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_PPC64
-# define LIB_ARCH_TUPLE "ppc64-linux-gnu"
-# else
-# define native_architecture() ARCHITECTURE_PPC64_LE
-# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu"
-# endif
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__powerpc__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_PPC
-# define LIB_ARCH_TUPLE "powerpc-linux-gnu"
-# else
-# define native_architecture() ARCHITECTURE_PPC_LE
-# error "Missing LIB_ARCH_TUPLE for PPCLE"
-# endif
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__ia64__)
-# define native_architecture() ARCHITECTURE_IA64
-# define LIB_ARCH_TUPLE "ia64-linux-gnu"
-#elif defined(__hppa64__)
-# define native_architecture() ARCHITECTURE_PARISC64
-# error "Missing LIB_ARCH_TUPLE for HPPA64"
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__hppa__)
-# define native_architecture() ARCHITECTURE_PARISC
-# define LIB_ARCH_TUPLE "hppa‑linux‑gnu"
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__s390x__)
-# define native_architecture() ARCHITECTURE_S390X
-# define LIB_ARCH_TUPLE "s390x-linux-gnu"
-#elif defined(__s390__)
-# define native_architecture() ARCHITECTURE_S390
-# define LIB_ARCH_TUPLE "s390-linux-gnu"
-#elif defined(__sparc64__)
-# define native_architecture() ARCHITECTURE_SPARC64
-# define LIB_ARCH_TUPLE "sparc64-linux-gnu"
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__sparc__)
-# define native_architecture() ARCHITECTURE_SPARC
-# define LIB_ARCH_TUPLE "sparc-linux-gnu"
-# define PROC_CPUINFO_MODEL "cpu"
-#elif defined(__mips64__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_MIPS64
-# error "Missing LIB_ARCH_TUPLE for MIPS64"
-# else
-# define native_architecture() ARCHITECTURE_MIPS64_LE
-# error "Missing LIB_ARCH_TUPLE for MIPS64_LE"
-# endif
-# define PROC_CPUINFO_MODEL "cpu model"
-#elif defined(__mips__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_MIPS
-# define LIB_ARCH_TUPLE "mips-linux-gnu"
-# else
-# define native_architecture() ARCHITECTURE_MIPS_LE
-# define LIB_ARCH_TUPLE "mipsel-linux-gnu"
-# endif
-# define PROC_CPUINFO_MODEL "cpu model"
-#elif defined(__alpha__)
-# define native_architecture() ARCHITECTURE_ALPHA
-# define LIB_ARCH_TUPLE "alpha-linux-gnu"
-#elif defined(__aarch64__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_ARM64_BE
-# define LIB_ARCH_TUPLE "aarch64_be-linux-gnu"
-# else
-# define native_architecture() ARCHITECTURE_ARM64
-# define LIB_ARCH_TUPLE "aarch64-linux-gnu"
-# endif
-#elif defined(__arm__)
-# if __BYTE_ORDER == __BIG_ENDIAN
-# define native_architecture() ARCHITECTURE_ARM_BE
-# if defined(__ARM_EABI__)
-# if defined(__ARM_PCS_VFP)
-# define LIB_ARCH_TUPLE "armeb-linux-gnueabihf"
-# else
-# define LIB_ARCH_TUPLE "armeb-linux-gnueabi"
-# endif
-# else
-# define LIB_ARCH_TUPLE "armeb-linux-gnu"
-# endif
-# else
-# define native_architecture() ARCHITECTURE_ARM
-# if defined(__ARM_EABI__)
-# if defined(__ARM_PCS_VFP)
-# define LIB_ARCH_TUPLE "arm-linux-gnueabihf"
-# else
-# define LIB_ARCH_TUPLE "arm-linux-gnueabi"
-# endif
-# else
-# define LIB_ARCH_TUPLE "arm-linux-gnu"
-# endif
-# endif
-# define PROC_CPUINFO_MODEL "model name"
-#elif defined(__sh64__)
-# define native_architecture() ARCHITECTURE_SH64
-# error "Missing LIB_ARCH_TUPLE for SH64"
-#elif defined(__sh__)
-# define native_architecture() ARCHITECTURE_SH
-# define LIB_ARCH_TUPLE "sh4-linux-gnu"
-#elif defined(__m68k__)
-# define native_architecture() ARCHITECTURE_M68K
-# define LIB_ARCH_TUPLE "m68k-linux-gnu"
-#elif defined(__tilegx__)
-# define native_architecture() ARCHITECTURE_TILEGX
-# error "Missing LIB_ARCH_TUPLE for TILEGX"
-#elif defined(__cris__)
-# define native_architecture() ARCHITECTURE_CRIS
-# error "Missing LIB_ARCH_TUPLE for CRIS"
-#else
-# error "Please register your architecture here!"
-#endif
-
-#ifndef PROC_CPUINFO_MODEL
-#warning "PROC_CPUINFO_MODEL not defined for your architecture"
-#define PROC_CPUINFO_MODEL "model name"
-#endif
-
-const char *architecture_to_string(int a) _const_;
-int architecture_from_string(const char *s) _pure_;
diff --git a/src/libshared/ask-password-api.c b/src/libshared/ask-password-api.c
index 6805873f9e..4a4bd8d3b8 100644
--- a/src/libshared/ask-password-api.c
+++ b/src/libshared/ask-password-api.c
@@ -431,7 +431,7 @@ static int create_socket(char **name) {
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
RUN_WITH_UMASK(0177) {
- if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
+ if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return -errno;
}
diff --git a/src/libshared/bus-unit-util.c b/src/libshared/bus-unit-util.c
new file mode 100644
index 0000000000..f68c4a41ac
--- /dev/null
+++ b/src/libshared/bus-unit-util.c
@@ -0,0 +1,1307 @@
+/***
+ 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", "IOAccounting", "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, "IOWeight", "StartupIOWeight")) {
+ uint64_t u;
+
+ r = cg_weight_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_blkio_weight_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", "SELinuxContext"))
+ 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 (cgroup_io_limit_type_from_string(field) >= 0 || 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;
+ }
+
+ if (streq(bandwidth, "max")) {
+ bytes = CGROUP_LIMIT_MAX;
+ } else {
+ 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 (STR_IN_SET(field, "IODeviceWeight", "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)", 1, 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;
+
+ /* changes is dereferenced when calling unit_file_dump_changes() later,
+ * so we have to make sure this is not NULL. */
+ assert(changes);
+ assert(n_changes);
+
+ 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 = special_glyph(more ? TREE_BRANCH : 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 = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
+
+ fputs(prefix, stdout);
+ fputs(special, stdout);
+ fputs(name, stdout);
+ fputc('\n', stdout);
+
+ special = special_glyph(more ? TREE_VERTICAL : 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,
+ special_glyph(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/libshared/bus-unit-util.h b/src/libshared/bus-unit-util.h
new file mode 100644
index 0000000000..8327189a63
--- /dev/null
+++ b/src/libshared/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 <systemd/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/libshared/bus-util.c b/src/libshared/bus-util.c
index a874bb84cc..62b5585e84 100644
--- a/src/libshared/bus-util.c
+++ b/src/libshared/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;
}
@@ -843,10 +833,10 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
if (r < 0)
return r;
- while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
+ 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,839 +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 *extra_args) {
- _cleanup_free_ char *service_shell_quoted = NULL, *systemctl_extra_args = NULL;
-
- assert(service);
-
- service_shell_quoted = shell_maybe_quote(service);
-
- systemctl_extra_args = strjoin("systemctl ", extra_args, " ", NULL);
- if (!systemctl_extra_args) {
- log_oom();
- return;
- }
-
- systemctl_extra_args = strstrip(systemctl_extra_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. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
- service,
- explanations[i].explanation,
- systemctl_extra_args,
- strna(service_shell_quoted));
-
- goto finish;
- }
- }
-
- log_error("Job for %s failed. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
- service,
- systemctl_extra_args,
- strna(service_shell_quoted));
-
-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\" followed by \"%1$s start %2$s\" again.",
- systemctl_extra_args,
- strna(service_shell_quoted));
-}
-
-static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *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 *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/libshared/bus-util.h b/src/libshared/bus-util.h
index 77474522be..f2b46530ba 100644
--- a/src/libshared/bus-util.h
+++ b/src/libshared/bus-util.h
@@ -24,15 +24,12 @@
#include <stdint.h>
#include <sys/types.h>
-#include <systemd/sd-bus-vtable.h>
#include <systemd/sd-bus.h>
#include <systemd/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 *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/libshared/cgroup-show.c b/src/libshared/cgroup-show.c
index f3039b23f7..3e451db715 100644
--- a/src/libshared/cgroup-show.c
+++ b/src/libshared/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++) {
@@ -78,16 +76,21 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un
get_process_cmdline(pids[i], n_columns, true, &t);
if (extra)
- printf("%s%s ", prefix, draw_special_char(DRAW_TRIANGULAR_BULLET));
+ printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET));
else
- printf("%s%s", prefix, draw_special_char(((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)));
+ printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT)));
printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t));
}
}
+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,20 +167,20 @@ 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;
}
if (last) {
- printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last)));
+ printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last)));
if (!p1) {
- p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL));
+ p1 = strappend(prefix, special_glyph(TREE_VERTICAL));
if (!p1)
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,10 +192,10 @@ 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)));
+ printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last)));
if (!p2) {
p2 = strappend(prefix, " ");
@@ -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/libshared/cgroup-show.h b/src/libshared/cgroup-show.h
index 3ab7dfb33c..5c1d6e6d98 100644
--- a/src/libshared/cgroup-show.h
+++ b/src/libshared/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/libshared/condition.c b/src/libshared/condition.c
index 1719a4c7a6..33ca6e029e 100644
--- a/src/libshared/condition.c
+++ b/src/libshared/condition.c
@@ -295,7 +295,7 @@ static int condition_test_needs_update(Condition *c) {
return false;
/* Any other failure means we should allow the condition to be true,
- * so that we rather invoke too many update tools then too
+ * so that we rather invoke too many update tools than too
* few. */
if (!path_is_absolute(c->parameter))
diff --git a/src/libshared/conf-parser.c b/src/libshared/conf-parser.c
index e7fe9ac21e..83be79a4f5 100644
--- a/src/libshared/conf-parser.c
+++ b/src/libshared/conf-parser.c
@@ -37,6 +37,7 @@
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
@@ -294,7 +295,7 @@ int config_parse(const char *unit,
_cleanup_free_ char *section = NULL, *continuation = NULL;
_cleanup_fclose_ FILE *ours = NULL;
unsigned line = 0, section_line = 0;
- bool section_ignored = false;
+ bool section_ignored = false, allow_bom = true;
int r;
assert(filename);
@@ -314,11 +315,11 @@ int config_parse(const char *unit,
fd_warn_permissions(filename, fileno(f));
- while (!feof(f)) {
- char l[LINE_MAX], *p, *c = NULL, *e;
+ for (;;) {
+ char buf[LINE_MAX], *l, *p, *c = NULL, *e;
bool escaped = false;
- if (!fgets(l, sizeof(l), f)) {
+ if (!fgets(buf, sizeof buf, f)) {
if (feof(f))
break;
@@ -326,6 +327,11 @@ int config_parse(const char *unit,
return -errno;
}
+ l = buf;
+ if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK))
+ l += strlen(UTF8_BYTE_ORDER_MARK);
+ allow_bom = false;
+
truncate_nl(l);
if (continuation) {
@@ -727,7 +733,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)
@@ -868,3 +874,40 @@ int config_parse_personality(
*personality = p;
return 0;
}
+
+int config_parse_ifname(
+ 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) {
+
+ char **s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!ifname_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = free_and_strdup(s, rvalue);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
diff --git a/src/libshared/conf-parser.h b/src/libshared/conf-parser.h
index a91c94c322..f6964e3fd4 100644
--- a/src/libshared/conf-parser.h
+++ b/src/libshared/conf-parser.h
@@ -125,6 +125,7 @@ int config_parse_log_facility(const char *unit, const char *filename, unsigned l
int config_parse_log_level(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_signal(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_personality(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_ifname(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);
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
int function(const char *unit, \
@@ -178,7 +179,7 @@ int config_parse_personality(const char *unit, const char *filename, unsigned li
assert(data); \
\
xs = new0(type, 1); \
- if(!xs) \
+ if (!xs) \
return -ENOMEM; \
\
*xs = invalid; \
diff --git a/src/libshared/dns-domain.c b/src/libshared/dns-domain.c
index 45d24c0079..835557c6b2 100644
--- a/src/libshared/dns-domain.c
+++ b/src/libshared/dns-domain.c
@@ -180,7 +180,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
unsigned slashes = 0;
for (y = terminal - 1; y >= name && *y == '\\'; y--)
- slashes ++;
+ slashes++;
if (slashes % 2 == 0) {
/* The '.' was not escaped */
@@ -192,7 +192,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
}
}
- terminal --;
+ terminal--;
}
r = dns_label_unescape(&name, dest, sz);
@@ -331,7 +331,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
l = strlen(buffer);
- /* Verify that the the result is not longer than one DNS label. */
+ /* Verify that the result is not longer than one DNS label. */
if (l <= 0 || l > DNS_LABEL_MAX)
return -EINVAL;
if (l > decoded_max)
@@ -1172,7 +1172,7 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
assert(a);
assert(ret);
- for (; n_labels > 0; n_labels --) {
+ for (; n_labels > 0; n_labels--) {
r = dns_name_parent(&a);
if (r < 0)
return r;
diff --git a/src/libshared/dns-domain.h b/src/libshared/dns-domain.h
index 2de3642cb3..af780f0b8b 100644
--- a/src/libshared/dns-domain.h
+++ b/src/libshared/dns-domain.h
@@ -1,3 +1,5 @@
+#pragma once
+
/***
This file is part of systemd.
@@ -17,9 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
-
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/libshared/dropin.c b/src/libshared/dropin.c
index cc1acd6f23..b9cd952ac8 100644
--- a/src/libshared/dropin.c
+++ b/src/libshared/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/libshared/gcrypt-util.c b/src/libshared/gcrypt-util.c
new file mode 100644
index 0000000000..39b544b6f0
--- /dev/null
+++ b/src/libshared/gcrypt-util.c
@@ -0,0 +1,71 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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/>.
+***/
+
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+
+#include "gcrypt-util.h"
+#include "hexdecoct.h"
+
+void initialize_libgcrypt(bool secmem) {
+ const char *p;
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ p = gcry_check_version("1.4.5");
+ assert(p);
+
+ /* Turn off "secmem". Clients which wish to make use of this
+ * feature should initialize the library manually */
+ if (!secmem)
+ gcry_control(GCRYCTL_DISABLE_SECMEM);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
+ gcry_md_hd_t md = NULL;
+ size_t hash_size;
+ void *hash;
+ char *enc;
+
+ initialize_libgcrypt(false);
+
+ hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ assert(hash_size > 0);
+
+ gcry_md_open(&md, md_algorithm, 0);
+ if (!md)
+ return -EIO;
+
+ gcry_md_write(md, s, len);
+
+ hash = gcry_md_read(md, 0);
+ if (!hash)
+ return -EIO;
+
+ enc = hexmem(hash, hash_size);
+ if (!enc)
+ return -ENOMEM;
+
+ *out = enc;
+ return 0;
+}
+#endif
diff --git a/src/libshared/gcrypt-util.h b/src/libshared/gcrypt-util.h
new file mode 100644
index 0000000000..cf33b3c59c
--- /dev/null
+++ b/src/libshared/gcrypt-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+
+void initialize_libgcrypt(bool secmem);
+int string_hashsum(const char *s, size_t len, int md_algorithm, char **out);
+#endif
+
+static inline int string_hashsum_sha224(const char *s, size_t len, char **out) {
+#ifdef HAVE_GCRYPT
+ return string_hashsum(s, len, GCRY_MD_SHA224, out);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
diff --git a/src/libshared/generator.c b/src/libshared/generator.c
index cd3c35cd55..70afc6a285 100644
--- a/src/libshared/generator.c
+++ b/src/libshared/generator.c
@@ -65,7 +65,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
"Description=File System Check on %2$s\n"
"DefaultDependencies=no\n"
"BindsTo=%3$s\n"
- "After=%3$s local-fs-pre.target\n"
+ "After=initrd-root-device.target local-fs-pre.target\n"
"Before=shutdown.target\n"
"\n"
"[Service]\n"
@@ -191,3 +191,17 @@ int generator_write_timeouts(
"[Unit]\nJobTimeoutSec=%s",
program_invocation_short_name, timeout);
}
+
+int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
+ _cleanup_free_ char *unit = NULL;
+ int r;
+
+ r = unit_name_from_path(what, ".device", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make unit name from path: %m");
+
+ return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
+ "# Automatically generated by %s\n\n"
+ "[Unit]\nRequires=%s\nAfter=%s",
+ program_invocation_short_name, unit, unit);
+}
diff --git a/src/libshared/generator.h b/src/libshared/generator.h
index a734e13970..a6017c1b76 100644
--- a/src/libshared/generator.h
+++ b/src/libshared/generator.h
@@ -34,3 +34,7 @@ int generator_write_timeouts(
const char *where,
const char *opts,
char **filtered);
+
+int generator_write_initrd_root_device_deps(
+ const char *dir,
+ const char *what);
diff --git a/src/libshared/gpt.h b/src/libshared/gpt.h
index 9b8f59abee..07153b51f4 100644
--- a/src/libshared/gpt.h
+++ b/src/libshared/gpt.h
@@ -1,3 +1,5 @@
+#pragma once
+
/***
This file is part of systemd.
@@ -17,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include <endian.h>
#include <systemd/sd-id128.h>
diff --git a/src/libshared/install-printf.h b/src/libshared/install-printf.h
index acf519f4f7..8a570fc265 100644
--- a/src/libshared/install-printf.h
+++ b/src/libshared/install-printf.h
@@ -1,3 +1,5 @@
+#pragma once
+
/***
This file is part of systemd.
@@ -17,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include "install.h"
int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret);
diff --git a/src/libshared/install.c b/src/libshared/install.c
index ef8f485cae..64d66a45d3 100644
--- a/src/libshared/install.c
+++ b/src/libshared/install.c
@@ -40,11 +40,13 @@
#include "hashmap.h"
#include "install-printf.h"
#include "install.h"
+#include "locale-util.h"
#include "log.h"
#include "macro.h"
#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 +67,65 @@ typedef struct {
OrderedHashmap *have_processed;
} InstallContext;
-static int in_search_path(const char *path, char **search) {
+typedef enum {
+ PRESET_UNKNOWN,
+ PRESET_ENABLE,
+ PRESET_DISABLE,
+} PresetAction;
+
+typedef struct {
+ char *pattern;
+ PresetAction action;
+} PresetRule;
+
+typedef struct {
+ PresetRule *rules;
+ size_t n_rules;
+} Presets;
+
+static inline void presets_freep(Presets *p) {
+ size_t i;
+
+ if (!p)
+ return;
+
+ for (i = 0; i < p->n_rules; i++)
+ free(p->rules[i].pattern);
+
+ free(p->rules);
+ p->n_rules = 0;
+}
+
+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 const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
+ [UNIT_FILE_TYPE_REGULAR] = "regular",
+ [UNIT_FILE_TYPE_SYMLINK] = "symlink",
+ [UNIT_FILE_TYPE_MASKED] = "masked",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
+
+static int in_search_path(const LookupPaths *p, const char *path) {
_cleanup_free_ char *parent = NULL;
char **i;
@@ -75,141 +135,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;
+
+ 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);
+}
- assert(root_dir);
+static int path_is_vendor(const LookupPaths *p, const char *path) {
+ const char *rpath;
- /* 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 */
+ assert(p);
+ assert(path);
- if (isempty(*root_dir) || path_equal(*root_dir, "/")) {
- *root_dir = NULL;
+ 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 +279,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 +291,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 +315,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 +323,76 @@ 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 %s %s.",
+ changes[i].path,
+ special_glyph(ARROW),
+ 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 UNIT_FILE_IS_DANGLING:
+ if (!quiet)
+ log_info("Unit %s is an alias to a unit that is not present, 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,
@@ -288,36 +408,52 @@ static int create_symlink(
/* Actually create a symlink, and remember that we did. Is
* smart enough to check if there's already a valid symlink in
- * place. */
+ * place.
+ *
+ * Returns 1 if a symlink was created or already exists and points to
+ * the right place, or negative on error.
+ */
mkdir_parents_label(new_path, 0755);
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;
+ return 1;
- 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 +489,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 +502,7 @@ static int remove_marked_symlinks_fd(
assert(fd >= 0);
assert(path);
assert(config_path);
+ assert(lp);
assert(restart);
d = fdopendir(fd);
@@ -400,12 +538,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 +554,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 +604,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 +613,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 +631,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 +645,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 +656,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 +690,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 +768,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 +785,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 +812,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 +879,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 +965,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 +1051,7 @@ static int config_parse_default_instance(
void *userdata) {
UnitFileInstallInfo *i = data;
+ const char *name;
char *printed;
int r;
@@ -881,6 +1059,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 +1087,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 +1098,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 +1109,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 +1175,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 +1221,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,9 +1240,9 @@ static int unit_file_search(
InstallContext *c,
UnitFileInstallInfo *info,
const LookupPaths *paths,
- const char *root_dir,
SearchFlags flags) {
+ _cleanup_free_ char *template = NULL;
char **p;
int r;
@@ -1056,59 +1255,53 @@ 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);
- if (r < 0) {
- if (r != -ENOENT)
- return r;
- } else {
+ r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
+ if (r >= 0) {
info->path = path;
path = NULL;
return r;
- }
+ } else if (r != -ENOENT)
+ return r;
}
if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
-
/* Unit file doesn't exist, however instance
* enablement was requested. We will check if it is
* possible to load template unit file. */
- _cleanup_free_ char *template = NULL;
-
r = unit_name_template(info->name, &template);
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);
- if (r < 0) {
- if (r != -ENOENT)
- return r;
- } else {
+ r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
+ if (r >= 0) {
info->path = path;
path = NULL;
return r;
- }
+ } else if (r != -ENOENT)
+ return r;
}
}
+ log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
return -ENOENT;
}
@@ -1140,10 +1333,14 @@ static int install_info_follow(
return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
}
+/**
+ * Search for the unit file. If the unit name is a symlink,
+ * follow the symlink to the target, maybe more than once.
+ * Propagate the instance name if present.
+ */
static int install_info_traverse(
UnitFileScope scope,
InstallContext *c,
- const char *root_dir,
const LookupPaths *paths,
UnitFileInstallInfo *start,
SearchFlags flags,
@@ -1157,7 +1354,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,17 +1365,19 @@ 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);
- if (r < 0) {
+ r = install_info_follow(c, i, paths->root_dir, flags);
+ if (r == -EXDEV) {
_cleanup_free_ char *buffer = NULL;
const char *bn;
- if (r != -EXDEV)
- return r;
-
/* Target has a different name, create a new
* install info object for that, and continue
* with that. */
@@ -1205,12 +1404,15 @@ static int install_info_traverse(
if (r < 0)
return r;
- r = unit_file_search(c, i, paths, root_dir, flags);
- if (r < 0)
- return r;
+ /* Try again, with the new target we found. */
+ r = unit_file_search(c, i, paths, flags);
+ if (r == -ENOENT)
+ /* Translate error code to highlight this specific case */
+ return -ENOLINK;
}
- /* Try again, with the new target we found. */
+ if (r < 0)
+ return r;
}
if (ret)
@@ -1219,10 +1421,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 +1455,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 +1474,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 +1489,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 +1501,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 +1514,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 +1535,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 +1550,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 +1564,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 +1577,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 +1609,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 +1632,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 +1659,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 +1683,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,12 +1707,18 @@ 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);
- if (r < 0)
+ r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
+ if (r == -ENOLINK)
+ return 0;
+ else if (r < 0)
return r;
- if (i->type != UNIT_FILE_TYPE_REGULAR)
+ if (i->type != UNIT_FILE_TYPE_REGULAR) {
+ log_debug("Unit %s has type %s, ignoring.",
+ i->name,
+ unit_file_type_to_string(i->type) ?: "invalid");
continue;
+ }
r = mark_symlink_for_removal(remove_symlinks_to, i->name);
if (r < 0)
@@ -1500,20 +1737,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 +1761,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 +1781,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 +1827,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 +1868,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);
- 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, files) {
_cleanup_free_ char *full = NULL;
@@ -1659,7 +1895,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 +1908,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 +1924,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 +1940,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 +2129,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 +2144,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 +2186,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 +2200,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 +2208,19 @@ int unit_file_enable(
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_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(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 +2230,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 +2243,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);
- 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, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
@@ -1867,11 +2266,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 +2311,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);
- 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, false, root_dir, &config_path);
+ r = install_info_discover(scope, &c, &paths, name, 0, &i);
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 +2356,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 +2375,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 +2392,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 +2403,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,32 +2462,42 @@ 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) {
+static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
+ _cleanup_(presets_freep) Presets ps = {};
+ size_t n_allocated = 0;
_cleanup_strv_free_ char **files = NULL;
char **p;
int r;
assert(scope >= 0);
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;
+ assert(presets);
if (scope == UNIT_FILE_SYSTEM)
r = conf_files_list(&files, ".preset", root_dir,
@@ -2100,8 +2514,11 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
- else
- return 1; /* Default is "enable" */
+ else {
+ *presets = (Presets){};
+
+ return 0;
+ }
if (r < 0)
return r;
@@ -2109,6 +2526,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
char line[LINE_MAX];
+ int n = 0;
f = fopen(*p, "re");
if (!f) {
@@ -2119,10 +2537,12 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
}
FOREACH_LINE(line, f, return -errno) {
+ PresetRule rule = {};
const char *parameter;
char *l;
l = strstrip(line);
+ n++;
if (isempty(l))
continue;
@@ -2131,31 +2551,87 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
parameter = first_word(l, "enable");
if (parameter) {
- if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
- log_debug("Preset file says enable %s.", name);
- return 1;
- }
+ char *pattern;
- continue;
+ pattern = strdup(parameter);
+ if (!pattern)
+ return -ENOMEM;
+
+ rule = (PresetRule) {
+ .pattern = pattern,
+ .action = PRESET_ENABLE,
+ };
}
parameter = first_word(l, "disable");
if (parameter) {
- if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
- log_debug("Preset file says disable %s.", name);
- return 0;
- }
+ char *pattern;
+
+ pattern = strdup(parameter);
+ if (!pattern)
+ return -ENOMEM;
+
+ rule = (PresetRule) {
+ .pattern = pattern,
+ .action = PRESET_DISABLE,
+ };
+ }
+
+ if (rule.action) {
+ if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
+ return -ENOMEM;
+ ps.rules[ps.n_rules++] = rule;
continue;
}
- log_debug("Couldn't parse line '%s'", l);
+ log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
}
}
- /* Default is "enable" */
- log_debug("Preset file doesn't say anything about %s, enabling.", name);
- return 1;
+ *presets = ps;
+ ps = (Presets){};
+
+ return 0;
+}
+
+static int query_presets(const char *name, const Presets presets) {
+ PresetAction action = PRESET_UNKNOWN;
+ size_t i;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ for (i = 0; i < presets.n_rules; i++)
+ if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
+ action = presets.rules[i].action;
+ break;
+ }
+
+ switch (action) {
+ case PRESET_UNKNOWN:
+ log_debug("Preset files don't specify rule for %s. Enabling.", name);
+ return 1;
+ case PRESET_ENABLE:
+ log_debug("Preset files say enable %s.", name);
+ return 1;
+ case PRESET_DISABLE:
+ log_debug("Preset files say disable %s.", name);
+ return 0;
+ default:
+ assert_not_reached("invalid preset action");
+ }
+}
+
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
+ _cleanup_(presets_freep) Presets presets = {};
+ int r;
+
+ r = read_presets(scope, root_dir, &presets);
+ if (r < 0)
+ return r;
+
+ return query_presets(name, presets);
}
static int execute_preset(
@@ -2164,7 +2640,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 +2656,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 +2668,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 +2685,11 @@ static int preset_prepare_one(
InstallContext *plus,
InstallContext *minus,
LookupPaths *paths,
- const char *root_dir,
UnitFilePresetMode mode,
- const char *name) {
+ const char *name,
+ Presets presets,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
UnitFileInstallInfo *i;
int r;
@@ -2221,19 +2698,20 @@ static int preset_prepare_one(
install_info_find(minus, name))
return 0;
- r = unit_file_query_preset(scope, root_dir, name);
+ r = query_presets(name, presets);
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 +2728,8 @@ int unit_file_preset(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char *config_path = NULL;
+ _cleanup_(presets_freep) Presets presets = {};
+ const char *config_path;
char **i;
int r;
@@ -2258,28 +2737,23 @@ 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;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
- r = get_config_path(scope, runtime, root_dir, &config_path);
+ r = read_presets(scope, root_dir, &presets);
if (r < 0)
return r;
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, presets, 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 +2767,8 @@ int unit_file_preset_all(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char *config_path = NULL;
+ _cleanup_(presets_freep) Presets presets = {};
+ const char *config_path = NULL;
char **i;
int r;
@@ -2301,28 +2776,21 @@ int unit_file_preset_all(
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;
+ config_path = runtime ? paths.runtime_config : paths.persistent_config;
- r = get_config_path(scope, runtime, root_dir, &config_path);
+ r = read_presets(scope, root_dir, &presets);
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;
@@ -2340,13 +2808,20 @@ 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, presets, NULL, 0);
+ if (r == -ERFKILL)
+ r = unit_file_changes_add(changes, n_changes,
+ UNIT_FILE_IS_MASKED, de->d_name, NULL);
+ else if (r == -ENOLINK)
+ r = unit_file_changes_add(changes, n_changes,
+ UNIT_FILE_IS_DANGLING, 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 +2846,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 +2858,15 @@ int unit_file_get_list(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(h);
- 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;
-
- 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 +2880,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 +2895,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 +2928,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 +2938,8 @@ 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",
+ [UNIT_FILE_IS_DANGLING] = "dangling",
};
DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
diff --git a/src/libshared/install.h b/src/libshared/install.h
index c1a43e23e7..c6aa4f6ef1 100644
--- a/src/libshared/install.h
+++ b/src/libshared/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,30 @@ enum UnitFilePresetMode {
enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
+ UNIT_FILE_IS_MASKED,
+ UNIT_FILE_IS_DANGLING,
_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 +139,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/libshared/local-addresses.c b/src/libshared/local-addresses.c
index fafc208eca..5cceddd03c 100644
--- a/src/libshared/local-addresses.c
+++ b/src/libshared/local-addresses.c
@@ -154,8 +154,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/libshared/logs-show.c b/src/libshared/logs-show.c
index 7ac6d49549..294fa3bede 100644
--- a/src/libshared/logs-show.c
+++ b/src/libshared/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,
@@ -997,7 +1013,7 @@ static int show_journal(FILE *f,
continue;
}
- line ++;
+ line++;
maybe_print_begin_newline(f, &flags);
r = output_journal(f, j, mode, n_columns, flags, ellipsized);
@@ -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/libshared/logs-show.h b/src/libshared/logs-show.h
index 682a4adc7b..15fe5b6e5c 100644
--- a/src/libshared/logs-show.h
+++ b/src/libshared/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/libshared/machine-image.c b/src/libshared/machine-image.c
index ed8a29c575..529d89ee2a 100644
--- a/src/libshared/machine-image.c
+++ b/src/libshared/machine-image.c
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fs.h>
@@ -400,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);
@@ -423,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;
@@ -473,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);
@@ -488,7 +487,7 @@ int image_rename(Image *i, const char *new_name) {
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
- * time we take possesion of it */
+ * time we take possession of it */
r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
if (r < 0)
return r;
@@ -506,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 */
@@ -539,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;
@@ -589,7 +588,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
- * time we take possesion of it */
+ * time we take possession of it */
r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
if (r < 0)
return r;
@@ -604,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;
@@ -641,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 */
@@ -672,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;
@@ -750,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/libshared/machine-image.h b/src/libshared/machine-image.h
index 31b720d50c..7410168c4f 100644
--- a/src/libshared/machine-image.h
+++ b/src/libshared/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/libshared/machine-pool.c b/src/libshared/machine-pool.c
index 056b5bfae5..c36efa0102 100644
--- a/src/libshared/machine-pool.c
+++ b/src/libshared/machine-pool.c
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
@@ -138,7 +139,7 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
if (errno == ENOENT)
- return 99;
+ _exit(99);
_exit(EXIT_FAILURE);
}
@@ -238,10 +239,8 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
}
r = mkfs_exists("btrfs");
- if (r == -ENOENT) {
- log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines.");
- return 0;
- }
+ if (r == 0)
+ return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
if (r < 0)
return r;
diff --git a/src/libshared/output-mode.c b/src/libshared/output-mode.c
new file mode 100644
index 0000000000..bec53ee0ae
--- /dev/null
+++ b/src/libshared/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/libshared/output-mode.h b/src/libshared/output-mode.h
index c5470e7c1b..f37189e57f 100644
--- a/src/libshared/output-mode.h
+++ b/src/libshared/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/libshared/pager.c b/src/libshared/pager.c
index 05b2b15e40..c16bc027be 100644
--- a/src/libshared/pager.c
+++ b/src/libshared/pager.c
@@ -52,11 +52,14 @@ noreturn static void pager_fallback(void) {
_exit(EXIT_SUCCESS);
}
-int pager_open(bool jump_to_end) {
+int pager_open(bool no_pager, bool jump_to_end) {
_cleanup_close_pair_ int fd[2] = { -1, -1 };
const char *pager;
pid_t parent_pid;
+ if (no_pager)
+ return 0;
+
if (pager_pid > 0)
return 1;
diff --git a/src/libshared/pager.h b/src/libshared/pager.h
index 9fb05796bb..893e1d2bb6 100644
--- a/src/libshared/pager.h
+++ b/src/libshared/pager.h
@@ -23,7 +23,7 @@
#include "macro.h"
-int pager_open(bool jump_to_end);
+int pager_open(bool no_pager, bool jump_to_end);
void pager_close(void);
bool pager_have(void) _pure_;
diff --git a/src/libshared/path-lookup.c b/src/libshared/path-lookup.c
index 5410620725..ca593b6963 100644
--- a/src/libshared/path-lookup.c
+++ b/src/libshared/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/libshared/path-lookup.h b/src/libshared/path-lookup.h
index 26c83d6111..f9bb2fe237 100644
--- a/src/libshared/path-lookup.h
+++ b/src/libshared/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/libshared/ptyfwd.c b/src/libshared/ptyfwd.c
index a97e0d5143..9629b50ed9 100644
--- a/src/libshared/ptyfwd.c
+++ b/src/libshared/ptyfwd.c
@@ -461,10 +461,7 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b)
return 0;
- if (b)
- f->flags |= PTY_FORWARD_IGNORE_VHANGUP;
- else
- f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP;
+ SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b);
if (!ignore_vhangup(f)) {
diff --git a/src/libshared/sleep-config.c b/src/libshared/sleep-config.c
index a0aef66bc8..f00624d0f2 100644
--- a/src/libshared/sleep-config.c
+++ b/src/libshared/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"
@@ -37,7 +38,7 @@
#include "string-util.h"
#include "strv.h"
-#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
+#define USE(x, y) do { (x) = (y); (y) = NULL; } while (0)
int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
@@ -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/libshared/sleep-config.h b/src/libshared/sleep-config.h
index 51f4621844..ad10039ff4 100644
--- a/src/libshared/sleep-config.h
+++ b/src/libshared/sleep-config.h
@@ -1,3 +1,5 @@
+#pragma once
+
/***
This file is part of systemd.
@@ -17,8 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
int parse_sleep_config(const char *verb, char ***modes, char ***states);
int can_sleep(const char *verb);
diff --git a/src/libshared/spawn-polkit-agent.c b/src/libshared/spawn-polkit-agent.c
index cf3c8ad5a3..7dae4d14fe 100644
--- a/src/libshared/spawn-polkit-agent.c
+++ b/src/libshared/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/libshared/tests.c b/src/libshared/tests.c
new file mode 100644
index 0000000000..409116290d
--- /dev/null
+++ b/src/libshared/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/libshared/tests.h b/src/libshared/tests.h
new file mode 100644
index 0000000000..93f09013a1
--- /dev/null
+++ b/src/libshared/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/libshared/uid-range.c b/src/libshared/uid-range.c
index eb251492c3..b6ec474390 100644
--- a/src/libshared/uid-range.c
+++ b/src/libshared/uid-range.c
@@ -54,7 +54,7 @@ static void uid_range_coalesce(UidRange **p, unsigned *n) {
if (*n > j+1)
memmove(y, y+1, sizeof(UidRange) * (*n - j -1));
- (*n) --;
+ (*n)--;
j--;
}
}