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