]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
dbus-execute: generate the correct transient unit setting
[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"
291d565a
LP
18#include "list.h"
19#include "locale-util.h"
83555251 20#include "mount-util.h"
add00535 21#include "nsflags.h"
291d565a
LP
22#include "parse-util.h"
23#include "path-util.h"
24#include "process-util.h"
25#include "rlimit-util.h"
cffaed83 26#include "securebits-util.h"
291d565a 27#include "signal-util.h"
e045e325 28#include "socket-protocol-list.h"
291d565a
LP
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);
0cf6628e
YW
106DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name);
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);
876 } else
877 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
7f452159 878
89ada3ba
YW
879 if (r < 0)
880 return bus_log_create_error(r);
7f452159 881
89ada3ba
YW
882 return 1;
883 }
7f452159 884
89ada3ba
YW
885 if (streq(field, "StandardInputText")) {
886 _cleanup_free_ char *unescaped = NULL;
7f452159 887
89ada3ba 888 r = cunescape(eq, 0, &unescaped);
7f452159 889 if (r < 0)
89ada3ba 890 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
7f452159 891
89ada3ba
YW
892 if (!strextend(&unescaped, "\n", NULL))
893 return log_oom();
7f452159 894
89ada3ba
YW
895 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
896 * interface anyway */
20b16441 897
89ada3ba
YW
898 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
899 }
20b16441 900
89ada3ba
YW
901 if (streq(field, "StandardInputData")) {
902 _cleanup_free_ void *decoded = NULL;
903 size_t sz;
904
905 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
20b16441 906 if (r < 0)
89ada3ba 907 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
20b16441 908
89ada3ba
YW
909 return bus_append_byte_array(m, field, decoded, sz);
910 }
20b16441 911
6550c24c
LP
912 if ((suffix = startswith(field, "Limit"))) {
913 int rl;
20b16441 914
6550c24c
LP
915 rl = rlimit_from_string(suffix);
916 if (rl >= 0) {
917 const char *sn;
918 struct rlimit l;
20b16441 919
6550c24c
LP
920 r = rlimit_parse(rl, eq, &l);
921 if (r < 0)
922 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
20b16441 923
6550c24c
LP
924 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
925 if (r < 0)
926 return bus_log_create_error(r);
20b16441 927
6550c24c
LP
928 sn = strjoina(field, "Soft");
929 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
930 if (r < 0)
931 return bus_log_create_error(r);
932
933 return 1;
934 }
89ada3ba 935 }
20b16441 936
89ada3ba
YW
937 if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
938 int ignore = 0;
939 const char *s = eq;
20b16441 940
89ada3ba
YW
941 if (eq[0] == '-') {
942 ignore = 1;
943 s = eq + 1;
20b16441
LP
944 }
945
89ada3ba 946 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
6bbfdc67 947 if (r < 0)
89ada3ba 948 return bus_log_create_error(r);
20b16441 949
89ada3ba
YW
950 return 1;
951 }
20b16441 952
89ada3ba
YW
953 if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
954 uint64_t sum = 0;
955 bool invert = false;
956 const char *p = eq;
afcb1cd3 957
89ada3ba
YW
958 if (*p == '~') {
959 invert = true;
960 p++;
961 }
20b16441 962
89ada3ba 963 r = capability_set_from_string(p, &sum);
20b16441 964 if (r < 0)
89ada3ba 965 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441 966
89ada3ba
YW
967 sum = invert ? ~sum : sum;
968
969 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
20b16441
LP
970 if (r < 0)
971 return bus_log_create_error(r);
972
89ada3ba
YW
973 return 1;
974 }
20b16441 975
89ada3ba
YW
976 if (streq(field, "CPUAffinity")) {
977 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
20b16441 978
89ada3ba
YW
979 r = parse_cpu_set(eq, &cpuset);
980 if (r < 0)
981 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
20b16441 982
89ada3ba
YW
983 return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
984 }
20b7a007 985
89ada3ba
YW
986 if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
987 int whitelist = 1;
988 const char *p = eq;
20b16441 989
89ada3ba
YW
990 if (*p == '~') {
991 whitelist = 0;
992 p++;
20b16441
LP
993 }
994
89ada3ba 995 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
20b16441
LP
996 if (r < 0)
997 return bus_log_create_error(r);
998
89ada3ba
YW
999 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1000 if (r < 0)
1001 return bus_log_create_error(r);
1002
1003 r = sd_bus_message_open_container(m, 'v', "(bas)");
1004 if (r < 0)
1005 return bus_log_create_error(r);
20b16441 1006
89ada3ba
YW
1007 r = sd_bus_message_open_container(m, 'r', "bas");
1008 if (r < 0)
1009 return bus_log_create_error(r);
cffaed83 1010
89ada3ba 1011 r = sd_bus_message_append_basic(m, 'b', &whitelist);
cffaed83
YW
1012 if (r < 0)
1013 return bus_log_create_error(r);
1014
1015 r = sd_bus_message_open_container(m, 'a', "s");
1016 if (r < 0)
1017 return bus_log_create_error(r);
1018
98008caa 1019 for (;;) {
cffaed83
YW
1020 _cleanup_free_ char *word = NULL;
1021
1022 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
cffaed83
YW
1023 if (r == 0)
1024 break;
89ada3ba
YW
1025 if (r == -ENOMEM)
1026 return log_oom();
1027 if (r < 0)
1028 return log_error_errno(r, "Invalid syntax: %s", eq);
cffaed83
YW
1029
1030 r = sd_bus_message_append_basic(m, 's', word);
1031 if (r < 0)
1032 return bus_log_create_error(r);
1033 }
1034
1035 r = sd_bus_message_close_container(m);
1036 if (r < 0)
1037 return bus_log_create_error(r);
1038
1039 r = sd_bus_message_close_container(m);
20b16441
LP
1040 if (r < 0)
1041 return bus_log_create_error(r);
1042
89ada3ba 1043 r = sd_bus_message_close_container(m);
20b16441
LP
1044 if (r < 0)
1045 return bus_log_create_error(r);
1046
20b16441
LP
1047 r = sd_bus_message_close_container(m);
1048 if (r < 0)
1049 return bus_log_create_error(r);
1050
89ada3ba
YW
1051 return 1;
1052 }
20b16441 1053
89ada3ba 1054 if (streq(field, "RestrictNamespaces")) {
add00535 1055 bool invert = false;
aa9d574d 1056 unsigned long flags;
add00535
LP
1057
1058 r = parse_boolean(eq);
1059 if (r > 0)
1060 flags = 0;
1061 else if (r == 0)
1062 flags = NAMESPACE_FLAGS_ALL;
1063 else {
aa9d574d
YW
1064 if (eq[0] == '~') {
1065 invert = true;
1066 eq++;
1067 }
1068
86c2a9f1 1069 r = namespace_flags_from_string(eq, &flags);
add00535
LP
1070 if (r < 0)
1071 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1072 }
1073
1074 if (invert)
1075 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1076
89ada3ba
YW
1077 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1078 if (r < 0)
1079 return bus_log_create_error(r);
afcb1cd3 1080
89ada3ba
YW
1081 return 1;
1082 }
afcb1cd3 1083
89ada3ba
YW
1084 if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1085 const char *p = eq;
83555251 1086
89ada3ba 1087 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
c7383828 1088 if (r < 0)
89ada3ba 1089 return bus_log_create_error(r);
afcb1cd3 1090
89ada3ba
YW
1091 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1092 if (r < 0)
1093 return bus_log_create_error(r);
d2d6c096
LP
1094
1095 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1096 if (r < 0)
89ada3ba 1097 return bus_log_create_error(r);
d2d6c096
LP
1098
1099 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1100 if (r < 0)
89ada3ba 1101 return bus_log_create_error(r);
d2d6c096
LP
1102
1103 for (;;) {
1104 _cleanup_free_ char *source = NULL, *destination = NULL;
1105 char *s = NULL, *d = NULL;
1106 bool ignore_enoent = false;
1107 uint64_t flags = MS_REC;
1108
1109 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1110 if (r < 0)
1111 return log_error_errno(r, "Failed to parse argument: %m");
1112 if (r == 0)
1113 break;
1114
1115 s = source;
1116 if (s[0] == '-') {
1117 ignore_enoent = true;
1118 s++;
1119 }
1120
1121 if (p && p[-1] == ':') {
1122 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1123 if (r < 0)
1124 return log_error_errno(r, "Failed to parse argument: %m");
baaa35ad
ZJS
1125 if (r == 0)
1126 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1127 "Missing argument after ':': %s",
1128 eq);
d2d6c096
LP
1129
1130 d = destination;
1131
1132 if (p && p[-1] == ':') {
1133 _cleanup_free_ char *options = NULL;
1134
1135 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1136 if (r < 0)
1137 return log_error_errno(r, "Failed to parse argument: %m");
1138
1139 if (isempty(options) || streq(options, "rbind"))
1140 flags = MS_REC;
1141 else if (streq(options, "norbind"))
1142 flags = 0;
baaa35ad
ZJS
1143 else
1144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1145 "Unknown options: %s",
1146 eq);
d2d6c096
LP
1147 }
1148 } else
1149 d = s;
1150
d2d6c096
LP
1151 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1152 if (r < 0)
89ada3ba 1153 return bus_log_create_error(r);
d2d6c096
LP
1154 }
1155
1156 r = sd_bus_message_close_container(m);
1157 if (r < 0)
89ada3ba
YW
1158 return bus_log_create_error(r);
1159
1160 r = sd_bus_message_close_container(m);
1161 if (r < 0)
1162 return bus_log_create_error(r);
d2d6c096
LP
1163
1164 r = sd_bus_message_close_container(m);
89ada3ba
YW
1165 if (r < 0)
1166 return bus_log_create_error(r);
f6c66be1 1167
89ada3ba
YW
1168 return 1;
1169 }
f6c66be1 1170
784ad252
YW
1171 if (streq(field, "TemporaryFileSystem")) {
1172 const char *p = eq;
1173
1174 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1175 if (r < 0)
1176 return bus_log_create_error(r);
1177
1178 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1179 if (r < 0)
1180 return bus_log_create_error(r);
1181
1182 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1183 if (r < 0)
1184 return bus_log_create_error(r);
1185
1186 r = sd_bus_message_open_container(m, 'a', "(ss)");
1187 if (r < 0)
1188 return bus_log_create_error(r);
1189
1190 for (;;) {
1191 _cleanup_free_ char *word = NULL, *path = NULL;
1192 const char *w;
1193
1194 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1195 if (r < 0)
1196 return log_error_errno(r, "Failed to parse argument: %m");
1197 if (r == 0)
1198 break;
1199
1200 w = word;
1201 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1202 if (r < 0)
1203 return log_error_errno(r, "Failed to parse argument: %m");
baaa35ad
ZJS
1204 if (r == 0)
1205 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1206 "Failed to parse argument: %s",
1207 p);
784ad252
YW
1208
1209 r = sd_bus_message_append(m, "(ss)", path, w);
1210 if (r < 0)
1211 return bus_log_create_error(r);
1212 }
1213
1214 r = sd_bus_message_close_container(m);
1215 if (r < 0)
1216 return bus_log_create_error(r);
1217
1218 r = sd_bus_message_close_container(m);
1219 if (r < 0)
1220 return bus_log_create_error(r);
1221
1222 r = sd_bus_message_close_container(m);
1223 if (r < 0)
1224 return bus_log_create_error(r);
1225
1226 return 1;
1227 }
1228
89ada3ba
YW
1229 return 0;
1230}
f6c66be1 1231
89ada3ba 1232static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
f6c66be1 1233
89ada3ba 1234 if (streq(field, "KillMode"))
f6c66be1 1235
89ada3ba 1236 return bus_append_string(m, field, eq);
f6c66be1 1237
89ada3ba 1238 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
f6c66be1 1239
89ada3ba 1240 return bus_append_parse_boolean(m, field, eq);
f6c66be1 1241
c87700a1 1242 if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
89ada3ba 1243
29a3db75 1244 return bus_append_signal_from_string(m, field, eq);
89ada3ba
YW
1245
1246 return 0;
1247}
1248
3d63c749 1249static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
89ada3ba 1250
3d63c749 1251 if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
89ada3ba
YW
1252
1253 return bus_append_string(m, field, eq);
1254
3d63c749
YW
1255 if (streq(field, "TimeoutSec"))
1256
1257 return bus_append_parse_sec_rename(m, field, eq);
1258
1259 if (streq(field, "DirectoryMode"))
1260
1261 return bus_append_parse_mode(m, field, eq);
1262
1263 if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1264
1265 return bus_append_parse_boolean(m, field, eq);
1266
1267 return 0;
1268}
1269
1270static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1271 int r;
1272
89ada3ba
YW
1273 if (streq(field, "MakeDirectory"))
1274
1275 return bus_append_parse_boolean(m, field, eq);
1276
1277 if (streq(field, "DirectoryMode"))
1278
1279 return bus_append_parse_mode(m, field, eq);
1280
3d63c749
YW
1281 if (STR_IN_SET(field,
1282 "PathExists", "PathExistsGlob", "PathChanged",
1283 "PathModified", "DirectoryNotEmpty")) {
1284
1285 if (isempty(eq))
1286 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1287 else
1288 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1289 if (r < 0)
1290 return bus_log_create_error(r);
1291
1292 return 1;
1293 }
1294
89ada3ba
YW
1295 return 0;
1296}
1297
1298static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 1299 int r;
89ada3ba 1300
3d63c749
YW
1301 if (STR_IN_SET(field,
1302 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1303 "USBFunctionDescriptors", "USBFunctionStrings"))
89ada3ba
YW
1304
1305 return bus_append_string(m, field, eq);
f6c66be1 1306
3d63c749 1307 if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
89ada3ba
YW
1308
1309 return bus_append_parse_boolean(m, field, eq);
1310
3d63c749 1311 if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
89ada3ba
YW
1312
1313 return bus_append_parse_sec_rename(m, field, eq);
1314
3d63c749
YW
1315 if (streq(field, "TimeoutSec")) {
1316
1317 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1318 if (r < 0)
1319 return r;
1320
1321 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1322 }
1323
89ada3ba
YW
1324 if (streq(field, "FileDescriptorStoreMax"))
1325
1326 return bus_append_safe_atou(m, field, eq);
1327
1328 if (STR_IN_SET(field,
1329 "ExecStartPre", "ExecStart", "ExecStartPost",
1330 "ExecReload", "ExecStop", "ExecStopPost"))
1331
1332 return bus_append_exec_command(m, field, eq);
1333
3d63c749
YW
1334 if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1335 _cleanup_free_ int *status = NULL, *signal = NULL;
1336 size_t sz_status = 0, sz_signal = 0;
1337 const char *p;
1338
1339 for (p = eq;;) {
1340 _cleanup_free_ char *word = NULL;
1341 int val;
1342
1343 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1344 if (r == 0)
1345 break;
1346 if (r == -ENOMEM)
1347 return log_oom();
1348 if (r < 0)
1349 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1350
1351 r = safe_atoi(word, &val);
1352 if (r < 0) {
29a3db75 1353 val = signal_from_string(word);
3d63c749
YW
1354 if (val < 0)
1355 return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
1356
aa484f35 1357 signal = reallocarray(signal, sz_signal + 1, sizeof(int));
3d63c749
YW
1358 if (!signal)
1359 return log_oom();
1360
1361 signal[sz_signal++] = val;
1362 } else {
aa484f35 1363 status = reallocarray(status, sz_status + 1, sizeof(int));
3d63c749
YW
1364 if (!status)
1365 return log_oom();
1366
1367 status[sz_status++] = val;
1368 }
1369 }
1370
1371 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1372 if (r < 0)
1373 return bus_log_create_error(r);
1374
1375 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1376 if (r < 0)
1377 return bus_log_create_error(r);
1378
1379 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1380 if (r < 0)
1381 return bus_log_create_error(r);
1382
1383 r = sd_bus_message_open_container(m, 'r', "aiai");
1384 if (r < 0)
1385 return bus_log_create_error(r);
1386
1387 r = sd_bus_message_append_array(m, 'i', status, sz_status);
1388 if (r < 0)
1389 return bus_log_create_error(r);
1390
1391 r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
1392 if (r < 0)
1393 return bus_log_create_error(r);
1394
1395 r = sd_bus_message_close_container(m);
1396 if (r < 0)
1397 return bus_log_create_error(r);
1398
1399 r = sd_bus_message_close_container(m);
1400 if (r < 0)
1401 return bus_log_create_error(r);
1402
1403 r = sd_bus_message_close_container(m);
1404 if (r < 0)
1405 return bus_log_create_error(r);
1406
1407 return 1;
1408 }
1409
89ada3ba
YW
1410 return 0;
1411}
1412
1413static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1414 int r;
1415
1416 if (STR_IN_SET(field,
1417 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1418 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1419
1420 return bus_append_parse_boolean(m, field, eq);
1421
1422 if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
1423
1424 return bus_append_safe_atoi(m, field, eq);
1425
1426 if (streq(field, "IPTOS"))
1427
1428 return bus_append_ip_tos_from_string(m, field, eq);
1429
1430 if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1431
1432 return bus_append_safe_atou(m, field, eq);
1433
1434 if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
1435
1436 return bus_append_parse_mode(m, field, eq);
1437
1438 if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1439
1440 return bus_append_safe_atoi64(m, field, eq);
1441
1442 if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1443
1444 return bus_append_parse_sec_rename(m, field, eq);
1445
1446 if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1447
b48e508d 1448 return bus_append_parse_size(m, field, eq, 1024);
89ada3ba
YW
1449
1450 if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1451
1452 return bus_append_exec_command(m, field, eq);
1453
1454 if (STR_IN_SET(field,
1455 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1456 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1457 "SocketUser", "SocketGroup"))
1458
1459 return bus_append_string(m, field, eq);
1460
1461 if (streq(field, "Symlinks"))
1462
1463 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1464
e045e325 1465 if (streq(field, "SocketProtocol"))
89ada3ba 1466
e045e325 1467 return bus_append_socket_protocol_from_name(m, field, eq);
89ada3ba
YW
1468
1469 if (STR_IN_SET(field,
1470 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1471 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1472
3d63c749
YW
1473 if (isempty(eq))
1474 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1475 else
81b1dc27 1476 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
f6c66be1 1477 if (r < 0)
89ada3ba
YW
1478 return bus_log_create_error(r);
1479
1480 return 1;
1481 }
1482
1483 return 0;
1484}
1485static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 1486 int r;
89ada3ba
YW
1487
1488 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
1489
1490 return bus_append_parse_boolean(m, field, eq);
1491
3d63c749
YW
1492 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1493
1494 return bus_append_parse_sec_rename(m, field, eq);
1495
89ada3ba
YW
1496 if (STR_IN_SET(field,
1497 "OnActiveSec", "OnBootSec", "OnStartupSec",
3d63c749 1498 "OnUnitActiveSec","OnUnitInactiveSec")) {
89ada3ba 1499
3d63c749
YW
1500 if (isempty(eq))
1501 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
1502 else {
1503 usec_t t;
1504 r = parse_sec(eq, &t);
1505 if (r < 0)
1506 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
89ada3ba 1507
3d63c749
YW
1508 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
1509 }
1510 if (r < 0)
1511 return bus_log_create_error(r);
89ada3ba 1512
3d63c749
YW
1513 return 1;
1514 }
1515
1516 if (streq(field, "OnCalendar")) {
1517
1518 if (isempty(eq))
1519 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
1520 else
1521 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
1522 if (r < 0)
1523 return bus_log_create_error(r);
1524
1525 return 1;
1526 }
89ada3ba
YW
1527
1528 return 0;
1529}
1530
1531static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749
YW
1532 ConditionType t = _CONDITION_TYPE_INVALID;
1533 bool is_condition = false;
1534 int r;
f6c66be1 1535
3d63c749
YW
1536 if (STR_IN_SET(field,
1537 "Description", "SourcePath", "OnFailureJobMode",
1538 "JobTimeoutAction", "JobTimeoutRebootArgument",
1539 "StartLimitAction", "FailureAction", "SuccessAction",
1540 "RebootArgument", "CollectMode"))
89ada3ba
YW
1541
1542 return bus_append_string(m, field, eq);
1543
3d63c749
YW
1544 if (STR_IN_SET(field,
1545 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1546 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
89ada3ba
YW
1547
1548 return bus_append_parse_boolean(m, field, eq);
1549
3d63c749
YW
1550 if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1551
1552 return bus_append_parse_sec_rename(m, field, eq);
1553
1554 if (streq(field, "StartLimitBurst"))
1555
1556 return bus_append_safe_atou(m, field, eq);
1557
1558 if (unit_dependency_from_string(field) >= 0 ||
1559 STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
89ada3ba 1560
eae194a5 1561 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
89ada3ba 1562
3d63c749
YW
1563 t = condition_type_from_string(field);
1564 if (t >= 0)
1565 is_condition = true;
1566 else
1567 t = assert_type_from_string(field);
1568 if (t >= 0) {
1569 if (isempty(eq))
1570 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
1571 else {
1572 const char *p = eq;
1573 int trigger, negate;
1574
1575 trigger = *p == '|';
1576 if (trigger)
1577 p++;
1578
1579 negate = *p == '!';
1580 if (negate)
1581 p++;
1582
1583 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
1584 field, trigger, negate, p);
1585 }
1586 if (r < 0)
1587 return bus_log_create_error(r);
1588
1589 return 1;
1590 }
1591
89ada3ba
YW
1592 return 0;
1593}
1594
1595int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1596 const char *eq, *field;
1597 int r;
1598
1599 assert(m);
1600 assert(assignment);
1601
1602 eq = strchr(assignment, '=');
baaa35ad
ZJS
1603 if (!eq)
1604 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1605 "Not an assignment: %s", assignment);
89ada3ba
YW
1606
1607 field = strndupa(assignment, eq - assignment);
1608 eq++;
1609
1610 switch (t) {
1611 case UNIT_SERVICE:
1612 r = bus_append_cgroup_property(m, field, eq);
1613 if (r != 0)
f6c66be1
LP
1614 return r;
1615
89ada3ba
YW
1616 r = bus_append_execute_property(m, field, eq);
1617 if (r != 0)
1618 return r;
f6c66be1 1619
89ada3ba
YW
1620 r = bus_append_kill_property(m, field, eq);
1621 if (r != 0)
1622 return r;
f6c66be1 1623
89ada3ba
YW
1624 r = bus_append_service_property(m, field, eq);
1625 if (r != 0)
1626 return r;
1627 break;
f6c66be1 1628
89ada3ba
YW
1629 case UNIT_SOCKET:
1630 r = bus_append_cgroup_property(m, field, eq);
1631 if (r != 0)
1632 return r;
f6c66be1 1633
89ada3ba
YW
1634 r = bus_append_execute_property(m, field, eq);
1635 if (r != 0)
1636 return r;
f6c66be1 1637
89ada3ba
YW
1638 r = bus_append_kill_property(m, field, eq);
1639 if (r != 0)
1640 return r;
f6c66be1 1641
89ada3ba
YW
1642 r = bus_append_socket_property(m, field, eq);
1643 if (r != 0)
f6c66be1 1644 return r;
89ada3ba 1645 break;
f6c66be1 1646
89ada3ba
YW
1647 case UNIT_TIMER:
1648 r = bus_append_timer_property(m, field, eq);
1649 if (r != 0)
1650 return r;
1651 break;
f6c66be1 1652
89ada3ba
YW
1653 case UNIT_PATH:
1654 r = bus_append_path_property(m, field, eq);
1655 if (r != 0)
1656 return r;
1657 break;
268833ed 1658
89ada3ba
YW
1659 case UNIT_SLICE:
1660 r = bus_append_cgroup_property(m, field, eq);
1661 if (r != 0)
1662 return r;
1663 break;
268833ed 1664
89ada3ba 1665 case UNIT_SCOPE:
3d63c749
YW
1666
1667 if (streq(field, "TimeoutStopSec"))
1668 return bus_append_parse_sec_rename(m, field, eq);
1669
89ada3ba
YW
1670 r = bus_append_cgroup_property(m, field, eq);
1671 if (r != 0)
1672 return r;
268833ed 1673
89ada3ba
YW
1674 r = bus_append_kill_property(m, field, eq);
1675 if (r != 0)
1676 return r;
1677 break;
535e0d19 1678
89ada3ba 1679 case UNIT_MOUNT:
3d63c749
YW
1680 r = bus_append_cgroup_property(m, field, eq);
1681 if (r != 0)
1682 return r;
1683
1684 r = bus_append_execute_property(m, field, eq);
1685 if (r != 0)
1686 return r;
1687
1688 r = bus_append_kill_property(m, field, eq);
1689 if (r != 0)
1690 return r;
1691
1692 r = bus_append_mount_property(m, field, eq);
1693 if (r != 0)
1694 return r;
1695
1696 break;
1697
89ada3ba 1698 case UNIT_AUTOMOUNT:
3d63c749
YW
1699 r = bus_append_automount_property(m, field, eq);
1700 if (r != 0)
1701 return r;
1702
89ada3ba 1703 break;
535e0d19 1704
89ada3ba
YW
1705 case UNIT_TARGET:
1706 case UNIT_DEVICE:
1707 case UNIT_SWAP:
baaa35ad
ZJS
1708 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1709 "Not supported unit type");
535e0d19 1710
89ada3ba 1711 default:
baaa35ad
ZJS
1712 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1713 "Invalid unit type");
20b16441
LP
1714 }
1715
89ada3ba
YW
1716 r = bus_append_unit_property(m, field, eq);
1717 if (r != 0)
1718 return r;
20b16441 1719
baaa35ad
ZJS
1720 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1721 "Unknown assignment: %s", assignment);
20b16441
LP
1722}
1723
89ada3ba 1724int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
8673cf13
LP
1725 char **i;
1726 int r;
1727
1728 assert(m);
1729
1730 STRV_FOREACH(i, l) {
89ada3ba 1731 r = bus_append_unit_property_assignment(m, t, *i);
8673cf13
LP
1732 if (r < 0)
1733 return r;
1734 }
1735
1736 return 0;
1737}
1738
20b16441
LP
1739typedef struct BusWaitForJobs {
1740 sd_bus *bus;
1741 Set *jobs;
1742
1743 char *name;
1744 char *result;
1745
1746 sd_bus_slot *slot_job_removed;
1747 sd_bus_slot *slot_disconnected;
1748} BusWaitForJobs;
1749
1750static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1751 assert(m);
1752
1753 log_error("Warning! D-Bus connection terminated.");
1754 sd_bus_close(sd_bus_message_get_bus(m));
1755
1756 return 0;
1757}
1758
1759static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1760 const char *path, *unit, *result;
1761 BusWaitForJobs *d = userdata;
1762 uint32_t id;
1763 char *found;
1764 int r;
1765
1766 assert(m);
1767 assert(d);
1768
1769 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1770 if (r < 0) {
1771 bus_log_parse_error(r);
1772 return 0;
1773 }
1774
1775 found = set_remove(d->jobs, (char*) path);
1776 if (!found)
1777 return 0;
1778
1779 free(found);
1780
1781 if (!isempty(result))
1782 d->result = strdup(result);
1783
1784 if (!isempty(unit))
1785 d->name = strdup(unit);
1786
1787 return 0;
1788}
1789
1790void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1791 if (!d)
1792 return;
1793
1794 set_free_free(d->jobs);
1795
1796 sd_bus_slot_unref(d->slot_disconnected);
1797 sd_bus_slot_unref(d->slot_job_removed);
1798
1799 sd_bus_unref(d->bus);
1800
1801 free(d->name);
1802 free(d->result);
1803
1804 free(d);
1805}
1806
1807int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1808 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1809 int r;
1810
1811 assert(bus);
1812 assert(ret);
1813
1814 d = new0(BusWaitForJobs, 1);
1815 if (!d)
1816 return -ENOMEM;
1817
1818 d->bus = sd_bus_ref(bus);
1819
1820 /* When we are a bus client we match by sender. Direct
1821 * connections OTOH have no initialized sender field, and
1822 * hence we ignore the sender then */
75152a4d 1823 r = sd_bus_match_signal_async(
20b16441
LP
1824 bus,
1825 &d->slot_job_removed,
75152a4d
LP
1826 bus->bus_client ? "org.freedesktop.systemd1" : NULL,
1827 "/org/freedesktop/systemd1",
1828 "org.freedesktop.systemd1.Manager",
1829 "JobRemoved",
1830 match_job_removed, NULL, d);
20b16441
LP
1831 if (r < 0)
1832 return r;
1833
75152a4d 1834 r = sd_bus_match_signal_async(
20b16441
LP
1835 bus,
1836 &d->slot_disconnected,
75152a4d
LP
1837 "org.freedesktop.DBus.Local",
1838 NULL,
1839 "org.freedesktop.DBus.Local",
1840 "Disconnected",
1841 match_disconnected, NULL, d);
20b16441
LP
1842 if (r < 0)
1843 return r;
1844
1cc6c93a 1845 *ret = TAKE_PTR(d);
20b16441
LP
1846
1847 return 0;
1848}
1849
1850static int bus_process_wait(sd_bus *bus) {
1851 int r;
1852
1853 for (;;) {
1854 r = sd_bus_process(bus, NULL);
1855 if (r < 0)
1856 return r;
1857 if (r > 0)
1858 return 0;
1859
1860 r = sd_bus_wait(bus, (uint64_t) -1);
1861 if (r < 0)
1862 return r;
1863 }
1864}
1865
1866static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1867 _cleanup_free_ char *dbus_path = NULL;
1868
1869 assert(d);
1870 assert(d->name);
1871 assert(result);
1872
bd5a1c91
LP
1873 if (!endswith(d->name, ".service"))
1874 return -EINVAL;
1875
20b16441
LP
1876 dbus_path = unit_dbus_path_from_name(d->name);
1877 if (!dbus_path)
1878 return -ENOMEM;
1879
1880 return sd_bus_get_property_string(d->bus,
1881 "org.freedesktop.systemd1",
1882 dbus_path,
1883 "org.freedesktop.systemd1.Service",
1884 "Result",
1885 NULL,
1886 result);
1887}
1888
1889static const struct {
1890 const char *result, *explanation;
1891} explanations [] = {
0b2de9d9 1892 { "resources", "of unavailable resources or another system error" },
7ed0a4c5 1893 { "protocol", "the service did not take the steps required by its unit configuration" },
20b16441
LP
1894 { "timeout", "a timeout was exceeded" },
1895 { "exit-code", "the control process exited with error code" },
1896 { "signal", "a fatal signal was delivered to the control process" },
1897 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1898 { "watchdog", "the service failed to send watchdog ping" },
1899 { "start-limit", "start of the service was attempted too often" }
1900};
1901
1902static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1903 _cleanup_free_ char *service_shell_quoted = NULL;
1904 const char *systemctl = "systemctl", *journalctl = "journalctl";
1905
1906 assert(service);
1907
804ee07c 1908 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
20b16441 1909
3a58ca47 1910 if (!strv_isempty((char**) extra_args)) {
20b16441
LP
1911 _cleanup_free_ char *t;
1912
1913 t = strv_join((char**) extra_args, " ");
1914 systemctl = strjoina("systemctl ", t ? : "<args>");
1915 journalctl = strjoina("journalctl ", t ? : "<args>");
1916 }
1917
1918 if (!isempty(result)) {
1919 unsigned i;
1920
1921 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1922 if (streq(result, explanations[i].result))
1923 break;
1924
1925 if (i < ELEMENTSOF(explanations)) {
1926 log_error("Job for %s failed because %s.\n"
1927 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1928 service,
1929 explanations[i].explanation,
1930 systemctl,
1931 service_shell_quoted ?: "<service>",
1932 journalctl);
1933 goto finish;
1934 }
1935 }
1936
1937 log_error("Job for %s failed.\n"
1938 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1939 service,
1940 systemctl,
1941 service_shell_quoted ?: "<service>",
1942 journalctl);
1943
1944finish:
1945 /* For some results maybe additional explanation is required */
1946 if (streq_ptr(result, "start-limit"))
1947 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1948 "followed by \"%1$s start %2$s\" again.",
1949 systemctl,
1950 service_shell_quoted ?: "<service>");
1951}
1952
1953static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
20b16441
LP
1954 assert(d->result);
1955
1956 if (!quiet) {
1957 if (streq(d->result, "canceled"))
1958 log_error("Job for %s canceled.", strna(d->name));
1959 else if (streq(d->result, "timeout"))
1960 log_error("Job for %s timed out.", strna(d->name));
1961 else if (streq(d->result, "dependency"))
1962 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1963 else if (streq(d->result, "invalid"))
1964 log_error("%s is not active, cannot reload.", strna(d->name));
1965 else if (streq(d->result, "assert"))
1966 log_error("Assertion failed on job for %s.", strna(d->name));
1967 else if (streq(d->result, "unsupported"))
1968 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
c5a97ed1
LP
1969 else if (streq(d->result, "collected"))
1970 log_error("Queued job for %s was garbage collected.", strna(d->name));
d4fd1cf2
LP
1971 else if (streq(d->result, "once"))
1972 log_error("Unit %s was started already once and can't be started again.", strna(d->name));
ae835bc7 1973 else if (!STR_IN_SET(d->result, "done", "skipped")) {
20b16441 1974 if (d->name) {
20b16441 1975 _cleanup_free_ char *result = NULL;
bd5a1c91 1976 int q;
20b16441
LP
1977
1978 q = bus_job_get_service_result(d, &result);
1979 if (q < 0)
bd5a1c91 1980 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
20b16441
LP
1981
1982 log_job_error_with_service_result(d->name, result, extra_args);
1983 } else
1984 log_error("Job failed. See \"journalctl -xe\" for details.");
1985 }
1986 }
1987
c5a97ed1 1988 if (STR_IN_SET(d->result, "canceled", "collected"))
d4fd1cf2 1989 return -ECANCELED;
20b16441 1990 else if (streq(d->result, "timeout"))
d4fd1cf2 1991 return -ETIME;
20b16441 1992 else if (streq(d->result, "dependency"))
d4fd1cf2 1993 return -EIO;
20b16441 1994 else if (streq(d->result, "invalid"))
d4fd1cf2 1995 return -ENOEXEC;
20b16441 1996 else if (streq(d->result, "assert"))
d4fd1cf2 1997 return -EPROTO;
20b16441 1998 else if (streq(d->result, "unsupported"))
d4fd1cf2
LP
1999 return -EOPNOTSUPP;
2000 else if (streq(d->result, "once"))
2001 return -ESTALE;
2002 else if (STR_IN_SET(d->result, "done", "skipped"))
2003 return 0;
20b16441 2004
baaa35ad
ZJS
2005 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
2006 "Unexpected job result, assuming server side newer than us: %s", d->result);
20b16441
LP
2007}
2008
2009int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
2010 int r = 0;
2011
2012 assert(d);
2013
2014 while (!set_isempty(d->jobs)) {
2015 int q;
2016
2017 q = bus_process_wait(d->bus);
2018 if (q < 0)
2019 return log_error_errno(q, "Failed to wait for response: %m");
2020
2021 if (d->result) {
2022 q = check_wait_response(d, quiet, extra_args);
2023 /* Return the first error as it is most likely to be
2024 * meaningful. */
2025 if (q < 0 && r == 0)
2026 r = q;
2027
2028 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
2029 }
2030
2031 d->name = mfree(d->name);
2032 d->result = mfree(d->result);
2033 }
2034
2035 return r;
2036}
2037
2038int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
2039 int r;
2040
2041 assert(d);
2042
2043 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
2044 if (r < 0)
2045 return r;
2046
2047 return set_put_strdup(d->jobs, path);
2048}
2049
2050int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
2051 int r;
2052
2053 r = bus_wait_for_jobs_add(d, path);
2054 if (r < 0)
2055 return log_oom();
2056
2057 return bus_wait_for_jobs(d, quiet, NULL);
2058}
2059
da6053d0 2060int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
20b16441
LP
2061 const char *type, *path, *source;
2062 int r;
2063
acc0269c
CH
2064 /* changes is dereferenced when calling unit_file_dump_changes() later,
2065 * so we have to make sure this is not NULL. */
2066 assert(changes);
2067 assert(n_changes);
2068
20b16441
LP
2069 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2070 if (r < 0)
2071 return bus_log_parse_error(r);
2072
2073 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2074 /* We expect only "success" changes to be sent over the bus.
2075 Hence, reject anything negative. */
2076 UnitFileChangeType ch = unit_file_change_type_from_string(type);
2077
2078 if (ch < 0) {
2079 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
2080 continue;
2081 }
2082
2083 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2084 if (r < 0)
2085 return r;
2086 }
2087 if (r < 0)
2088 return bus_log_parse_error(r);
2089
2090 r = sd_bus_message_exit_container(m);
2091 if (r < 0)
2092 return bus_log_parse_error(r);
2093
35d379b2 2094 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
20b16441
LP
2095 return 0;
2096}
2097
291d565a
LP
2098struct CGroupInfo {
2099 char *cgroup_path;
2100 bool is_const; /* If false, cgroup_path should be free()'d */
2101
2102 Hashmap *pids; /* PID → process name */
2103 bool done;
2104
2105 struct CGroupInfo *parent;
2106 LIST_FIELDS(struct CGroupInfo, siblings);
2107 LIST_HEAD(struct CGroupInfo, children);
2108 size_t n_children;
2109};
2110
2111static bool IS_ROOT(const char *p) {
2112 return isempty(p) || streq(p, "/");
2113}
2114
2115static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
2116 struct CGroupInfo *parent = NULL, *cg;
2117 int r;
2118
2119 assert(cgroups);
2120 assert(ret);
2121
2122 if (IS_ROOT(path))
2123 path = "/";
2124
2125 cg = hashmap_get(cgroups, path);
2126 if (cg) {
2127 *ret = cg;
2128 return 0;
2129 }
2130
2131 if (!IS_ROOT(path)) {
2132 const char *e, *pp;
2133
2134 e = strrchr(path, '/');
2135 if (!e)
2136 return -EINVAL;
2137
2138 pp = strndupa(path, e - path);
2139 if (!pp)
2140 return -ENOMEM;
2141
2142 r = add_cgroup(cgroups, pp, false, &parent);
2143 if (r < 0)
2144 return r;
2145 }
2146
2147 cg = new0(struct CGroupInfo, 1);
2148 if (!cg)
2149 return -ENOMEM;
2150
2151 if (is_const)
2152 cg->cgroup_path = (char*) path;
2153 else {
2154 cg->cgroup_path = strdup(path);
2155 if (!cg->cgroup_path) {
2156 free(cg);
2157 return -ENOMEM;
2158 }
2159 }
2160
2161 cg->is_const = is_const;
2162 cg->parent = parent;
2163
2164 r = hashmap_put(cgroups, cg->cgroup_path, cg);
2165 if (r < 0) {
2166 if (!is_const)
2167 free(cg->cgroup_path);
2168 free(cg);
2169 return r;
2170 }
2171
2172 if (parent) {
2173 LIST_PREPEND(siblings, parent->children, cg);
2174 parent->n_children++;
2175 }
2176
2177 *ret = cg;
2178 return 1;
2179}
2180
2181static int add_process(
2182 Hashmap *cgroups,
2183 const char *path,
2184 pid_t pid,
2185 const char *name) {
2186
2187 struct CGroupInfo *cg;
2188 int r;
2189
2190 assert(cgroups);
2191 assert(name);
2192 assert(pid > 0);
2193
2194 r = add_cgroup(cgroups, path, true, &cg);
2195 if (r < 0)
2196 return r;
2197
2198 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
2199 if (r < 0)
2200 return r;
2201
2202 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
2203}
2204
2205static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
2206 assert(cgroups);
2207 assert(cg);
2208
2209 while (cg->children)
2210 remove_cgroup(cgroups, cg->children);
2211
2212 hashmap_remove(cgroups, cg->cgroup_path);
2213
2214 if (!cg->is_const)
2215 free(cg->cgroup_path);
2216
2217 hashmap_free(cg->pids);
2218
2219 if (cg->parent)
2220 LIST_REMOVE(siblings, cg->parent->children, cg);
2221
2222 free(cg);
2223}
2224
93bab288
YW
2225static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) {
2226 return strcmp((*a)->cgroup_path, (*b)->cgroup_path);
291d565a
LP
2227}
2228
2229static int dump_processes(
2230 Hashmap *cgroups,
2231 const char *cgroup_path,
2232 const char *prefix,
2233 unsigned n_columns,
2234 OutputFlags flags) {
2235
2236 struct CGroupInfo *cg;
2237 int r;
2238
2239 assert(prefix);
2240
2241 if (IS_ROOT(cgroup_path))
2242 cgroup_path = "/";
2243
2244 cg = hashmap_get(cgroups, cgroup_path);
2245 if (!cg)
2246 return 0;
2247
2248 if (!hashmap_isempty(cg->pids)) {
2249 const char *name;
2250 size_t n = 0, i;
2251 pid_t *pids;
2252 void *pidp;
2253 Iterator j;
2254 int width;
2255
2256 /* Order processes by their PID */
2257 pids = newa(pid_t, hashmap_size(cg->pids));
2258
2259 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
2260 pids[n++] = PTR_TO_PID(pidp);
2261
2262 assert(n == hashmap_size(cg->pids));
93bab288 2263 typesafe_qsort(pids, n, pid_compare_func);
291d565a
LP
2264
2265 width = DECIMAL_STR_WIDTH(pids[n-1]);
2266
2267 for (i = 0; i < n; i++) {
2268 _cleanup_free_ char *e = NULL;
2269 const char *special;
2270 bool more;
2271
2272 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
2273 assert(name);
2274
2275 if (n_columns != 0) {
2276 unsigned k;
2277
2278 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2279
2280 e = ellipsize(name, k, 100);
2281 if (e)
2282 name = e;
2283 }
2284
2285 more = i+1 < n || cg->children;
323b7dc9 2286 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
2287
2288 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
2289 prefix,
2290 special,
2291 width, pids[i],
2292 name);
2293 }
2294 }
2295
2296 if (cg->children) {
2297 struct CGroupInfo **children, *child;
2298 size_t n = 0, i;
2299
2300 /* Order subcgroups by their name */
2301 children = newa(struct CGroupInfo*, cg->n_children);
2302 LIST_FOREACH(siblings, child, cg->children)
2303 children[n++] = child;
2304 assert(n == cg->n_children);
93bab288 2305 typesafe_qsort(children, n, cgroup_info_compare_func);
291d565a 2306
7351ded5
IL
2307 if (n_columns != 0)
2308 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
291d565a
LP
2309
2310 for (i = 0; i < n; i++) {
2311 _cleanup_free_ char *pp = NULL;
2312 const char *name, *special;
2313 bool more;
2314
2315 child = children[i];
2316
2317 name = strrchr(child->cgroup_path, '/');
2318 if (!name)
2319 return -EINVAL;
2320 name++;
2321
2322 more = i+1 < n;
323b7dc9 2323 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
2324
2325 fputs(prefix, stdout);
2326 fputs(special, stdout);
2327 fputs(name, stdout);
2328 fputc('\n', stdout);
2329
323b7dc9 2330 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
291d565a
LP
2331
2332 pp = strappend(prefix, special);
2333 if (!pp)
2334 return -ENOMEM;
2335
2336 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
2337 if (r < 0)
2338 return r;
2339 }
2340 }
2341
2342 cg->done = true;
2343 return 0;
2344}
2345
2346static int dump_extra_processes(
2347 Hashmap *cgroups,
2348 const char *prefix,
2349 unsigned n_columns,
2350 OutputFlags flags) {
2351
2352 _cleanup_free_ pid_t *pids = NULL;
2353 _cleanup_hashmap_free_ Hashmap *names = NULL;
2354 struct CGroupInfo *cg;
2355 size_t n_allocated = 0, n = 0, k;
2356 Iterator i;
2357 int width, r;
2358
2359 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2360 * combined, sorted, linear list. */
2361
2362 HASHMAP_FOREACH(cg, cgroups, i) {
2363 const char *name;
2364 void *pidp;
2365 Iterator j;
2366
2367 if (cg->done)
2368 continue;
2369
2370 if (hashmap_isempty(cg->pids))
2371 continue;
2372
2373 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2374 if (r < 0)
2375 return r;
2376
2377 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
2378 return -ENOMEM;
2379
2380 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
2381 pids[n++] = PTR_TO_PID(pidp);
2382
2383 r = hashmap_put(names, pidp, (void*) name);
2384 if (r < 0)
2385 return r;
2386 }
2387 }
2388
2389 if (n == 0)
2390 return 0;
2391
93bab288 2392 typesafe_qsort(pids, n, pid_compare_func);
291d565a
LP
2393 width = DECIMAL_STR_WIDTH(pids[n-1]);
2394
2395 for (k = 0; k < n; k++) {
2396 _cleanup_free_ char *e = NULL;
2397 const char *name;
2398
2399 name = hashmap_get(names, PID_TO_PTR(pids[k]));
2400 assert(name);
2401
2402 if (n_columns != 0) {
2403 unsigned z;
2404
2405 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2406
2407 e = ellipsize(name, z, 100);
2408 if (e)
2409 name = e;
2410 }
2411
2412 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2413 prefix,
323b7dc9 2414 special_glyph(TRIANGULAR_BULLET),
291d565a
LP
2415 width, pids[k],
2416 name);
2417 }
2418
2419 return 0;
2420}
2421
2422int unit_show_processes(
2423 sd_bus *bus,
2424 const char *unit,
2425 const char *cgroup_path,
2426 const char *prefix,
2427 unsigned n_columns,
2428 OutputFlags flags,
2429 sd_bus_error *error) {
2430
2431 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2432 Hashmap *cgroups = NULL;
2433 struct CGroupInfo *cg;
2434 int r;
2435
2436 assert(bus);
2437 assert(unit);
2438
2439 if (flags & OUTPUT_FULL_WIDTH)
2440 n_columns = 0;
2441 else if (n_columns <= 0)
2442 n_columns = columns();
2443
2444 prefix = strempty(prefix);
2445
2446 r = sd_bus_call_method(
2447 bus,
2448 "org.freedesktop.systemd1",
2449 "/org/freedesktop/systemd1",
2450 "org.freedesktop.systemd1.Manager",
2451 "GetUnitProcesses",
2452 error,
2453 &reply,
2454 "s",
2455 unit);
2456 if (r < 0)
2457 return r;
2458
548f6937 2459 cgroups = hashmap_new(&path_hash_ops);
291d565a
LP
2460 if (!cgroups)
2461 return -ENOMEM;
2462
2463 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
2464 if (r < 0)
2465 goto finish;
2466
2467 for (;;) {
2468 const char *path = NULL, *name = NULL;
2469 uint32_t pid;
2470
2471 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
2472 if (r < 0)
2473 goto finish;
2474 if (r == 0)
2475 break;
2476
2477 r = add_process(cgroups, path, pid, name);
2478 if (r < 0)
2479 goto finish;
2480 }
2481
2482 r = sd_bus_message_exit_container(reply);
2483 if (r < 0)
2484 goto finish;
2485
2486 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
2487 if (r < 0)
2488 goto finish;
2489
2490 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2491
2492finish:
2493 while ((cg = hashmap_first(cgroups)))
2494 remove_cgroup(cgroups, cg);
2495
2496 hashmap_free(cgroups);
2497
2498 return r;
2499}
bd062910
ZJS
2500
2501int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2502 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2503 _cleanup_free_ char *path = NULL;
2504 int r;
2505
2506 path = unit_dbus_path_from_name(name);
2507 if (!path)
2508 return log_oom();
2509
2510 /* This function warns on it's own, because otherwise it'd be awkward to pass
2511 * the dbus error message around. */
2512
2513 r = sd_bus_get_property_string(
2514 bus,
2515 "org.freedesktop.systemd1",
2516 path,
2517 "org.freedesktop.systemd1.Unit",
2518 "LoadState",
2519 &error,
2520 load_state);
2521 if (r < 0)
2522 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2523
2524 return 0;
2525}