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