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