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