]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
util: add some helpers for converting percent/permille/permyriad to parts of 2^32-1
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
291d565a
LP
2
3#include "alloc-util.h"
e45c81b8 4#include "bus-error.h"
291d565a
LP
5#include "bus-unit-util.h"
6#include "bus-util.h"
cffaed83 7#include "cap-list.h"
fdb3deca 8#include "cgroup-setup.h"
291d565a 9#include "cgroup-util.h"
3d63c749 10#include "condition.h"
ad21e542 11#include "coredump-util.h"
cffaed83 12#include "cpu-set-util.h"
9ece6444 13#include "dissect-image.h"
291d565a 14#include "escape.h"
b3d59367 15#include "exec-util.h"
2e2ed880 16#include "exit-status.h"
0389f4fa 17#include "fileio.h"
08f3be7a 18#include "hexdecoct.h"
3dc5ca97
LP
19#include "hostname-util.h"
20#include "in-addr-util.h"
da96ad5a 21#include "ip-protocol-list.h"
b3d13314 22#include "libmount-util.h"
291d565a 23#include "locale-util.h"
e45c81b8 24#include "log.h"
89382935 25#include "missing_fs.h"
049af8ad 26#include "mountpoint-util.h"
add00535 27#include "nsflags.h"
1808f768 28#include "numa-util.h"
291d565a 29#include "parse-util.h"
0389f4fa 30#include "path-util.h"
ed5033fd 31#include "percent-util.h"
291d565a
LP
32#include "process-util.h"
33#include "rlimit-util.h"
005bfaf1
TM
34#if HAVE_SECCOMP
35#include "seccomp-util.h"
36#endif
cffaed83 37#include "securebits-util.h"
291d565a 38#include "signal-util.h"
e45c81b8 39#include "socket-util.h"
760877e9 40#include "sort-util.h"
e2b2fb7f 41#include "stdio-util.h"
291d565a
LP
42#include "string-util.h"
43#include "syslog-util.h"
44#include "terminal-util.h"
89ada3ba 45#include "unit-def.h"
cffaed83 46#include "user-util.h"
291d565a 47#include "utf8.h"
291d565a 48
20b16441
LP
49int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
50 assert(message);
51 assert(u);
52
53 u->machine = NULL;
54
55 return sd_bus_message_read(
56 message,
57 "(ssssssouso)",
58 &u->id,
59 &u->description,
60 &u->load_state,
61 &u->active_state,
62 &u->sub_state,
63 &u->following,
64 &u->unit_path,
65 &u->job_id,
66 &u->job_type,
67 &u->job_path);
68}
69
0cf6628e
YW
70#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
71 static int bus_append_##parse_func( \
72 sd_bus_message *m, \
73 const char *field, \
74 const char *eq) { \
75 type val; \
76 int r; \
77 \
78 r = parse_func(eq, &val); \
79 if (r < 0) \
80 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
81 \
82 r = sd_bus_message_append(m, "(sv)", field, \
83 bus_type, (cast_type) val); \
84 if (r < 0) \
85 return bus_log_create_error(r); \
86 \
87 return 1; \
f6a8265b 88 }
3dc5ca97 89
0cf6628e
YW
90#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
91 static int bus_append_##parse_func( \
92 sd_bus_message *m, \
93 const char *field, \
94 const char *eq) { \
95 int r; \
96 \
97 r = parse_func(eq); \
baaa35ad
ZJS
98 if (r < 0) \
99 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
0cf6628e
YW
100 \
101 r = sd_bus_message_append(m, "(sv)", field, \
102 bus_type, (int32_t) r); \
103 if (r < 0) \
104 return bus_log_create_error(r); \
105 \
106 return 1; \
f6a8265b 107 }
3dc5ca97 108
0cf6628e
YW
109DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
110DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
111DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
112DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
113DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
005bfaf1
TM
114#if !HAVE_SECCOMP
115static inline int seccomp_parse_errno_or_action(const char *eq) { return -EINVAL; }
116#endif
117DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action);
0cf6628e
YW
118DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
119DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
29a3db75 120DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
d2b42d63 121DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
0cf6628e
YW
122DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
123DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
124DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
125DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
126DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
127DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
128DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
129DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
130DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
131DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
132DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
133DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
ad21e542 134DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
89ada3ba 135
a1e92eee 136static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
89ada3ba
YW
137 int r;
138
139 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
3dc5ca97 140 if (r < 0)
89ada3ba 141 return bus_log_create_error(r);
3dc5ca97 142
89ada3ba
YW
143 return 1;
144}
145
146static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
147 const char *p;
148 int r;
149
150 r = sd_bus_message_open_container(m, 'r', "sv");
3dc5ca97 151 if (r < 0)
89ada3ba 152 return bus_log_create_error(r);
3dc5ca97 153
89ada3ba 154 r = sd_bus_message_append_basic(m, 's', field);
3dc5ca97 155 if (r < 0)
89ada3ba 156 return bus_log_create_error(r);
3dc5ca97 157
89ada3ba 158 r = sd_bus_message_open_container(m, 'v', "as");
3dc5ca97 159 if (r < 0)
89ada3ba 160 return bus_log_create_error(r);
3dc5ca97 161
89ada3ba
YW
162 r = sd_bus_message_open_container(m, 'a', "s");
163 if (r < 0)
164 return bus_log_create_error(r);
3dc5ca97 165
89ada3ba
YW
166 for (p = eq;;) {
167 _cleanup_free_ char *word = NULL;
20b16441 168
89ada3ba
YW
169 r = extract_first_word(&p, &word, NULL, flags);
170 if (r == 0)
171 break;
172 if (r == -ENOMEM)
173 return log_oom();
174 if (r < 0)
175 return log_error_errno(r, "Invalid syntax: %s", eq);
20b16441 176
89ada3ba
YW
177 r = sd_bus_message_append_basic(m, 's', word);
178 if (r < 0)
179 return bus_log_create_error(r);
20b16441
LP
180 }
181
89ada3ba 182 r = sd_bus_message_close_container(m);
20b16441
LP
183 if (r < 0)
184 return bus_log_create_error(r);
185
89ada3ba
YW
186 r = sd_bus_message_close_container(m);
187 if (r < 0)
188 return bus_log_create_error(r);
20b16441 189
89ada3ba
YW
190 r = sd_bus_message_close_container(m);
191 if (r < 0)
192 return bus_log_create_error(r);
20b16441 193
89ada3ba
YW
194 return 1;
195}
20b16441 196
89ada3ba
YW
197static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
198 int r;
20b16441 199
89ada3ba
YW
200 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
201 if (r < 0)
202 return bus_log_create_error(r);
20b16441 203
89ada3ba
YW
204 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
205 if (r < 0)
206 return bus_log_create_error(r);
20b16441 207
89ada3ba
YW
208 r = sd_bus_message_open_container(m, 'v', "ay");
209 if (r < 0)
210 return bus_log_create_error(r);
20b16441 211
89ada3ba
YW
212 r = sd_bus_message_append_array(m, 'y', buf, n);
213 if (r < 0)
214 return bus_log_create_error(r);
9184ca48 215
89ada3ba
YW
216 r = sd_bus_message_close_container(m);
217 if (r < 0)
218 return bus_log_create_error(r);
20b16441 219
89ada3ba
YW
220 r = sd_bus_message_close_container(m);
221 if (r < 0)
222 return bus_log_create_error(r);
20b16441 223
89ada3ba
YW
224 return 1;
225}
d58d600e 226
89ada3ba
YW
227static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
228 char *n;
229 usec_t t;
230 size_t l;
231 int r;
d3070fbd 232
89ada3ba
YW
233 r = parse_sec(eq, &t);
234 if (r < 0)
235 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
d3070fbd 236
89ada3ba
YW
237 l = strlen(field);
238 n = newa(char, l + 2);
239 /* Change suffix Sec → USec */
240 strcpy(mempcpy(n, field, l - 3), "USec");
d3070fbd 241
89ada3ba
YW
242 r = sd_bus_message_append(m, "(sv)", n, "t", t);
243 if (r < 0)
244 return bus_log_create_error(r);
d3070fbd 245
89ada3ba
YW
246 return 1;
247}
d3070fbd 248
b48e508d 249static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
89ada3ba
YW
250 uint64_t v;
251 int r;
d3070fbd 252
b48e508d
YW
253 r = parse_size(eq, base, &v);
254 if (r < 0)
89ada3ba 255 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
d3070fbd 256
89ada3ba
YW
257 r = sd_bus_message_append(m, "(sv)", field, "t", v);
258 if (r < 0)
259 return bus_log_create_error(r);
d58d600e 260
89ada3ba
YW
261 return 1;
262}
d58d600e 263
89ada3ba 264static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
b3d59367
AZ
265 bool explicit_path = false, done = false;
266 _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
267 _cleanup_free_ char *path = NULL, *upgraded_name = NULL;
268 ExecCommandFlags flags = 0;
269 bool is_ex_prop = endswith(field, "Ex");
89ada3ba 270 int r;
d58d600e 271
89ada3ba
YW
272 do {
273 switch (*eq) {
d58d600e 274
89ada3ba 275 case '-':
b3d59367 276 if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
89ada3ba
YW
277 done = true;
278 else {
b3d59367 279 flags |= EXEC_COMMAND_IGNORE_FAILURE;
89ada3ba 280 eq++;
d58d600e 281 }
89ada3ba 282 break;
02638280 283
89ada3ba
YW
284 case '@':
285 if (explicit_path)
286 done = true;
287 else {
288 explicit_path = true;
289 eq++;
02638280 290 }
89ada3ba 291 break;
02638280 292
b3d59367
AZ
293 case ':':
294 if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
295 done = true;
296 else {
297 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
298 eq++;
299 }
300 break;
301
89ada3ba 302 case '+':
b3d59367
AZ
303 if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
304 done = true;
305 else {
306 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
307 eq++;
308 }
309 break;
310
89ada3ba 311 case '!':
b3d59367
AZ
312 if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
313 done = true;
314 else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
315 flags &= ~EXEC_COMMAND_NO_SETUID;
316 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
317 eq++;
318 } else {
319 flags |= EXEC_COMMAND_NO_SETUID;
320 eq++;
321 }
322 break;
83f8e808 323
89ada3ba
YW
324 default:
325 done = true;
326 break;
83f8e808 327 }
89ada3ba 328 } while (!done);
83f8e808 329
b3d59367
AZ
330 if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
331 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
332 is_ex_prop = true;
b910cc72 333 upgraded_name = strjoin(field, "Ex");
b3d59367
AZ
334 if (!upgraded_name)
335 return log_oom();
336 }
337
338 if (is_ex_prop) {
339 r = exec_command_flags_to_strv(flags, &ex_opts);
340 if (r < 0)
341 return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
342 }
343
89ada3ba 344 if (explicit_path) {
4ec85141 345 r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
89ada3ba
YW
346 if (r < 0)
347 return log_error_errno(r, "Failed to parse path: %m");
348 }
2038c3f5 349
90e30d76 350 r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
89ada3ba
YW
351 if (r < 0)
352 return log_error_errno(r, "Failed to parse command line: %m");
2038c3f5 353
89ada3ba
YW
354 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
355 if (r < 0)
356 return bus_log_create_error(r);
2038c3f5 357
b3d59367 358 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
89ada3ba
YW
359 if (r < 0)
360 return bus_log_create_error(r);
2038c3f5 361
b3d59367 362 r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
89ada3ba
YW
363 if (r < 0)
364 return bus_log_create_error(r);
08f3be7a 365
b3d59367 366 r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
89ada3ba
YW
367 if (r < 0)
368 return bus_log_create_error(r);
08f3be7a 369
89ada3ba 370 if (!strv_isempty(l)) {
08f3be7a 371
b3d59367 372 r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
89ada3ba
YW
373 if (r < 0)
374 return bus_log_create_error(r);
08f3be7a 375
89ada3ba 376 r = sd_bus_message_append(m, "s", path ?: l[0]);
08f3be7a
LP
377 if (r < 0)
378 return bus_log_create_error(r);
379
89ada3ba 380 r = sd_bus_message_append_strv(m, l);
08f3be7a
LP
381 if (r < 0)
382 return bus_log_create_error(r);
383
b3d59367 384 r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
08f3be7a
LP
385 if (r < 0)
386 return bus_log_create_error(r);
387
388 r = sd_bus_message_close_container(m);
89ada3ba
YW
389 if (r < 0)
390 return bus_log_create_error(r);
20b16441
LP
391 }
392
89ada3ba 393 r = sd_bus_message_close_container(m);
20b16441
LP
394 if (r < 0)
395 return bus_log_create_error(r);
396
89ada3ba
YW
397 r = sd_bus_message_close_container(m);
398 if (r < 0)
399 return bus_log_create_error(r);
20b16441 400
89ada3ba
YW
401 r = sd_bus_message_close_container(m);
402 if (r < 0)
403 return bus_log_create_error(r);
20b16441 404
89ada3ba
YW
405 return 1;
406}
20b16441 407
89ada3ba
YW
408static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
409 int r;
20b16441 410
89ada3ba
YW
411 assert(m);
412 assert(prefix);
20b16441 413
89ada3ba
YW
414 r = sd_bus_message_open_container(m, 'r', "iayu");
415 if (r < 0)
416 return r;
20b16441 417
89ada3ba
YW
418 r = sd_bus_message_append(m, "i", family);
419 if (r < 0)
420 return r;
66ebf6c0 421
89ada3ba
YW
422 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
423 if (r < 0)
424 return r;
66ebf6c0 425
89ada3ba
YW
426 r = sd_bus_message_append(m, "u", prefixlen);
427 if (r < 0)
428 return r;
66ebf6c0 429
89ada3ba
YW
430 return sd_bus_message_close_container(m);
431}
20b16441 432
89ada3ba
YW
433static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
434 int r;
20b16441 435
4d824a4e
AZ
436 if (STR_IN_SET(field, "DevicePolicy",
437 "Slice",
438 "ManagedOOMSwap",
4e806bfa
AZ
439 "ManagedOOMMemoryPressure",
440 "ManagedOOMPreference"))
89ada3ba 441 return bus_append_string(m, field, eq);
13c31542 442
0a9f9344
AZ
443 if (STR_IN_SET(field, "ManagedOOMMemoryPressureLimit")) {
444 char *n;
445
446 r = parse_permyriad(eq);
447 if (r < 0)
448 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
449
450 n = strjoina(field, "Permyriad");
451
452 r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) r);
453 if (r < 0)
454 return bus_log_create_error(r);
455
456 return 1;
457 }
458
c57d2a76
ZJS
459 if (STR_IN_SET(field, "CPUAccounting",
460 "MemoryAccounting",
461 "IOAccounting",
462 "BlockIOAccounting",
463 "TasksAccounting",
464 "IPAccounting"))
89ada3ba 465 return bus_append_parse_boolean(m, field, eq);
13c31542 466
c57d2a76
ZJS
467 if (STR_IN_SET(field, "CPUWeight",
468 "StartupCPUWeight",
469 "IOWeight",
470 "StartupIOWeight"))
89ada3ba 471 return bus_append_cg_weight_parse(m, field, eq);
20b16441 472
c57d2a76
ZJS
473 if (STR_IN_SET(field, "CPUShares",
474 "StartupCPUShares"))
89ada3ba 475 return bus_append_cg_cpu_shares_parse(m, field, eq);
268833ed 476
c57d2a76
ZJS
477 if (STR_IN_SET(field, "AllowedCPUs",
478 "AllowedMemoryNodes")) {
047f5d63
PH
479 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
480 _cleanup_free_ uint8_t *array = NULL;
481 size_t allocated;
482
483 r = parse_cpu_set(eq, &cpuset);
484 if (r < 0)
485 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
486
487 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
488 if (r < 0)
489 return log_error_errno(r, "Failed to serialize CPUSet: %m");
490
491 return bus_append_byte_array(m, field, array, allocated);
492 }
493
c57d2a76
ZJS
494 if (STR_IN_SET(field, "BlockIOWeight",
495 "StartupBlockIOWeight"))
89ada3ba 496 return bus_append_cg_blkio_weight_parse(m, field, eq);
08f3be7a 497
25cc30c4 498 if (streq(field, "DisableControllers"))
4ec85141 499 return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
25cc30c4 500
89ada3ba 501 if (streq(field, "Delegate")) {
89ada3ba 502 r = parse_boolean(eq);
08f3be7a 503 if (r < 0)
4ec85141 504 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
08f3be7a 505
89ada3ba 506 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
08f3be7a
LP
507 if (r < 0)
508 return bus_log_create_error(r);
509
89ada3ba
YW
510 return 1;
511 }
08f3be7a 512
27e946a5
ZJS
513 if (STR_IN_SET(field, "MemoryMin",
514 "DefaultMemoryLow",
515 "DefaultMemoryMin",
516 "MemoryLow",
517 "MemoryHigh",
518 "MemoryMax",
519 "MemorySwapMax",
520 "MemoryLimit",
521 "TasksMax")) {
cffaed83 522
db2b8d2e 523 if (streq(eq, "infinity")) {
89ada3ba
YW
524 r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
525 if (r < 0)
526 return bus_log_create_error(r);
527 return 1;
db2b8d2e 528 } else if (isempty(eq)) {
311a0e2e
ZJS
529 uint64_t empty_value = STR_IN_SET(field,
530 "DefaultMemoryLow",
531 "DefaultMemoryMin",
532 "MemoryLow",
533 "MemoryMin") ?
534 CGROUP_LIMIT_MIN :
535 CGROUP_LIMIT_MAX;
536
537 r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
89ada3ba
YW
538 if (r < 0)
539 return bus_log_create_error(r);
540 return 1;
cffaed83
YW
541 }
542
fe845b5e 543 r = parse_permyriad(eq);
89ada3ba
YW
544 if (r >= 0) {
545 char *n;
20b16441 546
f806dfd3
LP
547 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
548 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
549 * size can be determined server-side. */
20b16441 550
89ada3ba 551 n = strjoina(field, "Scale");
fe845b5e 552 r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 10000U));
89ada3ba
YW
553 if (r < 0)
554 return bus_log_create_error(r);
20b16441 555
89ada3ba 556 return 1;
20b16441
LP
557 }
558
89ada3ba 559 if (streq(field, "TasksMax"))
62b749a9 560 return bus_append_safe_atou64(m, field, eq);
20b16441 561
62b749a9 562 return bus_append_parse_size(m, field, eq, 1024);
89ada3ba 563 }
cffaed83 564
89ada3ba 565 if (streq(field, "CPUQuota")) {
89ada3ba
YW
566 if (isempty(eq))
567 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
568 else {
fe845b5e 569 r = parse_permyriad_unbounded(eq);
baaa35ad
ZJS
570 if (r == 0)
571 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
572 "CPU quota too small.");
f806dfd3
LP
573 if (r < 0)
574 return log_error_errno(r, "CPU quota '%s' invalid.", eq);
89ada3ba 575
fe845b5e 576 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 10000U));
cffaed83
YW
577 }
578
6bbfdc67 579 if (r < 0)
89ada3ba 580 return bus_log_create_error(r);
cffaed83 581
89ada3ba
YW
582 return 1;
583 }
cffaed83 584
10f28641
FB
585 if (streq(field, "CPUQuotaPeriodSec")) {
586 usec_t u = USEC_INFINITY;
587
588 r = parse_sec_def_infinity(eq, &u);
589 if (r < 0)
590 return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
591
592 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
593 if (r < 0)
594 return bus_log_create_error(r);
595
596 return 1;
597 }
598
89ada3ba 599 if (streq(field, "DeviceAllow")) {
20b16441 600 if (isempty(eq))
89ada3ba 601 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
20b16441 602 else {
d9f7305f 603 const char *path = eq, *rwm = NULL, *e;
20b16441
LP
604
605 e = strchr(eq, ' ');
606 if (e) {
607 path = strndupa(eq, e - eq);
608 rwm = e+1;
20b16441
LP
609 }
610
d9f7305f 611 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
20b16441
LP
612 }
613
89ada3ba
YW
614 if (r < 0)
615 return bus_log_create_error(r);
616
617 return 1;
618 }
619
620 if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
20b16441 621 if (isempty(eq))
89ada3ba 622 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
20b16441
LP
623 else {
624 const char *path, *bandwidth, *e;
625 uint64_t bytes;
626
627 e = strchr(eq, ' ');
baaa35ad
ZJS
628 if (!e)
629 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
630 "Failed to parse %s value %s.",
631 field, eq);
20b16441 632
d9f7305f
YW
633 path = strndupa(eq, e - eq);
634 bandwidth = e+1;
20b16441 635
79d53eb8 636 if (streq(bandwidth, "infinity"))
13c31542 637 bytes = CGROUP_LIMIT_MAX;
79d53eb8 638 else {
13c31542 639 r = parse_size(bandwidth, 1000, &bytes);
6bbfdc67
LP
640 if (r < 0)
641 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
20b16441
LP
642 }
643
89ada3ba 644 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
20b16441
LP
645 }
646
89ada3ba
YW
647 if (r < 0)
648 return bus_log_create_error(r);
649
650 return 1;
651 }
652
c57d2a76
ZJS
653 if (STR_IN_SET(field, "IODeviceWeight",
654 "BlockIODeviceWeight")) {
20b16441 655 if (isempty(eq))
89ada3ba 656 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
20b16441
LP
657 else {
658 const char *path, *weight, *e;
659 uint64_t u;
660
661 e = strchr(eq, ' ');
baaa35ad
ZJS
662 if (!e)
663 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
664 "Failed to parse %s value %s.",
665 field, eq);
20b16441 666
d9f7305f
YW
667 path = strndupa(eq, e - eq);
668 weight = e+1;
20b16441
LP
669
670 r = safe_atou64(weight, &u);
6bbfdc67
LP
671 if (r < 0)
672 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
673
89ada3ba 674 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
20b16441
LP
675 }
676
89ada3ba
YW
677 if (r < 0)
678 return bus_log_create_error(r);
3dc5ca97 679
89ada3ba
YW
680 return 1;
681 }
3dc5ca97 682
6ae4283c
TH
683 if (streq(field, "IODeviceLatencyTargetSec")) {
684 const char *field_usec = "IODeviceLatencyTargetUSec";
685
686 if (isempty(eq))
687 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
688 else {
689 const char *path, *target, *e;
690 usec_t usec;
691
692 e = strchr(eq, ' ');
baaa35ad
ZJS
693 if (!e)
694 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
695 "Failed to parse %s value %s.",
696 field, eq);
6ae4283c
TH
697
698 path = strndupa(eq, e - eq);
699 target = e+1;
700
701 r = parse_sec(target, &usec);
702 if (r < 0)
703 return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
704
705 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
706 }
707
708 if (r < 0)
709 return bus_log_create_error(r);
710
711 return 1;
712 }
713
c57d2a76
ZJS
714 if (STR_IN_SET(field, "IPAddressAllow",
715 "IPAddressDeny")) {
89ada3ba
YW
716 unsigned char prefixlen;
717 union in_addr_union prefix = {};
718 int family;
3dc5ca97 719
89ada3ba
YW
720 if (isempty(eq)) {
721 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
3dc5ca97
LP
722 if (r < 0)
723 return bus_log_create_error(r);
724
89ada3ba
YW
725 return 1;
726 }
3dc5ca97 727
89ada3ba
YW
728 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
729 if (r < 0)
730 return bus_log_create_error(r);
3dc5ca97 731
89ada3ba
YW
732 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
733 if (r < 0)
734 return bus_log_create_error(r);
3dc5ca97 735
89ada3ba
YW
736 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
737 if (r < 0)
738 return bus_log_create_error(r);
3dc5ca97 739
89ada3ba
YW
740 r = sd_bus_message_open_container(m, 'a', "(iayu)");
741 if (r < 0)
742 return bus_log_create_error(r);
3dc5ca97 743
89ada3ba
YW
744 if (streq(eq, "any")) {
745 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
3dc5ca97 746
89ada3ba
YW
747 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
748 if (r < 0)
749 return bus_log_create_error(r);
3dc5ca97 750
89ada3ba
YW
751 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
752 if (r < 0)
753 return bus_log_create_error(r);
3dc5ca97 754
89ada3ba
YW
755 } else if (is_localhost(eq)) {
756 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
3dc5ca97 757
89ada3ba
YW
758 prefix.in.s_addr = htobe32(0x7f000000);
759 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
760 if (r < 0)
761 return bus_log_create_error(r);
3dc5ca97 762
89ada3ba
YW
763 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
764 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
765 if (r < 0)
766 return r;
3dc5ca97 767
89ada3ba
YW
768 } else if (streq(eq, "link-local")) {
769 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
3dc5ca97 770
89ada3ba
YW
771 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
772 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
773 if (r < 0)
774 return bus_log_create_error(r);
3dc5ca97 775
89ada3ba
YW
776 prefix.in6 = (struct in6_addr) {
777 .s6_addr32[0] = htobe32(0xfe800000)
778 };
779 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
780 if (r < 0)
781 return bus_log_create_error(r);
3dc5ca97 782
89ada3ba
YW
783 } else if (streq(eq, "multicast")) {
784 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
3dc5ca97 785
89ada3ba
YW
786 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
787 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
788 if (r < 0)
789 return bus_log_create_error(r);
3dc5ca97 790
89ada3ba
YW
791 prefix.in6 = (struct in6_addr) {
792 .s6_addr32[0] = htobe32(0xff000000)
793 };
794 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
3dc5ca97
LP
795 if (r < 0)
796 return bus_log_create_error(r);
797
89ada3ba 798 } else {
afc1feae
RC
799 for (;;) {
800 _cleanup_free_ char *word = NULL;
801
802 r = extract_first_word(&eq, &word, NULL, 0);
803 if (r == 0)
804 break;
805 if (r == -ENOMEM)
806 return log_oom();
807 if (r < 0)
808 return log_error_errno(r, "Failed to parse %s: %s", field, eq);
89ada3ba 809
afc1feae
RC
810 r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
811 if (r < 0)
812 return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
813
814 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
815 if (r < 0)
816 return bus_log_create_error(r);
817 }
3dc5ca97
LP
818 }
819
89ada3ba
YW
820 r = sd_bus_message_close_container(m);
821 if (r < 0)
822 return bus_log_create_error(r);
cffaed83 823
89ada3ba
YW
824 r = sd_bus_message_close_container(m);
825 if (r < 0)
826 return bus_log_create_error(r);
cffaed83 827
89ada3ba
YW
828 r = sd_bus_message_close_container(m);
829 if (r < 0)
830 return bus_log_create_error(r);
fab34748
KL
831
832 return 1;
833 }
834
c57d2a76
ZJS
835 if (STR_IN_SET(field, "IPIngressFilterPath",
836 "IPEgressFilterPath")) {
fab34748
KL
837 if (isempty(eq))
838 r = sd_bus_message_append(m, "(sv)", field, "as", 0);
839 else
840 r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
841
842 if (r < 0)
843 return bus_log_create_error(r);
cffaed83 844
89ada3ba
YW
845 return 1;
846 }
cffaed83 847
89ada3ba
YW
848 return 0;
849}
cffaed83 850
3d63c749 851static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 852 if (streq(field, "Where"))
3d63c749
YW
853 return bus_append_string(m, field, eq);
854
855 if (streq(field, "DirectoryMode"))
3d63c749
YW
856 return bus_append_parse_mode(m, field, eq);
857
858 if (streq(field, "TimeoutIdleSec"))
3d63c749
YW
859 return bus_append_parse_sec_rename(m, field, eq);
860
861 return 0;
862}
863
89ada3ba 864static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
6550c24c
LP
865 const char *suffix;
866 int r;
cffaed83 867
c57d2a76
ZJS
868 if (STR_IN_SET(field, "User",
869 "Group",
870 "UtmpIdentifier",
871 "UtmpMode",
872 "PAMName",
873 "TTYPath",
874 "WorkingDirectory",
875 "RootDirectory",
876 "SyslogIdentifier",
877 "ProtectSystem",
878 "ProtectHome",
879 "SELinuxContext",
880 "RootImage",
0389f4fa 881 "RootVerity",
c57d2a76
ZJS
882 "RuntimeDirectoryPreserve",
883 "Personality",
884 "KeyringMode",
4e399953
LP
885 "ProtectProc",
886 "ProcSubset",
91dd5f7c
LP
887 "NetworkNamespacePath",
888 "LogNamespace"))
89ada3ba 889 return bus_append_string(m, field, eq);
cffaed83 890
c57d2a76
ZJS
891 if (STR_IN_SET(field, "IgnoreSIGPIPE",
892 "TTYVHangup",
893 "TTYReset",
894 "TTYVTDisallocate",
895 "PrivateTmp",
896 "PrivateDevices",
897 "PrivateNetwork",
898 "PrivateUsers",
899 "PrivateMounts",
900 "NoNewPrivileges",
901 "SyslogLevelPrefix",
902 "MemoryDenyWriteExecute",
903 "RestrictRealtime",
904 "DynamicUser",
905 "RemoveIPC",
906 "ProtectKernelTunables",
907 "ProtectKernelModules",
908 "ProtectKernelLogs",
fc64760d 909 "ProtectClock",
c57d2a76
ZJS
910 "ProtectControlGroups",
911 "MountAPIVFS",
912 "CPUSchedulingResetOnFork",
913 "LockPersonality",
914 "ProtectHostname",
915 "RestrictSUIDSGID"))
89ada3ba 916 return bus_append_parse_boolean(m, field, eq);
cffaed83 917
c57d2a76
ZJS
918 if (STR_IN_SET(field, "ReadWriteDirectories",
919 "ReadOnlyDirectories",
920 "InaccessibleDirectories",
921 "ReadWritePaths",
922 "ReadOnlyPaths",
923 "InaccessiblePaths",
ddc155b2
TM
924 "ExecPaths",
925 "NoExecPaths",
c57d2a76
ZJS
926 "RuntimeDirectory",
927 "StateDirectory",
928 "CacheDirectory",
929 "LogsDirectory",
930 "ConfigurationDirectory",
931 "SupplementaryGroups",
932 "SystemCallArchitectures"))
4ec85141 933 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
20b16441 934
c57d2a76
ZJS
935 if (STR_IN_SET(field, "SyslogLevel",
936 "LogLevelMax"))
89ada3ba 937 return bus_append_log_level_from_string(m, field, eq);
20b16441 938
89ada3ba 939 if (streq(field, "SyslogFacility"))
89ada3ba 940 return bus_append_log_facility_unshifted_from_string(m, field, eq);
cffaed83 941
89ada3ba 942 if (streq(field, "SecureBits"))
89ada3ba 943 return bus_append_secure_bits_from_string(m, field, eq);
cffaed83 944
89ada3ba 945 if (streq(field, "CPUSchedulingPolicy"))
89ada3ba 946 return bus_append_sched_policy_from_string(m, field, eq);
cffaed83 947
c57d2a76
ZJS
948 if (STR_IN_SET(field, "CPUSchedulingPriority",
949 "OOMScoreAdjust"))
89ada3ba 950 return bus_append_safe_atoi(m, field, eq);
3f856a28 951
ad21e542
ZJS
952 if (streq(field, "CoredumpFilter"))
953 return bus_append_coredump_filter_mask_from_string(m, field, eq);
954
89ada3ba 955 if (streq(field, "Nice"))
89ada3ba 956 return bus_append_parse_nice(m, field, eq);
3f856a28 957
89ada3ba 958 if (streq(field, "SystemCallErrorNumber"))
005bfaf1 959 return bus_append_seccomp_parse_errno_or_action(m, field, eq);
cffaed83 960
89ada3ba 961 if (streq(field, "IOSchedulingClass"))
89ada3ba 962 return bus_append_ioprio_class_from_string(m, field, eq);
cffaed83 963
89ada3ba 964 if (streq(field, "IOSchedulingPriority"))
89ada3ba 965 return bus_append_ioprio_parse_priority(m, field, eq);
cffaed83 966
c57d2a76
ZJS
967 if (STR_IN_SET(field, "RuntimeDirectoryMode",
968 "StateDirectoryMode",
969 "CacheDirectoryMode",
970 "LogsDirectoryMode",
971 "ConfigurationDirectoryMode",
972 "UMask"))
89ada3ba 973 return bus_append_parse_mode(m, field, eq);
cffaed83 974
89ada3ba 975 if (streq(field, "TimerSlackNSec"))
89ada3ba 976 return bus_append_parse_nsec(m, field, eq);
cffaed83 977
691d6f6d 978 if (streq(field, "LogRateLimitIntervalSec"))
90fc172e
AZ
979 return bus_append_parse_sec_rename(m, field, eq);
980
981 if (streq(field, "LogRateLimitBurst"))
90fc172e
AZ
982 return bus_append_safe_atou(m, field, eq);
983
89ada3ba 984 if (streq(field, "MountFlags"))
89ada3ba 985 return bus_append_mount_propagation_flags_from_string(m, field, eq);
cffaed83 986
c57d2a76
ZJS
987 if (STR_IN_SET(field, "Environment",
988 "UnsetEnvironment",
989 "PassEnvironment"))
4ec85141 990 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
cffaed83 991
89ada3ba 992 if (streq(field, "EnvironmentFile")) {
89ada3ba
YW
993 if (isempty(eq))
994 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
995 else
996 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
997 eq[0] == '-' ? eq + 1 : eq,
998 eq[0] == '-');
999 if (r < 0)
1000 return bus_log_create_error(r);
cffaed83 1001
89ada3ba
YW
1002 return 1;
1003 }
cffaed83 1004
bb0c0d6f
LP
1005 if (streq(field, "SetCredential")) {
1006 r = sd_bus_message_open_container(m, 'r', "sv");
1007 if (r < 0)
1008 return bus_log_create_error(r);
1009
1010 r = sd_bus_message_append_basic(m, 's', "SetCredential");
1011 if (r < 0)
1012 return bus_log_create_error(r);
1013
1014 r = sd_bus_message_open_container(m, 'v', "a(say)");
1015 if (r < 0)
1016 return bus_log_create_error(r);
1017
1018 if (isempty(eq))
1019 r = sd_bus_message_append(m, "a(say)", 0);
1020 else {
1021 _cleanup_free_ char *word = NULL, *unescaped = NULL;
1022 const char *p = eq;
1023 int l;
1024
1025 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1026 if (r == -ENOMEM)
1027 return log_oom();
1028 if (r < 0)
1029 return log_error_errno(r, "Failed to parse SetCredential= parameter: %s", eq);
1030 if (r == 0 || !p)
1031 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to SetCredential=.");
1032
1033 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
1034 if (l < 0)
1035 return log_error_errno(l, "Failed to unescape SetCredential= value: %s", p);
1036
1037 r = sd_bus_message_open_container(m, 'a', "(say)");
1038 if (r < 0)
1039 return bus_log_create_error(r);
1040
1041 r = sd_bus_message_open_container(m, 'r', "say");
1042 if (r < 0)
1043 return bus_log_create_error(r);
1044
1045 r = sd_bus_message_append(m, "s", word);
1046 if (r < 0)
1047 return bus_log_create_error(r);
1048
1049 r = sd_bus_message_append_array(m, 'y', unescaped, l);
1050 if (r < 0)
1051 return bus_log_create_error(r);
1052
1053 r = sd_bus_message_close_container(m);
1054 if (r < 0)
1055 return bus_log_create_error(r);
1056
1057 r = sd_bus_message_close_container(m);
1058 }
1059 if (r < 0)
1060 return bus_log_create_error(r);
1061
1062 r = sd_bus_message_close_container(m);
1063 if (r < 0)
1064 return bus_log_create_error(r);
1065
1066 r = sd_bus_message_close_container(m);
1067 if (r < 0)
1068 return bus_log_create_error(r);
1069
1070 return 1;
1071 }
1072
1073 if (streq(field, "LoadCredential")) {
1074 r = sd_bus_message_open_container(m, 'r', "sv");
1075 if (r < 0)
1076 return bus_log_create_error(r);
1077
1078 r = sd_bus_message_append_basic(m, 's', "LoadCredential");
1079 if (r < 0)
1080 return bus_log_create_error(r);
1081
1082 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1083 if (r < 0)
1084 return bus_log_create_error(r);
1085
1086 if (isempty(eq))
1087 r = sd_bus_message_append(m, "a(ss)", 0);
1088 else {
1089 _cleanup_free_ char *word = NULL;
1090 const char *p = eq;
1091
1092 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1093 if (r == -ENOMEM)
1094 return log_oom();
1095 if (r < 0)
1096 return log_error_errno(r, "Failed to parse LoadCredential= parameter: %s", eq);
1097 if (r == 0 || !p)
1098 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to LoadCredential=.");
1099
1100 r = sd_bus_message_append(m, "a(ss)", 1, word, p);
1101 }
1102 if (r < 0)
1103 return bus_log_create_error(r);
1104
1105 r = sd_bus_message_close_container(m);
1106 if (r < 0)
1107 return bus_log_create_error(r);
1108
1109 r = sd_bus_message_close_container(m);
1110 if (r < 0)
1111 return bus_log_create_error(r);
1112
1113 return 1;
1114 }
1115
89ada3ba 1116 if (streq(field, "LogExtraFields")) {
89ada3ba 1117 r = sd_bus_message_open_container(m, 'r', "sv");
3f856a28
YW
1118 if (r < 0)
1119 return bus_log_create_error(r);
1120
89ada3ba 1121 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
3f856a28
YW
1122 if (r < 0)
1123 return bus_log_create_error(r);
1124
89ada3ba 1125 r = sd_bus_message_open_container(m, 'v', "aay");
3f856a28
YW
1126 if (r < 0)
1127 return bus_log_create_error(r);
1128
89ada3ba 1129 r = sd_bus_message_open_container(m, 'a', "ay");
cffaed83
YW
1130 if (r < 0)
1131 return bus_log_create_error(r);
1132
89ada3ba 1133 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
3f856a28
YW
1134 if (r < 0)
1135 return bus_log_create_error(r);
cffaed83 1136
3f856a28
YW
1137 r = sd_bus_message_close_container(m);
1138 if (r < 0)
1139 return bus_log_create_error(r);
9f617cd0 1140
89ada3ba
YW
1141 r = sd_bus_message_close_container(m);
1142 if (r < 0)
1143 return bus_log_create_error(r);
9efb9df9 1144
89ada3ba 1145 r = sd_bus_message_close_container(m);
9efb9df9 1146 if (r < 0)
89ada3ba
YW
1147 return bus_log_create_error(r);
1148
1149 return 1;
1150 }
9efb9df9 1151
c57d2a76
ZJS
1152 if (STR_IN_SET(field, "StandardInput",
1153 "StandardOutput",
1154 "StandardError")) {
89ada3ba 1155 const char *n, *appended;
9efb9df9 1156
89ada3ba
YW
1157 if ((n = startswith(eq, "fd:"))) {
1158 appended = strjoina(field, "FileDescriptorName");
1159 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1160 } else if ((n = startswith(eq, "file:"))) {
1161 appended = strjoina(field, "File");
1162 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
8d33232e
LP
1163 } else if ((n = startswith(eq, "append:"))) {
1164 appended = strjoina(field, "FileToAppend");
1165 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
8d7dab1f
LW
1166 } else if ((n = startswith(eq, "truncate:"))) {
1167 appended = strjoina(field, "FileToTruncate");
1168 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
89ada3ba
YW
1169 } else
1170 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
89ada3ba
YW
1171 if (r < 0)
1172 return bus_log_create_error(r);
7f452159 1173
89ada3ba
YW
1174 return 1;
1175 }
7f452159 1176
89ada3ba
YW
1177 if (streq(field, "StandardInputText")) {
1178 _cleanup_free_ char *unescaped = NULL;
7f452159 1179
89ada3ba 1180 r = cunescape(eq, 0, &unescaped);
7f452159 1181 if (r < 0)
89ada3ba 1182 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
7f452159 1183
c2bc710b 1184 if (!strextend(&unescaped, "\n"))
89ada3ba 1185 return log_oom();
7f452159 1186
89ada3ba
YW
1187 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
1188 * interface anyway */
20b16441 1189
89ada3ba
YW
1190 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
1191 }
20b16441 1192
89ada3ba
YW
1193 if (streq(field, "StandardInputData")) {
1194 _cleanup_free_ void *decoded = NULL;
1195 size_t sz;
1196
1197 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
20b16441 1198 if (r < 0)
89ada3ba 1199 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
20b16441 1200
89ada3ba
YW
1201 return bus_append_byte_array(m, field, decoded, sz);
1202 }
20b16441 1203
6550c24c
LP
1204 if ((suffix = startswith(field, "Limit"))) {
1205 int rl;
20b16441 1206
6550c24c
LP
1207 rl = rlimit_from_string(suffix);
1208 if (rl >= 0) {
1209 const char *sn;
1210 struct rlimit l;
20b16441 1211
6550c24c
LP
1212 r = rlimit_parse(rl, eq, &l);
1213 if (r < 0)
1214 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
20b16441 1215
6550c24c
LP
1216 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
1217 if (r < 0)
1218 return bus_log_create_error(r);
20b16441 1219
6550c24c
LP
1220 sn = strjoina(field, "Soft");
1221 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
1222 if (r < 0)
1223 return bus_log_create_error(r);
1224
1225 return 1;
1226 }
89ada3ba 1227 }
20b16441 1228
c57d2a76
ZJS
1229 if (STR_IN_SET(field, "AppArmorProfile",
1230 "SmackProcessLabel")) {
89ada3ba
YW
1231 int ignore = 0;
1232 const char *s = eq;
20b16441 1233
89ada3ba
YW
1234 if (eq[0] == '-') {
1235 ignore = 1;
1236 s = eq + 1;
20b16441
LP
1237 }
1238
89ada3ba 1239 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
6bbfdc67 1240 if (r < 0)
89ada3ba 1241 return bus_log_create_error(r);
20b16441 1242
89ada3ba
YW
1243 return 1;
1244 }
20b16441 1245
c57d2a76
ZJS
1246 if (STR_IN_SET(field, "CapabilityBoundingSet",
1247 "AmbientCapabilities")) {
89ada3ba
YW
1248 uint64_t sum = 0;
1249 bool invert = false;
1250 const char *p = eq;
afcb1cd3 1251
89ada3ba
YW
1252 if (*p == '~') {
1253 invert = true;
1254 p++;
1255 }
20b16441 1256
89ada3ba 1257 r = capability_set_from_string(p, &sum);
20b16441 1258 if (r < 0)
89ada3ba 1259 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441 1260
89ada3ba
YW
1261 sum = invert ? ~sum : sum;
1262
1263 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
20b16441
LP
1264 if (r < 0)
1265 return bus_log_create_error(r);
1266
89ada3ba
YW
1267 return 1;
1268 }
20b16441 1269
89ada3ba 1270 if (streq(field, "CPUAffinity")) {
0985c7c4 1271 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
c367f996
MS
1272 _cleanup_free_ uint8_t *array = NULL;
1273 size_t allocated;
20b16441 1274
e2b2fb7f
MS
1275 if (eq && streq(eq, "numa")) {
1276 r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
1277 if (r < 0)
1278 return bus_log_create_error(r);
1279 return r;
1280 }
1281
89ada3ba
YW
1282 r = parse_cpu_set(eq, &cpuset);
1283 if (r < 0)
1284 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
20b16441 1285
c367f996
MS
1286 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
1287 if (r < 0)
1288 return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
1289
1290 return bus_append_byte_array(m, field, array, allocated);
89ada3ba 1291 }
20b7a007 1292
b070c7c0
MS
1293 if (streq(field, "NUMAPolicy")) {
1294 r = mpol_from_string(eq);
1295 if (r < 0)
1296 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1297
1298 r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
1299 if (r < 0)
1300 return bus_log_create_error(r);
1301
1302 return 1;
1303 }
1304
1305 if (streq(field, "NUMAMask")) {
1306 _cleanup_(cpu_set_reset) CPUSet nodes = {};
1307 _cleanup_free_ uint8_t *array = NULL;
1308 size_t allocated;
1309
332d387f
MS
1310 if (eq && streq(eq, "all")) {
1311 r = numa_mask_add_all(&nodes);
1312 if (r < 0)
1313 return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
1314 } else {
1315 r = parse_cpu_set(eq, &nodes);
1316 if (r < 0)
1317 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1318 }
b070c7c0
MS
1319
1320 r = cpu_set_to_dbus(&nodes, &array, &allocated);
1321 if (r < 0)
1322 return log_error_errno(r, "Failed to serialize NUMAMask: %m");
1323
1324 return bus_append_byte_array(m, field, array, allocated);
1325 }
1326
c57d2a76 1327 if (STR_IN_SET(field, "RestrictAddressFamilies",
9df2cdd8
TM
1328 "SystemCallFilter",
1329 "SystemCallLog")) {
6b000af4 1330 int allow_list = 1;
89ada3ba 1331 const char *p = eq;
20b16441 1332
89ada3ba 1333 if (*p == '~') {
6b000af4 1334 allow_list = 0;
89ada3ba 1335 p++;
20b16441
LP
1336 }
1337
89ada3ba 1338 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
20b16441
LP
1339 if (r < 0)
1340 return bus_log_create_error(r);
1341
89ada3ba
YW
1342 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1343 if (r < 0)
1344 return bus_log_create_error(r);
1345
1346 r = sd_bus_message_open_container(m, 'v', "(bas)");
1347 if (r < 0)
1348 return bus_log_create_error(r);
20b16441 1349
89ada3ba
YW
1350 r = sd_bus_message_open_container(m, 'r', "bas");
1351 if (r < 0)
1352 return bus_log_create_error(r);
cffaed83 1353
6b000af4 1354 r = sd_bus_message_append_basic(m, 'b', &allow_list);
cffaed83
YW
1355 if (r < 0)
1356 return bus_log_create_error(r);
1357
1358 r = sd_bus_message_open_container(m, 'a', "s");
1359 if (r < 0)
1360 return bus_log_create_error(r);
1361
98008caa 1362 for (;;) {
cffaed83
YW
1363 _cleanup_free_ char *word = NULL;
1364
4ec85141 1365 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
cffaed83
YW
1366 if (r == 0)
1367 break;
89ada3ba
YW
1368 if (r == -ENOMEM)
1369 return log_oom();
1370 if (r < 0)
1371 return log_error_errno(r, "Invalid syntax: %s", eq);
cffaed83
YW
1372
1373 r = sd_bus_message_append_basic(m, 's', word);
1374 if (r < 0)
1375 return bus_log_create_error(r);
1376 }
1377
1378 r = sd_bus_message_close_container(m);
1379 if (r < 0)
1380 return bus_log_create_error(r);
1381
1382 r = sd_bus_message_close_container(m);
20b16441
LP
1383 if (r < 0)
1384 return bus_log_create_error(r);
1385
89ada3ba 1386 r = sd_bus_message_close_container(m);
20b16441
LP
1387 if (r < 0)
1388 return bus_log_create_error(r);
1389
20b16441
LP
1390 r = sd_bus_message_close_container(m);
1391 if (r < 0)
1392 return bus_log_create_error(r);
1393
89ada3ba
YW
1394 return 1;
1395 }
20b16441 1396
89ada3ba 1397 if (streq(field, "RestrictNamespaces")) {
add00535 1398 bool invert = false;
aa9d574d 1399 unsigned long flags;
add00535
LP
1400
1401 r = parse_boolean(eq);
1402 if (r > 0)
1403 flags = 0;
1404 else if (r == 0)
1405 flags = NAMESPACE_FLAGS_ALL;
1406 else {
aa9d574d
YW
1407 if (eq[0] == '~') {
1408 invert = true;
1409 eq++;
1410 }
1411
86c2a9f1 1412 r = namespace_flags_from_string(eq, &flags);
add00535
LP
1413 if (r < 0)
1414 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1415 }
1416
1417 if (invert)
1418 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1419
89ada3ba
YW
1420 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1421 if (r < 0)
1422 return bus_log_create_error(r);
afcb1cd3 1423
89ada3ba
YW
1424 return 1;
1425 }
afcb1cd3 1426
c57d2a76
ZJS
1427 if (STR_IN_SET(field, "BindPaths",
1428 "BindReadOnlyPaths")) {
89ada3ba 1429 const char *p = eq;
83555251 1430
89ada3ba 1431 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
c7383828 1432 if (r < 0)
89ada3ba 1433 return bus_log_create_error(r);
afcb1cd3 1434
89ada3ba
YW
1435 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1436 if (r < 0)
1437 return bus_log_create_error(r);
d2d6c096
LP
1438
1439 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1440 if (r < 0)
89ada3ba 1441 return bus_log_create_error(r);
d2d6c096
LP
1442
1443 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1444 if (r < 0)
89ada3ba 1445 return bus_log_create_error(r);
d2d6c096
LP
1446
1447 for (;;) {
1448 _cleanup_free_ char *source = NULL, *destination = NULL;
1449 char *s = NULL, *d = NULL;
1450 bool ignore_enoent = false;
1451 uint64_t flags = MS_REC;
1452
4ec85141 1453 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
1454 if (r < 0)
1455 return log_error_errno(r, "Failed to parse argument: %m");
1456 if (r == 0)
1457 break;
1458
1459 s = source;
1460 if (s[0] == '-') {
1461 ignore_enoent = true;
1462 s++;
1463 }
1464
1465 if (p && p[-1] == ':') {
4ec85141 1466 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
1467 if (r < 0)
1468 return log_error_errno(r, "Failed to parse argument: %m");
baaa35ad
ZJS
1469 if (r == 0)
1470 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1471 "Missing argument after ':': %s",
1472 eq);
d2d6c096
LP
1473
1474 d = destination;
1475
1476 if (p && p[-1] == ':') {
1477 _cleanup_free_ char *options = NULL;
1478
4ec85141 1479 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
d2d6c096
LP
1480 if (r < 0)
1481 return log_error_errno(r, "Failed to parse argument: %m");
1482
1483 if (isempty(options) || streq(options, "rbind"))
1484 flags = MS_REC;
1485 else if (streq(options, "norbind"))
1486 flags = 0;
baaa35ad
ZJS
1487 else
1488 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1489 "Unknown options: %s",
1490 eq);
d2d6c096
LP
1491 }
1492 } else
1493 d = s;
1494
d2d6c096
LP
1495 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1496 if (r < 0)
89ada3ba 1497 return bus_log_create_error(r);
d2d6c096
LP
1498 }
1499
1500 r = sd_bus_message_close_container(m);
1501 if (r < 0)
89ada3ba
YW
1502 return bus_log_create_error(r);
1503
1504 r = sd_bus_message_close_container(m);
1505 if (r < 0)
1506 return bus_log_create_error(r);
d2d6c096
LP
1507
1508 r = sd_bus_message_close_container(m);
89ada3ba
YW
1509 if (r < 0)
1510 return bus_log_create_error(r);
f6c66be1 1511
89ada3ba
YW
1512 return 1;
1513 }
f6c66be1 1514
784ad252
YW
1515 if (streq(field, "TemporaryFileSystem")) {
1516 const char *p = eq;
1517
1518 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1519 if (r < 0)
1520 return bus_log_create_error(r);
1521
1522 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1523 if (r < 0)
1524 return bus_log_create_error(r);
1525
1526 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1527 if (r < 0)
1528 return bus_log_create_error(r);
1529
1530 r = sd_bus_message_open_container(m, 'a', "(ss)");
1531 if (r < 0)
1532 return bus_log_create_error(r);
1533
1534 for (;;) {
1535 _cleanup_free_ char *word = NULL, *path = NULL;
1536 const char *w;
1537
4ec85141 1538 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
784ad252
YW
1539 if (r < 0)
1540 return log_error_errno(r, "Failed to parse argument: %m");
1541 if (r == 0)
1542 break;
1543
1544 w = word;
1545 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1546 if (r < 0)
1547 return log_error_errno(r, "Failed to parse argument: %m");
baaa35ad
ZJS
1548 if (r == 0)
1549 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1550 "Failed to parse argument: %s",
1551 p);
784ad252
YW
1552
1553 r = sd_bus_message_append(m, "(ss)", path, w);
1554 if (r < 0)
1555 return bus_log_create_error(r);
1556 }
1557
1558 r = sd_bus_message_close_container(m);
1559 if (r < 0)
1560 return bus_log_create_error(r);
1561
1562 r = sd_bus_message_close_container(m);
1563 if (r < 0)
1564 return bus_log_create_error(r);
1565
1566 r = sd_bus_message_close_container(m);
1567 if (r < 0)
1568 return bus_log_create_error(r);
1569
1570 return 1;
1571 }
1572
0389f4fa
LB
1573 if (streq(field, "RootHash")) {
1574 _cleanup_free_ void *roothash_decoded = NULL;
1575 size_t roothash_decoded_size = 0;
1576
1577 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1578 if (path_is_absolute(eq))
1579 return bus_append_string(m, "RootHashPath", eq);
1580
1581 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1582 r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
1583 if (r < 0)
1584 return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
1585 if (roothash_decoded_size < sizeof(sd_id128_t))
1586 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
1587
1588 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
1589 }
1590
d4d55b0d
LB
1591 if (streq(field, "RootHashSignature")) {
1592 _cleanup_free_ void *roothash_sig_decoded = NULL;
1593 char *value;
1594 size_t roothash_sig_decoded_size = 0;
1595
1596 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1597 if (path_is_absolute(eq))
1598 return bus_append_string(m, "RootHashSignaturePath", eq);
1599
1600 if (!(value = startswith(eq, "base64:")))
1601 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
1602
1603 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1604 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1605 if (r < 0)
1606 return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
1607
1608 return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
1609 }
1610
18d73705 1611 if (streq(field, "RootImageOptions")) {
bc8d56d3
LB
1612 _cleanup_strv_free_ char **l = NULL;
1613 char **first = NULL, **second = NULL;
18d73705
LB
1614 const char *p = eq;
1615
1616 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1617 if (r < 0)
1618 return bus_log_create_error(r);
1619
1620 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1621 if (r < 0)
1622 return bus_log_create_error(r);
1623
9ece6444 1624 r = sd_bus_message_open_container(m, 'v', "a(ss)");
18d73705
LB
1625 if (r < 0)
1626 return bus_log_create_error(r);
1627
9ece6444 1628 r = sd_bus_message_open_container(m, 'a', "(ss)");
18d73705
LB
1629 if (r < 0)
1630 return bus_log_create_error(r);
1631
bc8d56d3
LB
1632 r = strv_split_colon_pairs(&l, p);
1633 if (r < 0)
1634 return log_error_errno(r, "Failed to parse argument: %m");
18d73705 1635
bc8d56d3 1636 STRV_FOREACH_PAIR(first, second, l) {
9ece6444
LB
1637 /* Format is either 'root:foo' or 'foo' (root is implied) */
1638 if (!isempty(*second) && partition_designator_from_string(*first) < 0)
1639 return bus_log_create_error(-EINVAL);
18d73705 1640
9ece6444
LB
1641 r = sd_bus_message_append(m, "(ss)",
1642 !isempty(*second) ? *first : "root",
1643 !isempty(*second) ? *second : *first);
18d73705
LB
1644 if (r < 0)
1645 return bus_log_create_error(r);
1646 }
1647
1648 r = sd_bus_message_close_container(m);
1649 if (r < 0)
b3d13314
LB
1650 return bus_log_create_error(r);
1651
1652 r = sd_bus_message_close_container(m);
1653 if (r < 0)
1654 return bus_log_create_error(r);
1655
1656 r = sd_bus_message_close_container(m);
1657 if (r < 0)
1658 return bus_log_create_error(r);
1659
1660 return 1;
1661 }
1662
1663 if (streq(field, "MountImages")) {
b3d13314
LB
1664 const char *p = eq;
1665
1666 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1667 if (r < 0)
1668 return bus_log_create_error(r);
1669
1670 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1671 if (r < 0)
1672 return bus_log_create_error(r);
1673
427353f6 1674 r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
b3d13314
LB
1675 if (r < 0)
1676 return bus_log_create_error(r);
1677
427353f6 1678 r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
b3d13314
LB
1679 if (r < 0)
1680 return bus_log_create_error(r);
1681
427353f6
LB
1682 for (;;) {
1683 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
1684 const char *q = NULL, *source = NULL;
b3d13314
LB
1685 bool permissive = false;
1686
427353f6
LB
1687 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1688 if (r < 0)
1689 return r;
1690 if (r == 0)
1691 break;
1692
1693 q = tuple;
1694 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
1695 if (r < 0)
1696 return r;
1697 if (r == 0)
1698 continue;
1699
1700 source = first;
1701 if (source[0] == '-') {
b3d13314 1702 permissive = true;
427353f6 1703 source++;
b3d13314
LB
1704 }
1705
427353f6 1706 if (isempty(second))
b3d13314
LB
1707 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1708 "Missing argument after ':': %s",
1709 eq);
1710
427353f6
LB
1711 r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
1712 if (r < 0)
1713 return bus_log_create_error(r);
1714
1715 r = sd_bus_message_append(m, "ssb", source, second, permissive);
1716 if (r < 0)
1717 return bus_log_create_error(r);
1718
1719 r = sd_bus_message_open_container(m, 'a', "(ss)");
1720 if (r < 0)
1721 return bus_log_create_error(r);
1722
1723 for (;;) {
1724 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1725
1726 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
1727 if (r < 0)
1728 return r;
1729 if (r == 0)
1730 break;
1731 /* Single set of options, applying to the root partition/single filesystem */
1732 if (r == 1) {
1733 r = sd_bus_message_append(m, "(ss)", "root", partition);
1734 if (r < 0)
1735 return bus_log_create_error(r);
1736
1737 break;
1738 }
1739
1740 if (partition_designator_from_string(partition) < 0)
1741 return bus_log_create_error(-EINVAL);
1742
1743 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1744 if (r < 0)
1745 return bus_log_create_error(r);
1746 }
1747
1748 r = sd_bus_message_close_container(m);
1749 if (r < 0)
1750 return bus_log_create_error(r);
1751
1752 r = sd_bus_message_close_container(m);
b3d13314
LB
1753 if (r < 0)
1754 return bus_log_create_error(r);
1755 }
1756
1757 r = sd_bus_message_close_container(m);
1758 if (r < 0)
18d73705
LB
1759 return bus_log_create_error(r);
1760
1761 r = sd_bus_message_close_container(m);
1762 if (r < 0)
1763 return bus_log_create_error(r);
1764
1765 r = sd_bus_message_close_container(m);
1766 if (r < 0)
1767 return bus_log_create_error(r);
1768
1769 return 1;
1770 }
1771
89ada3ba
YW
1772 return 0;
1773}
f6c66be1 1774
89ada3ba 1775static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
89ada3ba 1776 if (streq(field, "KillMode"))
89ada3ba 1777 return bus_append_string(m, field, eq);
f6c66be1 1778
c57d2a76
ZJS
1779 if (STR_IN_SET(field, "SendSIGHUP",
1780 "SendSIGKILL"))
89ada3ba 1781 return bus_append_parse_boolean(m, field, eq);
f6c66be1 1782
c57d2a76
ZJS
1783 if (STR_IN_SET(field, "KillSignal",
1784 "RestartKillSignal",
1785 "FinalKillSignal",
1786 "WatchdogSignal"))
29a3db75 1787 return bus_append_signal_from_string(m, field, eq);
89ada3ba
YW
1788
1789 return 0;
1790}
1791
3d63c749 1792static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
89ada3ba 1793
c57d2a76
ZJS
1794 if (STR_IN_SET(field, "What",
1795 "Where",
1796 "Options",
1797 "Type"))
89ada3ba
YW
1798 return bus_append_string(m, field, eq);
1799
3d63c749 1800 if (streq(field, "TimeoutSec"))
3d63c749
YW
1801 return bus_append_parse_sec_rename(m, field, eq);
1802
1803 if (streq(field, "DirectoryMode"))
3d63c749
YW
1804 return bus_append_parse_mode(m, field, eq);
1805
c57d2a76
ZJS
1806 if (STR_IN_SET(field, "SloppyOptions",
1807 "LazyUnmount",
c600357b
MH
1808 "ForceUnmount",
1809 "ReadwriteOnly"))
3d63c749
YW
1810 return bus_append_parse_boolean(m, field, eq);
1811
1812 return 0;
1813}
1814
1815static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1816 int r;
1817
89ada3ba 1818 if (streq(field, "MakeDirectory"))
89ada3ba
YW
1819 return bus_append_parse_boolean(m, field, eq);
1820
1821 if (streq(field, "DirectoryMode"))
89ada3ba
YW
1822 return bus_append_parse_mode(m, field, eq);
1823
c57d2a76
ZJS
1824 if (STR_IN_SET(field, "PathExists",
1825 "PathExistsGlob",
1826 "PathChanged",
1827 "PathModified",
1828 "DirectoryNotEmpty")) {
3d63c749
YW
1829 if (isempty(eq))
1830 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1831 else
1832 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1833 if (r < 0)
1834 return bus_log_create_error(r);
1835
1836 return 1;
1837 }
1838
89ada3ba
YW
1839 return 0;
1840}
1841
5a70a68f 1842static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
9ed7de60 1843 if (streq(field, "RuntimeMaxSec"))
9ed7de60
PW
1844 return bus_append_parse_sec_rename(m, field, eq);
1845
5a70a68f 1846 if (streq(field, "TimeoutStopSec"))
5a70a68f
PW
1847 return bus_append_parse_sec_rename(m, field, eq);
1848
1849 return 0;
1850}
1851
89ada3ba 1852static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 1853 int r;
89ada3ba 1854
c57d2a76
ZJS
1855 if (STR_IN_SET(field, "PIDFile",
1856 "Type",
1857 "Restart",
1858 "BusName",
1859 "NotifyAccess",
1860 "USBFunctionDescriptors",
1861 "USBFunctionStrings",
bf760801
JK
1862 "OOMPolicy",
1863 "TimeoutStartFailureMode",
1864 "TimeoutStopFailureMode"))
89ada3ba 1865 return bus_append_string(m, field, eq);
f6c66be1 1866
c57d2a76
ZJS
1867 if (STR_IN_SET(field, "PermissionsStartOnly",
1868 "RootDirectoryStartOnly",
1869 "RemainAfterExit",
1870 "GuessMainPID"))
89ada3ba
YW
1871 return bus_append_parse_boolean(m, field, eq);
1872
c57d2a76
ZJS
1873 if (STR_IN_SET(field, "RestartSec",
1874 "TimeoutStartSec",
1875 "TimeoutStopSec",
e737017b 1876 "TimeoutAbortSec",
c57d2a76
ZJS
1877 "RuntimeMaxSec",
1878 "WatchdogSec"))
89ada3ba
YW
1879 return bus_append_parse_sec_rename(m, field, eq);
1880
3d63c749 1881 if (streq(field, "TimeoutSec")) {
3d63c749
YW
1882 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1883 if (r < 0)
1884 return r;
1885
1886 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1887 }
1888
89ada3ba 1889 if (streq(field, "FileDescriptorStoreMax"))
89ada3ba
YW
1890 return bus_append_safe_atou(m, field, eq);
1891
c57d2a76
ZJS
1892 if (STR_IN_SET(field, "ExecCondition",
1893 "ExecStartPre",
1894 "ExecStart",
1895 "ExecStartPost",
1896 "ExecConditionEx",
1897 "ExecStartPreEx",
1898 "ExecStartEx",
1899 "ExecStartPostEx",
1900 "ExecReload",
1901 "ExecStop",
1902 "ExecStopPost",
1903 "ExecReloadEx",
1904 "ExecStopEx",
1905 "ExecStopPostEx"))
89ada3ba
YW
1906 return bus_append_exec_command(m, field, eq);
1907
c57d2a76
ZJS
1908 if (STR_IN_SET(field, "RestartPreventExitStatus",
1909 "RestartForceExitStatus",
1910 "SuccessExitStatus")) {
3d63c749 1911 _cleanup_free_ int *status = NULL, *signal = NULL;
62b21e2e 1912 size_t n_status = 0, n_signal = 0;
3d63c749
YW
1913 const char *p;
1914
1915 for (p = eq;;) {
1916 _cleanup_free_ char *word = NULL;
3d63c749 1917
4ec85141 1918 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3d63c749
YW
1919 if (r == 0)
1920 break;
1921 if (r == -ENOMEM)
1922 return log_oom();
1923 if (r < 0)
1924 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1925
2e2ed880
ZJS
1926 /* We need to call exit_status_from_string() first, because we want
1927 * to parse numbers as exit statuses, not signals. */
3d63c749 1928
2e2ed880
ZJS
1929 r = exit_status_from_string(word);
1930 if (r >= 0) {
1931 assert(r >= 0 && r < 256);
3d63c749 1932
62b21e2e 1933 status = reallocarray(status, n_status + 1, sizeof(int));
3d63c749
YW
1934 if (!status)
1935 return log_oom();
1936
2e2ed880
ZJS
1937 status[n_status++] = r;
1938
1939 } else if ((r = signal_from_string(word)) >= 0) {
1940 signal = reallocarray(signal, n_signal + 1, sizeof(int));
1941 if (!signal)
1942 return log_oom();
1943
1944 signal[n_signal++] = r;
1945
1946 } else
1947 /* original r from exit_status_to_string() */
1948 return log_error_errno(r, "Invalid status or signal %s in %s: %m",
1949 word, field);
3d63c749
YW
1950 }
1951
1952 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1953 if (r < 0)
1954 return bus_log_create_error(r);
1955
1956 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1957 if (r < 0)
1958 return bus_log_create_error(r);
1959
1960 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1961 if (r < 0)
1962 return bus_log_create_error(r);
1963
1964 r = sd_bus_message_open_container(m, 'r', "aiai");
1965 if (r < 0)
1966 return bus_log_create_error(r);
1967
62b21e2e 1968 r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
3d63c749
YW
1969 if (r < 0)
1970 return bus_log_create_error(r);
1971
62b21e2e 1972 r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
3d63c749
YW
1973 if (r < 0)
1974 return bus_log_create_error(r);
1975
1976 r = sd_bus_message_close_container(m);
1977 if (r < 0)
1978 return bus_log_create_error(r);
1979
1980 r = sd_bus_message_close_container(m);
1981 if (r < 0)
1982 return bus_log_create_error(r);
1983
1984 r = sd_bus_message_close_container(m);
1985 if (r < 0)
1986 return bus_log_create_error(r);
1987
1988 return 1;
1989 }
1990
89ada3ba
YW
1991 return 0;
1992}
1993
1994static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1995 int r;
1996
c57d2a76 1997 if (STR_IN_SET(field, "Accept",
3e5f04bf 1998 "FlushPending",
c57d2a76
ZJS
1999 "Writable",
2000 "KeepAlive",
2001 "NoDelay",
2002 "FreeBind",
2003 "Transparent",
2004 "Broadcast",
2005 "PassCredentials",
2006 "PassSecurity",
a3d19f5d 2007 "PassPacketInfo",
c57d2a76
ZJS
2008 "ReusePort",
2009 "RemoveOnStop",
2010 "SELinuxContextFromNet"))
89ada3ba
YW
2011 return bus_append_parse_boolean(m, field, eq);
2012
c57d2a76
ZJS
2013 if (STR_IN_SET(field, "Priority",
2014 "IPTTL",
2015 "Mark"))
89ada3ba
YW
2016 return bus_append_safe_atoi(m, field, eq);
2017
2018 if (streq(field, "IPTOS"))
89ada3ba
YW
2019 return bus_append_ip_tos_from_string(m, field, eq);
2020
c57d2a76
ZJS
2021 if (STR_IN_SET(field, "Backlog",
2022 "MaxConnections",
2023 "MaxConnectionsPerSource",
2024 "KeepAliveProbes",
2025 "TriggerLimitBurst"))
89ada3ba
YW
2026 return bus_append_safe_atou(m, field, eq);
2027
c57d2a76
ZJS
2028 if (STR_IN_SET(field, "SocketMode",
2029 "DirectoryMode"))
89ada3ba
YW
2030 return bus_append_parse_mode(m, field, eq);
2031
c57d2a76
ZJS
2032 if (STR_IN_SET(field, "MessageQueueMaxMessages",
2033 "MessageQueueMessageSize"))
89ada3ba
YW
2034 return bus_append_safe_atoi64(m, field, eq);
2035
c57d2a76
ZJS
2036 if (STR_IN_SET(field, "TimeoutSec",
2037 "KeepAliveTimeSec",
2038 "KeepAliveIntervalSec",
2039 "DeferAcceptSec",
2040 "TriggerLimitIntervalSec"))
89ada3ba
YW
2041 return bus_append_parse_sec_rename(m, field, eq);
2042
c57d2a76
ZJS
2043 if (STR_IN_SET(field, "ReceiveBuffer",
2044 "SendBuffer",
2045 "PipeSize"))
b48e508d 2046 return bus_append_parse_size(m, field, eq, 1024);
89ada3ba 2047
c57d2a76
ZJS
2048 if (STR_IN_SET(field, "ExecStartPre",
2049 "ExecStartPost",
2050 "ExecReload",
2051 "ExecStopPost"))
89ada3ba
YW
2052 return bus_append_exec_command(m, field, eq);
2053
c57d2a76
ZJS
2054 if (STR_IN_SET(field, "SmackLabel",
2055 "SmackLabelIPIn",
2056 "SmackLabelIPOut",
2057 "TCPCongestion",
2058 "BindToDevice",
2059 "BindIPv6Only",
2060 "FileDescriptorName",
2061 "SocketUser",
9b191525
LP
2062 "SocketGroup",
2063 "Timestamping"))
89ada3ba
YW
2064 return bus_append_string(m, field, eq);
2065
2066 if (streq(field, "Symlinks"))
4ec85141 2067 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
89ada3ba 2068
e045e325 2069 if (streq(field, "SocketProtocol"))
d2b42d63 2070 return bus_append_parse_ip_protocol(m, field, eq);
89ada3ba 2071
c57d2a76
ZJS
2072 if (STR_IN_SET(field, "ListenStream",
2073 "ListenDatagram",
2074 "ListenSequentialPacket",
2075 "ListenNetlink",
2076 "ListenSpecial",
2077 "ListenMessageQueue",
2078 "ListenFIFO",
2079 "ListenUSBFunction")) {
3d63c749
YW
2080 if (isempty(eq))
2081 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
2082 else
81b1dc27 2083 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
f6c66be1 2084 if (r < 0)
89ada3ba
YW
2085 return bus_log_create_error(r);
2086
2087 return 1;
2088 }
2089
2090 return 0;
2091}
2092static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 2093 int r;
89ada3ba 2094
c57d2a76
ZJS
2095 if (STR_IN_SET(field, "WakeSystem",
2096 "RemainAfterElapse",
2097 "Persistent",
2098 "OnTimezoneChange",
acf24a1a
KG
2099 "OnClockChange",
2100 "FixedRandomDelay"))
89ada3ba
YW
2101 return bus_append_parse_boolean(m, field, eq);
2102
c57d2a76
ZJS
2103 if (STR_IN_SET(field, "AccuracySec",
2104 "RandomizedDelaySec"))
3d63c749
YW
2105 return bus_append_parse_sec_rename(m, field, eq);
2106
c57d2a76
ZJS
2107 if (STR_IN_SET(field, "OnActiveSec",
2108 "OnBootSec",
2109 "OnStartupSec",
2110 "OnUnitActiveSec",
2111 "OnUnitInactiveSec")) {
3d63c749
YW
2112 if (isempty(eq))
2113 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
2114 else {
2115 usec_t t;
2116 r = parse_sec(eq, &t);
2117 if (r < 0)
2118 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
89ada3ba 2119
3d63c749
YW
2120 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
2121 }
2122 if (r < 0)
2123 return bus_log_create_error(r);
89ada3ba 2124
3d63c749
YW
2125 return 1;
2126 }
2127
2128 if (streq(field, "OnCalendar")) {
3d63c749
YW
2129 if (isempty(eq))
2130 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
2131 else
2132 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
2133 if (r < 0)
2134 return bus_log_create_error(r);
2135
2136 return 1;
2137 }
89ada3ba
YW
2138
2139 return 0;
2140}
2141
2142static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749
YW
2143 ConditionType t = _CONDITION_TYPE_INVALID;
2144 bool is_condition = false;
2145 int r;
f6c66be1 2146
c57d2a76
ZJS
2147 if (STR_IN_SET(field, "Description",
2148 "SourcePath",
2149 "OnFailureJobMode",
2150 "JobTimeoutAction",
2151 "JobTimeoutRebootArgument",
2152 "StartLimitAction",
2153 "FailureAction",
2154 "SuccessAction",
2155 "RebootArgument",
2156 "CollectMode"))
89ada3ba
YW
2157 return bus_append_string(m, field, eq);
2158
c57d2a76
ZJS
2159 if (STR_IN_SET(field, "StopWhenUnneeded",
2160 "RefuseManualStart",
2161 "RefuseManualStop",
2162 "AllowIsolate",
2163 "IgnoreOnIsolate",
2164 "DefaultDependencies"))
89ada3ba
YW
2165 return bus_append_parse_boolean(m, field, eq);
2166
c57d2a76
ZJS
2167 if (STR_IN_SET(field, "JobTimeoutSec",
2168 "JobRunningTimeoutSec",
2169 "StartLimitIntervalSec"))
3d63c749
YW
2170 return bus_append_parse_sec_rename(m, field, eq);
2171
2172 if (streq(field, "StartLimitBurst"))
3d63c749
YW
2173 return bus_append_safe_atou(m, field, eq);
2174
c57d2a76
ZJS
2175 if (STR_IN_SET(field, "SuccessActionExitStatus",
2176 "FailureActionExitStatus")) {
7af67e9a
LP
2177 if (isempty(eq))
2178 r = sd_bus_message_append(m, "(sv)", field, "i", -1);
2179 else {
2180 uint8_t u;
2181
2182 r = safe_atou8(eq, &u);
2183 if (r < 0)
2184 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
2185
2186 r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
2187 }
2188 if (r < 0)
2189 return bus_log_create_error(r);
2190
2191 return 1;
2192 }
2193
3d63c749 2194 if (unit_dependency_from_string(field) >= 0 ||
c57d2a76 2195 STR_IN_SET(field, "Documentation",
0dd3c090
ZJS
2196 "RequiresMountsFor",
2197 "Markers"))
4ec85141 2198 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
89ada3ba 2199
3d63c749
YW
2200 t = condition_type_from_string(field);
2201 if (t >= 0)
2202 is_condition = true;
2203 else
2204 t = assert_type_from_string(field);
2205 if (t >= 0) {
2206 if (isempty(eq))
2207 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
2208 else {
2209 const char *p = eq;
2210 int trigger, negate;
2211
2212 trigger = *p == '|';
2213 if (trigger)
2214 p++;
2215
2216 negate = *p == '!';
2217 if (negate)
2218 p++;
2219
2220 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
2221 field, trigger, negate, p);
2222 }
2223 if (r < 0)
2224 return bus_log_create_error(r);
2225
2226 return 1;
2227 }
2228
89ada3ba
YW
2229 return 0;
2230}
2231
2232int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
2233 const char *eq, *field;
2234 int r;
2235
2236 assert(m);
2237 assert(assignment);
2238
2239 eq = strchr(assignment, '=');
baaa35ad
ZJS
2240 if (!eq)
2241 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2242 "Not an assignment: %s", assignment);
89ada3ba
YW
2243
2244 field = strndupa(assignment, eq - assignment);
2245 eq++;
2246
2247 switch (t) {
2248 case UNIT_SERVICE:
2249 r = bus_append_cgroup_property(m, field, eq);
2250 if (r != 0)
f6c66be1
LP
2251 return r;
2252
89ada3ba
YW
2253 r = bus_append_execute_property(m, field, eq);
2254 if (r != 0)
2255 return r;
f6c66be1 2256
89ada3ba
YW
2257 r = bus_append_kill_property(m, field, eq);
2258 if (r != 0)
2259 return r;
f6c66be1 2260
89ada3ba
YW
2261 r = bus_append_service_property(m, field, eq);
2262 if (r != 0)
2263 return r;
2264 break;
f6c66be1 2265
89ada3ba
YW
2266 case UNIT_SOCKET:
2267 r = bus_append_cgroup_property(m, field, eq);
2268 if (r != 0)
2269 return r;
f6c66be1 2270
89ada3ba
YW
2271 r = bus_append_execute_property(m, field, eq);
2272 if (r != 0)
2273 return r;
f6c66be1 2274
89ada3ba
YW
2275 r = bus_append_kill_property(m, field, eq);
2276 if (r != 0)
2277 return r;
f6c66be1 2278
89ada3ba
YW
2279 r = bus_append_socket_property(m, field, eq);
2280 if (r != 0)
f6c66be1 2281 return r;
89ada3ba 2282 break;
f6c66be1 2283
89ada3ba
YW
2284 case UNIT_TIMER:
2285 r = bus_append_timer_property(m, field, eq);
2286 if (r != 0)
2287 return r;
2288 break;
f6c66be1 2289
89ada3ba
YW
2290 case UNIT_PATH:
2291 r = bus_append_path_property(m, field, eq);
2292 if (r != 0)
2293 return r;
2294 break;
268833ed 2295
89ada3ba
YW
2296 case UNIT_SLICE:
2297 r = bus_append_cgroup_property(m, field, eq);
2298 if (r != 0)
2299 return r;
2300 break;
268833ed 2301
89ada3ba
YW
2302 case UNIT_SCOPE:
2303 r = bus_append_cgroup_property(m, field, eq);
2304 if (r != 0)
2305 return r;
268833ed 2306
89ada3ba 2307 r = bus_append_kill_property(m, field, eq);
5a70a68f
PW
2308 if (r != 0)
2309 return r;
2310
2311 r = bus_append_scope_property(m, field, eq);
89ada3ba
YW
2312 if (r != 0)
2313 return r;
2314 break;
535e0d19 2315
89ada3ba 2316 case UNIT_MOUNT:
3d63c749
YW
2317 r = bus_append_cgroup_property(m, field, eq);
2318 if (r != 0)
2319 return r;
2320
2321 r = bus_append_execute_property(m, field, eq);
2322 if (r != 0)
2323 return r;
2324
2325 r = bus_append_kill_property(m, field, eq);
2326 if (r != 0)
2327 return r;
2328
2329 r = bus_append_mount_property(m, field, eq);
2330 if (r != 0)
2331 return r;
2332
2333 break;
2334
89ada3ba 2335 case UNIT_AUTOMOUNT:
3d63c749
YW
2336 r = bus_append_automount_property(m, field, eq);
2337 if (r != 0)
2338 return r;
2339
89ada3ba 2340 break;
535e0d19 2341
89ada3ba
YW
2342 case UNIT_TARGET:
2343 case UNIT_DEVICE:
2344 case UNIT_SWAP:
baaa35ad
ZJS
2345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2346 "Not supported unit type");
535e0d19 2347
89ada3ba 2348 default:
baaa35ad
ZJS
2349 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2350 "Invalid unit type");
20b16441
LP
2351 }
2352
89ada3ba
YW
2353 r = bus_append_unit_property(m, field, eq);
2354 if (r != 0)
2355 return r;
20b16441 2356
baaa35ad
ZJS
2357 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2358 "Unknown assignment: %s", assignment);
20b16441
LP
2359}
2360
89ada3ba 2361int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
8673cf13
LP
2362 char **i;
2363 int r;
2364
2365 assert(m);
2366
2367 STRV_FOREACH(i, l) {
89ada3ba 2368 r = bus_append_unit_property_assignment(m, t, *i);
8673cf13
LP
2369 if (r < 0)
2370 return r;
2371 }
2372
2373 return 0;
2374}
2375
da6053d0 2376int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
20b16441
LP
2377 const char *type, *path, *source;
2378 int r;
2379
acc0269c
CH
2380 /* changes is dereferenced when calling unit_file_dump_changes() later,
2381 * so we have to make sure this is not NULL. */
2382 assert(changes);
2383 assert(n_changes);
2384
20b16441
LP
2385 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2386 if (r < 0)
2387 return bus_log_parse_error(r);
2388
2389 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2390 /* We expect only "success" changes to be sent over the bus.
2391 Hence, reject anything negative. */
93419a96 2392 int ch = unit_file_change_type_from_string(type);
20b16441 2393 if (ch < 0) {
c1b48a7f
BR
2394 log_notice_errno(ch, "Manager reported unknown change type \"%s\" for path \"%s\", ignoring.",
2395 type, path);
20b16441
LP
2396 continue;
2397 }
2398
2399 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2400 if (r < 0)
2401 return r;
2402 }
2403 if (r < 0)
2404 return bus_log_parse_error(r);
2405
2406 r = sd_bus_message_exit_container(m);
2407 if (r < 0)
2408 return bus_log_parse_error(r);
2409
35d379b2 2410 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
20b16441
LP
2411 return 0;
2412}
2413
bd062910
ZJS
2414int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2415 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2416 _cleanup_free_ char *path = NULL;
2417 int r;
2418
2419 path = unit_dbus_path_from_name(name);
2420 if (!path)
2421 return log_oom();
2422
2423 /* This function warns on it's own, because otherwise it'd be awkward to pass
2424 * the dbus error message around. */
2425
2426 r = sd_bus_get_property_string(
2427 bus,
2428 "org.freedesktop.systemd1",
2429 path,
2430 "org.freedesktop.systemd1.Unit",
2431 "LoadState",
2432 &error,
2433 load_state);
2434 if (r < 0)
2435 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2436
2437 return 0;
2438}
4dcc0653
LP
2439
2440int unit_info_compare(const UnitInfo *a, const UnitInfo *b) {
2441 int r;
2442
2443 /* First, order by machine */
2444 r = strcasecmp_ptr(a->machine, b->machine);
2445 if (r != 0)
2446 return r;
2447
2448 /* Second, order by unit type */
2449 r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
2450 if (r != 0)
2451 return r;
2452
2453 /* Third, order by name */
2454 return strcasecmp(a->id, b->id);
2455}