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