]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
tree-wide: be more careful with the type of array sizes
[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
d2d6c096
LP
1101 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1102 if (r < 0)
89ada3ba 1103 return bus_log_create_error(r);
d2d6c096
LP
1104 }
1105
1106 r = sd_bus_message_close_container(m);
1107 if (r < 0)
89ada3ba
YW
1108 return bus_log_create_error(r);
1109
1110 r = sd_bus_message_close_container(m);
1111 if (r < 0)
1112 return bus_log_create_error(r);
d2d6c096
LP
1113
1114 r = sd_bus_message_close_container(m);
89ada3ba
YW
1115 if (r < 0)
1116 return bus_log_create_error(r);
f6c66be1 1117
89ada3ba
YW
1118 return 1;
1119 }
f6c66be1 1120
784ad252
YW
1121 if (streq(field, "TemporaryFileSystem")) {
1122 const char *p = eq;
1123
1124 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1125 if (r < 0)
1126 return bus_log_create_error(r);
1127
1128 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1129 if (r < 0)
1130 return bus_log_create_error(r);
1131
1132 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1133 if (r < 0)
1134 return bus_log_create_error(r);
1135
1136 r = sd_bus_message_open_container(m, 'a', "(ss)");
1137 if (r < 0)
1138 return bus_log_create_error(r);
1139
1140 for (;;) {
1141 _cleanup_free_ char *word = NULL, *path = NULL;
1142 const char *w;
1143
1144 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1145 if (r < 0)
1146 return log_error_errno(r, "Failed to parse argument: %m");
1147 if (r == 0)
1148 break;
1149
1150 w = word;
1151 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1152 if (r < 0)
1153 return log_error_errno(r, "Failed to parse argument: %m");
1154 if (r == 0)
1155 return log_error("Failed to parse argument: %m");
1156
1157 r = sd_bus_message_append(m, "(ss)", path, w);
1158 if (r < 0)
1159 return bus_log_create_error(r);
1160 }
1161
1162 r = sd_bus_message_close_container(m);
1163 if (r < 0)
1164 return bus_log_create_error(r);
1165
1166 r = sd_bus_message_close_container(m);
1167 if (r < 0)
1168 return bus_log_create_error(r);
1169
1170 r = sd_bus_message_close_container(m);
1171 if (r < 0)
1172 return bus_log_create_error(r);
1173
1174 return 1;
1175 }
1176
89ada3ba
YW
1177 return 0;
1178}
f6c66be1 1179
89ada3ba 1180static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
f6c66be1 1181
89ada3ba 1182 if (streq(field, "KillMode"))
f6c66be1 1183
89ada3ba 1184 return bus_append_string(m, field, eq);
f6c66be1 1185
89ada3ba 1186 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
f6c66be1 1187
89ada3ba 1188 return bus_append_parse_boolean(m, field, eq);
f6c66be1 1189
89ada3ba
YW
1190 if (streq(field, "KillSignal"))
1191
1192 return bus_append_signal_from_string_try_harder(m, field, eq);
1193
1194 return 0;
1195}
1196
3d63c749 1197static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
89ada3ba 1198
3d63c749 1199 if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
89ada3ba
YW
1200
1201 return bus_append_string(m, field, eq);
1202
3d63c749
YW
1203 if (streq(field, "TimeoutSec"))
1204
1205 return bus_append_parse_sec_rename(m, field, eq);
1206
1207 if (streq(field, "DirectoryMode"))
1208
1209 return bus_append_parse_mode(m, field, eq);
1210
1211 if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1212
1213 return bus_append_parse_boolean(m, field, eq);
1214
1215 return 0;
1216}
1217
1218static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1219 int r;
1220
89ada3ba
YW
1221 if (streq(field, "MakeDirectory"))
1222
1223 return bus_append_parse_boolean(m, field, eq);
1224
1225 if (streq(field, "DirectoryMode"))
1226
1227 return bus_append_parse_mode(m, field, eq);
1228
3d63c749
YW
1229 if (STR_IN_SET(field,
1230 "PathExists", "PathExistsGlob", "PathChanged",
1231 "PathModified", "DirectoryNotEmpty")) {
1232
1233 if (isempty(eq))
1234 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1235 else
1236 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1237 if (r < 0)
1238 return bus_log_create_error(r);
1239
1240 return 1;
1241 }
1242
89ada3ba
YW
1243 return 0;
1244}
1245
1246static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 1247 int r;
89ada3ba 1248
3d63c749
YW
1249 if (STR_IN_SET(field,
1250 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1251 "USBFunctionDescriptors", "USBFunctionStrings"))
89ada3ba
YW
1252
1253 return bus_append_string(m, field, eq);
f6c66be1 1254
3d63c749 1255 if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
89ada3ba
YW
1256
1257 return bus_append_parse_boolean(m, field, eq);
1258
3d63c749 1259 if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
89ada3ba
YW
1260
1261 return bus_append_parse_sec_rename(m, field, eq);
1262
3d63c749
YW
1263 if (streq(field, "TimeoutSec")) {
1264
1265 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1266 if (r < 0)
1267 return r;
1268
1269 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1270 }
1271
89ada3ba
YW
1272 if (streq(field, "FileDescriptorStoreMax"))
1273
1274 return bus_append_safe_atou(m, field, eq);
1275
1276 if (STR_IN_SET(field,
1277 "ExecStartPre", "ExecStart", "ExecStartPost",
1278 "ExecReload", "ExecStop", "ExecStopPost"))
1279
1280 return bus_append_exec_command(m, field, eq);
1281
3d63c749
YW
1282 if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1283 _cleanup_free_ int *status = NULL, *signal = NULL;
1284 size_t sz_status = 0, sz_signal = 0;
1285 const char *p;
1286
1287 for (p = eq;;) {
1288 _cleanup_free_ char *word = NULL;
1289 int val;
1290
1291 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1292 if (r == 0)
1293 break;
1294 if (r == -ENOMEM)
1295 return log_oom();
1296 if (r < 0)
1297 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1298
1299 r = safe_atoi(word, &val);
1300 if (r < 0) {
1301 val = signal_from_string_try_harder(word);
1302 if (val < 0)
1303 return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
1304
aa484f35 1305 signal = reallocarray(signal, sz_signal + 1, sizeof(int));
3d63c749
YW
1306 if (!signal)
1307 return log_oom();
1308
1309 signal[sz_signal++] = val;
1310 } else {
aa484f35 1311 status = reallocarray(status, sz_status + 1, sizeof(int));
3d63c749
YW
1312 if (!status)
1313 return log_oom();
1314
1315 status[sz_status++] = val;
1316 }
1317 }
1318
1319 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1320 if (r < 0)
1321 return bus_log_create_error(r);
1322
1323 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1324 if (r < 0)
1325 return bus_log_create_error(r);
1326
1327 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1328 if (r < 0)
1329 return bus_log_create_error(r);
1330
1331 r = sd_bus_message_open_container(m, 'r', "aiai");
1332 if (r < 0)
1333 return bus_log_create_error(r);
1334
1335 r = sd_bus_message_append_array(m, 'i', status, sz_status);
1336 if (r < 0)
1337 return bus_log_create_error(r);
1338
1339 r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
1340 if (r < 0)
1341 return bus_log_create_error(r);
1342
1343 r = sd_bus_message_close_container(m);
1344 if (r < 0)
1345 return bus_log_create_error(r);
1346
1347 r = sd_bus_message_close_container(m);
1348 if (r < 0)
1349 return bus_log_create_error(r);
1350
1351 r = sd_bus_message_close_container(m);
1352 if (r < 0)
1353 return bus_log_create_error(r);
1354
1355 return 1;
1356 }
1357
89ada3ba
YW
1358 return 0;
1359}
1360
1361static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1362 int r;
1363
1364 if (STR_IN_SET(field,
1365 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1366 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1367
1368 return bus_append_parse_boolean(m, field, eq);
1369
1370 if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
1371
1372 return bus_append_safe_atoi(m, field, eq);
1373
1374 if (streq(field, "IPTOS"))
1375
1376 return bus_append_ip_tos_from_string(m, field, eq);
1377
1378 if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1379
1380 return bus_append_safe_atou(m, field, eq);
1381
1382 if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
1383
1384 return bus_append_parse_mode(m, field, eq);
1385
1386 if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1387
1388 return bus_append_safe_atoi64(m, field, eq);
1389
1390 if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1391
1392 return bus_append_parse_sec_rename(m, field, eq);
1393
1394 if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1395
b48e508d 1396 return bus_append_parse_size(m, field, eq, 1024);
89ada3ba
YW
1397
1398 if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1399
1400 return bus_append_exec_command(m, field, eq);
1401
1402 if (STR_IN_SET(field,
1403 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1404 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1405 "SocketUser", "SocketGroup"))
1406
1407 return bus_append_string(m, field, eq);
1408
1409 if (streq(field, "Symlinks"))
1410
1411 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1412
e045e325 1413 if (streq(field, "SocketProtocol"))
89ada3ba 1414
e045e325 1415 return bus_append_socket_protocol_from_name(m, field, eq);
89ada3ba
YW
1416
1417 if (STR_IN_SET(field,
1418 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1419 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1420
3d63c749
YW
1421 if (isempty(eq))
1422 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1423 else
81b1dc27 1424 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
f6c66be1 1425 if (r < 0)
89ada3ba
YW
1426 return bus_log_create_error(r);
1427
1428 return 1;
1429 }
1430
1431 return 0;
1432}
1433static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749 1434 int r;
89ada3ba
YW
1435
1436 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
1437
1438 return bus_append_parse_boolean(m, field, eq);
1439
3d63c749
YW
1440 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1441
1442 return bus_append_parse_sec_rename(m, field, eq);
1443
89ada3ba
YW
1444 if (STR_IN_SET(field,
1445 "OnActiveSec", "OnBootSec", "OnStartupSec",
3d63c749 1446 "OnUnitActiveSec","OnUnitInactiveSec")) {
89ada3ba 1447
3d63c749
YW
1448 if (isempty(eq))
1449 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
1450 else {
1451 usec_t t;
1452 r = parse_sec(eq, &t);
1453 if (r < 0)
1454 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
89ada3ba 1455
3d63c749
YW
1456 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
1457 }
1458 if (r < 0)
1459 return bus_log_create_error(r);
89ada3ba 1460
3d63c749
YW
1461 return 1;
1462 }
1463
1464 if (streq(field, "OnCalendar")) {
1465
1466 if (isempty(eq))
1467 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
1468 else
1469 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
1470 if (r < 0)
1471 return bus_log_create_error(r);
1472
1473 return 1;
1474 }
89ada3ba
YW
1475
1476 return 0;
1477}
1478
1479static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
3d63c749
YW
1480 ConditionType t = _CONDITION_TYPE_INVALID;
1481 bool is_condition = false;
1482 int r;
f6c66be1 1483
3d63c749
YW
1484 if (STR_IN_SET(field,
1485 "Description", "SourcePath", "OnFailureJobMode",
1486 "JobTimeoutAction", "JobTimeoutRebootArgument",
1487 "StartLimitAction", "FailureAction", "SuccessAction",
1488 "RebootArgument", "CollectMode"))
89ada3ba
YW
1489
1490 return bus_append_string(m, field, eq);
1491
3d63c749
YW
1492 if (STR_IN_SET(field,
1493 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1494 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
89ada3ba
YW
1495
1496 return bus_append_parse_boolean(m, field, eq);
1497
3d63c749
YW
1498 if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1499
1500 return bus_append_parse_sec_rename(m, field, eq);
1501
1502 if (streq(field, "StartLimitBurst"))
1503
1504 return bus_append_safe_atou(m, field, eq);
1505
1506 if (unit_dependency_from_string(field) >= 0 ||
1507 STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
89ada3ba 1508
eae194a5 1509 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
89ada3ba 1510
3d63c749
YW
1511 t = condition_type_from_string(field);
1512 if (t >= 0)
1513 is_condition = true;
1514 else
1515 t = assert_type_from_string(field);
1516 if (t >= 0) {
1517 if (isempty(eq))
1518 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
1519 else {
1520 const char *p = eq;
1521 int trigger, negate;
1522
1523 trigger = *p == '|';
1524 if (trigger)
1525 p++;
1526
1527 negate = *p == '!';
1528 if (negate)
1529 p++;
1530
1531 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
1532 field, trigger, negate, p);
1533 }
1534 if (r < 0)
1535 return bus_log_create_error(r);
1536
1537 return 1;
1538 }
1539
89ada3ba
YW
1540 return 0;
1541}
1542
1543int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1544 const char *eq, *field;
1545 int r;
1546
1547 assert(m);
1548 assert(assignment);
1549
1550 eq = strchr(assignment, '=');
1551 if (!eq) {
1552 log_error("Not an assignment: %s", assignment);
1553 return -EINVAL;
1554 }
1555
1556 field = strndupa(assignment, eq - assignment);
1557 eq++;
1558
1559 switch (t) {
1560 case UNIT_SERVICE:
1561 r = bus_append_cgroup_property(m, field, eq);
1562 if (r != 0)
f6c66be1
LP
1563 return r;
1564
89ada3ba
YW
1565 r = bus_append_execute_property(m, field, eq);
1566 if (r != 0)
1567 return r;
f6c66be1 1568
89ada3ba
YW
1569 r = bus_append_kill_property(m, field, eq);
1570 if (r != 0)
1571 return r;
f6c66be1 1572
89ada3ba
YW
1573 r = bus_append_service_property(m, field, eq);
1574 if (r != 0)
1575 return r;
1576 break;
f6c66be1 1577
89ada3ba
YW
1578 case UNIT_SOCKET:
1579 r = bus_append_cgroup_property(m, field, eq);
1580 if (r != 0)
1581 return r;
f6c66be1 1582
89ada3ba
YW
1583 r = bus_append_execute_property(m, field, eq);
1584 if (r != 0)
1585 return r;
f6c66be1 1586
89ada3ba
YW
1587 r = bus_append_kill_property(m, field, eq);
1588 if (r != 0)
1589 return r;
f6c66be1 1590
89ada3ba
YW
1591 r = bus_append_socket_property(m, field, eq);
1592 if (r != 0)
f6c66be1 1593 return r;
89ada3ba 1594 break;
f6c66be1 1595
89ada3ba
YW
1596 case UNIT_TIMER:
1597 r = bus_append_timer_property(m, field, eq);
1598 if (r != 0)
1599 return r;
1600 break;
f6c66be1 1601
89ada3ba
YW
1602 case UNIT_PATH:
1603 r = bus_append_path_property(m, field, eq);
1604 if (r != 0)
1605 return r;
1606 break;
268833ed 1607
89ada3ba
YW
1608 case UNIT_SLICE:
1609 r = bus_append_cgroup_property(m, field, eq);
1610 if (r != 0)
1611 return r;
1612 break;
268833ed 1613
89ada3ba 1614 case UNIT_SCOPE:
3d63c749
YW
1615
1616 if (streq(field, "TimeoutStopSec"))
1617 return bus_append_parse_sec_rename(m, field, eq);
1618
89ada3ba
YW
1619 r = bus_append_cgroup_property(m, field, eq);
1620 if (r != 0)
1621 return r;
268833ed 1622
89ada3ba
YW
1623 r = bus_append_kill_property(m, field, eq);
1624 if (r != 0)
1625 return r;
1626 break;
535e0d19 1627
89ada3ba 1628 case UNIT_MOUNT:
3d63c749
YW
1629 r = bus_append_cgroup_property(m, field, eq);
1630 if (r != 0)
1631 return r;
1632
1633 r = bus_append_execute_property(m, field, eq);
1634 if (r != 0)
1635 return r;
1636
1637 r = bus_append_kill_property(m, field, eq);
1638 if (r != 0)
1639 return r;
1640
1641 r = bus_append_mount_property(m, field, eq);
1642 if (r != 0)
1643 return r;
1644
1645 break;
1646
89ada3ba 1647 case UNIT_AUTOMOUNT:
3d63c749
YW
1648 r = bus_append_automount_property(m, field, eq);
1649 if (r != 0)
1650 return r;
1651
89ada3ba 1652 break;
535e0d19 1653
89ada3ba
YW
1654 case UNIT_TARGET:
1655 case UNIT_DEVICE:
1656 case UNIT_SWAP:
1657 log_error("Not supported unit type");
1658 return -EINVAL;
535e0d19 1659
89ada3ba
YW
1660 default:
1661 log_error("Invalid unit type");
20b16441
LP
1662 return -EINVAL;
1663 }
1664
89ada3ba
YW
1665 r = bus_append_unit_property(m, field, eq);
1666 if (r != 0)
1667 return r;
20b16441 1668
89ada3ba
YW
1669 log_error("Unknown assignment: %s", assignment);
1670 return -EINVAL;
20b16441
LP
1671}
1672
89ada3ba 1673int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
8673cf13
LP
1674 char **i;
1675 int r;
1676
1677 assert(m);
1678
1679 STRV_FOREACH(i, l) {
89ada3ba 1680 r = bus_append_unit_property_assignment(m, t, *i);
8673cf13
LP
1681 if (r < 0)
1682 return r;
1683 }
1684
1685 return 0;
1686}
1687
20b16441
LP
1688typedef struct BusWaitForJobs {
1689 sd_bus *bus;
1690 Set *jobs;
1691
1692 char *name;
1693 char *result;
1694
1695 sd_bus_slot *slot_job_removed;
1696 sd_bus_slot *slot_disconnected;
1697} BusWaitForJobs;
1698
1699static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1700 assert(m);
1701
1702 log_error("Warning! D-Bus connection terminated.");
1703 sd_bus_close(sd_bus_message_get_bus(m));
1704
1705 return 0;
1706}
1707
1708static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1709 const char *path, *unit, *result;
1710 BusWaitForJobs *d = userdata;
1711 uint32_t id;
1712 char *found;
1713 int r;
1714
1715 assert(m);
1716 assert(d);
1717
1718 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1719 if (r < 0) {
1720 bus_log_parse_error(r);
1721 return 0;
1722 }
1723
1724 found = set_remove(d->jobs, (char*) path);
1725 if (!found)
1726 return 0;
1727
1728 free(found);
1729
1730 if (!isempty(result))
1731 d->result = strdup(result);
1732
1733 if (!isempty(unit))
1734 d->name = strdup(unit);
1735
1736 return 0;
1737}
1738
1739void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1740 if (!d)
1741 return;
1742
1743 set_free_free(d->jobs);
1744
1745 sd_bus_slot_unref(d->slot_disconnected);
1746 sd_bus_slot_unref(d->slot_job_removed);
1747
1748 sd_bus_unref(d->bus);
1749
1750 free(d->name);
1751 free(d->result);
1752
1753 free(d);
1754}
1755
1756int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1757 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1758 int r;
1759
1760 assert(bus);
1761 assert(ret);
1762
1763 d = new0(BusWaitForJobs, 1);
1764 if (!d)
1765 return -ENOMEM;
1766
1767 d->bus = sd_bus_ref(bus);
1768
1769 /* When we are a bus client we match by sender. Direct
1770 * connections OTOH have no initialized sender field, and
1771 * hence we ignore the sender then */
75152a4d 1772 r = sd_bus_match_signal_async(
20b16441
LP
1773 bus,
1774 &d->slot_job_removed,
75152a4d
LP
1775 bus->bus_client ? "org.freedesktop.systemd1" : NULL,
1776 "/org/freedesktop/systemd1",
1777 "org.freedesktop.systemd1.Manager",
1778 "JobRemoved",
1779 match_job_removed, NULL, d);
20b16441
LP
1780 if (r < 0)
1781 return r;
1782
75152a4d 1783 r = sd_bus_match_signal_async(
20b16441
LP
1784 bus,
1785 &d->slot_disconnected,
75152a4d
LP
1786 "org.freedesktop.DBus.Local",
1787 NULL,
1788 "org.freedesktop.DBus.Local",
1789 "Disconnected",
1790 match_disconnected, NULL, d);
20b16441
LP
1791 if (r < 0)
1792 return r;
1793
1cc6c93a 1794 *ret = TAKE_PTR(d);
20b16441
LP
1795
1796 return 0;
1797}
1798
1799static int bus_process_wait(sd_bus *bus) {
1800 int r;
1801
1802 for (;;) {
1803 r = sd_bus_process(bus, NULL);
1804 if (r < 0)
1805 return r;
1806 if (r > 0)
1807 return 0;
1808
1809 r = sd_bus_wait(bus, (uint64_t) -1);
1810 if (r < 0)
1811 return r;
1812 }
1813}
1814
1815static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1816 _cleanup_free_ char *dbus_path = NULL;
1817
1818 assert(d);
1819 assert(d->name);
1820 assert(result);
1821
bd5a1c91
LP
1822 if (!endswith(d->name, ".service"))
1823 return -EINVAL;
1824
20b16441
LP
1825 dbus_path = unit_dbus_path_from_name(d->name);
1826 if (!dbus_path)
1827 return -ENOMEM;
1828
1829 return sd_bus_get_property_string(d->bus,
1830 "org.freedesktop.systemd1",
1831 dbus_path,
1832 "org.freedesktop.systemd1.Service",
1833 "Result",
1834 NULL,
1835 result);
1836}
1837
1838static const struct {
1839 const char *result, *explanation;
1840} explanations [] = {
0b2de9d9 1841 { "resources", "of unavailable resources or another system error" },
7ed0a4c5 1842 { "protocol", "the service did not take the steps required by its unit configuration" },
20b16441
LP
1843 { "timeout", "a timeout was exceeded" },
1844 { "exit-code", "the control process exited with error code" },
1845 { "signal", "a fatal signal was delivered to the control process" },
1846 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1847 { "watchdog", "the service failed to send watchdog ping" },
1848 { "start-limit", "start of the service was attempted too often" }
1849};
1850
1851static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1852 _cleanup_free_ char *service_shell_quoted = NULL;
1853 const char *systemctl = "systemctl", *journalctl = "journalctl";
1854
1855 assert(service);
1856
804ee07c 1857 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
20b16441 1858
3a58ca47 1859 if (!strv_isempty((char**) extra_args)) {
20b16441
LP
1860 _cleanup_free_ char *t;
1861
1862 t = strv_join((char**) extra_args, " ");
1863 systemctl = strjoina("systemctl ", t ? : "<args>");
1864 journalctl = strjoina("journalctl ", t ? : "<args>");
1865 }
1866
1867 if (!isempty(result)) {
1868 unsigned i;
1869
1870 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1871 if (streq(result, explanations[i].result))
1872 break;
1873
1874 if (i < ELEMENTSOF(explanations)) {
1875 log_error("Job for %s failed because %s.\n"
1876 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1877 service,
1878 explanations[i].explanation,
1879 systemctl,
1880 service_shell_quoted ?: "<service>",
1881 journalctl);
1882 goto finish;
1883 }
1884 }
1885
1886 log_error("Job for %s failed.\n"
1887 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1888 service,
1889 systemctl,
1890 service_shell_quoted ?: "<service>",
1891 journalctl);
1892
1893finish:
1894 /* For some results maybe additional explanation is required */
1895 if (streq_ptr(result, "start-limit"))
1896 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1897 "followed by \"%1$s start %2$s\" again.",
1898 systemctl,
1899 service_shell_quoted ?: "<service>");
1900}
1901
1902static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1903 int r = 0;
1904
1905 assert(d->result);
1906
1907 if (!quiet) {
1908 if (streq(d->result, "canceled"))
1909 log_error("Job for %s canceled.", strna(d->name));
1910 else if (streq(d->result, "timeout"))
1911 log_error("Job for %s timed out.", strna(d->name));
1912 else if (streq(d->result, "dependency"))
1913 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1914 else if (streq(d->result, "invalid"))
1915 log_error("%s is not active, cannot reload.", strna(d->name));
1916 else if (streq(d->result, "assert"))
1917 log_error("Assertion failed on job for %s.", strna(d->name));
1918 else if (streq(d->result, "unsupported"))
1919 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
c5a97ed1
LP
1920 else if (streq(d->result, "collected"))
1921 log_error("Queued job for %s was garbage collected.", strna(d->name));
ae835bc7 1922 else if (!STR_IN_SET(d->result, "done", "skipped")) {
20b16441 1923 if (d->name) {
20b16441 1924 _cleanup_free_ char *result = NULL;
bd5a1c91 1925 int q;
20b16441
LP
1926
1927 q = bus_job_get_service_result(d, &result);
1928 if (q < 0)
bd5a1c91 1929 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
20b16441
LP
1930
1931 log_job_error_with_service_result(d->name, result, extra_args);
1932 } else
1933 log_error("Job failed. See \"journalctl -xe\" for details.");
1934 }
1935 }
1936
c5a97ed1 1937 if (STR_IN_SET(d->result, "canceled", "collected"))
20b16441
LP
1938 r = -ECANCELED;
1939 else if (streq(d->result, "timeout"))
1940 r = -ETIME;
1941 else if (streq(d->result, "dependency"))
1942 r = -EIO;
1943 else if (streq(d->result, "invalid"))
1944 r = -ENOEXEC;
1945 else if (streq(d->result, "assert"))
1946 r = -EPROTO;
1947 else if (streq(d->result, "unsupported"))
1948 r = -EOPNOTSUPP;
ae835bc7 1949 else if (!STR_IN_SET(d->result, "done", "skipped"))
20b16441
LP
1950 r = -EIO;
1951
1952 return r;
1953}
1954
1955int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1956 int r = 0;
1957
1958 assert(d);
1959
1960 while (!set_isempty(d->jobs)) {
1961 int q;
1962
1963 q = bus_process_wait(d->bus);
1964 if (q < 0)
1965 return log_error_errno(q, "Failed to wait for response: %m");
1966
1967 if (d->result) {
1968 q = check_wait_response(d, quiet, extra_args);
1969 /* Return the first error as it is most likely to be
1970 * meaningful. */
1971 if (q < 0 && r == 0)
1972 r = q;
1973
1974 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1975 }
1976
1977 d->name = mfree(d->name);
1978 d->result = mfree(d->result);
1979 }
1980
1981 return r;
1982}
1983
1984int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1985 int r;
1986
1987 assert(d);
1988
1989 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1990 if (r < 0)
1991 return r;
1992
1993 return set_put_strdup(d->jobs, path);
1994}
1995
1996int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1997 int r;
1998
1999 r = bus_wait_for_jobs_add(d, path);
2000 if (r < 0)
2001 return log_oom();
2002
2003 return bus_wait_for_jobs(d, quiet, NULL);
2004}
2005
da6053d0 2006int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
20b16441
LP
2007 const char *type, *path, *source;
2008 int r;
2009
acc0269c
CH
2010 /* changes is dereferenced when calling unit_file_dump_changes() later,
2011 * so we have to make sure this is not NULL. */
2012 assert(changes);
2013 assert(n_changes);
2014
20b16441
LP
2015 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2016 if (r < 0)
2017 return bus_log_parse_error(r);
2018
2019 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2020 /* We expect only "success" changes to be sent over the bus.
2021 Hence, reject anything negative. */
2022 UnitFileChangeType ch = unit_file_change_type_from_string(type);
2023
2024 if (ch < 0) {
2025 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
2026 continue;
2027 }
2028
2029 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2030 if (r < 0)
2031 return r;
2032 }
2033 if (r < 0)
2034 return bus_log_parse_error(r);
2035
2036 r = sd_bus_message_exit_container(m);
2037 if (r < 0)
2038 return bus_log_parse_error(r);
2039
35d379b2 2040 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
20b16441
LP
2041 return 0;
2042}
2043
291d565a
LP
2044struct CGroupInfo {
2045 char *cgroup_path;
2046 bool is_const; /* If false, cgroup_path should be free()'d */
2047
2048 Hashmap *pids; /* PID → process name */
2049 bool done;
2050
2051 struct CGroupInfo *parent;
2052 LIST_FIELDS(struct CGroupInfo, siblings);
2053 LIST_HEAD(struct CGroupInfo, children);
2054 size_t n_children;
2055};
2056
2057static bool IS_ROOT(const char *p) {
2058 return isempty(p) || streq(p, "/");
2059}
2060
2061static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
2062 struct CGroupInfo *parent = NULL, *cg;
2063 int r;
2064
2065 assert(cgroups);
2066 assert(ret);
2067
2068 if (IS_ROOT(path))
2069 path = "/";
2070
2071 cg = hashmap_get(cgroups, path);
2072 if (cg) {
2073 *ret = cg;
2074 return 0;
2075 }
2076
2077 if (!IS_ROOT(path)) {
2078 const char *e, *pp;
2079
2080 e = strrchr(path, '/');
2081 if (!e)
2082 return -EINVAL;
2083
2084 pp = strndupa(path, e - path);
2085 if (!pp)
2086 return -ENOMEM;
2087
2088 r = add_cgroup(cgroups, pp, false, &parent);
2089 if (r < 0)
2090 return r;
2091 }
2092
2093 cg = new0(struct CGroupInfo, 1);
2094 if (!cg)
2095 return -ENOMEM;
2096
2097 if (is_const)
2098 cg->cgroup_path = (char*) path;
2099 else {
2100 cg->cgroup_path = strdup(path);
2101 if (!cg->cgroup_path) {
2102 free(cg);
2103 return -ENOMEM;
2104 }
2105 }
2106
2107 cg->is_const = is_const;
2108 cg->parent = parent;
2109
2110 r = hashmap_put(cgroups, cg->cgroup_path, cg);
2111 if (r < 0) {
2112 if (!is_const)
2113 free(cg->cgroup_path);
2114 free(cg);
2115 return r;
2116 }
2117
2118 if (parent) {
2119 LIST_PREPEND(siblings, parent->children, cg);
2120 parent->n_children++;
2121 }
2122
2123 *ret = cg;
2124 return 1;
2125}
2126
2127static int add_process(
2128 Hashmap *cgroups,
2129 const char *path,
2130 pid_t pid,
2131 const char *name) {
2132
2133 struct CGroupInfo *cg;
2134 int r;
2135
2136 assert(cgroups);
2137 assert(name);
2138 assert(pid > 0);
2139
2140 r = add_cgroup(cgroups, path, true, &cg);
2141 if (r < 0)
2142 return r;
2143
2144 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
2145 if (r < 0)
2146 return r;
2147
2148 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
2149}
2150
2151static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
2152 assert(cgroups);
2153 assert(cg);
2154
2155 while (cg->children)
2156 remove_cgroup(cgroups, cg->children);
2157
2158 hashmap_remove(cgroups, cg->cgroup_path);
2159
2160 if (!cg->is_const)
2161 free(cg->cgroup_path);
2162
2163 hashmap_free(cg->pids);
2164
2165 if (cg->parent)
2166 LIST_REMOVE(siblings, cg->parent->children, cg);
2167
2168 free(cg);
2169}
2170
2171static int cgroup_info_compare_func(const void *a, const void *b) {
2172 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
2173
2174 assert(x);
2175 assert(y);
2176
2177 return strcmp(x->cgroup_path, y->cgroup_path);
2178}
2179
2180static int dump_processes(
2181 Hashmap *cgroups,
2182 const char *cgroup_path,
2183 const char *prefix,
2184 unsigned n_columns,
2185 OutputFlags flags) {
2186
2187 struct CGroupInfo *cg;
2188 int r;
2189
2190 assert(prefix);
2191
2192 if (IS_ROOT(cgroup_path))
2193 cgroup_path = "/";
2194
2195 cg = hashmap_get(cgroups, cgroup_path);
2196 if (!cg)
2197 return 0;
2198
2199 if (!hashmap_isempty(cg->pids)) {
2200 const char *name;
2201 size_t n = 0, i;
2202 pid_t *pids;
2203 void *pidp;
2204 Iterator j;
2205 int width;
2206
2207 /* Order processes by their PID */
2208 pids = newa(pid_t, hashmap_size(cg->pids));
2209
2210 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
2211 pids[n++] = PTR_TO_PID(pidp);
2212
2213 assert(n == hashmap_size(cg->pids));
2214 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2215
2216 width = DECIMAL_STR_WIDTH(pids[n-1]);
2217
2218 for (i = 0; i < n; i++) {
2219 _cleanup_free_ char *e = NULL;
2220 const char *special;
2221 bool more;
2222
2223 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
2224 assert(name);
2225
2226 if (n_columns != 0) {
2227 unsigned k;
2228
2229 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2230
2231 e = ellipsize(name, k, 100);
2232 if (e)
2233 name = e;
2234 }
2235
2236 more = i+1 < n || cg->children;
323b7dc9 2237 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
2238
2239 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
2240 prefix,
2241 special,
2242 width, pids[i],
2243 name);
2244 }
2245 }
2246
2247 if (cg->children) {
2248 struct CGroupInfo **children, *child;
2249 size_t n = 0, i;
2250
2251 /* Order subcgroups by their name */
2252 children = newa(struct CGroupInfo*, cg->n_children);
2253 LIST_FOREACH(siblings, child, cg->children)
2254 children[n++] = child;
2255 assert(n == cg->n_children);
2256 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
2257
7351ded5
IL
2258 if (n_columns != 0)
2259 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
291d565a
LP
2260
2261 for (i = 0; i < n; i++) {
2262 _cleanup_free_ char *pp = NULL;
2263 const char *name, *special;
2264 bool more;
2265
2266 child = children[i];
2267
2268 name = strrchr(child->cgroup_path, '/');
2269 if (!name)
2270 return -EINVAL;
2271 name++;
2272
2273 more = i+1 < n;
323b7dc9 2274 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
2275
2276 fputs(prefix, stdout);
2277 fputs(special, stdout);
2278 fputs(name, stdout);
2279 fputc('\n', stdout);
2280
323b7dc9 2281 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
291d565a
LP
2282
2283 pp = strappend(prefix, special);
2284 if (!pp)
2285 return -ENOMEM;
2286
2287 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
2288 if (r < 0)
2289 return r;
2290 }
2291 }
2292
2293 cg->done = true;
2294 return 0;
2295}
2296
2297static int dump_extra_processes(
2298 Hashmap *cgroups,
2299 const char *prefix,
2300 unsigned n_columns,
2301 OutputFlags flags) {
2302
2303 _cleanup_free_ pid_t *pids = NULL;
2304 _cleanup_hashmap_free_ Hashmap *names = NULL;
2305 struct CGroupInfo *cg;
2306 size_t n_allocated = 0, n = 0, k;
2307 Iterator i;
2308 int width, r;
2309
2310 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2311 * combined, sorted, linear list. */
2312
2313 HASHMAP_FOREACH(cg, cgroups, i) {
2314 const char *name;
2315 void *pidp;
2316 Iterator j;
2317
2318 if (cg->done)
2319 continue;
2320
2321 if (hashmap_isempty(cg->pids))
2322 continue;
2323
2324 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2325 if (r < 0)
2326 return r;
2327
2328 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
2329 return -ENOMEM;
2330
2331 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
2332 pids[n++] = PTR_TO_PID(pidp);
2333
2334 r = hashmap_put(names, pidp, (void*) name);
2335 if (r < 0)
2336 return r;
2337 }
2338 }
2339
2340 if (n == 0)
2341 return 0;
2342
2343 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2344 width = DECIMAL_STR_WIDTH(pids[n-1]);
2345
2346 for (k = 0; k < n; k++) {
2347 _cleanup_free_ char *e = NULL;
2348 const char *name;
2349
2350 name = hashmap_get(names, PID_TO_PTR(pids[k]));
2351 assert(name);
2352
2353 if (n_columns != 0) {
2354 unsigned z;
2355
2356 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2357
2358 e = ellipsize(name, z, 100);
2359 if (e)
2360 name = e;
2361 }
2362
2363 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2364 prefix,
323b7dc9 2365 special_glyph(TRIANGULAR_BULLET),
291d565a
LP
2366 width, pids[k],
2367 name);
2368 }
2369
2370 return 0;
2371}
2372
2373int unit_show_processes(
2374 sd_bus *bus,
2375 const char *unit,
2376 const char *cgroup_path,
2377 const char *prefix,
2378 unsigned n_columns,
2379 OutputFlags flags,
2380 sd_bus_error *error) {
2381
2382 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2383 Hashmap *cgroups = NULL;
2384 struct CGroupInfo *cg;
2385 int r;
2386
2387 assert(bus);
2388 assert(unit);
2389
2390 if (flags & OUTPUT_FULL_WIDTH)
2391 n_columns = 0;
2392 else if (n_columns <= 0)
2393 n_columns = columns();
2394
2395 prefix = strempty(prefix);
2396
2397 r = sd_bus_call_method(
2398 bus,
2399 "org.freedesktop.systemd1",
2400 "/org/freedesktop/systemd1",
2401 "org.freedesktop.systemd1.Manager",
2402 "GetUnitProcesses",
2403 error,
2404 &reply,
2405 "s",
2406 unit);
2407 if (r < 0)
2408 return r;
2409
548f6937 2410 cgroups = hashmap_new(&path_hash_ops);
291d565a
LP
2411 if (!cgroups)
2412 return -ENOMEM;
2413
2414 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
2415 if (r < 0)
2416 goto finish;
2417
2418 for (;;) {
2419 const char *path = NULL, *name = NULL;
2420 uint32_t pid;
2421
2422 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
2423 if (r < 0)
2424 goto finish;
2425 if (r == 0)
2426 break;
2427
2428 r = add_process(cgroups, path, pid, name);
2429 if (r < 0)
2430 goto finish;
2431 }
2432
2433 r = sd_bus_message_exit_container(reply);
2434 if (r < 0)
2435 goto finish;
2436
2437 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
2438 if (r < 0)
2439 goto finish;
2440
2441 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2442
2443finish:
2444 while ((cg = hashmap_first(cgroups)))
2445 remove_cgroup(cgroups, cg);
2446
2447 hashmap_free(cgroups);
2448
2449 return r;
2450}
bd062910
ZJS
2451
2452int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2453 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2454 _cleanup_free_ char *path = NULL;
2455 int r;
2456
2457 path = unit_dbus_path_from_name(name);
2458 if (!path)
2459 return log_oom();
2460
2461 /* This function warns on it's own, because otherwise it'd be awkward to pass
2462 * the dbus error message around. */
2463
2464 r = sd_bus_get_property_string(
2465 bus,
2466 "org.freedesktop.systemd1",
2467 path,
2468 "org.freedesktop.systemd1.Unit",
2469 "LoadState",
2470 &error,
2471 load_state);
2472 if (r < 0)
2473 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2474
2475 return 0;
2476}