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