diff --git a/TRANSIENT-SETTINGS.md b/TRANSIENT-SETTINGS.md index 789ffe0dcf..cbdf83f24c 100644 --- a/TRANSIENT-SETTINGS.md +++ b/TRANSIENT-SETTINGS.md @@ -2,8 +2,8 @@ Our intention is to make all settings that are available as unit file settings also available for transient units, through the D-Bus API. At the moment, some -unit types (socket, swap, path) are not supported at all via unit types, but -most others are pretty well supported, with some notable omissions. +unit types (device, swap, target) are not supported at all via unit types, +but most others are pretty well supported, with some notable omissions. The lists below contain all settings currently available in unit files. The ones currently available in transient units are prefixed with `✓`. @@ -68,6 +68,7 @@ Only the most important generic unit settings are available for transient units. ConditionACPower= ConditionUser= ConditionGroup= + ConditionControlGroupController= AssertPathExists= AssertPathExistsGlob= AssertPathIsDirectory= @@ -88,6 +89,7 @@ Only the most important generic unit settings are available for transient units. AssertACPower= AssertUser= AssertGroup= + AssertControlGroupController= ✓ CollectMode= ``` @@ -341,71 +343,71 @@ of their own beyond the generic unit and resource control settings. ## Scope Unit Settings Scope units are fully supported as transient units (in fact they only exist as -such), but they have no settings of their own beyond the generic unit and -resource control settings. +such), but they have no settings of their own beyond the generic unit, +resource control, and process killing settings. ## Socket Unit Settings -Socket units are currently not available at all as transient units: +Most socket unit settings are available to transient units. ``` - ListenStream= - ListenDatagram= - ListenSequentialPacket= - ListenFIFO= - ListenNetlink= - ListenSpecial= - ListenMessageQueue= - ListenUSBFunction= - SocketProtocol= - BindIPv6Only= - Backlog= - BindToDevice= - ExecStartPre= - ExecStartPost= - ExecStopPre= - ExecStopPost= - TimeoutSec= - SocketUser= - SocketGroup= - SocketMode= - DirectoryMode= - Accept= - Writable= - MaxConnections= - MaxConnectionsPerSource= - KeepAlive= - KeepAliveTimeSec= - KeepAliveIntervalSec= - KeepAliveProbes= - DeferAcceptSec= - NoDelay= - Priority= - ReceiveBuffer= - SendBuffer= - IPTOS= - IPTTL= - Mark= - PipeSize= - FreeBind= - Transparent= - Broadcast= - PassCredentials= - PassSecurity= - TCPCongestion= - ReusePort= - MessageQueueMaxMessages= - MessageQueueMessageSize= - RemoveOnStop= - Symlinks= - FileDescriptorName= +✓ ListenStream= +✓ ListenDatagram= +✓ ListenSequentialPacket= +✓ ListenFIFO= +✓ ListenNetlink= +✓ ListenSpecial= +✓ ListenMessageQueue= +✓ ListenUSBFunction= +✓ SocketProtocol= +✓ BindIPv6Only= +✓ Backlog= +✓ BindToDevice= +✓ ExecStartPre= +✓ ExecStartPost= +✓ ExecStopPre= +✓ ExecStopPost= +✓ TimeoutSec= +✓ SocketUser= +✓ SocketGroup= +✓ SocketMode= +✓ DirectoryMode= +✓ Accept= +✓ Writable= +✓ MaxConnections= +✓ MaxConnectionsPerSource= +✓ KeepAlive= +✓ KeepAliveTimeSec= +✓ KeepAliveIntervalSec= +✓ KeepAliveProbes= +✓ DeferAcceptSec= +✓ NoDelay= +✓ Priority= +✓ ReceiveBuffer= +✓ SendBuffer= +✓ IPTOS= +✓ IPTTL= +✓ Mark= +✓ PipeSize= +✓ FreeBind= +✓ Transparent= +✓ Broadcast= +✓ PassCredentials= +✓ PassSecurity= +✓ TCPCongestion= +✓ ReusePort= +✓ MessageQueueMaxMessages= +✓ MessageQueueMessageSize= +✓ RemoveOnStop= +✓ Symlinks= +✓ FileDescriptorName= Service= - TriggerLimitIntervalSec= - TriggerLimitBurst= - SmackLabel= - SmackLabelIPIn= - SmackLabelIPOut= - SELinuxContextFromNet= +✓ TriggerLimitIntervalSec= +✓ TriggerLimitBurst= +✓ SmackLabel= +✓ SmackLabelIPIn= +✓ SmackLabelIPOut= +✓ SELinuxContextFromNet= ``` ## Swap Unit Settings diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 7bcea9bc30..4f47ef8191 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -47,7 +47,7 @@ systemd-run - Run programs in transient scope units, service units, or timer-scheduled service units + Run programs in transient scope units, service units, or path-, socket-, or timer-triggered service units @@ -58,6 +58,20 @@ ARGS + + systemd-run + OPTIONS + PATH OPTIONS + COMMAND + ARGS + + + systemd-run + OPTIONS + SOCKET OPTIONS + COMMAND + ARGS + systemd-run OPTIONS @@ -72,8 +86,8 @@ systemd-run may be used to create and start a transient .service or .scope unit and run the specified COMMAND in it. It may also be - used to create and start a transient .timer unit, that activates a - .service unit when elapsing. + used to create and start a transient .path, .socket, or + .timer unit, that activates a .service unit when elapsing. If a command is run as transient service unit, it will be started and managed by the service manager like any other service, and thus shows up in the output of systemctl list-units like any other unit. It @@ -88,12 +102,13 @@ list-units. Execution in this case is synchronous, and will return only when the command finishes. This mode is enabled via the switch (see below). - If a command is run with timer options such as (see below), a transient timer - unit is created alongside the service unit for the specified command. Only the transient timer unit is started - immediately, the transient service unit will be started when the timer elapses. If the - option is specified, the COMMAND may be omitted. In this case, - systemd-run creates only a .timer unit that invokes the specified unit when - elapsing. + If a command is run with path, socket, or timer options such as (see below), + a transient path, socket, or timer unit is created alongside the service unit for the specified command. Only the + transient path, socket, or timer unit is started immediately, the transient service unit will be triggered by the + path, socket, or timer unit. If the option is specified, the + COMMAND may be omitted. In this case, systemd-run creates only a + .path, .socket, or .timer unit that triggers the + specified unit. @@ -140,8 +155,8 @@ - Provide a description for the service, scope or timer unit. If not specified, the command - itself will be used as a description. See Description= in + Provide a description for the service, scope, path, socket, or timer unit. If not specified, + the command itself will be used as a description. See Description= in systemd.unit5. @@ -278,7 +293,8 @@ command. See OnActiveSec=, OnBootSec=, OnStartupSec=, OnUnitActiveSec= and OnUnitInactiveSec= in systemd.timer5 for - details. These options may not be combined with or . + details. These options are shortcuts for --timer-property= with the relevant properties. + These options may not be combined with or . @@ -287,20 +303,23 @@ Defines a calendar timer for starting the specified command. See OnCalendar= in systemd.timer5. This - option may not be combined with or . + option is a shortcut for --timer-property=OnCalendar=. This option may not be combined with + or . + + - Sets a property on the timer unit that is created. This option is similar to - but applies to the transient timer unit rather than the transient service unit - created. This option only has an effect in conjunction with , - , , , - or . This option takes an assignment in the - same format as systemctl1's - set-property command. + Sets a property on the path, socket, or timer unit that is created. This option is similar to + but applies to the transient path, socket, or timer unit rather than the + transient service unit created. This option takes an assignment in the same format as + systemctl1's + set-property command. These options may not be combined with + or . + @@ -323,7 +342,7 @@ completed). On exit, terse information about the unit's runtime is shown, including total runtime (as well as CPU usage, if was set) and the exit code and status of the main process. This output may be suppressed with . This option may not be combined with - , or the various timer options. + , or the various path, socket, or timer options. diff --git a/src/basic/generate-socket-protocol-list.sh b/src/basic/generate-socket-protocol-list.sh new file mode 100755 index 0000000000..18a540f32b --- /dev/null +++ b/src/basic/generate-socket-protocol-list.sh @@ -0,0 +1,5 @@ +#!/bin/sh -eu + +$1 -dM -include netinet/in.h - . +***/ + +#include +#include + +#include "socket-protocol-list.h" +#include "macro.h" + +static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len); + +#include "socket-protocol-from-name.h" +#include "socket-protocol-to-name.h" + +const char *socket_protocol_to_name(int id) { + + if (id < 0) + return NULL; + + if (id >= (int) ELEMENTSOF(socket_protocol_names)) + return NULL; + + return socket_protocol_names[id]; +} + +int socket_protocol_from_name(const char *name) { + const struct socket_protocol_name *sc; + + assert(name); + + sc = lookup_socket_protocol(name, strlen(name)); + if (!sc) + return 0; + + return sc->id; +} + +int socket_protocol_max(void) { + return ELEMENTSOF(socket_protocol_names); +} diff --git a/src/basic/socket-protocol-list.h b/src/basic/socket-protocol-list.h new file mode 100644 index 0000000000..12fd053382 --- /dev/null +++ b/src/basic/socket-protocol-list.h @@ -0,0 +1,26 @@ +/* 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 . +***/ + +const char *socket_protocol_to_name(int id); +int socket_protocol_from_name(const char *name); + +int socket_protocol_max(void); diff --git a/src/basic/socket-protocol-to-name.awk b/src/basic/socket-protocol-to-name.awk new file mode 100644 index 0000000000..4848a7631a --- /dev/null +++ b/src/basic/socket-protocol-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const socket_protocol_names[] = { " +} +!/HOPOPTS/ { + printf " [IPPROTO_%s] = \"%s\",\n", $1, tolower($1) +} +END{ + print "};" +} diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index a458fc2902..fa74465b92 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -55,6 +55,17 @@ # define IDN_FLAGS 0 #endif +static const char* const socket_address_type_table[] = { + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", + [SOCK_SEQPACKET] = "SequentialPacket", + [SOCK_DCCP] = "DatagramCongestionControl", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 272e74b0cc..0f84a5e93e 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -72,6 +72,9 @@ typedef enum SocketAddressBindIPv6Only { #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) +const char* socket_address_type_to_string(int t) _const_; +int socket_address_type_from_string(const char *s) _pure_; + int socket_address_parse(SocketAddress *a, const char *s); int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index abca4e112d..755b66ef8f 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -446,7 +446,7 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "CPUWeight")) { + } else if (STR_IN_SET(name, "CPUWeight", "StartupCPUWeight")) { uint64_t weight; r = sd_bus_message_read(message, "t", &weight); @@ -454,43 +454,25 @@ int bus_cgroup_set_property( return r; if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUWeight= value out of range"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= value out of range", name); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_weight = weight; + if (streq(name, "CPUWeight")) + c->cpu_weight = weight; + else /* "StartupCPUWeight" */ + c->startup_cpu_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "CPUWeight="); + unit_write_settingf(u, flags, name, "%s=", name); else - unit_write_settingf(u, flags, name, "CPUWeight=%" PRIu64, weight); + unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, weight); } return 1; - } else if (streq(name, "StartupCPUWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_cpu_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - - if (weight == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "StartupCPUWeight="); - else - unit_write_settingf(u, flags, name, "StartupCPUWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "CPUShares")) { + } else if (STR_IN_SET(name, "CPUShares", "StartupCPUShares")) { uint64_t shares; r = sd_bus_message_read(message, "t", &shares); @@ -498,38 +480,20 @@ int bus_cgroup_set_property( return r; if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUShares= value out of range"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= value out of range", name); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_shares = shares; + if (streq(name, "CPUShares")) + c->cpu_shares = shares; + else /* "StartupCPUShares" */ + c->startup_cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "CPUShares="); + unit_write_settingf(u, flags, name, "%s=", name); else - unit_write_settingf(u, flags, name, "CPUShares=%" PRIu64, shares); - } - - return 1; - - } else if (streq(name, "StartupCPUShares")) { - uint64_t shares; - - r = sd_bus_message_read(message, "t", &shares); - if (r < 0) - return r; - - if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUShares= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_cpu_shares = shares; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - - if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "StartupCPUShares="); - else - unit_write_settingf(u, flags, name, "StartupCPUShares=%" PRIu64, shares); + unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, shares); } return 1; @@ -575,7 +539,7 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "IOWeight")) { + } else if (STR_IN_SET(name, "IOWeight", "StartupIOWeight")) { uint64_t weight; r = sd_bus_message_read(message, "t", &weight); @@ -583,38 +547,20 @@ int bus_cgroup_set_property( return r; if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IOWeight= value out of range"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= value out of range", name); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->io_weight = weight; + if (streq(name, "IOWeight")) + c->io_weight = weight; + else /* "StartupIOWeight" */ + c->startup_io_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "IOWeight="); + unit_write_settingf(u, flags, name, "%s=", name); else - unit_write_settingf(u, flags, name, "IOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "StartupIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupIOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_io_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "StartupIOWeight="); - else - unit_write_settingf(u, flags, name, "StartupIOWeight=%" PRIu64, weight); + unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, weight); } return 1; @@ -630,6 +576,10 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupIODeviceLimit *a = NULL, *b; @@ -714,6 +664,10 @@ 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 (!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"); @@ -796,7 +750,7 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "BlockIOWeight")) { + } else if (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight")) { uint64_t weight; r = sd_bus_message_read(message, "t", &weight); @@ -804,38 +758,20 @@ int bus_cgroup_set_property( return r; if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIOWeight= value out of range"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= value out of range", name); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->blockio_weight = weight; + if (streq(name, "BlockIOWeight")) + c->blockio_weight = weight; + else /* "StartupBlockIOWeight" */ + c->startup_blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "BlockIOWeight="); + unit_write_settingf(u, flags, name, "%s=", name); else - unit_write_settingf(u, flags, name, "BlockIOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "StartupBlockIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupBlockIOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_blockio_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "StartupBlockIOWeight="); - else - unit_write_settingf(u, flags, name, "StartupBlockIOWeight=%" PRIu64, weight); + unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, weight); } return 1; @@ -855,6 +791,10 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupBlockIODeviceBandwidth *a = NULL, *b; @@ -951,6 +891,10 @@ 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 (!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"); @@ -1034,7 +978,7 @@ int bus_cgroup_set_property( return 1; - } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) { + } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) { uint64_t v; r = sd_bus_message_read(message, "t", &v); @@ -1050,8 +994,10 @@ int bus_cgroup_set_property( c->memory_high = v; else if (streq(name, "MemorySwapMax")) c->memory_swap_max = v; - else + else if (streq(name, "MemoryMax")) c->memory_max = v; + else /* "MemoryLimit" */ + c->memory_limit = v; unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); @@ -1063,7 +1009,7 @@ int bus_cgroup_set_property( return 1; - } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale", "MemorySwapMaxScale")) { + } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale", "MemorySwapMaxScale", "MemoryLimitScale")) { uint32_t raw; uint64_t v; @@ -1086,10 +1032,12 @@ int bus_cgroup_set_property( c->memory_low = v; else if (streq(name, "MemoryHigh")) c->memory_high = v; - else if (streq(name, "MemorySwapMaxScale")) + else if (streq(name, "MemorySwapMax")) c->memory_swap_max = v; - else /* MemoryMax */ + else if (streq(name, "MemoryMax")) c->memory_max = v; + else /* "MemoryLimit" */ + c->memory_limit = v; unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, @@ -1098,48 +1046,6 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "MemoryLimit")) { - uint64_t limit; - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (limit <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - - if (limit == CGROUP_LIMIT_MAX) - unit_write_setting(u, flags, name, "MemoryLimit=infinity"); - else - unit_write_settingf(u, flags, name, "MemoryLimit=%" PRIu64, limit); - } - - return 1; - - } else if (streq(name, "MemoryLimitScale")) { - uint64_t limit; - uint32_t raw; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - limit = physical_memory_scale(raw, UINT32_MAX); - if (limit <= 0 || limit == UINT64_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_settingf(u, flags, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - } else if (streq(name, "DevicePolicy")) { const char *policy; CGroupDevicePolicy p; @@ -1170,10 +1076,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { - if ((!path_startswith(path, "/dev/") && - !path_startswith(path, "/run/systemd/inaccessible/") && - !startswith(path, "block-") && - !startswith(path, "char-")) || + 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"); @@ -1450,12 +1354,8 @@ int bus_cgroup_set_property( return 1; } - if (u->transient && u->load_state == UNIT_STUB) { - r = bus_cgroup_set_transient_property(u, c, name, message, flags, error); - if (r != 0) - return r; - - } + if (u->transient && u->load_state == UNIT_STUB) + return bus_cgroup_set_transient_property(u, c, name, message, flags, error); return 0; } diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index be25b6e987..55e8349fc5 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1036,6 +1036,121 @@ int bus_property_get_exec_command_list( return sd_bus_message_close_container(reply); } +int bus_exec_command_set_transient_property( + Unit *u, + const char *name, + ExecCommand **exec_command, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + unsigned n = 0; + int r; + + r = sd_bus_message_enter_container(message, 'a', "(sasb)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { + _cleanup_strv_free_ char **argv = NULL; + const char *path; + int b; + + r = sd_bus_message_read(message, "s", &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); + + r = sd_bus_message_read_strv(message, &argv); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + ExecCommand *c; + + c = new0(ExecCommand, 1); + if (!c) + return -ENOMEM; + + c->path = strdup(path); + if (!c->path) { + free(c); + return -ENOMEM; + } + + c->argv = argv; + argv = NULL; + + c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0; + + path_kill_slashes(c->path); + exec_command_append_list(exec_command, c); + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + ExecCommand *c; + size_t size = 0; + + if (n == 0) + *exec_command = exec_command_free_list(*exec_command); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + + fputs("ExecStart=\n", f); + + LIST_FOREACH(command, c, *exec_command) { + _cleanup_free_ char *a = NULL, *t = NULL; + const char *p; + + p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t); + if (!p) + return -ENOMEM; + + a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS); + if (!a) + return -ENOMEM; + + fprintf(f, "%s=%s@%s %s\n", + name, + c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "", + p, + a); + } + + r = fflush_and_check(f); + if (r < 0) + return r; + + unit_write_setting(u, flags, name, buf); + } + + return 1; +} + int bus_exec_context_set_transient_property( Unit *u, ExecContext *c, @@ -1054,7 +1169,7 @@ int bus_exec_context_set_transient_property( flags |= UNIT_PRIVATE; - if (streq(name, "User")) { + if (STR_IN_SET(name, "User", "Group")) { const char *uu; r = sd_bus_message_read(message, "s", &uu); @@ -1062,36 +1177,18 @@ int bus_exec_context_set_transient_property( return r; if (!isempty(uu) && !valid_user_group_name_or_id(uu)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name: %s", uu); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %s", name, uu); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = free_and_strdup(&c->user, empty_to_null(uu)); + if (streq(name, "User")) + r = free_and_strdup(&c->user, empty_to_null(uu)); + else /* "Group" */ + r = free_and_strdup(&c->group, empty_to_null(uu)); if (r < 0) return r; - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "User=%s", uu); - } - - return 1; - - } else if (streq(name, "Group")) { - const char *gg; - - r = sd_bus_message_read(message, "s", &gg); - if (r < 0) - return r; - - if (!isempty(gg) && !valid_user_group_name_or_id(gg)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group name: %s", gg); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - - r = free_and_strdup(&c->group, empty_to_null(gg)); - if (r < 0) - return r; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Group=%s", gg); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, uu); } return 1; @@ -1139,10 +1236,9 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (isempty(id)) - c->syslog_identifier = mfree(c->syslog_identifier); - else if (free_and_strdup(&c->syslog_identifier, id) < 0) - return -ENOMEM; + r = free_and_strdup(&c->syslog_identifier, empty_to_null(id)); + if (r < 0) + return r; unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "SyslogIdentifier=%s", id); } @@ -1361,59 +1457,42 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; + bool invert = !whitelist; + char **s; if (strv_isempty(l)) { c->syscall_whitelist = false; c->syscall_filter = hashmap_free(c->syscall_filter); - } else { - char **s; + + unit_write_settingf(u, flags, name, "SystemCallFilter="); + return 1; + } + + if (!c->syscall_filter) { + c->syscall_filter = hashmap_new(NULL); + if (!c->syscall_filter) + return log_oom(); c->syscall_whitelist = whitelist; - r = hashmap_ensure_allocated(&c->syscall_filter, NULL); + if (c->syscall_whitelist) { + r = seccomp_parse_syscall_filter(invert, "@default", -1, c->syscall_filter, true); + if (r < 0) + return r; + } + } + + STRV_FOREACH(s, l) { + _cleanup_free_ char *n = NULL; + int e; + + r = parse_syscall_and_errno(*s, &n, &e); if (r < 0) return r; - STRV_FOREACH(s, l) { - _cleanup_free_ char *n = NULL; - int e; - - r = parse_syscall_and_errno(*s, &n, &e); - if (r < 0) - return r; - - if (*n == '@') { - const SyscallFilterSet *set; - const char *i; - - set = syscall_filter_set_find(n); - if (!set) - return -EINVAL; - - NULSTR_FOREACH(i, set->value) { - int id; - - id = seccomp_syscall_resolve_name(i); - if (id == __NR_SCMP_ERROR) - return -EINVAL; - - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); - if (r < 0) - return r; - } - - } else { - int id; - - id = seccomp_syscall_resolve_name(n); - if (id == __NR_SCMP_ERROR) - return -EINVAL; - - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); - if (r < 0) - return r; - } - } + r = seccomp_parse_syscall_filter(invert, n, e, c->syscall_filter, c->syscall_whitelist); + if (r < 0) + return r; } joined = strv_join(l, " "); @@ -1507,30 +1586,38 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; + bool invert = !whitelist; + char **s; if (strv_isempty(l)) { c->address_families_whitelist = false; c->address_families = set_free(c->address_families); - } else { - char **s; + + unit_write_settingf(u, flags, name, "RestrictAddressFamilies="); + return 1; + } + + if (!c->address_families) { + c->address_families = set_new(NULL); + if (!c->address_families) + return log_oom(); c->address_families_whitelist = whitelist; + } - r = set_ensure_allocated(&c->address_families, NULL); - if (r < 0) - return r; + STRV_FOREACH(s, l) { + int af; - STRV_FOREACH(s, l) { - int af; - - af = af_from_name(*s); - if (af <= 0) - return -EINVAL; + af = af_from_name(*s); + if (af <= 0) + return -EINVAL; + if (!invert == c->address_families_whitelist) { r = set_put(c->address_families, INT_TO_PTR(af)); if (r < 0) return r; - } + } else + (void) set_remove(c->address_families, INT_TO_PTR(af)); } joined = strv_join(l, " "); @@ -1573,7 +1660,7 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!ioprio_priority_is_valid(n)) + if (!sched_priority_is_valid(n)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling priority"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -1717,17 +1804,17 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!path_is_absolute(s)) + if (!isempty(s) && !path_is_absolute(s)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (streq(name, "TTYPath")) - r = free_and_strdup(&c->tty_path, s); + r = free_and_strdup(&c->tty_path, empty_to_null(s)); else if (streq(name, "RootImage")) - r = free_and_strdup(&c->root_image, s); + r = free_and_strdup(&c->root_image, empty_to_null(s)); else { assert(streq(name, "RootDirectory")); - r = free_and_strdup(&c->root_directory, s); + r = free_and_strdup(&c->root_directory, empty_to_null(s)); } if (r < 0) return r; @@ -1751,7 +1838,7 @@ int bus_exec_context_set_transient_property( } else missing_ok = false; - if (!streq(s, "~") && !path_is_absolute(s)) + if (!isempty(s) && !streq(s, "~") && !path_is_absolute(s)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -1759,7 +1846,7 @@ int bus_exec_context_set_transient_property( c->working_directory = mfree(c->working_directory); c->working_directory_home = true; } else { - r = free_and_strdup(&c->working_directory, s); + r = free_and_strdup(&c->working_directory, empty_to_null(s)); if (r < 0) return r; @@ -1840,15 +1927,13 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (isempty(s)) - s = NULL; - else if (!fdname_is_valid(s)) + if (!isempty(s) && !fdname_is_valid(s)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (streq(name, "StandardInputFileDescriptorName")) { - r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, s); + r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, empty_to_null(s)); if (r < 0) return r; @@ -1856,7 +1941,7 @@ int bus_exec_context_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=fd:%s", exec_context_fdname(c, STDIN_FILENO)); } else if (streq(name, "StandardOutputFileDescriptorName")) { - r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, s); + r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, empty_to_null(s)); if (r < 0) return r; @@ -1866,7 +1951,7 @@ int bus_exec_context_set_transient_property( } else { assert(streq(name, "StandardErrorFileDescriptorName")); - r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s); + r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1884,15 +1969,17 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!path_is_absolute(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s); - if (!path_is_normalized(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s); + if (!isempty(s)) { + if (!path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s); + if (!path_is_normalized(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s); + } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (streq(name, "StandardInputFile")) { - r = free_and_strdup(&c->stdio_file[STDIN_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDIN_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1900,7 +1987,7 @@ int bus_exec_context_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s); } else if (streq(name, "StandardOutputFile")) { - r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1910,7 +1997,7 @@ int bus_exec_context_set_transient_property( } else { assert(streq(name, "StandardErrorFile")); - r = free_and_strdup(&c->stdio_file[STDERR_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -2323,7 +2410,7 @@ int bus_exec_context_set_transient_property( return r; STRV_FOREACH(p, l) { - const char *i = *p; + char *i = *p; size_t offset; if (!utf8_is_valid(i)) @@ -2333,6 +2420,8 @@ int bus_exec_context_set_transient_property( offset += i[offset] == '+'; 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); } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -2527,15 +2616,16 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "SELinuxContext")) { const char *s; + r = sd_bus_message_read(message, "s", &s); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (isempty(s)) - c->selinux_context = mfree(c->selinux_context); - else if (free_and_strdup(&c->selinux_context, s) < 0) - return -ENOMEM; + + r = free_and_strdup(&c->selinux_context, empty_to_null(s)); + if (r < 0) + return r; unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(s)); } diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index f30f0774cd..3654598f4b 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -44,3 +44,4 @@ int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *int int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_exec_command_set_transient_property(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 0189952124..c5eba5f4e5 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -239,114 +239,8 @@ static int bus_service_set_transient_property( return 1; - } else if ((ci = service_exec_command_from_string(name)) >= 0) { - unsigned n = 0; - - r = sd_bus_message_enter_container(message, 'a', "(sasb)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { - _cleanup_strv_free_ char **argv = NULL; - const char *path; - int b; - - r = sd_bus_message_read(message, "s", &path); - if (r < 0) - return r; - - if (!path_is_absolute(path)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); - - r = sd_bus_message_read_strv(message, &argv); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - ExecCommand *c; - - c = new0(ExecCommand, 1); - if (!c) - return -ENOMEM; - - c->path = strdup(path); - if (!c->path) { - free(c); - return -ENOMEM; - } - - c->argv = argv; - argv = NULL; - - c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0; - - path_kill_slashes(c->path); - exec_command_append_list(&s->exec_command[ci], c); - } - - n++; - } - - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - ExecCommand *c; - size_t size = 0; - - if (n == 0) - s->exec_command[ci] = exec_command_free_list(s->exec_command[ci]); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - fputs("ExecStart=\n", f); - - LIST_FOREACH(command, c, s->exec_command[ci]) { - _cleanup_free_ char *a = NULL, *t = NULL; - const char *p; - - p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t); - if (!p) - return -ENOMEM; - - a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS); - if (!a) - return -ENOMEM; - - fprintf(f, "%s=%s@%s %s\n", - name, - c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "", - p, - a); - } - - r = fflush_and_check(f); - if (r < 0) - return r; - - unit_write_setting(UNIT(s), flags, name, buf); - } - - return 1; - } + } else if ((ci = service_exec_command_from_string(name)) >= 0) + return bus_exec_command_set_transient_property(UNIT(s), name, &s->exec_command[ci], message, flags, error); return 0; } diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 930b7fa87d..e73ae9ddfa 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -22,10 +22,18 @@ #include "bus-util.h" #include "dbus-cgroup.h" #include "dbus-execute.h" +#include "dbus-kill.h" #include "dbus-socket.h" +#include "fd-util.h" +#include "parse-util.h" +#include "path-util.h" #include "socket.h" +#include "socket-protocol-list.h" +#include "socket-util.h" #include "string-util.h" #include "unit.h" +#include "user-util.h" +#include "utf8.h" 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); @@ -141,6 +149,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TCPCongestion", "s", NULL, offsetof(Socket, tcp_congestion), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackLabel", "s", NULL, offsetof(Socket, smack), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackLabelIPIn", "s", NULL, offsetof(Socket, smack_ip_in), SD_BUS_VTABLE_PROPERTY_CONST), @@ -162,6 +171,465 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_VTABLE_END }; +static int bus_socket_set_transient_property( + Socket *s, + const char *name, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + SocketExecCommand ci; + Unit *u = UNIT(s); + int r; + + assert(s); + assert(name); + assert(message); + + flags |= UNIT_PRIVATE; + + if (STR_IN_SET(name, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", + "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "Accept")) + s->accept = b; + else if (streq(name, "Writable")) + s->writable = b; + else if (streq(name, "KeepAlive")) + s->keep_alive = b; + else if (streq(name, "NoDelay")) + s->no_delay = b; + else if (streq(name, "FreeBind")) + s->free_bind = b; + else if (streq(name, "Transparent")) + s->transparent = b; + else if (streq(name, "Broadcast")) + s->broadcast = b; + else if (streq(name, "PassCredentials")) + s->pass_cred = b; + else if (streq(name, "PassSecurity")) + s->pass_sec = b; + else if (streq(name, "ReusePort")) + s->reuse_port = b; + else if (streq(name, "RemoveOnStop")) + s->remove_on_stop = b; + else /* "SELinuxContextFromNet" */ + s->selinux_context_from_net = b; + + unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b)); + } + + return 1; + + } else if (STR_IN_SET(name, "Priority", "IPTTL", "Mark")) { + int32_t i; + + r = sd_bus_message_read(message, "i", &i); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "Priority")) + s->priority = i; + else if (streq(name, "IPTTL")) + s->ip_ttl = i; + else /* "Mark" */ + s->mark = i; + + unit_write_settingf(u, flags, name, "%s=%i", name, i); + } + + return 1; + + } else if (streq(name, "IPTOS")) { + _cleanup_free_ char *str = NULL; + int32_t i; + + r = sd_bus_message_read(message, "i", &i); + if (r < 0) + return r; + + r = ip_tos_to_string_alloc(i, &str); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %i", name, i); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + s->ip_tos = i; + + unit_write_settingf(u, flags, name, "%s=%s", name, str); + } + + return 1; + + } else if (streq(name, "SocketProtocol")) { + const char *p; + int32_t i; + + r = sd_bus_message_read(message, "i", &i); + if (r < 0) + return r; + + p = socket_protocol_to_name(i); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %i", name, i); + + if (!IN_SET(i, IPPROTO_UDPLITE, IPPROTO_SCTP)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported socket protocol: %s", p); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + s->socket_protocol = i; + unit_write_settingf(u, flags, name, "%s=%s", name, p); + } + + return 1; + + } else if (STR_IN_SET(name, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst")) { + uint32_t n; + + r = sd_bus_message_read(message, "u", &n); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "Backlog")) + s->backlog = n; + else if (streq(name, "MaxConnections")) + s->max_connections = n; + else if (streq(name, "MaxConnectionsPerSource")) + s->max_connections_per_source = n; + else if (streq(name, "KeepAliveProbes")) + s->keep_alive_cnt = n; + else /* "TriggerLimitBurst" */ + s->trigger_limit.burst = n; + + unit_write_settingf(u, flags, name, "%s=%u", name, n); + } + + return 1; + + } else if (STR_IN_SET(name, "SocketMode", "DirectoryMode")) { + mode_t m; + + r = sd_bus_message_read(message, "u", &m); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "SocketMode")) + s->socket_mode = m; + else /* "DirectoryMode" */ + s->directory_mode = m; + + unit_write_settingf(u, flags, name, "%s=%040o", name, m); + } + + return 1; + + } else if (STR_IN_SET(name, "MessageQueueMaxMessages", "MessageQueueMessageSize")) { + int64_t n; + + r = sd_bus_message_read(message, "x", &n); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "MessageQueueMaxMessages")) + s->mq_maxmsg = (long) n; + else /* "MessageQueueMessageSize" */ + s->mq_msgsize = (long) n; + + unit_write_settingf(u, flags, name, "%s=%" PRIi64, name, n); + } + + return 1; + + } else if (STR_IN_SET(name, "TimeoutUSec", "KeepAliveTimeUSec", "KeepAliveIntervalUSec", "DeferAcceptUSec", "TriggerLimitIntervalUSec")) { + usec_t t; + + r = sd_bus_message_read(message, "t", &t); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "TimeoutUSec")) + s->timeout_usec = t ?: USEC_INFINITY; + else if (streq(name, "KeepAliveTimeUSec")) + s->keep_alive_time = t; + else if (streq(name, "KeepAliveIntervalUSec")) + s->keep_alive_interval = t; + else if (streq(name, "DeferAcceptUSec")) + s->defer_accept = t; + else /* "TriggerLimitIntervalUSec" */ + s->trigger_limit.interval = t; + + unit_write_settingf(u, flags, name, "%s=" USEC_FMT, name, t); + } + + return 1; + + } else if (STR_IN_SET(name, "ReceiveBuffer", "SendBuffer", "PipeSize")) { + uint64_t t; + + r = sd_bus_message_read(message, "t", &t); + if (r < 0) + return r; + + if ((uint64_t) (size_t) t != t) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %" PRIu64, name, t); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "ReceiveBuffer")) + s->receive_buffer = t; + else if (streq(name, "SendBuffer")) + s->send_buffer = t; + else /* "PipeSize" */ + s->pipe_size = t; + + unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, t); + } + + return 1; + + } else if (STR_IN_SET(name, "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + + if (streq(name, "SmackLabel")) + r = free_and_strdup(&s->smack, empty_to_null(n)); + else if (streq(name, "SmackLabelIPin")) + r = free_and_strdup(&s->smack_ip_in, empty_to_null(n)); + else if (streq(name, "SmackLabelIPOut")) + r = free_and_strdup(&s->smack_ip_out, empty_to_null(n)); + else /* "TCPCongestion" */ + r = free_and_strdup(&s->tcp_congestion, empty_to_null(n)); + if (r < 0) + return r; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(n)); + } + + return 1; + + } else if (streq(name, "BindToDevice")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (n[0] && !streq(n, "*")) { + if (!ifname_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface name for %s: %s", name, n); + } else + n = NULL; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + + r = free_and_strdup(&s->bind_to_device, empty_to_null(n)); + if (r < 0) + return r; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(n)); + } + + return 1; + + } else if (streq(name, "BindIPv6Only")) { + SocketAddressBindIPv6Only b; + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + b = socket_address_bind_ipv6_only_from_string(n); + if (b < 0) { + r = parse_boolean(n); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %s", name, n); + + b = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; + } + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + s->bind_ipv6_only = b; + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, n); + } + + return 1; + + } else if (streq(name, "FileDescriptorName")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (!isempty(n) && !fdname_is_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %s", name, n); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + r = free_and_strdup(&s->fdname, empty_to_null(n)); + if (r < 0) + return r; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(n)); + } + + return 1; + + } else if (STR_IN_SET(name, "SocketUser", "SocketGroup")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (!isempty(n) && !valid_user_group_name_or_id(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s: %s", name, n); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + + if (streq(name, "SocketUser")) + r = free_and_strdup(&s->user, empty_to_null(n)); + else /* "SocketGroup" */ + r = free_and_strdup(&s->user, empty_to_null(n)); + if (r < 0) + return r; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(n)); + } + + return 1; + + } else if (streq(name, "Symlinks")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!utf8_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "String is not UTF-8 clean, ignoring assignment: %s", *p); + + if (!path_is_absolute(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Symlink path is not absolute: %s", *p); + } + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (strv_isempty(l)) { + s->symlinks = strv_free(s->symlinks); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=", name); + } else { + _cleanup_free_ char *joined = NULL; + + r = strv_extend_strv(&s->symlinks, l, true); + if (r < 0) + return -ENOMEM; + + joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, joined); + } + } + + return 1; + + } else if (streq(name, "Listen")) { + const char *t, *a; + bool empty = true; + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &t, &a)) > 0) { + _cleanup_free_ SocketPort *p = NULL; + + p = new0(SocketPort, 1); + if (!p) + return log_oom(); + + p->type = socket_port_type_from_string(t); + if (p->type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t); + + if (p->type != SOCKET_SOCKET) { + p->path = strdup(a); + path_kill_slashes(p->path); + + } else if (streq(t, "Netlink")) { + r = socket_address_parse_netlink(&p->address, a); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid netlink address: %s", a); + + } else { + r = socket_address_parse(&p->address, a); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address: %s", a); + + p->address.type = socket_address_type_from_string(t); + if (p->address.type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address type: %s", t); + + if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Address family not supported: %s", a); + } + + p->fd = -1; + p->auxiliary_fds = NULL; + p->n_auxiliary_fds = 0; + p->socket = s; + + empty = false; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + SocketPort *tail; + + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); + + p = NULL; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Listen%s=%s", t, a); + } + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + socket_free_ports(s); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "ListenStream="); + } + + return 1; + + } else if ((ci = socket_exec_command_from_string(name)) >= 0) + return bus_exec_command_set_transient_property(UNIT(s), name, &s->exec_command[ci], message, flags, error); + + return 0; +} + int bus_socket_set_property( Unit *u, const char *name, @@ -170,12 +638,37 @@ int bus_socket_set_property( sd_bus_error *error) { Socket *s = SOCKET(u); + int r; assert(s); assert(name); assert(message); - return bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error); + assert(s); + assert(name); + assert(message); + + r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error); + if (r != 0) + return r; + + if (u->transient && u->load_state == UNIT_STUB) { + /* This is a transient unit, let's load a little more */ + + r = bus_socket_set_transient_property(s, name, message, flags, error); + if (r != 0) + return r; + + r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, flags, error); + if (r != 0) + return r; + + r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, flags, error); + if (r != 0) + return r; + } + + return 0; } int bus_socket_commit_properties(Unit *u) { diff --git a/src/core/execute.c b/src/core/execute.c index b91f654994..aa9120b2ed 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -4162,19 +4162,19 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->pam_name) fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); - if (strv_length(c->read_write_paths) > 0) { + if (!strv_isempty(c->read_write_paths)) { fprintf(f, "%sReadWritePaths:", prefix); strv_fprintf(f, c->read_write_paths); fputs("\n", f); } - if (strv_length(c->read_only_paths) > 0) { + if (!strv_isempty(c->read_only_paths)) { fprintf(f, "%sReadOnlyPaths:", prefix); strv_fprintf(f, c->read_only_paths); fputs("\n", f); } - if (strv_length(c->inaccessible_paths) > 0) { + if (!strv_isempty(c->inaccessible_paths)) { fprintf(f, "%sInaccessiblePaths:", prefix); strv_fprintf(f, c->inaccessible_paths); fputs("\n", f); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index d6c616502b..f9c2acc53a 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -64,6 +64,7 @@ #include "securebits.h" #include "securebits-util.h" #include "signal-util.h" +#include "socket-protocol-list.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -433,11 +434,9 @@ int config_parse_socket_listen(const char *unit, p->n_auxiliary_fds = 0; p->socket = s; - if (s->ports) { - LIST_FIND_TAIL(port, s->ports, tail); - LIST_INSERT_AFTER(port, s->ports, tail, p); - } else - LIST_PREPEND(port, s->ports, p); + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); + p = NULL; return 0; @@ -454,6 +453,7 @@ int config_parse_socket_protocol(const char *unit, void *data, void *userdata) { Socket *s; + int r; assert(filename); assert(lvalue); @@ -462,15 +462,17 @@ int config_parse_socket_protocol(const char *unit, s = SOCKET(data); - if (streq(rvalue, "udplite")) - s->socket_protocol = IPPROTO_UDPLITE; - else if (streq(rvalue, "sctp")) - s->socket_protocol = IPPROTO_SCTP; - else { + 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; } @@ -2891,60 +2893,6 @@ int config_parse_documentation(const char *unit, } #if HAVE_SECCOMP - -static int syscall_filter_parse_one( - const char *unit, - const char *filename, - unsigned line, - ExecContext *c, - bool invert, - const char *t, - bool warn, - int errno_num) { - int r; - - if (t[0] == '@') { - const SyscallFilterSet *set; - const char *i; - - set = syscall_filter_set_find(t); - if (!set) { - if (warn) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", t); - return 0; - } - - NULSTR_FOREACH(i, set->value) { - r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false, errno_num); - if (r < 0) - return r; - } - } else { - int id; - - id = seccomp_syscall_resolve_name(t); - if (id == __NR_SCMP_ERROR) { - if (warn) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t); - return 0; - } - - /* If we previously wanted to forbid a syscall and now - * we want to allow it, then remove it from the list. - */ - if (!invert == c->syscall_whitelist) { - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); - if (r == 0) - return 0; - if (r < 0) - return log_oom(); - } else - (void) hashmap_remove(c->syscall_filter, INT_TO_PTR(id + 1)); - } - - return 0; -} - int config_parse_syscall_filter( const char *unit, const char *filename, @@ -2993,7 +2941,7 @@ int config_parse_syscall_filter( c->syscall_whitelist = true; /* Accept default syscalls if we are on a whitelist */ - r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false, -1); + r = seccomp_parse_syscall_filter(false, "@default", -1, c->syscall_filter, true); if (r < 0) return r; } @@ -3020,7 +2968,7 @@ int config_parse_syscall_filter( continue; } - r = syscall_filter_parse_one(unit, filename, line, c, invert, name, true, num); + r = seccomp_parse_syscall_filter_and_warn(invert, name, num, c->syscall_filter, c->syscall_whitelist, unit, filename, line); if (r < 0) return r; } @@ -3575,7 +3523,8 @@ int config_parse_device_allow( if (!path) return log_oom(); - if (!is_deviceallow_pattern(path)) { + 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); return 0; } @@ -3675,7 +3624,8 @@ int config_parse_io_device_weight( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + 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); return 0; } @@ -3748,7 +3698,8 @@ int config_parse_io_limit( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + 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); return 0; } @@ -3862,7 +3813,8 @@ int config_parse_blockio_device_weight( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + 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); return 0; } @@ -3936,7 +3888,8 @@ int config_parse_blockio_bandwidth( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + 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); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index 7e3630ada7..d2c469433d 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -53,6 +53,7 @@ #include "signal-util.h" #include "smack-util.h" #include "socket.h" +#include "socket-protocol-list.h" #include "special.h" #include "string-table.h" #include "string-util.h" @@ -655,7 +656,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { SocketExecCommand c; Socket *s = SOCKET(u); SocketPort *p; - const char *prefix2; + const char *prefix2, *str; assert(s); assert(f); @@ -680,7 +681,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sTCPCongestion: %s\n" "%sRemoveOnStop: %s\n" "%sWritable: %s\n" - "%sFDName: %s\n" + "%sFileDescriptorName: %s\n" "%sSELinuxContextFromNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), @@ -715,10 +716,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sAccepted: %u\n" "%sNConnections: %u\n" - "%sMaxConnections: %u\n", + "%sMaxConnections: %u\n" + "%sMaxConnectionsPerSource: %u\n", prefix, s->n_accepted, prefix, s->n_connections, - prefix, s->max_connections); + prefix, s->max_connections, + prefix, s->max_connections_per_source); if (s->priority >= 0) fprintf(f, @@ -843,6 +846,24 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), prefix, s->trigger_limit.burst); + str = socket_protocol_to_name(s->socket_protocol); + if (str) + fprintf(f, "%sSocketProtocol: %s\n", prefix, str); + + if (!strv_isempty(s->symlinks)) { + char **q; + + fprintf(f, "%sSymlinks:", prefix); + STRV_FOREACH(q, s->symlinks) + fprintf(f, " %s", *q); + + fprintf(f, "\n"); + } + + fprintf(f, + "%sTimeoutSec: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->timeout_usec, USEC_PER_SEC)); + exec_context_dump(&s->exec_context, f, prefix); kill_context_dump(&s->kill_context, f, prefix); @@ -2780,6 +2801,23 @@ const char* socket_port_type_to_string(SocketPort *p) { } } +SocketType socket_port_type_from_string(const char *s) { + assert(s); + + if (STR_IN_SET(s, "Stream", "Datagram", "SequentialPacket", "Netlink")) + return SOCKET_SOCKET; + else if (streq(s, "Special")) + return SOCKET_SPECIAL; + else if (streq(s, "MessageQueue")) + return SOCKET_MQUEUE; + else if (streq(s, "FIFO")) + return SOCKET_FIFO; + else if (streq(s, "USBFunction")) + return SOCKET_USB_FUNCTION; + else + return _SOCKET_TYPE_INVALID; +} + _pure_ static bool socket_check_gc(Unit *u) { Socket *s = SOCKET(u); @@ -3212,10 +3250,7 @@ char *socket_fdname(Socket *s) { * didn't specify anything specifically, use the socket unit's * name as fallback. */ - if (s->fdname) - return s->fdname; - - return UNIT(s)->id; + return s->fdname ?: UNIT(s)->id; } static int socket_control_pid(Unit *u) { @@ -3227,11 +3262,11 @@ static int socket_control_pid(Unit *u) { } static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { - [SOCKET_EXEC_START_PRE] = "StartPre", - [SOCKET_EXEC_START_CHOWN] = "StartChown", - [SOCKET_EXEC_START_POST] = "StartPost", - [SOCKET_EXEC_STOP_PRE] = "StopPre", - [SOCKET_EXEC_STOP_POST] = "StopPost" + [SOCKET_EXEC_START_PRE] = "ExecStartPre", + [SOCKET_EXEC_START_CHOWN] = "ExecStartChown", + [SOCKET_EXEC_START_POST] = "ExecStartPost", + [SOCKET_EXEC_STOP_PRE] = "ExecStopPre", + [SOCKET_EXEC_STOP_POST] = "ExecStopPost" }; DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); @@ -3264,6 +3299,8 @@ const UnitVTable socket_vtable = { "Install\0", .private_section = "Socket", + .can_transient = true, + .init = socket_init, .done = socket_done, .load = socket_load, diff --git a/src/core/socket.h b/src/core/socket.h index e9e560e57e..9c528fb39c 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -43,8 +43,8 @@ typedef enum SocketType { SOCKET_SPECIAL, SOCKET_MQUEUE, SOCKET_USB_FUNCTION, - _SOCKET_FIFO_MAX, - _SOCKET_FIFO_INVALID = -1 + _SOCKET_TYPE_MAX, + _SOCKET_TYPE_INVALID = -1 } SocketType; typedef enum SocketResult { @@ -194,3 +194,4 @@ const char* socket_result_to_string(SocketResult i) _const_; 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_; diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index f82e9dac44..131f160b50 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -40,6 +40,7 @@ #include "stat-util.h" #include "strv.h" #include "udev-util.h" +#include "unit-def.h" #include "unit-name.h" #include "user-util.h" #include "terminal-util.h" @@ -403,7 +404,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int transient_unit_set_properties(sd_bus_message *m, char **properties) { +static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) { int r; if (!isempty(arg_description)) { @@ -432,7 +433,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { return r; } - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, t, properties); if (r < 0) return r; @@ -445,7 +446,7 @@ static int transient_mount_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_MOUNT, arg_property); if (r < 0) return r; @@ -503,7 +504,7 @@ static int transient_automount_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_automount_property); + r = transient_unit_set_properties(m, UNIT_AUTOMOUNT, arg_automount_property); if (r < 0) return r; diff --git a/src/notify/notify.c b/src/notify/notify.c index 3e511b7e47..4a34e1e9db 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -179,7 +179,7 @@ int main(int argc, char* argv[]) { goto finish; } - if (strv_length(final_env) <= 0) { + if (strv_isempty(final_env)) { r = 0; goto finish; } diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index ef9db31df7..07d68242c6 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -203,7 +203,7 @@ int register_machine( if (r < 0) return r; - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, UNIT_SERVICE, properties); if (r < 0) return r; @@ -339,7 +339,7 @@ int allocate_scope( if (r < 0) return r; - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, UNIT_SCOPE, properties); if (r < 0) return r; diff --git a/src/run/run.c b/src/run/run.c index 5d7441ac93..b018d1547e 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -40,6 +40,7 @@ #include "spawn-polkit-agent.h" #include "strv.h" #include "terminal-util.h" +#include "unit-def.h" #include "unit-name.h" #include "user-util.h" @@ -68,6 +69,8 @@ static enum { ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */ ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */ } arg_stdio = ARG_STDIO_NONE; +static char **arg_path_property = NULL; +static char **arg_socket_property = NULL; static char **arg_timer_property = NULL; static bool with_timer = false; static bool arg_quiet = false; @@ -101,6 +104,10 @@ static void help(void) { " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" " -q --quiet Suppress information messages during runtime\n" " -G --collect Unload unit after it ran, even when failed\n\n" + "Path options:\n" + " --path-property=NAME=VALUE Set path unit property\n\n" + "Socket options:\n" + " --socket-property=NAME=VALUE Set socket unit property\n\n" "Timer options:\n" " --on-active=SECONDS Run after SECONDS delay\n" " --on-boot=SECONDS Run SECONDS after machine was booted up\n" @@ -152,6 +159,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_UNIT_INACTIVE, ARG_ON_CALENDAR, ARG_TIMER_PROPERTY, + ARG_PATH_PROPERTY, + ARG_SOCKET_PROPERTY, ARG_NO_BLOCK, ARG_NO_ASK_PASSWORD, ARG_WAIT, @@ -188,12 +197,15 @@ static int parse_argv(int argc, char *argv[]) { { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, + { "path-property", required_argument, NULL, ARG_PATH_PROPERTY }, + { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { "collect", no_argument, NULL, 'G' }, {}, }; + bool with_trigger = false; int r, c; assert(argc >= 0); @@ -368,6 +380,20 @@ static int parse_argv(int argc, char *argv[]) { !!startswith(optarg, "OnCalendar="); break; + case ARG_PATH_PROPERTY: + + if (strv_extend(&arg_path_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_SOCKET_PROPERTY: + + if (strv_extend(&arg_socket_property, optarg) < 0) + return log_oom(); + + break; + case ARG_NO_BLOCK: arg_no_block = true; break; @@ -387,6 +413,13 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + with_trigger = !!arg_path_property || !!arg_socket_property || with_timer; + + /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */ + if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1) { + log_error("Only single trigger (path, socket, timer) unit can be created."); + return -EINVAL; + } if (arg_stdio == ARG_STDIO_AUTO) { /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully @@ -397,7 +430,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_STDIO_DIRECT; } - if ((optind >= argc) && (!arg_unit || !with_timer)) { + if ((optind >= argc) && (!arg_unit || !with_trigger)) { log_error("Command line to execute required."); return -EINVAL; } @@ -417,7 +450,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_stdio != ARG_STDIO_NONE && (with_timer || arg_scope)) { + if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope)) { log_error("--pty/--pipe is not compatible in timer or --scope mode."); return -EINVAL; } @@ -432,8 +465,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_scope && with_timer) { - log_error("Timer options are not supported in --scope mode."); + if (arg_scope && with_trigger) { + log_error("Path, socket or timer options are not supported in --scope mode."); return -EINVAL; } @@ -448,8 +481,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (with_timer) { - log_error("--wait may not be combined with timer operations."); + if (with_trigger) { + log_error("--wait may not be combined with path, socket or timer operations."); return -EINVAL; } @@ -462,7 +495,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int transient_unit_set_properties(sd_bus_message *m, char **properties) { +static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) { int r; r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); @@ -475,7 +508,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { return bus_log_create_error(r); } - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, t, properties); if (r < 0) return r; @@ -521,7 +554,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property); if (r < 0) return r; @@ -694,7 +727,7 @@ static int transient_scope_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property); if (r < 0) return r; @@ -718,7 +751,7 @@ static int transient_timer_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_timer_property); + r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property); if (r < 0) return r; @@ -1282,14 +1315,15 @@ static int start_transient_scope( return log_error_errno(errno, "Failed to execute: %m"); } -static int start_transient_timer( +static int start_transient_trigger( sd_bus *bus, - char **argv) { + char **argv, + const char *suffix) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; - _cleanup_free_ char *timer = NULL, *service = NULL; + _cleanup_free_ char *trigger = NULL, *service = NULL; const char *object = NULL; int r; @@ -1308,17 +1342,17 @@ static int start_transient_timer( if (!service) return log_oom(); - r = unit_name_change_suffix(service, ".timer", &timer); + r = unit_name_change_suffix(service, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); break; case UNIT_TIMER: - timer = strdup(arg_unit); - if (!timer) + trigger = strdup(arg_unit); + if (!trigger) return log_oom(); - r = unit_name_change_suffix(timer, ".service", &service); + r = unit_name_change_suffix(trigger, ".service", &service); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); break; @@ -1328,7 +1362,7 @@ static int start_transient_timer( if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); - r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); @@ -1339,7 +1373,7 @@ static int start_transient_timer( if (r < 0) return r; - r = unit_name_change_suffix(service, ".timer", &timer); + r = unit_name_change_suffix(service, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); } @@ -1359,7 +1393,7 @@ static int start_transient_timer( return bus_log_create_error(r); /* Name and Mode */ - r = sd_bus_message_append(m, "ss", timer, "fail"); + r = sd_bus_message_append(m, "ss", trigger, "fail"); if (r < 0) return bus_log_create_error(r); @@ -1368,7 +1402,14 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); - r = transient_timer_set_properties(m); + if (streq(suffix, ".path")) + r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property); + else if (streq(suffix, ".socket")) + r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property); + else if (streq(suffix, ".timer")) + r = transient_timer_set_properties(m); + else + assert_not_reached("Invalid suffix"); if (r < 0) return r; @@ -1414,7 +1455,7 @@ static int start_transient_timer( r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { - log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); + log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r)); return r; } @@ -1427,7 +1468,7 @@ static int start_transient_timer( return r; if (!arg_quiet) { - log_info("Running timer as unit: %s", timer); + log_info("Running %s as unit: %s", suffix + 1, trigger); if (argv[0]) log_info("Will run service as unit: %s", service); } @@ -1488,14 +1529,20 @@ int main(int argc, char* argv[]) { if (arg_scope) r = start_transient_scope(bus, argv + optind); + else if (arg_path_property) + r = start_transient_trigger(bus, argv + optind, ".path"); + else if (arg_socket_property) + r = start_transient_trigger(bus, argv + optind, ".socket"); else if (with_timer) - r = start_transient_timer(bus, argv + optind); + r = start_transient_trigger(bus, argv + optind, ".timer"); else r = start_transient_service(bus, argv + optind, &retval); finish: strv_free(arg_environment); strv_free(arg_property); + strv_free(arg_path_property); + strv_free(arg_socket_property); strv_free(arg_timer_property); return r < 0 ? EXIT_FAILURE : retval; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 17928a9732..346d8c9d24 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -656,7 +656,7 @@ int ask_password_agent( goto finish; } - if (strv_length(l) <= 0) { + if (strv_isempty(l)) { l = strv_free(l); log_debug("Invalid packet"); continue; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 8af738d8ce..6940f35482 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -42,9 +42,11 @@ #include "rlimit-util.h" #include "securebits-util.h" #include "signal-util.h" +#include "socket-protocol-list.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" +#include "unit-def.h" #include "user-util.h" #include "utf8.h" #include "util.h" @@ -70,6 +72,294 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { &u->job_path); } +#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \ + static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \ + type val; \ + int r; \ + \ + r = parse_func(eq, &val); \ + if (r < 0) \ + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \ + \ + r = sd_bus_message_append(m, "(sv)", field, bus_type, (cast_type) val); \ + if (r < 0) \ + return bus_log_create_error(r); \ + \ + return 1; \ + } + +#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \ + static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \ + int r; \ + \ + r = parse_func(eq); \ + if (r < 0) { \ + log_error("Failed to parse %s: %s", field, eq); \ + return -EINVAL; \ + } \ + \ + r = sd_bus_message_append(m, "(sv)", field, bus_type, (int32_t) r); \ + if (r < 0) \ + return bus_log_create_error(r); \ + \ + return 1; \ + } + +DEFINE_BUS_APPEND_PARSE("b", parse_boolean) +DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string) +DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string) +DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string) +DEFINE_BUS_APPEND_PARSE("i", log_level_from_string) +DEFINE_BUS_APPEND_PARSE("i", parse_errno) +DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string) +DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string) +DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder) +DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name) +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority) +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice) +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, usec_t, parse_sec) +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64) +DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode) +DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou) +DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64) + +static inline int bus_append_string(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = sd_bus_message_append(m, "(sv)", field, "s", eq); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) { + const char *p; + int r; + + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (p = eq;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, flags); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Invalid syntax: %s", eq); + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) { + int r; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', buf, n); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) { + char *n; + usec_t t; + size_t l; + int r; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + + l = strlen(field); + n = newa(char, l + 2); + /* Change suffix Sec → USec */ + strcpy(mempcpy(n, field, l - 3), "USec"); + + r = sd_bus_message_append(m, "(sv)", n, "t", t); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) { + uint64_t v; + int r; + + r = parse_size(eq, base, &v); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + + r = sd_bus_message_append(m, "(sv)", field, "t", v); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) { + bool ignore_failure = false, explicit_path = false, done = false; + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *path = NULL; + int r; + + do { + switch (*eq) { + + case '-': + if (ignore_failure) + done = true; + else { + ignore_failure = true; + eq++; + } + break; + + case '@': + if (explicit_path) + done = true; + else { + explicit_path = true; + eq++; + } + break; + + case '+': + case '!': + /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */ + log_error("Sorry, but +, ! and !! are currently not supported for transient services."); + return -EOPNOTSUPP; + + default: + done = true; + break; + } + } while (!done); + + if (explicit_path) { + r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse path: %m"); + } + + r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse command line: %m"); + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (r < 0) + return bus_log_create_error(r); + + if (!strv_isempty(l)) { + + r = sd_bus_message_open_container(m, 'r', "sasb"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", path ?: l[0]); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, l); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "b", ignore_failure); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) { int r; @@ -95,31 +385,80 @@ static int bus_append_ip_address_access(sd_bus_message *m, int family, const uni return sd_bus_message_close_container(m); } -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { - const char *eq, *field; - UnitDependency dep; - int r, rl; +static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) { + int r; - assert(m); - assert(assignment); + if (STR_IN_SET(field, "DevicePolicy", "Slice")) - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, + "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", + "TasksAccounting", "IPAccounting")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight")) + + return bus_append_cg_weight_parse(m, field, eq); + + if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) + + return bus_append_cg_cpu_shares_parse(m, field, eq); + + if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) + + return bus_append_cg_blkio_weight_parse(m, field, eq); + + if (streq(field, "Delegate")) { + + r = parse_boolean(eq); + if (r < 0) + return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES); + + r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r); + if (r < 0) + return bus_log_create_error(r); + + return 1; } - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); + if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { - field = strndupa(assignment, eq - assignment); - eq++; + if (isempty(eq) || streq(eq, "infinity")) { + r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); + if (r < 0) + return bus_log_create_error(r); + return 1; + } + + r = parse_percent(eq); + if (r >= 0) { + char *n; + + /* When this is a percentage we'll convert this into a relative value in the range + * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related + * ones). This way the physical memory size can be determined server-side */ + + n = strjoina(field, "Scale"); + r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (streq(field, "TasksMax")) + return bus_append_safe_atou64(m, field, eq); + + return bus_append_parse_size(m, field, eq, 1024); + + } if (streq(field, "CPUQuota")) { if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); else { r = parse_percent_unbounded(eq); if (r <= 0) { @@ -127,173 +466,366 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return -EINVAL; } - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); } - goto finish; + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "EnvironmentFile")) { + return 1; + } + + if (streq(field, "DeviceAllow")) { if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 0); + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); + else { + const char *path = eq, *rwm = NULL, *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + rwm = e+1; + } + + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm)); + } + + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); + else { + const char *path, *bandwidth, *e; + uint64_t bytes; + + e = strchr(eq, ' '); + if (!e) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + path = strndupa(eq, e - eq); + bandwidth = e+1; + + if (streq(bandwidth, "infinity")) { + bytes = CGROUP_LIMIT_MAX; + } else { + r = parse_size(bandwidth, 1000, &bytes); + if (r < 0) + return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); + } + + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes); + } + + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); + else { + const char *path, *weight, *e; + uint64_t u; + + e = strchr(eq, ' '); + if (!e) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + path = strndupa(eq, e - eq); + weight = e+1; + + r = safe_atou64(weight, &u); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); + + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u); + } + + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) { + unsigned char prefixlen; + union in_addr_union prefix = {}; + int family; + + if (isempty(eq)) { + r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(iayu)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(iayu)"); + if (r < 0) + return bus_log_create_error(r); + + if (streq(eq, "any")) { + /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ + + r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); + + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); + + } else if (is_localhost(eq)) { + /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ + + prefix.in.s_addr = htobe32(0x7f000000); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); + if (r < 0) + return r; + + } else if (streq(eq, "link-local")) { + /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ + + prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xfe800000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); + if (r < 0) + return bus_log_create_error(r); + + } else if (streq(eq, "multicast")) { + /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ + + prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xff000000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); + if (r < 0) + return bus_log_create_error(r); + + } else { + r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen); + if (r < 0) + return log_error_errno(r, "Failed to parse IP address prefix: %s", eq); + + r = bus_append_ip_address_access(m, family, &prefix, prefixlen); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + return 0; +} + +static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) { + int r, rl; + + if (STR_IN_SET(field, + "User", "Group", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "WorkingDirectory", "RootDirectory", "SyslogIdentifier", + "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage", + "RuntimeDirectoryPreserve", "Personality", "KeyringMode")) + + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", + "NoNewPrivileges", "SyslogLevelPrefix", + "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", + "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", + "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, + "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", + "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", + "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory", + "SupplementaryGroups", "SystemCallArchitectures")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); + + if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) + + return bus_append_log_level_from_string(m, field, eq); + + if (streq(field, "SyslogFacility")) + + return bus_append_log_facility_unshifted_from_string(m, field, eq); + + if (streq(field, "SecureBits")) + + return bus_append_secure_bits_from_string(m, field, eq); + + if (streq(field, "CPUSchedulingPolicy")) + + return bus_append_sched_policy_from_string(m, field, eq); + + if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust")) + + return bus_append_safe_atoi(m, field, eq); + + if (streq(field, "Nice")) + + return bus_append_parse_nice(m, field, eq); + + if (streq(field, "SystemCallErrorNumber")) + + return bus_append_parse_errno(m, field, eq); + + if (streq(field, "IOSchedulingClass")) + + return bus_append_ioprio_class_from_string(m, field, eq); + + if (streq(field, "IOSchedulingPriority")) + + return bus_append_ioprio_parse_priority(m, field, eq); + + if (STR_IN_SET(field, + "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", + "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) + + return bus_append_parse_mode(m, field, eq); + + if (streq(field, "TimerSlackNSec")) + + return bus_append_parse_nsec(m, field, eq); + + if (streq(field, "MountFlags")) + + return bus_append_mount_propagation_flags_from_string(m, field, eq); + + if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + + if (streq(field, "EnvironmentFile")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0); else - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, + r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1, eq[0] == '-' ? eq + 1 : eq, eq[0] == '-'); - goto finish; - - } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { - char *n; - usec_t t; - size_t l; - - r = parse_sec(eq, &t); if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + return bus_log_create_error(r); - l = strlen(field); - n = newa(char, l + 2); + return 1; + } - /* Change suffix Sec → USec */ - strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_append(m, "sv", n, "t", t); - goto finish; + if (streq(field, "LogExtraFields")) { - } else if (streq(field, "LogExtraFields")) { - - r = sd_bus_message_append(m, "s", "LogExtraFields"); + r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) - goto finish; + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', "LogExtraFields"); + if (r < 0) + return bus_log_create_error(r); r = sd_bus_message_open_container(m, 'v', "aay"); if (r < 0) - goto finish; + return bus_log_create_error(r); r = sd_bus_message_open_container(m, 'a', "ay"); if (r < 0) - goto finish; + return bus_log_create_error(r); r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); if (r < 0) - goto finish; + return bus_log_create_error(r); r = sd_bus_message_close_container(m); if (r < 0) - goto finish; + return bus_log_create_error(r); r = sd_bus_message_close_container(m); - goto finish; + if (r < 0) + return bus_log_create_error(r); - } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) { - uint64_t bytes; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - if (isempty(eq) || streq(eq, "infinity")) - bytes = CGROUP_LIMIT_MAX; - else { - r = parse_percent(eq); - if (r >= 0) { - char *n; + return 1; + } - /* When this is a percentage we'll convert this into a relative value in the range - * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related - * ones). This way the physical memory size can be determined server-side */ - - n = strjoina(field, "Scale"); - r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; - - } else { - r = parse_size(eq, 1024, &bytes); - if (r < 0) - return log_error_errno(r, "Failed to parse bytes specification %s", assignment); - } - } - - r = sd_bus_message_append(m, "sv", field, "t", bytes); - goto finish; - - } else if (streq(field, "Delegate")) { - - r = parse_boolean(eq); - if (r < 0) { - const char *p = eq; - - r = sd_bus_message_append(m, "s", "DelegateControllers"); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - goto finish; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Invalid syntax: %s", eq); - - r = sd_bus_message_append(m, "s", word); - if (r < 0) - goto finish; - } - - r = sd_bus_message_close_container(m); - if (r < 0) - goto finish; - - r = sd_bus_message_close_container(m); - } else - r = sd_bus_message_append(m, "sv", "Delegate", "b", r); - - goto finish; - - } else if (streq(field, "TasksMax")) { - uint64_t t; - - if (isempty(eq) || streq(eq, "infinity")) - t = (uint64_t) -1; - else { - r = parse_percent(eq); - if (r >= 0) { - r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; - } else { - r = safe_atou64(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment); - } - - } - - r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); - goto finish; - - } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) { + if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) { const char *n, *appended; - n = startswith(eq, "fd:"); - if (n) { + if ((n = startswith(eq, "fd:"))) { appended = strjoina(field, "FileDescriptorName"); - r = sd_bus_message_append(m, "sv", appended, "s", n); - + r = sd_bus_message_append(m, "(sv)", appended, "s", n); } else if ((n = startswith(eq, "file:"))) { appended = strjoina(field, "File"); - r = sd_bus_message_append(m, "sv", appended, "s", n); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); } else - r = sd_bus_message_append(m, "sv", field, "s", eq); + r = sd_bus_message_append(m, "(sv)", field, "s", eq); - goto finish; + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "StandardInputText")) { + return 1; + } + + if (streq(field, "StandardInputText")) { _cleanup_free_ char *unescaped = NULL; r = cunescape(eq, 0, &unescaped); @@ -306,25 +838,19 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic * interface anyway */ - r = sd_bus_message_append(m, "s", "StandardInputData"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "ay"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - goto finish; + return bus_append_byte_array(m, field, unescaped, strlen(unescaped)); } - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "StandardInputData")) { + _cleanup_free_ void *decoded = NULL; + size_t sz; + + r = unbase64mem(eq, (size_t) -1, &decoded, &sz); + if (r < 0) + return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); + + return bus_append_byte_array(m, field, decoded, sz); + } rl = rlimit_from_string(field); if (rl >= 0) { @@ -335,153 +861,39 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", eq); - r = sd_bus_message_append(m, "v", "t", l.rlim_max); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max); if (r < 0) return bus_log_create_error(r); sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); - - } else if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", - "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", - "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", - "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", - "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent", - "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", - "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", - "CPUSchedulingResetOnFork", "LockPersonality", "MakeDirectory")) { - - r = parse_boolean(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); - - r = sd_bus_message_append(m, "v", "b", r); - - } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) { - uint64_t u; - - r = cg_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) { - uint64_t u; - - r = cg_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { - uint64_t u; - - r = cg_blkio_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, - "User", "Group", "DevicePolicy", "KillMode", - "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome", "SELinuxContext", "Restart", "RootImage", - "NotifyAccess", "RuntimeDirectoryPreserve", "Personality", - "KeyringMode", "CollectMode", "FailureAction", "SuccessAction", - "OnCalendar")) - - r = sd_bus_message_append(m, "v", "s", eq); - - else if (streq(field, "StandardInputData")) { - _cleanup_free_ void *decoded = NULL; - size_t sz; - - r = unbase64mem(eq, (size_t) -1, &decoded, &sz); - if (r < 0) - return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); - - r = sd_bus_message_open_container(m, 'v', "ay"); + r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'y', decoded, sz); - if (r < 0) - return bus_log_create_error(r); + return 1; + } - r = sd_bus_message_close_container(m); - - } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { - bool ignore; - const char *s; + if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { + int ignore = 0; + const char *s = eq; if (eq[0] == '-') { - ignore = true; + ignore = 1; s = eq + 1; - } else { - ignore = false; - s = eq; } - r = sd_bus_message_append(m, "v", "(bs)", ignore, s); - - } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) { - int level; - - level = log_level_from_string(eq); - if (level < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", level); - - } else if (streq(field, "SyslogFacility")) { - int facility; - - facility = log_facility_unshifted_from_string(eq); - if (facility < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", facility); - - } else if (streq(field, "SecureBits")) { - - r = secure_bits_from_string(eq); + r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s); if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "i", r); + return 1; + } - } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) { + if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) { uint64_t sum = 0; bool invert = false; - const char *p; + const char *p = eq; - p = eq; if (*p == '~') { invert = true; p++; @@ -493,354 +905,40 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen sum = invert ? ~sum : sum; - r = sd_bus_message_append(m, "v", "t", sum); - - } else if (streq(field, "DeviceAllow")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(ss)", 0); - else { - const char *path, *rwm, *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!is_deviceallow_pattern(path)) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); - } - - } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *bandwidth, *e; - uint64_t bytes; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - bandwidth = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - if (streq(bandwidth, "infinity")) { - bytes = CGROUP_LIMIT_MAX; - } else { - r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) - return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); - } - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); - } - - } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *weight, *e; - uint64_t u; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - weight = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = safe_atou64(weight, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); - } - - } else if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(iayu)", 0); - else { - unsigned char prefixlen; - union in_addr_union prefix = {}; - int family; - - r = sd_bus_message_open_container(m, 'v', "a(iayu)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(iayu)"); - if (r < 0) - return bus_log_create_error(r); - - if (streq(eq, "any")) { - /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ - - r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); - if (r < 0) - return bus_log_create_error(r); - - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); - if (r < 0) - return bus_log_create_error(r); - - } else if (is_localhost(eq)) { - /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ - - prefix.in.s_addr = htobe32(0x7f000000); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); - if (r < 0) - return r; - - } else if (streq(eq, "link-local")) { - - /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ - - prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xfe800000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); - if (r < 0) - return bus_log_create_error(r); - - } else if (streq(eq, "multicast")) { - - /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ - - prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xff000000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); - - } else { - r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen); - if (r < 0) - return log_error_errno(r, "Failed to parse IP address prefix: %s", eq); - - r = bus_append_ip_address_access(m, family, &prefix, prefixlen); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - } else if (streq(field, "CPUSchedulingPolicy")) { - int n; - - n = sched_policy_from_string(eq); - if (n < 0) - return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq); - - r = sd_bus_message_append(m, "v", "i", (int32_t) n); - - } else if (streq(field, "CPUSchedulingPriority")) { - int n; - - r = safe_atoi(eq, &n); + r = sd_bus_message_append(m, "(sv)", field, "t", sum); if (r < 0) - return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq); - if (!sched_priority_is_valid(n)) - return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "i", (int32_t) n); + return 1; + } - } else if (streq(field, "CPUAffinity")) { + if (streq(field, "CPUAffinity")) { _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; - int ncpus; - ncpus = parse_cpu_set(eq, &cpuset); - if (ncpus < 0) + r = parse_cpu_set(eq, &cpuset); + if (r < 0) return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - r = sd_bus_message_open_container(m, 'v', "ay"); - if (r < 0) - return bus_log_create_error(r); + return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r)); + } - r = sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "Nice")) { - int n; - - r = parse_nice(eq, &n); - if (r < 0) - return log_error_errno(r, "Failed to parse nice value: %s", eq); - - r = sd_bus_message_append(m, "v", "i", (int32_t) n); - - } else if (streq(field, "SystemCallFilter")) { - int whitelist; - _cleanup_strv_free_ char **l = NULL; - const char *p; - - p = eq; - if (*p == '~') { - whitelist = 0; - p++; - } else - whitelist = 1; - - if (whitelist != 0) { - r = strv_extend(&l, "@default"); - if (r < 0) - return log_oom(); - } - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - if (r == 0) - break; - - r = strv_extend(&l, word); - if (r < 0) - return log_oom(); - } - - r = sd_bus_message_open_container(m, 'v', "(bas)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'r', "bas"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 'b', &whitelist); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - } else if (streq(field, "SystemCallArchitectures")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - if (r == 0) - break; - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "SystemCallErrorNumber")) { - int n; - - n = parse_errno(eq); - if (n <= 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - - r = sd_bus_message_append(m, "v", "i", (int32_t) n); - - } else if (streq(field, "RestrictAddressFamilies")) { - int whitelist; - _cleanup_strv_free_ char **l = NULL; + if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { + int whitelist = 1; const char *p = eq; if (*p == '~') { whitelist = 0; p++; - } else - whitelist = 1; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - if (r == 0) - break; - - r = strv_extend(&l, word); - if (r < 0) - return log_oom(); } + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_open_container(m, 'v', "(bas)"); if (r < 0) return bus_log_create_error(r); @@ -853,181 +951,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - } else if (streq(field, "FileDescriptorStoreMax")) { - unsigned u; - - r = safe_atou(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq); - - r = sd_bus_message_append(m, "v", "u", (uint32_t) u); - - } else if (streq(field, "IOSchedulingClass")) { - int c; - - c = ioprio_class_from_string(eq); - if (c < 0) - return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq); - - r = sd_bus_message_append(m, "v", "i", (int32_t) c); - - } else if (streq(field, "IOSchedulingPriority")) { - int q; - - r = ioprio_parse_priority(eq, &q); - if (r < 0) - return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq); - - r = sd_bus_message_append(m, "v", "i", (int32_t) q); - - } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse Environment value %s: %m", eq); - if (r == 0) - break; - - if (streq(field, "Environment")) { - if (!env_assignment_is_valid(word)) { - log_error("Invalid environment assignment: %s", word); - return -EINVAL; - } - } else if (streq(field, "UnsetEnvironment")) { - if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) { - log_error("Invalid environment name or assignment: %s", word); - return -EINVAL; - } - } else { /* PassEnvironment */ - if (!env_name_is_valid(word)) { - log_error("Invalid environment variable name: %s", word); - return -EINVAL; - } - } - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "KillSignal")) { - int sig; - - sig = signal_from_string_try_harder(eq); - if (sig < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", sig); - - } else if (streq(field, "TimerSlackNSec")) { - nsec_t n; - - r = parse_nsec(eq, &n); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - r = sd_bus_message_append(m, "v", "t", n); - } else if (streq(field, "OOMScoreAdjust")) { - int oa; - - r = safe_atoi(eq, &oa); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - if (!oom_score_adjust_is_valid(oa)) { - log_error("OOM score adjust value out of range"); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", oa); - - } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", - "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - size_t offset; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - if (r == 0) - break; - - if (!utf8_is_valid(word)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - offset = word[0] == '-'; - offset += word[offset] == '+'; - - if (!path_is_absolute(word + offset)) { - log_error("Path specified by %s is not absolute: %s", field, eq); - return -EINVAL; - } - - path_kill_slashes(word + offset); - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "SupplementaryGroups")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'a', "s"); if (r < 0) return bus_log_create_error(r); @@ -1036,60 +959,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); if (r == 0) break; - - if (!valid_user_group_name_or_id(word)) { - log_error("Invalid group name or id is specified by %s: %s", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (STR_IN_SET(field, - "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", - "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask", - "DirectoryMode")) { - mode_t mode; - - r = parse_mode(eq, &mode); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s", field, eq); - - r = sd_bus_message_append(m, "v", "u", mode); - - } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == -ENOMEM) return log_oom(); if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s", field, eq); - if (r == 0) - break; + return log_error_errno(r, "Invalid syntax: %s", eq); r = sd_bus_message_append_basic(m, 's', word); if (r < 0) @@ -1101,8 +976,21 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "RestrictNamespaces")) { + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (streq(field, "RestrictNamespaces")) { bool invert = false; unsigned long flags = 0; @@ -1125,31 +1013,31 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (invert) flags = (~flags) & NAMESPACE_FLAGS_ALL; - r = sd_bus_message_append(m, "v", "t", (uint64_t) flags); - - } else if ((dep = unit_dependency_from_string(field)) >= 0) - - r = sd_bus_message_append(m, "v", "as", 1, eq); - - else if (streq(field, "MountFlags")) { - unsigned long f; - - r = mount_propagation_flags_from_string(eq, &f); + r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags); if (r < 0) - return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", (uint64_t) f); + return 1; + } - } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) { + if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) { const char *p = eq; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_open_container(m, 'v', "a(ssbt)"); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_open_container(m, 'a', "(ssbt)"); if (r < 0) - return r; + return bus_log_create_error(r); for (;;) { _cleanup_free_ char *source = NULL, *destination = NULL; @@ -1202,148 +1090,311 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags); if (r < 0) - return r; + return bus_log_create_error(r); } r = sd_bus_message_close_container(m); if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - - } else if (STR_IN_SET(field, "ExecStartPre", "ExecStart", "ExecStartPost", - "ExecReload", "ExecStop", "ExecStopPost")) { - - bool ignore_failure = false, explicit_path = false, done = false; - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ char *path = NULL; - - do { - switch (*eq) { - - case '-': - if (ignore_failure) - done = true; - else { - ignore_failure = true; - eq++; - } - break; - - case '@': - if (explicit_path) - done = true; - else { - explicit_path = true; - eq++; - } - break; - - case '+': - case '!': - /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */ - log_error("Sorry, but +, ! and !! are currently not supported for transient services."); - return -EOPNOTSUPP; - - default: - done = true; - break; - } - } while (!done); - - if (explicit_path) { - r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse path: %m"); - } - - r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse command line: %m"); - - r = sd_bus_message_open_container(m, 'v', "a(sasb)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sasb)"); - if (r < 0) - return r; - - if (strv_length(l) > 0) { - - r = sd_bus_message_open_container(m, 'r', "sasb"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", path ?: l[0]); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "b", ignore_failure); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } + return bus_log_create_error(r); r = sd_bus_message_close_container(m); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_close_container(m); - - } else if (STR_IN_SET(field, - "OnActiveSec", "OnBootSec", "OnStartupSec", - "OnUnitActiveSec","OnUnitInactiveSec")) { - usec_t t; - - r = parse_sec(eq, &t); if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", t); - - } else if (STR_IN_SET(field, - "PathExists", "PathExistsGlob", "PathChanged", - "PathModified", "DirectoryNotEmpty")) { - - if (!path_is_absolute(eq)) { - log_error("Path specified by %s= is not absolute: %s", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "s", eq); - - } else { - log_error("Unknown assignment: %s", assignment); - return -EINVAL; + return 1; } -finish: - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - return 0; } -int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) { +static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) { + + if (streq(field, "KillMode")) + + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL")) + + return bus_append_parse_boolean(m, field, eq); + + if (streq(field, "KillSignal")) + + return bus_append_signal_from_string_try_harder(m, field, eq); + + return 0; +} + +static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) { + + if (STR_IN_SET(field, + "PathExists", "PathExistsGlob", "PathChanged", + "PathModified", "DirectoryNotEmpty")) + + return bus_append_string(m, field, eq); + + if (streq(field, "MakeDirectory")) + + return bus_append_parse_boolean(m, field, eq); + + if (streq(field, "DirectoryMode")) + + return bus_append_parse_mode(m, field, eq); + + return 0; +} + +static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) { + + if (STR_IN_SET(field, "Type", "Restart", "NotifyAccess")) + + return bus_append_string(m, field, eq); + + if (streq(field, "RemainAfterExit")) + + return bus_append_parse_boolean(m, field, eq); + + if (streq(field, "RuntimeMaxSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (streq(field, "FileDescriptorStoreMax")) + + return bus_append_safe_atou(m, field, eq); + + if (STR_IN_SET(field, + "ExecStartPre", "ExecStart", "ExecStartPost", + "ExecReload", "ExecStop", "ExecStopPost")) + + return bus_append_exec_command(m, field, eq); + + return 0; +} + +static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", + "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "Priority", "IPTTL", "Mark")) + + return bus_append_safe_atoi(m, field, eq); + + if (streq(field, "IPTOS")) + + return bus_append_ip_tos_from_string(m, field, eq); + + if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst")) + + return bus_append_safe_atou(m, field, eq); + + if (STR_IN_SET(field, "SocketMode", "DirectoryMode")) + + return bus_append_parse_mode(m, field, eq); + + if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize")) + + return bus_append_safe_atoi64(m, field, eq); + + if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize")) + + return bus_append_parse_size(m, field, eq, 1024); + + if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost")) + + return bus_append_exec_command(m, field, eq); + + if (STR_IN_SET(field, + "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion", + "BindToDevice", "BindIPv6Only", "FileDescriptorName", + "SocketUser", "SocketGroup")) + + return bus_append_string(m, field, eq); + + if (streq(field, "Symlinks")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); + + if (streq(field, "SocketProtocol")) + + return bus_append_socket_protocol_from_name(m, field, eq); + + if (STR_IN_SET(field, + "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink", + "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) { + + r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + strlen("Listen"), eq); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + return 0; +} +static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) { + + if (streq(field, "OnCalendar")) + + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, + "OnActiveSec", "OnBootSec", "OnStartupSec", + "OnUnitActiveSec","OnUnitInactiveSec")) + + return bus_append_parse_sec(m, field, eq); + + if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec")) + + return bus_append_parse_sec_rename(m, field, eq); + + return 0; +} + +static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) { + + if (STR_IN_SET(field, "Description", "CollectMode", "FailureAction", "SuccessAction")) + + return bus_append_string(m, field, eq); + + if (streq(field, "DefaultDependencies")) + + return bus_append_parse_boolean(m, field, eq); + + if (unit_dependency_from_string(field) >= 0) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); + + return 0; +} + +int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) { + const char *eq, *field; + int r; + + assert(m); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + field = strndupa(assignment, eq - assignment); + eq++; + + switch (t) { + case UNIT_SERVICE: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_execute_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_service_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SOCKET: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_execute_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_socket_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_TIMER: + r = bus_append_timer_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_PATH: + r = bus_append_path_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SLICE: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SCOPE: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_MOUNT: + case UNIT_AUTOMOUNT: + break; + + case UNIT_TARGET: + case UNIT_DEVICE: + case UNIT_SWAP: + log_error("Not supported unit type"); + return -EINVAL; + + default: + log_error("Invalid unit type"); + return -EINVAL; + } + + r = bus_append_unit_property(m, field, eq); + if (r != 0) + return r; + + log_error("Unknown assignment: %s", assignment); + return -EINVAL; +} + +int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) { char **i; int r; assert(m); STRV_FOREACH(i, l) { - r = bus_append_unit_property_assignment(m, *i); + r = bus_append_unit_property_assignment(m, t, *i); if (r < 0) return r; } diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index 1a137e8b84..514e6edb76 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -20,10 +20,10 @@ along with systemd; If not, see . ***/ -#include "sd-bus.h" - -#include "output-mode.h" #include "install.h" +#include "output-mode.h" +#include "sd-bus.h" +#include "unit-def.h" typedef struct UnitInfo { const char *machine; @@ -41,8 +41,8 @@ typedef struct UnitInfo { int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); -int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l); +int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment); +int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l); typedef struct BusWaitForJobs BusWaitForJobs; diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 4f098238d9..fbb232cd45 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -950,6 +950,65 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u return 0; } +int seccomp_parse_syscall_filter_internal( + bool invert, + const char *name, + int errno_num, + Hashmap *filter, + bool whitelist, + bool warn, + const char *unit, + const char *filename, + unsigned line) { + + int r; + + assert(name); + assert(filter); + + if (name[0] == '@') { + const SyscallFilterSet *set; + const char *i; + + set = syscall_filter_set_find(name); + if (!set) { + if (warn) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", name); + return 0; + } else + return -EINVAL; + } + + NULSTR_FOREACH(i, set->value) { + r = seccomp_parse_syscall_filter_internal(invert, i, errno_num, filter, whitelist, warn, unit, filename, line); + if (r < 0) + return r; + } + } else { + int id; + + id = seccomp_syscall_resolve_name(name); + if (id == __NR_SCMP_ERROR) { + if (warn) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", name); + return 0; + } else + return -EINVAL; + } + + /* If we previously wanted to forbid a syscall and now + * we want to allow it, then remove it from the list. */ + if (!invert == whitelist) { + r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); + if (r < 0) + return warn ? log_oom() : -ENOMEM; + } else + (void) hashmap_remove(filter, INT_TO_PTR(id + 1)); + } + + return 0; +} + int seccomp_restrict_namespaces(unsigned long retain) { uint32_t arch; int r; diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index ad2ab7f6b5..0b30cdf388 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -81,6 +81,24 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action); +int seccomp_parse_syscall_filter_internal( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist, + bool warn, const char *unit, const char *filename, unsigned line); + +static inline int seccomp_parse_syscall_filter_and_warn( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist, + const char *unit, const char *filename, unsigned line) { + assert(unit); + assert(filename); + + return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, true, unit, filename, line); +} + +static inline int seccomp_parse_syscall_filter( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist) { + return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, false, NULL, NULL, 0); +} + int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); int seccomp_protect_sysctl(void); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6d3adc5803..50a10ace77 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -84,6 +84,7 @@ #include "stat-util.h" #include "strv.h" #include "terminal-util.h" +#include "unit-def.h" #include "unit-name.h" #include "user-util.h" #include "util.h" @@ -5582,6 +5583,7 @@ static int set_property(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *n = NULL; + UnitType t; sd_bus *bus; int r; @@ -5605,6 +5607,12 @@ static int set_property(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); + t = unit_name_to_type(n); + if (t < 0) { + log_error("Invalid unit type: %s", n); + return -EINVAL; + } + r = sd_bus_message_append(m, "sb", n, arg_runtime); if (r < 0) return bus_log_create_error(r); @@ -5613,7 +5621,7 @@ static int set_property(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2)); + r = bus_append_unit_property_assignment_many(m, t, strv_skip(argv, 2)); if (r < 0) return r;