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