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