Systemd/src/shared/bus-unit-util.c

2433 lines
92 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-unit-util.h"
#include "bus-util.h"
#include "cap-list.h"
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "condition.h"
#include "coredump-util.h"
#include "cpu-set-util.h"
#include "dissect-image.h"
#include "escape.h"
#include "exec-util.h"
#include "exit-status.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "ip-protocol-list.h"
#include "libmount-util.h"
#include "locale-util.h"
#include "log.h"
#include "missing_fs.h"
Split out part of mount-util.c into mountpoint-util.c The idea is that anything which is related to actually manipulating mounts is in mount-util.c, but functions for mountpoint introspection are moved to the new file. Anything which requires libmount must be in mount-util.c. This was supposed to be a preparation for further changes, with no functional difference, but it results in a significant change in linkage: $ ldd build/libnss_*.so.2 (before) build/libnss_myhostname.so.2: linux-vdso.so.1 (0x00007fff77bf5000) librt.so.1 => /lib64/librt.so.1 (0x00007f4bbb7b2000) libmount.so.1 => /lib64/libmount.so.1 (0x00007f4bbb755000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4bbb734000) libc.so.6 => /lib64/libc.so.6 (0x00007f4bbb56e000) /lib64/ld-linux-x86-64.so.2 (0x00007f4bbb8c1000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f4bbb51b000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f4bbb512000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f4bbb4e3000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f4bbb45e000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f4bbb458000) build/libnss_mymachines.so.2: linux-vdso.so.1 (0x00007ffc19cc0000) librt.so.1 => /lib64/librt.so.1 (0x00007fdecb74b000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fdecb744000) libmount.so.1 => /lib64/libmount.so.1 (0x00007fdecb6e7000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fdecb6c6000) libc.so.6 => /lib64/libc.so.6 (0x00007fdecb500000) /lib64/ld-linux-x86-64.so.2 (0x00007fdecb8a9000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fdecb4ad000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007fdecb4a2000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fdecb475000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fdecb3f0000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fdecb3ea000) build/libnss_resolve.so.2: linux-vdso.so.1 (0x00007ffe8ef8e000) librt.so.1 => /lib64/librt.so.1 (0x00007fcf314bd000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fcf314b6000) libmount.so.1 => /lib64/libmount.so.1 (0x00007fcf31459000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fcf31438000) libc.so.6 => /lib64/libc.so.6 (0x00007fcf31272000) /lib64/ld-linux-x86-64.so.2 (0x00007fcf31615000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fcf3121f000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007fcf31214000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fcf311e7000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fcf31162000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fcf3115c000) build/libnss_systemd.so.2: linux-vdso.so.1 (0x00007ffda6d17000) librt.so.1 => /lib64/librt.so.1 (0x00007f610b83c000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f610b835000) libmount.so.1 => /lib64/libmount.so.1 (0x00007f610b7d8000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f610b7b7000) libc.so.6 => /lib64/libc.so.6 (0x00007f610b5f1000) /lib64/ld-linux-x86-64.so.2 (0x00007f610b995000) libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f610b59e000) libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f610b593000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f610b566000) libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f610b4e1000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f610b4db000) (after) build/libnss_myhostname.so.2: linux-vdso.so.1 (0x00007fff0b5e2000) librt.so.1 => /lib64/librt.so.1 (0x00007fde0c328000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fde0c307000) libc.so.6 => /lib64/libc.so.6 (0x00007fde0c141000) /lib64/ld-linux-x86-64.so.2 (0x00007fde0c435000) build/libnss_mymachines.so.2: linux-vdso.so.1 (0x00007ffdc30a7000) librt.so.1 => /lib64/librt.so.1 (0x00007f06ecabb000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f06ecab4000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f06eca93000) libc.so.6 => /lib64/libc.so.6 (0x00007f06ec8cd000) /lib64/ld-linux-x86-64.so.2 (0x00007f06ecc15000) build/libnss_resolve.so.2: linux-vdso.so.1 (0x00007ffe95747000) librt.so.1 => /lib64/librt.so.1 (0x00007fa56a80f000) libcap.so.2 => /lib64/libcap.so.2 (0x00007fa56a808000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fa56a7e7000) libc.so.6 => /lib64/libc.so.6 (0x00007fa56a621000) /lib64/ld-linux-x86-64.so.2 (0x00007fa56a964000) build/libnss_systemd.so.2: linux-vdso.so.1 (0x00007ffe67b51000) librt.so.1 => /lib64/librt.so.1 (0x00007ffb32113000) libcap.so.2 => /lib64/libcap.so.2 (0x00007ffb3210c000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffb320eb000) libc.so.6 => /lib64/libc.so.6 (0x00007ffb31f25000) /lib64/ld-linux-x86-64.so.2 (0x00007ffb3226a000) I don't quite understand what is going on here, but let's not be too picky.
2018-11-29 10:24:39 +01:00
#include "mountpoint-util.h"
#include "nsflags.h"
#include "numa-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "securebits-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "sort-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
#include "unit-def.h"
#include "user-util.h"
#include "utf8.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);
}
#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
static int bus_append_##parse_func( \
sd_bus_message *m, \
const char *field, \
const char *eq) { \
type val; \
int r; \
\
r = parse_func(eq, &val); \
if (r < 0) \
return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
\
r = sd_bus_message_append(m, "(sv)", field, \
bus_type, (cast_type) val); \
if (r < 0) \
return bus_log_create_error(r); \
\
return 1; \
}
#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
static int bus_append_##parse_func( \
sd_bus_message *m, \
const char *field, \
const char *eq) { \
int r; \
\
r = parse_func(eq); \
if (r < 0) \
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
\
r = sd_bus_message_append(m, "(sv)", field, \
bus_type, (int32_t) r); \
if (r < 0) \
return bus_log_create_error(r); \
\
return 1; \
}
DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
#if !HAVE_SECCOMP
static inline int seccomp_parse_errno_or_action(const char *eq) { return -EINVAL; }
#endif
DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action);
DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
int r;
r = sd_bus_message_append(m, "(sv)", field, "s", eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
const char *p;
int r;
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, 's', field);
if (r < 0)
return bus_log_create_error(r);
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);
for (p = eq;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, flags);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Invalid syntax: %s", eq);
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);
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 1;
}
static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
int r;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "ay");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_array(m, 'y', buf, n);
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
char *n;
usec_t t;
size_t l;
int r;
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
r = parse_sec(eq, &t);
if (r < 0)
return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
l = strlen(field);
n = newa(char, l + 2);
/* Change suffix Sec → USec */
strcpy(mempcpy(n, field, l - 3), "USec");
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
r = sd_bus_message_append(m, "(sv)", n, "t", t);
if (r < 0)
return bus_log_create_error(r);
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
return 1;
}
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
uint64_t v;
int r;
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
r = parse_size(eq, base, &v);
if (r < 0)
return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
core: implement /run/systemd/units/-based path for passing unit info from PID 1 to journald And let's make use of it to implement two new unit settings with it: 1. LogLevelMax= is a new per-unit setting that may be used to configure log priority filtering: set it to LogLevelMax=notice and only messages of level "notice" and lower (i.e. more important) will be processed, all others are dropped. 2. LogExtraFields= is a new per-unit setting for configuring per-unit journal fields, that are implicitly included in every log record generated by the unit's processes. It takes field/value pairs in the form of FOO=BAR. Also, related to this, one exisiting unit setting is ported to this new facility: 3. The invocation ID is now pulled from /run/systemd/units/ instead of cgroupfs xattrs. This substantially relaxes requirements of systemd on the kernel version and the privileges it runs with (specifically, cgroupfs xattrs are not available in containers, since they are stored in kernel memory, and hence are unsafe to permit to lesser privileged code). /run/systemd/units/ is a new directory, which contains a number of files and symlinks encoding the above information. PID 1 creates and manages these files, and journald reads them from there. Note that this is supposed to be a direct path between PID 1 and the journal only, due to the special runtime environment the journal runs in. Normally, today we shouldn't introduce new interfaces that (mis-)use a file system as IPC framework, and instead just an IPC system, but this is very hard to do between the journal and PID 1, as long as the IPC system is a subject PID 1 manages, and itself a client to the journal. This patch cleans up a couple of types used in journal code: specifically we switch to size_t for a couple of memory-sizing values, as size_t is the right choice for everything that is memory. Fixes: #4089 Fixes: #3041 Fixes: #4441
2017-11-02 19:43:32 +01:00
r = sd_bus_message_append(m, "(sv)", field, "t", v);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
bool explicit_path = false, done = false;
_cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
_cleanup_free_ char *path = NULL, *upgraded_name = NULL;
ExecCommandFlags flags = 0;
bool is_ex_prop = endswith(field, "Ex");
int r;
do {
switch (*eq) {
case '-':
if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
done = true;
else {
flags |= EXEC_COMMAND_IGNORE_FAILURE;
eq++;
}
break;
case '@':
if (explicit_path)
done = true;
else {
explicit_path = true;
eq++;
}
break;
case ':':
if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
done = true;
else {
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
eq++;
}
break;
case '+':
if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
done = true;
else {
flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
eq++;
}
break;
case '!':
if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
done = true;
else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
flags &= ~EXEC_COMMAND_NO_SETUID;
flags |= EXEC_COMMAND_AMBIENT_MAGIC;
eq++;
} else {
flags |= EXEC_COMMAND_NO_SETUID;
eq++;
}
break;
default:
done = true;
break;
}
} while (!done);
if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
/* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
is_ex_prop = true;
upgraded_name = strjoin(field, "Ex");
if (!upgraded_name)
return log_oom();
}
if (is_ex_prop) {
r = exec_command_flags_to_strv(flags, &ex_opts);
if (r < 0)
return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
}
if (explicit_path) {
r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse path: %m");
}
r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse command line: %m");
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
if (r < 0)
return bus_log_create_error(r);
if (!strv_isempty(l)) {
r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", path ?: l[0]);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, l);
if (r < 0)
return bus_log_create_error(r);
r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
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);
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
int r;
assert(m);
assert(prefix);
r = sd_bus_message_open_container(m, 'r', "iayu");
if (r < 0)
return r;
r = sd_bus_message_append(m, "i", family);
if (r < 0)
return r;
r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
if (r < 0)
return r;
r = sd_bus_message_append(m, "u", prefixlen);
if (r < 0)
return r;
return sd_bus_message_close_container(m);
}
static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
int r;
if (STR_IN_SET(field, "DevicePolicy",
"Slice",
"ManagedOOMSwap",
"ManagedOOMMemoryPressure",
"ManagedOOMMemoryPressureLimitPercent"))
return bus_append_string(m, field, eq);
core: add io controller support on the unified hierarchy On the unified hierarchy, blkio controller is renamed to io and the interface is changed significantly. * blkio.weight and blkio.weight_device are consolidated into io.weight which uses the standardized weight range [1, 10000] with 100 as the default value. * blkio.throttle.{read|write}_{bps|iops}_device are consolidated into io.max. Expansion of throttling features is being worked on to support work-conserving absolute limits (io.low and io.high). * All stats are consolidated into io.stats. This patchset adds support for the new interface. As the interface has been revamped and new features are expected to be added, it seems best to treat it as a separate controller rather than trying to expand the blkio settings although we might add automatic translation if only blkio settings are specified. * io.weight handling is mostly identical to blkio.weight[_device] handling except that the weight range is different. * Both read and write bandwidth settings are consolidated into CGroupIODeviceLimit which describes all limits applicable to the device. This makes it less painful to add new limits. * "max" can be used to specify the maximum limit which is equivalent to no config for max limits and treated as such. If a given CGroupIODeviceLimit doesn't contain any non-default configs, the config struct is discarded once the no limit config is applied to cgroup. * lookup_blkio_device() is renamed to lookup_block_device(). Signed-off-by: Tejun Heo <htejun@fb.com>
2016-05-05 22:42:55 +02:00
if (STR_IN_SET(field, "CPUAccounting",
"MemoryAccounting",
"IOAccounting",
"BlockIOAccounting",
"TasksAccounting",
"IPAccounting"))
return bus_append_parse_boolean(m, field, eq);
core: add io controller support on the unified hierarchy On the unified hierarchy, blkio controller is renamed to io and the interface is changed significantly. * blkio.weight and blkio.weight_device are consolidated into io.weight which uses the standardized weight range [1, 10000] with 100 as the default value. * blkio.throttle.{read|write}_{bps|iops}_device are consolidated into io.max. Expansion of throttling features is being worked on to support work-conserving absolute limits (io.low and io.high). * All stats are consolidated into io.stats. This patchset adds support for the new interface. As the interface has been revamped and new features are expected to be added, it seems best to treat it as a separate controller rather than trying to expand the blkio settings although we might add automatic translation if only blkio settings are specified. * io.weight handling is mostly identical to blkio.weight[_device] handling except that the weight range is different. * Both read and write bandwidth settings are consolidated into CGroupIODeviceLimit which describes all limits applicable to the device. This makes it less painful to add new limits. * "max" can be used to specify the maximum limit which is equivalent to no config for max limits and treated as such. If a given CGroupIODeviceLimit doesn't contain any non-default configs, the config struct is discarded once the no limit config is applied to cgroup. * lookup_blkio_device() is renamed to lookup_block_device(). Signed-off-by: Tejun Heo <htejun@fb.com>
2016-05-05 22:42:55 +02:00
if (STR_IN_SET(field, "CPUWeight",
"StartupCPUWeight",
"IOWeight",
"StartupIOWeight"))
return bus_append_cg_weight_parse(m, field, eq);
if (STR_IN_SET(field, "CPUShares",
"StartupCPUShares"))
return bus_append_cg_cpu_shares_parse(m, field, eq);
if (STR_IN_SET(field, "AllowedCPUs",
"AllowedMemoryNodes")) {
_cleanup_(cpu_set_reset) CPUSet cpuset = {};
_cleanup_free_ uint8_t *array = NULL;
size_t allocated;
r = parse_cpu_set(eq, &cpuset);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
r = cpu_set_to_dbus(&cpuset, &array, &allocated);
if (r < 0)
return log_error_errno(r, "Failed to serialize CPUSet: %m");
return bus_append_byte_array(m, field, array, allocated);
}
if (STR_IN_SET(field, "BlockIOWeight",
"StartupBlockIOWeight"))
return bus_append_cg_blkio_weight_parse(m, field, eq);
if (streq(field, "DisableControllers"))
return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
if (streq(field, "Delegate")) {
r = parse_boolean(eq);
if (r < 0)
return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (STR_IN_SET(field, "MemoryMin",
"DefaultMemoryLow",
"DefaultMemoryMin",
"MemoryLow",
"MemoryHigh",
"MemoryMax",
"MemorySwapMax",
"MemoryLimit",
"TasksMax")) {
if (streq(eq, "infinity")) {
r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
if (r < 0)
return bus_log_create_error(r);
return 1;
} else if (isempty(eq)) {
uint64_t empty_value = STR_IN_SET(field,
"DefaultMemoryLow",
"DefaultMemoryMin",
"MemoryLow",
"MemoryMin") ?
CGROUP_LIMIT_MIN :
CGROUP_LIMIT_MAX;
r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
r = parse_permille(eq);
if (r >= 0) {
char *n;
/* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
* and pass it in the MemoryLowScale property (and related ones). This way the physical memory
* size can be determined server-side. */
n = strjoina(field, "Scale");
r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "TasksMax"))
return bus_append_safe_atou64(m, field, eq);
return bus_append_parse_size(m, field, eq, 1024);
}
if (streq(field, "CPUQuota")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else {
r = parse_permille_unbounded(eq);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"CPU quota too small.");
if (r < 0)
return log_error_errno(r, "CPU quota '%s' invalid.", eq);
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "CPUQuotaPeriodSec")) {
usec_t u = USEC_INFINITY;
r = parse_sec_def_infinity(eq, &u);
if (r < 0)
return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "DeviceAllow")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
else {
const char *path = eq, *rwm = NULL, *e;
e = strchr(eq, ' ');
if (e) {
path = strndupa(eq, e - eq);
rwm = e+1;
}
r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else {
const char *path, *bandwidth, *e;
uint64_t bytes;
e = strchr(eq, ' ');
if (!e)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse %s value %s.",
field, eq);
path = strndupa(eq, e - eq);
bandwidth = e+1;
2018-07-04 15:38:53 +02:00
if (streq(bandwidth, "infinity"))
core: add io controller support on the unified hierarchy On the unified hierarchy, blkio controller is renamed to io and the interface is changed significantly. * blkio.weight and blkio.weight_device are consolidated into io.weight which uses the standardized weight range [1, 10000] with 100 as the default value. * blkio.throttle.{read|write}_{bps|iops}_device are consolidated into io.max. Expansion of throttling features is being worked on to support work-conserving absolute limits (io.low and io.high). * All stats are consolidated into io.stats. This patchset adds support for the new interface. As the interface has been revamped and new features are expected to be added, it seems best to treat it as a separate controller rather than trying to expand the blkio settings although we might add automatic translation if only blkio settings are specified. * io.weight handling is mostly identical to blkio.weight[_device] handling except that the weight range is different. * Both read and write bandwidth settings are consolidated into CGroupIODeviceLimit which describes all limits applicable to the device. This makes it less painful to add new limits. * "max" can be used to specify the maximum limit which is equivalent to no config for max limits and treated as such. If a given CGroupIODeviceLimit doesn't contain any non-default configs, the config struct is discarded once the no limit config is applied to cgroup. * lookup_blkio_device() is renamed to lookup_block_device(). Signed-off-by: Tejun Heo <htejun@fb.com>
2016-05-05 22:42:55 +02:00
bytes = CGROUP_LIMIT_MAX;
2018-07-04 15:38:53 +02:00
else {
core: add io controller support on the unified hierarchy On the unified hierarchy, blkio controller is renamed to io and the interface is changed significantly. * blkio.weight and blkio.weight_device are consolidated into io.weight which uses the standardized weight range [1, 10000] with 100 as the default value. * blkio.throttle.{read|write}_{bps|iops}_device are consolidated into io.max. Expansion of throttling features is being worked on to support work-conserving absolute limits (io.low and io.high). * All stats are consolidated into io.stats. This patchset adds support for the new interface. As the interface has been revamped and new features are expected to be added, it seems best to treat it as a separate controller rather than trying to expand the blkio settings although we might add automatic translation if only blkio settings are specified. * io.weight handling is mostly identical to blkio.weight[_device] handling except that the weight range is different. * Both read and write bandwidth settings are consolidated into CGroupIODeviceLimit which describes all limits applicable to the device. This makes it less painful to add new limits. * "max" can be used to specify the maximum limit which is equivalent to no config for max limits and treated as such. If a given CGroupIODeviceLimit doesn't contain any non-default configs, the config struct is discarded once the no limit config is applied to cgroup. * lookup_blkio_device() is renamed to lookup_block_device(). Signed-off-by: Tejun Heo <htejun@fb.com>
2016-05-05 22:42:55 +02:00
r = parse_size(bandwidth, 1000, &bytes);
if (r < 0)
return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
}
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (STR_IN_SET(field, "IODeviceWeight",
"BlockIODeviceWeight")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else {
const char *path, *weight, *e;
uint64_t u;
e = strchr(eq, ' ');
if (!e)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse %s value %s.",
field, eq);
path = strndupa(eq, e - eq);
weight = e+1;
r = safe_atou64(weight, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "IODeviceLatencyTargetSec")) {
const char *field_usec = "IODeviceLatencyTargetUSec";
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
else {
const char *path, *target, *e;
usec_t usec;
e = strchr(eq, ' ');
if (!e)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse %s value %s.",
field, eq);
path = strndupa(eq, e - eq);
target = e+1;
r = parse_sec(target, &usec);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (STR_IN_SET(field, "IPAddressAllow",
"IPAddressDeny")) {
unsigned char prefixlen;
union in_addr_union prefix = {};
int family;
if (isempty(eq)) {
r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(iayu)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(iayu)");
if (r < 0)
return bus_log_create_error(r);
if (streq(eq, "any")) {
/* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
if (r < 0)
return bus_log_create_error(r);
r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
if (r < 0)
return bus_log_create_error(r);
} else if (is_localhost(eq)) {
/* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
prefix.in.s_addr = htobe32(0x7f000000);
r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
if (r < 0)
return bus_log_create_error(r);
prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
if (r < 0)
return r;
} else if (streq(eq, "link-local")) {
/* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
if (r < 0)
return bus_log_create_error(r);
prefix.in6 = (struct in6_addr) {
.s6_addr32[0] = htobe32(0xfe800000)
};
r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
if (r < 0)
return bus_log_create_error(r);
} else if (streq(eq, "multicast")) {
/* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
if (r < 0)
return bus_log_create_error(r);
prefix.in6 = (struct in6_addr) {
.s6_addr32[0] = htobe32(0xff000000)
};
r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
if (r < 0)
return bus_log_create_error(r);
} else {
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&eq, &word, NULL, 0);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to parse %s: %s", field, eq);
r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
if (r < 0)
return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
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);
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 1;
}
if (STR_IN_SET(field, "IPIngressFilterPath",
"IPEgressFilterPath")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "as", 0);
else
r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
return 0;
}
static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "Where"))
return bus_append_string(m, field, eq);
if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq);
if (streq(field, "TimeoutIdleSec"))
return bus_append_parse_sec_rename(m, field, eq);
return 0;
}
static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
const char *suffix;
int r;
if (STR_IN_SET(field, "User",
"Group",
"UtmpIdentifier",
"UtmpMode",
"PAMName",
"TTYPath",
"WorkingDirectory",
"RootDirectory",
"SyslogIdentifier",
"ProtectSystem",
"ProtectHome",
"SELinuxContext",
"RootImage",
"RootVerity",
"RuntimeDirectoryPreserve",
"Personality",
"KeyringMode",
"ProtectProc",
"ProcSubset",
"NetworkNamespacePath",
"LogNamespace"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "IgnoreSIGPIPE",
"TTYVHangup",
"TTYReset",
"TTYVTDisallocate",
"PrivateTmp",
"PrivateDevices",
"PrivateNetwork",
"PrivateUsers",
"PrivateMounts",
"NoNewPrivileges",
"SyslogLevelPrefix",
"MemoryDenyWriteExecute",
"RestrictRealtime",
"DynamicUser",
"RemoveIPC",
"ProtectKernelTunables",
"ProtectKernelModules",
"ProtectKernelLogs",
"ProtectClock",
"ProtectControlGroups",
"MountAPIVFS",
"CPUSchedulingResetOnFork",
"LockPersonality",
"ProtectHostname",
"RestrictSUIDSGID"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "ReadWriteDirectories",
"ReadOnlyDirectories",
"InaccessibleDirectories",
"ReadWritePaths",
"ReadOnlyPaths",
"InaccessiblePaths",
"RuntimeDirectory",
"StateDirectory",
"CacheDirectory",
"LogsDirectory",
"ConfigurationDirectory",
"SupplementaryGroups",
"SystemCallArchitectures"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (STR_IN_SET(field, "SyslogLevel",
"LogLevelMax"))
return bus_append_log_level_from_string(m, field, eq);
if (streq(field, "SyslogFacility"))
return bus_append_log_facility_unshifted_from_string(m, field, eq);
if (streq(field, "SecureBits"))
return bus_append_secure_bits_from_string(m, field, eq);
if (streq(field, "CPUSchedulingPolicy"))
return bus_append_sched_policy_from_string(m, field, eq);
if (STR_IN_SET(field, "CPUSchedulingPriority",
"OOMScoreAdjust"))
return bus_append_safe_atoi(m, field, eq);
if (streq(field, "CoredumpFilter"))
return bus_append_coredump_filter_mask_from_string(m, field, eq);
if (streq(field, "Nice"))
return bus_append_parse_nice(m, field, eq);
if (streq(field, "SystemCallErrorNumber"))
return bus_append_seccomp_parse_errno_or_action(m, field, eq);
if (streq(field, "IOSchedulingClass"))
return bus_append_ioprio_class_from_string(m, field, eq);
if (streq(field, "IOSchedulingPriority"))
return bus_append_ioprio_parse_priority(m, field, eq);
if (STR_IN_SET(field, "RuntimeDirectoryMode",
"StateDirectoryMode",
"CacheDirectoryMode",
"LogsDirectoryMode",
"ConfigurationDirectoryMode",
"UMask"))
return bus_append_parse_mode(m, field, eq);
if (streq(field, "TimerSlackNSec"))
return bus_append_parse_nsec(m, field, eq);
if (streq(field, "LogRateLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "LogRateLimitBurst"))
return bus_append_safe_atou(m, field, eq);
if (streq(field, "MountFlags"))
return bus_append_mount_propagation_flags_from_string(m, field, eq);
if (STR_IN_SET(field, "Environment",
"UnsetEnvironment",
"PassEnvironment"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (streq(field, "EnvironmentFile")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
else
r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
eq[0] == '-' ? eq + 1 : eq,
eq[0] == '-');
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "SetCredential")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, 's', "SetCredential");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(say)");
if (r < 0)
return bus_log_create_error(r);
if (isempty(eq))
r = sd_bus_message_append(m, "a(say)", 0);
else {
_cleanup_free_ char *word = NULL, *unescaped = NULL;
const char *p = eq;
int l;
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to parse SetCredential= parameter: %s", eq);
if (r == 0 || !p)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to SetCredential=.");
l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
if (l < 0)
return log_error_errno(l, "Failed to unescape SetCredential= value: %s", p);
r = sd_bus_message_open_container(m, 'a', "(say)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'r', "say");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "s", word);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_array(m, 'y', unescaped, l);
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);
}
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "LoadCredential")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, 's', "LoadCredential");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ss)");
if (r < 0)
return bus_log_create_error(r);
if (isempty(eq))
r = sd_bus_message_append(m, "a(ss)", 0);
else {
_cleanup_free_ char *word = NULL;
const char *p = eq;
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to parse LoadCredential= parameter: %s", eq);
if (r == 0 || !p)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to LoadCredential=.");
r = sd_bus_message_append(m, "a(ss)", 1, word, p);
}
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "LogExtraFields")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "aay");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "ay");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
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);
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 1;
}
if (STR_IN_SET(field, "StandardInput",
"StandardOutput",
"StandardError")) {
const char *n, *appended;
if ((n = startswith(eq, "fd:"))) {
appended = strjoina(field, "FileDescriptorName");
r = sd_bus_message_append(m, "(sv)", appended, "s", n);
} else if ((n = startswith(eq, "file:"))) {
appended = strjoina(field, "File");
r = sd_bus_message_append(m, "(sv)", appended, "s", n);
} else if ((n = startswith(eq, "append:"))) {
appended = strjoina(field, "FileToAppend");
r = sd_bus_message_append(m, "(sv)", appended, "s", n);
} else
r = sd_bus_message_append(m, "(sv)", field, "s", eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "StandardInputText")) {
_cleanup_free_ char *unescaped = NULL;
r = cunescape(eq, 0, &unescaped);
if (r < 0)
return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
if (!strextend(&unescaped, "\n"))
return log_oom();
/* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
* interface anyway */
return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
}
if (streq(field, "StandardInputData")) {
_cleanup_free_ void *decoded = NULL;
size_t sz;
r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
if (r < 0)
return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
return bus_append_byte_array(m, field, decoded, sz);
}
if ((suffix = startswith(field, "Limit"))) {
int rl;
rl = rlimit_from_string(suffix);
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, "(sv)", field, "t", l.rlim_max);
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
}
if (STR_IN_SET(field, "AppArmorProfile",
"SmackProcessLabel")) {
int ignore = 0;
const char *s = eq;
if (eq[0] == '-') {
ignore = 1;
s = eq + 1;
}
r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (STR_IN_SET(field, "CapabilityBoundingSet",
"AmbientCapabilities")) {
uint64_t sum = 0;
bool invert = false;
const char *p = eq;
2017-12-14 08:34:26 +01:00
if (*p == '~') {
invert = true;
p++;
}
r = capability_set_from_string(p, &sum);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
sum = invert ? ~sum : sum;
r = sd_bus_message_append(m, "(sv)", field, "t", sum);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "CPUAffinity")) {
_cleanup_(cpu_set_reset) CPUSet cpuset = {};
_cleanup_free_ uint8_t *array = NULL;
size_t allocated;
if (eq && streq(eq, "numa")) {
r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
if (r < 0)
return bus_log_create_error(r);
return r;
}
r = parse_cpu_set(eq, &cpuset);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
r = cpu_set_to_dbus(&cpuset, &array, &allocated);
if (r < 0)
return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
return bus_append_byte_array(m, field, array, allocated);
}
if (streq(field, "NUMAPolicy")) {
r = mpol_from_string(eq);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "NUMAMask")) {
_cleanup_(cpu_set_reset) CPUSet nodes = {};
_cleanup_free_ uint8_t *array = NULL;
size_t allocated;
if (eq && streq(eq, "all")) {
r = numa_mask_add_all(&nodes);
if (r < 0)
return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
} else {
r = parse_cpu_set(eq, &nodes);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
}
r = cpu_set_to_dbus(&nodes, &array, &allocated);
if (r < 0)
return log_error_errno(r, "Failed to serialize NUMAMask: %m");
return bus_append_byte_array(m, field, array, allocated);
}
if (STR_IN_SET(field, "RestrictAddressFamilies",
"SystemCallFilter",
"SystemCallLog")) {
int allow_list = 1;
const char *p = eq;
if (*p == '~') {
allow_list = 0;
p++;
}
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "(bas)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'r', "bas");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, 'b', &allow_list);
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);
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Invalid syntax: %s", eq);
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);
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);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "RestrictNamespaces")) {
bool invert = false;
unsigned long flags;
r = parse_boolean(eq);
if (r > 0)
flags = 0;
else if (r == 0)
flags = NAMESPACE_FLAGS_ALL;
else {
if (eq[0] == '~') {
invert = true;
eq++;
}
r = namespace_flags_from_string(eq, &flags);
if (r < 0)
return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
}
if (invert)
flags = (~flags) & NAMESPACE_FLAGS_ALL;
r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
if (r < 0)
return bus_log_create_error(r);
2017-12-14 08:34:26 +01:00
return 1;
}
2017-12-14 08:34:26 +01:00
if (STR_IN_SET(field, "BindPaths",
"BindReadOnlyPaths")) {
const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
2017-12-14 08:34:26 +01:00
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ssbt)");
if (r < 0)
return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *source = NULL, *destination = NULL;
char *s = NULL, *d = NULL;
bool ignore_enoent = false;
uint64_t flags = MS_REC;
r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
break;
s = source;
if (s[0] == '-') {
ignore_enoent = true;
s++;
}
if (p && p[-1] == ':') {
r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Missing argument after ':': %s",
eq);
d = destination;
if (p && p[-1] == ':') {
_cleanup_free_ char *options = NULL;
r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (isempty(options) || streq(options, "rbind"))
flags = MS_REC;
else if (streq(options, "norbind"))
flags = 0;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown options: %s",
eq);
}
} else
d = s;
r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
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);
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 1;
}
if (streq(field, "TemporaryFileSystem")) {
const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ss)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ss)");
if (r < 0)
return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *word = NULL, *path = NULL;
const char *w;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
break;
w = word;
r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse argument: %s",
p);
r = sd_bus_message_append(m, "(ss)", path, w);
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);
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 1;
}
if (streq(field, "RootHash")) {
_cleanup_free_ void *roothash_decoded = NULL;
size_t roothash_decoded_size = 0;
/* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
if (path_is_absolute(eq))
return bus_append_string(m, "RootHashPath", eq);
/* We have a roothash to decode, eg: RootHash=012345789abcdef */
r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
if (r < 0)
return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
if (roothash_decoded_size < sizeof(sd_id128_t))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
}
if (streq(field, "RootHashSignature")) {
_cleanup_free_ void *roothash_sig_decoded = NULL;
char *value;
size_t roothash_sig_decoded_size = 0;
/* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
if (path_is_absolute(eq))
return bus_append_string(m, "RootHashSignaturePath", eq);
if (!(value = startswith(eq, "base64:")))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
/* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
if (r < 0)
return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
}
if (streq(field, "RootImageOptions")) {
_cleanup_strv_free_ char **l = NULL;
char **first = NULL, **second = NULL;
const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ss)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ss)");
if (r < 0)
return bus_log_create_error(r);
r = strv_split_colon_pairs(&l, p);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
STRV_FOREACH_PAIR(first, second, l) {
/* Format is either 'root:foo' or 'foo' (root is implied) */
if (!isempty(*second) && partition_designator_from_string(*first) < 0)
return bus_log_create_error(-EINVAL);
r = sd_bus_message_append(m, "(ss)",
!isempty(*second) ? *first : "root",
!isempty(*second) ? *second : *first);
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);
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 1;
}
if (streq(field, "MountImages")) {
const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
if (r < 0)
return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
const char *q = NULL, *source = NULL;
bool permissive = false;
r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
source = first;
if (source[0] == '-') {
permissive = true;
source++;
}
if (isempty(second))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Missing argument after ':': %s",
eq);
r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "ssb", source, second, permissive);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ss)");
if (r < 0)
return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *partition = NULL, *mount_options = NULL;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
if (r < 0)
return r;
if (r == 0)
break;
/* Single set of options, applying to the root partition/single filesystem */
if (r == 1) {
r = sd_bus_message_append(m, "(ss)", "root", partition);
if (r < 0)
return bus_log_create_error(r);
break;
}
if (partition_designator_from_string(partition) < 0)
return bus_log_create_error(-EINVAL);
r = sd_bus_message_append(m, "(ss)", partition, mount_options);
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);
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);
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 1;
}
return 0;
}
static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "KillMode"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "SendSIGHUP",
"SendSIGKILL"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "KillSignal",
"RestartKillSignal",
"FinalKillSignal",
"WatchdogSignal"))
return bus_append_signal_from_string(m, field, eq);
return 0;
}
static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
if (STR_IN_SET(field, "What",
"Where",
"Options",
"Type"))
return bus_append_string(m, field, eq);
if (streq(field, "TimeoutSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, "SloppyOptions",
"LazyUnmount",
"ForceUnmount",
"ReadwriteOnly"))
return bus_append_parse_boolean(m, field, eq);
return 0;
}
static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
int r;
if (streq(field, "MakeDirectory"))
return bus_append_parse_boolean(m, field, eq);
if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, "PathExists",
"PathExistsGlob",
"PathChanged",
"PathModified",
"DirectoryNotEmpty")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
else
r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
return 0;
}
static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "RuntimeMaxSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "TimeoutStopSec"))
return bus_append_parse_sec_rename(m, field, eq);
return 0;
}
static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
int r;
if (STR_IN_SET(field, "PIDFile",
"Type",
"Restart",
"BusName",
"NotifyAccess",
"USBFunctionDescriptors",
"USBFunctionStrings",
"OOMPolicy",
"TimeoutStartFailureMode",
"TimeoutStopFailureMode"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "PermissionsStartOnly",
"RootDirectoryStartOnly",
"RemainAfterExit",
"GuessMainPID"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "RestartSec",
"TimeoutStartSec",
"TimeoutStopSec",
"TimeoutAbortSec",
"RuntimeMaxSec",
"WatchdogSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "TimeoutSec")) {
r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
if (r < 0)
return r;
return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
}
if (streq(field, "FileDescriptorStoreMax"))
return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, "ExecCondition",
"ExecStartPre",
"ExecStart",
"ExecStartPost",
"ExecConditionEx",
"ExecStartPreEx",
"ExecStartEx",
"ExecStartPostEx",
"ExecReload",
"ExecStop",
"ExecStopPost",
"ExecReloadEx",
"ExecStopEx",
"ExecStopPostEx"))
return bus_append_exec_command(m, field, eq);
if (STR_IN_SET(field, "RestartPreventExitStatus",
"RestartForceExitStatus",
"SuccessExitStatus")) {
_cleanup_free_ int *status = NULL, *signal = NULL;
size_t n_status = 0, n_signal = 0;
const char *p;
for (p = eq;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
/* We need to call exit_status_from_string() first, because we want
* to parse numbers as exit statuses, not signals. */
r = exit_status_from_string(word);
if (r >= 0) {
assert(r >= 0 && r < 256);
status = reallocarray(status, n_status + 1, sizeof(int));
if (!status)
return log_oom();
status[n_status++] = r;
} else if ((r = signal_from_string(word)) >= 0) {
signal = reallocarray(signal, n_signal + 1, sizeof(int));
if (!signal)
return log_oom();
signal[n_signal++] = r;
} else
/* original r from exit_status_to_string() */
return log_error_errno(r, "Invalid status or signal %s in %s: %m",
word, field);
}
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "(aiai)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'r', "aiai");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
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);
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 1;
}
return 0;
}
static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
int r;
if (STR_IN_SET(field, "Accept",
"FlushPending",
"Writable",
"KeepAlive",
"NoDelay",
"FreeBind",
"Transparent",
"Broadcast",
"PassCredentials",
"PassSecurity",
"PassPacketInfo",
"ReusePort",
"RemoveOnStop",
"SELinuxContextFromNet"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "Priority",
"IPTTL",
"Mark"))
return bus_append_safe_atoi(m, field, eq);
if (streq(field, "IPTOS"))
return bus_append_ip_tos_from_string(m, field, eq);
if (STR_IN_SET(field, "Backlog",
"MaxConnections",
"MaxConnectionsPerSource",
"KeepAliveProbes",
"TriggerLimitBurst"))
return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, "SocketMode",
"DirectoryMode"))
return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, "MessageQueueMaxMessages",
"MessageQueueMessageSize"))
return bus_append_safe_atoi64(m, field, eq);
if (STR_IN_SET(field, "TimeoutSec",
"KeepAliveTimeSec",
"KeepAliveIntervalSec",
"DeferAcceptSec",
"TriggerLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (STR_IN_SET(field, "ReceiveBuffer",
"SendBuffer",
"PipeSize"))
return bus_append_parse_size(m, field, eq, 1024);
if (STR_IN_SET(field, "ExecStartPre",
"ExecStartPost",
"ExecReload",
"ExecStopPost"))
return bus_append_exec_command(m, field, eq);
if (STR_IN_SET(field, "SmackLabel",
"SmackLabelIPIn",
"SmackLabelIPOut",
"TCPCongestion",
"BindToDevice",
"BindIPv6Only",
"FileDescriptorName",
"SocketUser",
"SocketGroup",
"Timestamping"))
return bus_append_string(m, field, eq);
if (streq(field, "Symlinks"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (streq(field, "SocketProtocol"))
return bus_append_parse_ip_protocol(m, field, eq);
if (STR_IN_SET(field, "ListenStream",
"ListenDatagram",
"ListenSequentialPacket",
"ListenNetlink",
"ListenSpecial",
"ListenMessageQueue",
"ListenFIFO",
"ListenUSBFunction")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
else
r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
return 0;
}
static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
int r;
if (STR_IN_SET(field, "WakeSystem",
"RemainAfterElapse",
"Persistent",
"OnTimezoneChange",
"OnClockChange",
"FixedRandomDelay"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "AccuracySec",
"RandomizedDelaySec"))
return bus_append_parse_sec_rename(m, field, eq);
if (STR_IN_SET(field, "OnActiveSec",
"OnBootSec",
"OnStartupSec",
"OnUnitActiveSec",
"OnUnitInactiveSec")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
else {
usec_t t;
r = parse_sec(eq, &t);
if (r < 0)
return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "OnCalendar")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
else
r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
return 0;
}
static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
ConditionType t = _CONDITION_TYPE_INVALID;
bool is_condition = false;
int r;
if (STR_IN_SET(field, "Description",
"SourcePath",
"OnFailureJobMode",
"JobTimeoutAction",
"JobTimeoutRebootArgument",
"StartLimitAction",
"FailureAction",
"SuccessAction",
"RebootArgument",
"CollectMode"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "StopWhenUnneeded",
"RefuseManualStart",
"RefuseManualStop",
"AllowIsolate",
"IgnoreOnIsolate",
"DefaultDependencies"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "JobTimeoutSec",
"JobRunningTimeoutSec",
"StartLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "StartLimitBurst"))
return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, "SuccessActionExitStatus",
"FailureActionExitStatus")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "i", -1);
else {
uint8_t u;
r = safe_atou8(eq, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse %s=%s", field, eq);
r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation",
"RequiresMountsFor"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
t = condition_type_from_string(field);
if (t >= 0)
is_condition = true;
else
t = assert_type_from_string(field);
if (t >= 0) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
else {
const char *p = eq;
int trigger, negate;
trigger = *p == '|';
if (trigger)
p++;
negate = *p == '!';
if (negate)
p++;
r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
field, trigger, negate, p);
}
if (r < 0)
return bus_log_create_error(r);
return 1;
}
return 0;
}
int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
const char *eq, *field;
int r;
assert(m);
assert(assignment);
eq = strchr(assignment, '=');
if (!eq)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Not an assignment: %s", assignment);
field = strndupa(assignment, eq - assignment);
eq++;
switch (t) {
case UNIT_SERVICE:
r = bus_append_cgroup_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_execute_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_kill_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_service_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_SOCKET:
r = bus_append_cgroup_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_execute_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_kill_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_socket_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_TIMER:
r = bus_append_timer_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_PATH:
r = bus_append_path_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_SLICE:
r = bus_append_cgroup_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_SCOPE:
r = bus_append_cgroup_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_kill_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_scope_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_MOUNT:
r = bus_append_cgroup_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_execute_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_kill_property(m, field, eq);
if (r != 0)
return r;
r = bus_append_mount_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_AUTOMOUNT:
r = bus_append_automount_property(m, field, eq);
if (r != 0)
return r;
break;
case UNIT_TARGET:
case UNIT_DEVICE:
case UNIT_SWAP:
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Not supported unit type");
default:
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid unit type");
}
r = bus_append_unit_property(m, field, eq);
if (r != 0)
return r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown assignment: %s", assignment);
}
int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
char **i;
int r;
assert(m);
STRV_FOREACH(i, l) {
r = bus_append_unit_property_assignment(m, t, *i);
if (r < 0)
return r;
}
return 0;
}
int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *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, quiet);
return 0;
}
int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *path = NULL;
int r;
path = unit_dbus_path_from_name(name);
if (!path)
return log_oom();
/* This function warns on it's own, because otherwise it'd be awkward to pass
* the dbus error message around. */
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"LoadState",
&error,
load_state);
if (r < 0)
return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
return 0;
}
int unit_info_compare(const UnitInfo *a, const UnitInfo *b) {
int r;
/* First, order by machine */
r = strcasecmp_ptr(a->machine, b->machine);
if (r != 0)
return r;
/* Second, order by unit type */
r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
if (r != 0)
return r;
/* Third, order by name */
return strcasecmp(a->id, b->id);
}