]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
a9c17d29e2a0a25813539f179f661107a674b254
[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 = TAKE_PTR(d);
1809
1810 return 0;
1811 }
1812
1813 static int bus_process_wait(sd_bus *bus) {
1814 int r;
1815
1816 for (;;) {
1817 r = sd_bus_process(bus, NULL);
1818 if (r < 0)
1819 return r;
1820 if (r > 0)
1821 return 0;
1822
1823 r = sd_bus_wait(bus, (uint64_t) -1);
1824 if (r < 0)
1825 return r;
1826 }
1827 }
1828
1829 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1830 _cleanup_free_ char *dbus_path = NULL;
1831
1832 assert(d);
1833 assert(d->name);
1834 assert(result);
1835
1836 if (!endswith(d->name, ".service"))
1837 return -EINVAL;
1838
1839 dbus_path = unit_dbus_path_from_name(d->name);
1840 if (!dbus_path)
1841 return -ENOMEM;
1842
1843 return sd_bus_get_property_string(d->bus,
1844 "org.freedesktop.systemd1",
1845 dbus_path,
1846 "org.freedesktop.systemd1.Service",
1847 "Result",
1848 NULL,
1849 result);
1850 }
1851
1852 static const struct {
1853 const char *result, *explanation;
1854 } explanations [] = {
1855 { "resources", "of unavailable resources or another system error" },
1856 { "protocol", "the service did not take the steps required by its unit configuration" },
1857 { "timeout", "a timeout was exceeded" },
1858 { "exit-code", "the control process exited with error code" },
1859 { "signal", "a fatal signal was delivered to the control process" },
1860 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1861 { "watchdog", "the service failed to send watchdog ping" },
1862 { "start-limit", "start of the service was attempted too often" }
1863 };
1864
1865 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1866 _cleanup_free_ char *service_shell_quoted = NULL;
1867 const char *systemctl = "systemctl", *journalctl = "journalctl";
1868
1869 assert(service);
1870
1871 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1872
1873 if (!strv_isempty((char**) extra_args)) {
1874 _cleanup_free_ char *t;
1875
1876 t = strv_join((char**) extra_args, " ");
1877 systemctl = strjoina("systemctl ", t ? : "<args>");
1878 journalctl = strjoina("journalctl ", t ? : "<args>");
1879 }
1880
1881 if (!isempty(result)) {
1882 unsigned i;
1883
1884 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1885 if (streq(result, explanations[i].result))
1886 break;
1887
1888 if (i < ELEMENTSOF(explanations)) {
1889 log_error("Job for %s failed because %s.\n"
1890 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1891 service,
1892 explanations[i].explanation,
1893 systemctl,
1894 service_shell_quoted ?: "<service>",
1895 journalctl);
1896 goto finish;
1897 }
1898 }
1899
1900 log_error("Job for %s failed.\n"
1901 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1902 service,
1903 systemctl,
1904 service_shell_quoted ?: "<service>",
1905 journalctl);
1906
1907 finish:
1908 /* For some results maybe additional explanation is required */
1909 if (streq_ptr(result, "start-limit"))
1910 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1911 "followed by \"%1$s start %2$s\" again.",
1912 systemctl,
1913 service_shell_quoted ?: "<service>");
1914 }
1915
1916 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1917 int r = 0;
1918
1919 assert(d->result);
1920
1921 if (!quiet) {
1922 if (streq(d->result, "canceled"))
1923 log_error("Job for %s canceled.", strna(d->name));
1924 else if (streq(d->result, "timeout"))
1925 log_error("Job for %s timed out.", strna(d->name));
1926 else if (streq(d->result, "dependency"))
1927 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1928 else if (streq(d->result, "invalid"))
1929 log_error("%s is not active, cannot reload.", strna(d->name));
1930 else if (streq(d->result, "assert"))
1931 log_error("Assertion failed on job for %s.", strna(d->name));
1932 else if (streq(d->result, "unsupported"))
1933 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1934 else if (streq(d->result, "collected"))
1935 log_error("Queued job for %s was garbage collected.", strna(d->name));
1936 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1937 if (d->name) {
1938 _cleanup_free_ char *result = NULL;
1939 int q;
1940
1941 q = bus_job_get_service_result(d, &result);
1942 if (q < 0)
1943 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1944
1945 log_job_error_with_service_result(d->name, result, extra_args);
1946 } else
1947 log_error("Job failed. See \"journalctl -xe\" for details.");
1948 }
1949 }
1950
1951 if (STR_IN_SET(d->result, "canceled", "collected"))
1952 r = -ECANCELED;
1953 else if (streq(d->result, "timeout"))
1954 r = -ETIME;
1955 else if (streq(d->result, "dependency"))
1956 r = -EIO;
1957 else if (streq(d->result, "invalid"))
1958 r = -ENOEXEC;
1959 else if (streq(d->result, "assert"))
1960 r = -EPROTO;
1961 else if (streq(d->result, "unsupported"))
1962 r = -EOPNOTSUPP;
1963 else if (!STR_IN_SET(d->result, "done", "skipped"))
1964 r = -EIO;
1965
1966 return r;
1967 }
1968
1969 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1970 int r = 0;
1971
1972 assert(d);
1973
1974 while (!set_isempty(d->jobs)) {
1975 int q;
1976
1977 q = bus_process_wait(d->bus);
1978 if (q < 0)
1979 return log_error_errno(q, "Failed to wait for response: %m");
1980
1981 if (d->result) {
1982 q = check_wait_response(d, quiet, extra_args);
1983 /* Return the first error as it is most likely to be
1984 * meaningful. */
1985 if (q < 0 && r == 0)
1986 r = q;
1987
1988 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1989 }
1990
1991 d->name = mfree(d->name);
1992 d->result = mfree(d->result);
1993 }
1994
1995 return r;
1996 }
1997
1998 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1999 int r;
2000
2001 assert(d);
2002
2003 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
2004 if (r < 0)
2005 return r;
2006
2007 return set_put_strdup(d->jobs, path);
2008 }
2009
2010 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
2011 int r;
2012
2013 r = bus_wait_for_jobs_add(d, path);
2014 if (r < 0)
2015 return log_oom();
2016
2017 return bus_wait_for_jobs(d, quiet, NULL);
2018 }
2019
2020 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
2021 const char *type, *path, *source;
2022 int r;
2023
2024 /* changes is dereferenced when calling unit_file_dump_changes() later,
2025 * so we have to make sure this is not NULL. */
2026 assert(changes);
2027 assert(n_changes);
2028
2029 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2030 if (r < 0)
2031 return bus_log_parse_error(r);
2032
2033 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2034 /* We expect only "success" changes to be sent over the bus.
2035 Hence, reject anything negative. */
2036 UnitFileChangeType ch = unit_file_change_type_from_string(type);
2037
2038 if (ch < 0) {
2039 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
2040 continue;
2041 }
2042
2043 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2044 if (r < 0)
2045 return r;
2046 }
2047 if (r < 0)
2048 return bus_log_parse_error(r);
2049
2050 r = sd_bus_message_exit_container(m);
2051 if (r < 0)
2052 return bus_log_parse_error(r);
2053
2054 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
2055 return 0;
2056 }
2057
2058 struct CGroupInfo {
2059 char *cgroup_path;
2060 bool is_const; /* If false, cgroup_path should be free()'d */
2061
2062 Hashmap *pids; /* PID → process name */
2063 bool done;
2064
2065 struct CGroupInfo *parent;
2066 LIST_FIELDS(struct CGroupInfo, siblings);
2067 LIST_HEAD(struct CGroupInfo, children);
2068 size_t n_children;
2069 };
2070
2071 static bool IS_ROOT(const char *p) {
2072 return isempty(p) || streq(p, "/");
2073 }
2074
2075 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
2076 struct CGroupInfo *parent = NULL, *cg;
2077 int r;
2078
2079 assert(cgroups);
2080 assert(ret);
2081
2082 if (IS_ROOT(path))
2083 path = "/";
2084
2085 cg = hashmap_get(cgroups, path);
2086 if (cg) {
2087 *ret = cg;
2088 return 0;
2089 }
2090
2091 if (!IS_ROOT(path)) {
2092 const char *e, *pp;
2093
2094 e = strrchr(path, '/');
2095 if (!e)
2096 return -EINVAL;
2097
2098 pp = strndupa(path, e - path);
2099 if (!pp)
2100 return -ENOMEM;
2101
2102 r = add_cgroup(cgroups, pp, false, &parent);
2103 if (r < 0)
2104 return r;
2105 }
2106
2107 cg = new0(struct CGroupInfo, 1);
2108 if (!cg)
2109 return -ENOMEM;
2110
2111 if (is_const)
2112 cg->cgroup_path = (char*) path;
2113 else {
2114 cg->cgroup_path = strdup(path);
2115 if (!cg->cgroup_path) {
2116 free(cg);
2117 return -ENOMEM;
2118 }
2119 }
2120
2121 cg->is_const = is_const;
2122 cg->parent = parent;
2123
2124 r = hashmap_put(cgroups, cg->cgroup_path, cg);
2125 if (r < 0) {
2126 if (!is_const)
2127 free(cg->cgroup_path);
2128 free(cg);
2129 return r;
2130 }
2131
2132 if (parent) {
2133 LIST_PREPEND(siblings, parent->children, cg);
2134 parent->n_children++;
2135 }
2136
2137 *ret = cg;
2138 return 1;
2139 }
2140
2141 static int add_process(
2142 Hashmap *cgroups,
2143 const char *path,
2144 pid_t pid,
2145 const char *name) {
2146
2147 struct CGroupInfo *cg;
2148 int r;
2149
2150 assert(cgroups);
2151 assert(name);
2152 assert(pid > 0);
2153
2154 r = add_cgroup(cgroups, path, true, &cg);
2155 if (r < 0)
2156 return r;
2157
2158 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
2159 if (r < 0)
2160 return r;
2161
2162 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
2163 }
2164
2165 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
2166 assert(cgroups);
2167 assert(cg);
2168
2169 while (cg->children)
2170 remove_cgroup(cgroups, cg->children);
2171
2172 hashmap_remove(cgroups, cg->cgroup_path);
2173
2174 if (!cg->is_const)
2175 free(cg->cgroup_path);
2176
2177 hashmap_free(cg->pids);
2178
2179 if (cg->parent)
2180 LIST_REMOVE(siblings, cg->parent->children, cg);
2181
2182 free(cg);
2183 }
2184
2185 static int cgroup_info_compare_func(const void *a, const void *b) {
2186 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
2187
2188 assert(x);
2189 assert(y);
2190
2191 return strcmp(x->cgroup_path, y->cgroup_path);
2192 }
2193
2194 static int dump_processes(
2195 Hashmap *cgroups,
2196 const char *cgroup_path,
2197 const char *prefix,
2198 unsigned n_columns,
2199 OutputFlags flags) {
2200
2201 struct CGroupInfo *cg;
2202 int r;
2203
2204 assert(prefix);
2205
2206 if (IS_ROOT(cgroup_path))
2207 cgroup_path = "/";
2208
2209 cg = hashmap_get(cgroups, cgroup_path);
2210 if (!cg)
2211 return 0;
2212
2213 if (!hashmap_isempty(cg->pids)) {
2214 const char *name;
2215 size_t n = 0, i;
2216 pid_t *pids;
2217 void *pidp;
2218 Iterator j;
2219 int width;
2220
2221 /* Order processes by their PID */
2222 pids = newa(pid_t, hashmap_size(cg->pids));
2223
2224 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
2225 pids[n++] = PTR_TO_PID(pidp);
2226
2227 assert(n == hashmap_size(cg->pids));
2228 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2229
2230 width = DECIMAL_STR_WIDTH(pids[n-1]);
2231
2232 for (i = 0; i < n; i++) {
2233 _cleanup_free_ char *e = NULL;
2234 const char *special;
2235 bool more;
2236
2237 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
2238 assert(name);
2239
2240 if (n_columns != 0) {
2241 unsigned k;
2242
2243 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2244
2245 e = ellipsize(name, k, 100);
2246 if (e)
2247 name = e;
2248 }
2249
2250 more = i+1 < n || cg->children;
2251 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
2252
2253 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
2254 prefix,
2255 special,
2256 width, pids[i],
2257 name);
2258 }
2259 }
2260
2261 if (cg->children) {
2262 struct CGroupInfo **children, *child;
2263 size_t n = 0, i;
2264
2265 /* Order subcgroups by their name */
2266 children = newa(struct CGroupInfo*, cg->n_children);
2267 LIST_FOREACH(siblings, child, cg->children)
2268 children[n++] = child;
2269 assert(n == cg->n_children);
2270 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
2271
2272 if (n_columns != 0)
2273 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
2274
2275 for (i = 0; i < n; i++) {
2276 _cleanup_free_ char *pp = NULL;
2277 const char *name, *special;
2278 bool more;
2279
2280 child = children[i];
2281
2282 name = strrchr(child->cgroup_path, '/');
2283 if (!name)
2284 return -EINVAL;
2285 name++;
2286
2287 more = i+1 < n;
2288 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
2289
2290 fputs(prefix, stdout);
2291 fputs(special, stdout);
2292 fputs(name, stdout);
2293 fputc('\n', stdout);
2294
2295 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
2296
2297 pp = strappend(prefix, special);
2298 if (!pp)
2299 return -ENOMEM;
2300
2301 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
2302 if (r < 0)
2303 return r;
2304 }
2305 }
2306
2307 cg->done = true;
2308 return 0;
2309 }
2310
2311 static int dump_extra_processes(
2312 Hashmap *cgroups,
2313 const char *prefix,
2314 unsigned n_columns,
2315 OutputFlags flags) {
2316
2317 _cleanup_free_ pid_t *pids = NULL;
2318 _cleanup_hashmap_free_ Hashmap *names = NULL;
2319 struct CGroupInfo *cg;
2320 size_t n_allocated = 0, n = 0, k;
2321 Iterator i;
2322 int width, r;
2323
2324 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2325 * combined, sorted, linear list. */
2326
2327 HASHMAP_FOREACH(cg, cgroups, i) {
2328 const char *name;
2329 void *pidp;
2330 Iterator j;
2331
2332 if (cg->done)
2333 continue;
2334
2335 if (hashmap_isempty(cg->pids))
2336 continue;
2337
2338 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2339 if (r < 0)
2340 return r;
2341
2342 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
2343 return -ENOMEM;
2344
2345 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
2346 pids[n++] = PTR_TO_PID(pidp);
2347
2348 r = hashmap_put(names, pidp, (void*) name);
2349 if (r < 0)
2350 return r;
2351 }
2352 }
2353
2354 if (n == 0)
2355 return 0;
2356
2357 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2358 width = DECIMAL_STR_WIDTH(pids[n-1]);
2359
2360 for (k = 0; k < n; k++) {
2361 _cleanup_free_ char *e = NULL;
2362 const char *name;
2363
2364 name = hashmap_get(names, PID_TO_PTR(pids[k]));
2365 assert(name);
2366
2367 if (n_columns != 0) {
2368 unsigned z;
2369
2370 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2371
2372 e = ellipsize(name, z, 100);
2373 if (e)
2374 name = e;
2375 }
2376
2377 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2378 prefix,
2379 special_glyph(TRIANGULAR_BULLET),
2380 width, pids[k],
2381 name);
2382 }
2383
2384 return 0;
2385 }
2386
2387 int unit_show_processes(
2388 sd_bus *bus,
2389 const char *unit,
2390 const char *cgroup_path,
2391 const char *prefix,
2392 unsigned n_columns,
2393 OutputFlags flags,
2394 sd_bus_error *error) {
2395
2396 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2397 Hashmap *cgroups = NULL;
2398 struct CGroupInfo *cg;
2399 int r;
2400
2401 assert(bus);
2402 assert(unit);
2403
2404 if (flags & OUTPUT_FULL_WIDTH)
2405 n_columns = 0;
2406 else if (n_columns <= 0)
2407 n_columns = columns();
2408
2409 prefix = strempty(prefix);
2410
2411 r = sd_bus_call_method(
2412 bus,
2413 "org.freedesktop.systemd1",
2414 "/org/freedesktop/systemd1",
2415 "org.freedesktop.systemd1.Manager",
2416 "GetUnitProcesses",
2417 error,
2418 &reply,
2419 "s",
2420 unit);
2421 if (r < 0)
2422 return r;
2423
2424 cgroups = hashmap_new(&path_hash_ops);
2425 if (!cgroups)
2426 return -ENOMEM;
2427
2428 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
2429 if (r < 0)
2430 goto finish;
2431
2432 for (;;) {
2433 const char *path = NULL, *name = NULL;
2434 uint32_t pid;
2435
2436 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
2437 if (r < 0)
2438 goto finish;
2439 if (r == 0)
2440 break;
2441
2442 r = add_process(cgroups, path, pid, name);
2443 if (r < 0)
2444 goto finish;
2445 }
2446
2447 r = sd_bus_message_exit_container(reply);
2448 if (r < 0)
2449 goto finish;
2450
2451 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
2452 if (r < 0)
2453 goto finish;
2454
2455 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2456
2457 finish:
2458 while ((cg = hashmap_first(cgroups)))
2459 remove_cgroup(cgroups, cg);
2460
2461 hashmap_free(cgroups);
2462
2463 return r;
2464 }