diff options
author | Michael Biebl <biebl@debian.org> | 2018-06-22 13:38:31 +0200 |
---|---|---|
committer | Michael Biebl <biebl@debian.org> | 2018-06-22 13:38:31 +0200 |
commit | b012e92123bdc9fa10c2f079ec5bd9313b23e21a (patch) | |
tree | 94b74f04796e0da187092db7c2487aaf30f0faf1 /src/core | |
parent | 98393f852f2f66a74f7370aa63c07b26d610343c (diff) | |
download | systemd-b012e92123bdc9fa10c2f079ec5bd9313b23e21a.tar.gz |
New upstream version 239
Diffstat (limited to 'src/core')
130 files changed, 3846 insertions, 6021 deletions
diff --git a/src/core/all-units.h b/src/core/all-units.h new file mode 100644 index 0000000000..ed8350ebe0 --- /dev/null +++ b/src/core/all-units.h @@ -0,0 +1,14 @@ +#pragma once + +#include "unit.h" + +#include "automount.h" +#include "device.h" +#include "path.h" +#include "scope.h" +#include "service.h" +#include "slice.h" +#include "socket.h" +#include "swap.h" +#include "target.h" +#include "timer.h" diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c index 60e79035af..fdef433923 100644 --- a/src/core/audit-fd.c +++ b/src/core/audit-fd.c @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 <errno.h> diff --git a/src/core/audit-fd.h b/src/core/audit-fd.h index 43f4f193d8..8c1e471b96 100644 --- a/src/core/audit-fd.h +++ b/src/core/audit-fd.h @@ -1,24 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - 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/>. -***/ - int get_audit_fd(void); void close_audit_fd(void); diff --git a/src/core/automount.c b/src/core/automount.c index 01a6ff806e..1b96a52c00 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <fcntl.h> @@ -185,16 +167,16 @@ static int automount_verify(Automount *a) { if (path_equal(a->where, "/")) { log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing."); - return -EINVAL; + return -ENOEXEC; } r = unit_name_from_path(a->where, ".automount", &e); if (r < 0) - return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m"); + return log_unit_error_errno(UNIT(a), r, "Failed to generate unit name from path: %m"); if (!unit_has_name(UNIT(a), e)) { log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -212,7 +194,7 @@ static int automount_set_where(Automount *a) { if (r < 0) return r; - path_kill_slashes(a->where); + path_simplify(a->where, false); return 1; } @@ -265,7 +247,7 @@ static void automount_set_state(Automount *a, AutomountState state) { if (state != old_state) log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state)); - unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], 0); } static int automount_coldplug(Unit *u) { @@ -346,7 +328,7 @@ static int open_dev_autofs(Manager *m) { if (m->dev_autofs_fd >= 0) return m->dev_autofs_fd; - label_fix("/dev/autofs", false, false); + (void) label_fix("/dev/autofs", 0); m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY); if (m->dev_autofs_fd < 0) @@ -429,7 +411,7 @@ static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) { param.timeout.timeout = 0; else /* Convert to seconds, rounding up. */ - param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC; + param.timeout.timeout = DIV_ROUND_UP(usec, USEC_PER_SEC); if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0) return -errno; diff --git a/src/core/automount.h b/src/core/automount.h index b8be4d316e..21dd1c0774 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Automount Automount; #include "unit.h" @@ -58,3 +39,5 @@ extern const UnitVTable automount_vtable; const char* automount_result_to_string(AutomountResult i) _const_; AutomountResult automount_result_from_string(const char *s) _pure_; + +DEFINE_CAST(AUTOMOUNT, Automount); diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index 48666f64a2..8b66ef73dc 100644 --- a/src/core/bpf-firewall.c +++ b/src/core/bpf-firewall.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2016 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <arpa/inet.h> #include <assert.h> @@ -311,8 +293,7 @@ static int bpf_firewall_compile_bpf( return r; } while (false); - *ret = p; - p = NULL; + *ret = TAKE_PTR(p); return 0; } @@ -705,13 +686,14 @@ int bpf_firewall_supported(void) { 1, BPF_F_NO_PREALLOC); if (fd < 0) { - log_debug_errno(r, "Can't allocate BPF LPM TRIE map, BPF firewalling is not supported: %m"); + log_debug_errno(fd, "Can't allocate BPF LPM TRIE map, BPF firewalling is not supported: %m"); return supported = BPF_FIREWALL_UNSUPPORTED; } safe_close(fd); - if (bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program) < 0) { + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program); + if (r < 0) { log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m"); return supported = BPF_FIREWALL_UNSUPPORTED; } @@ -742,8 +724,7 @@ int bpf_firewall_supported(void) { .attach_bpf_fd = -1, }; - r = bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)); - if (r < 0) { + if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) { if (errno != EBADF) { log_debug_errno(errno, "Didn't get EBADF from BPF_PROG_ATTACH, BPF firewalling is not supported: %m"); return supported = BPF_FIREWALL_UNSUPPORTED; @@ -767,8 +748,7 @@ int bpf_firewall_supported(void) { .attach_flags = BPF_F_ALLOW_MULTI, }; - r = bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)); - if (r < 0) { + if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) { if (errno == EBADF) { log_debug_errno(errno, "Got EBADF when using BPF_F_ALLOW_MULTI, which indicates it is supported. Yay!"); return supported = BPF_FIREWALL_SUPPORTED_WITH_MULTI; diff --git a/src/core/bpf-firewall.h b/src/core/bpf-firewall.h index a0658e3b86..e2d08a0fc8 100644 --- a/src/core/bpf-firewall.h +++ b/src/core/bpf-firewall.h @@ -1,24 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2016 Daniel Mack - - 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 <inttypes.h> diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 3c0ff09639..bb02436203 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <fcntl.h> #include <fnmatch.h> @@ -24,6 +6,7 @@ #include "alloc-util.h" #include "blockdev-util.h" #include "bpf-firewall.h" +#include "btrfs-util.h" #include "bus-error.h" #include "cgroup-util.h" #include "cgroup.h" @@ -53,7 +36,7 @@ bool manager_owns_root_cgroup(Manager *m) { if (detect_container() > 0) return false; - return isempty(m->cgroup_root) || path_equal(m->cgroup_root, "/"); + return empty_or_root(m->cgroup_root); } bool unit_has_root_cgroup(Unit *u) { @@ -319,32 +302,36 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { } } -static int lookup_block_device(const char *p, dev_t *dev) { +static int lookup_block_device(const char *p, dev_t *ret) { struct stat st; int r; assert(p); - assert(dev); + assert(ret); - r = stat(p, &st); - if (r < 0) - return log_warning_errno(errno, "Couldn't stat device %s: %m", p); + if (stat(p, &st) < 0) + return log_warning_errno(errno, "Couldn't stat device '%s': %m", p); if (S_ISBLK(st.st_mode)) - *dev = st.st_rdev; - else if (major(st.st_dev) != 0) { - /* If this is not a device node then find the block - * device this file is stored on */ - *dev = st.st_dev; - - /* If this is a partition, try to get the originating - * block device */ - (void) block_get_whole_disk(*dev, dev); - } else { - log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p); - return -ENODEV; + *ret = st.st_rdev; + else if (major(st.st_dev) != 0) + *ret = st.st_dev; /* If this is not a device node then use the block device this file is stored on */ + else { + /* If this is btrfs, getting the backing block device is a bit harder */ + r = btrfs_get_block_device(p, ret); + if (r < 0 && r != -ENOTTY) + return log_warning_errno(r, "Failed to determine block device backing btrfs file system '%s': %m", p); + if (r == -ENOTTY) { + log_warning("'%s' is not a block device node, and file system block device cannot be determined or is not local.", p); + return -ENODEV; + } } + /* If this is a LUKS device, try to get the originating block device */ + (void) block_get_originating(*ret, ret); + + /* If this is a partition, try to get the originating block device */ + (void) block_get_whole_disk(*ret, ret); return 0; } @@ -632,26 +619,22 @@ static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint "Failed to set blkio.weight_device: %m"); } -static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { +static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; CGroupIOLimitType type; dev_t dev; - unsigned n = 0; int r; r = lookup_block_device(dev_path, &dev); if (r < 0) - return 0; + return; - for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { - if (limits[type] != cgroup_io_limit_defaults[type]) { + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + if (limits[type] != cgroup_io_limit_defaults[type]) xsprintf(limit_bufs[type], "%" PRIu64, limits[type]); - n++; - } else { + else xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0"); - } - } xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev), limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX], @@ -660,36 +643,28 @@ static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint if (r < 0) log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set io.max: %m"); - return n; } -static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) { +static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) { char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; dev_t dev; - unsigned n = 0; int r; r = lookup_block_device(dev_path, &dev); if (r < 0) - return 0; + return; - if (rbps != CGROUP_LIMIT_MAX) - n++; sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps); r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf); if (r < 0) log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.throttle.read_bps_device: %m"); - if (wbps != CGROUP_LIMIT_MAX) - n++; sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps); r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf); if (r < 0) log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.throttle.write_bps_device: %m"); - - return n; } static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { @@ -840,16 +815,15 @@ static void cgroup_context_apply( /* Apply limits and free ones without config. */ if (has_io) { - CGroupIODeviceLimit *l, *next; + CGroupIODeviceLimit *l; + + LIST_FOREACH(device_limits, l, c->io_device_limits) + cgroup_apply_io_device_limit(u, l->path, l->limits); - LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { - if (!cgroup_apply_io_device_limit(u, l->path, l->limits)) - cgroup_context_free_io_device_limit(c, l); - } } else if (has_blockio) { - CGroupBlockIODeviceBandwidth *b, *next; + CGroupBlockIODeviceBandwidth *b; - LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; CGroupIOLimitType type; @@ -862,8 +836,7 @@ static void cgroup_context_apply( log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s", b->rbps, b->wbps, b->path); - if (!cgroup_apply_io_device_limit(u, b->path, limits)) - cgroup_context_free_blockio_device_bandwidth(c, b); + cgroup_apply_io_device_limit(u, b->path, limits); } } } @@ -917,21 +890,19 @@ static void cgroup_context_apply( /* Apply limits and free ones without config. */ if (has_io) { - CGroupIODeviceLimit *l, *next; + CGroupIODeviceLimit *l; - LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + LIST_FOREACH(device_limits, l, c->io_device_limits) { log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s", l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path); - if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX])) - cgroup_context_free_io_device_limit(c, l); + cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]); } } else if (has_blockio) { - CGroupBlockIODeviceBandwidth *b, *next; + CGroupBlockIODeviceBandwidth *b; - LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) - if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps)) - cgroup_context_free_blockio_device_bandwidth(c, b); + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) + cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps); } } @@ -1318,7 +1289,7 @@ const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) { if (u->cgroup_path && u->cgroup_realized && - (u->cgroup_realized_mask & mask) == mask) + FLAGS_SET(u->cgroup_realized_mask, mask)) return u->cgroup_path; u = UNIT_DEREF(u->slice); @@ -1381,8 +1352,7 @@ int unit_set_cgroup_path(Unit *u, const char *path) { unit_release_cgroup(u); - u->cgroup_path = p; - p = NULL; + u->cgroup_path = TAKE_PTR(p); return 1; } @@ -1469,6 +1439,7 @@ static int unit_create_cgroup( CGroupContext *c; int r; + bool created; assert(u); @@ -1485,14 +1456,20 @@ static int unit_create_cgroup( r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path); if (r < 0) return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path); + created = !!r; /* Start watching it */ (void) unit_watch_cgroup(u); - /* Enable all controllers we need */ - r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path); - if (r < 0) - log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path); + /* Preserve enabled controllers in delegated units, adjust others. */ + if (created || !unit_cgroup_delegate(u)) { + + /* Enable all controllers we need */ + r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", + u->cgroup_path); + } /* Keep track that this is now realized */ u->cgroup_realized = true; @@ -1535,7 +1512,7 @@ static int unit_attach_pid_to_cgroup_via_bus(Unit *u, pid_t pid, const char *suf return -EINVAL; pp = strjoina("/", pp, suffix_path); - path_kill_slashes(pp); + path_simplify(pp, false); r = sd_bus_call_method(u->manager->system_bus, "org.freedesktop.systemd1", @@ -1706,7 +1683,6 @@ static void unit_remove_from_cgroup_realize_queue(Unit *u) { u->in_cgroup_realize_queue = false; } - /* Check if necessary controllers and attributes for a unit are in place. * * If so, do nothing. @@ -2057,7 +2033,7 @@ static int on_cgroup_empty_event(sd_event_source *s, void *userdata) { /* More stuff queued, let's make sure we remain enabled */ r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); if (r < 0) - log_debug_errno(r, "Failed to reenable cgroup empty event source: %m"); + log_debug_errno(r, "Failed to reenable cgroup empty event source, ignoring: %m"); } unit_add_to_gc_queue(u); @@ -2272,19 +2248,20 @@ int manager_setup_cgroup(Manager *m) { /* 5. Make sure we are in the special "init.scope" unit in the root slice. */ scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); - if (r < 0) - return log_error_errno(r, "Failed to create %s control group: %m", scope_path); + if (r >= 0) { + /* Also, move all other userspace processes remaining in the root cgroup into that scope. */ + r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); + if (r < 0) + log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); - /* Also, move all other userspace processes remaining in the root cgroup into that scope. */ - r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); - if (r < 0) - log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); + /* 6. And pin it, so that it cannot be unmounted */ + safe_close(m->pin_cgroupfs_fd); + m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); + if (m->pin_cgroupfs_fd < 0) + return log_error_errno(errno, "Failed to open pin file: %m"); - /* 6. And pin it, so that it cannot be unmounted */ - safe_close(m->pin_cgroupfs_fd); - m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); - if (m->pin_cgroupfs_fd < 0) - return log_error_errno(errno, "Failed to open pin file: %m"); + } else if (r < 0 && !m->test_run_flags) + return log_error_errno(r, "Failed to create %s control group: %m", scope_path); /* 7. Always enable hierarchical support if it exists... */ if (!all_unified && m->test_run_flags == 0) @@ -2305,7 +2282,7 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { /* We can't really delete the group, since we are in it. But * let's trim it. */ - if (delete && m->cgroup_root) + if (delete && m->cgroup_root && m->test_run_flags != MANAGER_TEST_RUN_MINIMAL) (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); m->cgroup_empty_event_source = sd_event_source_unref(m->cgroup_empty_event_source); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index ae5f1c7647..2d2ff6fc3c 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 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 <stdbool.h> #include "cgroup-util.h" @@ -140,7 +121,8 @@ typedef enum CGroupIPAccountingMetric { _CGROUP_IP_ACCOUNTING_METRIC_INVALID = -1, } CGroupIPAccountingMetric; -#include "unit.h" +typedef struct Unit Unit; +typedef struct Manager Manager; void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c index f021f03a06..c4794501c2 100644 --- a/src/core/chown-recursive.c +++ b/src/core/chown-recursive.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2017 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <sys/types.h> #include <sys/stat.h> diff --git a/src/core/chown-recursive.h b/src/core/chown-recursive.h index 7540a8db59..f3fa40a656 100644 --- a/src/core/chown-recursive.h +++ b/src/core/chown-recursive.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2017 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include <sys/types.h> int path_chown_recursive(const char *path, uid_t uid, gid_t gid); diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index 113d352a43..bd6e6a9dde 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "automount.h" #include "bus-util.h" diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h index 378119410c..3e165b0566 100644 --- a/src/core/dbus-automount.h +++ b/src/core/dbus-automount.h @@ -1,25 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ +#include "sd-bus.h" +#include "sd-bus-vtable.h" +#include "unit.h" extern const sd_bus_vtable bus_automount_vtable[]; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index f480664613..540bc77aed 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 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 <arpa/inet.h> #include <stdio_ext.h> @@ -389,7 +371,7 @@ static int bus_cgroup_set_transient_property( cc = cgroup_controller_from_string(t); if (cc < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown cgroup contoller '%s'", t); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown cgroup controller '%s'", t); mask |= CGROUP_CONTROLLER_TO_MASK(cc); } @@ -446,14 +428,129 @@ static int bus_cgroup_set_boolean( return 1; } -static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,); -static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID,); -static BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,); -static BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID,); -static BUS_DEFINE_SET_CGROUP_WEIGHT(memory, CGROUP_MASK_MEMORY, , CGROUP_LIMIT_MAX, "infinity"); -static BUS_DEFINE_SET_CGROUP_WEIGHT(tasks_max, CGROUP_MASK_PIDS, , (uint64_t) -1, "infinity"); -static BUS_DEFINE_SET_CGROUP_SCALE(memory, CGROUP_MASK_MEMORY, physical_memory_scale); -static BUS_DEFINE_SET_CGROUP_SCALE(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale); +#define BUS_DEFINE_SET_CGROUP_WEIGHT(function, mask, check, val) \ + static int bus_cgroup_set_##function( \ + Unit *u, \ + const char *name, \ + uint64_t *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + uint64_t v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "t", &v); \ + if (r < 0) \ + return r; \ + \ + if (!check(v)) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Value specified in %s is out of range", name); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = v; \ + unit_invalidate_cgroup(u, (mask)); \ + \ + if (v == (val)) \ + unit_write_settingf(u, flags, name, \ + "%s=", name); \ + else \ + unit_write_settingf(u, flags, name, \ + "%s=%" PRIu64, name, v); \ + } \ + \ + return 1; \ + } + +#define BUS_DEFINE_SET_CGROUP_LIMIT(function, mask, scale, minimum) \ + static int bus_cgroup_set_##function( \ + Unit *u, \ + const char *name, \ + uint64_t *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + uint64_t v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "t", &v); \ + if (r < 0) \ + return r; \ + \ + if (v < minimum) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Value specified in %s is out of range", name); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = v; \ + unit_invalidate_cgroup(u, (mask)); \ + \ + if (v == CGROUP_LIMIT_MAX) \ + unit_write_settingf(u, flags, name, \ + "%s=infinity", name); \ + else \ + unit_write_settingf(u, flags, name, \ + "%s=%" PRIu64, name, v); \ + } \ + \ + return 1; \ + } \ + static int bus_cgroup_set_##function##_scale( \ + Unit *u, \ + const char *name, \ + uint64_t *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + uint64_t v; \ + uint32_t raw; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "u", &raw); \ + if (r < 0) \ + return r; \ + \ + v = scale(raw, UINT32_MAX); \ + if (v < minimum || v >= UINT64_MAX) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Value specified in %s is out of range", name); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + const char *e; \ + \ + *p = v; \ + unit_invalidate_cgroup(u, (mask)); \ + \ + /* Chop off suffix */ \ + assert_se(e = endswith(name, "Scale")); \ + name = strndupa(name, e - name); \ + \ + unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, \ + (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); \ + } \ + \ + return 1; \ + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" +BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); +BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID); +BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); +BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID); +BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1); +BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0); +BUS_DEFINE_SET_CGROUP_LIMIT(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale, 1); +#pragma GCC diagnostic pop int bus_cgroup_set_property( Unit *u, @@ -516,7 +613,7 @@ int bus_cgroup_set_property( return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); if (streq(name, "MemorySwapMax")) - return bus_cgroup_set_memory(u, name, &c->memory_swap_max, message, flags, error); + return bus_cgroup_set_swap(u, name, &c->memory_swap_max, message, flags, error); if (streq(name, "MemoryMax")) return bus_cgroup_set_memory(u, name, &c->memory_max, message, flags, error); @@ -531,7 +628,7 @@ int bus_cgroup_set_property( return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); if (streq(name, "MemorySwapMaxScale")) - return bus_cgroup_set_memory_scale(u, name, &c->memory_swap_max, message, flags, error); + return bus_cgroup_set_swap_scale(u, name, &c->memory_swap_max, message, flags, error); if (streq(name, "MemoryMaxScale")) return bus_cgroup_set_memory_scale(u, name, &c->memory_max, message, flags, error); @@ -585,9 +682,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!path_is_normalized(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupIODeviceLimit *a = NULL, *b; @@ -673,9 +769,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!path_is_normalized(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range"); @@ -759,9 +854,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!path_is_normalized(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupBlockIODeviceBandwidth *a = NULL, *b; @@ -859,9 +953,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!path_is_normalized(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range"); @@ -961,15 +1054,12 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { - if ((!is_deviceallow_pattern(path) && - !path_startswith(path, "/run/systemd/inaccessible/")) || - strpbrk(path, WHITESPACE)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node"); + if (!valid_device_allow_pattern(path) || strpbrk(path, WHITESPACE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern"); if (isempty(rwm)) rwm = "rwm"; - - if (!in_charset(rwm, "rwm")) + else if (!in_charset(rwm, "rwm")) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h index 0588370aa5..188baa99e9 100644 --- a/src/core/dbus-cgroup.h +++ b/src/core/dbus-cgroup.h @@ -1,27 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" +#include "unit.h" #include "cgroup.h" extern const sd_bus_vtable bus_cgroup_vtable[]; diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c index fbb60d821e..6cf7f58e02 100644 --- a/src/core/dbus-device.c +++ b/src/core/dbus-device.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "dbus-device.h" #include "device.h" diff --git a/src/core/dbus-device.h b/src/core/dbus-device.h index c8098708f4..077a2bf128 100644 --- a/src/core/dbus-device.h +++ b/src/core/dbus-device.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "unit.h" +#include "sd-bus-vtable.h" extern const sd_bus_vtable bus_device_vtable[]; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 635213a866..c44970c10c 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1,23 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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/mount.h> #include <sys/prctl.h> #include <stdio_ext.h> @@ -63,13 +46,18 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); - static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode); - -static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); -static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_home, protect_home, ProtectHome); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_system, protect_system, ProtectSystem); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_personality, personality, unsigned long); +static BUS_DEFINE_PROPERTY_GET(property_get_ioprio, "i", ExecContext, exec_context_get_effective_ioprio); +static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_class, "i", ExecContext, exec_context_get_effective_ioprio, IOPRIO_PRIO_CLASS); +static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, exec_context_get_effective_ioprio, IOPRIO_PRIO_DATA); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL); +static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI); +static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC); static int property_get_environment_files( sd_bus *bus, @@ -112,7 +100,6 @@ static int property_get_oom_score_adjust( void *userdata, sd_bus_error *error) { - ExecContext *c = userdata; int32_t n; @@ -142,7 +129,6 @@ static int property_get_nice( void *userdata, sd_bus_error *error) { - ExecContext *c = userdata; int32_t n; @@ -162,63 +148,6 @@ static int property_get_nice( return sd_bus_message_append(reply, "i", n); } -static int property_get_ioprio( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", exec_context_get_effective_ioprio(c)); -} - -static int property_get_ioprio_class( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", IOPRIO_PRIO_CLASS(exec_context_get_effective_ioprio(c))); -} - -static int property_get_ioprio_priority( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", IOPRIO_PRIO_DATA(exec_context_get_effective_ioprio(c))); -} - static int property_get_cpu_sched_policy( sd_bus *bus, const char *path, @@ -291,10 +220,7 @@ static int property_get_cpu_affinity( assert(reply); assert(c); - if (c->cpuset) - return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); - else - return sd_bus_message_append_array(reply, 'y', NULL, 0); + return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); } static int property_get_timer_slack_nsec( @@ -321,57 +247,6 @@ static int property_get_timer_slack_nsec( return sd_bus_message_append(reply, "t", u); } -static int property_get_capability_bounding_set( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "t", c->capability_bounding_set); -} - -static int property_get_ambient_capabilities( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "t", c->capability_ambient_set); -} - -static int property_get_empty_string( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", ""); -} - static int property_get_syscall_filter( sd_bus *bus, const char *path, @@ -424,10 +299,8 @@ static int property_get_syscall_filter( if (r < 0) return -ENOMEM; } - } else { - s = name; - name = NULL; - } + } else + s = TAKE_PTR(name); r = strv_consume(&l, s); if (r < 0) @@ -489,24 +362,6 @@ static int property_get_syscall_archs( return 0; } -static int property_get_syscall_errno( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", (int32_t) c->syscall_errno); -} - static int property_get_selinux_context( sd_bus *bus, const char *path, @@ -561,24 +416,6 @@ static int property_get_smack_process_label( return sd_bus_message_append(reply, "(bs)", c->smack_process_label_ignore, c->smack_process_label); } -static int property_get_personality( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "s", personality_to_string(c->personality)); -} - static int property_get_address_families( sd_bus *bus, const char *path, @@ -654,42 +491,6 @@ static int property_get_working_directory( return sd_bus_message_append(reply, "s", wd); } -static int property_get_syslog_level( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", LOG_PRI(c->syslog_priority)); -} - -static int property_get_syslog_facility( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - ExecContext *c = userdata; - - assert(bus); - assert(reply); - assert(c); - - return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority)); -} - static int property_get_stdio_fdname( sd_bus *bus, const char *path, @@ -757,7 +558,7 @@ static int property_get_bind_paths( assert(property); assert(reply); - ro = !!strstr(property, "ReadOnly"); + ro = strstr(property, "ReadOnly"); r = sd_bus_message_open_container(reply, 'a', "(ssbt)"); if (r < 0) @@ -914,13 +715,13 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST), @@ -938,8 +739,9 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateMounts", "b", bus_property_get_bool, offsetof(ExecContext, private_mounts), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectHome", "s", property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectSystem", "s", property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), @@ -950,8 +752,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Personality", "s", property_get_personality, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SystemCallErrorNumber", "i", bus_property_get_int, offsetof(ExecContext, syscall_errno), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Personality", "s", property_get_personality, offsetof(ExecContext, personality), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1125,12 +927,11 @@ int bus_set_transient_exec_command( return -ENOMEM; } - c->argv = argv; - argv = NULL; + c->argv = TAKE_PTR(argv); c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0; - path_kill_slashes(c->path); + path_simplify(c->path, false); exec_command_append_list(exec_command, c); } @@ -1219,15 +1020,15 @@ static BUS_DEFINE_SET_TRANSIENT_IS_VALID(nice, "i", int32_t, int, "%" PRIi32, ni static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(utmp_mode, ExecUtmpMode, exec_utmp_mode_from_string); -static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_system, ProtectSystem, parse_protect_system_or_bool); -static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, parse_protect_home_or_bool); +static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_system, ProtectSystem, protect_system_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, protect_home_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyring_mode_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string_alloc); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(sched_policy, "i", int32_t, int, "%" PRIi32, sched_policy_to_string_alloc_with_check); -static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flag_to_string_many_with_check); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flags_to_string); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check); int bus_exec_context_set_transient_property( @@ -1238,8 +1039,8 @@ int bus_exec_context_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { - const char *soft = NULL; - int r, ri; + const char *suffix; + int r; assert(u); assert(c); @@ -1305,6 +1106,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "PrivateDevices")) return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error); + if (streq(name, "PrivateMounts")) + return bus_set_transient_bool(u, name, &c->private_mounts, message, flags, error); + if (streq(name, "PrivateNetwork")) return bus_set_transient_bool(u, name, &c->private_network, message, flags, error); @@ -2043,9 +1847,7 @@ int bus_exec_context_set_transient_property( if (!e) return -ENOMEM; - strv_free(c->environment); - c->environment = e; - + strv_free_and_replace(c->environment, e); unit_write_settingf(u, flags, name, "Environment=%s", joined); } } @@ -2079,9 +1881,7 @@ int bus_exec_context_set_transient_property( if (!e) return -ENOMEM; - strv_free(c->unset_environment); - c->unset_environment = e; - + strv_free_and_replace(c->unset_environment, e); unit_write_settingf(u, flags, name, "UnsetEnvironment=%s", joined); } } @@ -2250,7 +2050,7 @@ int bus_exec_context_set_transient_property( if (!path_is_absolute(i + offset)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); - path_kill_slashes(i + offset); + path_simplify(i + offset, false); } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -2290,8 +2090,14 @@ int bus_exec_context_set_transient_property( return r; STRV_FOREACH(p, l) { - if (!path_is_normalized(*p) || path_is_absolute(*p)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= path is not valid: %s", name, *p); + if (!path_is_normalized(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= path is not normalized: %s", name, *p); + + if (path_is_absolute(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= path is absolute: %s", name, *p); + + if (path_startswith(*p, "private")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= path can't be 'private': %s", name, *p); } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -2464,73 +2270,77 @@ int bus_exec_context_set_transient_property( } return 1; - } - ri = rlimit_from_string(name); - if (ri < 0) { - soft = endswith(name, "Soft"); - if (soft) { - const char *n; + } else if ((suffix = startswith(name, "Limit"))) { + const char *soft = NULL; + int ri; - n = strndupa(name, soft - name); - ri = rlimit_from_string(n); - if (ri >= 0) - name = n; + ri = rlimit_from_string(suffix); + if (ri < 0) { + soft = endswith(suffix, "Soft"); + if (soft) { + const char *n; + n = strndupa(suffix, soft - suffix); + ri = rlimit_from_string(n); + if (ri >= 0) + name = strjoina("Limit", n); + } } - } - if (ri >= 0) { - uint64_t rl; - rlim_t x; + if (ri >= 0) { + uint64_t rl; + rlim_t x; - r = sd_bus_message_read(message, "t", &rl); - if (r < 0) - return r; - - if (rl == (uint64_t) -1) - x = RLIM_INFINITY; - else { - x = (rlim_t) rl; - - if ((uint64_t) x != rl) - return -ERANGE; - } - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *f = NULL; - struct rlimit nl; - - if (c->rlimit[ri]) { - nl = *c->rlimit[ri]; - - if (soft) - nl.rlim_cur = x; - else - nl.rlim_max = x; - } else - /* When the resource limit is not initialized yet, then assign the value to both fields */ - nl = (struct rlimit) { - .rlim_cur = x, - .rlim_max = x, - }; - - r = rlimit_format(&nl, &f); + r = sd_bus_message_read(message, "t", &rl); if (r < 0) return r; - if (c->rlimit[ri]) - *c->rlimit[ri] = nl; + if (rl == (uint64_t) -1) + x = RLIM_INFINITY; else { - c->rlimit[ri] = newdup(struct rlimit, &nl, 1); - if (!c->rlimit[ri]) - return -ENOMEM; + x = (rlim_t) rl; + + if ((uint64_t) x != rl) + return -ERANGE; } - unit_write_settingf(u, flags, name, "%s=%s", name, f); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *f = NULL; + struct rlimit nl; + + if (c->rlimit[ri]) { + nl = *c->rlimit[ri]; + + if (soft) + nl.rlim_cur = x; + else + nl.rlim_max = x; + } else + /* When the resource limit is not initialized yet, then assign the value to both fields */ + nl = (struct rlimit) { + .rlim_cur = x, + .rlim_max = x, + }; + + r = rlimit_format(&nl, &f); + if (r < 0) + return r; + + if (c->rlimit[ri]) + *c->rlimit[ri] = nl; + else { + c->rlimit[ri] = newdup(struct rlimit, &nl, 1); + if (!c->rlimit[ri]) + return -ENOMEM; + } + + unit_write_settingf(u, flags, name, "%s=%s", name, f); + } + + return 1; } - return 1; } return 0; diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index 4d9b368f81..84051700ab 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "execute.h" diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 0802fc9773..5551c56d0e 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include "sd-bus.h" @@ -274,8 +256,7 @@ int bus_job_coldplug_bus_track(Job *j) { assert(j); - deserialized_clients = j->deserialized_clients; - j->deserialized_clients = NULL; + deserialized_clients = TAKE_PTR(j->deserialized_clients); if (strv_isempty(deserialized_clients)) return 0; diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h index 65d87814a6..3cc60f22ee 100644 --- a/src/core/dbus-job.h +++ b/src/core/dbus-job.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "job.h" diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 53d0aad63b..028e7ec1c1 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 "bus-util.h" #include "dbus-kill.h" diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h index 1df3b37c65..8192e94fbb 100644 --- a/src/core/dbus-kill.h +++ b/src/core/dbus-kill.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - 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 "sd-bus.h" +#include "sd-bus-vtable.h" #include "kill.h" #include "unit.h" diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 889779d548..4ed68af1e0 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sys/prctl.h> @@ -39,6 +21,7 @@ #include "fs-util.h" #include "install.h" #include "log.h" +#include "os-util.h" #include "parse-util.h" #include "path-util.h" #include "selinux-access.h" @@ -59,35 +42,14 @@ static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) { (force ? UNIT_FILE_FORCE : 0); } -static int property_get_version( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", PACKAGE_VERSION); -} - -static int property_get_features( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", SYSTEMD_FEATURES); -} +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_version, "s", PACKAGE_VERSION); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_features, "s", SYSTEMD_FEATURES); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_architecture, "s", architecture_to_string(uname_architecture())); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_log_target, "s", log_target_to_string(log_get_target())); +static BUS_DEFINE_PROPERTY_GET2(property_get_system_state, "s", Manager, manager_state, manager_state_to_string); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_timer_slack_nsec, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); +static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "u", Hashmap *, hashmap_size); +static BUS_DEFINE_PROPERTY_GET_REF(property_get_set_size, "u", Set *, set_size); static int property_get_virtualization( sd_bus *bus, @@ -112,22 +74,7 @@ static int property_get_virtualization( return sd_bus_message_append( reply, "s", - v == VIRTUALIZATION_NONE ? "" : virtualization_to_string(v)); -} - -static int property_get_architecture( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", architecture_to_string(uname_architecture())); + v == VIRTUALIZATION_NONE ? NULL : virtualization_to_string(v)); } static int property_get_tainted( @@ -153,21 +100,6 @@ static int property_get_tainted( return sd_bus_message_append(reply, "s", s); } -static int property_get_log_target( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", log_target_to_string(log_get_target())); -} - static int property_set_log_target( sd_bus *bus, const char *path, @@ -177,6 +109,7 @@ static int property_set_log_target( void *userdata, sd_bus_error *error) { + Manager *m = userdata; const char *t; int r; @@ -187,7 +120,19 @@ static int property_set_log_target( if (r < 0) return r; - return log_set_target_from_string(t); + if (isempty(t)) + manager_restore_original_log_target(m); + else { + LogTarget target; + + target = log_target_from_string(t); + if (target < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log target '%s'", t); + + manager_override_log_target(m, target); + } + + return 0; } static int property_get_log_level( @@ -221,6 +166,7 @@ static int property_set_log_level( void *userdata, sd_bus_error *error) { + Manager *m = userdata; const char *t; int r; @@ -231,64 +177,19 @@ static int property_set_log_level( if (r < 0) return r; - r = log_set_max_level_from_string(t); - if (r == 0) - log_info("Setting log level to %s.", t); - return r; -} - -static int property_get_n_names( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->units)); -} - -static int property_get_n_failed_units( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "u", (uint32_t) set_size(m->failed_units)); -} - -static int property_get_n_jobs( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { + if (isempty(t)) + manager_restore_original_log_level(m); + else { + int level; - Manager *m = userdata; + level = log_level_from_string(t); + if (level < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log level '%s'", t); - assert(bus); - assert(reply); - assert(m); + manager_override_log_level(m, level); + } - return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->jobs)); + return 0; } static int property_get_progress( @@ -315,7 +216,7 @@ static int property_get_progress( return sd_bus_message_append(reply, "d", d); } -static int property_get_system_state( +static int property_get_show_status( sd_bus *bus, const char *path, const char *interface, @@ -325,12 +226,14 @@ static int property_get_system_state( sd_bus_error *error) { Manager *m = userdata; + int b; assert(bus); assert(reply); assert(m); - return sd_bus_message_append(reply, "s", manager_state_to_string(manager_state(m))); + b = m->show_status > 0; + return sd_bus_message_append_basic(reply, 'b', &b); } static int property_set_runtime_watchdog( @@ -357,21 +260,6 @@ static int property_set_runtime_watchdog( return watchdog_set_timeout(t); } -static int property_get_timer_slack_nsec( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); -} - static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) { Unit *u; int r; @@ -557,6 +445,32 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd return sd_bus_reply_method_return(message, "o", path); } +static int method_get_unit_by_control_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + Manager *m = userdata; + const char *cgroup; + Unit *u; + int r; + + r = sd_bus_message_read(message, "s", &cgroup); + if (r < 0) + return r; + + u = manager_get_unit_by_cgroup(m, cgroup); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Control group '%s' is not valid or not managed by this instance", cgroup); + + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + + path = unit_dbus_path(u); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *path = NULL; Manager *m = userdata; @@ -714,7 +628,7 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = bus_unit_check_load_state(u, error); + r = bus_unit_validate_load_state(u, error); if (r < 0) return r; @@ -738,7 +652,7 @@ static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; - r = bus_unit_check_load_state(u, error); + r = bus_unit_validate_load_state(u, error); if (r < 0) return r; @@ -762,7 +676,7 @@ static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_err if (r < 0) return r; - r = bus_unit_check_load_state(u, error); + r = bus_unit_validate_load_state(u, error); if (r < 0) return r; @@ -1308,7 +1222,7 @@ static int method_unsubscribe(sd_bus_message *message, void *userdata, sd_bus_er return sd_bus_reply_method_return(message, NULL); } -static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int dump_impl(sd_bus_message *message, void *userdata, sd_bus_error *error, int (*reply)(sd_bus_message *, char *)) { _cleanup_free_ char *dump = NULL; Manager *m = userdata; int r; @@ -1326,9 +1240,31 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er if (r < 0) return r; + return reply(message, dump); +} + +static int reply_dump(sd_bus_message *message, char *dump) { return sd_bus_reply_method_return(message, "s", dump); } +static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return dump_impl(message, userdata, error, reply_dump); +} + +static int reply_dump_by_fd(sd_bus_message *message, char *dump) { + _cleanup_close_ int fd = -1; + + fd = acquire_data_fd(dump, strlen(dump), 0); + if (fd < 0) + return fd; + + return sd_bus_reply_method_return(message, "h", fd); +} + +static int method_dump_by_fd(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return dump_impl(message, userdata, error, reply_dump_by_fd); +} + static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed."); } @@ -1546,7 +1482,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (available < RELOAD_DISK_SPACE_MIN) { char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX]; - log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfuly. " + log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfully. " "Currently, %s are free, but %s are suggested. Proceeding anyway.", format_bytes(fb_available, sizeof(fb_available), available), format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN)); @@ -1583,7 +1519,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (!path_is_absolute(init)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path to init binary '%s' not absolute.", init); - r = chase_symlinks(init, root, CHASE_PREFIX_ROOT, &chased); + r = chase_symlinks(init, root, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &chased); if (r < 0) return sd_bus_error_set_errnof(error, r, "Could not resolve init executable %s: %m", init); @@ -1798,6 +1734,50 @@ static int method_lookup_dynamic_user_by_uid(sd_bus_message *message, void *user return sd_bus_reply_method_return(message, "s", name); } +static int method_get_dynamic_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + DynamicUser *d; + Iterator i; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance."); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(us)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(d, m->dynamic_users, i) { + uid_t uid; + + r = dynamic_user_current(d, &uid); + if (r == -EAGAIN) /* not realized yet? */ + continue; + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to lookup a dynamic user."); + + r = sd_bus_message_append(reply, "(us)", uid, d->name); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; @@ -1941,9 +1921,10 @@ static int install_error( sd_bus_error *error, int c, UnitFileChange *changes, - unsigned n_changes) { + size_t n_changes) { + + size_t i; int r; - unsigned i; for (i = 0; i < n_changes; i++) @@ -1999,12 +1980,12 @@ static int reply_unit_file_changes_and_free( sd_bus_message *message, int carries_install_info, UnitFileChange *changes, - unsigned n_changes, + size_t n_changes, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; bool bad = false, good = false; - unsigned i; + size_t i; int r; if (unit_file_changes_have_modification(changes, n_changes)) { @@ -2065,13 +2046,13 @@ fail: static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), + int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), bool carries_install_info, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; UnitFileFlags flags; int runtime, force, r; @@ -2113,7 +2094,7 @@ static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bu return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); } -static int unit_file_preset_without_mode(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes) { +static int unit_file_preset_without_mode(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { return unit_file_preset(scope, flags, root_dir, files, UNIT_FILE_PRESET_FULL, changes, n_changes); } @@ -2129,7 +2110,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; int runtime, force, r; @@ -2173,12 +2154,12 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use static int method_disable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), + int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; int r, runtime; assert(message); @@ -2216,7 +2197,7 @@ static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_ static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; Manager *m = userdata; int r; @@ -2242,7 +2223,7 @@ static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_ static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; Manager *m = userdata; const char *name; int force, r; @@ -2273,7 +2254,7 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; const char *mode; @@ -2318,7 +2299,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; UnitFileChange *changes = NULL; - unsigned n_changes = 0; + size_t n_changes = 0; int runtime, force, r; char *target, *type; UnitDependency dep; @@ -2357,7 +2338,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd static int method_get_unit_file_links(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; UnitFileChange *changes = NULL; - unsigned n_changes = 0, i; + size_t n_changes = 0, i; UnitFileFlags flags; const char *name; char **p; @@ -2439,15 +2420,15 @@ const sd_bus_vtable bus_manager_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0), SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0), - SD_BUS_PROPERTY("NNames", "u", property_get_n_names, 0, 0), - SD_BUS_PROPERTY("NFailedUnits", "u", property_get_n_failed_units, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("NJobs", "u", property_get_n_jobs, 0, 0), + SD_BUS_PROPERTY("NNames", "u", property_get_hashmap_size, offsetof(Manager, units), 0), + SD_BUS_PROPERTY("NFailedUnits", "u", property_get_set_size, offsetof(Manager, failed_units), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("NJobs", "u", property_get_hashmap_size, offsetof(Manager, jobs), 0), SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0), SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0), SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0), SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ShowStatus", "b", property_get_show_status, 0, 0), SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2508,6 +2489,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitByInvocationID", "ay", "o", method_get_unit_by_invocation_id, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitByControlGroup", "s", "o", method_get_unit_by_control_group, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2539,8 +2521,9 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("DumpByFileDescriptor", NULL, "h", method_dump_by_fd, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), + SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0), @@ -2572,6 +2555,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetDynamicUsers", NULL, "a(us)", method_get_dynamic_users, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("UnitNew", "so", 0), SD_BUS_SIGNAL("UnitRemoved", "so", 0), diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h index 1a6090f0f5..d0306adc70 100644 --- a/src/core/dbus-manager.h +++ b/src/core/dbus-manager.h @@ -1,24 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ +#include "sd-bus-vtable.h" #include "manager.h" diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 9e52f55fa5..3f98d3ecf0 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "bus-util.h" #include "dbus-cgroup.h" @@ -28,78 +10,33 @@ #include "string-util.h" #include "unit.h" -static int property_get_what( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Mount *m = userdata; - const char *d = NULL; - - assert(bus); - assert(reply); - assert(m); - +static const char *mount_get_what(const Mount *m) { if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what) - d = m->parameters_proc_self_mountinfo.what; - else if (m->from_fragment && m->parameters_fragment.what) - d = m->parameters_fragment.what; - - return sd_bus_message_append(reply, "s", d); + return m->parameters_proc_self_mountinfo.what; + if (m->from_fragment && m->parameters_fragment.what) + return m->parameters_fragment.what; + return NULL; } -static int property_get_options( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Mount *m = userdata; - const char *d = NULL; - - assert(bus); - assert(reply); - assert(m); - +static const char *mount_get_options(const Mount *m) { if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options) - d = m->parameters_proc_self_mountinfo.options; - else if (m->from_fragment && m->parameters_fragment.options) - d = m->parameters_fragment.options; - - return sd_bus_message_append(reply, "s", d); + return m->parameters_proc_self_mountinfo.options; + if (m->from_fragment && m->parameters_fragment.options) + return m->parameters_fragment.options; + return NULL; } -static int property_get_type( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - const char *fstype = NULL; - Mount *m = userdata; - - assert(bus); - assert(reply); - assert(m); - +static const char *mount_get_fstype(const Mount *m) { if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) - fstype = m->parameters_proc_self_mountinfo.fstype; + return m->parameters_proc_self_mountinfo.fstype; else if (m->from_fragment && m->parameters_fragment.fstype) - fstype = m->parameters_fragment.fstype; - - return sd_bus_message_append(reply, "s", fstype); + return m->parameters_fragment.fstype; + return NULL; } +static BUS_DEFINE_PROPERTY_GET(property_get_what, "s", Mount, mount_get_what); +static BUS_DEFINE_PROPERTY_GET(property_get_options, "s", Mount, mount_get_options); +static BUS_DEFINE_PROPERTY_GET(property_get_type, "s", Mount, mount_get_fstype); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResult); const sd_bus_vtable bus_mount_vtable[] = { @@ -115,8 +52,8 @@ const sd_bus_vtable bus_mount_vtable[] = { SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h index 5d5e1f679f..f7112a9f02 100644 --- a/src/core/dbus-mount.h +++ b/src/core/dbus-mount.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index b3f502f4c9..1a97d62486 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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-util.h" @@ -60,29 +42,9 @@ static int property_get_paths( return sd_bus_message_close_container(reply); } -static int property_get_unit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *p = userdata, *trigger; - - assert(bus); - assert(reply); - assert(p); - - trigger = UNIT_TRIGGER(p); - - return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); -} - const sd_bus_vtable bus_path_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Paths", "a(ss)", property_get_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MakeDirectory", "b", bus_property_get_bool, offsetof(Path, make_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Path, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), @@ -141,13 +103,14 @@ static int bus_path_set_transient_property( if (!k) return -ENOMEM; + path_simplify(k, false); + s = new0(PathSpec, 1); if (!s) return -ENOMEM; s->unit = u; - s->path = path_kill_slashes(k); - k = NULL; + s->path = TAKE_PTR(k); s->type = t; s->inotify_fd = -1; diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h index ccd88c7f86..ad42b23662 100644 --- a/src/core/dbus-path.h +++ b/src/core/dbus-path.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index 4d9ced81de..6725f62794 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 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-common-errors.h" diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h index aa604897eb..7c080dbcf7 100644 --- a/src/core/dbus-scope.h +++ b/src/core/dbus-scope.h @@ -1,27 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" +#include "scope.h" #include "unit.h" extern const sd_bus_vtable bus_scope_vtable[]; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 6de905b69c..1b4c98c7d2 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <stdio_ext.h> @@ -136,12 +118,12 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0), SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -354,7 +336,8 @@ static int bus_service_set_transient_property( if (streq(name, "SuccessExitStatus")) return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error); - if ((ci = service_exec_command_from_string(name)) >= 0) + ci = service_exec_command_from_string(name); + if (ci >= 0) return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error); if (streq(name, "StandardInputFileDescriptor")) diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index 2da29ec601..22d2b887b4 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c index fa2ff72151..722a5688a5 100644 --- a/src/core/dbus-slice.c +++ b/src/core/dbus-slice.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 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 "dbus-cgroup.h" #include "dbus-slice.h" diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h index 0c21919ad1..88cc48c808 100644 --- a/src/core/dbus-slice.h +++ b/src/core/dbus-slice.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 035651f213..913cc74918 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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-util.h" @@ -36,6 +18,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, socket_result, SocketResult); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); +static BUS_DEFINE_PROPERTY_GET(property_get_fdname, "s", Socket, socket_fdname); static int property_get_listen( sd_bus *bus, @@ -46,7 +29,6 @@ static int property_get_listen( void *userdata, sd_bus_error *error) { - Socket *s = SOCKET(userdata); SocketPort *p; int r; @@ -92,25 +74,6 @@ static int property_get_listen( return sd_bus_message_close_container(reply); } - -static int property_get_fdname( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Socket *s = SOCKET(userdata); - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "s", socket_fdname(s)); -} - const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST), @@ -157,12 +120,13 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0), SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), + SD_BUS_PROPERTY("NRefused", "u", bus_property_get_unsigned, offsetof(Socket, n_refused), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), @@ -174,7 +138,10 @@ static inline bool check_size_t_truncation(uint64_t t) { return (size_t) t == t; } -static inline const char* socket_protocol_to_name_supported(int32_t i) { +static inline const char* supported_socket_protocol_to_string(int32_t i) { + if (i == IPPROTO_IP) + return ""; + if (!IN_SET(i, IPPROTO_UDPLITE, IPPROTO_SCTP)) return NULL; @@ -184,11 +151,11 @@ static inline const char* socket_protocol_to_name_supported(int32_t i) { static BUS_DEFINE_SET_TRANSIENT(int, "i", int32_t, int, "%" PRIi32); static BUS_DEFINE_SET_TRANSIENT(message_queue, "x", int64_t, long, "%" PRIi64); static BUS_DEFINE_SET_TRANSIENT_IS_VALID(size_t_check_truncation, "t", uint64_t, size_t, "%" PRIu64, check_size_t_truncation); -static BUS_DEFINE_SET_TRANSIENT_PARSE(bind_ipv6_only, SocketAddressBindIPv6Only, parse_socket_address_bind_ipv6_only_or_bool); +static BUS_DEFINE_SET_TRANSIENT_PARSE(bind_ipv6_only, SocketAddressBindIPv6Only, socket_address_bind_ipv6_only_or_bool_from_string); static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(fdname, fdname_is_valid); static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(ifname, ifname_valid); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(ip_tos, "i", int32_t, int, "%" PRIi32, ip_tos_to_string_alloc); -static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, socket_protocol_to_name_supported); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, supported_socket_protocol_to_string); static int bus_socket_set_transient_property( Socket *s, @@ -394,7 +361,7 @@ static int bus_socket_set_transient_property( if (p->type != SOCKET_SOCKET) { p->path = strdup(a); - path_kill_slashes(p->path); + path_simplify(p->path, false); } else if (streq(t, "Netlink")) { r = socket_address_parse_netlink(&p->address, a); diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h index e6db2d0772..9aa8133d18 100644 --- a/src/core/dbus-socket.h +++ b/src/core/dbus-socket.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 795aaa94fe..b272d10113 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -1,22 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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/>. + Copyright © 2010 Maarten Lankhorst ***/ #include "bus-util.h" @@ -27,54 +11,22 @@ #include "swap.h" #include "unit.h" -static int property_get_priority( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Swap *s = SWAP(userdata); - int p; - - assert(bus); - assert(reply); - assert(s); - +static int swap_get_priority(Swap *s) { if (s->from_proc_swaps) - p = s->parameters_proc_swaps.priority; - else if (s->from_fragment) - p = s->parameters_fragment.priority; - else - p = -1; - - return sd_bus_message_append(reply, "i", p); + return s->parameters_proc_swaps.priority; + if (s->from_fragment) + return s->parameters_fragment.priority; + return -1; } -static int property_get_options( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Swap *s = SWAP(userdata); - const char *options = NULL; - - assert(bus); - assert(reply); - assert(s); - +static const char *swap_get_options(Swap *s) { if (s->from_fragment) - options = s->parameters_fragment.options; - - return sd_bus_message_append(reply, "s", options); + return s->parameters_fragment.options; + return NULL; } +static BUS_DEFINE_PROPERTY_GET(property_get_priority, "i", Swap, swap_get_priority); +static BUS_DEFINE_PROPERTY_GET(property_get_options, "s", Swap, swap_get_options); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, swap_result, SwapResult); const sd_bus_vtable bus_swap_vtable[] = { @@ -85,8 +37,8 @@ const sd_bus_vtable bus_swap_vtable[] = { SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_VTABLE_END diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h index 6cca7483d8..b114fe04c7 100644 --- a/src/core/dbus-swap.h +++ b/src/core/dbus-swap.h @@ -2,26 +2,11 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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/>. + Copyright © 2010 Maarten Lankhorst ***/ #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c index 7060b65c6e..ba50113641 100644 --- a/src/core/dbus-target.c +++ b/src/core/dbus-target.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "dbus-target.h" #include "unit.h" diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h index 7852917962..ad02a1db74 100644 --- a/src/core/dbus-target.h +++ b/src/core/dbus-target.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-bus.h" +#include "sd-bus-vtable.h" extern const sd_bus_vtable bus_target_vtable[]; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 1eedf217fe..b9d2f3d07e 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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-util.h" @@ -116,26 +98,6 @@ static int property_get_calendar_timers( return sd_bus_message_close_container(reply); } -static int property_get_unit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata, *trigger; - - assert(bus); - assert(reply); - assert(u); - - trigger = UNIT_TRIGGER(u); - - return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); -} - static int property_get_next_elapse_monotonic( sd_bus *bus, const char *path, @@ -158,7 +120,7 @@ static int property_get_next_elapse_monotonic( const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -290,8 +252,7 @@ static int bus_timer_set_transient_property( return -ENOMEM; v->base = b; - v->calendar_spec = c; - c = NULL; + v->calendar_spec = TAKE_PTR(c); LIST_PREPEND(value, t->values, v); } @@ -377,8 +338,7 @@ static int bus_timer_set_transient_property( return -ENOMEM; v->base = TIMER_CALENDAR; - v->calendar_spec = c; - c = NULL; + v->calendar_spec = TAKE_PTR(c); LIST_PREPEND(value, t->values, v); } diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h index d810b048ee..bb126b22dc 100644 --- a/src/core/dbus-timer.h +++ b/src/core/dbus-timer.h @@ -1,26 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" #include "unit.h" diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 50a5ab9819..ae0410414e 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include "sd-bus.h" @@ -42,10 +24,32 @@ #include "user-util.h" #include "web-util.h" +static bool unit_can_start_refuse_manual(Unit *u) { + return unit_can_start(u) && !u->refuse_manual_start; +} + +static bool unit_can_stop_refuse_manual(Unit *u) { + return unit_can_stop(u) && !u->refuse_manual_stop; +} + +static bool unit_can_isolate_refuse_manual(Unit *u) { + return unit_can_isolate(u) && !u->refuse_manual_start; +} + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); +static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description); +static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string); +static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string); +static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string); +static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload); +static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual); +static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual); +static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual); +static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0); static int property_get_names( sd_bus *bus, @@ -56,20 +60,20 @@ static int property_get_names( void *userdata, sd_bus_error *error) { - Unit *u = userdata; + Set **s = userdata; Iterator i; const char *t; int r; assert(bus); assert(reply); - assert(u); + assert(s); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) return r; - SET_FOREACH(t, u->names, i) { + SET_FOREACH(t, *s, i) { r = sd_bus_message_append(reply, "s", t); if (r < 0) return r; @@ -94,7 +98,7 @@ static int property_get_following( assert(u); f = unit_following(u); - return sd_bus_message_append(reply, "s", f ? f->id : ""); + return sd_bus_message_append(reply, "s", f ? f->id : NULL); } static int property_get_dependencies( @@ -106,7 +110,7 @@ static int property_get_dependencies( void *userdata, sd_bus_error *error) { - Hashmap *h = *(Hashmap**) userdata; + Hashmap **h = userdata; Iterator j; Unit *u; void *v; @@ -114,12 +118,13 @@ static int property_get_dependencies( assert(bus); assert(reply); + assert(h); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) return r; - HASHMAP_FOREACH_KEY(v, u, h, j) { + HASHMAP_FOREACH_KEY(v, u, *h, j) { r = sd_bus_message_append(reply, "s", u->id); if (r < 0) return r; @@ -128,22 +133,6 @@ static int property_get_dependencies( return sd_bus_message_close_container(reply); } -static int property_get_obsolete_dependencies( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - /* For dependency types we don't support anymore always return an empty array */ - return sd_bus_message_append(reply, "as", 0); -} - static int property_get_requires_mounts_for( sd_bus *bus, const char *path, @@ -153,7 +142,7 @@ static int property_get_requires_mounts_for( void *userdata, sd_bus_error *error) { - Hashmap *h = *(Hashmap**) userdata; + Hashmap **h = userdata; const char *p; Iterator j; void *v; @@ -161,12 +150,13 @@ static int property_get_requires_mounts_for( assert(bus); assert(reply); + assert(h); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) return r; - HASHMAP_FOREACH_KEY(v, p, h, j) { + HASHMAP_FOREACH_KEY(v, p, *h, j) { r = sd_bus_message_append(reply, "s", p); if (r < 0) return r; @@ -175,60 +165,6 @@ static int property_get_requires_mounts_for( return sd_bus_message_close_container(reply); } -static int property_get_description( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_description(u)); -} - -static int property_get_active_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u))); -} - -static int property_get_sub_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u)); -} - static int property_get_unit_file_preset( sd_bus *bus, const char *path, @@ -248,100 +184,10 @@ static int property_get_unit_file_preset( r = unit_get_unit_file_preset(u); return sd_bus_message_append(reply, "s", - r < 0 ? "": + r < 0 ? NULL: r > 0 ? "enabled" : "disabled"); } -static int property_get_unit_file_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u))); -} - -static int property_get_can_start( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start); -} - -static int property_get_can_stop( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_stop(u) && !u->refuse_manual_stop); -} - -static int property_get_can_reload( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_reload(u)); -} - -static int property_get_can_isolate( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start); -} - static int property_get_job( sd_bus *bus, const char *path, @@ -352,38 +198,20 @@ static int property_get_job( sd_bus_error *error) { _cleanup_free_ char *p = NULL; - Unit *u = userdata; + Job **j = userdata; assert(bus); assert(reply); - assert(u); + assert(j); - if (!u->job) + if (!*j) return sd_bus_message_append(reply, "(uo)", 0, "/"); - p = job_dbus_path(u->job); + p = job_dbus_path(*j); if (!p) return -ENOMEM; - return sd_bus_message_append(reply, "(uo)", u->job->id, p); -} - -static int property_get_need_daemon_reload( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u)); + return sd_bus_message_append(reply, "(uo)", (*j)->id, p); } static int property_get_conditions( @@ -439,15 +267,17 @@ static int property_get_load_error( _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; Unit *u = userdata; + int r; assert(bus); assert(reply); assert(u); - if (u->load_error != 0) - sd_bus_error_set_errno(&e, u->load_error); + r = bus_unit_validate_load_state(u, &e); + if (r < 0) + return sd_bus_message_append(reply, "(ss)", e.name, e.message); - return sd_bus_message_append(reply, "(ss)", e.name, e.message); + return sd_bus_message_append(reply, "(ss)", NULL, NULL); } static int bus_verify_manage_units_async_full( @@ -735,7 +565,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Names", "as", property_get_names, offsetof(Unit, names), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0), SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -777,7 +607,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST), @@ -821,11 +651,12 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED), - /* Obsolete properties or obsolete alias names */ - SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + /* For dependency types we don't support anymore always return an empty array */ + SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), + /* Obsolete alias names */ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_VTABLE_END @@ -931,7 +762,7 @@ static int property_get_cgroup( sd_bus_error *error) { Unit *u = userdata; - const char *t; + const char *t = NULL; assert(bus); assert(reply); @@ -944,9 +775,7 @@ static int property_get_cgroup( * other cases we report as-is. */ if (u->cgroup_path) - t = isempty(u->cgroup_path) ? "/" : u->cgroup_path; - else - t = ""; + t = empty_to_root(u->cgroup_path); return sd_bus_message_append(reply, "s", t); } @@ -992,7 +821,7 @@ static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { assert(p); r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); - if (r == ENOENT) + if (r == -ENOENT) return 0; if (r < 0) return r; @@ -1043,7 +872,7 @@ static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(set_freep) Set *pids = NULL; + _cleanup_set_free_ Set *pids = NULL; Unit *u = userdata; pid_t pid; int r; @@ -1130,7 +959,7 @@ static int property_get_ip_counter( int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(set_freep) Set *pids = NULL; + _cleanup_set_free_ Set *pids = NULL; Unit *u = userdata; const char *path; int r; @@ -1408,7 +1237,7 @@ int bus_unit_queue_job( } if (type == JOB_STOP && - (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR)) && + IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR, UNIT_BAD_SETTING) && unit_active_state(u) == UNIT_INACTIVE) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); @@ -1880,22 +1709,33 @@ int bus_unit_set_properties( return n; } -int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { +int bus_unit_validate_load_state(Unit *u, sd_bus_error *error) { assert(u); - if (u->load_state == UNIT_LOADED) - return 0; + /* Generates a pretty error if a unit isn't properly loaded. */ - /* Give a better description of the unit error when - * possible. Note that in the case of UNIT_MASKED, load_error - * is not set. */ - if (u->load_state == UNIT_MASKED) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit %s is masked.", u->id); + switch (u->load_state) { + + case UNIT_LOADED: + return 0; - if (u->load_state == UNIT_NOT_FOUND) + case UNIT_NOT_FOUND: return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not found.", u->id); - return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id); + case UNIT_BAD_SETTING: + return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Unit %s has a bad unit file setting.", u->id); + + case UNIT_ERROR: /* Only show .load_error in UNIT_ERROR state */ + return sd_bus_error_set_errnof(error, u->load_error, "Unit %s failed to loaded properly: %m.", u->id); + + case UNIT_MASKED: + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit %s is masked.", u->id); + + case UNIT_STUB: + case UNIT_MERGED: + default: + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unexpected load state of unit %s", u->id); + } } static int bus_unit_track_handler(sd_bus_track *t, void *userdata) { diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index bb4e43f5ca..68eb621836 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -1,27 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" +#include "sd-bus-vtable.h" +#include "job.h" #include "unit.h" extern const sd_bus_vtable bus_unit_vtable[]; @@ -42,7 +25,7 @@ int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *e int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); -int bus_unit_check_load_state(Unit *u, sd_bus_error *error); +int bus_unit_validate_load_state(Unit *u, sd_bus_error *error); int bus_unit_track_add_name(Unit *u, const char *name); int bus_unit_track_add_sender(Unit *u, sd_bus_message *m); diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c index 75bbd07604..f4fbb72cb9 100644 --- a/src/core/dbus-util.c +++ b/src/core/dbus-util.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "bus-util.h" #include "dbus-util.h" @@ -26,6 +8,26 @@ #include "user-util.h" #include "unit.h" +int bus_property_get_triggered_unit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata, *trigger; + + assert(bus); + assert(reply); + assert(u); + + trigger = UNIT_TRIGGER(u); + + return sd_bus_message_append(reply, "s", trigger ? trigger->id : NULL); +} + BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id); diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h index 8260298577..12b055e4ac 100644 --- a/src/core/dbus-util.h +++ b/src/core/dbus-util.h @@ -1,28 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - #include "sd-bus.h" + #include "unit.h" +int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + #define BUS_DEFINE_SET_TRANSIENT(function, bus_type, type, cast_type, fmt) \ int bus_set_transient_##function( \ Unit *u, \ @@ -48,8 +32,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_IS_VALID(function, bus_type, type, cast_type, fmt, check) \ int bus_set_transient_##function( \ @@ -80,8 +63,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_TO_STRING(function, bus_type, type, cast_type, fmt, to_string) \ int bus_set_transient_##function( \ @@ -114,8 +96,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(function, bus_type, type, cast_type, fmt, to_string) \ int bus_set_transient_##function( \ @@ -150,8 +131,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_PARSE(function, type, parse) \ int bus_set_transient_##function( \ @@ -184,8 +164,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(function, type, parse) \ int bus_set_transient_##function( \ @@ -218,8 +197,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #define BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(function, check) \ int bus_set_transient_##function( \ @@ -253,88 +231,7 @@ } \ \ return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define BUS_DEFINE_SET_CGROUP_WEIGHT(function, mask, check, val, str) \ - int bus_cgroup_set_##function( \ - Unit *u, \ - const char *name, \ - uint64_t *p, \ - sd_bus_message *message, \ - UnitWriteFlags flags, \ - sd_bus_error *error) { \ - \ - uint64_t v; \ - int r; \ - \ - assert(p); \ - \ - r = sd_bus_message_read(message, "t", &v); \ - if (r < 0) \ - return r; \ - \ - if (!check(v)) \ - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ - "Value specified in %s is out of range", name); \ - \ - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ - *p = v; \ - unit_invalidate_cgroup(u, (mask)); \ - \ - if (v == (val)) \ - unit_write_settingf(u, flags, name, \ - "%s=" str, name); \ - else \ - unit_write_settingf(u, flags, name, \ - "%s=%" PRIu64, name, v); \ - } \ - \ - return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define BUS_DEFINE_SET_CGROUP_SCALE(function, mask, scale) \ - int bus_cgroup_set_##function##_scale( \ - Unit *u, \ - const char *name, \ - uint64_t *p, \ - sd_bus_message *message, \ - UnitWriteFlags flags, \ - sd_bus_error *error) { \ - \ - uint64_t v; \ - uint32_t raw; \ - int r; \ - \ - assert(p); \ - \ - r = sd_bus_message_read(message, "u", &raw); \ - if (r < 0) \ - return r; \ - \ - v = scale(raw, UINT32_MAX); \ - if (v <= 0 || v >= UINT64_MAX) \ - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ - "Value specified in %s is out of range", name); \ - \ - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ - const char *e; \ - \ - *p = v; \ - unit_invalidate_cgroup(u, (mask)); \ - \ - /* Chop off suffix */ \ - assert_se(e = endswith(name, "Scale")); \ - name = strndupa(name, e - name); \ - \ - unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, \ - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); \ - } \ - \ - return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); diff --git a/src/core/dbus.c b/src/core/dbus.c index 56b43adcda..bf5917696e 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sys/epoll.h> @@ -29,11 +11,22 @@ #include "bus-error.h" #include "bus-internal.h" #include "bus-util.h" +#include "dbus-automount.h" #include "dbus-cgroup.h" +#include "dbus-device.h" #include "dbus-execute.h" #include "dbus-job.h" #include "dbus-kill.h" #include "dbus-manager.h" +#include "dbus-mount.h" +#include "dbus-path.h" +#include "dbus-scope.h" +#include "dbus-service.h" +#include "dbus-slice.h" +#include "dbus-socket.h" +#include "dbus-swap.h" +#include "dbus-target.h" +#include "dbus-timer.h" #include "dbus-unit.h" #include "dbus.h" #include "fd-util.h" @@ -43,6 +36,7 @@ #include "mkdir.h" #include "process-util.h" #include "selinux-access.h" +#include "service.h" #include "special.h" #include "string-util.h" #include "strv.h" @@ -242,7 +236,6 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er path = sd_bus_message_get_path(message); if (object_path_startswith("/org/freedesktop/systemd1", path)) { - r = mac_selinux_access_check(message, verb, error); if (r < 0) return r; @@ -270,7 +263,6 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er else manager_load_unit_from_dbus_path(m, path, NULL, &u); } - if (!u) return 0; @@ -501,8 +493,7 @@ static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char assert(hashmap_size(m->jobs) == k); - *nodes = l; - l = NULL; + *nodes = TAKE_PTR(l); return k; } @@ -526,8 +517,7 @@ static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, cha k++; } - *nodes = l; - l = NULL; + *nodes = TAKE_PTR(l); return k; } @@ -897,9 +887,9 @@ int bus_init_api(Manager *m) { bus = sd_bus_ref(m->system_bus); else { if (MANAGER_IS_SYSTEM(m)) - r = sd_bus_open_system(&bus); + r = sd_bus_open_system_with_description(&bus, "bus-api-system"); else - r = sd_bus_open_user(&bus); + r = sd_bus_open_user_with_description(&bus, "bus-api-user"); if (r < 0) return log_error_errno(r, "Failed to connect to API bus: %m"); @@ -916,8 +906,7 @@ int bus_init_api(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to set up API bus: %m"); - m->api_bus = bus; - bus = NULL; + m->api_bus = TAKE_PTR(bus); r = manager_enqueue_sync_bus_names(m); if (r < 0) @@ -961,7 +950,7 @@ int bus_init_system(Manager *m) { if (MANAGER_IS_SYSTEM(m) && m->api_bus) bus = sd_bus_ref(m->api_bus); else { - r = sd_bus_open_system(&bus); + r = sd_bus_open_system_with_description(&bus, "bus-system"); if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); @@ -978,8 +967,7 @@ int bus_init_system(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to set up system bus: %m"); - m->system_bus = bus; - bus = NULL; + m->system_bus = TAKE_PTR(bus); return 0; } @@ -1105,17 +1093,11 @@ static void destroy_bus(Manager *m, sd_bus **bus) { } void bus_done_api(Manager *m) { - assert(m); - - if (m->api_bus) - destroy_bus(m, &m->api_bus); + destroy_bus(m, &m->api_bus); } void bus_done_system(Manager *m) { - assert(m); - - if (m->system_bus) - destroy_bus(m, &m->system_bus); + destroy_bus(m, &m->system_bus); } void bus_done_private(Manager *m) { @@ -1310,3 +1292,38 @@ uint64_t manager_bus_n_queued_write(Manager *m) { return c; } + +static void vtable_dump_bus_properties(FILE *f, const sd_bus_vtable *table) { + const sd_bus_vtable *i; + + for (i = table; i->type != _SD_BUS_VTABLE_END; i++) { + if (!IN_SET(i->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY) || + (i->flags & (SD_BUS_VTABLE_DEPRECATED | SD_BUS_VTABLE_HIDDEN)) != 0) + continue; + + fprintf(f, "%s\n", i->x.property.member); + } +} + +void dump_bus_properties(FILE *f) { + assert(f); + + vtable_dump_bus_properties(f, bus_automount_vtable); + vtable_dump_bus_properties(f, bus_cgroup_vtable); + vtable_dump_bus_properties(f, bus_device_vtable); + vtable_dump_bus_properties(f, bus_exec_vtable); + vtable_dump_bus_properties(f, bus_job_vtable); + vtable_dump_bus_properties(f, bus_kill_vtable); + vtable_dump_bus_properties(f, bus_manager_vtable); + vtable_dump_bus_properties(f, bus_mount_vtable); + vtable_dump_bus_properties(f, bus_path_vtable); + vtable_dump_bus_properties(f, bus_scope_vtable); + vtable_dump_bus_properties(f, bus_service_vtable); + vtable_dump_bus_properties(f, bus_slice_vtable); + vtable_dump_bus_properties(f, bus_socket_vtable); + vtable_dump_bus_properties(f, bus_swap_vtable); + vtable_dump_bus_properties(f, bus_target_vtable); + vtable_dump_bus_properties(f, bus_timer_vtable); + vtable_dump_bus_properties(f, bus_unit_vtable); + vtable_dump_bus_properties(f, bus_unit_cgroup_vtable); +} diff --git a/src/core/dbus.h b/src/core/dbus.h index 702bc48ae1..382a96da7d 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -1,24 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ +#include "sd-bus.h" #include "manager.h" @@ -50,3 +33,5 @@ int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_er int bus_forward_agent_released(Manager *m, const char *path); uint64_t manager_bus_n_queued_write(Manager *m); + +void dump_bus_properties(FILE *f); diff --git a/src/core/device.c b/src/core/device.c index b0dd469fd1..a2d00a0fbe 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sys/epoll.h> @@ -24,6 +6,7 @@ #include "libudev.h" #include "alloc-util.h" +#include "bus-error.h" #include "dbus-device.h" #include "device.h" #include "log.h" @@ -43,6 +26,7 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { }; static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask); static void device_unset_sysfs(Device *d) { Hashmap *devices; @@ -68,8 +52,8 @@ static void device_unset_sysfs(Device *d) { } static int device_set_sysfs(Device *d, const char *sysfs) { + _cleanup_free_ char *copy = NULL; Device *first; - char *copy; int r; assert(d); @@ -93,12 +77,10 @@ static int device_set_sysfs(Device *d, const char *sysfs) { r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first); if (r < 0) { LIST_REMOVE(same_sysfs, first, d); - free(copy); return r; } - d->sysfs = copy; - + d->sysfs = TAKE_PTR(copy); return 0; } @@ -116,6 +98,8 @@ static void device_init(Unit *u) { u->job_running_timeout = u->manager->default_timeout_start_usec; u->ignore_on_isolate = true; + + d->deserialized_state = _DEVICE_STATE_INVALID; } static void device_done(Unit *u) { @@ -124,6 +108,7 @@ static void device_done(Unit *u) { assert(d); device_unset_sysfs(d); + d->wants_property = strv_free(d->wants_property); } static void device_set_state(Device *d, DeviceState state) { @@ -133,10 +118,13 @@ static void device_set_state(Device *d, DeviceState state) { old_state = d->state; d->state = state; + if (state == DEVICE_DEAD) + device_unset_sysfs(d); + if (state != old_state) log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state)); - unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0); } static int device_coldplug(Unit *u) { @@ -145,19 +133,93 @@ static int device_coldplug(Unit *u) { assert(d); assert(d->state == DEVICE_DEAD); - if (d->found & DEVICE_FOUND_UDEV) - /* If udev says the device is around, it's around */ - device_set_state(d, DEVICE_PLUGGED); - else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED) - /* If a device is found in /proc/self/mountinfo or - * /proc/swaps, and was not yet announced via udev, - * it's "tentatively" around. */ - device_set_state(d, DEVICE_TENTATIVE); + /* First, let's put the deserialized state and found mask into effect, if we have it. */ + + if (d->deserialized_state < 0 || + (d->deserialized_state == d->state && + d->deserialized_found == d->found)) + return 0; + + d->found = d->deserialized_found; + device_set_state(d, d->deserialized_state); + return 0; +} + +static void device_catchup(Unit *u) { + Device *d = DEVICE(u); + + assert(d); + + /* Second, let's update the state with the enumerated state if it's different */ + if (d->enumerated_found == d->found) + return; + + device_update_found_one(d, d->enumerated_found, DEVICE_FOUND_MASK); +} + +static const struct { + DeviceFound flag; + const char *name; +} device_found_map[] = { + { DEVICE_FOUND_UDEV, "found-udev" }, + { DEVICE_FOUND_MOUNT, "found-mount" }, + { DEVICE_FOUND_SWAP, "found-swap" }, +}; + +static int device_found_to_string_many(DeviceFound flags, char **ret) { + _cleanup_free_ char *s = NULL; + unsigned i; + + assert(ret); + + for (i = 0; i < ELEMENTSOF(device_found_map); i++) { + if (!FLAGS_SET(flags, device_found_map[i].flag)) + continue; + + if (!strextend_with_separator(&s, ",", device_found_map[i].name, NULL)) + return -ENOMEM; + } + + *ret = TAKE_PTR(s); return 0; } +static int device_found_from_string_many(const char *name, DeviceFound *ret) { + DeviceFound flags = 0; + int r; + + assert(ret); + + for (;;) { + _cleanup_free_ char *word = NULL; + DeviceFound f = 0; + unsigned i; + + r = extract_first_word(&name, &word, ",", 0); + if (r < 0) + return r; + if (r == 0) + break; + + for (i = 0; i < ELEMENTSOF(device_found_map); i++) + if (streq(word, device_found_map[i].name)) { + f = device_found_map[i].flag; + break; + } + + if (f == 0) + return -EINVAL; + + flags |= f; + } + + *ret = flags; + return 0; +} + static int device_serialize(Unit *u, FILE *f, FDSet *fds) { + _cleanup_free_ char *s = NULL; Device *d = DEVICE(u); assert(u); @@ -166,11 +228,15 @@ static int device_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "state", device_state_to_string(d->state)); + if (device_found_to_string_many(d->found, &s) >= 0) + unit_serialize_item(u, f, "found", s); + return 0; } static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Device *d = DEVICE(u); + int r; assert(u); assert(key); @@ -182,9 +248,15 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value, state = device_state_from_string(value); if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); + log_unit_debug(u, "Failed to parse state value, ignoring: %s", value); else d->deserialized_state = state; + + } else if (streq(key, "found")) { + r = device_found_from_string_many(value, &d->deserialized_found); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to parse found value, ignoring: %s", value); + } else log_unit_debug(u, "Unknown serialization key: %s", key); @@ -193,14 +265,27 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value, static void device_dump(Unit *u, FILE *f, const char *prefix) { Device *d = DEVICE(u); + _cleanup_free_ char *s = NULL; assert(d); + (void) device_found_to_string_many(d->found, &s); + fprintf(f, "%sDevice State: %s\n" - "%sSysfs Path: %s\n", + "%sSysfs Path: %s\n" + "%sFound: %s\n", prefix, device_state_to_string(d->state), - prefix, strna(d->sysfs)); + prefix, strna(d->sysfs), + prefix, strna(s)); + + if (!strv_isempty(d->wants_property)) { + char **i; + + STRV_FOREACH(i, d->wants_property) + fprintf(f, "%sudev SYSTEMD_WANTS: %s\n", + prefix, *i); + } } _pure_ static UnitActiveState device_active_state(Unit *u) { @@ -241,26 +326,27 @@ static int device_update_description(Unit *u, struct udev_device *dev, const cha _cleanup_free_ char *j; j = strjoin(model, " ", label); - if (j) - r = unit_set_description(u, j); - else - r = -ENOMEM; + if (!j) + return log_oom(); + + r = unit_set_description(u, j); } else r = unit_set_description(u, model); } else r = unit_set_description(u, path); - if (r < 0) - log_unit_error_errno(u, r, "Failed to set device description: %m"); + return log_unit_error_errno(u, r, "Failed to set device description: %m"); - return r; + return 0; } static int device_add_udev_wants(Unit *u, struct udev_device *dev) { + _cleanup_strv_free_ char **added = NULL; const char *wants, *property; + Device *d = DEVICE(u); int r; - assert(u); + assert(d); assert(dev); property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; @@ -274,21 +360,21 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { r = extract_first_word(&wants, &word, NULL, EXTRACT_QUOTES); if (r == 0) - return 0; + break; if (r == -ENOMEM) return log_oom(); if (r < 0) return log_unit_error_errno(u, r, "Failed to parse property %s with value %s: %m", property, wants); - if (unit_name_is_valid(word, UNIT_NAME_TEMPLATE) && DEVICE(u)->sysfs) { + if (unit_name_is_valid(word, UNIT_NAME_TEMPLATE) && d->sysfs) { _cleanup_free_ char *escaped = NULL; /* If the unit name is specified as template, then automatically fill in the sysfs path of the * device as instance name, properly escaped. */ - r = unit_name_path_escape(DEVICE(u)->sysfs, &escaped); + r = unit_name_path_escape(d->sysfs, &escaped); if (r < 0) - return log_unit_error_errno(u, r, "Failed to escape %s: %m", DEVICE(u)->sysfs); + return log_unit_error_errno(u, r, "Failed to escape %s: %m", d->sysfs); r = unit_name_replace_instance(word, escaped, &k); if (r < 0) @@ -296,7 +382,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { } else { /* If this is not a template, then let's mangle it so, that it becomes a valid unit name. */ - r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k); + r = unit_name_mangle(word, UNIT_NAME_MANGLE_WARN, &k); if (r < 0) return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word); } @@ -304,7 +390,43 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true, UNIT_DEPENDENCY_UDEV); if (r < 0) return log_unit_error_errno(u, r, "Failed to add Wants= dependency: %m"); + + r = strv_push(&added, k); + if (r < 0) + return log_oom(); + + k = NULL; } + + if (d->state != DEVICE_DEAD) { + char **i; + + /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not + * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device + * changes while the device unit is already up, let's manually trigger any new units listed in it not + * seen before. This typically appens during the boot-time switch root transition, as udev devices + * will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev + * rules only available on the host system, and thus only when the initial udev coldplug trigger runs. + * + * We do this only if the device has been up already when we parse this, as otherwise the usual + * dependency logic that is run from the dead → plugged transition will trigger these deps. */ + + STRV_FOREACH(i, added) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */ + continue; + + r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, &error, NULL); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r)); + } + } + + strv_free(d->wants_property); + d->wants_property = TAKE_PTR(added); + + return 0; } static bool device_is_bound_by_mounts(Device *d, struct udev_device *dev) { @@ -327,7 +449,7 @@ static bool device_is_bound_by_mounts(Device *d, struct udev_device *dev) { return d->bind_mounts; } -static int device_upgrade_mount_deps(Unit *u) { +static void device_upgrade_mount_deps(Unit *u) { Unit *other; Iterator i; void *v; @@ -341,9 +463,8 @@ static int device_upgrade_mount_deps(Unit *u) { r = unit_add_dependency(other, UNIT_BINDS_TO, u, true, UNIT_DEPENDENCY_UDEV); if (r < 0) - return r; + log_unit_warning_errno(u, r, "Failed to add BindsTo= dependency between device and mount unit, ignoring: %m"); } - return 0; } static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) { @@ -358,8 +479,10 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa if (dev) { sysfs = udev_device_get_syspath(dev); - if (!sysfs) + if (!sysfs) { + log_debug("Couldn't get syspath from udev device, ignoring."); return 0; + } } r = unit_name_from_path(path, ".device", &e); @@ -368,17 +491,21 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa u = manager_get_unit(m, e); if (u) { - /* The device unit can still be present even if the device was unplugged: a mount unit can reference it hence - * preventing the GC to have garbaged it. That's desired since the device unit may have a dependency on the - * mount unit which was added during the loading of the later. */ - if (dev && DEVICE(u)->state == DEVICE_PLUGGED) { - - /* This unit is in plugged state: we're sure it's attached to a device. */ - if (!path_equal(DEVICE(u)->sysfs, sysfs)) { - log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", - e, DEVICE(u)->sysfs, sysfs); - return -EEXIST; - } + /* The device unit can still be present even if the device was unplugged: a mount unit can reference it + * hence preventing the GC to have garbaged it. That's desired since the device unit may have a + * dependency on the mount unit which was added during the loading of the later. When the device is + * plugged the sysfs might not be initialized yet, as we serialize the device's state but do not + * serialize the sysfs path across reloads/reexecs. Hence, when coming back from a reload/restart we + * might have the state valid, but not the sysfs path. Hence, let's filter out conflicting devices, but + * let's accept devices in any state with no sysfs path set. */ + + if (DEVICE(u)->state == DEVICE_PLUGGED && + DEVICE(u)->sysfs && + sysfs && + !path_equal(DEVICE(u)->sysfs, sysfs)) { + log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s, ignoring the latter.", + e, DEVICE(u)->sysfs, sysfs); + return -EEXIST; } delete = false; @@ -390,24 +517,26 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa delete = true; r = unit_new_for_name(m, sizeof(Device), e, &u); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to allocate device unit %s: %m", e); goto fail; + } unit_add_to_load_queue(u); } - /* If this was created via some dependency and has not - * actually been seen yet ->sysfs will not be + /* If this was created via some dependency and has not actually been seen yet ->sysfs will not be * initialized. Hence initialize it if necessary. */ if (sysfs) { r = device_set_sysfs(DEVICE(u), sysfs); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to set sysfs path %s for device unit %s: %m", sysfs, e); goto fail; + } (void) device_update_description(u, dev, path); - /* The additional systemd udev properties we only interpret - * for the main object */ + /* The additional systemd udev properties we only interpret for the main object */ if (main) (void) device_add_udev_wants(u, dev); } @@ -419,13 +548,11 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa device_upgrade_mount_deps(u); /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */ - unit_add_to_dbus_queue(u); + return 0; fail: - log_unit_warning_errno(u, r, "Failed to set up device unit: %m"); - if (delete) unit_free(u); @@ -462,8 +589,7 @@ static int device_process_new(Manager *m, struct udev_device *dev) { /* Don't bother with the /dev/block links */ p = udev_list_entry_get_name(item); - if (path_startswith(p, "/dev/block/") || - path_startswith(p, "/dev/char/")) + if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/")) continue; /* Verify that the symlink in the FS actually belongs @@ -489,76 +615,81 @@ static int device_process_new(Manager *m, struct udev_device *dev) { r = extract_first_word(&alias, &word, NULL, EXTRACT_QUOTES); if (r == 0) - return 0; + break; if (r == -ENOMEM) return log_oom(); if (r < 0) return log_warning_errno(r, "Failed to add parse SYSTEMD_ALIAS for %s: %m", sysfs); - if (path_is_absolute(word)) - (void) device_setup_unit(m, dev, word, false); - else + if (!path_is_absolute(word)) log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, word); + else if (!path_is_normalized(word)) + log_warning("SYSTEMD_ALIAS for %s is not a normalized path, ignoring: %s", sysfs, word); + else + (void) device_setup_unit(m, dev, word, false); } -} -static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) { - DeviceFound n, previous; + return 0; +} +static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) { assert(d); - n = add ? (d->found | found) : (d->found & ~found); - if (n == d->found) - return; - - previous = d->found; - d->found = n; - - if (!now) - return; - /* Didn't exist before, but does now? if so, generate a new invocation ID for it */ - if (previous == DEVICE_NOT_FOUND && d->found != DEVICE_NOT_FOUND) + if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND) (void) unit_acquire_invocation_id(UNIT(d)); - if (d->found & DEVICE_FOUND_UDEV) - /* When the device is known to udev we consider it - * plugged. */ + if (FLAGS_SET(now, DEVICE_FOUND_UDEV)) + /* When the device is known to udev we consider it plugged. */ device_set_state(d, DEVICE_PLUGGED); - else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0) - /* If the device has not been seen by udev yet, but is - * now referenced by the kernel, then we assume the + else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV)) + /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the * kernel knows it now, and udev might soon too. */ device_set_state(d, DEVICE_TENTATIVE); - else { - /* If nobody sees the device, or if the device was - * previously seen by udev and now is only referenced - * from the kernel, then we consider the device is - * gone, the kernel just hasn't noticed it yet. */ - + else + /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced + * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */ device_set_state(d, DEVICE_DEAD); - device_unset_sysfs(d); - } +} + +static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) { + assert(d); + + if (MANAGER_IS_RUNNING(UNIT(d)->manager)) { + DeviceFound n, previous; + + /* When we are already running, then apply the new mask right-away, and trigger state changes + * right-away */ + + n = (d->found & ~mask) | (found & mask); + if (n == d->found) + return; + + previous = d->found; + d->found = n; + device_found_changed(d, previous, n); + } else + /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as + * soon as we catch-up with the state. */ + d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask); } -static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) { +static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) { Device *d, *l, *n; assert(m); assert(sysfs); - if (found == DEVICE_NOT_FOUND) - return 0; + if (mask == 0) + return; l = hashmap_get(m->devices_by_sysfs, sysfs); LIST_FOREACH_SAFE(same_sysfs, d, n, l) - device_update_found_one(d, add, found, now); - - return 0; + device_update_found_one(d, found, mask); } -static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) { +static int device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) { _cleanup_free_ char *e = NULL; Unit *u; int r; @@ -566,7 +697,7 @@ static int device_update_found_by_name(Manager *m, const char *path, bool add, D assert(m); assert(path); - if (found == DEVICE_NOT_FOUND) + if (mask == 0) return 0; r = unit_name_from_path(path, ".device", &e); @@ -577,7 +708,7 @@ static int device_update_found_by_name(Manager *m, const char *path, bool add, D if (!u) return 0; - device_update_found_one(DEVICE(u), add, found, now); + device_update_found_one(DEVICE(u), found, mask); return 0; } @@ -619,7 +750,7 @@ static Unit *device_following(Unit *u) { static int device_following_set(Unit *u, Set **_set) { Device *d = DEVICE(u), *other; - Set *set; + _cleanup_set_free_ Set *set = NULL; int r; assert(d); @@ -637,38 +768,29 @@ static int device_following_set(Unit *u, Set **_set) { LIST_FOREACH_AFTER(same_sysfs, other, d) { r = set_put(set, other); if (r < 0) - goto fail; + return r; } LIST_FOREACH_BEFORE(same_sysfs, other, d) { r = set_put(set, other); if (r < 0) - goto fail; + return r; } - *_set = set; + *_set = TAKE_PTR(set); return 1; - -fail: - set_free(set); - return r; } static void device_shutdown(Manager *m) { assert(m); m->udev_event_source = sd_event_source_unref(m->udev_event_source); - - if (m->udev_monitor) { - udev_monitor_unref(m->udev_monitor); - m->udev_monitor = NULL; - } - + m->udev_monitor = udev_monitor_unref(m->udev_monitor); m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); } static void device_enumerate(Manager *m) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; int r; @@ -677,7 +799,7 @@ static void device_enumerate(Manager *m) { if (!m->udev_monitor) { m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); if (!m->udev_monitor) { - log_oom(); + log_error_errno(errno, "Failed to allocate udev monitor: %m"); goto fail; } @@ -709,7 +831,7 @@ static void device_enumerate(Manager *m) { e = udev_enumerate_new(m->udev); if (!e) { - log_oom(); + log_error_errno(errno, "Failed to alloacte udev enumerator: %m"); goto fail; } @@ -733,14 +855,20 @@ static void device_enumerate(Manager *m) { first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL; const char *sysfs; sysfs = udev_list_entry_get_name(item); dev = udev_device_new_from_syspath(m->udev, sysfs); if (!dev) { - log_oom(); + if (errno == ENOMEM) { + log_oom(); + goto fail; + } + + /* If we can't create a device, don't bother, it probably just disappeared. */ + log_debug_errno(errno, "Failed to create udev device object for %s: %m", sysfs); continue; } @@ -748,8 +876,7 @@ static void device_enumerate(Manager *m) { continue; (void) device_process_new(m, dev); - - device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); + device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV); } return; @@ -758,8 +885,26 @@ fail: device_shutdown(m); } +static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) { + Device *d, *l, *n; + int r; + + assert(m); + assert(sysfs); + + l = hashmap_get(m->devices_by_sysfs, sysfs); + LIST_FOREACH_SAFE(same_sysfs, d, n, l) { + if (d->state == DEVICE_DEAD) + continue; + + r = manager_propagate_reload(m, UNIT(d), JOB_REPLACE, NULL); + if (r < 0) + log_warning_errno(r, "Failed to propagate reload, ignoring: %m"); + } +} + static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL; Manager *m = userdata; const char *action, *sysfs; int r; @@ -769,8 +914,8 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (revents != EPOLLIN) { static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); - if (!ratelimit_test(&limit)) - log_error_errno(errno, "Failed to get udev event: %m"); + if (ratelimit_below(&limit)) + log_warning("Failed to get udev event"); if (!(revents & EPOLLIN)) return 0; } @@ -795,20 +940,8 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, return 0; } - if (streq(action, "change")) { - Unit *u; - Device *d, *l, *n; - - l = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_FOREACH_SAFE(same_sysfs, d, n, l) { - u = &d->meta; - if (u && UNIT_VTABLE(u)->active_state(u) == UNIT_ACTIVE) { - r = manager_propagate_reload(m, u, JOB_REPLACE, NULL); - if (r < 0) - log_error_errno(r, "Failed to propagate reload: %m"); - } - } - } + if (streq(action, "change")) + device_propagate_reload_by_sysfs(m, sysfs); /* A change event can signal that a device is becoming ready, in particular if * the device is using the SYSTEMD_READY logic in udev @@ -816,12 +949,12 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (streq(action, "remove")) { r = swap_process_device_remove(m, dev); if (r < 0) - log_error_errno(r, "Failed to process swap device remove event: %m"); + log_warning_errno(r, "Failed to process swap device remove event, ignoring: %m"); /* If we get notified that a device was removed by * udev, then it's completely gone, hence unset all * found bits */ - device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true); + device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP); } else if (device_is_ready(dev)) { @@ -829,19 +962,19 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, r = swap_process_device_new(m, dev); if (r < 0) - log_error_errno(r, "Failed to process swap device new event: %m"); + log_warning_errno(r, "Failed to process swap device new event, ignoring: %m"); manager_dispatch_load_queue(m); /* The device is found now, set the udev found bit */ - device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true); + device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV); } else { /* The device is nominally around, but not ready for * us. Hence unset the udev bit, but leave the rest * around. */ - device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true); + device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_UDEV); } return 0; @@ -859,61 +992,88 @@ static bool device_supported(void) { return read_only <= 0; } -int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; +static int validate_node(Manager *m, const char *node, struct udev_device **ret) { struct stat st; + int r; assert(m); assert(node); + assert(ret); - if (!device_supported()) - return 0; + /* Validates a device node that showed up in /proc/swaps or /proc/self/mountinfo if it makes sense for us to + * track. Note that this validator is fine within missing device nodes, but not with badly set up ones! */ - /* This is called whenever we find a device referenced in - * /proc/swaps or /proc/self/mounts. Such a device might be - * mounted/enabled at a time where udev has not finished - * probing it yet, and we thus haven't learned about it - * yet. In this case we will set the device unit to - * "tentative" state. */ + if (!path_startswith(node, "/dev")) { + *ret = NULL; + return 0; /* bad! */ + } - if (add) { - if (!path_startswith(node, "/dev")) - return 0; + if (stat(node, &st) < 0) { + if (errno != ENOENT) + return log_error_errno(errno, "Failed to stat() device node file %s: %m", node); - /* We make an extra check here, if the device node - * actually exists. If it's missing, then this is an - * indication that device was unplugged but is still - * referenced in /proc/swaps or - * /proc/self/mountinfo. Note that this check doesn't - * really cover all cases where a device might be gone - * away, since drives that can have a medium inserted - * will still have a device node even when the medium - * is not there... */ + *ret = NULL; + return 1; /* good! (though missing) */ - if (stat(node, &st) >= 0) { - if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) - return 0; + } else { + _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL; + + r = udev_device_new_from_stat_rdev(m->udev, &st, &dev); + if (r == -ENOENT) { + *ret = NULL; + return 1; /* good! (though missing) */ + } else if (r == -ENOTTY) { + *ret = NULL; + return 0; /* bad! (not a device node but some other kind of file system node) */ + } else if (r < 0) + return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); + + *ret = TAKE_PTR(dev); + return 1; /* good! */ + } +} - dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev); - if (!dev && errno != ENOENT) - return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); +void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFound mask) { + int r; - } else if (errno != ENOENT) - return log_error_errno(errno, "Failed to stat device node file %s: %m", node); + assert(m); + assert(node); + + if (!device_supported()) + return; + + if (mask == 0) + return; + + /* This is called whenever we find a device referenced in /proc/swaps or /proc/self/mounts. Such a device might + * be mounted/enabled at a time where udev has not finished probing it yet, and we thus haven't learned about + * it yet. In this case we will set the device unit to "tentative" state. + * + * This takes a pair of DeviceFound flags parameters. The 'mask' parameter is a bit mask that indicates which + * bits of 'found' to copy into the per-device DeviceFound flags field. Thus, this function may be used to set + * and unset individual bits in a single call, while merging partially with previous state. */ + + if ((found & mask) != 0) { + _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL; - /* If the device is known in the kernel and newly - * appeared, then we'll create a device unit for it, - * under the name referenced in /proc/swaps or - * /proc/self/mountinfo. */ + /* If the device is known in the kernel and newly appeared, then we'll create a device unit for it, + * under the name referenced in /proc/swaps or /proc/self/mountinfo. But first, let's validate if + * everything is alright with the device node. */ + + r = validate_node(m, node, &dev); + if (r <= 0) + return; /* Don't create a device unit for this if the device node is borked. */ (void) device_setup_unit(m, dev, node, false); } /* Update the device unit's state, should it exist */ - return device_update_found_by_name(m, node, add, found, now); + (void) device_update_found_by_name(m, node, found, mask); } bool device_shall_be_bound_by(Unit *device, Unit *u) { + assert(device); + assert(u); if (u->type != UNIT_MOUNT) return false; @@ -935,6 +1095,7 @@ const UnitVTable device_vtable = { .load = unit_load_fragment_and_dropin_optional, .coldplug = device_coldplug, + .catchup = device_catchup, .serialize = device_serialize, .deserialize_item = device_deserialize_item, diff --git a/src/core/device.h b/src/core/device.h index a551e7748a..a119b33e57 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -1,51 +1,43 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "unit.h" typedef struct Device Device; +/* A mask specifying where we have seen the device currently. This is a bitmask because the device might show up + * asynchronously from each other at various places. For example, in very common case a device might already be mounted + * before udev finished probing it (think: a script setting up a loopback block device, formatting it and mounting it + * in quick succession). Hence we need to track precisely where it is already visible and where not. */ typedef enum DeviceFound { - DEVICE_NOT_FOUND = 0, - DEVICE_FOUND_UDEV = 1, - DEVICE_FOUND_MOUNT = 2, - DEVICE_FOUND_SWAP = 4, + DEVICE_NOT_FOUND = 0, + DEVICE_FOUND_UDEV = 1U << 1, /* The device has shown up in the udev database */ + DEVICE_FOUND_MOUNT = 1U << 2, /* The device has shown up in /proc/self/mountinfo */ + DEVICE_FOUND_SWAP = 1U << 3, /* The device has shown up in /proc/swaps */ + DEVICE_FOUND_MASK = DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, } DeviceFound; struct Device { Unit meta; char *sysfs; - DeviceFound found; - /* In order to be able to distinguish dependencies on - different device nodes we might end up creating multiple - devices for the same sysfs path. We chain them up here. */ + /* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple + * devices for the same sysfs path. We chain them up here. */ LIST_FIELDS(struct Device, same_sysfs); DeviceState state, deserialized_state; + DeviceFound found, deserialized_found, enumerated_found; bool bind_mounts; + + /* The SYSTEMD_WANTS udev property for this device the last time we saw it */ + char **wants_property; }; extern const UnitVTable device_vtable; -int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now); +void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFound mask); bool device_shall_be_bound_by(Unit *device, Unit *u); + +DEFINE_CAST(DEVICE, Device); diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index 3da31bf870..7c5111ddf6 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 <grp.h> #include <pwd.h> @@ -30,6 +12,7 @@ #include "io-util.h" #include "parse-util.h" #include "random-util.h" +#include "socket-util.h" #include "stdio-util.h" #include "string-util.h" #include "user-util.h" @@ -319,10 +302,7 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) { (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */ *ret_uid = candidate; - r = lock_fd; - lock_fd = -1; - - return r; + return TAKE_FD(lock_fd); next: ; @@ -354,7 +334,7 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) { /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock * on the socket taken. */ - k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (k < 0) return -errno; @@ -563,7 +543,7 @@ static int dynamic_user_realize( return 0; } -static int dynamic_user_current(DynamicUser *d, uid_t *ret) { +int dynamic_user_current(DynamicUser *d, uid_t *ret) { _cleanup_(unlockfp) int storage_socket0_lock = -1; _cleanup_close_ int lock_fd = -1; uid_t uid; @@ -770,8 +750,7 @@ int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) { if (check_uid != uid) /* lock file doesn't match our own idea */ return -ESRCH; - *ret = user; - user = NULL; + *ret = TAKE_PTR(user); return 0; } diff --git a/src/core/dynamic-user.h b/src/core/dynamic-user.h index b073c65837..791a8ba0ef 100644 --- a/src/core/dynamic-user.h +++ b/src/core/dynamic-user.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #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/>. -***/ - typedef struct DynamicUser DynamicUser; typedef struct DynamicCreds { @@ -48,6 +29,7 @@ int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds); void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds); void dynamic_user_vacuum(Manager *m, bool close_user); +int dynamic_user_current(DynamicUser *d, uid_t *ret); int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret); int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret); diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c index 3d37a986bc..76e1124cff 100644 --- a/src/core/emergency-action.c +++ b/src/core/emergency-action.c @@ -1,22 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - Copyright 2012 Michael Olbrich - - 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/>. + Copyright © 2012 Michael Olbrich ***/ #include <sys/reboot.h> diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h index 4079e8499a..61791f176f 100644 --- a/src/core/emergency-action.h +++ b/src/core/emergency-action.h @@ -2,23 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - Copyright 2012 Michael Olbrich - - 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/>. + Copyright © 2012 Michael Olbrich ***/ typedef enum EmergencyAction { diff --git a/src/core/execute.c b/src/core/execute.c index 7292b815db..8ac69d1a0f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <fcntl.h> @@ -99,6 +81,7 @@ #include "selinux-util.h" #include "signal-util.h" #include "smack-util.h" +#include "socket-util.h" #include "special.h" #include "stat-util.h" #include "string-table.h" @@ -119,7 +102,7 @@ #define SNDBUF_SIZE (8*1024*1024) -static int shift_fds(int fds[], unsigned n_fds) { +static int shift_fds(int fds[], size_t n_fds) { int start, restart_from; if (n_fds <= 0) @@ -164,8 +147,8 @@ static int shift_fds(int fds[], unsigned n_fds) { return 0; } -static int flags_fds(const int fds[], unsigned n_storage_fds, unsigned n_socket_fds, bool nonblock) { - unsigned i, n_fds; +static int flags_fds(const int fds[], size_t n_storage_fds, size_t n_socket_fds, bool nonblock) { + size_t i, n_fds; int r; n_fds = n_storage_fds + n_socket_fds; @@ -1126,7 +1109,7 @@ static int setup_pam( gid_t gid, const char *tty, char ***env, - int fds[], unsigned n_fds) { + int fds[], size_t n_fds) { #if HAVE_PAM @@ -1300,10 +1283,7 @@ static int setup_pam( if (!barrier_place_and_sync(&barrier)) log_error("PAM initialization failed"); - strv_free(*env); - *env = e; - - return 0; + return strv_free_and_replace(*env, e); fail: if (pam_code != PAM_SUCCESS) { @@ -1609,7 +1589,7 @@ static int build_environment( const Unit *u, const ExecContext *c, const ExecParameters *p, - unsigned n_fds, + size_t n_fds, const char *home, const char *username, const char *shell, @@ -1618,7 +1598,7 @@ static int build_environment( char ***ret) { _cleanup_strv_free_ char **our_env = NULL; - unsigned n_env = 0; + size_t n_env = 0; char *x; assert(u); @@ -1636,7 +1616,7 @@ static int build_environment( return -ENOMEM; our_env[n_env++] = x; - if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0) + if (asprintf(&x, "LISTEN_FDS=%zu", n_fds) < 0) return -ENOMEM; our_env[n_env++] = x; @@ -1733,8 +1713,7 @@ static int build_environment( our_env[n_env++] = NULL; assert(n_env <= 12); - *ret = our_env; - our_env = NULL; + *ret = TAKE_PTR(our_env); return 0; } @@ -1758,13 +1737,11 @@ static int build_pass_environment(const ExecContext *c, char ***ret) { if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) return -ENOMEM; - pass_env[n_env++] = x; + pass_env[n_env++] = TAKE_PTR(x); pass_env[n_env] = NULL; - x = NULL; } - *ret = pass_env; - pass_env = NULL; + *ret = TAKE_PTR(pass_env); return 0; } @@ -1798,6 +1775,7 @@ static bool exec_needs_mount_namespace( return true; if (context->private_devices || + context->private_mounts || context->protect_system != PROTECT_SYSTEM_NO || context->protect_home != PROTECT_HOME_NO || context->protect_kernel_tunables || @@ -1805,8 +1783,20 @@ static bool exec_needs_mount_namespace( context->protect_control_groups) return true; - if (context->mount_apivfs && (context->root_image || context->root_directory)) - return true; + if (context->root_directory) { + ExecDirectoryType t; + + if (context->mount_apivfs) + return true; + + for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) { + if (!params->prefix[t]) + continue; + + if (!strv_isempty(context->directories[t].paths)) + return true; + } + } if (context->dynamic_user && (!strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) || @@ -2051,7 +2041,7 @@ static int setup_exec_directory( } /* First set up private root if it doesn't exist yet, with access mode 0700 and owned by root:root */ - r = mkdir_safe_label(private_root, 0700, 0, 0, false); + r = mkdir_safe_label(private_root, 0700, 0, 0, MKDIR_WARN_MODE); if (r < 0) goto fail; @@ -2107,10 +2097,10 @@ static int setup_exec_directory( } } else { r = mkdir_label(p, context->directories[type].mode); - if (r == -EEXIST) - continue; - if (r < 0) + if (r < 0 && r != -EEXIST) goto fail; + if (r == -EEXIST && !context->dynamic_user) + continue; } /* Don't change the owner of the configuration directory, as in the common case it is not written to by @@ -2168,12 +2158,12 @@ static int compile_bind_mounts( const ExecContext *context, const ExecParameters *params, BindMount **ret_bind_mounts, - unsigned *ret_n_bind_mounts, + size_t *ret_n_bind_mounts, char ***ret_empty_directories) { _cleanup_strv_free_ char **empty_directories = NULL; BindMount *bind_mounts; - unsigned n, h = 0, i; + size_t n, h = 0, i; ExecDirectoryType t; int r; @@ -2238,7 +2228,8 @@ static int compile_bind_mounts( continue; if (context->dynamic_user && - !IN_SET(t, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION)) { + !IN_SET(t, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION) && + !(context->root_directory || context->root_image)) { char *private_root; /* So this is for a dynamic user, and we need to make sure the process can access its own @@ -2269,7 +2260,15 @@ static int compile_bind_mounts( goto finish; } - d = strdup(s); + if (context->dynamic_user && + !IN_SET(t, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION) && + (context->root_directory || context->root_image)) + /* When RootDirectory= or RootImage= are set, then the symbolic link to the private + * directory is not created on the root directory. So, let's bind-mount the directory + * on the 'non-private' place. */ + d = strjoin(params->prefix[t], "/", *suffix); + else + d = strdup(s); if (!d) { free(s); r = -ENOMEM; @@ -2290,9 +2289,7 @@ static int compile_bind_mounts( *ret_bind_mounts = bind_mounts; *ret_n_bind_mounts = n; - *ret_empty_directories = empty_directories; - - empty_directories = NULL; + *ret_empty_directories = TAKE_PTR(empty_directories); return (int) n; @@ -2311,17 +2308,10 @@ static int apply_mount_namespace( _cleanup_strv_free_ char **empty_directories = NULL; char *tmp = NULL, *var = NULL; const char *root_dir = NULL, *root_image = NULL; - NamespaceInfo ns_info = { - .ignore_protect_paths = false, - .private_dev = context->private_devices, - .protect_control_groups = context->protect_control_groups, - .protect_kernel_tunables = context->protect_kernel_tunables, - .protect_kernel_modules = context->protect_kernel_modules, - .mount_apivfs = context->mount_apivfs, - }; + NamespaceInfo ns_info; bool needs_sandboxing; BindMount *bind_mounts = NULL; - unsigned n_bind_mounts = 0; + size_t n_bind_mounts = 0; int r; assert(context); @@ -2348,15 +2338,28 @@ static int apply_mount_namespace( if (r < 0) return r; - /* - * If DynamicUser=no and RootDirectory= is set then lets pass a relaxed - * sandbox info, otherwise enforce it, don't ignore protected paths and - * fail if we are enable to apply the sandbox inside the mount namespace. - */ - if (!context->dynamic_user && root_dir) - ns_info.ignore_protect_paths = true; - needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED); + if (needs_sandboxing) + ns_info = (NamespaceInfo) { + .ignore_protect_paths = false, + .private_dev = context->private_devices, + .protect_control_groups = context->protect_control_groups, + .protect_kernel_tunables = context->protect_kernel_tunables, + .protect_kernel_modules = context->protect_kernel_modules, + .mount_apivfs = context->mount_apivfs, + .private_mounts = context->private_mounts, + }; + else if (!context->dynamic_user && root_dir) + /* + * If DynamicUser=no and RootDirectory= is set then lets pass a relaxed + * sandbox info, otherwise enforce it, don't ignore protected paths and + * fail if we are enable to apply the sandbox inside the mount namespace. + */ + ns_info = (NamespaceInfo) { + .ignore_protect_paths = true, + }; + else + ns_info = (NamespaceInfo) {}; r = setup_namespace(root_dir, root_image, &ns_info, context->read_write_paths, @@ -2438,7 +2441,9 @@ static int setup_keyring( uid_t uid, gid_t gid) { key_serial_t keyring; - int r; + int r = 0; + uid_t saved_uid; + gid_t saved_gid; assert(u); assert(context); @@ -2457,6 +2462,26 @@ static int setup_keyring( if (context->keyring_mode == EXEC_KEYRING_INHERIT) return 0; + /* Acquiring a reference to the user keyring is nasty. We briefly change identity in order to get things set up + * properly by the kernel. If we don't do that then we can't create it atomically, and that sucks for parallel + * execution. This mimics what pam_keyinit does, too. Setting up session keyring, to be owned by the right user + * & group is just as nasty as acquiring a reference to the user keyring. */ + + saved_uid = getuid(); + saved_gid = getgid(); + + if (gid_is_valid(gid) && gid != saved_gid) { + if (setregid(gid, -1) < 0) + return log_unit_error_errno(u, errno, "Failed to change GID for user keyring: %m"); + } + + if (uid_is_valid(uid) && uid != saved_uid) { + if (setreuid(uid, -1) < 0) { + r = log_unit_error_errno(u, errno, "Failed to change UID for user keyring: %m"); + goto out; + } + } + keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); if (keyring == -1) { if (errno == ENOSYS) @@ -2466,12 +2491,36 @@ static int setup_keyring( else if (errno == EDQUOT) log_unit_debug_errno(u, errno, "Out of kernel keyrings to allocate, ignoring."); else - return log_unit_error_errno(u, errno, "Setting up kernel keyring failed: %m"); + r = log_unit_error_errno(u, errno, "Setting up kernel keyring failed: %m"); - return 0; + goto out; + } + + /* When requested link the user keyring into the session keyring. */ + if (context->keyring_mode == EXEC_KEYRING_SHARED) { + + if (keyctl(KEYCTL_LINK, + KEY_SPEC_USER_KEYRING, + KEY_SPEC_SESSION_KEYRING, 0, 0) < 0) { + r = log_unit_error_errno(u, errno, "Failed to link user keyring into session keyring: %m"); + goto out; + } + } + + /* Restore uid/gid back */ + if (uid_is_valid(uid) && uid != saved_uid) { + if (setreuid(saved_uid, -1) < 0) { + r = log_unit_error_errno(u, errno, "Failed to change UID back for user keyring: %m"); + goto out; + } } - /* Populate they keyring with the invocation ID by default. */ + if (gid_is_valid(gid) && gid != saved_gid) { + if (setregid(saved_gid, -1) < 0) + return log_unit_error_errno(u, errno, "Failed to change GID back for user keyring: %m"); + } + + /* Populate they keyring with the invocation ID by default, as original saved_uid. */ if (!sd_id128_is_null(u->invocation_id)) { key_serial_t key; @@ -2482,68 +2531,23 @@ static int setup_keyring( if (keyctl(KEYCTL_SETPERM, key, KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH, 0, 0) < 0) - return log_unit_error_errno(u, errno, "Failed to restrict invocation ID permission: %m"); + r = log_unit_error_errno(u, errno, "Failed to restrict invocation ID permission: %m"); } } - /* And now, make the keyring owned by the service's user */ - if (uid_is_valid(uid) || gid_is_valid(gid)) - if (keyctl(KEYCTL_CHOWN, keyring, uid, gid, 0) < 0) - return log_unit_error_errno(u, errno, "Failed to change ownership of session keyring: %m"); +out: + /* Revert back uid & gid for the the last time, and exit */ + /* no extra logging, as only the first already reported error matters */ + if (getuid() != saved_uid) + (void) setreuid(saved_uid, -1); - /* When requested link the user keyring into the session keyring. */ - if (context->keyring_mode == EXEC_KEYRING_SHARED) { - uid_t saved_uid; - gid_t saved_gid; + if (getgid() != saved_gid) + (void) setregid(saved_gid, -1); - /* Acquiring a reference to the user keyring is nasty. We briefly change identity in order to get things - * set up properly by the kernel. If we don't do that then we can't create it atomically, and that - * sucks for parallel execution. This mimics what pam_keyinit does, too.*/ - - saved_uid = getuid(); - saved_gid = getgid(); - - if (gid_is_valid(gid) && gid != saved_gid) { - if (setregid(gid, -1) < 0) - return log_unit_error_errno(u, errno, "Failed to change GID for user keyring: %m"); - } - - if (uid_is_valid(uid) && uid != saved_uid) { - if (setreuid(uid, -1) < 0) { - (void) setregid(saved_gid, -1); - return log_unit_error_errno(u, errno, "Failed to change UID for user keyring: %m"); - } - } - - if (keyctl(KEYCTL_LINK, - KEY_SPEC_USER_KEYRING, - KEY_SPEC_SESSION_KEYRING, 0, 0) < 0) { - - r = -errno; - - (void) setreuid(saved_uid, -1); - (void) setregid(saved_gid, -1); - - return log_unit_error_errno(u, r, "Failed to link user keyring into session keyring: %m"); - } - - if (uid_is_valid(uid) && uid != saved_uid) { - if (setreuid(saved_uid, -1) < 0) { - (void) setregid(saved_gid, -1); - return log_unit_error_errno(u, errno, "Failed to change UID back for user keyring: %m"); - } - } - - if (gid_is_valid(gid) && gid != saved_gid) { - if (setregid(saved_gid, -1) < 0) - return log_unit_error_errno(u, errno, "Failed to change GID back for user keyring: %m"); - } - } - - return 0; + return r; } -static void append_socket_pair(int *array, unsigned *n, const int pair[2]) { +static void append_socket_pair(int *array, size_t *n, const int pair[2]) { assert(array); assert(n); @@ -2562,9 +2566,9 @@ static int close_remaining_fds( const DynamicCreds *dcreds, int user_lookup_fd, int socket_fd, - int *fds, unsigned n_fds) { + int *fds, size_t n_fds) { - unsigned n_dont_close = 0; + size_t n_dont_close = 0; int dont_close[n_fds + 12]; assert(params); @@ -2696,8 +2700,7 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p } } - *ret = list; - list = NULL; + *ret = TAKE_PTR(list); return 0; } @@ -2715,8 +2718,8 @@ static int exec_child( int socket_fd, int named_iofds[3], int *fds, - unsigned n_storage_fds, - unsigned n_socket_fds, + size_t n_storage_fds, + size_t n_socket_fds, char **files_env, int user_lookup_fd, int *exit_status) { @@ -2744,8 +2747,8 @@ static int exec_child( #endif uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; - int i, r, ngids = 0; - unsigned n_fds; + int r, ngids = 0; + size_t n_fds; ExecDirectoryType dt; int secure_bits; @@ -2933,15 +2936,9 @@ static int exec_child( } if (context->oom_score_adjust_set) { - char t[DECIMAL_STR_MAX(context->oom_score_adjust)]; - - /* When we can't make this change due to EPERM, then - * let's silently skip over it. User namespaces - * prohibit write access to this file, and we - * shouldn't trip up over that. */ - - sprintf(t, "%i", context->oom_score_adjust); - r = write_string_file("/proc/self/oom_score_adj", t, 0); + /* When we can't make this change due to EPERM, then let's silently skip over it. User namespaces + * prohibit write access to this file, and we shouldn't trip up over that. */ + r = set_oom_score_adjust(context->oom_score_adjust); if (IN_SET(r, -EPERM, -EACCES)) log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m"); else if (r < 0) { @@ -3184,17 +3181,12 @@ static int exec_child( if (needs_sandboxing) { uint64_t bset; + int which_failed; - for (i = 0; i < _RLIMIT_MAX; i++) { - - if (!context->rlimit[i]) - continue; - - r = setrlimit_closest(i, context->rlimit[i]); - if (r < 0) { - *exit_status = EXIT_LIMITS; - return log_unit_error_errno(unit, r, "Failed to adjust resource limit %s: %m", rlimit_to_string(i)); - } + r = setrlimit_closest_all((const struct rlimit* const *) context->rlimit, &which_failed); + if (r < 0) { + *exit_status = EXIT_LIMITS; + return log_unit_error_errno(unit, r, "Failed to adjust resource limit RLIMIT_%s: %m", rlimit_to_string(which_failed)); } /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */ @@ -3394,8 +3386,7 @@ static int exec_child( return log_oom(); } - strv_free(accum_env); - accum_env = ee; + strv_free_and_replace(accum_env, ee); } final_argv = replace_env_argv(argv, accum_env); @@ -3408,29 +3399,24 @@ static int exec_child( _cleanup_free_ char *line; line = exec_command_line(final_argv); - if (line) { + if (line) log_struct(LOG_DEBUG, "EXECUTABLE=%s", command->path, LOG_UNIT_MESSAGE(unit, "Executing: %s", line), LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit), - NULL); - } + LOG_UNIT_INVOCATION_ID(unit)); } execve(command->path, final_argv, accum_env); if (errno == ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) { - log_struct_errno(LOG_INFO, errno, "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, LOG_UNIT_ID(unit), LOG_UNIT_INVOCATION_ID(unit), LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m", command->path), - "EXECUTABLE=%s", command->path, - NULL); - + "EXECUTABLE=%s", command->path); return 0; } @@ -3451,7 +3437,7 @@ int exec_spawn(Unit *unit, _cleanup_strv_free_ char **files_env = NULL; int *fds = NULL; - unsigned n_storage_fds = 0, n_socket_fds = 0; + size_t n_storage_fds = 0, n_socket_fds = 0; _cleanup_free_ char *line = NULL; int socket_fd, r; int named_iofds[3] = { -1, -1, -1 }; @@ -3504,8 +3490,7 @@ int exec_spawn(Unit *unit, LOG_UNIT_MESSAGE(unit, "About to execute: %s", line), "EXECUTABLE=%s", command->path, LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit), - NULL); + LOG_UNIT_INVOCATION_ID(unit)); pid = fork(); if (pid < 0) @@ -3530,7 +3515,7 @@ int exec_spawn(Unit *unit, unit->manager->user_lookup_fds[1], &exit_status); - if (r < 0) { + if (r < 0) log_struct_errno(LOG_ERR, r, "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, LOG_UNIT_ID(unit), @@ -3538,9 +3523,7 @@ int exec_spawn(Unit *unit, LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD), command->path), - "EXECUTABLE=%s", command->path, - NULL); - } + "EXECUTABLE=%s", command->path); _exit(exit_status); } @@ -3577,7 +3560,8 @@ void exec_context_init(ExecContext *c) { for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) c->directories[i].mode = 0755; c->capability_bounding_set = CAP_ALL; - c->restrict_namespaces = NAMESPACE_FLAGS_ALL; + assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL); + c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL; c->log_level_max = -1; } @@ -3592,8 +3576,7 @@ void exec_context_done(ExecContext *c) { c->pass_environment = strv_free(c->pass_environment); c->unset_environment = strv_free(c->unset_environment); - for (l = 0; l < ELEMENTSOF(c->rlimit); l++) - c->rlimit[l] = mfree(c->rlimit[l]); + rlimit_free_all(c->rlimit); for (l = 0; l < 3; l++) { c->stdio_fdname[l] = mfree(c->stdio_fdname[l]); @@ -3676,8 +3659,8 @@ static void exec_command_done(ExecCommand *c) { c->argv = strv_free(c->argv); } -void exec_command_done_array(ExecCommand *c, unsigned n) { - unsigned i; +void exec_command_done_array(ExecCommand *c, size_t n) { + size_t i; for (i = 0; i < n; i++) exec_command_done(c+i); @@ -3695,8 +3678,8 @@ ExecCommand* exec_command_free_list(ExecCommand *c) { return NULL; } -void exec_command_free_array(ExecCommand **c, unsigned n) { - unsigned i; +void exec_command_free_array(ExecCommand **c, size_t n) { + size_t i; for (i = 0; i < n; i++) c[i] = exec_command_free_list(c[i]); @@ -3742,9 +3725,9 @@ const char* exec_context_fdname(const ExecContext *c, int fd_index) { } static int exec_context_named_iofds(const ExecContext *c, const ExecParameters *p, int named_iofds[3]) { - unsigned i, targets; + size_t i, targets; const char* stdio_fdname[3]; - unsigned n_fds; + size_t n_fds; assert(c); assert(p); @@ -3993,9 +3976,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { for (i = 0; i < RLIM_NLIMITS; i++) if (c->rlimit[i]) { - fprintf(f, "%s%s: " RLIM_FMT "\n", + fprintf(f, "Limit%s%s: " RLIM_FMT "\n", prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max); - fprintf(f, "%s%sSoft: " RLIM_FMT "\n", + fprintf(f, "Limit%s%sSoft: " RLIM_FMT "\n", prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur); } @@ -4280,7 +4263,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { if (exec_context_restrict_namespaces_set(c)) { _cleanup_free_ char *s = NULL; - r = namespace_flag_to_string_many(c->restrict_namespaces, &s); + r = namespace_flags_to_string(c->restrict_namespaces, &s); if (r >= 0) fprintf(f, "%sRestrictNamespaces: %s\n", prefix, s); @@ -4507,10 +4490,7 @@ int exec_command_set(ExecCommand *c, const char *path, ...) { free(c->path); c->path = p; - strv_free(c->argv); - c->argv = l; - - return 0; + return strv_free_and_replace(c->argv, l); } int exec_command_append(ExecCommand *c, const char *path, ...) { @@ -4857,12 +4837,11 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, } else return 0; - /* If the object is newly created, then put it to the hashmap which manages ExecRuntime objects. */ if (rt_create) { r = hashmap_put(u->manager->exec_runtime_by_id, rt_create->id, rt_create); if (r < 0) { - log_unit_debug_errno(u, r, "Failed to put runtime paramter to manager's storage: %m"); + log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m"); return 0; } diff --git a/src/core/execute.h b/src/core/execute.h index a34cf0b873..77ffe82323 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct ExecStatus ExecStatus; typedef struct ExecCommand ExecCommand; typedef struct ExecContext ExecContext; @@ -218,9 +199,9 @@ struct ExecContext { char **read_write_paths, **read_only_paths, **inaccessible_paths; unsigned long mount_flags; BindMount *bind_mounts; - unsigned n_bind_mounts; + size_t n_bind_mounts; TemporaryFileSystem *temporary_filesystems; - unsigned n_temporary_filesystems; + size_t n_temporary_filesystems; uint64_t capability_bounding_set; uint64_t capability_ambient_set; @@ -241,6 +222,7 @@ struct ExecContext { bool private_network; bool private_devices; bool private_users; + bool private_mounts; ProtectSystem protect_system; ProtectHome protect_home; bool protect_kernel_tunables; @@ -292,20 +274,20 @@ static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { } typedef enum ExecFlags { - EXEC_APPLY_SANDBOXING = 1U << 0, - EXEC_APPLY_CHROOT = 1U << 1, - EXEC_APPLY_TTY_STDIN = 1U << 2, - EXEC_NEW_KEYRING = 1U << 3, - EXEC_PASS_LOG_UNIT = 1U << 4, /* Whether to pass the unit name to the service's journal stream connection */ - EXEC_CHOWN_DIRECTORIES = 1U << 5, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */ - EXEC_NSS_BYPASS_BUS = 1U << 6, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */ - EXEC_CGROUP_DELEGATE = 1U << 7, + EXEC_APPLY_SANDBOXING = 1 << 0, + EXEC_APPLY_CHROOT = 1 << 1, + EXEC_APPLY_TTY_STDIN = 1 << 2, + EXEC_NEW_KEYRING = 1 << 3, + EXEC_PASS_LOG_UNIT = 1 << 4, /* Whether to pass the unit name to the service's journal stream connection */ + EXEC_CHOWN_DIRECTORIES = 1 << 5, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */ + EXEC_NSS_BYPASS_BUS = 1 << 6, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */ + EXEC_CGROUP_DELEGATE = 1 << 7, /* The following are not used by execute.c, but by consumers internally */ - EXEC_PASS_FDS = 1U << 8, - EXEC_IS_CONTROL = 1U << 9, - EXEC_SETENV_RESULT = 1U << 10, - EXEC_SET_WATCHDOG = 1U << 11, + EXEC_PASS_FDS = 1 << 8, + EXEC_IS_CONTROL = 1 << 9, + EXEC_SETENV_RESULT = 1 << 10, + EXEC_SET_WATCHDOG = 1 << 11, } ExecFlags; struct ExecParameters { @@ -314,8 +296,8 @@ struct ExecParameters { int *fds; char **fd_names; - unsigned n_storage_fds; - unsigned n_socket_fds; + size_t n_storage_fds; + size_t n_socket_fds; ExecFlags flags; bool selinux_context_net:1; @@ -347,10 +329,10 @@ int exec_spawn(Unit *unit, DynamicCreds *dynamic_creds, pid_t *ret); -void exec_command_done_array(ExecCommand *c, unsigned n); +void exec_command_done_array(ExecCommand *c, size_t n); ExecCommand* exec_command_free_list(ExecCommand *c); -void exec_command_free_array(ExecCommand **c, unsigned n); +void exec_command_free_array(ExecCommand **c, size_t n); void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix); void exec_command_append_list(ExecCommand **l, ExecCommand *e); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 19299918cc..83cce88131 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <stdio.h> diff --git a/src/core/hostname-setup.h b/src/core/hostname-setup.h index 8bf8769859..dc7b9a6262 100644 --- a/src/core/hostname-setup.h +++ b/src/core/hostname-setup.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - int hostname_setup(void); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index 80319622ad..013d6c5de3 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -1,23 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group — http://security.polito.it - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - 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/>. + Copyright © 2012 Roberto Sassu - Politecnico di Torino, Italy + TORSEC group — http://security.polito.it ***/ #include <errno.h> diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h index 1eae74bceb..cf478795a1 100644 --- a/src/core/ima-setup.h +++ b/src/core/ima-setup.h @@ -2,24 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group — http://security.polito.it - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - 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/>. + Copyright © 2012 Roberto Sassu - Politecnico di Torino, Italy + TORSEC group — http://security.polito.it ***/ int ima_setup(void); diff --git a/src/core/ip-address-access.c b/src/core/ip-address-access.c index f10138c6de..1d431bb674 100644 --- a/src/core/ip-address-access.c +++ b/src/core/ip-address-access.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2016 Daniel Mack - - 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 <stdio.h> #include <stdlib.h> diff --git a/src/core/ip-address-access.h b/src/core/ip-address-access.h index 536142e904..7babf19562 100644 --- a/src/core/ip-address-access.h +++ b/src/core/ip-address-access.h @@ -1,25 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2016 Daniel Mack - - 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 "conf-parser.h" #include "in-addr-util.h" #include "list.h" @@ -32,7 +15,7 @@ struct IPAddressAccessItem { LIST_FIELDS(IPAddressAccessItem, items); }; -int config_parse_ip_address_access(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); +CONFIG_PARSER_PROTOTYPE(config_parse_ip_address_access); IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first); diff --git a/src/core/job.c b/src/core/job.c index 1b3534a7a6..734756b666 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> @@ -56,6 +38,7 @@ Job* job_new_raw(Unit *unit) { j->manager = unit->manager; j->unit = unit; j->type = _JOB_TYPE_INVALID; + j->reloaded = false; return j; } @@ -77,7 +60,7 @@ Job* job_new(Unit *unit, JobType type) { return j; } -void job_free(Job *j) { +void job_unlink(Job *j) { assert(j); assert(!j->installed); assert(!j->transaction_prev); @@ -85,16 +68,33 @@ void job_free(Job *j) { assert(!j->subject_list); assert(!j->object_list); - if (j->in_run_queue) + if (j->in_run_queue) { LIST_REMOVE(run_queue, j->manager->run_queue, j); + j->in_run_queue = false; + } - if (j->in_dbus_queue) + if (j->in_dbus_queue) { LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + j->in_dbus_queue = false; + } - if (j->in_gc_queue) + if (j->in_gc_queue) { LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j); + j->in_gc_queue = false; + } + + j->timer_event_source = sd_event_source_unref(j->timer_event_source); +} + +void job_free(Job *j) { + assert(j); + assert(!j->installed); + assert(!j->transaction_prev); + assert(!j->transaction_next); + assert(!j->subject_list); + assert(!j->object_list); - sd_event_source_unref(j->timer_event_source); + job_unlink(j); sd_bus_track_unref(j->bus_track); strv_free(j->deserialized_clients); @@ -254,6 +254,7 @@ int job_install_deserialized(Job *j) { *pj = j; j->installed = true; + j->reloaded = true; if (j->state == JOB_RUNNING) j->unit->manager->n_running_jobs++; @@ -576,7 +577,6 @@ int job_run_and_invalidate(Job *j) { job_set_state(j, JOB_RUNNING); job_add_to_dbus_queue(j); - switch (j->type) { case JOB_VERIFY_ACTIVE: { @@ -626,6 +626,8 @@ int job_run_and_invalidate(Job *j) { r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); else if (r == -ENOLINK) r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); + else if (r == -ESTALE) + r = job_finish_and_invalidate(j, JOB_ONCE, true, false); else if (r == -EAGAIN) job_set_state(j, JOB_WAITING); else if (r < 0) @@ -645,6 +647,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR [JOB_ASSERT] = "Assertion failed for %s.", [JOB_UNSUPPORTED] = "Starting of %s not supported.", [JOB_COLLECTED] = "Unnecessary job for %s was removed.", + [JOB_ONCE] = "Unit %s has been started before and cannot be started again." }; static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Stopped %s.", @@ -704,6 +707,7 @@ static const struct { [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" }, [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" }, /* JOB_COLLECTED */ + [JOB_ONCE] = { ANSI_HIGHLIGHT_RED, " ONCE " }, }; static void job_print_status_message(Unit *u, JobType t, JobResult result) { @@ -761,6 +765,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { [JOB_ASSERT] = LOG_WARNING, [JOB_UNSUPPORTED] = LOG_WARNING, [JOB_COLLECTED] = LOG_INFO, + [JOB_ONCE] = LOG_ERR, }; assert(u); @@ -808,8 +813,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { "JOB_TYPE=%s", job_type_to_string(t), "JOB_RESULT=%s", job_result_to_string(result), LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - NULL); + LOG_UNIT_INVOCATION_ID(u)); return; } @@ -819,8 +823,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { "JOB_RESULT=%s", job_result_to_string(result), LOG_UNIT_ID(u), LOG_UNIT_INVOCATION_ID(u), - mid, - NULL); + mid); } static void job_emit_status_message(Unit *u, JobType t, JobResult result) { @@ -853,6 +856,19 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) { } } +static int job_save_pending_finished_job(Job *j) { + int r; + + assert(j); + + r = set_ensure_allocated(&j->manager->pending_finished_jobs, NULL); + if (r < 0) + return r; + + job_unlink(j); + return set_put(j->manager->pending_finished_jobs, j); +} + int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { Unit *u; Unit *other; @@ -892,7 +908,11 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr j->manager->n_failed_jobs++; job_uninstall(j); - job_free(j); + /* Keep jobs started before the reload to send singal later, free all others */ + if (!MANAGER_IS_RELOADING(j->manager) || + !j->reloaded || + job_save_pending_finished_job(j) < 0) + job_free(j); /* Fail depending jobs on failure */ if (result != JOB_DONE && recursive) { @@ -916,8 +936,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.", u->id, job_type_to_string(t), - job_result_to_string(result)), - NULL); + job_result_to_string(result))); unit_start_on_failure(u); } @@ -1439,8 +1458,7 @@ int job_get_before(Job *j, Job*** ret) { n = sort_job_list(list, n); - *ret = list; - list = NULL; + *ret = TAKE_PTR(list); return (int) n; } @@ -1489,8 +1507,7 @@ int job_get_after(Job *j, Job*** ret) { n = sort_job_list(list, n); - *ret = list; - list = NULL; + *ret = TAKE_PTR(list); return (int) n; } @@ -1539,6 +1556,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = { [JOB_ASSERT] = "assert", [JOB_UNSUPPORTED] = "unsupported", [JOB_COLLECTED] = "collected", + [JOB_ONCE] = "once", }; DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); diff --git a/src/core/job.h b/src/core/job.h index 2edb63169c..2f5f3f3989 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 <stdbool.h> #include "sd-event.h" @@ -109,6 +90,7 @@ enum JobResult { JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */ JOB_COLLECTED, /* Job was garbage collected, since nothing needed it anymore */ + JOB_ONCE, /* Unit was started before, and hence can't be started again */ _JOB_RESULT_MAX, _JOB_RESULT_INVALID = -1 }; @@ -174,10 +156,12 @@ struct Job { bool irreversible:1; bool in_gc_queue:1; bool ref_by_private_bus:1; + bool reloaded:1; }; Job* job_new(Unit *unit, JobType type); Job* job_new_raw(Unit *unit); +void job_unlink(Job *job); void job_free(Job *job); Job* job_install(Job *j); int job_install_deserialized(Job *j); diff --git a/src/core/kill.c b/src/core/kill.c index 5dfcb780fa..929eebfe37 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 "kill.h" #include "signal-util.h" diff --git a/src/core/kill.h b/src/core/kill.h index f0d6ec41e9..2d6aa943a6 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - 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/>. -***/ - typedef struct KillContext KillContext; #include <stdbool.h> diff --git a/src/core/killall.c b/src/core/killall.c index daa9c4ea20..87d207fd3d 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -1,21 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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/>. + Copyright © 2010 ProFUSION embedded systems ***/ #include <errno.h> @@ -211,7 +196,6 @@ static int killall(int sig, Set *pids, bool send_sighup) { make sure to only send this after SIGTERM so that SIGTERM is always first in the queue. */ - if (get_ctty_devnr(pid, NULL) >= 0) /* it's OK if the process is gone, just ignore the result */ (void) kill(pid, SIGHUP); diff --git a/src/core/killall.h b/src/core/killall.h index 45e97ab594..cbd202651c 100644 --- a/src/core/killall.h +++ b/src/core/killall.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - 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 "time-util.h" void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout); diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index a2809d03f6..9251929558 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <ftw.h> #include <string.h> diff --git a/src/core/kmod-setup.h b/src/core/kmod-setup.h index b5ea6b55a9..801c7bf699 100644 --- a/src/core/kmod-setup.h +++ b/src/core/kmod-setup.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - int kmod_setup(void); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 57ed686d1f..4b422cc54e 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "conf-parser.h" #include "fs-util.h" @@ -31,26 +12,21 @@ #include "unit.h" static int unit_name_compatible(const char *a, const char *b) { - _cleanup_free_ char *prefix = NULL; + _cleanup_free_ char *template = NULL; int r; - /* the straightforward case: the symlink name matches the target */ + /* The straightforward case: the symlink name matches the target */ if (streq(a, b)) return 1; - r = unit_name_template(a, &prefix); + r = unit_name_template(a, &template); if (r == -EINVAL) - /* not a template */ - return 0; + return 0; /* Not a template */ if (r < 0) - /* oom, or some other failure. Just skip the warning. */ - return r; - - /* an instance name points to a target that is just the template name */ - if (streq(prefix, b)) - return 1; + return r; /* OOM, or some other failure. Just skip the warning. */ - return 0; + /* An instance name points to a target that is just the template name */ + return streq(template, b); } static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { @@ -69,8 +45,8 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff return r; STRV_FOREACH(p, paths) { - const char *entry; _cleanup_free_ char *target = NULL; + const char *entry; entry = basename(*p); @@ -146,10 +122,9 @@ int unit_load_dropin(Unit *u) { if (r <= 0) return 0; - if (!u->dropin_paths) { - u->dropin_paths = l; - l = NULL; - } else { + if (!u->dropin_paths) + u->dropin_paths = TAKE_PTR(l); + else { r = strv_extend_strv(&u->dropin_paths, l, true); if (r < 0) return log_oom(); diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 4c8ab487b3..bb10a76338 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -1,31 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "dropin.h" #include "unit.h" /* Read service data supplementary drop-in directories */ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { + assert(u); + return unit_file_find_dropin_conf_paths(NULL, u->manager->lookup_paths.search_path, u->manager->unit_path_cache, diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5d90a7c054..15fb47838c 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -6,6 +6,8 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "conf-parser.h" #include "load-fragment.h" #include "missing.h" + +#include "all-units.h" %} struct ConfigPerfItem; %null_strings @@ -57,11 +59,11 @@ $1.SyslogLevelPrefix, config_parse_bool, 0, $1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max) $1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context) $1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) -$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) +$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context.secure_bits) $1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set) $1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set) $1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec) -$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context) +$1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges) $1.KeyringMode, config_parse_exec_keyring_mode, 0, offsetof($1, exec_context.keyring_mode) m4_ifdef(`HAVE_SECCOMP', `$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) @@ -80,22 +82,22 @@ $1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CO $1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) -$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) -$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) -$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) -$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) -$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) -$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) -$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) -$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) -$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) -$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) -$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) -$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) -$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) -$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) -$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) +$1.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) +$1.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) +$1.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) +$1.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) +$1.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) +$1.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) +$1.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) +$1.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof($1, exec_context.rlimit) +$1.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) +$1.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) +$1.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) +$1.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) +$1.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) +$1.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) +$1.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) +$1.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) $1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) $1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) $1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) @@ -112,9 +114,10 @@ $1.ProtectKernelModules, config_parse_bool, 0, $1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups) $1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) -$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) -$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context) -$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) +$1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts) +$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context.protect_system) +$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context.protect_home) +$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context.mount_flags) $1.MountAPIVFS, config_parse_bool, 0, offsetof($1, exec_context.mount_apivfs) $1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality) $1.RuntimeDirectoryPreserve, config_parse_runtime_preserve_mode, 0, offsetof($1, exec_context.runtime_directory_preserve_mode) @@ -153,8 +156,8 @@ $1.KillSignal, config_parse_signal, 0, m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', `$1.Slice, config_parse_unit_slice, 0, 0 $1.CPUAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.cpu_accounting) -$1.CPUWeight, config_parse_cpu_weight, 0, offsetof($1, cgroup_context.cpu_weight) -$1.StartupCPUWeight, config_parse_cpu_weight, 0, offsetof($1, cgroup_context.startup_cpu_weight) +$1.CPUWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.cpu_weight) +$1.StartupCPUWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.startup_cpu_weight) $1.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.cpu_shares) $1.StartupCPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.startup_cpu_shares) $1.CPUQuota, config_parse_cpu_quota, 0, offsetof($1, cgroup_context) @@ -167,8 +170,8 @@ $1.MemoryLimit, config_parse_memory_limit, 0, $1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context) $1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy) $1.IOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.io_accounting) -$1.IOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.io_weight) -$1.StartupIOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.startup_io_weight) +$1.IOWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.io_weight) +$1.StartupIOWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.startup_io_weight) $1.IODeviceWeight, config_parse_io_device_weight, 0, offsetof($1, cgroup_context) $1.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) $1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) @@ -289,7 +292,7 @@ Service.ExecStopPost, config_parse_exec, SERVICE_EXE Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec) Service.TimeoutSec, config_parse_service_timeout, 0, 0 Service.TimeoutStartSec, config_parse_service_timeout, 0, 0 -Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 +Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec) Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) m4_dnl The following five only exist for compatibility, they moved into Unit, see above @@ -328,8 +331,8 @@ Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCK Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0 -Socket.SocketProtocol, config_parse_socket_protocol, 0, 0 -Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, +Socket.SocketProtocol, config_parse_socket_protocol, 0, offsetof(Socket, socket_protocol) +Socket.BindIPv6Only, config_parse_socket_bind, 0, offsetof(Socket, bind_ipv6_only) Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 1e3416c40b..d9a5094aa0 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1,22 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2012 Holger Hans Peter Freyther - - 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/>. + Copyright © 2012 Holger Hans Peter Freyther ***/ #include <errno.h> @@ -33,6 +17,7 @@ #include "af-list.h" #include "alloc-util.h" +#include "all-units.h" #include "bus-error.h" #include "bus-internal.h" #include "bus-util.h" @@ -57,7 +42,6 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" -#include "rlimit-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" #endif @@ -70,43 +54,45 @@ #include "strv.h" #include "unit-name.h" #include "unit-printf.h" -#include "unit.h" #include "user-util.h" -#include "utf8.h" #include "web-util.h" -int config_parse_warn_compat( - 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) { - Disabled reason = ltype; - - switch(reason) { - case DISABLED_CONFIGURATION: - log_syntax(unit, LOG_DEBUG, filename, line, 0, - "Support for option %s= has been disabled at compile time and it is ignored", lvalue); - break; - case DISABLED_LEGACY: - log_syntax(unit, LOG_INFO, filename, line, 0, - "Support for option %s= has been removed and it is ignored", lvalue); - break; - case DISABLED_EXPERIMENTAL: - log_syntax(unit, LOG_INFO, filename, line, 0, - "Support for option %s= has not yet been enabled and it is ignored", lvalue); - break; - }; +static int supported_socket_protocol_from_string(const char *s) { + int r; - return 0; + if (isempty(s)) + return IPPROTO_IP; + + r = socket_protocol_from_name(s); + if (r < 0) + return -EINVAL; + if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP)) + return -EPROTONOSUPPORT; + + return r; } +DEFINE_CONFIG_PARSE(config_parse_socket_protocol, supported_socket_protocol_from_string, "Failed to parse socket protocol"); +DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits"); DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value"); +DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value"); +DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight"); +DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight"); +DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares"); +DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag"); int config_parse_unit_deps( const char *unit, @@ -145,7 +131,7 @@ int config_parse_unit_deps( r = unit_name_printf(u, word, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word); continue; } @@ -198,7 +184,7 @@ int config_parse_unit_string_printf( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } @@ -228,7 +214,7 @@ int config_parse_unit_strv_printf( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } @@ -257,11 +243,19 @@ int config_parse_unit_path_printf( assert(rvalue); assert(u); + /* Let's not bother with anything that is too long */ + if (strlen(rvalue) >= PATH_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s value too long%s.", + lvalue, fatal ? "" : ", ignoring"); + return fatal ? -ENAMETOOLONG : 0; + } + r = unit_full_printf(u, rvalue, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve unit specifiers on %s%s: %m", - fatal ? "" : ", ignoring", rvalue); + "Failed to resolve unit specifiers in '%s'%s: %m", + rvalue, fatal ? "" : ", ignoring"); return fatal ? -ENOEXEC : 0; } @@ -312,22 +306,13 @@ int config_parse_unit_path_strv_printf( r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word); - return 0; - } - - if (!utf8_is_valid(k)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + "Failed to resolve unit specifiers in '%s', ignoring: %m", word); return 0; } - if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Symlink path is not absolute: %s", k); + r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) return 0; - } - - path_kill_slashes(k); r = strv_push(x, k); if (r < 0) @@ -370,47 +355,51 @@ int config_parse_socket_listen(const char *unit, return log_oom(); if (ltype != SOCKET_SOCKET) { + _cleanup_free_ char *k = NULL; - p->type = ltype; - r = unit_full_printf(UNIT(s), rvalue, &p->path); + r = unit_full_printf(UNIT(s), rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } - path_kill_slashes(p->path); + r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + return 0; + + free_and_replace(p->path, k); + p->type = ltype; } else if (streq(lvalue, "ListenNetlink")) { _cleanup_free_ char *k = NULL; - p->type = SOCKET_SOCKET; r = unit_full_printf(UNIT(s), rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } r = socket_address_parse_netlink(&p->address, k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k); return 0; } + p->type = SOCKET_SOCKET; + } else { _cleanup_free_ char *k = NULL; - p->type = SOCKET_SOCKET; r = unit_full_printf(UNIT(s), rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } r = socket_address_parse_and_warn(&p->address, k); if (r < 0) { if (r != -EAFNOSUPPORT) - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); - + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k); return 0; } @@ -427,6 +416,8 @@ int config_parse_socket_listen(const char *unit, log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue); return 0; } + + p->type = SOCKET_SOCKET; } p->fd = -1; @@ -442,72 +433,6 @@ int config_parse_socket_listen(const char *unit, return 0; } -int config_parse_socket_protocol(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) { - Socket *s; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - r = socket_protocol_from_name(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid socket protocol, ignoring: %s", rvalue); - return 0; - } else if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue); - return 0; - } - - s->socket_protocol = r; - - return 0; -} - -int config_parse_socket_bind(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) { - - Socket *s; - SocketAddressBindIPv6Only b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - b = parse_socket_address_bind_ipv6_only_or_bool(rvalue); - if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); - return 0; - } - - s->bind_ipv6_only = b; - - return 0; -} - int config_parse_exec_nice( const char *unit, const char *filename, @@ -528,13 +453,17 @@ int config_parse_exec_nice( assert(rvalue); assert(data); + if (isempty(rvalue)) { + c->nice_set = false; + return 0; + } + r = parse_nice(rvalue, &priority); if (r < 0) { if (r == -ERANGE) log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue); else - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority '%s', ignoring: %m", rvalue); return 0; } @@ -544,16 +473,17 @@ int config_parse_exec_nice( return 0; } -int config_parse_exec_oom_score_adjust(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_oom_score_adjust( + 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) { ExecContext *c = data; int oa, r; @@ -563,14 +493,17 @@ int config_parse_exec_oom_score_adjust(const char* unit, assert(rvalue); assert(data); - r = safe_atoi(rvalue, &oa); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); + if (isempty(rvalue)) { + c->oom_score_adjust_set = false; return 0; } - if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue); + r = parse_oom_score_adjust(rvalue, &oa); + if (r < 0) { + if (r == -ERANGE) + log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue); + else + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue); return 0; } @@ -662,7 +595,7 @@ int config_parse_exec( r = unit_full_printf(u, f, &path); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve unit specifiers on %s%s: %m", + "Failed to resolve unit specifiers in '%s'%s: %m", f, ignore ? ", ignoring" : ""); return ignore ? 0 : -ENOEXEC; } @@ -670,29 +603,57 @@ int config_parse_exec( if (isempty(path)) { /* First word is either "-" or "@" with no command. */ log_syntax(unit, LOG_ERR, filename, line, 0, - "Empty path in command line%s: \"%s\"", + "Empty path in command line%s: '%s'", ignore ? ", ignoring" : "", rvalue); return ignore ? 0 : -ENOEXEC; } if (!string_is_safe(path)) { log_syntax(unit, LOG_ERR, filename, line, 0, - "Executable path contains special characters%s: %s", - ignore ? ", ignoring" : "", rvalue); - return ignore ? 0 : -ENOEXEC; - } - if (!path_is_absolute(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Executable path is not absolute%s: %s", - ignore ? ", ignoring" : "", rvalue); + "Executable name contains special characters%s: %s", + ignore ? ", ignoring" : "", path); return ignore ? 0 : -ENOEXEC; } if (endswith(path, "/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory%s: %s", - ignore ? ", ignoring" : "", rvalue); + ignore ? ", ignoring" : "", path); return ignore ? 0 : -ENOEXEC; } + if (!path_is_absolute(path)) { + const char *prefix; + bool found = false; + + if (!filename_is_valid(path)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Neither a valid executable name nor an absolute path%s: %s", + ignore ? ", ignoring" : "", path); + return ignore ? 0 : -ENOEXEC; + } + + /* Resolve a single-component name to a full path */ + NULSTR_FOREACH(prefix, DEFAULT_PATH_NULSTR) { + _cleanup_free_ char *fullpath = NULL; + + fullpath = strjoin(prefix, "/", path); + if (!fullpath) + return log_oom(); + + if (access(fullpath, F_OK) >= 0) { + free_and_replace(path, fullpath); + found = true; + break; + } + } + + if (!found) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Executable \"%s\" not found in path \"%s\"%s", + path, DEFAULT_PATH, ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; + } + } + if (!separate_argv0) { char *w = NULL; @@ -706,7 +667,7 @@ int config_parse_exec( n[nlen] = NULL; } - path_kill_slashes(path); + path_simplify(path, false); while (!isempty(p)) { _cleanup_free_ char *word = NULL, *resolved = NULL; @@ -748,16 +709,16 @@ int config_parse_exec( r = unit_full_printf(u, word, &resolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve unit specifiers on %s%s: %m", + "Failed to resolve unit specifiers in %s%s: %m", word, ignore ? ", ignoring" : ""); return ignore ? 0 : -ENOEXEC; } if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) return log_oom(); - n[nlen++] = resolved; + + n[nlen++] = TAKE_PTR(resolved); n[nlen] = NULL; - resolved = NULL; } if (!n || !n[0]) { @@ -771,15 +732,13 @@ int config_parse_exec( if (!nce) return log_oom(); - nce->argv = n; - nce->path = path; + nce->argv = TAKE_PTR(n); + nce->path = TAKE_PTR(path); nce->flags = flags; exec_command_append_list(e, nce); /* Do not _cleanup_free_ these. */ - n = NULL; - path = NULL; nce = NULL; rvalue = p; @@ -788,9 +747,6 @@ int config_parse_exec( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); - int config_parse_socket_bindtodevice( const char* unit, const char *filename, @@ -804,27 +760,24 @@ int config_parse_socket_bindtodevice( void *userdata) { Socket *s = data; - char *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (rvalue[0] && !streq(rvalue, "*")) { - if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue); - return 0; - } + if (isempty(rvalue) || streq(rvalue, "*")) { + s->bind_to_device = mfree(s->bind_to_device); + return 0; + } - n = strdup(rvalue); - if (!n) - return log_oom(); - } else - n = NULL; + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid interface name, ignoring: %s", rvalue); + return 0; + } - free(s->bind_to_device); - s->bind_to_device = n; + if (free_and_strdup(&s->bind_to_device, rvalue) < 0) + return log_oom(); return 0; } @@ -858,13 +811,13 @@ int config_parse_exec_input( r = unit_full_printf(u, n, &resolved); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n); + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", n); if (isempty(resolved)) resolved = mfree(resolved); else if (!fdname_is_valid(resolved)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved); - return -EINVAL; + return -ENOEXEC; } free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved); @@ -876,17 +829,11 @@ int config_parse_exec_input( r = unit_full_printf(u, n, &resolved); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n); - - if (!path_is_absolute(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved); - return -EINVAL; - } + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", n); - if (!path_is_normalized(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name: %s", resolved); - return -EINVAL; - } + r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue); + if (r < 0) + return -ENOEXEC; free_and_replace(c->stdio_file[STDIN_FILENO], resolved); @@ -937,11 +884,11 @@ int config_parse_exec_input_text( r = cunescape(rvalue, 0, &unescaped); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text: %s", rvalue); + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text '%s': %m", rvalue); r = unit_full_printf(u, unescaped, &resolved); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", unescaped); + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s': %m", unescaped); sz = strlen(resolved); if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */ @@ -1045,13 +992,13 @@ int config_parse_exec_output( if (n) { r = unit_full_printf(u, n, &resolved); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n); + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n); if (isempty(resolved)) resolved = mfree(resolved); else if (!fdname_is_valid(resolved)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved); - return -EINVAL; + return -ENOEXEC; } eo = EXEC_OUTPUT_NAMED_FD; @@ -1060,17 +1007,11 @@ int config_parse_exec_output( r = unit_full_printf(u, n, &resolved); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n); - - if (!path_is_absolute(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved); - return -EINVAL; - } + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n); - if (!path_is_normalized(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name, ignoring: %s", resolved); - return -EINVAL; - } + r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue); + if (r < 0) + return -ENOEXEC; eo = EXEC_OUTPUT_FILE; @@ -1123,6 +1064,12 @@ int config_parse_exec_io_class(const char *unit, assert(rvalue); assert(data); + if (isempty(rvalue)) { + c->ioprio_set = false; + c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); + return 0; + } + x = ioprio_class_from_string(rvalue); if (x < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue); @@ -1154,6 +1101,12 @@ int config_parse_exec_io_priority(const char *unit, assert(rvalue); assert(data); + if (isempty(rvalue)) { + c->ioprio_set = false; + c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); + return 0; + } + r = ioprio_parse_priority(rvalue, &i); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue); @@ -1177,7 +1130,6 @@ int config_parse_exec_cpu_sched_policy(const char *unit, void *data, void *userdata) { - ExecContext *c = data; int x; @@ -1186,6 +1138,13 @@ int config_parse_exec_cpu_sched_policy(const char *unit, assert(rvalue); assert(data); + if (isempty(rvalue)) { + c->cpu_sched_set = false; + c->cpu_sched_policy = SCHED_OTHER; + c->cpu_sched_priority = 0; + return 0; + } + x = sched_policy_from_string(rvalue); if (x < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); @@ -1221,7 +1180,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, r = safe_atoi(rvalue, &i); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue); return 0; } @@ -1272,8 +1231,7 @@ int config_parse_exec_cpu_affinity(const char *unit, } if (!c->cpuset) { - c->cpuset = cpuset; - cpuset = NULL; + c->cpuset = TAKE_PTR(cpuset); c->cpuset_ncpus = (unsigned) ncpus; return 0; } @@ -1281,8 +1239,7 @@ int config_parse_exec_cpu_affinity(const char *unit, if (c->cpuset_ncpus < (unsigned) ncpus) { CPU_OR_S(CPU_ALLOC_SIZE(c->cpuset_ncpus), cpuset, c->cpuset, cpuset); CPU_FREE(c->cpuset); - c->cpuset = cpuset; - cpuset = NULL; + c->cpuset = TAKE_PTR(cpuset); c->cpuset_ncpus = (unsigned) ncpus; return 0; } @@ -1292,45 +1249,6 @@ int config_parse_exec_cpu_affinity(const char *unit, return 0; } -int config_parse_exec_secure_bits(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) { - - ExecContext *c = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* An empty assignment resets the field */ - c->secure_bits = 0; - return 0; - } - - r = secure_bits_from_string(rvalue); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid syntax, ignoring: %s", rvalue); - return 0; - } - - c->secure_bits = r; - - return 0; -} - int config_parse_capability_set( const char *unit, const char *filename, @@ -1363,10 +1281,8 @@ int config_parse_capability_set( /* else "AmbientCapabilities" initialized to all bits off */ r = capability_set_from_string(rvalue, &sum); - if (r == -ENOMEM) - return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue); return 0; } @@ -1384,109 +1300,6 @@ int config_parse_capability_set( return 0; } -int config_parse_limit( - 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) { - - struct rlimit **rl = data, d = {}; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = rlimit_parse(ltype, rvalue, &d); - if (r == -EILSEQ) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); - return 0; - } - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } - - if (rl[ltype]) - *rl[ltype] = d; - else { - rl[ltype] = newdup(struct rlimit, &d, 1); - if (!rl[ltype]) - return log_oom(); - } - - return 0; -} - -#if HAVE_SYSV_COMPAT -int config_parse_sysv_priority(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 *priority = data; - int i, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &i); - if (r < 0 || i < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue); - return 0; - } - - *priority = (int) i; - return 0; -} -#endif - -DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - -int config_parse_exec_mount_flags( - 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) { - - - ExecContext *c = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = mount_propagation_flags_from_string(rvalue, &c->mount_flags); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue); - - return 0; -} - int config_parse_exec_selinux_context( const char *unit, const char *filename, @@ -1525,13 +1338,12 @@ int config_parse_exec_selinux_context( r = unit_full_printf(u, rvalue, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers%s: %m", - ignore ? ", ignoring" : ""); + "Failed to resolve unit specifiers in '%s'%s: %m", + rvalue, ignore ? ", ignoring" : ""); return ignore ? 0 : -ENOEXEC; } - free(c->selinux_context); - c->selinux_context = k; + free_and_replace(c->selinux_context, k); c->selinux_context_ignore = ignore; return 0; @@ -1575,13 +1387,12 @@ int config_parse_exec_apparmor_profile( r = unit_full_printf(u, rvalue, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers%s: %m", - ignore ? ", ignoring" : ""); + "Failed to resolve unit specifiers in '%s'%s: %m", + rvalue, ignore ? ", ignoring" : ""); return ignore ? 0 : -ENOEXEC; } - free(c->apparmor_profile); - c->apparmor_profile = k; + free_and_replace(c->apparmor_profile, k); c->apparmor_profile_ignore = ignore; return 0; @@ -1625,13 +1436,12 @@ int config_parse_exec_smack_process_label( r = unit_full_printf(u, rvalue, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers%s: %m", - ignore ? ", ignoring" : ""); + "Failed to resolve unit specifiers in '%s'%s: %m", + rvalue, ignore ? ", ignoring" : ""); return ignore ? 0 : -ENOEXEC; } - free(c->smack_process_label); - c->smack_process_label = k; + free_and_replace(c->smack_process_label, k); c->smack_process_label_ignore = ignore; return 0; @@ -1652,7 +1462,7 @@ int config_parse_timer(const char *unit, usec_t usec = 0; TimerValue *v; TimerBase b; - CalendarSpec *c = NULL; + _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; Unit *u = userdata; _cleanup_free_ char *k = NULL; int r; @@ -1676,7 +1486,7 @@ int config_parse_timer(const char *unit, r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } @@ -1685,22 +1495,19 @@ int config_parse_timer(const char *unit, log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k); return 0; } - } else { + } else if (parse_sec(k, &usec) < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k); return 0; } - } v = new0(TimerValue, 1); - if (!v) { - calendar_spec_free(c); + if (!v) return log_oom(); - } v->base = b; v->value = usec; - v->calendar_spec = c; + v->calendar_spec = TAKE_PTR(c); LIST_PREPEND(value, t->values, v); @@ -1736,7 +1543,7 @@ int config_parse_trigger_unit( r = unit_name_printf(u, rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); return 0; } @@ -1795,22 +1602,20 @@ int config_parse_path_spec(const char *unit, r = unit_full_printf(UNIT(p), rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); return 0; } - if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k); + r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) return 0; - } s = new0(PathSpec, 1); if (!s) return log_oom(); s->unit = UNIT(p); - s->path = path_kill_slashes(k); - k = NULL; + s->path = TAKE_PTR(k); s->type = b; s->inotify_fd = -1; @@ -1844,7 +1649,7 @@ int config_parse_socket_service( r = unit_name_printf(UNIT(s), rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); return -ENOEXEC; } @@ -1892,7 +1697,7 @@ int config_parse_fdname( r = unit_full_printf(UNIT(s), rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } @@ -1941,7 +1746,7 @@ int config_parse_service_sockets( r = unit_name_printf(UNIT(s), word, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word); continue; } @@ -1985,12 +1790,12 @@ int config_parse_bus_name( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); return 0; } if (!service_name_is_valid(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name, ignoring: %s", k); return 0; } @@ -2018,26 +1823,21 @@ int config_parse_service_timeout( assert(rvalue); assert(s); - /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */ + /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */ - r = parse_sec(rvalue, &usec); + /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens + * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle + * all other timeouts. */ + r = parse_sec_fix_0(rvalue, &usec); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); return 0; } - /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens - * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle - * all other timeouts. */ - if (usec <= 0) - usec = USEC_INFINITY; + s->start_timeout_defined = true; + s->timeout_start_usec = usec; - if (!streq(lvalue, "TimeoutStopSec")) { - s->start_timeout_defined = true; - s->timeout_start_usec = usec; - } - - if (!streq(lvalue, "TimeoutStartSec")) + if (streq(lvalue, "TimeoutSec")) s->timeout_stop_usec = usec; return 0; @@ -2088,7 +1888,8 @@ int config_parse_user_group( void *data, void *userdata) { - char **user = data, *n; + _cleanup_free_ char *k = NULL; + char **user = data; Unit *u = userdata; int r; @@ -2097,30 +1898,23 @@ int config_parse_user_group( assert(rvalue); assert(u); - if (isempty(rvalue)) - n = NULL; - else { - _cleanup_free_ char *k = NULL; - - r = unit_full_printf(u, rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); - return -ENOEXEC; - } - - if (!valid_user_group_name_or_id(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); - return -ENOEXEC; - } + if (isempty(rvalue)) { + *user = mfree(*user); + return 0; + } - n = k; - k = NULL; + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); + return -ENOEXEC; } - free(*user); - *user = n; + if (!valid_user_group_name_or_id(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } - return 0; + return free_and_replace(*user, k); } int config_parse_user_group_strv( @@ -2137,7 +1931,7 @@ int config_parse_user_group_strv( char ***users = data; Unit *u = userdata; - const char *p; + const char *p = rvalue; int r; assert(filename); @@ -2150,7 +1944,6 @@ int config_parse_user_group_strv( return 0; } - p = rvalue; for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; @@ -2208,6 +2001,12 @@ int config_parse_working_directory( assert(c); assert(u); + if (isempty(rvalue)) { + c->working_directory_home = false; + c->working_directory = mfree(c->working_directory); + return 0; + } + if (rvalue[0] == '-') { missing_ok = true; rvalue++; @@ -2228,19 +2027,9 @@ int config_parse_working_directory( return missing_ok ? 0 : -ENOEXEC; } - path_kill_slashes(k); - - if (!utf8_is_valid(k)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - return missing_ok ? 0 : -ENOEXEC; - } - - if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Working directory path '%s' is not absolute%s.", - rvalue, missing_ok ? ", ignoring" : ""); + r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue); + if (r < 0) return missing_ok ? 0 : -ENOEXEC; - } c->working_directory_home = false; free_and_replace(c->working_directory, k); @@ -2279,19 +2068,20 @@ int config_parse_unit_env_file(const char *unit, r = unit_full_printf(u, rvalue, &n); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } - if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n); + r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) return 0; - } - r = strv_extend(env, n); + r = strv_push(env, n); if (r < 0) return log_oom(); + n = NULL; + return 0; } @@ -2341,13 +2131,11 @@ int config_parse_environ( r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers, ignoring: %s", word); + "Failed to resolve unit specifiers in %s, ignoring: %m", word); continue; } - } else { - k = word; - word = NULL; - } + } else + k = TAKE_PTR(word); if (!env_assignment_is_valid(k)) { log_syntax(unit, LOG_ERR, filename, line, 0, @@ -2375,10 +2163,10 @@ int config_parse_pass_environ( void *data, void *userdata) { - const char *whole_rvalue = rvalue; _cleanup_strv_free_ char **n = NULL; size_t nlen = 0, nbufsize = 0; char*** passenv = data; + const char *p = rvalue; Unit *u = userdata; int r; @@ -2396,14 +2184,14 @@ int config_parse_pass_environ( for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; - r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES); + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); + "Trailing garbage in %s, ignoring: %s", lvalue, rvalue); break; } @@ -2411,13 +2199,11 @@ int config_parse_pass_environ( r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers, ignoring: %s", word); + "Failed to resolve specifiers in %s, ignoring: %m", word); continue; } - } else { - k = word; - word = NULL; - } + } else + k = TAKE_PTR(word); if (!env_name_is_valid(k)) { log_syntax(unit, LOG_ERR, filename, line, 0, @@ -2428,9 +2214,8 @@ int config_parse_pass_environ( if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) return log_oom(); - n[nlen++] = k; + n[nlen++] = TAKE_PTR(k); n[nlen] = NULL; - k = NULL; } if (n) { @@ -2455,9 +2240,9 @@ int config_parse_unset_environ( void *userdata) { _cleanup_strv_free_ char **n = NULL; - const char *whole_rvalue = rvalue; size_t nlen = 0, nbufsize = 0; char*** unsetenv = data; + const char *p = rvalue; Unit *u = userdata; int r; @@ -2475,14 +2260,14 @@ int config_parse_unset_environ( for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; - r = extract_first_word(&rvalue, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES); + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); + "Trailing garbage in %s, ignoring: %s", lvalue, rvalue); break; } @@ -2490,13 +2275,11 @@ int config_parse_unset_environ( r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers, ignoring: %s", word); + "Failed to resolve unit specifiers in %s, ignoring: %m", word); continue; } - } else { - k = word; - word = NULL; - } + } else + k = TAKE_PTR(word); if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) { log_syntax(unit, LOG_ERR, filename, line, 0, @@ -2507,9 +2290,8 @@ int config_parse_unset_environ( if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) return log_oom(); - n[nlen++] = k; + n[nlen++] = TAKE_PTR(k); n[nlen] = NULL; - k = NULL; } if (n) { @@ -2535,7 +2317,7 @@ int config_parse_log_extra_fields( ExecContext *c = data; Unit *u = userdata; - const char *p; + const char *p = rvalue; int r; assert(filename); @@ -2548,14 +2330,14 @@ int config_parse_log_extra_fields( return 0; } - for (p = rvalue;; ) { + for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; struct iovec *t; const char *eq; r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES); if (r == 0) - break; + return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) { @@ -2565,18 +2347,18 @@ int config_parse_log_extra_fields( r = unit_full_printf(u, word, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring field: %m", word); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); continue; } eq = strchr(k, '='); if (!eq) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring field: %s", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k); continue; } if (!journal_field_valid(k, eq-k, false)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring field: %s", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring: %s", k); continue; } @@ -2589,36 +2371,6 @@ int config_parse_log_extra_fields( k = NULL; } - - return 0; -} - -int config_parse_ip_tos(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 *ip_tos = data, x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = ip_tos_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue); - return 0; - } - - *ip_tos = x; - return 0; } int config_parse_unit_condition_path( @@ -2661,14 +2413,13 @@ int config_parse_unit_condition_path( r = unit_full_printf(u, rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); return 0; } - if (!path_is_absolute(p)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p); + r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) return 0; - } c = condition_new(t, p, trigger, negate); if (!c) @@ -2718,7 +2469,7 @@ int config_parse_unit_condition_string( r = unit_full_printf(u, rvalue, &s); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } @@ -2782,9 +2533,6 @@ int config_parse_unit_condition_null( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier"); - int config_parse_unit_requires_mounts_for( const char *unit, const char *filename, @@ -2797,8 +2545,8 @@ int config_parse_unit_requires_mounts_for( void *data, void *userdata) { + const char *p = rvalue; Unit *u = userdata; - const char *p; int r; assert(filename); @@ -2806,7 +2554,7 @@ int config_parse_unit_requires_mounts_for( assert(rvalue); assert(data); - for (p = rvalue;; ) { + for (;;) { _cleanup_free_ char *word = NULL, *resolved = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); @@ -2820,20 +2568,19 @@ int config_parse_unit_requires_mounts_for( return 0; } - if (!utf8_is_valid(word)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - continue; - } - r = unit_full_printf(u, word, &resolved); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word); continue; } + r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + continue; + r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved); continue; } } @@ -2947,12 +2694,12 @@ int config_parse_syscall_filter( r = extract_first_word(&p, &word, NULL, 0); if (r == 0) - break; + return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - break; + return 0; } r = parse_syscall_and_errno(word, &name, &num); @@ -2967,8 +2714,6 @@ int config_parse_syscall_filter( if (r < 0) return r; } - - return 0; } int config_parse_syscall_archs( @@ -2983,8 +2728,8 @@ int config_parse_syscall_archs( void *data, void *userdata) { + const char *p = rvalue; Set **archs = data; - const char *p; int r; if (isempty(rvalue)) { @@ -2996,7 +2741,7 @@ int config_parse_syscall_archs( if (r < 0) return log_oom(); - for (p = rvalue;;) { + for (;;) { _cleanup_free_ char *word = NULL; uint32_t a; @@ -3116,9 +2861,9 @@ int config_parse_address_families( } af = af_from_name(word); - if (af <= 0) { + if (af <= 0) { log_syntax(unit, LOG_ERR, filename, line, 0, - "Failed to parse address family \"%s\", ignoring: %m", word); + "Failed to parse address family, ignoring: %s", word); continue; } @@ -3147,11 +2892,22 @@ int config_parse_restrict_namespaces( void *userdata) { ExecContext *c = data; + unsigned long flags; bool invert = false; int r; if (isempty(rvalue)) { /* Reset to the default. */ + c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL; + return 0; + } + + /* Boolean parameter ignores the previous settings */ + r = parse_boolean(rvalue); + if (r > 0) { + c->restrict_namespaces = 0; + return 0; + } else if (r == 0) { c->restrict_namespaces = NAMESPACE_FLAGS_ALL; return 0; } @@ -3161,23 +2917,19 @@ int config_parse_restrict_namespaces( rvalue++; } - r = parse_boolean(rvalue); - if (r > 0) - c->restrict_namespaces = 0; - else if (r == 0) - c->restrict_namespaces = NAMESPACE_FLAGS_ALL; - else { - /* Not a boolean argument, in this case it's a list of namespace types. */ - - r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue); - return 0; - } + /* Not a boolean argument, in this case it's a list of namespace types. */ + r = namespace_flags_from_string(rvalue, &flags); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue); + return 0; } - if (invert) - c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL; + if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL) + /* Initial assignment. Just set the value. */ + c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags; + else + /* Merge the value with the previous one. */ + SET_FLAG(c->restrict_namespaces, flags, !invert); return 0; } @@ -3195,6 +2947,7 @@ int config_parse_unit_slice( void *data, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *k = NULL; Unit *u = userdata, *slice = NULL; int r; @@ -3206,77 +2959,19 @@ int config_parse_unit_slice( r = unit_name_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); return 0; } - r = manager_load_unit(u->manager, k, NULL, NULL, &slice); + r = manager_load_unit(u->manager, k, NULL, &error, &slice); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r)); return 0; } r = unit_set_slice(u, slice); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id); - return 0; - } - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); - -int config_parse_cpu_weight( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - uint64_t *weight = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_weight_parse(rvalue, weight); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - -int config_parse_cpu_shares( - 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) { - - uint64_t *shares = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_cpu_shares_parse(rvalue, shares); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id); return 0; } @@ -3309,7 +3004,7 @@ int config_parse_cpu_quota( r = parse_percent_unbounded(rvalue); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue); return 0; } @@ -3339,14 +3034,15 @@ int config_parse_memory_limit( if (r < 0) { r = parse_size(rvalue, 1024, &bytes); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue); return 0; } } else bytes = physical_memory_scale(r, 100U); - if (bytes <= 0 || bytes >= UINT64_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue); + if (bytes >= UINT64_MAX || + (bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue); return 0; } } @@ -3397,14 +3093,14 @@ int config_parse_tasks_max( if (r < 0) { r = safe_atou64(rvalue, &v); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue); return 0; } } else v = system_tasks_max_scale(r, 100U); if (v <= 0 || v >= UINT64_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue); return 0; } @@ -3425,13 +3121,23 @@ int config_parse_delegate( void *userdata) { CGroupContext *c = data; + UnitType t; int r; + t = unit_name_to_type(unit); + assert(t != _UNIT_TYPE_INVALID); + + if (!unit_vtable[t]->can_delegate) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring."); + return 0; + } + /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the * mask to delegate. */ if (isempty(rvalue)) { + /* An empty string resets controllers and set Delegate=yes. */ c->delegate = true; c->delegate_controllers = 0; return 0; @@ -3453,12 +3159,12 @@ int config_parse_delegate( return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - return r; + return 0; } cc = cgroup_controller_from_string(word); if (cc < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", word); continue; } @@ -3491,11 +3197,10 @@ int config_parse_device_allow( void *data, void *userdata) { - _cleanup_free_ char *path = NULL, *t = NULL; + _cleanup_free_ char *path = NULL, *resolved = NULL; CGroupContext *c = data; CGroupDeviceAllow *a; - const char *m = NULL; - size_t n; + const char *p = rvalue; int r; if (isempty(rvalue)) { @@ -3505,31 +3210,41 @@ int config_parse_device_allow( return 0; } - r = unit_full_printf(userdata, rvalue, &t); + r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to resolve specifiers in %s, ignoring: %m", - rvalue); + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract device path and rights from '%s', ignoring.", rvalue); + return 0; } - n = strcspn(t, WHITESPACE); - - path = strndup(t, n); - if (!path) - return log_oom(); - - if (!is_deviceallow_pattern(path) && - !path_startswith(path, "/run/systemd/inaccessible/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + r = unit_full_printf(userdata, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", path); return 0; } - m = t + n + strspn(t + n, WHITESPACE); - if (isempty(m)) - m = "rwm"; + if (!startswith(resolved, "block-") && !startswith(resolved, "char-")) { + + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + if (!valid_device_node_path(resolved)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved); + return 0; + } + } - if (!in_charset(m, "rwm")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m); + if (!isempty(p) && !in_charset(p, "rwm")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s', ignoring.", p); return 0; } @@ -3537,44 +3252,15 @@ int config_parse_device_allow( if (!a) return log_oom(); - a->path = path; - path = NULL; - a->r = !!strchr(m, 'r'); - a->w = !!strchr(m, 'w'); - a->m = !!strchr(m, 'm'); + a->path = TAKE_PTR(resolved); + a->r = isempty(p) || !!strchr(p, 'r'); + a->w = isempty(p) || !!strchr(p, 'w'); + a->m = isempty(p) || !!strchr(p, 'm'); LIST_PREPEND(device_allow, c->device_allow, a); return 0; } -int config_parse_io_weight( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - uint64_t *weight = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_weight_parse(rvalue, weight); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - int config_parse_io_device_weight( const char *unit, const char *filename, @@ -3587,12 +3273,11 @@ int config_parse_io_device_weight( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *resolved = NULL; CGroupIODeviceWeight *w; CGroupContext *c = data; - const char *weight; + const char *p = rvalue; uint64_t u; - size_t n; int r; assert(filename); @@ -3606,28 +3291,34 @@ int config_parse_io_device_weight( return 0; } - n = strcspn(rvalue, WHITESPACE); - weight = rvalue + n; - weight += strspn(weight, WHITESPACE); - - if (isempty(weight)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); + r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract device path and weight from '%s', ignoring.", rvalue); return 0; } - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + r = unit_full_printf(userdata, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", path); return 0; } - r = cg_weight_parse(weight, &u); + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + r = cg_weight_parse(p, &u); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight); + log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p); return 0; } @@ -3637,9 +3328,7 @@ int config_parse_io_device_weight( if (!w) return log_oom(); - w->path = path; - path = NULL; - + w->path = TAKE_PTR(resolved); w->weight = u; LIST_PREPEND(device_weights, c->io_device_weights, w); @@ -3658,13 +3347,12 @@ int config_parse_io_limit( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *resolved = NULL; CGroupIODeviceLimit *l = NULL, *t; CGroupContext *c = data; CGroupIOLimitType type; - const char *limit; + const char *p = rvalue; uint64_t num; - size_t n; int r; assert(filename); @@ -3680,37 +3368,43 @@ int config_parse_io_limit( return 0; } - n = strcspn(rvalue, WHITESPACE); - limit = rvalue + n; - limit += strspn(limit, WHITESPACE); - - if (!*limit) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); + r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue); return 0; } - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + r = unit_full_printf(userdata, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", path); return 0; } - if (streq("infinity", limit)) { + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + if (streq("infinity", p)) num = CGROUP_LIMIT_MAX; - } else { - r = parse_size(limit, 1000, &num); + else { + r = parse_size(p, 1000, &num); if (r < 0 || num <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid IO limit '%s', ignoring.", p); return 0; } } LIST_FOREACH(device_limits, t, c->io_device_limits) { - if (path_equal(path, t->path)) { + if (path_equal(resolved, t->path)) { l = t; break; } @@ -3723,8 +3417,7 @@ int config_parse_io_limit( if (!l) return log_oom(); - l->path = path; - path = NULL; + l->path = TAKE_PTR(resolved); for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++) l->limits[ttype] = cgroup_io_limit_defaults[ttype]; @@ -3736,34 +3429,6 @@ int config_parse_io_limit( return 0; } -int config_parse_blockio_weight( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - uint64_t *weight = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_blkio_weight_parse(rvalue, weight); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - int config_parse_blockio_device_weight( const char *unit, const char *filename, @@ -3776,12 +3441,11 @@ int config_parse_blockio_device_weight( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *resolved = NULL; CGroupBlockIODeviceWeight *w; CGroupContext *c = data; - const char *weight; + const char *p = rvalue; uint64_t u; - size_t n; int r; assert(filename); @@ -3795,28 +3459,34 @@ int config_parse_blockio_device_weight( return 0; } - n = strcspn(rvalue, WHITESPACE); - weight = rvalue + n; - weight += strspn(weight, WHITESPACE); - - if (isempty(weight)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); + r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract device node and weight from '%s', ignoring.", rvalue); return 0; } - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + r = unit_full_printf(userdata, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", path); return 0; } - r = cg_blkio_weight_parse(weight, &u); + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + r = cg_blkio_weight_parse(p, &u); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p); return 0; } @@ -3826,9 +3496,7 @@ int config_parse_blockio_device_weight( if (!w) return log_oom(); - w->path = path; - path = NULL; - + w->path = TAKE_PTR(resolved); w->weight = u; LIST_PREPEND(device_weights, c->blockio_device_weights, w); @@ -3847,13 +3515,12 @@ int config_parse_blockio_bandwidth( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *resolved = NULL; CGroupBlockIODeviceBandwidth *b = NULL, *t; CGroupContext *c = data; - const char *bandwidth; + const char *p = rvalue; uint64_t bytes; bool read; - size_t n; int r; assert(filename); @@ -3870,33 +3537,39 @@ int config_parse_blockio_bandwidth( return 0; } - n = strcspn(rvalue, WHITESPACE); - bandwidth = rvalue + n; - bandwidth += strspn(bandwidth, WHITESPACE); - - if (!*bandwidth) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); + r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue); return 0; } - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev") && - !path_startswith(path, "/run/systemd/inaccessible/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + r = unit_full_printf(userdata, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", path); return 0; } - r = parse_size(bandwidth, 1000, &bytes); + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + r = parse_size(p, 1000, &bytes); if (r < 0 || bytes <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p); return 0; } LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { - if (path_equal(path, t->path)) { + if (path_equal(resolved, t->path)) { b = t; break; } @@ -3907,8 +3580,7 @@ int config_parse_blockio_bandwidth( if (!b) return log_oom(); - b->path = path; - path = NULL; + b->path = TAKE_PTR(resolved); b->rbps = CGROUP_LIMIT_MAX; b->wbps = CGROUP_LIMIT_MAX; @@ -3923,8 +3595,6 @@ int config_parse_blockio_bandwidth( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); - int config_parse_job_mode_isolate( const char *unit, const char *filename, @@ -3956,8 +3626,6 @@ int config_parse_job_mode_isolate( return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode"); - int config_parse_exec_directories( const char *unit, const char *filename, @@ -4003,13 +3671,17 @@ int config_parse_exec_directories( r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers in \"%s\", ignoring: %m", word); + "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word); continue; } - if (!path_is_normalized(k) || path_is_absolute(k)) { + r = path_simplify_and_warn(k, PATH_CHECK_RELATIVE, unit, filename, line, lvalue); + if (r < 0) + continue; + + if (path_startswith(k, "private")) { log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not valid, ignoring assignment: %s", lvalue, rvalue); + "%s= path can't be 'private', ingoring assignment: %s", lvalue, word); continue; } @@ -4059,7 +3731,7 @@ int config_parse_set_status( r = safe_atoi(temp, &val); if (r < 0) { - val = signal_from_string_try_harder(temp); + val = signal_from_string(temp); if (val <= 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word); @@ -4079,10 +3751,8 @@ int config_parse_set_status( return log_oom(); r = set_put(*set, INT_TO_PTR(val)); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word); - return r; - } + if (r < 0) + return log_oom(); } if (!isempty(state)) log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); @@ -4104,7 +3774,7 @@ int config_parse_namespace_path_strv( Unit *u = userdata; char*** sv = data; - const char *cur; + const char *p = rvalue; int r; assert(filename); @@ -4118,13 +3788,12 @@ int config_parse_namespace_path_strv( return 0; } - cur = rvalue; for (;;) { _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL; const char *w; bool ignore_enoent = false, shall_prefix = false; - r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES); + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == 0) break; if (r == -ENOMEM) @@ -4134,11 +3803,6 @@ int config_parse_namespace_path_strv( return 0; } - if (!utf8_is_valid(word)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word); - continue; - } - w = word; if (startswith(w, "-")) { ignore_enoent = true; @@ -4151,16 +3815,13 @@ int config_parse_namespace_path_strv( r = unit_full_printf(u, w, &resolved); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w); continue; } - if (!path_is_absolute(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved); + r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) continue; - } - - path_kill_slashes(resolved); joined = strjoin(ignore_enoent ? "-" : "", shall_prefix ? "+" : "", @@ -4190,7 +3851,7 @@ int config_parse_temporary_filesystems( Unit *u = userdata; ExecContext *c = data; - const char *cur; + const char *p = rvalue; int r; assert(filename); @@ -4206,14 +3867,13 @@ int config_parse_temporary_filesystems( return 0; } - cur = rvalue; for (;;) { _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL; const char *w; - r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES); + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == 0) - break; + return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) { @@ -4223,23 +3883,26 @@ int config_parse_temporary_filesystems( w = word; r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = unit_full_printf(u, path, &resolved); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s, ignoring: %m", word); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", word); + continue; + } + if (r == 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", word); continue; } - if (!path_is_absolute(resolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved); + r = unit_full_printf(u, path, &resolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path); continue; } - path_kill_slashes(resolved); + r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + continue; r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, path, w); if (r == -ENOMEM) @@ -4249,8 +3912,6 @@ int config_parse_temporary_filesystems( continue; } } - - return 0; } int config_parse_bind_paths( @@ -4296,15 +3957,15 @@ int config_parse_bind_paths( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue); return 0; } r = unit_full_printf(u, source, &sresolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolved specifiers in \"%s\", ignoring: %m", source); - return 0; + "Failed to resolved unit specifiers in \"%s\", ignoring: %m", source); + continue; } s = sresolved; @@ -4313,16 +3974,9 @@ int config_parse_bind_paths( s++; } - if (!utf8_is_valid(s)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s); - return 0; - } - if (!path_is_absolute(s)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s); - return 0; - } - - path_kill_slashes(s); + r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + continue; /* Optionally, the destination is specified. */ if (p && p[-1] == ':') { @@ -4330,31 +3984,26 @@ int config_parse_bind_paths( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue); return 0; } if (r == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':', ignoring: %s", s); + continue; } r = unit_full_printf(u, destination, &dresolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolved specifiers in \"%s\", ignoring: %m", destination); - return 0; + continue; } - if (!utf8_is_valid(dresolved)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved); - return 0; - } - if (!path_is_absolute(dresolved)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved); - return 0; - } + r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + continue; - d = path_kill_slashes(dresolved); + d = dresolved; /* Optionally, there's also a short option string specified */ if (p && p[-1] == ':') { @@ -4374,7 +4023,7 @@ int config_parse_bind_paths( rbind = false; else { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options); - return 0; + continue; } } } else @@ -4395,107 +4044,6 @@ int config_parse_bind_paths( return 0; } -int config_parse_no_new_privileges( - 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) { - - ExecContext *c = data; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); - return 0; - } - - c->no_new_privileges = k; - - return 0; -} - -int config_parse_protect_home( - 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) { - - ExecContext *c = data; - ProtectHome h; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* Our enum shall be a superset of booleans, hence first try - * to parse as boolean, and then as enum */ - - h = parse_protect_home_or_bool(rvalue); - if (h < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); - return 0; - } - - c->protect_home = h; - - return 0; -} - -int config_parse_protect_system( - 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) { - - ExecContext *c = data; - ProtectSystem s; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* Our enum shall be a superset of booleans, hence first try - * to parse as boolean, and then as enum */ - - s = parse_protect_system_or_bool(rvalue); - if (s < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); - return 0; - } - - c->protect_system = s; - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode"); - int config_parse_job_timeout_sec( const char* unit, const char *filename, @@ -4590,7 +4138,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { if (c++ >= FOLLOW_MAX) return -ELOOP; - path_kill_slashes(*filename); + path_simplify(*filename, false); /* Add the file name we are currently looking at to * the names of this unit, but only if it is a valid @@ -4623,8 +4171,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { if (r < 0) return r; - free(*filename); - *filename = target; + free_and_replace(*filename, target); } f = fdopen(fd, "re"); @@ -4798,9 +4345,7 @@ static int load_from_path(Unit *u, const char *path) { return r; } - free(u->fragment_path); - u->fragment_path = filename; - filename = NULL; + free_and_replace(u->fragment_path, filename); if (u->source_path) { if (stat(u->source_path, &st) >= 0) @@ -4907,9 +4452,7 @@ void unit_dump_config_items(FILE *f) { const ConfigParserCallback callback; const char *rvalue; } table[] = { -#if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR { config_parse_warn_compat, "NOTSUPPORTED" }, -#endif { config_parse_int, "INTEGER" }, { config_parse_unsigned, "UNSIGNED" }, { config_parse_iec_size, "SIZE" }, @@ -4935,14 +4478,11 @@ void unit_dump_config_items(FILE *f) { { config_parse_log_level, "LEVEL" }, { config_parse_exec_secure_bits, "SECUREBITS" }, { config_parse_capability_set, "BOUNDINGSET" }, - { config_parse_limit, "LIMIT" }, + { config_parse_rlimit, "LIMIT" }, { config_parse_unit_deps, "UNIT [...]" }, { config_parse_exec, "PATH [ARGUMENT [...]]" }, { config_parse_service_type, "SERVICETYPE" }, { config_parse_service_restart, "SERVICERESTART" }, -#if HAVE_SYSV_COMPAT - { config_parse_sysv_priority, "SYSVPRIORITY" }, -#endif { config_parse_kill_mode, "KILLMODE" }, { config_parse_signal, "SIGNAL" }, { config_parse_socket_listen, "SOCKET [...]" }, @@ -4978,12 +4518,11 @@ void unit_dump_config_items(FILE *f) { { config_parse_restrict_namespaces, "NAMESPACES" }, #endif { config_parse_cpu_shares, "SHARES" }, - { config_parse_cpu_weight, "WEIGHT" }, + { config_parse_cg_weight, "WEIGHT" }, { config_parse_memory_limit, "LIMIT" }, { config_parse_device_allow, "DEVICE" }, { config_parse_device_policy, "POLICY" }, { config_parse_io_limit, "LIMIT" }, - { config_parse_io_weight, "WEIGHT" }, { config_parse_io_device_weight, "DEVICEWEIGHT" }, { config_parse_blockio_bandwidth, "BANDWIDTH" }, { config_parse_blockio_weight, "WEIGHT" }, @@ -5005,13 +4544,24 @@ void unit_dump_config_items(FILE *f) { NULSTR_FOREACH(i, load_fragment_gperf_nulstr) { const char *rvalue = "OTHER", *lvalue; - unsigned j; + const ConfigPerfItem *p; size_t prefix_len; const char *dot; - const ConfigPerfItem *p; + unsigned j; assert_se(p = load_fragment_gperf_lookup(i, strlen(i))); + /* Hide legacy settings */ + if (p->parse == config_parse_warn_compat && + p->ltype == DISABLED_LEGACY) + continue; + + for (j = 0; j < ELEMENTSOF(table); j++) + if (p->parse == table[j].callback) { + rvalue = table[j].rvalue; + break; + } + dot = strchr(i, '.'); lvalue = dot ? dot + 1 : i; prefix_len = dot-i; @@ -5024,12 +4574,6 @@ void unit_dump_config_items(FILE *f) { fprintf(f, "[%.*s]\n", (int) prefix_len, i); } - for (j = 0; j < ELEMENTSOF(table); j++) - if (p->parse == table[j].callback) { - rvalue = table[j].rvalue; - break; - } - fprintf(f, "%s=%s\n", lvalue, rvalue); prev = i; } diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 163b5ce485..dad281ef72 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -1,25 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "conf-parser.h" #include "unit.h" /* Read service data from .desktop file style configuration fragments */ @@ -28,110 +10,99 @@ int unit_load_fragment(Unit *u); void unit_dump_config_items(FILE *f); -int config_parse_warn_compat(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_unit_deps(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_obsolete_unit_deps(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_unit_string_printf(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_unit_strv_printf(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_unit_path_printf(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_unit_path_strv_printf(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_documentation(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_socket_listen(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_socket_protocol(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_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec(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_service_timeout(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_service_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_restart(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_socket_bindtodevice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_output(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_input(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_input_text(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_input_data(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_io_class(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_io_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_secure_bits(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_capability_set(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_limit(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_sysv_priority(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_kill_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_exec_mount_flags(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_timer(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_trigger_unit(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_path_spec(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_socket_service(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_service_sockets(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_unit_env_file(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_ip_tos(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_unit_condition_path(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_unit_condition_string(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_unit_condition_null(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_kill_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_notify_access(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_emergency_action(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_unit_requires_mounts_for(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_syscall_filter(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_syscall_archs(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_syscall_errno(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_environ(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_pass_environ(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_unset_environ(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_unit_slice(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_cpu_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_cpu_shares(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_memory_limit(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_tasks_max(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_delegate(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_device_policy(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_device_allow(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_io_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_io_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_io_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_netclass(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_smack_process_label(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_address_families(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_runtime_preserve_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_directories(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_set_status(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_namespace_path_strv(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_temporary_filesystems(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_no_new_privileges(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_cpu_quota(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_protect_home(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_protect_system(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_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_working_directory(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_fdname(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_sec_fix_0(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_user_group(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_user_group_strv(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_restrict_namespaces(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_bind_paths(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_keyring_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_job_timeout_sec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_job_running_timeout_sec(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_log_extra_fields(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_collect_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_deps); +CONFIG_PARSER_PROTOTYPE(config_parse_obsolete_unit_deps); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_string_printf); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_strv_printf); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_path_printf); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_path_strv_printf); +CONFIG_PARSER_PROTOTYPE(config_parse_documentation); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_listen); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_protocol); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_bind); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_nice); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_oom_score_adjust); +CONFIG_PARSER_PROTOTYPE(config_parse_exec); +CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout); +CONFIG_PARSER_PROTOTYPE(config_parse_service_type); +CONFIG_PARSER_PROTOTYPE(config_parse_service_restart); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_output); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_input); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_input_text); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_input_data); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_io_class); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_io_priority); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits); +CONFIG_PARSER_PROTOTYPE(config_parse_capability_set); +CONFIG_PARSER_PROTOTYPE(config_parse_kill_signal); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags); +CONFIG_PARSER_PROTOTYPE(config_parse_timer); +CONFIG_PARSER_PROTOTYPE(config_parse_trigger_unit); +CONFIG_PARSER_PROTOTYPE(config_parse_path_spec); +CONFIG_PARSER_PROTOTYPE(config_parse_socket_service); +CONFIG_PARSER_PROTOTYPE(config_parse_service_sockets); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_env_file); +CONFIG_PARSER_PROTOTYPE(config_parse_ip_tos); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_path); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_string); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_null); +CONFIG_PARSER_PROTOTYPE(config_parse_kill_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_notify_access); +CONFIG_PARSER_PROTOTYPE(config_parse_emergency_action); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_requires_mounts_for); +CONFIG_PARSER_PROTOTYPE(config_parse_syscall_filter); +CONFIG_PARSER_PROTOTYPE(config_parse_syscall_archs); +CONFIG_PARSER_PROTOTYPE(config_parse_syscall_errno); +CONFIG_PARSER_PROTOTYPE(config_parse_environ); +CONFIG_PARSER_PROTOTYPE(config_parse_pass_environ); +CONFIG_PARSER_PROTOTYPE(config_parse_unset_environ); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_slice); +CONFIG_PARSER_PROTOTYPE(config_parse_cg_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_cpu_shares); +CONFIG_PARSER_PROTOTYPE(config_parse_memory_limit); +CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max); +CONFIG_PARSER_PROTOTYPE(config_parse_delegate); +CONFIG_PARSER_PROTOTYPE(config_parse_device_policy); +CONFIG_PARSER_PROTOTYPE(config_parse_device_allow); +CONFIG_PARSER_PROTOTYPE(config_parse_io_device_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_io_limit); +CONFIG_PARSER_PROTOTYPE(config_parse_blockio_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_blockio_device_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_blockio_bandwidth); +CONFIG_PARSER_PROTOTYPE(config_parse_netclass); +CONFIG_PARSER_PROTOTYPE(config_parse_job_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_job_mode_isolate); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_selinux_context); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_apparmor_profile); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_smack_process_label); +CONFIG_PARSER_PROTOTYPE(config_parse_address_families); +CONFIG_PARSER_PROTOTYPE(config_parse_runtime_preserve_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories); +CONFIG_PARSER_PROTOTYPE(config_parse_set_status); +CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv); +CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems); +CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota); +CONFIG_PARSER_PROTOTYPE(config_parse_protect_home); +CONFIG_PARSER_PROTOTYPE(config_parse_protect_system); +CONFIG_PARSER_PROTOTYPE(config_parse_bus_name); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_working_directory); +CONFIG_PARSER_PROTOTYPE(config_parse_fdname); +CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0); +CONFIG_PARSER_PROTOTYPE(config_parse_user_group); +CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv); +CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces); +CONFIG_PARSER_PROTOTYPE(config_parse_bind_paths); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec); +CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec); +CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields); +CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); extern const char load_fragment_gperf_nulstr[]; - -typedef enum Disabled { - DISABLED_CONFIGURATION, - DISABLED_LEGACY, - DISABLED_EXPERIMENTAL, -} Disabled; diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index 0c43cf2418..c14523fee9 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <stdlib.h> @@ -37,7 +19,7 @@ int locale_setup(char ***environment) { int r = 0, i; if (detect_container() <= 0) { - r = parse_env_file("/proc/cmdline", WHITESPACE, + r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE, "locale.LANG", &variables[VARIABLE_LANG], "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], @@ -61,7 +43,7 @@ int locale_setup(char ***environment) { /* Hmm, nothing set on the kernel cmd line? Then let's * try /etc/locale.conf */ if (r <= 0) { - r = parse_env_file("/etc/locale.conf", NEWLINE, + r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE, "LANG", &variables[VARIABLE_LANG], "LANGUAGE", &variables[VARIABLE_LANGUAGE], "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], @@ -110,8 +92,7 @@ int locale_setup(char ***environment) { goto finish; } - strv_free(*environment); - *environment = e; + strv_free_and_replace(*environment, e); } r = 0; diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h index c1bee3812b..01fadd06c7 100644 --- a/src/core/locale-setup.h +++ b/src/core/locale-setup.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - int locale_setup(char ***environment); diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 1528034e81..835553ec8f 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <net/if.h> #include <stdlib.h> diff --git a/src/core/loopback-setup.h b/src/core/loopback-setup.h index 9fac0189e4..c0eea100ed 100644 --- a/src/core/loopback-setup.h +++ b/src/core/loopback-setup.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - int loopback_setup(void); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 1b7424800c..11528f83c4 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <fcntl.h> #include <sched.h> diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h index cbd83ba270..d6ac62a882 100644 --- a/src/core/machine-id-setup.h +++ b/src/core/machine-id-setup.h @@ -1,24 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - int machine_id_commit(const char *root); int machine_id_setup(const char *root, sd_id128_t requested, sd_id128_t *ret); diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 4e27e1b06a..f3b74f4273 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -4,19 +4,6 @@ # 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/>. # RPM macros for packages installing systemd unit files @@ -27,10 +14,13 @@ %_udevhwdbdir @udevhwdbdir@ %_udevrulesdir @udevrulesdir@ %_journalcatalogdir @catalogdir@ -%_tmpfilesdir @tmpfilesdir@ -%_sysusersdir @sysusersdir@ -%_sysctldir @sysctldir@ %_binfmtdir @binfmtdir@ +%_sysctldir @sysctldir@ +%_sysusersdir @sysusersdir@ +%_tmpfilesdir @tmpfilesdir@ +%_environmnentdir @environmentdir@ +%_modulesloaddir @modulesloaddir@ +%_modprobedir @modprobedir@ %_systemdgeneratordir @systemgeneratordir@ %_systemdusergeneratordir @usergeneratordir@ %_systemd_system_env_generator_dir @systemenvgeneratordir@ @@ -51,23 +41,23 @@ OrderWithRequires(postun): systemd \ %systemd_post() \ if [ $1 -eq 1 ] ; then \ # Initial installation \ - systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \ + systemctl --no-reload preset %{?*} &>/dev/null || : \ fi \ %{nil} -%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}} +%systemd_user_post() %{expand:%systemd_post \\--global %%{?*}} %systemd_preun() \ if [ $1 -eq 0 ] ; then \ # Package removal, not upgrade \ - systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \ + systemctl --no-reload disable --now %{?*} &>/dev/null || : \ fi \ %{nil} %systemd_user_preun() \ if [ $1 -eq 0 ] ; then \ # Package removal, not upgrade \ - systemctl --no-reload --user --global disable %{?*} > /dev/null 2>&1 || : \ + systemctl --global disable %{?*} &>/dev/null || : \ fi \ %{nil} @@ -78,7 +68,7 @@ fi \ %systemd_postun_with_restart() \ if [ $1 -ge 1 ] ; then \ # Package upgrade, not uninstall \ - systemctl try-restart %{?*} >/dev/null 2>&1 || : \ + systemctl try-restart %{?*} &>/dev/null || : \ fi \ %{nil} @@ -92,16 +82,18 @@ fi \ # Deprecated. Use %tmpfiles_create_package instead %tmpfiles_create() \ -systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \ +systemd-tmpfiles --create %{?*} &>/dev/null || : \ %{nil} # Deprecated. Use %sysusers_create_package instead %sysusers_create() \ -systemd-sysusers %{?*} >/dev/null 2>&1 || : \ +systemd-sysusers %{?*} &>/dev/null || : \ %{nil} %sysusers_create_inline() \ -echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : \ +systemd-sysusers - <<SYSTEMD_INLINE_EOF &>/dev/null || : \ +%{?*} \ +SYSTEMD_INLINE_EOF \ %{nil} # This should be used by package installation scripts which require users or @@ -118,7 +110,9 @@ echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : \ # %files # %{_sysusersdir}/%{name}.conf %sysusers_create_package() \ -echo "%(cat %2)" | systemd-sysusers --replace=%_sysusersdir/%1.conf - >/dev/null 2>&1 || : \ +systemd-sysusers --replace=%_sysusersdir/%1.conf - <<SYSTEMD_INLINE_EOF &>/dev/null || : \ +%(cat %2) \ +SYSTEMD_INLINE_EOF \ %{nil} # This may be used by package installation scripts to create files according to @@ -135,13 +129,15 @@ echo "%(cat %2)" | systemd-sysusers --replace=%_sysusersdir/%1.conf - >/dev/null # %files # %{_tmpfilesdir}/%{name}.conf %tmpfiles_create_package() \ -echo "%(cat %2)" | systemd-tmpfiles --replace=%_tmpfilesdir/%1.conf --create - >/dev/null 2>&1 || : \ +systemd-tmpfiles --replace=%_tmpfilesdir/%1.conf --create - <<SYSTEMD_INLINE_EOF &>/dev/null || : \ +%(cat %2) \ +SYSTEMD_INLINE_EOF \ %{nil} %sysctl_apply() \ -@rootlibexecdir@/systemd-sysctl %{?*} >/dev/null 2>&1 || : \ +@rootlibexecdir@/systemd-sysctl %{?*} &>/dev/null || : \ %{nil} %binfmt_apply() \ -@rootlibexecdir@/systemd-binfmt %{?*} >/dev/null 2>&1 || : \ +@rootlibexecdir@/systemd-binfmt %{?*} &>/dev/null || : \ %{nil} diff --git a/src/core/main.c b/src/core/main.c index 076846a41c..44dd8348be 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <fcntl.h> @@ -49,10 +31,12 @@ #include "clock-util.h" #include "conf-parser.h" #include "cpu-set-util.h" +#include "dbus.h" #include "dbus-manager.h" #include "def.h" #include "emergency-action.h" #include "env-util.h" +#include "exit-status.h" #include "fd-util.h" #include "fdset.h" #include "fileio.h" @@ -69,6 +53,7 @@ #include "manager.h" #include "missing.h" #include "mount-setup.h" +#include "os-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -100,7 +85,8 @@ static enum { ACTION_HELP, ACTION_VERSION, ACTION_TEST, - ACTION_DUMP_CONFIGURATION_ITEMS + ACTION_DUMP_CONFIGURATION_ITEMS, + ACTION_DUMP_BUS_PROPERTIES, } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; static bool arg_system = false; @@ -127,6 +113,7 @@ static char *arg_watchdog_device = NULL; static char **arg_default_environment = NULL; static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {}; static uint64_t arg_capability_bounding_set = CAP_ALL; +static bool arg_no_new_privs = false; static nsec_t arg_timer_slack_nsec = NSEC_INFINITY; static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE; static Set* arg_syscall_archs = NULL; @@ -141,7 +128,7 @@ static uint64_t arg_default_tasks_max = UINT64_MAX; static sd_id128_t arg_machine_id = {}; static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; -noreturn static void freeze_or_reboot(void) { +_noreturn_ static void freeze_or_reboot(void) { if (arg_crash_reboot) { log_notice("Rebooting in 10s..."); @@ -156,7 +143,7 @@ noreturn static void freeze_or_reboot(void) { freeze(); } -noreturn static void crash(int sig) { +_noreturn_ static void crash(int sig) { struct sigaction sa; pid_t pid; @@ -319,6 +306,7 @@ static int parse_confirm_spawn(const char *value, char **console) { s = strjoin("/dev/", value); if (!s) return -ENOMEM; + *console = s; return 0; } @@ -532,10 +520,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; \ } -DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level") -DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target") -DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ) -DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location") +DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level"); +DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target"); +DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ); +DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location"); static int config_parse_cpu_affinity2( const char *unit, @@ -671,6 +659,7 @@ static int parse_config_file(void) { { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog }, { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device }, { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set }, + { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs }, #if HAVE_SECCOMP { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, #endif @@ -685,22 +674,22 @@ static int parse_config_file(void) { { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, - { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit }, - { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit }, - { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit }, - { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit }, - { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit }, - { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit }, - { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit }, - { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit }, - { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit }, - { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit }, - { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit }, - { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit }, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit }, - { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit }, - { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit }, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit }, + { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit }, + { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit }, + { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit }, + { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit }, + { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit }, + { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit }, + { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit }, + { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit }, + { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit }, + { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit }, + { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit }, + { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit }, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit }, + { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit }, + { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit }, + { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit }, { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting }, { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting }, @@ -738,6 +727,10 @@ static void set_manager_defaults(Manager *m) { assert(m); + /* Propagates the various default unit property settings into the manager object, i.e. properties that do not + * affect the manager itself, but are just what newly allocated units will have set if they haven't set + * anything else. (Also see set_manager_settings() for the settings that affect the manager's own behaviour) */ + m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec; m->default_std_output = arg_default_std_output; m->default_std_error = arg_default_std_error; @@ -762,6 +755,9 @@ static void set_manager_settings(Manager *m) { assert(m); + /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager + * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */ + m->confirm_spawn = arg_confirm_spawn; m->service_watchdogs = arg_service_watchdogs; m->runtime_watchdog = arg_runtime_watchdog; @@ -772,7 +768,6 @@ static void set_manager_settings(Manager *m) { } static int parse_argv(int argc, char *argv[]) { - enum { ARG_LOG_LEVEL = 0x100, ARG_LOG_TARGET, @@ -785,6 +780,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_PAGER, ARG_VERSION, ARG_DUMP_CONFIGURATION_ITEMS, + ARG_DUMP_BUS_PROPERTIES, ARG_DUMP_CORE, ARG_CRASH_CHVT, ARG_CRASH_SHELL, @@ -812,6 +808,7 @@ static int parse_argv(int argc, char *argv[]) { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, + { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, @@ -931,6 +928,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; break; + case ARG_DUMP_BUS_PROPERTIES: + arg_action = ACTION_DUMP_BUS_PROPERTIES; + break; + case ARG_DUMP_CORE: if (!optarg) arg_dump_core = true; @@ -1074,6 +1075,7 @@ static int help(void) { " --test Determine startup sequence, dump it and exit\n" " --no-pager Do not pipe output into a pager\n" " --dump-configuration-items Dump understood unit configuration items\n" + " --dump-bus-properties Dump exposed bus properties\n" " --unit=UNIT Set default unit\n" " --system Run a system instance, even if PID != 1\n" " --user Run a user instance\n" @@ -1130,20 +1132,14 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching if (r < 0) return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); - *_f = f; - *_fds = fds; - - f = NULL; - fds = NULL; + *_f = TAKE_PTR(f); + *_fds = TAKE_PTR(fds); return 0; } static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { - struct rlimit nl; - int r; - int min_max; - _cleanup_free_ char *nr_open = NULL; + int r, nr; assert(saved_rlimit); @@ -1164,17 +1160,9 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { arg_default_rlimit[RLIMIT_NOFILE] = rl; } - /* Get current RLIMIT_NOFILE maximum compiled into the kernel. */ - r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); - if (r >= 0) - r = safe_atoi(nr_open, &min_max); - /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ - if (r < 0) - min_max = 1024 * 1024; - - /* Bump up the resource limit for ourselves substantially */ - nl.rlim_cur = nl.rlim_max = min_max; - r = setrlimit_closest(RLIMIT_NOFILE, &nl); + /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows */ + nr = read_nr_open(); + r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(nr)); if (r < 0) return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m"); @@ -1229,23 +1217,18 @@ static int enforce_syscall_archs(Set *archs) { static int status_welcome(void) { _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; - const char *fn; int r; if (arg_show_status <= 0) return 0; - FOREACH_STRING(fn, "/etc/os-release", "/usr/lib/os-release") { - r = parse_env_file(fn, NEWLINE, - "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color, - NULL); - - if (r != -ENOENT) - break; - } - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file, ignoring: %m"); + r = parse_os_release(NULL, + "PRETTY_NAME", &pretty_name, + "ANSI_COLOR", &ansi_color, + NULL); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); if (log_get_show_color()) return status_printf(NULL, false, false, @@ -1279,10 +1262,9 @@ static int bump_unix_max_dgram_qlen(void) { unsigned long v; int r; - /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel - * default of 16 is simply too low. We set the value really - * really early during boot, so that it is actually applied to - * all our sockets, including the $NOTIFY_SOCKET one. */ + /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel default of 16 is simply too low. We set the value + * really really early during boot, so that it is actually applied to all our sockets, including the + * $NOTIFY_SOCKET one. */ r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen); if (r < 0) @@ -1290,16 +1272,12 @@ static int bump_unix_max_dgram_qlen(void) { r = safe_atolu(qlen, &v); if (r < 0) - return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m"); + return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length '%s', ignoring: %m", qlen); if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN) return 0; - qlen = mfree(qlen); - if (asprintf(&qlen, "%lu\n", DEFAULT_UNIX_MAX_DGRAM_QLEN) < 0) - return log_oom(); - - r = write_string_file("/proc/sys/net/unix/max_dgram_qlen", qlen, 0); + r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", 0, "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN); if (r < 0) return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump AF_UNIX datagram queue length, ignoring: %m"); @@ -1487,7 +1465,7 @@ static void initialize_clock(void) { } static void initialize_coredump(bool skip_setup) { - +#if ENABLE_COREDUMP if (getpid_cached() != 1) return; @@ -1500,6 +1478,7 @@ static void initialize_coredump(bool skip_setup) { * until the systemd-coredump tool is enabled via sysctl. */ if (!skip_setup) disable_coredumps(); +#endif } static void do_reexecute( @@ -1656,20 +1635,35 @@ static int invoke_main_loop( switch (m->exit_code) { - case MANAGER_RELOAD: + case MANAGER_RELOAD: { + LogTarget saved_log_target; + int saved_log_level; + log_info("Reloading."); + /* First, save any overridden log level/target, then parse the configuration file, which might + * change the log level to new settings. */ + + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + r = parse_config_file(); if (r < 0) log_warning_errno(r, "Failed to parse config file, ignoring: %m"); set_manager_defaults(m); + if (saved_log_level >= 0) + manager_override_log_level(m, saved_log_level); + if (saved_log_target >= 0) + manager_override_log_target(m, saved_log_target); + r = manager_reload(m); if (r < 0) log_warning_errno(r, "Failed to reload, ignoring: %m"); break; + } case MANAGER_REEXECUTE: @@ -1865,6 +1859,13 @@ static int initialize_runtime( } } + if (arg_system && arg_no_new_privs) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + *ret_error_message = "Failed to disable new privileges"; + return log_emergency_errno(errno, "Failed to disable new privileges: %m"); + } + } + if (arg_syscall_archs) { r = enforce_syscall_archs(arg_syscall_archs); if (r < 0) { @@ -1898,28 +1899,15 @@ static int do_queue_default_job( log_debug("Activating default unit: %s", arg_default_unit); - r = manager_load_unit(m, arg_default_unit, NULL, &error, &target); - if (r < 0) - log_error("Failed to load default target: %s", bus_error_message(&error, r)); - else if (IN_SET(target->load_state, UNIT_ERROR, UNIT_NOT_FOUND)) - log_error_errno(target->load_error, "Failed to load default target: %m"); - else if (target->load_state == UNIT_MASKED) - log_error("Default target masked."); - - if (!target || target->load_state != UNIT_LOADED) { - log_info("Trying to load rescue target..."); + r = manager_load_startable_unit_or_warn(m, arg_default_unit, NULL, &target); + if (r < 0) { + log_info("Falling back to rescue target: " SPECIAL_RESCUE_TARGET); - r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); + r = manager_load_startable_unit_or_warn(m, SPECIAL_RESCUE_TARGET, NULL, &target); if (r < 0) { - *ret_error_message = "Failed to load rescue target"; - return log_emergency_errno(r, "Failed to load rescue target: %s", bus_error_message(&error, r)); - } else if (IN_SET(target->load_state, UNIT_ERROR, UNIT_NOT_FOUND)) { - *ret_error_message = "Failed to load rescue target"; - return log_emergency_errno(target->load_error, "Failed to load rescue target: %m"); - } else if (target->load_state == UNIT_MASKED) { - *ret_error_message = "Rescue target masked"; - log_emergency("Rescue target masked."); - return -ERFKILL; + *ret_error_message = r == -ERFKILL ? "Rescue target masked" + : "Failed to load rescue target"; + return r; } } @@ -1948,12 +1936,9 @@ static int do_queue_default_job( } static void free_arguments(void) { - size_t j; /* Frees all arg_* variables, with the exception of arg_serialization */ - - for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) - arg_default_rlimit[j] = mfree(arg_default_rlimit[j]); + rlimit_free_all(arg_default_rlimit); arg_default_unit = mfree(arg_default_unit); arg_confirm_spawn = mfree(arg_confirm_spawn); @@ -2150,7 +2135,6 @@ static bool early_skip_setup_check(int argc, char *argv[]) { * anyway, even if in that case we also do deserialization. */ for (i = 1; i < argc; i++) { - if (streq(argv[i], "--switched-root")) return false; /* If we switched root, don't skip the setup. */ else if (streq(argv[i], "--deserialize")) @@ -2199,11 +2183,14 @@ int main(int argc, char *argv[]) { log_set_upgrade_syslog_to_journal(true); if (getpid_cached() == 1) { + /* When we run as PID 1 force system mode */ + arg_system = true; + /* Disable the umask logic */ umask(0); - /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be activated - * yet (even though the log socket for it exists). */ + /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be + * activated yet (even though the log socket for it exists). */ log_set_prohibit_ipc(true); /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is @@ -2211,62 +2198,68 @@ int main(int argc, char *argv[]) { * child process right before execve()'ing the actual binary, at a point in time where socket * activation stderr/stdout area already set up. */ log_set_always_reopen_console(true); - } - if (getpid_cached() == 1 && detect_container() <= 0) { + if (detect_container() <= 0) { - /* Running outside of a container as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_KMSG); - log_open(); + /* Running outside of a container as PID 1 */ + log_set_target(LOG_TARGET_KMSG); + log_open(); - if (in_initrd()) - initrd_timestamp = userspace_timestamp; + if (in_initrd()) + initrd_timestamp = userspace_timestamp; - if (!skip_setup) { - r = mount_setup_early(); - if (r < 0) { - error_message = "Failed to mount early API filesystems"; - goto finish; + if (!skip_setup) { + r = mount_setup_early(); + if (r < 0) { + error_message = "Failed to mount early API filesystems"; + goto finish; + } + + r = initialize_security( + &loaded_policy, + &security_start_timestamp, + &security_finish_timestamp, + &error_message); + if (r < 0) + goto finish; } - r = initialize_security( - &loaded_policy, - &security_start_timestamp, - &security_finish_timestamp, - &error_message); - if (r < 0) + if (mac_selinux_init() < 0) { + error_message = "Failed to initialize SELinux policy"; goto finish; - } + } - if (mac_selinux_init() < 0) { - error_message = "Failed to initialize SELinux policy"; - goto finish; + if (!skip_setup) + initialize_clock(); + + /* Set the default for later on, but don't actually open the logs like this for now. Note that + * if we are transitioning from the initrd there might still be journal fd open, and we + * shouldn't attempt opening that before we parsed /proc/cmdline which might redirect output + * elsewhere. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + + } else { + /* Running inside a container, as PID 1 */ + log_set_target(LOG_TARGET_CONSOLE); + log_open(); + + /* For later on, see above... */ + log_set_target(LOG_TARGET_JOURNAL); + + /* clear the kernel timestamp, + * because we are in a container */ + kernel_timestamp = DUAL_TIMESTAMP_NULL; } - if (!skip_setup) - initialize_clock(); - - /* Set the default for later on, but don't actually - * open the logs like this for now. Note that if we - * are transitioning from the initrd there might still - * be journal fd open, and we shouldn't attempt - * opening that before we parsed /proc/cmdline which - * might redirect output elsewhere. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - - } else if (getpid_cached() == 1) { - /* Running inside a container, as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_CONSOLE); - log_open(); + initialize_coredump(skip_setup); - /* For later on, see above... */ - log_set_target(LOG_TARGET_JOURNAL); + r = fixup_environment(); + if (r < 0) { + log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); + error_message = "Failed to fix up PID1 environment"; + goto finish; + } - /* clear the kernel timestamp, - * because we are in a container */ - kernel_timestamp = DUAL_TIMESTAMP_NULL; } else { /* Running as user instance */ arg_system = false; @@ -2278,24 +2271,14 @@ int main(int argc, char *argv[]) { kernel_timestamp = DUAL_TIMESTAMP_NULL; } - initialize_coredump(skip_setup); - - r = fixup_environment(); - if (r < 0) { - log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); - error_message = "Failed to fix up PID1 environment"; - goto finish; - } - if (arg_system) { - - /* Try to figure out if we can use colors with the console. No - * need to do that for user instances since they never log - * into the console. */ + /* Try to figure out if we can use colors with the console. No need to do that for user instances since + * they never log into the console. */ log_show_color(colors_enabled()); + r = make_null_stdio(); if (r < 0) - log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); + log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m"); } /* Mount /proc, /sys and friends, so that /proc/cmdline and @@ -2325,8 +2308,8 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS)) - pager_open(arg_no_pager, false); + if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES)) + (void) pager_open(arg_no_pager, false); if (arg_action != ACTION_RUN) skip_setup = true; @@ -2341,6 +2324,10 @@ int main(int argc, char *argv[]) { unit_dump_config_items(stdout); retval = EXIT_SUCCESS; goto finish; + } else if (arg_action == ACTION_DUMP_BUS_PROPERTIES) { + dump_bus_properties(stdout); + retval = EXIT_SUCCESS; + goto finish; } assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST)); @@ -2439,10 +2426,10 @@ int main(int argc, char *argv[]) { finish: pager_close(); - if (m) + if (m) { arg_shutdown_watchdog = m->shutdown_watchdog; - - m = manager_free(m); + m = manager_free(m); + } free_arguments(); mac_selinux_finish(); @@ -2469,13 +2456,12 @@ finish: * in become_shutdown() so normally we cannot free them yet. */ watchdog_free_device(); arg_watchdog_device = mfree(arg_watchdog_device); - return 0; + return retval; } #endif if (shutdown_verb) { r = become_shutdown(shutdown_verb, retval); - log_error_errno(r, "Failed to execute shutdown binary, %s: %m", getpid_cached() == 1 ? "freezing" : "quitting"); error_message = "Failed to execute shutdown binary"; } diff --git a/src/core/manager.c b/src/core/manager.c index 84adb9c666..930df4e23a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <fcntl.h> @@ -41,6 +23,7 @@ #include "sd-path.h" #include "alloc-util.h" +#include "all-units.h" #include "audit-fd.h" #include "boot-timestamps.h" #include "bus-common-errors.h" @@ -76,14 +59,17 @@ #include "path-util.h" #include "process-util.h" #include "ratelimit.h" +#include "rlimit-util.h" #include "rm-rf.h" #include "signal-util.h" +#include "socket-util.h" #include "special.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "strxcpyx.h" +#include "syslog-util.h" #include "terminal-util.h" #include "time-util.h" #include "transaction.h" @@ -118,6 +104,7 @@ static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_dispatch_sigchld(sd_event_source *source, void *userdata); +static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata); static int manager_run_environment_generators(Manager *m); static int manager_run_generators(Manager *m); @@ -362,35 +349,27 @@ static void manager_close_idle_pipe(Manager *m) { static int manager_setup_time_change(Manager *m) { int r; - /* We only care for the cancellation event, hence we set the - * timeout to the latest possible value. */ - struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - assert(m); - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); if (m->test_run_flags) return 0; - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever - * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); + m->time_change_fd = safe_close(m->time_change_fd); - m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + m->time_change_fd = time_change_fd(); if (m->time_change_fd < 0) - return log_error_errno(errno, "Failed to create timerfd: %m"); - - if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { - log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); - m->time_change_fd = safe_close(m->time_change_fd); - return 0; - } + return log_error_errno(m->time_change_fd, "Failed to create timer change timer fd: %m"); r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); if (r < 0) return log_error_errno(r, "Failed to create time change event source: %m"); + /* Schedule this slightly earlier than the .timer event sources */ + r = sd_event_source_set_priority(m->time_change_event_source, SD_EVENT_PRIORITY_NORMAL-1); + if (r < 0) + return log_error_errno(r, "Failed to set priority of time change event sources: %m"); + (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change"); log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); @@ -398,6 +377,70 @@ static int manager_setup_time_change(Manager *m) { return 0; } +static int manager_read_timezone_stat(Manager *m) { + struct stat st; + bool changed; + + assert(m); + + /* Read the current stat() data of /etc/localtime so that we detect changes */ + if (lstat("/etc/localtime", &st) < 0) { + log_debug_errno(errno, "Failed to stat /etc/localtime, ignoring: %m"); + changed = m->etc_localtime_accessible; + m->etc_localtime_accessible = false; + } else { + usec_t k; + + k = timespec_load(&st.st_mtim); + changed = !m->etc_localtime_accessible || k != m->etc_localtime_mtime; + + m->etc_localtime_mtime = k; + m->etc_localtime_accessible = true; + } + + return changed; +} + +static int manager_setup_timezone_change(Manager *m) { + _cleanup_(sd_event_source_unrefp) sd_event_source *new_event = NULL; + int r; + + assert(m); + + if (m->test_run_flags != 0) + return 0; + + /* We watch /etc/localtime for three events: change of the link count (which might mean removal from /etc even + * though another link might be kept), renames, and file close operations after writing. Note we don't bother + * with IN_DELETE_SELF, as that would just report when the inode is removed entirely, i.e. after the link count + * went to zero and all fds to it are closed. + * + * Note that we never follow symlinks here. This is a simplification, but should cover almost all cases + * correctly. + * + * Note that we create the new event source first here, before releasing the old one. This should optimize + * behaviour as this way sd-event can reuse the old watch in case the inode didn't change. */ + + r = sd_event_add_inotify(m->event, &new_event, "/etc/localtime", + IN_ATTRIB|IN_MOVE_SELF|IN_CLOSE_WRITE|IN_DONT_FOLLOW, manager_dispatch_timezone_change, m); + if (r == -ENOENT) /* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created + * either by O_CREATE or by rename() */ + r = sd_event_add_inotify(m->event, &new_event, "/etc", + IN_CREATE|IN_MOVED_TO|IN_ONLYDIR, manager_dispatch_timezone_change, m); + if (r < 0) + return log_error_errno(r, "Failed to create timezone change event source: %m"); + + /* Schedule this slightly earlier than the .timer event sources */ + r = sd_event_source_set_priority(new_event, SD_EVENT_PRIORITY_NORMAL-1); + if (r < 0) + return log_error_errno(r, "Failed to set priority of timezone change event sources: %m"); + + sd_event_source_unref(m->timezone_change_event_source); + m->timezone_change_event_source = TAKE_PTR(new_event); + + return 0; +} + static int enable_special_signals(Manager *m) { _cleanup_close_ int fd = -1; @@ -426,6 +469,8 @@ static int enable_special_signals(Manager *m) { return 0; } +#define RTSIG_IF_AVAILABLE(signum) (signum <= SIGRTMAX ? signum : -1) + static int manager_setup_signals(Manager *m) { struct sigaction sa = { .sa_handler = SIG_DFL, @@ -479,22 +524,22 @@ static int manager_setup_signals(Manager *m) { /* .. one free signal here ... */ -#if !defined(__hppa64__) && !defined(__hppa__) - /* Apparently Linux on hppa has fewer RT - * signals (SIGRTMAX is SIGRTMIN+25 there), - * hence let's not try to make use of them - * here. Since these commands are accessible - * by different means and only really a safety - * net, the missing functionality on hppa - * shouldn't matter. */ - - SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ - SIGRTMIN+27, /* systemd: set log target to console */ - SIGRTMIN+28, /* systemd: set log target to kmsg */ - SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */ + /* Apparently Linux on hppa had fewer RT signals until v3.18, + * SIGRTMAX was SIGRTMIN+25, and then SIGRTMIN was lowered, + * see commit v3.17-7614-g1f25df2eff. + * + * We cannot unconditionally make use of those signals here, + * so let's use a runtime check. Since these commands are + * accessible by different means and only really a safety + * net, the missing functionality on hppa shouldn't matter. + */ + + RTSIG_IF_AVAILABLE(SIGRTMIN+26), /* systemd: set log target to journal-or-kmsg */ + RTSIG_IF_AVAILABLE(SIGRTMIN+27), /* systemd: set log target to console */ + RTSIG_IF_AVAILABLE(SIGRTMIN+28), /* systemd: set log target to kmsg */ + RTSIG_IF_AVAILABLE(SIGRTMIN+29), /* systemd: set log target to syslog-or-kmsg (obsolete) */ /* ... one free signal here SIGRTMIN+30 ... */ -#endif -1); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); @@ -667,7 +712,7 @@ static int manager_setup_sigchld_event_source(Manager *m) { } int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { - Manager *m; + _cleanup_(manager_freep) Manager *m = NULL; int r; assert(_m); @@ -686,6 +731,8 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { m->default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; m->default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; m->default_restart_usec = DEFAULT_RESTART_USEC; + m->original_log_level = -1; + m->original_log_target = _LOG_TARGET_INVALID; #if ENABLE_EFI if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) @@ -729,62 +776,74 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { r = manager_default_environment(m); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->units, &string_hash_ops); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->jobs, NULL); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->cgroup_unit, &path_hash_ops); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops); if (r < 0) - goto fail; + return r; + + r = manager_setup_prefix(m); + if (r < 0) + return r; + + m->udev = udev_new(); + if (!m->udev) + return -ENOMEM; r = sd_event_default(&m->event); if (r < 0) - goto fail; + return r; r = manager_setup_run_queue(m); if (r < 0) - goto fail; + return r; - r = manager_setup_signals(m); - if (r < 0) - goto fail; + if (test_run_flags == MANAGER_TEST_RUN_MINIMAL) { + m->cgroup_root = strdup(""); + if (!m->cgroup_root) + return -ENOMEM; + } else { + r = manager_setup_signals(m); + if (r < 0) + return r; - r = manager_setup_cgroup(m); - if (r < 0) - goto fail; + r = manager_setup_cgroup(m); + if (r < 0) + return r; - r = manager_setup_time_change(m); - if (r < 0) - goto fail; + r = manager_setup_time_change(m); + if (r < 0) + return r; - r = manager_setup_sigchld_event_source(m); - if (r < 0) - goto fail; + r = manager_read_timezone_stat(m); + if (r < 0) + return r; - m->udev = udev_new(); - if (!m->udev) { - r = -ENOMEM; - goto fail; - } + r = manager_setup_timezone_change(m); + if (r < 0) + return r; - r = manager_setup_prefix(m); - if (r < 0) - goto fail; + r = manager_setup_sigchld_event_source(m); + if (r < 0) + return r; + } if (MANAGER_IS_SYSTEM(m) && test_run_flags == 0) { r = mkdir_label("/run/systemd/units", 0755); if (r < 0 && r != -EEXIST) - goto fail; + return r; } m->taint_usr = @@ -794,12 +853,9 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { /* Note that we do not set up the notify fd here. We do that after deserialization, * since they might have gotten serialized across the reexec. */ - *_m = m; - return 0; + *_m = TAKE_PTR(m); -fail: - manager_free(m); - return r; + return 0; } static int manager_setup_notify(Manager *m) { @@ -841,8 +897,7 @@ static int manager_setup_notify(Manager *m) { if (r < 0) return log_error_errno(errno, "SO_PASSCRED failed: %m"); - m->notify_fd = fd; - fd = -1; + m->notify_fd = TAKE_FD(fd); log_debug("Using notification socket %s", m->notify_socket); } @@ -1183,7 +1238,6 @@ static void manager_clear_jobs_and_units(Manager *m) { Manager* manager_free(Manager *m) { UnitType c; - int i; ExecDirectoryType dt; if (!m) @@ -1222,6 +1276,7 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->notify_event_source); sd_event_source_unref(m->cgroups_agent_event_source); sd_event_source_unref(m->time_change_event_source); + sd_event_source_unref(m->timezone_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); sd_event_source_unref(m->user_lookup_event_source); @@ -1251,8 +1306,7 @@ Manager* manager_free(Manager *m) { free(m->switch_root); free(m->switch_root_init); - for (i = 0; i < _RLIMIT_MAX; i++) - m->rlimit[i] = mfree(m->rlimit[i]); + rlimit_free_all(m->rlimit); assert(hashmap_isempty(m->units_requiring_mounts_for)); hashmap_free(m->units_requiring_mounts_for); @@ -1266,23 +1320,37 @@ Manager* manager_free(Manager *m) { return mfree(m); } -void manager_enumerate(Manager *m) { +static void manager_enumerate_perpetual(Manager *m) { UnitType c; assert(m); - /* Let's ask every type to load all units from disk/kernel - * that it might know */ + /* Let's ask every type to load all units from disk/kernel that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) { if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; } - if (!unit_vtable[c]->enumerate) + if (unit_vtable[c]->enumerate_perpetual) + unit_vtable[c]->enumerate_perpetual(m); + } +} + +static void manager_enumerate(Manager *m) { + UnitType c; + + assert(m); + + /* Let's ask every type to load all units from disk/kernel that it might know */ + for (c = 0; c < _UNIT_TYPE_MAX; c++) { + if (!unit_type_supported(c)) { + log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; + } - unit_vtable[c]->enumerate(m); + if (unit_vtable[c]->enumerate) + unit_vtable[c]->enumerate(m); } manager_dispatch_load_queue(m); @@ -1296,7 +1364,9 @@ static void manager_coldplug(Manager *m) { assert(m); - /* Then, let's set up their initial state. */ + log_debug("Invoking unit coldplug() handlers…"); + + /* Let's place the units back into their deserialized state */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ @@ -1309,6 +1379,26 @@ static void manager_coldplug(Manager *m) { } } +static void manager_catchup(Manager *m) { + Iterator i; + Unit *u; + char *k; + + assert(m); + + log_debug("Invoking unit catchup() handlers…"); + + /* Let's catch up on any state changes that happened while we were reloading/reexecing */ + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + + /* ignore aliases */ + if (u->id != k) + continue; + + unit_catchup(u); + } +} + static void manager_build_unit_path_cache(Manager *m) { char **i; int r; @@ -1409,6 +1499,48 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) { return true; } +static void manager_setup_bus(Manager *m) { + assert(m); + + /* Let's set up our private bus connection now, unconditionally */ + (void) bus_init_private(m); + + /* If we are in --user mode also connect to the system bus now */ + if (MANAGER_IS_USER(m)) + (void) bus_init_system(m); + + /* Let's connect to the bus now, but only if the unit is supposed to be up */ + if (manager_dbus_is_running(m, MANAGER_IS_RELOADING(m))) { + (void) bus_init_api(m); + + if (MANAGER_IS_SYSTEM(m)) + (void) bus_init_system(m); + } +} + +static void manager_preset_all(Manager *m) { + int r; + + assert(m); + + if (m->first_boot <= 0) + return; + + if (!MANAGER_IS_SYSTEM(m)) + return; + + if (m->test_run_flags != 0) + return; + + /* If this is the first boot, and we are in the host system, then preset everything */ + r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); + if (r < 0) + log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, + "Failed to populate /etc with preset unit settings, ignoring: %m"); + else + log_info("Populated /etc with preset unit settings."); +} + int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { int r; @@ -1432,19 +1564,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (r < 0) return r; - /* If this is the first boot, and we are in the host system, then preset everything */ - if (m->first_boot > 0 && - MANAGER_IS_SYSTEM(m) && - !m->test_run_flags) { - - r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); - if (r < 0) - log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, - "Failed to populate /etc with preset unit settings, ignoring: %m"); - else - log_info("Populated /etc with preset unit settings."); - } - + manager_preset_all(m); lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); @@ -1456,6 +1576,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* First, enumerate what we can from all config files */ dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_START); + manager_enumerate_perpetual(m); manager_enumerate(m); dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_FINISH); @@ -1489,20 +1610,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* This shouldn't fail, except if things are really broken. */ return r; - /* Let's set up our private bus connection now, unconditionally */ - (void) bus_init_private(m); - - /* If we are in --user mode also connect to the system bus now */ - if (MANAGER_IS_USER(m)) - (void) bus_init_system(m); - - /* Let's connect to the bus now, but only if the unit is supposed to be up */ - if (manager_dbus_is_running(m, !!serialization)) { - (void) bus_init_api(m); - - if (MANAGER_IS_SYSTEM(m)) - (void) bus_init_system(m); - } + /* Connect to the bus if we are good for it */ + manager_setup_bus(m); /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed); @@ -1530,6 +1639,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { m->send_reloading_done = true; } + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + return 0; } @@ -1668,6 +1780,42 @@ Unit *manager_get_unit(Manager *m, const char *name) { return hashmap_get(m->units, name); } +static int manager_dispatch_target_deps_queue(Manager *m) { + Unit *u; + unsigned k; + int r = 0; + + static const UnitDependency deps[] = { + UNIT_REQUIRED_BY, + UNIT_REQUISITE_OF, + UNIT_WANTED_BY, + UNIT_BOUND_BY + }; + + assert(m); + + while ((u = m->target_deps_queue)) { + assert(u->in_target_deps_queue); + + LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); + u->in_target_deps_queue = false; + + for (k = 0; k < ELEMENTSOF(deps); k++) { + Unit *target; + Iterator i; + void *v; + + HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(u, target); + if (r < 0) + return r; + } + } + } + + return r; +} + unsigned manager_dispatch_load_queue(Manager *m) { Unit *u; unsigned n = 0; @@ -1691,6 +1839,11 @@ unsigned manager_dispatch_load_queue(Manager *m) { } m->dispatching_load_queue = false; + + /* Dispatch the units waiting for their target dependencies to be added now, as all targets that we know about + * should be loaded and have aliases resolved */ + (void) manager_dispatch_target_deps_queue(m); + return n; } @@ -1701,6 +1854,7 @@ int manager_load_unit_prepare( sd_bus_error *e, Unit **_ret) { + _cleanup_(unit_freep) Unit *cleanup_ret = NULL; Unit *ret; UnitType t; int r; @@ -1733,29 +1887,26 @@ int manager_load_unit_prepare( return 1; } - ret = unit_new(m, unit_vtable[t]->object_size); + ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size); if (!ret) return -ENOMEM; if (path) { ret->fragment_path = strdup(path); - if (!ret->fragment_path) { - unit_free(ret); + if (!ret->fragment_path) return -ENOMEM; - } } r = unit_add_name(ret, name); - if (r < 0) { - unit_free(ret); + if (r < 0) return r; - } unit_add_to_load_queue(ret); unit_add_to_dbus_queue(ret); unit_add_to_gc_queue(ret); *_ret = ret; + cleanup_ret = NULL; return 0; } @@ -1782,7 +1933,32 @@ int manager_load_unit( manager_dispatch_load_queue(m); *_ret = unit_follow_merge(*_ret); + return 0; +} + +int manager_load_startable_unit_or_warn( + Manager *m, + const char *name, + const char *path, + Unit **ret) { + + /* Load a unit, make sure it loaded fully and is not masked. */ + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *unit; + int r; + r = manager_load_unit(m, name, path, &error, &unit); + if (r < 0) + return log_error_errno(r, "Failed to load %s %s: %s", + name ? "unit" : "unit file", name ?: path, + bus_error_message(&error, r)); + + r = bus_unit_validate_load_state(unit, &error); + if (r < 0) + return log_error_errno(r, "%s", bus_error_message(&error, r)); + + *ret = unit; return 0; } @@ -1853,8 +2029,7 @@ int manager_get_dump_string(Manager *m, char **ret) { f = safe_fclose(f); - *ret = dump; - dump = NULL; + *ret = TAKE_PTR(dump); return 0; } @@ -2049,7 +2224,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t _cleanup_free_ Unit **array_copy = NULL; Unit *u1, *u2, **array; int r, *fd_array = NULL; - unsigned n_fds = 0; + size_t n_fds = 0; bool found = false; ssize_t n; @@ -2283,7 +2458,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) { * 7 times within 2s, we reboot/shutdown immediately, * unless it was disabled in system.conf */ - if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE) + if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE) manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); else emergency_action(m, m->cad_burst_action, NULL, @@ -2335,8 +2510,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case SIGTERM: if (MANAGER_IS_SYSTEM(m)) { - /* This is for compatibility with the - * original sysvinit */ + /* This is for compatibility with the original sysvinit */ r = verify_run_space_and_log("Refusing to reexecute"); if (r >= 0) m->exit_code = MANAGER_REEXECUTE; @@ -2353,21 +2527,20 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGWINCH: + /* This is a nop on non-init */ if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); - /* This is a nop on non-init */ break; case SIGPWR: + /* This is a nop on non-init */ if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); - /* This is a nop on non-init */ break; case SIGUSR1: - if (manager_dbus_is_running(m, false)) { log_info("Trying to reconnect to bus..."); @@ -2450,13 +2623,11 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case 22: - log_set_max_level(LOG_DEBUG); - log_info("Setting log level to debug."); + manager_override_log_level(m, LOG_DEBUG); break; case 23: - log_set_max_level(LOG_INFO); - log_info("Setting log level to info."); + manager_restore_original_log_level(m); break; case 24: @@ -2470,18 +2641,15 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case 26: case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_notice("Setting log target to journal-or-kmsg."); + manager_restore_original_log_target(m); break; case 27: - log_set_target(LOG_TARGET_CONSOLE); - log_notice("Setting log target to console."); + manager_override_log_target(m, LOG_TARGET_CONSOLE); break; case 28: - log_set_target(LOG_TARGET_KMSG); - log_notice("Setting log target to kmsg."); + manager_override_log_target(m, LOG_TARGET_KMSG); break; default: @@ -2502,14 +2670,10 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint log_struct(LOG_DEBUG, "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR, - LOG_MESSAGE("Time has been changed"), - NULL); + LOG_MESSAGE("Time has been changed")); /* Restart the watch */ - m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); - m->time_change_fd = safe_close(m->time_change_fd); - - manager_setup_time_change(m); + (void) manager_setup_time_change(m); HASHMAP_FOREACH(u, m->units, i) if (UNIT_VTABLE(u)->time_change) @@ -2518,6 +2682,41 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint return 0; } +static int manager_dispatch_timezone_change( + sd_event_source *source, + const struct inotify_event *e, + void *userdata) { + + Manager *m = userdata; + int changed; + Iterator i; + Unit *u; + + assert(m); + + log_debug("inotify event for /etc/localtime"); + + changed = manager_read_timezone_stat(m); + if (changed < 0) + return changed; + if (!changed) + return 0; + + /* Something changed, restart the watch, to ensure we watch the new /etc/localtime if it changed */ + (void) manager_setup_timezone_change(m); + + /* Read the new timezone */ + tzset(); + + log_debug("Timezone has been changed (now: %s).", tzname[daylight]); + + HASHMAP_FOREACH(u, m->units, i) + if (UNIT_VTABLE(u)->timezone_change) + UNIT_VTABLE(u)->timezone_change(u); + + return 0; +} + static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; @@ -2580,7 +2779,7 @@ int manager_loop(Manager *m) { if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) watchdog_ping(); - if (!ratelimit_test(&rl)) { + if (!ratelimit_below(&rl)) { /* Yay, something is going seriously wrong, pause a little */ log_warning("Looping too fast. Throttling execution a little."); sleep(1); @@ -2644,12 +2843,19 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, return 0; } - return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(invocation_id)); + return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, + "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", + SD_ID128_FORMAT_VAL(invocation_id)); } /* If this didn't work, we check if this is a unit name */ - if (!unit_name_is_valid(n, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is neither a valid invocation ID nor unit name.", n); + if (!unit_name_is_valid(n, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { + _cleanup_free_ char *nn = NULL; + + nn = cescape(n); + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, + "Unit name %s is neither a valid invocation ID nor unit name.", strnull(nn)); + } r = manager_load_unit(m, n, NULL, e, &u); if (r < 0) @@ -2813,6 +3019,11 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged)); fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs)); + if (m->log_level_overridden) + fprintf(f, "log-level-override=%i\n", log_get_max_level()); + if (m->log_target_overridden) + fprintf(f, "log-target-override=%s\n", log_target_to_string(log_get_target())); + for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */ if (in_initrd() && IN_SET(q, MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH)) @@ -2897,8 +3108,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { assert(m->n_reloading > 0); m->n_reloading--; - if (ferror(f)) - return -EIO; + r = fflush_and_check(f); + if (r < 0) + return r; r = bus_fdset_add_all(m, fds); if (r < 0) @@ -2996,6 +3208,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->service_watchdogs = b; + } else if ((val = startswith(l, "log-level-override="))) { + int level; + + level = log_level_from_string(val); + if (level < 0) + log_notice("Failed to parse log-level-override value '%s', ignoring.", val); + else + manager_override_log_level(m, level); + + } else if ((val = startswith(l, "log-target-override="))) { + LogTarget target; + + target = log_target_from_string(val); + if (target < 0) + log_notice("Failed to parse log-target-override value '%s', ignoring.", val); + else + manager_override_log_target(m, target); + } else if (startswith(l, "env=")) { r = deserialize_environment(&m->environment, l); if (r == -ENOMEM) @@ -3126,6 +3356,17 @@ finish: return r; } +static void manager_flush_finished_jobs(Manager *m) { + Job *j; + + while ((j = set_steal_first(m->pending_finished_jobs))) { + bus_job_send_removed_signal(j); + job_free(j); + } + + m->pending_finished_jobs = set_free(m->pending_finished_jobs); +} + int manager_reload(Manager *m) { int r, q; _cleanup_fclose_ FILE *f = NULL; @@ -3194,8 +3435,7 @@ int manager_reload(Manager *m) { r = q; } - fclose(f); - f = NULL; + f = safe_fclose(f); /* Re-register notify_fd as event source */ q = manager_setup_notify(m); @@ -3222,17 +3462,23 @@ int manager_reload(Manager *m) { exec_runtime_vacuum(m); + assert(m->n_reloading > 0); + m->n_reloading--; + /* It might be safe to log to the journal now and connect to dbus */ manager_recheck_journal(m); manager_recheck_dbus(m); + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + /* Sync current state of bus names with our set of listening units */ q = manager_enqueue_sync_bus_names(m); if (q < 0 && r >= 0) r = q; - assert(m->n_reloading > 0); - m->n_reloading--; + if (!MANAGER_IS_RELOADING(m)) + manager_flush_finished_jobs(m); m->send_reloading_done = true; @@ -3280,8 +3526,7 @@ static void log_taint_string(Manager *m) { log_struct(LOG_NOTICE, LOG_MESSAGE("System is tainted: %s", taint), "TAINT=%s", taint, - "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR, - NULL); + "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR); } static void manager_notify_finished(Manager *m) { @@ -3328,8 +3573,7 @@ static void manager_notify_finished(Manager *m) { format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } else { /* The initrd-less case on bare-metal*/ @@ -3344,8 +3588,7 @@ static void manager_notify_finished(Manager *m) { buf, format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } } else { /* The container and --user case */ @@ -3356,8 +3599,7 @@ static void manager_notify_finished(Manager *m) { "MESSAGE_ID=" SD_MESSAGE_USER_STARTUP_FINISHED_STR, "USERSPACE_USEC="USEC_FMT, userspace_usec, LOG_MESSAGE("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); @@ -3597,6 +3839,9 @@ void manager_recheck_dbus(Manager *m) { * connection of the API bus). That's because the system bus after all runs as service of the system instance, * while in the user instance we can assume it's already there. */ + if (MANAGER_IS_RELOADING(m)) + return; /* don't check while we are reloading… */ + if (manager_dbus_is_running(m, false)) { (void) bus_init_api(m); @@ -3647,6 +3892,10 @@ void manager_recheck_journal(Manager *m) { if (getpid_cached() != 1) return; + /* Don't check this while we are reloading, things might still change */ + if (MANAGER_IS_RELOADING(m)) + return; + /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. If the * journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we might trigger * an activation ourselves we can't fulfill. */ @@ -3786,7 +4035,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { assert(path); strcpy(p, path); - path_kill_slashes(p); + path_simplify(p, false); return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p); } @@ -4215,6 +4464,59 @@ void manager_unref_console(Manager *m) { m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */ } +void manager_override_log_level(Manager *m, int level) { + _cleanup_free_ char *s = NULL; + assert(m); + + if (!m->log_level_overridden) { + m->original_log_level = log_get_max_level(); + m->log_level_overridden = true; + } + + (void) log_level_to_string_alloc(level, &s); + log_info("Setting log level to %s.", strna(s)); + + log_set_max_level(level); +} + +void manager_restore_original_log_level(Manager *m) { + _cleanup_free_ char *s = NULL; + assert(m); + + if (!m->log_level_overridden) + return; + + (void) log_level_to_string_alloc(m->original_log_level, &s); + log_info("Restoring log level to original (%s).", strna(s)); + + log_set_max_level(m->original_log_level); + m->log_level_overridden = false; +} + +void manager_override_log_target(Manager *m, LogTarget target) { + assert(m); + + if (!m->log_target_overridden) { + m->original_log_target = log_get_target(); + m->log_target_overridden = true; + } + + log_info("Setting log target to %s.", log_target_to_string(target)); + log_set_target(target); +} + +void manager_restore_original_log_target(Manager *m) { + assert(m); + + if (!m->log_target_overridden) + return; + + log_info("Restoring log target to original %s.", log_target_to_string(m->original_log_target)); + + log_set_target(m->original_log_target); + m->log_target_overridden = false; +} + static const char *const manager_state_table[_MANAGER_STATE_MAX] = { [MANAGER_INITIALIZING] = "initializing", [MANAGER_STARTING] = "starting", diff --git a/src/core/manager.h b/src/core/manager.h index d4eaaa1c4b..ea5d425030 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -1,26 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 <libmount.h> #include <stdbool.h> #include <stdio.h> @@ -34,6 +14,9 @@ #include "list.h" #include "ratelimit.h" +struct libmnt_monitor; +typedef struct Unit Unit; + /* Enforce upper limit how many names we allow */ #define MANAGER_MAX_NAMES 131072 /* 128K */ @@ -96,10 +79,11 @@ typedef enum ManagerTimestamp { enum { /* 0 = run normally */ - MANAGER_TEST_RUN_MINIMAL = 1, /* run test w/o generators */ - MANAGER_TEST_RUN_ENV_GENERATORS = 2, /* also run env generators */ - MANAGER_TEST_RUN_GENERATORS = 4, /* also run unit generators */ - MANAGER_TEST_FULL = MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS, + MANAGER_TEST_RUN_MINIMAL = 1 << 1, /* create basic data structures */ + MANAGER_TEST_RUN_BASIC = 1 << 2, /* interact with the environment */ + MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 3, /* also run env generators */ + MANAGER_TEST_RUN_GENERATORS = 1 << 4, /* also run unit generators */ + MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS, }; assert_cc((MANAGER_TEST_FULL & UINT8_MAX) == MANAGER_TEST_FULL); @@ -143,6 +127,9 @@ struct Manager { /* Units whose cgroup ran empty */ LIST_HEAD(Unit, cgroup_empty_queue); + /* Target units whose default target dependencies haven't been set yet */ + LIST_HEAD(Unit, target_deps_queue); + sd_event *event; /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in @@ -177,6 +164,8 @@ struct Manager { int time_change_fd; sd_event_source *time_change_event_source; + sd_event_source *timezone_change_event_source; + sd_event_source *jobs_in_progress_event_source; int user_lookup_fds[2]; @@ -257,6 +246,10 @@ struct Manager { unsigned gc_marker; + /* The stat() data the last time we saw /etc/localtime */ + usec_t etc_localtime_mtime; + bool etc_localtime_accessible:1; + /* Flags */ ManagerExitCode exit_code:5; @@ -303,10 +296,18 @@ struct Manager { uint64_t default_tasks_max; usec_t default_timer_accuracy_usec; + int original_log_level; + LogTarget original_log_target; + bool log_level_overridden:1; + bool log_target_overridden:1; + struct rlimit *rlimit[_RLIMIT_MAX]; /* non-zero if we are reloading or reexecuting, */ int n_reloading; + /* A set which contains all jobs that started before reload and finished + * during it */ + Set *pending_finished_jobs; unsigned n_installed_jobs; unsigned n_failed_jobs; @@ -379,8 +380,8 @@ struct Manager { int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **m); Manager* manager_free(Manager *m); +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); -void manager_enumerate(Manager *m); int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); @@ -390,6 +391,7 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); +int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret); int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u); int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret); @@ -462,6 +464,12 @@ char *manager_taint_string(Manager *m); void manager_ref_console(Manager *m); void manager_unref_console(Manager *m); +void manager_override_log_level(Manager *m, int level); +void manager_restore_original_log_level(Manager *m); + +void manager_override_log_target(Manager *m, LogTarget target); +void manager_restore_original_log_target(Manager *m); + const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/meson.build b/src/core/meson.build index 3fc43f6335..3852c5e9d8 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -1,19 +1,4 @@ # SPDX-License-Identifier: LGPL-2.1+ -# -# Copyright 2017 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/>. libcore_la_sources = ''' audit-fd.c @@ -140,7 +125,7 @@ load_fragment_gperf_gperf = custom_target( 'load-fragment-gperf.gperf', input : 'load-fragment-gperf.gperf.m4', output: 'load-fragment-gperf.gperf', - command : [m4, '-P'] + m4_defines + ['@INPUT@'], + command : [meson_apply_m4, config_h, '@INPUT@'], capture : true) load_fragment_gperf_c = custom_target( @@ -163,13 +148,14 @@ libcore = static_library( load_fragment_gperf_c, load_fragment_gperf_nulstr_c, include_directories : includes, - link_with : [libshared_static], dependencies : [threads, + librt, libseccomp, libpam, libaudit, libkmod, libapparmor, + libselinux, libmount]) systemd_sources = files('main.c') diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 536c17b4d5..16880e6157 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -1,27 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <ftw.h> #include <stdlib.h> #include <sys/mount.h> +#include <sys/statvfs.h> #include <unistd.h> #include "alloc-util.h" @@ -47,10 +30,10 @@ #include "virt.h" typedef enum MountMode { - MNT_NONE = 0, - MNT_FATAL = 1 << 0, - MNT_IN_CONTAINER = 1 << 1, - MNT_CHECK_WRITABLE = 1 << 2, + MNT_NONE = 0, + MNT_FATAL = 1 << 0, + MNT_IN_CONTAINER = 1 << 1, + MNT_CHECK_WRITABLE = 1 << 2, } MountMode; typedef struct MountPoint { @@ -168,7 +151,7 @@ static int mount_one(const MountPoint *p, bool relabel) { /* Relabel first, just in case */ if (relabel) - (void) label_fix(p->where, true, true); + (void) label_fix(p->where, LABEL_IGNORE_ENOENT|LABEL_IGNORE_EROFS); r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW); if (r < 0 && r != -ENOENT) { @@ -206,7 +189,7 @@ static int mount_one(const MountPoint *p, bool relabel) { /* Relabel again, since we now mounted something fresh here */ if (relabel) - (void) label_fix(p->where, false, false); + (void) label_fix(p->where, 0); if (p->mode & MNT_CHECK_WRITABLE) { if (access(p->where, W_OK) < 0) { @@ -248,6 +231,7 @@ int mount_setup_early(void) { int mount_cgroup_controllers(char ***join_controllers) { _cleanup_set_free_free_ Set *controllers = NULL; + bool has_argument = !!join_controllers; int r; if (!cg_is_legacy_wanted()) @@ -255,7 +239,7 @@ int mount_cgroup_controllers(char ***join_controllers) { /* Mount all available cgroup controllers that are built into the kernel. */ - if (!join_controllers) + if (!has_argument) /* The defaults: * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio". * @@ -290,7 +274,7 @@ int mount_cgroup_controllers(char ***join_controllers) { if (strv_find(*k, controller)) break; - if (k && *k) { + if (*k) { char **i, **j; for (i = *k, j = *k; *i; i++) { @@ -300,7 +284,8 @@ int mount_cgroup_controllers(char ***join_controllers) { t = set_remove(controllers, *i); if (!t) { - free(*i); + if (has_argument) + free(*i); continue; } } @@ -313,10 +298,8 @@ int mount_cgroup_controllers(char ***join_controllers) { options = strv_join(*k, ","); if (!options) return log_oom(); - } else { - options = controller; - controller = NULL; - } + } else + options = TAKE_PTR(controller); where = strappend("/sys/fs/cgroup/", options); if (!where) @@ -329,7 +312,7 @@ int mount_cgroup_controllers(char ***join_controllers) { if (r < 0) return r; - if (r > 0 && k && *k) { + if (r > 0 && *k) { char **i; for (i = *k; *i; i++) { @@ -374,7 +357,7 @@ static int nftw_cb( if (_unlikely_(ftwbuf->level == 0)) return FTW_CONTINUE; - label_fix(fpath, false, false); + (void) label_fix(fpath, 0); /* /run/initramfs is static data and big, no need to * dynamically relabel its contents at boot... */ @@ -385,6 +368,34 @@ static int nftw_cb( return FTW_CONTINUE; }; + +static int relabel_cgroup_filesystems(void) { + int r; + struct statfs st; + + r = cg_all_unified(); + if (r == 0) { + /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this + only when the filesystem has been already populated by a previous instance of systemd + running from initrd. Otherwise don't remount anything and leave the filesystem read-write + for the cgroup filesystems to be mounted inside. */ + if (statfs("/sys/fs/cgroup", &st) < 0) + return log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup: %m"); + + if (st.f_flags & ST_RDONLY) + (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL); + + (void) label_fix("/sys/fs/cgroup", 0); + (void) nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + + if (st.f_flags & ST_RDONLY) + (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL); + + } else if (r < 0) + return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m"); + + return 0; +} #endif int mount_setup(bool loaded_policy) { @@ -405,19 +416,13 @@ int mount_setup(bool loaded_policy) { before_relabel = now(CLOCK_MONOTONIC); - nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + (void) nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + (void) nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + (void) nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - /* Temporarily remount the root cgroup filesystem to give it a proper label. */ - r = cg_all_unified(); - if (r == 0) { - (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL); - label_fix("/sys/fs/cgroup", false, false); - nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL); - } else if (r < 0) - return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m"); + r = relabel_cgroup_filesystems(); + if (r < 0) + return r; after_relabel = now(CLOCK_MONOTONIC); @@ -447,14 +452,20 @@ int mount_setup(bool loaded_policy) { (void) mkdir_label("/run/systemd", 0755); (void) mkdir_label("/run/systemd/system", 0755); - /* Set up inaccessible items */ + /* Set up inaccessible (and empty) file nodes of all types */ (void) mkdir_label("/run/systemd/inaccessible", 0000); (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0); (void) mkdir_label("/run/systemd/inaccessible/dir", 0000); - (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0)); - (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0)); (void) mkfifo("/run/systemd/inaccessible/fifo", 0000); (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0); + /* The following two are likely to fail if we lack the privs for it (for example in an userns environment, if + * CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 device nodes to be + * created). But that's entirely fine. Consumers of these files should carry fallback to use a different node + * then, for example /run/systemd/inaccessible/sock, which is close enough in behaviour and semantics for most + * uses. */ + (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0)); + (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0)); + return 0; } diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h index 2c96e64e52..43cd8908de 100644 --- a/src/core/mount-setup.h +++ b/src/core/mount-setup.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 <stdbool.h> int mount_setup_early(void); diff --git a/src/core/mount.c b/src/core/mount.c index cfe8ec9044..21437dad08 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1,32 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <signal.h> #include <stdio.h> #include <sys/epoll.h> +#include <libmount.h> + #include "sd-messages.h" #include "alloc-util.h" #include "dbus-mount.h" +#include "device.h" #include "escape.h" #include "exit-status.h" #include "format-util.h" @@ -527,23 +512,23 @@ static int mount_verify(Mount *m) { if (!unit_has_name(UNIT(m), e)) { log_unit_error(UNIT(m), "Where= setting doesn't match unit name. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) { log_unit_error(UNIT(m), "Cannot create mount unit for API file system %s. Refusing.", m->where); - return -EINVAL; + return -ENOEXEC; } p = get_mount_parameters_fragment(m); if (p && !p->what) { log_unit_error(UNIT(m), "What= setting is missing. Refusing."); - return -EBADMSG; + return -ENOEXEC; } if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) { log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -564,7 +549,7 @@ static int mount_add_extras(Mount *m) { return r; } - path_kill_slashes(m->where); + path_simplify(m->where, false); if (!u->description) { r = unit_set_description(u, m->where); @@ -667,7 +652,8 @@ static void mount_set_state(Mount *m, MountState state) { if (state != old_state) log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); - unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS); + unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], + m->reload_result == MOUNT_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE); } static int mount_coldplug(Unit *u) { @@ -1608,11 +1594,8 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { assert(m); t = mnt_new_table(); - if (!t) - return log_oom(); - i = mnt_new_iter(MNT_ITER_FORWARD); - if (!i) + if (!t || !i) return log_oom(); r = mnt_table_parse_mtab(t, NULL); @@ -1621,9 +1604,9 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { r = 0; for (;;) { + struct libmnt_fs *fs; const char *device, *path, *options, *fstype; _cleanup_free_ char *d = NULL, *p = NULL; - struct libmnt_fs *fs; int k; k = mnt_table_next_fs(t, i, &fs); @@ -1646,7 +1629,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { if (cunescape(path, UNESCAPE_RELAX, &p) < 0) return log_oom(); - (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags); + device_found_node(m, d, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT); k = mount_setup_unit(m, d, p, options, fstype, set_flags); if (r == 0 && k < 0) @@ -1683,7 +1666,7 @@ static int mount_get_timeout(Unit *u, usec_t *timeout) { return 1; } -static int synthesize_root_mount(Manager *m) { +static void mount_enumerate_perpetual(Manager *m) { Unit *u; int r; @@ -1695,8 +1678,10 @@ static int synthesize_root_mount(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_MOUNT); if (!u) { r = unit_new_for_name(m, sizeof(Mount), SPECIAL_ROOT_MOUNT, &u); - if (r < 0) - return log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m"); + if (r < 0) { + log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m"); + return; + } } u->perpetual = true; @@ -1704,8 +1689,6 @@ static int synthesize_root_mount(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } static bool mount_is_mounted(Mount *m) { @@ -1719,10 +1702,6 @@ static void mount_enumerate(Manager *m) { assert(m); - r = synthesize_root_mount(m); - if (r < 0) - goto fail; - mnt_init_debug(0); if (!m->mount_monitor) { @@ -1908,7 +1887,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, continue; /* Let the device units know that the device is no longer mounted */ - (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true); + device_found_node(m, what, 0, DEVICE_FOUND_MOUNT); } return 0; @@ -2013,6 +1992,7 @@ const UnitVTable mount_vtable = { .can_transient = true, + .enumerate_perpetual = mount_enumerate_perpetual, .enumerate = mount_enumerate, .shutdown = mount_shutdown, diff --git a/src/core/mount.h b/src/core/mount.h index 1a496def84..67ab8ecf93 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -1,29 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Mount Mount; #include "kill.h" #include "dynamic-user.h" +#include "unit.h" typedef enum MountExecCommand { MOUNT_EXEC_MOUNT, @@ -110,3 +92,5 @@ MountExecCommand mount_exec_command_from_string(const char *s) _pure_; const char* mount_result_to_string(MountResult i) _const_; MountResult mount_result_from_string(const char *s) _pure_; + +DEFINE_CAST(MOUNT, Mount); diff --git a/src/core/namespace.c b/src/core/namespace.c index 0e9c7b8fb4..e4930db15c 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sched.h> @@ -74,16 +56,18 @@ typedef struct MountEntry { bool ignore:1; /* Ignore if path does not exist? */ bool has_prefix:1; /* Already is prefixed by the root dir? */ bool read_only:1; /* Shall this mount point be read-only? */ + bool applied:1; /* Already applied */ char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */ const char *source_const; /* The source path, for bind mounts */ char *source_malloc; const char *options_const;/* Mount options for tmpfs */ char *options_malloc; unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ + unsigned n_followed; } MountEntry; /* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted - * something there already. These mounts are hence overriden by any other explicitly configured mounts. */ + * something there already. These mounts are hence overridden by any other explicitly configured mounts. */ static const MountEntry apivfs_table[] = { { "/proc", PROCFS, false }, { "/dev", BIND_DEV, false }, @@ -92,23 +76,26 @@ static const MountEntry apivfs_table[] = { /* ProtectKernelTunables= option and the related filesystem APIs */ static const MountEntry protect_kernel_tunables_table[] = { - { "/proc/sys", READONLY, false }, - { "/proc/sysrq-trigger", READONLY, true }, - { "/proc/latency_stats", READONLY, true }, - { "/proc/mtrr", READONLY, true }, - { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */ { "/proc/acpi", READONLY, true }, - { "/proc/timer_stats", READONLY, true }, + { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */ { "/proc/asound", READONLY, true }, { "/proc/bus", READONLY, true }, { "/proc/fs", READONLY, true }, { "/proc/irq", READONLY, true }, + { "/proc/kallsyms", INACCESSIBLE, true }, + { "/proc/kcore", INACCESSIBLE, true }, + { "/proc/latency_stats", READONLY, true }, + { "/proc/mtrr", READONLY, true }, + { "/proc/scsi", READONLY, true }, + { "/proc/sys", READONLY, false }, + { "/proc/sysrq-trigger", READONLY, true }, + { "/proc/timer_stats", READONLY, true }, { "/sys", READONLY, false }, - { "/sys/kernel/debug", READONLY, true }, - { "/sys/kernel/tracing", READONLY, true }, { "/sys/fs/bpf", READONLY, true }, { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */ { "/sys/fs/selinux", READWRITE, true }, + { "/sys/kernel/debug", READONLY, true }, + { "/sys/kernel/tracing", READONLY, true }, }; /* ProtectKernelModules= option */ @@ -286,8 +273,8 @@ static int append_empty_dir_mounts(MountEntry **p, char **strv) { return 0; } -static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n) { - unsigned i; +static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n) { + size_t i; assert(p); @@ -306,8 +293,8 @@ static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n return 0; } -static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, unsigned n) { - unsigned i; +static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, size_t n) { + size_t i; int r; assert(p); @@ -330,7 +317,7 @@ static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, if (r < 0) return r; - ro = !!(flags & MS_RDONLY); + ro = flags & MS_RDONLY; if (ro) flags ^= MS_RDONLY; } @@ -349,8 +336,8 @@ static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, return 0; } -static int append_static_mounts(MountEntry **p, const MountEntry *mounts, unsigned n, bool ignore_protect) { - unsigned i; +static int append_static_mounts(MountEntry **p, const MountEntry *mounts, size_t n, bool ignore_protect) { + size_t i; assert(p); assert(mounts); @@ -423,15 +410,14 @@ static int mount_path_compare(const void *a, const void *b) { /* If the paths are equal, check the mode */ if (p->mode < q->mode) return -1; - if (p->mode > q->mode) return 1; return 0; } -static int prefix_where_needed(MountEntry *m, unsigned n, const char *root_directory) { - unsigned i; +static int prefix_where_needed(MountEntry *m, size_t n, const char *root_directory) { + size_t i; /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs * that. */ @@ -456,7 +442,7 @@ static int prefix_where_needed(MountEntry *m, unsigned n, const char *root_direc return 0; } -static void drop_duplicates(MountEntry *m, unsigned *n) { +static void drop_duplicates(MountEntry *m, size_t *n) { MountEntry *f, *t, *previous; assert(m); @@ -467,8 +453,10 @@ static void drop_duplicates(MountEntry *m, unsigned *n) { for (f = m, t = m, previous = NULL; f < m + *n; f++) { /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() - * above. */ - if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) { + * above. Note that we only drop duplicates that haven't been mounted yet. */ + if (previous && + path_equal(mount_entry_path(f), mount_entry_path(previous)) && + !f->applied && !previous->applied) { log_debug("%s is duplicate.", mount_entry_path(f)); previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */ mount_entry_done(f); @@ -483,7 +471,7 @@ static void drop_duplicates(MountEntry *m, unsigned *n) { *n = t - m; } -static void drop_inaccessible(MountEntry *m, unsigned *n) { +static void drop_inaccessible(MountEntry *m, size_t *n) { MountEntry *f, *t; const char *clear = NULL; @@ -512,7 +500,7 @@ static void drop_inaccessible(MountEntry *m, unsigned *n) { *n = t - m; } -static void drop_nop(MountEntry *m, unsigned *n) { +static void drop_nop(MountEntry *m, size_t *n) { MountEntry *f, *t; assert(m); @@ -551,7 +539,7 @@ static void drop_nop(MountEntry *m, unsigned *n) { *n = t - m; } -static void drop_outside_root(const char *root_directory, MountEntry *m, unsigned *n) { +static void drop_outside_root(const char *root_directory, MountEntry *m, size_t *n) { MountEntry *f, *t; assert(m); @@ -578,14 +566,14 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne *n = t - m; } -static int clone_device_node(const char *d, const char *temporary_mount) { +static int clone_device_node(const char *d, const char *temporary_mount, bool *make_devnode) { const char *dn; struct stat st; int r; if (stat(d, &st) < 0) { if (errno == ENOENT) - return 0; + return -ENXIO; return -errno; } @@ -594,17 +582,41 @@ static int clone_device_node(const char *d, const char *temporary_mount) { return -EINVAL; if (st.st_rdev == 0) - return 0; + return -ENXIO; dn = strjoina(temporary_mount, d); - mac_selinux_create_file_prepare(d, st.st_mode); - r = mknod(dn, st.st_mode, st.st_rdev); + if (*make_devnode) { + mac_selinux_create_file_prepare(d, st.st_mode); + r = mknod(dn, st.st_mode, st.st_rdev); + mac_selinux_create_file_clear(); + + if (r == 0) + return 0; + if (errno != EPERM) + return log_debug_errno(errno, "mknod failed for %s: %m", d); + + *make_devnode = false; + } + + /* We're about to fallback to bind-mounting the device + * node. So create a dummy bind-mount target. */ + mac_selinux_create_file_prepare(d, 0); + r = mknod(dn, S_IFREG, 0); mac_selinux_create_file_clear(); - if (r < 0) - return log_debug_errno(errno, "mknod failed for %s: %m", d); - return 1; + if (r < 0 && errno != EEXIST) + return log_debug_errno(errno, "mknod fallback failed for %s: %m", d); + + /* Fallback to bind-mounting: + * The assumption here is that all used device nodes carry standard + * properties. Specifically, the devices nodes we bind-mount should + * either be owned by root:root or root:tty (e.g. /dev/tty, /dev/ptmx) + * and should not carry ACLs. */ + if (mount(d, dn, NULL, MS_BIND, NULL) < 0) + return log_debug_errno(errno, "mount failed for %s: %m", d); + + return 0; } static int mount_private_dev(MountEntry *m) { @@ -618,6 +630,7 @@ static int mount_private_dev(MountEntry *m) { char temporary_mount[] = "/tmp/namespace-dev-XXXXXX"; const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL; + bool can_mknod = true; _cleanup_umask_ mode_t u; int r; @@ -658,13 +671,9 @@ static int mount_private_dev(MountEntry *m) { goto fail; } } else { - r = clone_device_node("/dev/ptmx", temporary_mount); + r = clone_device_node("/dev/ptmx", temporary_mount, &can_mknod); if (r < 0) goto fail; - if (r == 0) { - r = -ENXIO; - goto fail; - } } devshm = strjoina(temporary_mount, "/dev/shm"); @@ -687,8 +696,9 @@ static int mount_private_dev(MountEntry *m) { (void) symlink("/run/systemd/journal/dev-log", devlog); NULSTR_FOREACH(d, devnodes) { - r = clone_device_node(d, temporary_mount); - if (r < 0) + r = clone_device_node(d, temporary_mount, &can_mknod); + /* ENXIO means the the *source* is not a device file, skip creation in that case */ + if (r < 0 && r != -ENXIO) goto fail; } @@ -808,36 +818,37 @@ static int mount_tmpfs(const MountEntry *m) { return 1; } -static int mount_entry_chase( +static int follow_symlink( const char *root_directory, - const MountEntry *m, - const char *path, - bool chase_nonexistent, - char **location) { + MountEntry *m) { - char *chased; + _cleanup_free_ char *target = NULL; int r; - assert(m); + /* Let's chase symlinks, but only one step at a time. That's because depending where the symlink points we + * might need to change the order in which we mount stuff. Hence: let's normalize piecemeal, and do one step at + * a time by specifying CHASE_STEP. This function returns 0 if we resolved one step, and > 0 if we reached the + * end and already have a fully normalized name. */ - /* Since mount() will always follow symlinks and we need to take the different root directory into account we - * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if - * that applies). The result is stored in "location". */ + r = chase_symlinks(mount_entry_path(m), root_directory, CHASE_STEP|CHASE_NONEXISTENT, &target); + if (r < 0) + return log_debug_errno(r, "Failed to chase symlinks '%s': %m", mount_entry_path(m)); + if (r > 0) /* Reached the end, nothing more to resolve */ + return 1; - r = chase_symlinks(path, root_directory, chase_nonexistent ? CHASE_NONEXISTENT : 0, &chased); - if (r == -ENOENT && m->ignore) { - log_debug_errno(r, "Path %s does not exist, ignoring.", path); - return 0; + if (m->n_followed >= CHASE_SYMLINKS_MAX) { /* put a boundary on things */ + log_debug("Symlink loop on '%s'.", mount_entry_path(m)); + return -ELOOP; } - if (r < 0) - return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path); - log_debug("Followed symlinks %s → %s.", path, chased); + log_debug("Followed mount entry path symlink %s → %s.", mount_entry_path(m), target); - free(*location); - *location = chased; + free_and_replace(m->path_malloc, target); + m->has_prefix = true; - return 1; + m->n_followed ++; + + return 0; } static int apply_mount( @@ -850,10 +861,6 @@ static int apply_mount( assert(m); - r = mount_entry_chase(root_directory, m, mount_entry_path(m), !IN_SET(m->mode, INACCESSIBLE, READONLY, READWRITE), &m->path_malloc); - if (r <= 0) - return r; - log_debug("Applying namespace mount on %s", mount_entry_path(m)); switch (m->mode) { @@ -866,8 +873,12 @@ static int apply_mount( * inaccessible path. */ (void) umount_recursive(mount_entry_path(m), 0); - if (lstat(mount_entry_path(m), &target) < 0) + if (lstat(mount_entry_path(m), &target) < 0) { + if (errno == ENOENT && m->ignore) + return 0; + return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m)); + } what = mode_to_inaccessible_node(target.st_mode); if (!what) { @@ -880,6 +891,8 @@ static int apply_mount( case READONLY: case READWRITE: r = path_is_mount_point(mount_entry_path(m), root_directory, 0); + if (r == -ENOENT && m->ignore) + return 0; if (r < 0) return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m)); if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */ @@ -892,16 +905,29 @@ static int apply_mount( rbind = false; _fallthrough_; - case BIND_MOUNT_RECURSIVE: - /* Also chase the source mount */ + case BIND_MOUNT_RECURSIVE: { + _cleanup_free_ char *chased = NULL; - r = mount_entry_chase(root_directory, m, mount_entry_source(m), false, &m->source_malloc); - if (r <= 0) - return r; + /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note that bind + * mount source paths are always relative to the host root, hence we pass NULL as root directory to + * chase_symlinks() here. */ + + r = chase_symlinks(mount_entry_source(m), NULL, CHASE_TRAIL_SLASH, &chased); + if (r == -ENOENT && m->ignore) { + log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_source(m)); + return 0; + } + if (r < 0) + return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_source(m)); + + log_debug("Followed source symlinks %s → %s.", mount_entry_source(m), chased); + + free_and_replace(m->source_malloc, chased); what = mount_entry_source(m); make = true; break; + } case EMPTY_DIR: case TMPFS: @@ -939,14 +965,22 @@ static int apply_mount( /* Hmm, either the source or the destination are missing. Let's see if we can create the destination, then try again */ - if (stat(what, &st) >= 0) { + if (stat(what, &st) < 0) + log_debug_errno(errno, "Mount point source '%s' is not accessible: %m", what); + else { + int q; (void) mkdir_parents(mount_entry_path(m), 0755); if (S_ISDIR(st.st_mode)) - try_again = mkdir(mount_entry_path(m), 0755) >= 0; + q = mkdir(mount_entry_path(m), 0755) < 0 ? -errno : 0; + else + q = touch(mount_entry_path(m)); + + if (q < 0) + log_debug_errno(q, "Failed to create destination mount point node '%s': %m", mount_entry_path(m)); else - try_again = touch(mount_entry_path(m)) >= 0; + try_again = true; } } @@ -1011,22 +1045,22 @@ static bool namespace_info_mount_apivfs(const char *root_directory, const Namesp ns_info->protect_kernel_tunables); } -static unsigned namespace_calculate_mounts( +static size_t namespace_calculate_mounts( const char* root_directory, const NamespaceInfo *ns_info, char** read_write_paths, char** read_only_paths, char** inaccessible_paths, char** empty_directories, - unsigned n_bind_mounts, - unsigned n_temporary_filesystems, + size_t n_bind_mounts, + size_t n_temporary_filesystems, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, ProtectSystem protect_system) { - unsigned protect_home_cnt; - unsigned protect_system_cnt = + size_t protect_home_cnt; + size_t protect_system_cnt = (protect_system == PROTECT_SYSTEM_STRICT ? ELEMENTSOF(protect_system_strict_table) : ((protect_system == PROTECT_SYSTEM_FULL) ? @@ -1057,6 +1091,18 @@ static unsigned namespace_calculate_mounts( (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table) : 0); } +static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) { + assert(n_mounts); + assert(mounts || *n_mounts == 0); + + qsort_safe(mounts, *n_mounts, sizeof(MountEntry), mount_path_compare); + + drop_duplicates(mounts, n_mounts); + drop_outside_root(root_directory, mounts, n_mounts); + drop_inaccessible(mounts, n_mounts); + drop_nop(mounts, n_mounts); +} + int setup_namespace( const char* root_directory, const char* root_image, @@ -1066,9 +1112,9 @@ int setup_namespace( char** inaccessible_paths, char** empty_directories, const BindMount *bind_mounts, - unsigned n_bind_mounts, + size_t n_bind_mounts, const TemporaryFileSystem *temporary_filesystems, - unsigned n_temporary_filesystems, + size_t n_temporary_filesystems, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, @@ -1082,9 +1128,9 @@ int setup_namespace( _cleanup_free_ void *root_hash = NULL; MountEntry *m, *mounts = NULL; size_t root_hash_size = 0; - bool make_slave = false; const char *root; - unsigned n_mounts; + size_t n_mounts; + bool make_slave; bool require_prefix = false; int r = 0; @@ -1096,7 +1142,9 @@ int setup_namespace( if (root_image) { dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT; - if (protect_system == PROTECT_SYSTEM_STRICT && strv_isempty(read_write_paths)) + if (protect_system == PROTECT_SYSTEM_STRICT && + protect_home != PROTECT_HOME_NO && + strv_isempty(read_write_paths)) dissect_image_flags |= DISSECT_IMAGE_READ_ONLY; r = loop_device_make_by_path(root_image, @@ -1120,19 +1168,17 @@ int setup_namespace( if (root_directory) root = root_directory; - else if (root_image || n_bind_mounts > 0 || n_temporary_filesystems > 0) { - - /* If we are booting from an image, create a mount point for the image, if it's still missing. We use - * the same mount point for all images, which is safe, since they all live in their own namespaces - * after all, and hence won't see each other. We also use such a root directory whenever there are bind - * mounts configured, so that their source mounts are never obstructed by mounts we already applied - * while we are applying them. */ + else { + /* Always create the mount namespace in a temporary directory, instead of operating + * directly in the root. The temporary directory prevents any mounts from being + * potentially obscured my other mounts we already applied. + * We use the same mount point for all images, which is safe, since they all live + * in their own namespaces after all, and hence won't see each other. */ root = "/run/systemd/unit-root"; (void) mkdir_label(root, 0700); require_prefix = true; - } else - root = NULL; + } n_mounts = namespace_calculate_mounts( root, @@ -1147,8 +1193,7 @@ int setup_namespace( protect_home, protect_system); /* Set mount slave mode */ - if (root || n_mounts > 0) - make_slave = true; + make_slave = root || n_mounts > 0 || ns_info->private_mounts; if (n_mounts > 0) { m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry)); @@ -1239,12 +1284,7 @@ int setup_namespace( if (r < 0) goto finish; - qsort(mounts, n_mounts, sizeof(MountEntry), mount_path_compare); - - drop_duplicates(mounts, &n_mounts); - drop_outside_root(root, mounts, &n_mounts); - drop_inaccessible(mounts, &n_mounts); - drop_nop(mounts, &n_mounts); + normalize_mounts(root_directory, mounts, &n_mounts); } if (unshare(CLONE_NEWNS) < 0) { @@ -1304,7 +1344,7 @@ int setup_namespace( if (n_mounts > 0) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; char **blacklist; - unsigned j; + size_t j; /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc. * For example, this is the case with the option: 'InaccessiblePaths=/proc' */ @@ -1314,11 +1354,38 @@ int setup_namespace( goto finish; } - /* First round, add in all special mounts we need */ - for (m = mounts; m < mounts + n_mounts; ++m) { - r = apply_mount(root, m); - if (r < 0) - goto finish; + /* First round, establish all mounts we need */ + for (;;) { + bool again = false; + + for (m = mounts; m < mounts + n_mounts; ++m) { + + if (m->applied) + continue; + + r = follow_symlink(root, m); + if (r < 0) + goto finish; + if (r == 0) { + /* We hit a symlinked mount point. The entry got rewritten and might point to a + * very different place now. Let's normalize the changed list, and start from + * the beginning. After all to mount the entry at the new location we might + * need some other mounts first */ + again = true; + break; + } + + r = apply_mount(root, m); + if (r < 0) + goto finish; + + m->applied = true; + } + + if (!again) + break; + + normalize_mounts(root_directory, mounts, &n_mounts); } /* Create a blacklist we can pass to bind_mount_recursive() */ @@ -1359,8 +1426,8 @@ finish: return r; } -void bind_mount_free_many(BindMount *b, unsigned n) { - unsigned i; +void bind_mount_free_many(BindMount *b, size_t n) { + size_t i; assert(b || n == 0); @@ -1372,7 +1439,7 @@ void bind_mount_free_many(BindMount *b, unsigned n) { free(b); } -int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) { +int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) { _cleanup_free_ char *s = NULL, *d = NULL; BindMount *c; @@ -1395,19 +1462,18 @@ int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) { *b = c; c[(*n) ++] = (BindMount) { - .source = s, - .destination = d, + .source = TAKE_PTR(s), + .destination = TAKE_PTR(d), .read_only = item->read_only, .recursive = item->recursive, .ignore_enoent = item->ignore_enoent, }; - s = d = NULL; return 0; } -void temporary_filesystem_free_many(TemporaryFileSystem *t, unsigned n) { - unsigned i; +void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n) { + size_t i; assert(t || n == 0); @@ -1421,7 +1487,7 @@ void temporary_filesystem_free_many(TemporaryFileSystem *t, unsigned n) { int temporary_filesystem_add( TemporaryFileSystem **t, - unsigned *n, + size_t *n, const char *path, const char *options) { @@ -1449,11 +1515,10 @@ int temporary_filesystem_add( *t = c; c[(*n) ++] = (TemporaryFileSystem) { - .path = p, - .options = o, + .path = TAKE_PTR(p), + .options = TAKE_PTR(o), }; - p = o = NULL; return 0; } @@ -1491,8 +1556,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { return -errno; } - *path = x; - x = NULL; + *path = TAKE_PTR(x); return 0; } @@ -1608,19 +1672,7 @@ static const char *const protect_home_table[_PROTECT_HOME_MAX] = { [PROTECT_HOME_TMPFS] = "tmpfs", }; -DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome); - -ProtectHome parse_protect_home_or_bool(const char *s) { - int r; - - r = parse_boolean(s); - if (r > 0) - return PROTECT_HOME_YES; - if (r == 0) - return PROTECT_HOME_NO; - - return protect_home_from_string(s); -} +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES); static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { [PROTECT_SYSTEM_NO] = "no", @@ -1629,19 +1681,7 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { [PROTECT_SYSTEM_STRICT] = "strict", }; -DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem); - -ProtectSystem parse_protect_system_or_bool(const char *s) { - int r; - - r = parse_boolean(s); - if (r > 0) - return PROTECT_SYSTEM_YES; - if (r == 0) - return PROTECT_SYSTEM_NO; - - return protect_system_from_string(s); -} +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES); static const char* const namespace_type_table[] = { [NAMESPACE_MOUNT] = "mnt", diff --git a/src/core/namespace.h b/src/core/namespace.h index 3d56a7302d..1188c6d595 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -2,23 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2016 Djalal Harouni - - 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/>. + Copyright © 2016 Djalal Harouni ***/ typedef struct NamespaceInfo NamespaceInfo; @@ -63,6 +47,7 @@ typedef enum ProtectSystem { struct NamespaceInfo { bool ignore_protect_paths:1; bool private_dev:1; + bool private_mounts:1; bool protect_control_groups:1; bool protect_kernel_tunables:1; bool protect_kernel_modules:1; @@ -91,9 +76,9 @@ int setup_namespace( char **inaccessible_paths, char **empty_directories, const BindMount *bind_mounts, - unsigned n_bind_mounts, + size_t n_bind_mounts, const TemporaryFileSystem *temporary_filesystems, - unsigned n_temporary_filesystems, + size_t n_temporary_filesystems, const char *tmp_dir, const char *var_tmp_dir, ProtectHome protect_home, @@ -110,17 +95,15 @@ int setup_netns(int netns_storage_socket[2]); const char* protect_home_to_string(ProtectHome p) _const_; ProtectHome protect_home_from_string(const char *s) _pure_; -ProtectHome parse_protect_home_or_bool(const char *s); const char* protect_system_to_string(ProtectSystem p) _const_; ProtectSystem protect_system_from_string(const char *s) _pure_; -ProtectSystem parse_protect_system_or_bool(const char *s); -void bind_mount_free_many(BindMount *b, unsigned n); -int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item); +void bind_mount_free_many(BindMount *b, size_t n); +int bind_mount_add(BindMount **b, size_t *n, const BindMount *item); -void temporary_filesystem_free_many(TemporaryFileSystem *t, unsigned n); -int temporary_filesystem_add(TemporaryFileSystem **t, unsigned *n, +void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n); +int temporary_filesystem_add(TemporaryFileSystem **t, size_t *n, const char *path, const char *options); const char* namespace_type_to_string(NamespaceType t) _const_; diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 645c8f1659..415b3f5d84 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -62,6 +62,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="GetUnitByControlGroup"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="LoadUnit"/> <allow send_destination="org.freedesktop.systemd1" @@ -114,6 +118,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="DumpByFileDescriptor"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListUnitFiles"/> <allow send_destination="org.freedesktop.systemd1" @@ -140,6 +148,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="LookupDynamicUserByUID"/> + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" + send_member="GetDynamicUsers"/> + <!-- Completely open to anyone: org.freedesktop.systemd1.Unit interface --> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/org.freedesktop.systemd1.policy.in b/src/core/org.freedesktop.systemd1.policy.in index 648221b85c..001408d34a 100644 --- a/src/core/org.freedesktop.systemd1.policy.in +++ b/src/core/org.freedesktop.systemd1.policy.in @@ -47,6 +47,7 @@ <allow_inactive>auth_admin</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.systemd1.reload-daemon org.freedesktop.systemd1.manage-units</annotate> </action> <action id="org.freedesktop.systemd1.set-environment"> diff --git a/src/core/path.c b/src/core/path.c index 1893d8de45..68b13b610a 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sys/epoll.h> @@ -80,7 +62,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { (void) sd_event_source_set_description(s->event_source, "path"); - /* This function assumes the path was passed through path_kill_slashes()! */ + /* This function assumes the path was passed through path_simplify()! */ assert(!strstr(s->path, "//")); for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { @@ -302,7 +284,7 @@ static int path_verify(Path *p) { if (!p->specs) { log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -438,7 +420,7 @@ static void path_set_state(Path *p, PathState state) { if (state != old_state) log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state)); - unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0); } static void path_enter_waiting(Path *p, bool initial, bool recheck); diff --git a/src/core/path.h b/src/core/path.h index 41e31986f2..4d4b6236c2 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Path Path; typedef struct PathSpec PathSpec; @@ -92,3 +73,5 @@ PathType path_type_from_string(const char *s) _pure_; const char* path_result_to_string(PathResult i) _const_; PathResult path_result_from_string(const char *s) _pure_; + +DEFINE_CAST(PATH, Path); diff --git a/src/core/scope.c b/src/core/scope.c index 5b9c2bb3c4..751556fecf 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 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 <errno.h> #include <unistd.h> @@ -105,13 +87,15 @@ static void scope_set_state(Scope *s, ScopeState state) { if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) s->timer_event_source = sd_event_source_unref(s->timer_event_source); - if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) + if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) { unit_unwatch_all_pids(UNIT(s)); + unit_dequeue_rewatch_pids(UNIT(s)); + } if (state != old_state) log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0); } static int scope_add_default_dependencies(Scope *s) { @@ -144,7 +128,7 @@ static int scope_verify(Scope *s) { !MANAGER_IS_RELOADING(UNIT(s)->manager) && !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); - return -EINVAL; + return -ENOENT; } return 0; @@ -225,7 +209,7 @@ static int scope_coldplug(Unit *u) { } if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED)) - unit_watch_all_pids(UNIT(s)); + (void) unit_enqueue_rewatch_pids(u); bus_scope_track_controller(s); @@ -270,7 +254,12 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { if (s->result == SCOPE_SUCCESS) s->result = f; - unit_watch_all_pids(UNIT(s)); + /* Before sending any signal, make sure we track all members of this cgroup */ + (void) unit_watch_all_pids(UNIT(s)); + + /* Also, enqueue a job that we recheck all our PIDs a bit later, given that it's likely some processes have + * died now */ + (void) unit_enqueue_rewatch_pids(UNIT(s)); /* If we have a controller set let's ask the controller nicely to terminate the scope, instead of us going * directly into SIGTERM berserk mode */ @@ -353,6 +342,9 @@ static int scope_start(Unit *u) { s->result = SCOPE_SUCCESS; scope_set_state(s, SCOPE_RUNNING); + + /* Start watching the PIDs currently in the scope */ + (void) unit_enqueue_rewatch_pids(UNIT(s)); return 1; } @@ -468,17 +460,13 @@ static void scope_notify_cgroup_empty_event(Unit *u) { } static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { - assert(u); /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to * watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original * process we watched was probably the parent of them, and they are hence now our children. */ - unit_tidy_watch_pids(u, 0, 0); - unit_watch_all_pids(u); - /* If the PID set is empty now, then let's finish this off. */ - unit_synthesize_cgroup_empty_event(u); + (void) unit_enqueue_rewatch_pids(u); } static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { @@ -528,13 +516,9 @@ int scope_abandon(Scope *s) { scope_set_state(s, SCOPE_ABANDONED); - /* The client is no longer watching the remaining processes, - * so let's step in here, under the assumption that the - * remaining processes will be sooner or later reassigned to - * us as parent. */ - - unit_tidy_watch_pids(UNIT(s), 0, 0); - unit_watch_all_pids(UNIT(s)); + /* The client is no longer watching the remaining processes, so let's step in here, under the assumption that + * the remaining processes will be sooner or later reassigned to us as parent. */ + (void) unit_enqueue_rewatch_pids(UNIT(s)); return 0; } @@ -551,7 +535,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) { return scope_state_to_string(SCOPE(u)->state); } -static void scope_enumerate(Manager *m) { +static void scope_enumerate_perpetual(Manager *m) { Unit *u; int r; @@ -600,6 +584,7 @@ const UnitVTable scope_vtable = { .can_transient = true, .can_delegate = true, + .once_only = true, .init = scope_init, .load = scope_load, @@ -632,5 +617,5 @@ const UnitVTable scope_vtable = { .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, - .enumerate = scope_enumerate, + .enumerate_perpetual = scope_enumerate_perpetual, }; diff --git a/src/core/scope.h b/src/core/scope.h index ca7c6c868f..c38afb5e5d 100644 --- a/src/core/scope.h +++ b/src/core/scope.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - typedef struct Scope Scope; #include "cgroup.h" @@ -59,3 +40,5 @@ int scope_abandon(Scope *s); const char* scope_result_to_string(ScopeResult i) _const_; ScopeResult scope_result_from_string(const char *s) _pure_; + +DEFINE_CAST(SCOPE, Scope); diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 475c3181c9..39e994afd7 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -1,21 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2012 Dan Walsh - - 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/>. + Copyright © 2012 Dan Walsh ***/ #include "selinux-access.h" diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index dd48d0654e..59f2e60c77 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -2,22 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2012 Dan Walsh - - 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/>. + Copyright © 2012 Dan Walsh ***/ #include "sd-bus.h" diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index 7135542106..bac1aa3ff6 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <stdio.h> @@ -47,13 +29,15 @@ int mac_selinux_setup(bool *loaded_policy) { usec_t before_load, after_load; char *con; int r; - union selinux_callback cb; + static const union selinux_callback cb = { + .func_log = null_log, + }; + bool initialized = false; assert(loaded_policy); /* Turn off all of SELinux' own logging, we want to do that */ - cb.func_log = null_log; selinux_set_callback(SELINUX_CB_LOG, cb); /* Don't load policy in the initrd if we don't appear to have diff --git a/src/core/selinux-setup.h b/src/core/selinux-setup.h index 410991c49f..ad0d4f65dc 100644 --- a/src/core/selinux-setup.h +++ b/src/core/selinux-setup.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 <stdbool.h> int mac_selinux_setup(bool *loaded_policy); diff --git a/src/core/service.c b/src/core/service.c index df36019f62..db1356c417 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <signal.h> @@ -396,6 +378,9 @@ static void service_done(Unit *u) { s->bus_name_owner = mfree(s->bus_name_owner); + s->usb_function_descriptors = mfree(s->usb_function_descriptors); + s->usb_function_strings = mfree(s->usb_function_strings); + service_close_socket_fd(s); s->peer = socket_peer_unref(s->peer); @@ -553,37 +538,37 @@ static int service_verify(Service *s) { if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) { log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) { log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) { log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { log_unit_error(UNIT(s), "Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) { log_unit_error(UNIT(s), "Service has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) { log_unit_error(UNIT(s), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->type == SERVICE_DBUS && !s->bus_name) { log_unit_error(UNIT(s), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->bus_name && s->type != SERVICE_DBUS) @@ -591,7 +576,7 @@ static int service_verify(Service *s) { if (s->exec_context.pam_name && !IN_SET(s->kill_context.kill_mode, KILL_CONTROL_GROUP, KILL_MIXED)) { log_unit_error(UNIT(s), "Service has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->usb_function_descriptors && !s->usb_function_strings) @@ -725,10 +710,9 @@ static int service_add_extras(Service *s) { if (r < 0) return r; - if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) - s->notify_access = NOTIFY_MAIN; - - if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) + /* If the service needs the notify socket, let's enable it automatically. */ + if (s->notify_access == NOTIFY_NONE && + (s->type == SERVICE_NOTIFY || s->watchdog_usec > 0 || s->n_fd_store_max > 0)) s->notify_access = NOTIFY_MAIN; r = service_add_default_dependencies(s); @@ -876,7 +860,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { if (s->n_fd_store_max > 0) fprintf(f, "%sFile Descriptor Store Max: %u\n" - "%sFile Descriptor Store Current: %u\n", + "%sFile Descriptor Store Current: %zu\n", prefix, s->n_fd_store_max, prefix, s->n_fd_store); @@ -1069,8 +1053,10 @@ static void service_set_state(Service *s, ServiceState state) { s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } - if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) { unit_unwatch_all_pids(UNIT(s)); + unit_dequeue_rewatch_pids(UNIT(s)); + } if (!IN_SET(state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, @@ -1091,7 +1077,9 @@ static void service_set_state(Service *s, ServiceState state) { if (old_state != state) log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state)); - unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); + unit_notify(UNIT(s), table[old_state], table[state], + (s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) | + (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0)); } static usec_t service_coldplug_timeout(Service *s) { @@ -1163,17 +1151,15 @@ static int service_coldplug(Unit *u) { return r; } - if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) - unit_watch_all_pids(UNIT(s)); - - if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) - service_start_watchdog(s); - if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) { + (void) unit_enqueue_rewatch_pids(u); (void) unit_setup_dynamic_creds(u); (void) unit_setup_exec_runtime(u); } + if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + service_start_watchdog(s); + if (UNIT_ISSET(s->accept_socket)) { Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket)); @@ -1247,10 +1233,8 @@ static int service_collect_fds(Service *s, continue; if (!rfds) { - rfds = cfds; + rfds = TAKE_PTR(cfds); rn_socket_fds = cn_fds; - - cfds = NULL; } else { int *t; @@ -1302,14 +1286,11 @@ static int service_collect_fds(Service *s, rfd_names[n_fds] = NULL; } - *fds = rfds; - *fd_names = rfd_names; + *fds = TAKE_PTR(rfds); + *fd_names = TAKE_PTR(rfd_names); *n_socket_fds = rn_socket_fds; *n_storage_fds = rn_storage_fds; - rfds = NULL; - rfd_names = NULL; - return 0; } @@ -1606,6 +1587,7 @@ static bool service_will_restart(Unit *u) { return false; if (UNIT(s)->job->type == JOB_START) return true; + return false; } @@ -1692,7 +1674,8 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { s->result = f; service_unwatch_control_pid(s); - unit_watch_all_pids(UNIT(s)); + + (void) unit_enqueue_rewatch_pids(UNIT(s)); s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; if (s->control_command) { @@ -1744,7 +1727,12 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (s->result == SERVICE_SUCCESS) s->result = f; - unit_watch_all_pids(UNIT(s)); + /* Before sending any signal, make sure we track all members of this cgroup */ + (void) unit_watch_all_pids(UNIT(s)); + + /* Also, enqueue a job that we recheck all our PIDs a bit later, given that it's likely some processes have + * died now */ + (void) unit_enqueue_rewatch_pids(UNIT(s)); r = unit_kill_context( UNIT(s), @@ -1785,7 +1773,7 @@ fail: static void service_enter_stop_by_notify(Service *s) { assert(s); - unit_watch_all_pids(UNIT(s)); + (void) unit_enqueue_rewatch_pids(UNIT(s)); service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); @@ -1802,7 +1790,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { s->result = f; service_unwatch_control_pid(s); - unit_watch_all_pids(UNIT(s)); + (void) unit_enqueue_rewatch_pids(UNIT(s)); s->control_command = s->exec_command[SERVICE_EXEC_STOP]; if (s->control_command) { @@ -1855,10 +1843,11 @@ static void service_enter_running(Service *s, ServiceResult f) { service_unwatch_control_pid(s); - if (service_good(s)) { + if (s->result != SERVICE_SUCCESS) + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); + else if (service_good(s)) { - /* If there are any queued up sd_notify() - * notifications, process them now */ + /* If there are any queued up sd_notify() notifications, process them now */ if (s->notify_state == NOTIFY_RELOADING) service_enter_reload_by_notify(s); else if (s->notify_state == NOTIFY_STOPPING) @@ -1868,9 +1857,7 @@ static void service_enter_running(Service *s, ServiceResult f) { service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec)); } - } else if (f != SERVICE_SUCCESS) - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - else if (s->remain_after_exit) + } else if (s->remain_after_exit) service_set_state(s, SERVICE_EXITED); else service_enter_stop(s, SERVICE_SUCCESS); @@ -1955,7 +1942,7 @@ static void service_enter_start(Service *s) { /* There's no command line configured for the main command? Hmm, that is strange. This can only * happen if the configuration changes at runtime. In this case, let's enter a failure * state. */ - log_unit_error(UNIT(s), "There's no 'start' task anymore we could start: %m"); + log_unit_error(UNIT(s), "There's no 'start' task anymore we could start."); r = -ENXIO; goto fail; } @@ -2084,8 +2071,7 @@ static void service_enter_restart(Service *s) { LOG_UNIT_ID(UNIT(s)), LOG_UNIT_INVOCATION_ID(UNIT(s)), LOG_UNIT_MESSAGE(UNIT(s), "Scheduled restart job, restart counter is at %u.", s->n_restarts), - "N_RESTARTS=%u", s->n_restarts, - NULL); + "N_RESTARTS=%u", s->n_restarts); /* Notify clients about changed restart counter */ unit_add_to_dbus_queue(UNIT(s)); @@ -2551,8 +2537,7 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char state = STATE_EXEC_COMMAND_PATH; break; case STATE_EXEC_COMMAND_PATH: - path = arg; - arg = NULL; + path = TAKE_PTR(arg); state = STATE_EXEC_COMMAND_ARGS; if (!path_is_absolute(path)) @@ -2904,7 +2889,7 @@ static int service_demand_pid_file(Service *s) { return -ENOMEM; } - path_kill_slashes(ps->path); + path_simplify(ps->path, false); /* PATH_CHANGED would not be enough. There are daemons (sendmail) that * keep their PID file open all the time. */ @@ -3083,8 +3068,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "EXIT_CODE=%s", sigchld_code_to_string(code), "EXIT_STATUS=%i", status, LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - NULL); + LOG_UNIT_INVOCATION_ID(u)); if (s->result == SERVICE_SUCCESS) s->result = f; @@ -3226,7 +3210,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { * The PID file might actually be created by a START_POST * script. In that case don't worry if the loading fails. */ - has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + has_start_post = s->exec_command[SERVICE_EXEC_START_POST]; r = service_load_pid_file(s, !has_start_post); if (!has_start_post && r < 0) { r = service_demand_pid_file(s); @@ -3307,11 +3291,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch, * under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched * was probably the parent of them, and they are hence now our children. */ - unit_tidy_watch_pids(u, s->main_pid, s->control_pid); - unit_watch_all_pids(u); - - /* If the PID set is empty now, then let's check if the cgroup is empty too and finish off the unit. */ - unit_synthesize_cgroup_empty_event(u); + (void) unit_enqueue_rewatch_pids(u); } static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { @@ -3397,10 +3377,15 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us break; case SERVICE_AUTO_RESTART: - log_unit_info(UNIT(s), - s->restart_usec > 0 ? - "Service hold-off time over, scheduling restart." : - "Service has no hold-off time, scheduling restart."); + if (s->restart_usec > 0) { + char buf_restart[FORMAT_TIMESPAN_MAX]; + log_unit_info(UNIT(s), + "Service RestartSec=%s expired, scheduling restart.", + format_timespan(buf_restart, sizeof buf_restart, s->restart_usec, USEC_PER_SEC)); + } else + log_unit_info(UNIT(s), + "Service has no hold-off time (RestartSec=0), scheduling restart."); + service_enter_restart(s); break; diff --git a/src/core/service.h b/src/core/service.h index 5c5b24e3ef..9c06e91883 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Service Service; typedef struct ServiceFDStore ServiceFDStore; @@ -27,6 +8,8 @@ typedef struct ServiceFDStore ServiceFDStore; #include "kill.h" #include "path.h" #include "ratelimit.h" +#include "socket.h" +#include "unit.h" typedef enum ServiceRestart { SERVICE_RESTART_NO, @@ -183,7 +166,7 @@ struct Service { NotifyState notify_state; ServiceFDStore *fd_store; - unsigned n_fd_store; + size_t n_fd_store; unsigned n_fd_store_max; unsigned n_keep_fd_store; @@ -217,3 +200,5 @@ NotifyState notify_state_from_string(const char *s) _pure_; const char* service_result_to_string(ServiceResult i) _const_; ServiceResult service_result_from_string(const char *s) _pure_; + +DEFINE_CAST(SERVICE, Service); diff --git a/src/core/show-status.c b/src/core/show-status.c index 40658a2e16..63262cc716 100644 --- a/src/core/show-status.c +++ b/src/core/show-status.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 "alloc-util.h" #include "fd-util.h" diff --git a/src/core/show-status.h b/src/core/show-status.h index a13d52484c..1a80de33d9 100644 --- a/src/core/show-status.h +++ b/src/core/show-status.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #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 <stdbool.h> #include "macro.h" diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 58c9a9de79..038345b752 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -1,21 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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/>. + Copyright © 2010 ProFUSION embedded systems ***/ #include <errno.h> @@ -52,8 +37,6 @@ #include "virt.h" #include "watchdog.h" -#define FINALIZE_ATTEMPTS 50 - #define SYNC_PROGRESS_ATTEMPTS 3 #define SYNC_TIMEOUT_USEC (10*USEC_PER_SEC) @@ -269,11 +252,10 @@ static void sync_with_progress(void) { int main(int argc, char *argv[]) { bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; - bool in_container, use_watchdog = false; + bool in_container, use_watchdog = false, can_initrd; _cleanup_free_ char *cgroup = NULL; char *arguments[3]; - unsigned retries; - int cmd, r; + int cmd, r, umount_log_level = LOG_INFO; static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; char *watchdog_device; @@ -318,7 +300,7 @@ int main(int argc, char *argv[]) { (void) cg_get_root_path(&cgroup); in_container = detect_container() > 0; - use_watchdog = !!getenv("WATCHDOG_USEC"); + use_watchdog = getenv("WATCHDOG_USEC"); watchdog_device = getenv("WATCHDOG_DEVICE"); if (watchdog_device) { r = watchdog_set_device(watchdog_device); @@ -349,9 +331,10 @@ int main(int argc, char *argv[]) { need_swapoff = !in_container; need_loop_detach = !in_container; need_dm_detach = !in_container; + can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0; /* Unmount all mountpoints, swaps, and loopback devices */ - for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { + for (;;) { bool changed = false; if (use_watchdog) @@ -366,7 +349,7 @@ int main(int argc, char *argv[]) { if (need_umount) { log_info("Unmounting file systems."); - r = umount_all(&changed); + r = umount_all(&changed, umount_log_level); if (r == 0) { need_umount = false; log_info("All filesystems unmounted."); @@ -390,7 +373,7 @@ int main(int argc, char *argv[]) { if (need_loop_detach) { log_info("Detaching loop devices."); - r = loopback_detach_all(&changed); + r = loopback_detach_all(&changed, umount_log_level); if (r == 0) { need_loop_detach = false; log_info("All loop devices detached."); @@ -402,7 +385,7 @@ int main(int argc, char *argv[]) { if (need_dm_detach) { log_info("Detaching DM devices."); - r = dm_detach_all(&changed); + r = dm_detach_all(&changed, umount_log_level); if (r == 0) { need_dm_detach = false; log_info("All DM devices detached."); @@ -413,10 +396,19 @@ int main(int argc, char *argv[]) { } if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { - if (retries > 0) - log_info("All filesystems, swaps, loop devices, DM devices detached."); + log_info("All filesystems, swaps, loop devices and DM devices detached."); /* Yay, done */ - goto initrd_jump; + break; + } + + if (!changed && umount_log_level == LOG_INFO && !can_initrd) { + /* There are things we cannot get rid of. Loop one more time + * with LOG_ERR to inform the user. Note that we don't need + * to do this if there is a initrd to switch to, because that + * one is likely to get rid of the remounting mounts. If not, + * it will log about them. */ + umount_log_level = LOG_ERR; + continue; } /* If in this iteration we didn't manage to @@ -427,21 +419,16 @@ int main(int argc, char *argv[]) { need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", need_dm_detach ? " DM devices," : ""); - goto initrd_jump; + break; } - log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.", - retries + 1, + log_debug("Couldn't finalize remaining %s%s%s%s trying again.", need_umount ? " file systems," : "", need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", need_dm_detach ? " DM devices," : ""); } - log_error("Too many iterations, giving up."); - - initrd_jump: - /* We're done with the watchdog. */ watchdog_free_device(); @@ -450,8 +437,7 @@ int main(int argc, char *argv[]) { arguments[2] = NULL; execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); - if (!in_container && !in_initrd() && - access("/run/initramfs/shutdown", X_OK) == 0) { + if (can_initrd) { r = switch_root_initramfs(); if (r >= 0) { argv[0] = (char*) "/shutdown"; diff --git a/src/core/slice.c b/src/core/slice.c index 1c4574b8bb..58f18a4dad 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 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 <errno.h> @@ -55,7 +37,7 @@ static void slice_set_state(Slice *t, SliceState state) { slice_state_to_string(old_state), slice_state_to_string(state)); - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0); } static int slice_add_parent_slice(Slice *s) { @@ -110,7 +92,7 @@ static int slice_verify(Slice *s) { if (!slice_name_is_valid(UNIT(s)->id)) { log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id); - return -EINVAL; + return -ENOEXEC; } r = slice_build_parent_slice(UNIT(s)->id, &parent); @@ -119,7 +101,7 @@ static int slice_verify(Slice *s) { if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) { log_unit_error(UNIT(s), "Located outside of parent slice. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -339,7 +321,7 @@ static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) { return 0; } -static void slice_enumerate(Manager *m) { +static void slice_enumerate_perpetual(Manager *m) { Unit *u; int r; @@ -396,7 +378,7 @@ const UnitVTable slice_vtable = { .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, - .enumerate = slice_enumerate, + .enumerate_perpetual = slice_enumerate_perpetual, .status_message_formats = { .finished_start_job = { diff --git a/src/core/slice.h b/src/core/slice.h index 418327e933..4678c085c3 100644 --- a/src/core/slice.h +++ b/src/core/slice.h @@ -1,24 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 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 "unit.h" typedef struct Slice Slice; @@ -31,3 +14,5 @@ struct Slice { }; extern const UnitVTable slice_vtable; + +DEFINE_CAST(SLICE, Slice); diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index b0d3612d69..50115c0454 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -1,23 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation + Copyright © 2013 Intel Corporation Authors: Nathaniel Chen <nathaniel.chen@intel.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ #include <dirent.h> diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h index 8c2de5fdc5..b65daafcd8 100644 --- a/src/core/smack-setup.h +++ b/src/core/smack-setup.h @@ -2,24 +2,9 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation + Copyright © 2013 Intel Corporation Authors: Nathaniel Chen <nathaniel.chen@intel.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ int mac_smack_setup(bool *loaded_policy); diff --git a/src/core/socket.c b/src/core/socket.c index 41988788b8..56d32225c4 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <arpa/inet.h> #include <errno.h> @@ -454,32 +436,32 @@ static int socket_verify(Socket *s) { if (!s->ports) { log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->accept && have_non_accept_socket(s)) { log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->accept && s->max_connections <= 0) { log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->accept && UNIT_DEREF(s->service)) { log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing."); - return -EINVAL; + return -ENOEXEC; } if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) { log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -629,8 +611,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { remote->socket = s; - *p = remote; - remote = NULL; + *p = TAKE_PTR(remote); return 1; } @@ -800,27 +781,28 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sKeepAliveTimeSec: %s\n", prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_time, USEC_PER_SEC)); - if (s->keep_alive_interval) + if (s->keep_alive_interval > 0) fprintf(f, "%sKeepAliveIntervalSec: %s\n", prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_interval, USEC_PER_SEC)); - if (s->keep_alive_cnt) + if (s->keep_alive_cnt > 0) fprintf(f, "%sKeepAliveProbes: %u\n", prefix, s->keep_alive_cnt); - if (s->defer_accept) + if (s->defer_accept > 0) fprintf(f, "%sDeferAcceptSec: %s\n", prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->defer_accept, USEC_PER_SEC)); LIST_FOREACH(port, p, s->ports) { - if (p->type == SOCKET_SOCKET) { + switch (p->type) { + case SOCKET_SOCKET: { + _cleanup_free_ char *k = NULL; const char *t; int r; - char *k = NULL; r = socket_address_print(&p->address, &k); if (r < 0) @@ -829,15 +811,20 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { t = k; fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t); - free(k); - } else if (p->type == SOCKET_SPECIAL) + break; + } + case SOCKET_SPECIAL: fprintf(f, "%sListenSpecial: %s\n", prefix, p->path); - else if (p->type == SOCKET_USB_FUNCTION) + break; + case SOCKET_USB_FUNCTION: fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path); - else if (p->type == SOCKET_MQUEUE) + break; + case SOCKET_MQUEUE: fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path); - else + break; + default: fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); + } } fprintf(f, @@ -1047,43 +1034,43 @@ static void socket_apply_socket_options(Socket *s, int fd) { assert(fd >= 0); if (s->keep_alive) { - int b = s->keep_alive; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0) + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0) log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m"); } - if (s->keep_alive_time) { + if (s->keep_alive_time > 0) { int value = s->keep_alive_time / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m"); } - if (s->keep_alive_interval) { + if (s->keep_alive_interval > 0) { int value = s->keep_alive_interval / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m"); } - if (s->keep_alive_cnt) { + if (s->keep_alive_cnt > 0) { int value = s->keep_alive_cnt; if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m"); } - if (s->defer_accept) { + if (s->defer_accept > 0) { int value = s->defer_accept / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m"); } if (s->no_delay) { - int b = s->no_delay; + int one = 1; if (s->socket_protocol == IPPROTO_SCTP) { - if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &b, sizeof(b)) < 0) + if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &one, sizeof(one)) < 0) log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m"); } else { - if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0) + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m"); } } @@ -1114,7 +1101,6 @@ static void socket_apply_socket_options(Socket *s, int fd) { int value = (int) s->receive_buffer; /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m"); @@ -1204,7 +1190,7 @@ static int fifo_address_create( return r; /* Enforce the right access mode for the fifo */ - old_mask = umask(~ socket_mode); + old_mask = umask(~socket_mode); /* Include the original umask in our mask */ (void) umask(~socket_mode | old_mask); @@ -1238,10 +1224,7 @@ static int fifo_address_create( goto fail; } - r = fd; - fd = -1; - - return r; + return TAKE_FD(fd); fail: mac_selinux_create_file_clear(); @@ -1251,7 +1234,6 @@ fail: static int special_address_create(const char *path, bool writable) { _cleanup_close_ int fd = -1; struct stat st; - int r; assert(path); @@ -1266,16 +1248,12 @@ static int special_address_create(const char *path, bool writable) { if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) return -EEXIST; - r = fd; - fd = -1; - - return r; + return TAKE_FD(fd); } static int usbffs_address_create(const char *path) { _cleanup_close_ int fd = -1; struct stat st; - int r; assert(path); @@ -1290,10 +1268,7 @@ static int usbffs_address_create(const char *path) { if (!S_ISREG(st.st_mode)) return -EEXIST; - r = fd; - fd = -1; - - return r; + return TAKE_FD(fd); } static int mq_address_create( @@ -1306,7 +1281,6 @@ static int mq_address_create( struct stat st; mode_t old_mask; struct mq_attr _attr, *attr = NULL; - int r; assert(path); @@ -1320,7 +1294,7 @@ static int mq_address_create( } /* Enforce the right access mode for the mq */ - old_mask = umask(~ mq_mode); + old_mask = umask(~mq_mode); /* Include the original umask in our mask */ (void) umask(~mq_mode | old_mask); @@ -1338,10 +1312,7 @@ static int mq_address_create( st.st_gid != getgid()) return -EEXIST; - r = fd; - fd = -1; - - return r; + return TAKE_FD(fd); } static int socket_symlink(Socket *s) { @@ -1395,13 +1366,14 @@ static int usbffs_select_ep(const struct dirent *d) { static int usbffs_dispatch_eps(SocketPort *p) { _cleanup_free_ struct dirent **ent = NULL; - int r, i, n, k; + size_t n, k, i; + int r; r = scandir(p->path, &ent, usbffs_select_ep, alphasort); if (r < 0) return -errno; - n = r; + n = (size_t) r; p->auxiliary_fds = new(int, n); if (!p->auxiliary_fds) return -ENOMEM; @@ -1416,15 +1388,13 @@ static int usbffs_dispatch_eps(SocketPort *p) { if (!ep) return -ENOMEM; - path_kill_slashes(ep); + path_simplify(ep, false); r = usbffs_address_create(ep); if (r < 0) goto fail; - p->auxiliary_fds[k] = r; - - ++k; + p->auxiliary_fds[k++] = r; free(ent[i]); } @@ -1439,7 +1409,9 @@ fail: } static int socket_determine_selinux_label(Socket *s, char **ret) { + Service *service; ExecCommand *c; + _cleanup_free_ char *path = NULL; int r; assert(s); @@ -1461,11 +1433,16 @@ static int socket_determine_selinux_label(Socket *s, char **ret) { if (!UNIT_ISSET(s->service)) goto no_label; - c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]; + service = SERVICE(UNIT_DEREF(s->service)); + c = service->exec_command[SERVICE_EXEC_START]; if (!c) goto no_label; - r = mac_selinux_get_create_label_from_exe(c->path, ret); + r = chase_symlinks(c->path, service->exec_context.root_directory, CHASE_PREFIX_ROOT, &path); + if (r < 0) + goto no_label; + + r = mac_selinux_get_create_label_from_exe(path, ret); if (IN_SET(r, -EPERM, -EOPNOTSUPP)) goto no_label; } @@ -1813,7 +1790,7 @@ static void socket_set_state(Socket *s, SocketState state) { if (state != old_state) log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0); } static int socket_coldplug(Unit *u) { @@ -2261,18 +2238,17 @@ static void socket_enter_running(Socket *s, int cfd) { log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); if (cfd >= 0) - cfd = safe_close(cfd); + goto refuse; else flush_ports(s); return; } - if (!ratelimit_test(&s->trigger_limit)) { - safe_close(cfd); + if (!ratelimit_below(&s->trigger_limit)) { log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); - return; + goto refuse; } if (cfd < 0) { @@ -2310,15 +2286,13 @@ static void socket_enter_running(Socket *s, int cfd) { if (s->n_connections >= s->max_connections) { log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.", s->n_connections); - safe_close(cfd); - return; + goto refuse; } if (s->max_connections_per_source > 0) { r = socket_acquire_peer(s, cfd, &p); if (r < 0) { - safe_close(cfd); - return; + goto refuse; } else if (r > 0 && p->n_ref > s->max_connections_per_source) { _cleanup_free_ char *t = NULL; @@ -2327,8 +2301,7 @@ static void socket_enter_running(Socket *s, int cfd) { log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source %s, dropping connection.", p->n_ref, strnull(t)); - safe_close(cfd); - return; + goto refuse; } } @@ -2344,8 +2317,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* ENOTCONN is legitimate if TCP RST was received. * This connection is over, but the socket unit lives on. */ log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); - safe_close(cfd); - return; + goto refuse; } r = unit_name_to_prefix(UNIT(s)->id, &prefix); @@ -2373,8 +2345,7 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; - service->peer = p; /* Pass ownership of the peer reference */ - p = NULL; + service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { @@ -2390,6 +2361,11 @@ static void socket_enter_running(Socket *s, int cfd) { return; +refuse: + s->n_refused++; + safe_close(cfd); + return; + fail: log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", cfd >= 0 ? "template" : "non-template", @@ -2533,6 +2509,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); unit_serialize_item(u, f, "result", socket_result_to_string(s->result)); unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); + unit_serialize_item_format(u, f, "n-refused", "%u", s->n_refused); if (s->control_pid > 0) unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); @@ -2613,6 +2590,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, log_unit_debug(u, "Failed to parse n-accepted value: %s", value); else s->n_accepted += k; + } else if (streq(key, "n-refused")) { + unsigned k; + + if (safe_atou(value, &k) < 0) + log_unit_debug(u, "Failed to parse n-refused value: %s", value); + else + s->n_refused += k; } else if (streq(key, "control-pid")) { pid_t pid; @@ -3120,8 +3104,9 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use } int socket_collect_fds(Socket *s, int **fds) { - int *rfds, k = 0, n = 0; + size_t k = 0, n = 0; SocketPort *p; + int *rfds; assert(s); assert(fds); @@ -3144,7 +3129,7 @@ int socket_collect_fds(Socket *s, int **fds) { return -ENOMEM; LIST_FOREACH(port, p, s->ports) { - int i; + size_t i; if (p->fd >= 0) rfds[k++] = p->fd; @@ -3155,7 +3140,7 @@ int socket_collect_fds(Socket *s, int **fds) { assert(k == n); *fds = rfds; - return n; + return (int) n; } static void socket_reset_failed(Unit *u) { diff --git a/src/core/socket.h b/src/core/socket.h index 84ec9cff08..c4e25db1fc 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -1,31 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Socket Socket; typedef struct SocketPeer SocketPeer; #include "mount.h" #include "service.h" #include "socket-util.h" +#include "unit.h" typedef enum SocketExecCommand { SOCKET_EXEC_START_PRE, @@ -67,7 +49,7 @@ typedef struct SocketPort { SocketType type; int fd; int *auxiliary_fds; - int n_auxiliary_fds; + size_t n_auxiliary_fds; SocketAddress address; char *path; @@ -85,6 +67,7 @@ struct Socket { unsigned n_accepted; unsigned n_connections; + unsigned n_refused; unsigned max_connections; unsigned max_connections_per_source; @@ -195,3 +178,5 @@ SocketResult socket_result_from_string(const char *s) _pure_; const char* socket_port_type_to_string(SocketPort *p) _pure_; SocketType socket_port_type_from_string(const char *p) _pure_; + +DEFINE_CAST(SOCKET, Socket); diff --git a/src/core/swap.c b/src/core/swap.c index ed397c4c9e..b78b1aa266 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <sys/epoll.h> @@ -27,6 +9,7 @@ #include "alloc-util.h" #include "dbus-swap.h" +#include "device.h" #include "escape.h" #include "exit-status.h" #include "fd-util.h" @@ -252,30 +235,34 @@ static int swap_verify(Swap *s) { if (!unit_has_name(UNIT(s), e)) { log_unit_error(UNIT(s), "Value of What= and unit name do not match, not loading."); - return -EINVAL; + return -ENOEXEC; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load."); - return -EINVAL; + return -ENOEXEC; } return 0; } static int swap_load_devnode(Swap *s) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_(udev_device_unrefp) struct udev_device *d = NULL; struct stat st; const char *p; + int r; assert(s); if (stat(s->what, &st) < 0 || !S_ISBLK(st.st_mode)) return 0; - d = udev_device_new_from_devnum(UNIT(s)->manager->udev, 'b', st.st_rdev); - if (!d) + r = udev_device_new_from_stat_rdev(UNIT(s)->manager->udev, &st, &d); + if (r < 0) { + log_unit_full(UNIT(s), r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to allocate udev device for swap %s: %m", s->what); return 0; + } p = udev_device_get_devnode(d); if (!p) @@ -319,7 +306,7 @@ static int swap_load(Unit *u) { return -ENOMEM; } - path_kill_slashes(s->what); + path_simplify(s->what, false); if (!UNIT(s)->description) { r = unit_set_description(u, s->what); @@ -438,7 +425,7 @@ fail: } static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_(udev_device_unrefp) struct udev_device *d = NULL; struct udev_list_entry *item = NULL, *first = NULL; const char *dn; struct stat st; @@ -455,9 +442,12 @@ static int swap_process_new(Manager *m, const char *device, int prio, bool set_f if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode)) return 0; - d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev); - if (!d) + r = udev_device_new_from_stat_rdev(m->udev, &st, &d); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to allocate udev device for swap %s: %m", device); return 0; + } /* Add the main device node */ dn = udev_device_get_devnode(d); @@ -508,7 +498,7 @@ static void swap_set_state(Swap *s, SwapState state) { if (state != old_state) log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0); /* If there other units for the same device node have a job queued it might be worth checking again if it is runnable @@ -1124,7 +1114,7 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) { if (cunescape(dev, UNESCAPE_RELAX, &d) < 0) return log_oom(); - device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags); + device_found_node(m, d, DEVICE_FOUND_SWAP, DEVICE_FOUND_SWAP); k = swap_process_new(m, d, prio, set_flags); if (k < 0) @@ -1179,7 +1169,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v } if (swap->what) - device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true); + device_found_node(m, swap->what, 0, DEVICE_FOUND_SWAP); } else if (swap->just_activated) { @@ -1253,7 +1243,7 @@ static Unit *swap_following(Unit *u) { static int swap_following_set(Unit *u, Set **_set) { Swap *s = SWAP(u), *other; - Set *set; + _cleanup_set_free_ Set *set = NULL; int r; assert(s); @@ -1271,24 +1261,18 @@ static int swap_following_set(Unit *u, Set **_set) { LIST_FOREACH_OTHERS(same_devnode, other, s) { r = set_put(set, other); if (r < 0) - goto fail; + return r; } - *_set = set; + *_set = TAKE_PTR(set); return 1; - -fail: - set_free(set); - return r; } static void swap_shutdown(Manager *m) { assert(m); m->swap_event_source = sd_event_source_unref(m->swap_event_source); - m->proc_swaps = safe_fclose(m->proc_swaps); - m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); } @@ -1301,9 +1285,9 @@ static void swap_enumerate(Manager *m) { m->proc_swaps = fopen("/proc/swaps", "re"); if (!m->proc_swaps) { if (errno == ENOENT) - log_debug("Not swap enabled, skipping enumeration"); + log_debug_errno(errno, "Not swap enabled, skipping enumeration."); else - log_error_errno(errno, "Failed to open /proc/swaps: %m"); + log_warning_errno(errno, "Failed to open /proc/swaps, ignoring: %m"); return; } diff --git a/src/core/swap.h b/src/core/swap.h index fa9d45ac0c..1c0c7fcadc 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -2,26 +2,11 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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/>. + Copyright © 2010 Maarten Lankhorst ***/ #include "libudev.h" +#include "unit.h" typedef struct Swap Swap; @@ -108,3 +93,5 @@ SwapExecCommand swap_exec_command_from_string(const char *s) _pure_; const char* swap_result_to_string(SwapResult i) _const_; SwapResult swap_result_from_string(const char *s) _pure_; + +DEFINE_CAST(SWAP, Swap); diff --git a/src/core/system.conf.in b/src/core/system.conf.in index 08cbe529ba..f0a59a79a5 100644 --- a/src/core/system.conf.in +++ b/src/core/system.conf.in @@ -27,6 +27,7 @@ #RuntimeWatchdogSec=0 #ShutdownWatchdogSec=10min #CapabilityBoundingSet= +#NoNewPrivileges=no #SystemCallArchitectures= #TimerSlackNSec= #DefaultTimerAccuracySec=1min diff --git a/src/core/target.c b/src/core/target.c index 756cbbfb6c..6446767504 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "dbus-target.h" #include "log.h" @@ -44,7 +26,7 @@ static void target_set_state(Target *t, TargetState state) { target_state_to_string(old_state), target_state_to_string(state)); - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0); } static int target_add_default_dependencies(Target *t) { diff --git a/src/core/target.h b/src/core/target.h index 8d44a11545..28f78888dc 100644 --- a/src/core/target.h +++ b/src/core/target.h @@ -1,24 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "unit.h" typedef struct Target Target; @@ -29,3 +12,5 @@ struct Target { }; extern const UnitVTable target_vtable; + +DEFINE_CAST(TARGET, Target); diff --git a/src/core/timer.c b/src/core/timer.c index ddb9c82b87..db202971d3 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> @@ -91,7 +73,7 @@ static int timer_verify(Timer *t) { if (!t->values) { log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); - return -EINVAL; + return -ENOEXEC; } return 0; @@ -277,7 +259,7 @@ static void timer_set_state(Timer *t, TimerState state) { if (state != old_state) log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state)); - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0); } static void timer_enter_waiting(Timer *t, bool initial); @@ -832,6 +814,18 @@ static void timer_time_change(Unit *u) { timer_enter_waiting(t, false); } +static void timer_timezone_change(Unit *u) { + Timer *t = TIMER(u); + + assert(u); + + if (t->state != TIMER_WAITING) + return; + + log_unit_debug(u, "Timezone change, recalculating next elapse."); + timer_enter_waiting(t, false); +} + static const char* const timer_base_table[_TIMER_BASE_MAX] = { [TIMER_ACTIVE] = "OnActiveSec", [TIMER_BOOT] = "OnBootSec", @@ -881,6 +875,7 @@ const UnitVTable timer_vtable = { .reset_failed = timer_reset_failed, .time_change = timer_time_change, + .timezone_change = timer_timezone_change, .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, diff --git a/src/core/timer.h b/src/core/timer.h index 096d48bd34..833aadb0b8 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -1,28 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Timer Timer; #include "calendarspec.h" +#include "unit.h" typedef enum TimerBase { TIMER_ACTIVE, @@ -90,3 +72,5 @@ TimerBase timer_base_from_string(const char *s) _pure_; const char* timer_result_to_string(TimerResult i) _const_; TimerResult timer_result_from_string(const char *s) _pure_; + +DEFINE_CAST(TIMER, Timer); diff --git a/src/core/transaction.c b/src/core/transaction.c index 32ad660026..1c7efb207a 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <fcntl.h> #include <unistd.h> @@ -413,7 +395,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi j->unit->id, unit_id == array ? "ordering cycle" : "dependency", *unit_id, *job_type, - unit_ids, NULL); + unit_ids); if (delete) { const char *status; @@ -422,7 +404,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi "MESSAGE=%s: Job %s/%s deleted to break ordering cycle starting with %s/%s", j->unit->id, delete->unit->id, job_type_to_string(delete->type), j->unit->id, job_type_to_string(j->type), - unit_ids, NULL); + unit_ids); if (log_get_show_color()) status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL; @@ -438,7 +420,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi log_struct(LOG_ERR, "MESSAGE=%s: Unable to break cycle starting with %s/%s", j->unit->id, j->unit->id, job_type_to_string(j->type), - unit_ids, NULL); + unit_ids); return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details."); @@ -919,11 +901,13 @@ int transaction_add_job_and_dependencies( /* by ? by->unit->id : "NA", */ /* by ? job_type_to_string(by->type) : "NA"); */ - if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED)) + /* Safety check that the unit is a valid state, i.e. not in UNIT_STUB or UNIT_MERGED which should only be set + * temporarily. */ + if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_MASKED)) return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); if (type != JOB_STOP) { - r = bus_unit_check_load_state(unit, e); + r = bus_unit_validate_load_state(unit, e); if (r < 0) return r; } @@ -933,7 +917,6 @@ int transaction_add_job_and_dependencies( "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->id); - /* First add the job. */ ret = transaction_add_one_job(tr, type, unit, &is_new); if (!ret) diff --git a/src/core/transaction.h b/src/core/transaction.h index 0d57b27f2e..70d74a4ccb 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - typedef struct Transaction Transaction; #include "hashmap.h" diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in index c582d40977..10b1889b57 100644 --- a/src/core/triggers.systemd.in +++ b/src/core/triggers.systemd.in @@ -2,22 +2,7 @@ # SPDX-License-Identifier: LGPL-2.1+ # # This file is part of systemd. -# -# Copyright 2015 Zbigniew Jędrzejewski-Szmek -# Copyright 2018 Neal Gompa -# -# 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/>. +# Copyright © 2018 Neal Gompa # The contents of this are an example to be copied into systemd.spec. # @@ -84,7 +69,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -P 100500 -- @tmpfilesdir@ +%transfiletriggerin -P 100500 -p <lua> -- @tmpfilesdir@ -- This script will process files installed in @tmpfilesdir@ to create -- tmpfiles automatically. The priority is set such that it will run -- after the sysusers file trigger, but before any other triggers. @@ -97,7 +82,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -- @udevhwdbdir@ +%transfiletriggerin -p <lua> -- @udevhwdbdir@ -- This script will automatically invoke hwdb update if files have been -- installed or updated in @udevhwdbdir@. if posix.access("/run/systemd/system") then @@ -109,7 +94,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -- @catalogdir@ +%transfiletriggerin -p <lua> -- @catalogdir@ -- This script will automatically invoke journal catalog update if files -- have been installed or updated in @catalogdir@. if posix.access("/run/systemd/system") then @@ -121,7 +106,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -- @udevrulesdir@ +%transfiletriggerin -p <lua> -- @udevrulesdir@ -- This script will automatically update udev with new rules if files -- have been installed or updated in @udevrulesdir@. if posix.access("/run/systemd/system") then @@ -133,7 +118,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -- @sysctldir@ +%transfiletriggerin -p <lua> -- @sysctldir@ -- This script will automatically apply sysctl rules if files have been -- installed or updated in @sysctldir@. if posix.access("/run/systemd/system") then @@ -145,7 +130,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -- @binfmtdir@ +%transfiletriggerin -p <lua> -- @binfmtdir@ -- This script will automatically apply binfmt rules if files have been -- installed or updated in @binfmtdir@. if posix.access("/run/systemd/system") then diff --git a/src/core/umount.c b/src/core/umount.c index ff3e63710c..241fe6fc62 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -1,21 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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/>. + Copyright © 2010 ProFUSION embedded systems ***/ #include <errno.h> @@ -25,6 +10,9 @@ #include <sys/mount.h> #include <sys/swap.h> +/* This needs to be after sys/mount.h :( */ +#include <libmount.h> + #include "libudev.h" #include "alloc-util.h" @@ -34,7 +22,6 @@ #include "fd-util.h" #include "fstab-util.h" #include "linux-3.13/dm-ioctl.h" -#include "list.h" #include "mount-setup.h" #include "mount-util.h" #include "path-util.h" @@ -46,13 +33,8 @@ #include "util.h" #include "virt.h" -typedef struct MountPoint { - char *path; - char *options; - char *type; - dev_t devnum; - LIST_FIELDS(struct MountPoint, mount_point); -} MountPoint; +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); static void mount_point_free(MountPoint **head, MountPoint *m) { assert(head); @@ -61,58 +43,57 @@ static void mount_point_free(MountPoint **head, MountPoint *m) { LIST_REMOVE(mount_point, *head, m); free(m->path); + free(m->remount_options); free(m); } -static void mount_points_list_free(MountPoint **head) { +void mount_points_list_free(MountPoint **head) { assert(head); while (*head) mount_point_free(head, *head); } -static int mount_points_list_get(MountPoint **head) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - unsigned int i; +int mount_points_list_get(const char *mountinfo, MountPoint **head) { + _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; int r; assert(head); - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; + t = mnt_new_table(); + i = mnt_new_iter(MNT_ITER_FORWARD); + if (!t || !i) + return log_oom(); - for (i = 1;; i++) { - _cleanup_free_ char *path = NULL, *options = NULL, *type = NULL; - char *p = NULL; + r = mnt_table_parse_mtab(t, mountinfo); + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %m", mountinfo); + + for (;;) { + struct libmnt_fs *fs; + const char *path, *options, *fstype; + _cleanup_free_ char *p = NULL; + unsigned long remount_flags = 0u; + _cleanup_free_ char *remount_options = NULL; + bool try_remount_ro; MountPoint *m; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount flags */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%ms" /* (11) mount options */ - "%*[^\n]", /* some rubbish at the end */ - &path, &type, &options); - if (k != 3) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - continue; - } - r = cunescape(path, UNESCAPE_RELAX, &p); + r = mnt_table_next_fs(t, i, &fs); + if (r == 1) + break; if (r < 0) - return r; + return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo); + + path = mnt_fs_get_target(fs); + if (!path) + continue; + + if (cunescape(path, UNESCAPE_RELAX, &p) < 0) + return log_oom(); + + options = mnt_fs_get_options(fs); + fstype = mnt_fs_get_fstype(fs); /* Ignore mount points we can't unmount because they * are API or because we are keeping them open (like @@ -125,22 +106,56 @@ static int mount_points_list_get(MountPoint **head) { mount_point_ignore(p) || path_startswith(p, "/dev") || path_startswith(p, "/sys") || - path_startswith(p, "/proc")) { - free(p); + path_startswith(p, "/proc")) continue; + + /* If we are in a container, don't attempt to + * read-only mount anything as that brings no real + * benefits, but might confuse the host, as we remount + * the superblock here, not the bind mount. + * + * If the filesystem is a network fs, also skip the + * remount. It brings no value (we cannot leave + * a "dirty fs") and could hang if the network is down. + * Note that umount2() is more careful and will not + * hang because of the network being down. */ + try_remount_ro = detect_container() <= 0 && + !fstype_is_network(fstype) && + !fstype_is_api_vfs(fstype) && + !fstype_is_ro(fstype) && + !fstab_test_yes_no_option(options, "ro\0rw\0"); + + if (try_remount_ro) { + /* mount(2) states that mount flags and options need to be exactly the same + * as they were when the filesystem was mounted, except for the desired + * changes. So we reconstruct both here and adjust them for the later + * remount call too. */ + + r = mnt_fs_get_propagation(fs, &remount_flags); + if (r < 0) { + log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path); + continue; + } + + r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options); + if (r < 0) { + log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path); + continue; + } + + /* MS_BIND is special. If it is provided it will only make the mount-point + * read-only. If left out, the super block itself is remounted, which we want. */ + remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND; } m = new0(MountPoint, 1); - if (!m) { - free(p); - return -ENOMEM; - } + if (!m) + return log_oom(); - m->path = p; - m->options = options; - options = NULL; - m->type = type; - type = NULL; + free_and_replace(m->path, p); + free_and_replace(m->remount_options, remount_options); + m->remount_flags = remount_flags; + m->try_remount_ro = try_remount_ro; LIST_PREPEND(mount_point, *head, m); } @@ -148,44 +163,40 @@ static int mount_points_list_get(MountPoint **head) { return 0; } -static int swap_list_get(MountPoint **head) { - _cleanup_fclose_ FILE *proc_swaps = NULL; - unsigned int i; +int swap_list_get(const char *swaps, MountPoint **head) { + _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; int r; assert(head); - proc_swaps = fopen("/proc/swaps", "re"); - if (!proc_swaps) - return (errno == ENOENT) ? 0 : -errno; + t = mnt_new_table(); + i = mnt_new_iter(MNT_ITER_FORWARD); + if (!t || !i) + return log_oom(); + + r = mnt_table_parse_swaps(t, swaps); + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %m", swaps); - (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); + for (;;) { + struct libmnt_fs *fs; - for (i = 2;; i++) { MountPoint *swap; - _cleanup_free_ char *dev = NULL, *d = NULL; - int k; - - k = fscanf(proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%*s\n", /* priority */ - &dev); - - if (k != 1) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u.", i); - continue; - } + const char *source; + _cleanup_free_ char *d = NULL; - if (endswith(dev, " (deleted)")) + r = mnt_table_next_fs(t, i, &fs); + if (r == 1) + break; + if (r < 0) + return log_error_errno(r, "Failed to get next entry from %s: %m", swaps); + + source = mnt_fs_get_source(fs); + if (!source) continue; - r = cunescape(dev, UNESCAPE_RELAX, &d); + r = cunescape(source, UNESCAPE_RELAX, &d); if (r < 0) return r; @@ -201,9 +212,9 @@ static int swap_list_get(MountPoint **head) { } static int loopback_list_get(MountPoint **head) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_(udev_unrefp) struct udev *udev = NULL; int r; assert(head); @@ -234,10 +245,9 @@ static int loopback_list_get(MountPoint **head) { first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { - MountPoint *lb; - _cleanup_udev_device_unref_ struct udev_device *d; - char *loop; + _cleanup_(udev_device_unrefp) struct udev_device *d; const char *dn; + _cleanup_free_ MountPoint *lb = NULL; d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); if (!d) @@ -247,27 +257,25 @@ static int loopback_list_get(MountPoint **head) { if (!dn) continue; - loop = strdup(dn); - if (!loop) - return -ENOMEM; - lb = new0(MountPoint, 1); - if (!lb) { - free(loop); + if (!lb) return -ENOMEM; - } - lb->path = loop; + r = free_and_strdup(&lb->path, dn); + if (r < 0) + return r; + LIST_PREPEND(mount_point, *head, lb); + lb = NULL; } return 0; } static int dm_list_get(MountPoint **head) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_(udev_unrefp) struct udev *udev = NULL; int r; assert(head); @@ -294,11 +302,10 @@ static int dm_list_get(MountPoint **head) { first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { - MountPoint *m; - _cleanup_udev_device_unref_ struct udev_device *d; + _cleanup_(udev_device_unrefp) struct udev_device *d; dev_t devnum; - char *node; const char *dn; + _cleanup_free_ MountPoint *m = NULL; d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); if (!d) @@ -309,19 +316,17 @@ static int dm_list_get(MountPoint **head) { if (major(devnum) == 0 || !dn) continue; - node = strdup(dn); - if (!node) + m = new0(MountPoint, 1); + if (!m) return -ENOMEM; - m = new(MountPoint, 1); - if (!m) { - free(node); - return -ENOMEM; - } - - m->path = node; m->devnum = devnum; + r = free_and_strdup(&m->path, dn); + if (r < 0) + return r; + LIST_PREPEND(mount_point, *head, m); + m = NULL; } return 0; @@ -331,6 +336,8 @@ static int delete_loopback(const char *device) { _cleanup_close_ int fd = -1; int r; + assert(device); + fd = open(device, O_RDONLY|O_CLOEXEC); if (fd < 0) return errno == ENOENT ? 0 : -errno; @@ -380,12 +387,14 @@ static bool nonunmountable_path(const char *path) { || path_startswith(path, "/run/initramfs"); } -static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { +static int remount_with_timeout(MountPoint *m, int umount_log_level) { pid_t pid; int r; BLOCK_SIGNALS(SIGCHLD); + assert(m); + /* Due to the possiblity of a remount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular remount @@ -394,12 +403,12 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { if (r < 0) return r; if (r == 0) { - log_info("Remounting '%s' read-only in with options '%s'.", m->path, options); + log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options); /* Start the mount operation here in the child */ - r = mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options); + r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options); if (r < 0) - log_error_errno(errno, "Failed to remount '%s' read-only: %m", m->path); + log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -409,19 +418,21 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); (void) kill(pid, SIGKILL); } else if (r == -EPROTO) - log_error_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); + log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); else if (r < 0) log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); return r; } -static int umount_with_timeout(MountPoint *m, bool *changed) { +static int umount_with_timeout(MountPoint *m, int umount_log_level) { pid_t pid; int r; BLOCK_SIGNALS(SIGCHLD); + assert(m); + /* Due to the possiblity of a umount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular umount @@ -441,7 +452,7 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { * then return EBUSY).*/ r = umount2(m->path, MNT_FORCE); if (r < 0) - log_error_errno(errno, "Failed to unmount %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -451,7 +462,7 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); (void) kill(pid, SIGKILL); } else if (r == -EPROTO) - log_error_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); + log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); else if (r < 0) log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); @@ -460,38 +471,15 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { /* This includes remounting readonly, which changes the kernel mount options. * Therefore the list passed to this function is invalidated, and should not be reused. */ - -static int mount_points_list_umount(MountPoint **head, bool *changed) { +static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m; int n_failed = 0; assert(head); + assert(changed); LIST_FOREACH(mount_point, m, *head) { - bool mount_is_readonly; - - mount_is_readonly = fstab_test_yes_no_option(m->options, "ro\0rw\0"); - - /* If we are in a container, don't attempt to - read-only mount anything as that brings no real - benefits, but might confuse the host, as we remount - the superblock here, not the bind mount. - If the filesystem is a network fs, also skip the - remount. It brings no value (we cannot leave - a "dirty fs") and could hang if the network is down. - Note that umount2() is more careful and will not - hang because of the network being down. */ - if (detect_container() <= 0 && - !fstype_is_network(m->type) && - !mount_is_readonly) { - _cleanup_free_ char *options = NULL; - /* MS_REMOUNT requires that the data parameter - * should be the same from the original mount - * except for the desired changes. Since we want - * to remount read-only, we should filter out - * rw (and ro too, because it confuses the kernel) */ - (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options); - + if (m->try_remount_ro) { /* We always try to remount directories * read-only first, before we go on and umount * them. @@ -506,16 +494,19 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) { * somehwere else via a bind mount. If we * explicitly remount the super block of that * alias read-only we hence should be - * relatively safe regarding keeping dirty an fs + * relatively safe regarding keeping a dirty fs * we cannot otherwise see. * * Since the remount can hang in the instance of * remote filesystems, we remount asynchronously - * and skip the subsequent umount if it fails */ - if (remount_with_timeout(m, options, &n_failed) < 0) { - if (nonunmountable_path(m->path)) + * and skip the subsequent umount if it fails. */ + if (remount_with_timeout(m, umount_log_level) < 0) { + /* Remount failed, but try unmounting anyway, + * unless this is a mount point we want to skip. */ + if (nonunmountable_path(m->path)) { n_failed++; - continue; + continue; + } } } @@ -526,12 +517,10 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) { continue; /* Trying to umount */ - if (umount_with_timeout(m, changed) < 0) + if (umount_with_timeout(m, umount_log_level) < 0) n_failed++; - else { - if (changed) - *changed = true; - } + else + *changed = true; } return n_failed; @@ -542,13 +531,12 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { int n_failed = 0; assert(head); + assert(changed); LIST_FOREACH_SAFE(mount_point, m, n, *head) { log_info("Deactivating swap %s.", m->path); if (swapoff(m->path) == 0) { - if (changed) - *changed = true; - + *changed = true; mount_point_free(head, m); } else { log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); @@ -559,12 +547,13 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { return n_failed; } -static int loopback_points_list_detach(MountPoint **head, bool *changed) { +static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m, *n; int n_failed = 0, k; struct stat root_st; assert(head); + assert(changed); k = lstat("/", &root_st); @@ -583,12 +572,12 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { log_info("Detaching loopback %s.", m->path); r = delete_loopback(m->path); if (r >= 0) { - if (r > 0 && changed) + if (r > 0) *changed = true; mount_point_free(head, m); } else { - log_warning_errno(errno, "Could not detach loopback %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path); n_failed++; } } @@ -596,12 +585,13 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { return n_failed; } -static int dm_points_list_detach(MountPoint **head, bool *changed) { +static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m, *n; int n_failed = 0, r; dev_t rootdev; assert(head); + assert(changed); r = get_block_device("/", &rootdev); if (r <= 0) @@ -609,21 +599,18 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (major(rootdev) != 0) - if (rootdev == m->devnum) { - n_failed ++; - continue; - } + if (major(rootdev) != 0 && rootdev == m->devnum) { + n_failed ++; + continue; + } log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); r = delete_dm(m->devnum); if (r >= 0) { - if (changed) - *changed = true; - + *changed = true; mount_point_free(head, m); } else { - log_warning_errno(errno, "Could not detach DM %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path); n_failed++; } } @@ -631,34 +618,33 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { return n_failed; } -static int umount_all_once(bool *changed) { +static int umount_all_once(bool *changed, int umount_log_level) { int r; - LIST_HEAD(MountPoint, mp_list_head); + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); + + assert(changed); LIST_HEAD_INIT(mp_list_head); - r = mount_points_list_get(&mp_list_head); + r = mount_points_list_get(NULL, &mp_list_head); if (r < 0) - goto end; - - r = mount_points_list_umount(&mp_list_head, changed); - - end: - mount_points_list_free(&mp_list_head); + return r; - return r; + return mount_points_list_umount(&mp_list_head, changed, umount_log_level); } -int umount_all(bool *changed) { +int umount_all(bool *changed, int umount_log_level) { bool umount_changed; int r; + assert(changed); + /* Retry umount, until nothing can be umounted anymore. Mounts are * processed in order, newest first. The retries are needed when * an old mount has been moved, to a path inside a newer mount. */ do { umount_changed = false; - r = umount_all_once(&umount_changed); + r = umount_all_once(&umount_changed, umount_log_level); if (umount_changed) *changed = true; } while (umount_changed); @@ -667,55 +653,46 @@ int umount_all(bool *changed) { } int swapoff_all(bool *changed) { + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head); int r; - LIST_HEAD(MountPoint, swap_list_head); + + assert(changed); LIST_HEAD_INIT(swap_list_head); - r = swap_list_get(&swap_list_head); + r = swap_list_get(NULL, &swap_list_head); if (r < 0) - goto end; - - r = swap_points_list_off(&swap_list_head, changed); - - end: - mount_points_list_free(&swap_list_head); + return r; - return r; + return swap_points_list_off(&swap_list_head, changed); } -int loopback_detach_all(bool *changed) { +int loopback_detach_all(bool *changed, int umount_log_level) { + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head); int r; - LIST_HEAD(MountPoint, loopback_list_head); + + assert(changed); LIST_HEAD_INIT(loopback_list_head); r = loopback_list_get(&loopback_list_head); if (r < 0) - goto end; - - r = loopback_points_list_detach(&loopback_list_head, changed); - - end: - mount_points_list_free(&loopback_list_head); + return r; - return r; + return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level); } -int dm_detach_all(bool *changed) { +int dm_detach_all(bool *changed, int umount_log_level) { + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head); int r; - LIST_HEAD(MountPoint, dm_list_head); + + assert(changed); LIST_HEAD_INIT(dm_list_head); r = dm_list_get(&dm_list_head); if (r < 0) - goto end; - - r = dm_points_list_detach(&dm_list_head, changed); - - end: - mount_points_list_free(&dm_list_head); + return r; - return r; + return dm_points_list_detach(&dm_list_head, changed, umount_log_level); } diff --git a/src/core/umount.h b/src/core/umount.h index 7c029c384c..6f2b24d195 100644 --- a/src/core/umount.h +++ b/src/core/umount.h @@ -2,28 +2,29 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems + Copyright © 2010 ProFUSION embedded systems +***/ - 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. +#include "list.h" - 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. +int umount_all(bool *changed, int umount_log_level); - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ +int swapoff_all(bool *changed); -int umount_all(bool *changed); +int loopback_detach_all(bool *changed, int umount_log_level); -int swapoff_all(bool *changed); +int dm_detach_all(bool *changed, int umount_log_level); -int loopback_detach_all(bool *changed); +/* This is exported just for testing */ +typedef struct MountPoint { + char *path; + char *remount_options; + unsigned long remount_flags; + bool try_remount_ro; + dev_t devnum; + LIST_FIELDS(struct MountPoint, mount_point); +} MountPoint; -int dm_detach_all(bool *changed); +int mount_points_list_get(const char *mountinfo, MountPoint **head); +void mount_points_list_free(MountPoint **head); +int swap_list_get(const char *swaps, MountPoint **head); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index e61e0d1475..046e937e92 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 "cgroup-util.h" @@ -68,6 +50,37 @@ static int specifier_instance_unescaped(char specifier, void *data, void *userda return unit_name_unescape(strempty(u->instance), ret); } +static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + _cleanup_free_ char *prefix = NULL; + char *dash; + int r; + + assert(u); + + r = unit_name_to_prefix(u->id, &prefix); + if (r < 0) + return r; + + dash = strrchr(prefix, '-'); + if (dash) + return specifier_string(specifier, dash + 1, userdata, ret); + + *ret = TAKE_PTR(prefix); + return 0; +} + +static int specifier_last_component_unescaped(char specifier, void *data, void *userdata, char **ret) { + _cleanup_free_ char *p = NULL; + int r; + + r = specifier_last_component(specifier, data, userdata, &p); + if (r < 0) + return r; + + return unit_name_unescape(p, ret); +} + static int specifier_filename(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; @@ -214,6 +227,9 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { * %S: the state directory root (e.g. /var/lib or $XDG_CONFIG_HOME) * %C: the cache directory root (e.g. /var/cache or $XDG_CACHE_HOME) * %L: the log directory root (e.g. /var/log or $XDG_CONFIG_HOME/log) + * %E: the configuration directory root (e.g. /etc or $XDG_CONFIG_HOME) + * %T: the temporary directory (e.g. /tmp, or $TMPDIR, $TEMP, $TMP) + * %V: the temporary directory for large, persistent stuff (e.g. /var/tmp, or $TMPDIR, $TEMP, $TMP) * * %h: the homedir of the running user * %s: the shell of the running user @@ -226,31 +242,37 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { */ const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'P', specifier_prefix_unescaped, NULL }, - { 'i', specifier_string, u->instance }, - { 'I', specifier_instance_unescaped, NULL }, - - { 'f', specifier_filename, NULL }, - { 'c', specifier_cgroup, NULL }, - { 'r', specifier_cgroup_slice, NULL }, - { 'R', specifier_cgroup_root, NULL }, - { 't', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_RUNTIME) }, - { 'S', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_STATE) }, - { 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) }, - { 'L', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_LOGS) }, - - { 'U', specifier_user_id, NULL }, - { 'u', specifier_user_name, NULL }, - { 'h', specifier_user_home, NULL }, - { 's', specifier_user_shell, NULL }, - - { 'm', specifier_machine_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'v', specifier_kernel_release, NULL }, + { 'n', specifier_string, u->id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'P', specifier_prefix_unescaped, NULL }, + { 'i', specifier_string, u->instance }, + { 'I', specifier_instance_unescaped, NULL }, + { 'j', specifier_last_component, NULL }, + { 'J', specifier_last_component_unescaped, NULL }, + + { 'f', specifier_filename, NULL }, + { 'c', specifier_cgroup, NULL }, + { 'r', specifier_cgroup_slice, NULL }, + { 'R', specifier_cgroup_root, NULL }, + + { 't', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_RUNTIME) }, + { 'S', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_STATE) }, + { 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) }, + { 'L', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_LOGS) }, + { 'E', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CONFIGURATION) }, + { 'T', specifier_tmp_dir, NULL }, + { 'V', specifier_var_tmp_dir, NULL }, + + { 'U', specifier_user_id, NULL }, + { 'u', specifier_user_name, NULL }, + { 'h', specifier_user_home, NULL }, + { 's', specifier_user_shell, NULL }, + + { 'm', specifier_machine_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'v', specifier_kernel_release, NULL }, {} }; diff --git a/src/core/unit-printf.h b/src/core/unit-printf.h index f1b620e162..5bd1d77bb2 100644 --- a/src/core/unit-printf.h +++ b/src/core/unit-printf.h @@ -1,25 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 "unit.h" int unit_name_printf(Unit *u, const char* text, char **ret); diff --git a/src/core/unit.c b/src/core/unit.c index c3056624ef..113205bf25 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> #include <stdlib.h> @@ -29,6 +11,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "all-units.h" #include "bus-common-errors.h" #include "bus-util.h" #include "cgroup-util.h" @@ -128,7 +111,7 @@ Unit *unit_new(Manager *m, size_t size) { } int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) { - Unit *u; + _cleanup_(unit_freep) Unit *u = NULL; int r; u = unit_new(m, size); @@ -136,12 +119,11 @@ int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) { return -ENOMEM; r = unit_add_name(u, name); - if (r < 0) { - unit_free(u); + if (r < 0) return r; - } - *ret = u; + *ret = TAKE_PTR(u); + return r; } @@ -267,13 +249,11 @@ int unit_add_name(Unit *u, const char *text) { if (u->type == _UNIT_TYPE_INVALID) { u->type = t; u->id = s; - u->instance = i; + u->instance = TAKE_PTR(i); LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); unit_init(u); - - i = NULL; } s = NULL; @@ -582,8 +562,9 @@ void unit_free(Unit *u) { unit_done(u); - sd_bus_slot_unref(u->match_bus_slot); + unit_dequeue_rewatch_pids(u); + sd_bus_slot_unref(u->match_bus_slot); sd_bus_track_unref(u->bus_track); u->deserialized_refs = strv_free(u->deserialized_refs); @@ -650,6 +631,9 @@ void unit_free(Unit *u) { if (u->in_cleanup_queue) LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u); + if (u->in_target_deps_queue) + LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); + safe_close(u->ip_accounting_ingress_map_fd); safe_close(u->ip_accounting_egress_map_fd); @@ -710,10 +694,8 @@ static int set_complete_move(Set **s, Set **other) { if (*s) return set_move(*s, *other); - else { - *s = *other; - *other = NULL; - } + else + *s = TAKE_PTR(*other); return 0; } @@ -727,10 +709,8 @@ static int hashmap_complete_move(Hashmap **s, Hashmap **other) { if (*s) return hashmap_move(*s, *other); - else { - *s = *other; - *other = NULL; - } + else + *s = TAKE_PTR(*other); return 0; } @@ -1069,7 +1049,7 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency if (mask == 0) break; - if ((mask & table[i].mask) == table[i].mask) { + if (FLAGS_SET(mask, table[i].mask)) { if (*space) fputc(' ', f); else @@ -1331,7 +1311,7 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { /* Same as unit_load_fragment_and_dropin(), but whether * something can be loaded or not doesn't matter. */ - /* Load a .service file */ + /* Load a .service/.socket/.slice/… file */ r = unit_load_fragment(u); if (r < 0) return r; @@ -1343,6 +1323,18 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { return unit_load_dropin(unit_follow_merge(u)); } +void unit_add_to_target_deps_queue(Unit *u) { + Manager *m = u->manager; + + assert(u); + + if (u->in_target_deps_queue) + return; + + LIST_PREPEND(target_deps_queue, m->target_deps_queue, u); + u->in_target_deps_queue = true; +} + int unit_add_default_target_dependency(Unit *u, Unit *target) { assert(u); assert(target); @@ -1369,35 +1361,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { return unit_add_dependency(target, UNIT_AFTER, u, true, UNIT_DEPENDENCY_DEFAULT); } -static int unit_add_target_dependencies(Unit *u) { - - static const UnitDependency deps[] = { - UNIT_REQUIRED_BY, - UNIT_REQUISITE_OF, - UNIT_WANTED_BY, - UNIT_BOUND_BY - }; - - unsigned k; - int r = 0; - - assert(u); - - for (k = 0; k < ELEMENTSOF(deps); k++) { - Unit *target; - Iterator i; - void *v; - - HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]], i) { - r = unit_add_default_target_dependency(u, target); - if (r < 0) - return r; - } - } - - return r; -} - static int unit_add_slice_dependencies(Unit *u) { UnitDependencyMask mask; assert(u); @@ -1526,10 +1489,7 @@ int unit_load(Unit *u) { } if (u->load_state == UNIT_LOADED) { - - r = unit_add_target_dependencies(u); - if (r < 0) - goto fail; + unit_add_to_target_deps_queue(u); r = unit_add_slice_dependencies(u); if (r < 0) @@ -1545,7 +1505,7 @@ int unit_load(Unit *u) { if (u->on_failure_job_mode == JOB_ISOLATE && hashmap_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing."); - r = -EINVAL; + r = -ENOEXEC; goto fail; } @@ -1563,14 +1523,18 @@ int unit_load(Unit *u) { return 0; fail: - u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : UNIT_ERROR; + /* We convert ENOEXEC errors to the UNIT_BAD_SETTING load state here. Configuration parsing code should hence + * return ENOEXEC to ensure units are placed in this state after loading */ + + u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : + r == -ENOEXEC ? UNIT_BAD_SETTING : + UNIT_ERROR; u->load_error = r; + unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); - log_unit_debug_errno(u, r, "Failed to load configuration: %m"); - - return r; + return log_unit_debug_errno(u, r, "Failed to load configuration: %m"); } static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) { @@ -1716,8 +1680,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { LOG_MESSAGE("%s", buf), LOG_UNIT_ID(u), LOG_UNIT_INVOCATION_ID(u), - mid, - NULL); + mid); } void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { @@ -1732,7 +1695,7 @@ void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { int unit_start_limit_test(Unit *u) { assert(u); - if (ratelimit_test(&u->start_limit)) { + if (ratelimit_below(&u->start_limit)) { u->start_limit_hit = false; return 0; } @@ -1790,6 +1753,7 @@ static bool unit_verify_deps(Unit *u) { * -EINVAL: Unit not loaded * -EOPNOTSUPP: Unit type not supported * -ENOLINK: The necessary dependencies are not fulfilled. + * -ESTALE: This unit has been started before and can't be started a second time */ int unit_start(Unit *u) { UnitActiveState state; @@ -1809,6 +1773,10 @@ int unit_start(Unit *u) { if (u->load_state != UNIT_LOADED) return -EINVAL; + /* Refuse starting scope units more than once */ + if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_enter_timestamp)) + return -ESTALE; + /* If the conditions failed, don't do anything at all. If we * already are activating this call might still be useful to * speed up activation in case there is some hold-off time, @@ -1872,6 +1840,10 @@ bool unit_can_start(Unit *u) { if (!unit_supported(u)) return false; + /* Scope units may be started only once */ + if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_exit_timestamp)) + return false; + return !!UNIT_VTABLE(u)->start; } @@ -1959,7 +1931,7 @@ int unit_reload(Unit *u) { if (!UNIT_VTABLE(u)->reload) { /* Unit doesn't have a reload function, but we need to propagate the reload anyway */ - unit_notify(u, unit_active_state(u), unit_active_state(u), true); + unit_notify(u, unit_active_state(u), unit_active_state(u), 0); return 0; } @@ -2016,7 +1988,7 @@ static void unit_check_unneeded(Unit *u) { /* If stopping a unit fails continuously we might enter a stop * loop here, hence stop acting on the service being * unnecessary after a while. */ - if (!ratelimit_test(&u->auto_stop_ratelimit)) { + if (!ratelimit_below(&u->auto_stop_ratelimit)) { log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); return; } @@ -2066,7 +2038,7 @@ static void unit_check_binds_to(Unit *u) { /* If stopping a unit fails continuously we might enter a stop * loop here, hence stop acting on the service being * unnecessary after a while. */ - if (!ratelimit_test(&u->auto_stop_ratelimit)) { + if (!ratelimit_below(&u->auto_stop_ratelimit)) { log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id); return; } @@ -2153,6 +2125,7 @@ void unit_start_on_failure(Unit *u) { Unit *other; Iterator i; void *v; + int r; assert(u); @@ -2162,11 +2135,11 @@ void unit_start_on_failure(Unit *u) { log_unit_info(u, "Triggering OnFailure= dependencies."); HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE], i) { - int r; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, &error, NULL); if (r < 0) - log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m"); + log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r)); } } @@ -2324,10 +2297,9 @@ static void unit_update_on_console(Unit *u) { manager_ref_console(u->manager); else manager_unref_console(u->manager); - } -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) { bool unexpected; Manager *m; @@ -2403,7 +2375,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); + job_finish_and_invalidate(u->job, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false); else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) { unexpected = true; @@ -2456,7 +2428,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (ns != os && ns == UNIT_FAILED) { log_unit_debug(u, "Unit entered failed state."); - unit_start_on_failure(u); + + if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART)) + unit_start_on_failure(u); } } @@ -2504,6 +2478,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su manager_recheck_journal(m); manager_recheck_dbus(m); + unit_trigger_notify(u); if (!MANAGER_IS_RELOADING(u->manager)) { @@ -2629,7 +2604,8 @@ void unit_unwatch_all_pids(Unit *u) { u->pids = set_free(u->pids); } -void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { +static void unit_tidy_watch_pids(Unit *u) { + pid_t except1, except2; Iterator i; void *e; @@ -2637,6 +2613,9 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { /* Cleans dead PIDs from our list */ + except1 = unit_main_pid(u); + except2 = unit_control_pid(u); + SET_FOREACH(e, u->pids, i) { pid_t pid = PTR_TO_PID(e); @@ -2648,6 +2627,76 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { } } +static int on_rewatch_pids_event(sd_event_source *s, void *userdata) { + Unit *u = userdata; + + assert(s); + assert(u); + + unit_tidy_watch_pids(u); + unit_watch_all_pids(u); + + /* If the PID set is empty now, then let's finish this off. */ + unit_synthesize_cgroup_empty_event(u); + + return 0; +} + +int unit_enqueue_rewatch_pids(Unit *u) { + int r; + + assert(u); + + if (!u->cgroup_path) + return -ENOENT; + + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return r; + if (r > 0) /* On unified we can use proper notifications */ + return 0; + + /* Enqueues a low-priority job that will clean up dead PIDs from our list of PIDs to watch and subscribe to new + * PIDs that might have appeared. We do this in a delayed job because the work might be quite slow, as it + * involves issuing kill(pid, 0) on all processes we watch. */ + + if (!u->rewatch_pids_event_source) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + + r = sd_event_add_defer(u->manager->event, &s, on_rewatch_pids_event, u); + if (r < 0) + return log_error_errno(r, "Failed to allocate event source for tidying watched PIDs: %m"); + + r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + return log_error_errno(r, "Failed to adjust priority of event source for tidying watched PIDs: m"); + + (void) sd_event_source_set_description(s, "tidy-watch-pids"); + + u->rewatch_pids_event_source = TAKE_PTR(s); + } + + r = sd_event_source_set_enabled(u->rewatch_pids_event_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "Failed to enable event source for tidying watched PIDs: %m"); + + return 0; +} + +void unit_dequeue_rewatch_pids(Unit *u) { + int r; + assert(u); + + if (!u->rewatch_pids_event_source) + return; + + r = sd_event_source_set_enabled(u->rewatch_pids_event_source, SD_EVENT_OFF); + if (r < 0) + log_warning_errno(r, "Failed to disable event source for tidying watched PIDs, ignoring: %m"); + + u->rewatch_pids_event_source = sd_event_source_unref(u->rewatch_pids_event_source); +} + bool unit_job_is_applicable(Unit *u, JobType j) { assert(u); assert(j >= 0 && j < _JOB_TYPE_MAX); @@ -2721,8 +2770,8 @@ static int unit_add_dependency_hashmap( if (info.data) { /* Entry already exists. Add in our mask. */ - if ((info.origin_mask & origin_mask) == info.origin_mask && - (info.destination_mask & destination_mask) == info.destination_mask) + if (FLAGS_SET(origin_mask, info.origin_mask) && + FLAGS_SET(destination_mask, info.destination_mask)) return 0; /* NOP */ info.origin_mask |= origin_mask; @@ -3628,7 +3677,6 @@ void unit_deserialize_skip(FILE *f) { } } - int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency dep, UnitDependencyMask mask) { Unit *device; _cleanup_free_ char *e = NULL; @@ -3680,8 +3728,7 @@ int unit_coldplug(Unit *u) { assert(u); - /* Make sure we don't enter a loop, when coldplugging - * recursively. */ + /* Make sure we don't enter a loop, when coldplugging recursively. */ if (u->coldplugged) return 0; @@ -3709,6 +3756,13 @@ int unit_coldplug(Unit *u) { return r; } +void unit_catchup(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->catchup) + UNIT_VTABLE(u)->catchup(u); +} + static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_masked) { struct stat st; @@ -3843,7 +3897,7 @@ int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { } static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { - Set *pid_set; + _cleanup_set_free_ Set *pid_set = NULL; int r; pid_set = set_new(NULL); @@ -3854,20 +3908,16 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { if (main_pid > 0) { r = set_put(pid_set, PID_TO_PTR(main_pid)); if (r < 0) - goto fail; + return NULL; } if (control_pid > 0) { r = set_put(pid_set, PID_TO_PTR(control_pid)); if (r < 0) - goto fail; + return NULL; } - return pid_set; - -fail: - set_free(pid_set); - return NULL; + return TAKE_PTR(pid_set); } int unit_kill_common( @@ -4017,8 +4067,7 @@ static int user_from_unit_name(Unit *u, char **ret) { return r; if (valid_user_group_name(n)) { - *ret = n; - n = NULL; + *ret = TAKE_PTR(n); return 0; } @@ -4220,7 +4269,7 @@ char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) { char* unit_concat_strv(char **l, UnitWriteFlags flags) { _cleanup_free_ char *result = NULL; size_t n = 0, allocated = 0; - char **i, *ret; + char **i; /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a * way suitable for ExecStart= stanzas */ @@ -4255,10 +4304,7 @@ char* unit_concat_strv(char **l, UnitWriteFlags flags) { result[n] = 0; - ret = result; - result = NULL; - - return ret; + return TAKE_PTR(result); } int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const char *data) { @@ -4562,7 +4608,8 @@ int unit_kill_context( } int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) { - char prefix[strlen(path) + 1], *p; + _cleanup_free_ char *p = NULL; + char *prefix; UnitDependencyInfo di; int r; @@ -4585,34 +4632,30 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) if (!p) return -ENOMEM; - path_kill_slashes(p); + path = path_simplify(p, false); - if (!path_is_normalized(p)) { - free(p); + if (!path_is_normalized(path)) return -EPERM; - } - if (hashmap_contains(u->requires_mounts_for, p)) { - free(p); + if (hashmap_contains(u->requires_mounts_for, path)) return 0; - } di = (UnitDependencyInfo) { .origin_mask = mask }; - r = hashmap_put(u->requires_mounts_for, p, di.data); - if (r < 0) { - free(p); + r = hashmap_put(u->requires_mounts_for, path, di.data); + if (r < 0) return r; - } + p = NULL; - PATH_FOREACH_PREFIX_MORE(prefix, p) { + prefix = alloca(strlen(path) + 1); + PATH_FOREACH_PREFIX_MORE(prefix, path) { Set *x; x = hashmap_get(u->manager->units_requiring_mounts_for, prefix); if (!x) { - char *q; + _cleanup_free_ char *q = NULL; r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &path_hash_ops); if (r < 0) @@ -4623,17 +4666,15 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) return -ENOMEM; x = set_new(NULL); - if (!x) { - free(q); + if (!x) return -ENOMEM; - } r = hashmap_put(u->manager->units_requiring_mounts_for, q, x); if (r < 0) { - free(q); set_free(x); return r; } + q = NULL; } r = set_put(x, u); @@ -4721,8 +4762,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) { LOG_UNIT_ID(u), LOG_UNIT_INVOCATION_ID(u), LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), - "WHERE=%s", where, - NULL); + "WHERE=%s", where); } int unit_fail_if_noncanonical(Unit *u, const char* where) { @@ -4748,8 +4788,7 @@ int unit_fail_if_noncanonical(Unit *u, const char* where) { LOG_UNIT_ID(u), LOG_UNIT_INVOCATION_ID(u), LOG_UNIT_MESSAGE(u, "Mount path %s is not canonical (contains a symlink).", where), - "WHERE=%s", where, - NULL); + "WHERE=%s", where); return -ELOOP; } @@ -4760,9 +4799,8 @@ bool unit_is_pristine(Unit *u) { /* Check if the unit already exists or is already around, * in a number of different ways. Note that to cater for unit * types such as slice, we are generally fine with units that - * are marked UNIT_LOADED even though nothing was - * actually loaded, as those unit types don't require a file - * on disk to validly load. */ + * are marked UNIT_LOADED even though nothing was actually + * loaded, as those unit types don't require a file on disk. */ return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) || u->fragment_path || @@ -5215,6 +5253,9 @@ void unit_export_state_files(Unit *u) { if (!MANAGER_IS_SYSTEM(u->manager)) return; + if (u->manager->test_run_flags != 0) + return; + /* Exports a couple of unit properties to /run/systemd/units/, so that journald can quickly query this data * from there. Ideally, journald would use IPC to query this, like everybody else, but that's hard, as long as * the IPC system itself and PID 1 also log to the journal. @@ -5378,7 +5419,7 @@ int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) { /* Some extra safety check */ if (pid == 1 || pid == getpid_cached()) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager processs, refusing.", pid); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager process, refusing.", pid); /* Don't even begin to bother with kernel threads */ r = is_kernel_thread(pid); diff --git a/src/core/unit.h b/src/core/unit.h index e903bf8ad7..b3131eba1b 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1,34 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 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 <stdbool.h> #include <stdlib.h> #include <unistd.h> -typedef struct Unit Unit; -typedef struct UnitVTable UnitVTable; -typedef struct UnitRef UnitRef; -typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; - #include "bpf-program.h" #include "condition.h" #include "emergency-action.h" @@ -37,6 +13,8 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; #include "unit-name.h" #include "cgroup.h" +typedef struct UnitRef UnitRef; + typedef enum KillOperation { KILL_TERMINATE, KILL_TERMINATE_AND_LOG, @@ -102,7 +80,7 @@ typedef enum UnitDependencyMask { /* A dependency created because of data read from /proc/swaps and no other configuration source */ UNIT_DEPENDENCY_PROC_SWAP = 1 << 7, - _UNIT_DEPENDENCY_MASK_FULL = (1 << 8) - 1, + _UNIT_DEPENDENCY_MASK_FULL = (1 << 8) - 1, } UnitDependencyMask; /* The Unit's dependencies[] hashmaps use this structure as value. It has the same size as a void pointer, and thus can @@ -133,7 +111,7 @@ typedef enum UnitCGroupBPFState { UNIT_CGROUP_BPF_INVALIDATED = -1, } UnitCGroupBPFState; -struct Unit { +typedef struct Unit { Manager *manager; UnitType type; @@ -231,6 +209,9 @@ struct Unit { /* cgroup empty queue */ LIST_FIELDS(Unit, cgroup_empty_queue); + /* Target dependencies queue */ + LIST_FIELDS(Unit, target_deps_queue); + /* PIDs we keep an eye on. Note that a unit might have many * more, but these are the ones we care enough about to * process SIGCHLD for */ @@ -292,6 +273,10 @@ struct Unit { uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX]; + /* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new + * ones which might have appeared. */ + sd_event_source *rewatch_pids_event_source; + /* How to start OnFailure units */ JobMode on_failure_job_mode; @@ -336,6 +321,7 @@ struct Unit { bool in_gc_queue:1; bool in_cgroup_realize_queue:1; bool in_cgroup_empty_queue:1; + bool in_target_deps_queue:1; bool sent_dbus_new_signal:1; @@ -367,13 +353,13 @@ struct Unit { /* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If * == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */ int last_section_private:2; -}; +} Unit; -struct UnitStatusMessageFormats { +typedef struct UnitStatusMessageFormats { const char *starting_stopping[2]; const char *finished_start_job[_JOB_RESULT_MAX]; const char *finished_stop_job[_JOB_RESULT_MAX]; -}; +} UnitStatusMessageFormats; /* Flags used when writing drop-in files or transient unit files */ typedef enum UnitWriteFlags { @@ -396,17 +382,9 @@ typedef enum UnitWriteFlags { /* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */ #define UNIT_WRITE_FLAGS_NOOP(flags) (((flags) & (UNIT_RUNTIME|UNIT_PERSISTENT)) == 0) -#include "automount.h" -#include "device.h" -#include "path.h" -#include "scope.h" -#include "slice.h" -#include "socket.h" -#include "swap.h" -#include "target.h" -#include "timer.h" - -struct UnitVTable { +#include "kill.h" + +typedef struct UnitVTable { /* How much memory does an object of this unit type need */ size_t object_size; @@ -453,10 +431,14 @@ struct UnitVTable { * UNIT_STUB if no configuration could be found. */ int (*load)(Unit *u); - /* If a lot of units got created via enumerate(), this is - * where to actually set the state and call unit_notify(). */ + /* During deserialization we only record the intended state to return to. With coldplug() we actually put the + * deserialized state in effect. This is where unit_notify() should be called to start things up. */ int (*coldplug)(Unit *u); + /* This is called shortly after all units' coldplug() call was invoked. It's supposed to catch up state changes + * we missed so far (for example because they took place while we were reloading/reexecing) */ + void (*catchup)(Unit *u); + void (*dump)(Unit *u, FILE *f, const char *prefix); int (*start)(Unit *u); @@ -532,6 +514,9 @@ struct UnitVTable { /* Called whenever CLOCK_REALTIME made a jump */ void (*time_change)(Unit *u); + /* Called whenever /etc/localtime was modified */ + void (*timezone_change)(Unit *u); + /* Returns the next timeout of a unit */ int (*get_timeout)(Unit *u, usec_t *timeout); @@ -544,11 +529,15 @@ struct UnitVTable { /* Returns true if the unit currently needs access to the console */ bool (*needs_console)(Unit *u); - /* This is called for each unit type and should be used to - * enumerate existing devices and load them. However, - * everything that is loaded here should still stay in - * inactive state. It is the job of the coldplug() call above - * to put the units into the initial state. */ + /* Like the enumerate() callback further down, but only enumerates the perpetual units, i.e. all units that + * unconditionally exist and are always active. The main reason to keep both enumeration functions separate is + * philosophical: the state of perpetual units should be put in place by coldplug(), while the state of those + * discovered through regular enumeration should be put in place by catchup(), see below. */ + void (*enumerate_perpetual)(Manager *m); + + /* This is called for each unit type and should be used to enumerate units already existing in the system + * internally and load them. However, everything that is loaded here should still stay in inactive state. It is + * the job of the catchup() call above to put the units into the discovered state. */ void (*enumerate)(Manager *m); /* Type specific cleanups. */ @@ -570,9 +559,12 @@ struct UnitVTable { /* True if cgroup delegation is permissible */ bool can_delegate:1; + /* True if units of this type shall be startable only once and then never again */ + bool once_only:1; + /* True if queued jobs of this type should be GC'ed if no other job needs them anymore */ bool gc_jobs:1; -}; +} UnitVTable; extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; @@ -596,20 +588,9 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; #define UNIT_TRIGGER(u) ((Unit*) hashmap_first_key((u)->dependencies[UNIT_TRIGGERS])) -DEFINE_CAST(SERVICE, Service); -DEFINE_CAST(SOCKET, Socket); -DEFINE_CAST(TARGET, Target); -DEFINE_CAST(DEVICE, Device); -DEFINE_CAST(MOUNT, Mount); -DEFINE_CAST(AUTOMOUNT, Automount); -DEFINE_CAST(SWAP, Swap); -DEFINE_CAST(TIMER, Timer); -DEFINE_CAST(PATH, Path); -DEFINE_CAST(SLICE, Slice); -DEFINE_CAST(SCOPE, Scope); - Unit *unit_new(Manager *m, size_t size); void unit_free(Unit *u); +DEFINE_TRIVIAL_CLEANUP_FUNC(Unit *, unit_free); int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret); int unit_add_name(Unit *u, const char *name); @@ -631,6 +612,7 @@ void unit_add_to_load_queue(Unit *u); void unit_add_to_dbus_queue(Unit *u); void unit_add_to_cleanup_queue(Unit *u); void unit_add_to_gc_queue(Unit *u); +void unit_add_to_target_deps_queue(Unit *u); int unit_merge(Unit *u, Unit *other); int unit_merge_by_name(Unit *u, const char *other); @@ -666,13 +648,19 @@ int unit_reload(Unit *u); int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error); int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error); -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success); +typedef enum UnitNotifyFlags { + UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0, + UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1, +} UnitNotifyFlags; + +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags); int unit_watch_pid(Unit *u, pid_t pid); void unit_unwatch_pid(Unit *u, pid_t pid); void unit_unwatch_all_pids(Unit *u); -void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2); +int unit_enqueue_rewatch_pids(Unit *u); +void unit_dequeue_rewatch_pids(Unit *u); int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name); int unit_watch_bus_name(Unit *u, const char *name); @@ -701,6 +689,7 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *v int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency d, UnitDependencyMask mask); int unit_coldplug(Unit *u); +void unit_catchup(Unit *u); void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0); void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t); |