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