]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
Merge pull request #9026 from yuwata/followup-9021
[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 = 0;
1012
1013 if (eq[0] == '~') {
1014 invert = true;
1015 eq++;
1016 }
1017
1018 r = parse_boolean(eq);
1019 if (r > 0)
1020 flags = 0;
1021 else if (r == 0)
1022 flags = NAMESPACE_FLAGS_ALL;
1023 else {
1024 r = namespace_flag_from_string_many(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 return log_error("Failed to parse argument: %m");
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 }