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