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