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