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