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