]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
tree-wide: install matches asynchronously
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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"
25 #include "cap-list.h"
26 #include "cgroup-util.h"
27 #include "condition.h"
28 #include "cpu-set-util.h"
29 #include "env-util.h"
30 #include "errno-list.h"
31 #include "escape.h"
32 #include "hashmap.h"
33 #include "hexdecoct.h"
34 #include "hostname-util.h"
35 #include "in-addr-util.h"
36 #include "list.h"
37 #include "locale-util.h"
38 #include "mount-util.h"
39 #include "nsflags.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "process-util.h"
43 #include "rlimit-util.h"
44 #include "securebits-util.h"
45 #include "signal-util.h"
46 #include "socket-protocol-list.h"
47 #include "string-util.h"
48 #include "syslog-util.h"
49 #include "terminal-util.h"
50 #include "unit-def.h"
51 #include "user-util.h"
52 #include "utf8.h"
53 #include "util.h"
54
55 int 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
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__
96
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__
118
119 DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
120 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
121 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
122 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
123 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
124 DEFINE_BUS_APPEND_PARSE("i", parse_errno);
125 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
126 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
127 DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder);
128 DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name);
129 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
130 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
131 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
132 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
133 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
134 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
135 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
136 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
137 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
138 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
139 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
140 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
141
142 static 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);
146 if (r < 0)
147 return bus_log_create_error(r);
148
149 return 1;
150 }
151
152 static 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");
157 if (r < 0)
158 return bus_log_create_error(r);
159
160 r = sd_bus_message_append_basic(m, 's', field);
161 if (r < 0)
162 return bus_log_create_error(r);
163
164 r = sd_bus_message_open_container(m, 'v', "as");
165 if (r < 0)
166 return bus_log_create_error(r);
167
168 r = sd_bus_message_open_container(m, 'a', "s");
169 if (r < 0)
170 return bus_log_create_error(r);
171
172 for (p = eq;;) {
173 _cleanup_free_ char *word = NULL;
174
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);
182
183 r = sd_bus_message_append_basic(m, 's', word);
184 if (r < 0)
185 return bus_log_create_error(r);
186 }
187
188 r = sd_bus_message_close_container(m);
189 if (r < 0)
190 return bus_log_create_error(r);
191
192 r = sd_bus_message_close_container(m);
193 if (r < 0)
194 return bus_log_create_error(r);
195
196 r = sd_bus_message_close_container(m);
197 if (r < 0)
198 return bus_log_create_error(r);
199
200 return 1;
201 }
202
203 static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
204 int r;
205
206 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
207 if (r < 0)
208 return bus_log_create_error(r);
209
210 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
211 if (r < 0)
212 return bus_log_create_error(r);
213
214 r = sd_bus_message_open_container(m, 'v', "ay");
215 if (r < 0)
216 return bus_log_create_error(r);
217
218 r = sd_bus_message_append_array(m, 'y', buf, n);
219 if (r < 0)
220 return bus_log_create_error(r);
221
222 r = sd_bus_message_close_container(m);
223 if (r < 0)
224 return bus_log_create_error(r);
225
226 r = sd_bus_message_close_container(m);
227 if (r < 0)
228 return bus_log_create_error(r);
229
230 return 1;
231 }
232
233 static 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;
238
239 r = parse_sec(eq, &t);
240 if (r < 0)
241 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
242
243 l = strlen(field);
244 n = newa(char, l + 2);
245 /* Change suffix Sec → USec */
246 strcpy(mempcpy(n, field, l - 3), "USec");
247
248 r = sd_bus_message_append(m, "(sv)", n, "t", t);
249 if (r < 0)
250 return bus_log_create_error(r);
251
252 return 1;
253 }
254
255 static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
256 uint64_t v;
257 int r;
258
259 r = parse_size(eq, base, &v);
260 if (r < 0)
261 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
262
263 r = sd_bus_message_append(m, "(sv)", field, "t", v);
264 if (r < 0)
265 return bus_log_create_error(r);
266
267 return 1;
268 }
269
270 static 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;
275
276 do {
277 switch (*eq) {
278
279 case '-':
280 if (ignore_failure)
281 done = true;
282 else {
283 ignore_failure = true;
284 eq++;
285 }
286 break;
287
288 case '@':
289 if (explicit_path)
290 done = true;
291 else {
292 explicit_path = true;
293 eq++;
294 }
295 break;
296
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;
302
303 default:
304 done = true;
305 break;
306 }
307 } while (!done);
308
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 }
314
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");
318
319 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
320 if (r < 0)
321 return bus_log_create_error(r);
322
323 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
324 if (r < 0)
325 return bus_log_create_error(r);
326
327 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
328 if (r < 0)
329 return bus_log_create_error(r);
330
331 r = sd_bus_message_open_container(m, 'a', "(sasb)");
332 if (r < 0)
333 return bus_log_create_error(r);
334
335 if (!strv_isempty(l)) {
336
337 r = sd_bus_message_open_container(m, 'r', "sasb");
338 if (r < 0)
339 return bus_log_create_error(r);
340
341 r = sd_bus_message_append(m, "s", path ?: l[0]);
342 if (r < 0)
343 return bus_log_create_error(r);
344
345 r = sd_bus_message_append_strv(m, l);
346 if (r < 0)
347 return bus_log_create_error(r);
348
349 r = sd_bus_message_append(m, "b", ignore_failure);
350 if (r < 0)
351 return bus_log_create_error(r);
352
353 r = sd_bus_message_close_container(m);
354 if (r < 0)
355 return bus_log_create_error(r);
356 }
357
358 r = sd_bus_message_close_container(m);
359 if (r < 0)
360 return bus_log_create_error(r);
361
362 r = sd_bus_message_close_container(m);
363 if (r < 0)
364 return bus_log_create_error(r);
365
366 r = sd_bus_message_close_container(m);
367 if (r < 0)
368 return bus_log_create_error(r);
369
370 return 1;
371 }
372
373 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
374 int r;
375
376 assert(m);
377 assert(prefix);
378
379 r = sd_bus_message_open_container(m, 'r', "iayu");
380 if (r < 0)
381 return r;
382
383 r = sd_bus_message_append(m, "i", family);
384 if (r < 0)
385 return r;
386
387 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
388 if (r < 0)
389 return r;
390
391 r = sd_bus_message_append(m, "u", prefixlen);
392 if (r < 0)
393 return r;
394
395 return sd_bus_message_close_container(m);
396 }
397
398 static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
399 int r;
400
401 if (STR_IN_SET(field, "DevicePolicy", "Slice"))
402
403 return bus_append_string(m, field, eq);
404
405 if (STR_IN_SET(field,
406 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
407 "TasksAccounting", "IPAccounting"))
408
409 return bus_append_parse_boolean(m, field, eq);
410
411 if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
412
413 return bus_append_cg_weight_parse(m, field, eq);
414
415 if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
416
417 return bus_append_cg_cpu_shares_parse(m, field, eq);
418
419 if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
420
421 return bus_append_cg_blkio_weight_parse(m, field, eq);
422
423 if (streq(field, "Delegate")) {
424
425 r = parse_boolean(eq);
426 if (r < 0)
427 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES);
428
429 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
430 if (r < 0)
431 return bus_log_create_error(r);
432
433 return 1;
434 }
435
436 if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
437
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;
443 }
444
445 r = parse_percent(eq);
446 if (r >= 0) {
447 char *n;
448
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 */
452
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);
457
458 return 1;
459 }
460
461 if (streq(field, "TasksMax"))
462 return bus_append_safe_atou64(m, field, eq);
463
464 return bus_append_parse_size(m, field, eq, 1024);
465
466 }
467
468 if (streq(field, "CPUQuota")) {
469
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);
480 }
481
482 if (r < 0)
483 return bus_log_create_error(r);
484
485 return 1;
486 }
487
488 if (streq(field, "DeviceAllow")) {
489
490 if (isempty(eq))
491 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
492 else {
493 const char *path = eq, *rwm = NULL, *e;
494
495 e = strchr(eq, ' ');
496 if (e) {
497 path = strndupa(eq, e - eq);
498 rwm = e+1;
499 }
500
501 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
502 }
503
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")) {
511
512 if (isempty(eq))
513 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
514 else {
515 const char *path, *bandwidth, *e;
516 uint64_t bytes;
517
518 e = strchr(eq, ' ');
519 if (!e) {
520 log_error("Failed to parse %s value %s.", field, eq);
521 return -EINVAL;
522 }
523
524 path = strndupa(eq, e - eq);
525 bandwidth = e+1;
526
527 if (streq(bandwidth, "infinity")) {
528 bytes = CGROUP_LIMIT_MAX;
529 } else {
530 r = parse_size(bandwidth, 1000, &bytes);
531 if (r < 0)
532 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
533 }
534
535 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
536 }
537
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")) {
545
546 if (isempty(eq))
547 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
548 else {
549 const char *path, *weight, *e;
550 uint64_t u;
551
552 e = strchr(eq, ' ');
553 if (!e) {
554 log_error("Failed to parse %s value %s.", field, eq);
555 return -EINVAL;
556 }
557
558 path = strndupa(eq, e - eq);
559 weight = e+1;
560
561 r = safe_atou64(weight, &u);
562 if (r < 0)
563 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
564
565 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
566 }
567
568 if (r < 0)
569 return bus_log_create_error(r);
570
571 return 1;
572 }
573
574 if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
575 unsigned char prefixlen;
576 union in_addr_union prefix = {};
577 int family;
578
579 if (isempty(eq)) {
580 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
581 if (r < 0)
582 return bus_log_create_error(r);
583
584 return 1;
585 }
586
587 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
588 if (r < 0)
589 return bus_log_create_error(r);
590
591 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
592 if (r < 0)
593 return bus_log_create_error(r);
594
595 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
596 if (r < 0)
597 return bus_log_create_error(r);
598
599 r = sd_bus_message_open_container(m, 'a', "(iayu)");
600 if (r < 0)
601 return bus_log_create_error(r);
602
603 if (streq(eq, "any")) {
604 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
605
606 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
607 if (r < 0)
608 return bus_log_create_error(r);
609
610 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
611 if (r < 0)
612 return bus_log_create_error(r);
613
614 } else if (is_localhost(eq)) {
615 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
616
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);
621
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;
626
627 } else if (streq(eq, "link-local")) {
628 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
629
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);
634
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);
641
642 } else if (streq(eq, "multicast")) {
643 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
644
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);
649
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);
654 if (r < 0)
655 return bus_log_create_error(r);
656
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);
663 if (r < 0)
664 return bus_log_create_error(r);
665 }
666
667 r = sd_bus_message_close_container(m);
668 if (r < 0)
669 return bus_log_create_error(r);
670
671 r = sd_bus_message_close_container(m);
672 if (r < 0)
673 return bus_log_create_error(r);
674
675 r = sd_bus_message_close_container(m);
676 if (r < 0)
677 return bus_log_create_error(r);
678
679 return 1;
680 }
681
682 return 0;
683 }
684
685 static 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
702 static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
703 int r, rl;
704
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"))
711
712 return bus_append_string(m, field, eq);
713
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"))
721
722 return bus_append_parse_boolean(m, field, eq);
723
724 if (STR_IN_SET(field,
725 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
726 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
727 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
728 "SupplementaryGroups", "SystemCallArchitectures"))
729
730 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
731
732 if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
733
734 return bus_append_log_level_from_string(m, field, eq);
735
736 if (streq(field, "SyslogFacility"))
737
738 return bus_append_log_facility_unshifted_from_string(m, field, eq);
739
740 if (streq(field, "SecureBits"))
741
742 return bus_append_secure_bits_from_string(m, field, eq);
743
744 if (streq(field, "CPUSchedulingPolicy"))
745
746 return bus_append_sched_policy_from_string(m, field, eq);
747
748 if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
749
750 return bus_append_safe_atoi(m, field, eq);
751
752 if (streq(field, "Nice"))
753
754 return bus_append_parse_nice(m, field, eq);
755
756 if (streq(field, "SystemCallErrorNumber"))
757
758 return bus_append_parse_errno(m, field, eq);
759
760 if (streq(field, "IOSchedulingClass"))
761
762 return bus_append_ioprio_class_from_string(m, field, eq);
763
764 if (streq(field, "IOSchedulingPriority"))
765
766 return bus_append_ioprio_parse_priority(m, field, eq);
767
768 if (STR_IN_SET(field,
769 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
770 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
771
772 return bus_append_parse_mode(m, field, eq);
773
774 if (streq(field, "TimerSlackNSec"))
775
776 return bus_append_parse_nsec(m, field, eq);
777
778 if (streq(field, "MountFlags"))
779
780 return bus_append_mount_propagation_flags_from_string(m, field, eq);
781
782 if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
783
784 return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
785
786 if (streq(field, "EnvironmentFile")) {
787
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);
796
797 return 1;
798 }
799
800 if (streq(field, "LogExtraFields")) {
801
802 r = sd_bus_message_open_container(m, 'r', "sv");
803 if (r < 0)
804 return bus_log_create_error(r);
805
806 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
807 if (r < 0)
808 return bus_log_create_error(r);
809
810 r = sd_bus_message_open_container(m, 'v', "aay");
811 if (r < 0)
812 return bus_log_create_error(r);
813
814 r = sd_bus_message_open_container(m, 'a', "ay");
815 if (r < 0)
816 return bus_log_create_error(r);
817
818 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
819 if (r < 0)
820 return bus_log_create_error(r);
821
822 r = sd_bus_message_close_container(m);
823 if (r < 0)
824 return bus_log_create_error(r);
825
826 r = sd_bus_message_close_container(m);
827 if (r < 0)
828 return bus_log_create_error(r);
829
830 r = sd_bus_message_close_container(m);
831 if (r < 0)
832 return bus_log_create_error(r);
833
834 return 1;
835 }
836
837 if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
838 const char *n, *appended;
839
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);
848
849 if (r < 0)
850 return bus_log_create_error(r);
851
852 return 1;
853 }
854
855 if (streq(field, "StandardInputText")) {
856 _cleanup_free_ char *unescaped = NULL;
857
858 r = cunescape(eq, 0, &unescaped);
859 if (r < 0)
860 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
861
862 if (!strextend(&unescaped, "\n", NULL))
863 return log_oom();
864
865 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
866 * interface anyway */
867
868 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
869 }
870
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);
876 if (r < 0)
877 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
878
879 return bus_append_byte_array(m, field, decoded, sz);
880 }
881
882 rl = rlimit_from_string(field);
883 if (rl >= 0) {
884 const char *sn;
885 struct rlimit l;
886
887 r = rlimit_parse(rl, eq, &l);
888 if (r < 0)
889 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
890
891 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
892 if (r < 0)
893 return bus_log_create_error(r);
894
895 sn = strjoina(field, "Soft");
896 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
897 if (r < 0)
898 return bus_log_create_error(r);
899
900 return 1;
901 }
902
903 if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
904 int ignore = 0;
905 const char *s = eq;
906
907 if (eq[0] == '-') {
908 ignore = 1;
909 s = eq + 1;
910 }
911
912 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
913 if (r < 0)
914 return bus_log_create_error(r);
915
916 return 1;
917 }
918
919 if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
920 uint64_t sum = 0;
921 bool invert = false;
922 const char *p = eq;
923
924 if (*p == '~') {
925 invert = true;
926 p++;
927 }
928
929 r = capability_set_from_string(p, &sum);
930 if (r < 0)
931 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
932
933 sum = invert ? ~sum : sum;
934
935 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
936 if (r < 0)
937 return bus_log_create_error(r);
938
939 return 1;
940 }
941
942 if (streq(field, "CPUAffinity")) {
943 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
944
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);
948
949 return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
950 }
951
952 if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
953 int whitelist = 1;
954 const char *p = eq;
955
956 if (*p == '~') {
957 whitelist = 0;
958 p++;
959 }
960
961 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
962 if (r < 0)
963 return bus_log_create_error(r);
964
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);
972
973 r = sd_bus_message_open_container(m, 'r', "bas");
974 if (r < 0)
975 return bus_log_create_error(r);
976
977 r = sd_bus_message_append_basic(m, 'b', &whitelist);
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);
989 if (r == 0)
990 break;
991 if (r == -ENOMEM)
992 return log_oom();
993 if (r < 0)
994 return log_error_errno(r, "Invalid syntax: %s", eq);
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);
1006 if (r < 0)
1007 return bus_log_create_error(r);
1008
1009 r = sd_bus_message_close_container(m);
1010 if (r < 0)
1011 return bus_log_create_error(r);
1012
1013 r = sd_bus_message_close_container(m);
1014 if (r < 0)
1015 return bus_log_create_error(r);
1016
1017 return 1;
1018 }
1019
1020 if (streq(field, "RestrictNamespaces")) {
1021 bool invert = false;
1022 unsigned long flags = 0;
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
1043 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1044 if (r < 0)
1045 return bus_log_create_error(r);
1046
1047 return 1;
1048 }
1049
1050 if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1051 const char *p = eq;
1052
1053 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1054 if (r < 0)
1055 return bus_log_create_error(r);
1056
1057 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1058 if (r < 0)
1059 return bus_log_create_error(r);
1060
1061 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1062 if (r < 0)
1063 return bus_log_create_error(r);
1064
1065 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1066 if (r < 0)
1067 return bus_log_create_error(r);
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)
1120 return bus_log_create_error(r);
1121 }
1122
1123 r = sd_bus_message_close_container(m);
1124 if (r < 0)
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);
1130
1131 r = sd_bus_message_close_container(m);
1132 if (r < 0)
1133 return bus_log_create_error(r);
1134
1135 return 1;
1136 }
1137
1138 return 0;
1139 }
1140
1141 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
1142
1143 if (streq(field, "KillMode"))
1144
1145 return bus_append_string(m, field, eq);
1146
1147 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
1148
1149 return bus_append_parse_boolean(m, field, eq);
1150
1151 if (streq(field, "KillSignal"))
1152
1153 return bus_append_signal_from_string_try_harder(m, field, eq);
1154
1155 return 0;
1156 }
1157
1158 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
1159
1160 if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
1161
1162 return bus_append_string(m, field, eq);
1163
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
1179 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1180 int r;
1181
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
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
1204 return 0;
1205 }
1206
1207 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
1208 int r;
1209
1210 if (STR_IN_SET(field,
1211 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1212 "USBFunctionDescriptors", "USBFunctionStrings"))
1213
1214 return bus_append_string(m, field, eq);
1215
1216 if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
1217
1218 return bus_append_parse_boolean(m, field, eq);
1219
1220 if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
1221
1222 return bus_append_parse_sec_rename(m, field, eq);
1223
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
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
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
1319 return 0;
1320 }
1321
1322 static 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
1357 return bus_append_parse_size(m, field, eq, 1024);
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
1374 if (streq(field, "SocketProtocol"))
1375
1376 return bus_append_socket_protocol_from_name(m, field, eq);
1377
1378 if (STR_IN_SET(field,
1379 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1380 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1381
1382 if (isempty(eq))
1383 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1384 else
1385 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + strlen("Listen"), eq);
1386 if (r < 0)
1387 return bus_log_create_error(r);
1388
1389 return 1;
1390 }
1391
1392 return 0;
1393 }
1394 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
1395 int r;
1396
1397 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
1398
1399 return bus_append_parse_boolean(m, field, eq);
1400
1401 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1402
1403 return bus_append_parse_sec_rename(m, field, eq);
1404
1405 if (STR_IN_SET(field,
1406 "OnActiveSec", "OnBootSec", "OnStartupSec",
1407 "OnUnitActiveSec","OnUnitInactiveSec")) {
1408
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);
1416
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);
1421
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 }
1436
1437 return 0;
1438 }
1439
1440 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
1441 ConditionType t = _CONDITION_TYPE_INVALID;
1442 bool is_condition = false;
1443 int r;
1444
1445 if (STR_IN_SET(field,
1446 "Description", "SourcePath", "OnFailureJobMode",
1447 "JobTimeoutAction", "JobTimeoutRebootArgument",
1448 "StartLimitAction", "FailureAction", "SuccessAction",
1449 "RebootArgument", "CollectMode"))
1450
1451 return bus_append_string(m, field, eq);
1452
1453 if (STR_IN_SET(field,
1454 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1455 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
1456
1457 return bus_append_parse_boolean(m, field, eq);
1458
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"))
1469
1470 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1471
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
1501 return 0;
1502 }
1503
1504 int 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)
1524 return r;
1525
1526 r = bus_append_execute_property(m, field, eq);
1527 if (r != 0)
1528 return r;
1529
1530 r = bus_append_kill_property(m, field, eq);
1531 if (r != 0)
1532 return r;
1533
1534 r = bus_append_service_property(m, field, eq);
1535 if (r != 0)
1536 return r;
1537 break;
1538
1539 case UNIT_SOCKET:
1540 r = bus_append_cgroup_property(m, field, eq);
1541 if (r != 0)
1542 return r;
1543
1544 r = bus_append_execute_property(m, field, eq);
1545 if (r != 0)
1546 return r;
1547
1548 r = bus_append_kill_property(m, field, eq);
1549 if (r != 0)
1550 return r;
1551
1552 r = bus_append_socket_property(m, field, eq);
1553 if (r != 0)
1554 return r;
1555 break;
1556
1557 case UNIT_TIMER:
1558 r = bus_append_timer_property(m, field, eq);
1559 if (r != 0)
1560 return r;
1561 break;
1562
1563 case UNIT_PATH:
1564 r = bus_append_path_property(m, field, eq);
1565 if (r != 0)
1566 return r;
1567 break;
1568
1569 case UNIT_SLICE:
1570 r = bus_append_cgroup_property(m, field, eq);
1571 if (r != 0)
1572 return r;
1573 break;
1574
1575 case UNIT_SCOPE:
1576
1577 if (streq(field, "TimeoutStopSec"))
1578 return bus_append_parse_sec_rename(m, field, eq);
1579
1580 r = bus_append_cgroup_property(m, field, eq);
1581 if (r != 0)
1582 return r;
1583
1584 r = bus_append_kill_property(m, field, eq);
1585 if (r != 0)
1586 return r;
1587 break;
1588
1589 case UNIT_MOUNT:
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
1608 case UNIT_AUTOMOUNT:
1609 r = bus_append_automount_property(m, field, eq);
1610 if (r != 0)
1611 return r;
1612
1613 break;
1614
1615 case UNIT_TARGET:
1616 case UNIT_DEVICE:
1617 case UNIT_SWAP:
1618 log_error("Not supported unit type");
1619 return -EINVAL;
1620
1621 default:
1622 log_error("Invalid unit type");
1623 return -EINVAL;
1624 }
1625
1626 r = bus_append_unit_property(m, field, eq);
1627 if (r != 0)
1628 return r;
1629
1630 log_error("Unknown assignment: %s", assignment);
1631 return -EINVAL;
1632 }
1633
1634 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
1635 char **i;
1636 int r;
1637
1638 assert(m);
1639
1640 STRV_FOREACH(i, l) {
1641 r = bus_append_unit_property_assignment(m, t, *i);
1642 if (r < 0)
1643 return r;
1644 }
1645
1646 return 0;
1647 }
1648
1649 typedef 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
1660 static 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
1669 static 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
1700 void 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
1717 int 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 */
1733 r = sd_bus_match_signal_async(
1734 bus,
1735 &d->slot_job_removed,
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);
1741 if (r < 0)
1742 return r;
1743
1744 r = sd_bus_match_signal_async(
1745 bus,
1746 &d->slot_disconnected,
1747 "org.freedesktop.DBus.Local",
1748 NULL,
1749 "org.freedesktop.DBus.Local",
1750 "Disconnected",
1751 match_disconnected, NULL, d);
1752 if (r < 0)
1753 return r;
1754
1755 *ret = d;
1756 d = NULL;
1757
1758 return 0;
1759 }
1760
1761 static 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
1777 static 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
1784 if (!endswith(d->name, ".service"))
1785 return -EINVAL;
1786
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
1800 static const struct {
1801 const char *result, *explanation;
1802 } explanations [] = {
1803 { "resources", "of unavailable resources or another system error" },
1804 { "protocol", "the service did not take the steps required by its unit configuration" },
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
1813 static 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
1819 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1820
1821 if (!strv_isempty((char**) extra_args)) {
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
1855 finish:
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
1864 static 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));
1882 else if (streq(d->result, "collected"))
1883 log_error("Queued job for %s was garbage collected.", strna(d->name));
1884 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1885 if (d->name) {
1886 _cleanup_free_ char *result = NULL;
1887 int q;
1888
1889 q = bus_job_get_service_result(d, &result);
1890 if (q < 0)
1891 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
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
1899 if (STR_IN_SET(d->result, "canceled", "collected"))
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;
1911 else if (!STR_IN_SET(d->result, "done", "skipped"))
1912 r = -EIO;
1913
1914 return r;
1915 }
1916
1917 int 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
1946 int 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
1958 int 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
1968 int 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
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
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
2002 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
2003 return 0;
2004 }
2005
2006 struct 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
2019 static bool IS_ROOT(const char *p) {
2020 return isempty(p) || streq(p, "/");
2021 }
2022
2023 static 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
2089 static 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
2113 static 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
2133 static 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
2142 static 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;
2199 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
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
2220 if (n_columns != 0)
2221 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
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;
2236 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
2237
2238 fputs(prefix, stdout);
2239 fputs(special, stdout);
2240 fputs(name, stdout);
2241 fputc('\n', stdout);
2242
2243 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
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
2259 static 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,
2327 special_glyph(TRIANGULAR_BULLET),
2328 width, pids[k],
2329 name);
2330 }
2331
2332 return 0;
2333 }
2334
2335 int 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
2372 cgroups = hashmap_new(&string_hash_ops);
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
2405 finish:
2406 while ((cg = hashmap_first(cgroups)))
2407 remove_cgroup(cgroups, cg);
2408
2409 hashmap_free(cgroups);
2410
2411 return r;
2412 }