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