+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include "bus-internal.h"
#include "bus-unit-util.h"
#include "bus-util.h"
+#include "cap-list.h"
#include "cgroup-util.h"
+#include "cpu-set-util.h"
#include "env-util.h"
+#include "errno-list.h"
#include "escape.h"
#include "hashmap.h"
+#include "hostname-util.h"
+#include "in-addr-util.h"
#include "list.h"
#include "locale-util.h"
+#include "mount-util.h"
+#include "nsflags.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "rlimit-util.h"
+#include "securebits-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
+#include "user-util.h"
#include "utf8.h"
#include "util.h"
&u->job_path);
}
+static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
+ int r;
+
+ assert(m);
+ assert(prefix);
+
+ r = sd_bus_message_open_container(m, 'r', "iayu");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "i", family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "u", prefixlen);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(m);
+}
+
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
const char *eq, *field;
+ UnitDependency dep;
int r, rl;
assert(m);
if (isempty(eq))
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else {
- r = parse_percent(eq);
+ r = parse_percent_unbounded(eq);
if (r <= 0) {
log_error_errno(r, "CPU quota '%s' invalid.", eq);
return -EINVAL;
r = sd_bus_message_append(m, "sv", n, "t", t);
goto finish;
+ } else if (streq(field, "LogExtraFields")) {
+
+ r = sd_bus_message_append(m, "s", "LogExtraFields");
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_open_container(m, 'v', "aay");
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_open_container(m, 'a', "ay");
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_close_container(m);
+ goto finish;
+
} else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
uint64_t bytes;
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;
r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
} else if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting",
- "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
- "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute")) {
+ "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
+ "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
+ "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
+ "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
+ "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
+ "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
+ "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
+ "CPUSchedulingResetOnFork", "LockPersonality")) {
r = parse_boolean(eq);
if (r < 0)
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) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", u);
+
} else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
uint64_t u;
"StandardInput", "StandardOutput", "StandardError",
"Description", "Slice", "Type", "WorkingDirectory",
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
- "ProtectHome", "SELinuxContext"))
+ "ProtectHome", "SELinuxContext", "Restart", "RootImage",
+ "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
+ "KeyringMode", "CollectMode"))
r = sd_bus_message_append(m, "v", "s", eq);
- else if (streq(field, "SyslogLevel")) {
+ else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
+ bool ignore;
+ const char *s;
+
+ if (eq[0] == '-') {
+ ignore = true;
+ 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);
r = sd_bus_message_append(m, "v", "i", facility);
+ } else if (streq(field, "SecureBits")) {
+
+ r = secure_bits_from_string(eq);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", r);
+
+ } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
+ uint64_t sum = 0;
+ bool invert = false;
+ const char *p;
+
+ p = eq;
+ if (*p == '~') {
+ invert = true;
+ p++;
+ }
+
+ r = capability_set_from_string(p, &sum);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ sum = invert ? ~sum : sum;
+
+ r = sd_bus_message_append(m, "v", "t", sum);
+
} else if (streq(field, "DeviceAllow")) {
if (isempty(eq))
rwm = "";
}
- if (!path_startswith(path, "/dev")) {
+ 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(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);
+ 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);
+
+ r = sd_bus_message_append(m, "v", "i", (int32_t) n);
+
+ } else if (streq(field, "CPUAffinity")) {
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+ int ncpus;
+
+ ncpus = parse_cpu_set(eq, &cpuset);
+ if (ncpus < 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);
+
+ if (cpuset)
+ sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus));
+
+ r = sd_bus_message_close_container(m);
+
} else if (streq(field, "Nice")) {
- int32_t i;
+ int n;
- r = safe_atoi32(eq, &i);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
+ 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);
+
+#if HAVE_SECCOMP
+
+ } 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_append(m, "v", "i", i);
+ 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);
- } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
+ 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);
- p = eq;
+ 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;
+ 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, '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);
+#endif
+ } 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) {
log_error("Failed to parse Environment value %s", eq);
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);
if (r < 0)
return bus_log_create_error(r);
- p = eq;
-
- for (;;) {
+ for (p = eq;;) {
_cleanup_free_ char *word = NULL;
- int offset;
+ size_t offset;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
if (r < 0) {
}
offset = word[0] == '-';
+ offset += word[offset] == '+';
+
if (!path_is_absolute(word + offset)) {
log_error("Failed to parse %s value %s", field, eq);
return -EINVAL;
r = sd_bus_message_close_container(m);
- } else if (streq(field, "RuntimeDirectory")) {
+ } 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);
- p = eq;
+ for (p = eq;;) {
+ _cleanup_free_ char *word = NULL;
- for (;;) {
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (!valid_user_group_name_or_id(word)) {
+ log_error("Failed to parse %s value %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")) {
+ 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;
r = sd_bus_message_close_container(m);
+ } else if (streq(field, "RestrictNamespaces")) {
+ bool invert = false;
+ unsigned long flags = 0;
+
+ if (eq[0] == '~') {
+ invert = true;
+ eq++;
+ }
+
+ r = parse_boolean(eq);
+ if (r > 0)
+ flags = 0;
+ else if (r == 0)
+ flags = NAMESPACE_FLAGS_ALL;
+ else {
+ r = namespace_flag_from_string_many(eq, &flags);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
+ }
+
+ if (invert)
+ flags = (~flags) & NAMESPACE_FLAGS_ALL;
+
+ r = sd_bus_message_append(m, "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);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
+
+ r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
+ } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
+ const char *p = eq;
+
+ r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "(ssbt)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *source = NULL, *destination = NULL;
+ char *s = NULL, *d = NULL;
+ bool ignore_enoent = false;
+ uint64_t flags = MS_REC;
+
+ r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse argument: %m");
+ if (r == 0)
+ break;
+
+ s = source;
+ if (s[0] == '-') {
+ ignore_enoent = true;
+ s++;
+ }
+
+ if (p && p[-1] == ':') {
+ r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse argument: %m");
+ if (r == 0) {
+ log_error("Missing argument after ':': %s", eq);
+ return -EINVAL;
+ }
+
+ d = destination;
+
+ if (p && p[-1] == ':') {
+ _cleanup_free_ char *options = NULL;
+
+ r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse argument: %m");
+
+ if (isempty(options) || streq(options, "rbind"))
+ flags = MS_REC;
+ else if (streq(options, "norbind"))
+ flags = 0;
+ else {
+ log_error("Unknown options: %s", eq);
+ return -EINVAL;
+ }
+ }
+ } else
+ d = s;
+
+
+ r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
} else {
log_error("Unknown assignment %s.", assignment);
return -EINVAL;
return 0;
}
+int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
+ char **i;
+ int r;
+
+ assert(m);
+
+ STRV_FOREACH(i, l) {
+ r = bus_append_unit_property_assignment(m, *i);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
typedef struct BusWaitForJobs {
sd_bus *bus;
Set *jobs;
assert(d->name);
assert(result);
+ if (!endswith(d->name, ".service"))
+ return -EINVAL;
+
dbus_path = unit_dbus_path_from_name(d->name);
if (!dbus_path)
return -ENOMEM;
const char *result, *explanation;
} explanations [] = {
{ "resources", "of unavailable resources or another system error" },
+ { "protocol", "the service did not take the steps required by its unit configuration" },
{ "timeout", "a timeout was exceeded" },
{ "exit-code", "the control process exited with error code" },
{ "signal", "a fatal signal was delivered to the control process" },
assert(service);
- service_shell_quoted = shell_maybe_quote(service);
+ service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
- if (extra_args && extra_args[1]) {
+ if (!strv_isempty((char**) extra_args)) {
_cleanup_free_ char *t;
t = strv_join((char**) extra_args, " ");
log_error("Assertion failed on job for %s.", strna(d->name));
else if (streq(d->result, "unsupported"))
log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
- else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
+ else if (streq(d->result, "collected"))
+ log_error("Queued job for %s was garbage collected.", strna(d->name));
+ else if (!STR_IN_SET(d->result, "done", "skipped")) {
if (d->name) {
- int q;
_cleanup_free_ char *result = NULL;
+ int q;
q = bus_job_get_service_result(d, &result);
if (q < 0)
- log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
+ log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
log_job_error_with_service_result(d->name, result, extra_args);
} else
}
}
- if (streq(d->result, "canceled"))
+ if (STR_IN_SET(d->result, "canceled", "collected"))
r = -ECANCELED;
else if (streq(d->result, "timeout"))
r = -ETIME;
r = -EPROTO;
else if (streq(d->result, "unsupported"))
r = -EOPNOTSUPP;
- else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+ else if (!STR_IN_SET(d->result, "done", "skipped"))
r = -EIO;
return r;
if (r < 0)
return bus_log_parse_error(r);
- unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
+ unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
return 0;
}