]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
core: add new PrivateMounts= unit setting
[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 "PrivateMounts", "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 }