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