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