]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
Merge pull request #8859 from poettering/virt-xen-lying
[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 int r, rl;
688
689 if (STR_IN_SET(field,
690 "User", "Group",
691 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
692 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
693 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
694 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
695
696 return bus_append_string(m, field, eq);
697
698 if (STR_IN_SET(field,
699 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
700 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
701 "NoNewPrivileges", "SyslogLevelPrefix",
702 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
703 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
704 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
705
706 return bus_append_parse_boolean(m, field, eq);
707
708 if (STR_IN_SET(field,
709 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
710 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
711 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
712 "SupplementaryGroups", "SystemCallArchitectures"))
713
714 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
715
716 if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
717
718 return bus_append_log_level_from_string(m, field, eq);
719
720 if (streq(field, "SyslogFacility"))
721
722 return bus_append_log_facility_unshifted_from_string(m, field, eq);
723
724 if (streq(field, "SecureBits"))
725
726 return bus_append_secure_bits_from_string(m, field, eq);
727
728 if (streq(field, "CPUSchedulingPolicy"))
729
730 return bus_append_sched_policy_from_string(m, field, eq);
731
732 if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
733
734 return bus_append_safe_atoi(m, field, eq);
735
736 if (streq(field, "Nice"))
737
738 return bus_append_parse_nice(m, field, eq);
739
740 if (streq(field, "SystemCallErrorNumber"))
741
742 return bus_append_parse_errno(m, field, eq);
743
744 if (streq(field, "IOSchedulingClass"))
745
746 return bus_append_ioprio_class_from_string(m, field, eq);
747
748 if (streq(field, "IOSchedulingPriority"))
749
750 return bus_append_ioprio_parse_priority(m, field, eq);
751
752 if (STR_IN_SET(field,
753 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
754 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
755
756 return bus_append_parse_mode(m, field, eq);
757
758 if (streq(field, "TimerSlackNSec"))
759
760 return bus_append_parse_nsec(m, field, eq);
761
762 if (streq(field, "MountFlags"))
763
764 return bus_append_mount_propagation_flags_from_string(m, field, eq);
765
766 if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
767
768 return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
769
770 if (streq(field, "EnvironmentFile")) {
771
772 if (isempty(eq))
773 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
774 else
775 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
776 eq[0] == '-' ? eq + 1 : eq,
777 eq[0] == '-');
778 if (r < 0)
779 return bus_log_create_error(r);
780
781 return 1;
782 }
783
784 if (streq(field, "LogExtraFields")) {
785
786 r = sd_bus_message_open_container(m, 'r', "sv");
787 if (r < 0)
788 return bus_log_create_error(r);
789
790 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
791 if (r < 0)
792 return bus_log_create_error(r);
793
794 r = sd_bus_message_open_container(m, 'v', "aay");
795 if (r < 0)
796 return bus_log_create_error(r);
797
798 r = sd_bus_message_open_container(m, 'a', "ay");
799 if (r < 0)
800 return bus_log_create_error(r);
801
802 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
803 if (r < 0)
804 return bus_log_create_error(r);
805
806 r = sd_bus_message_close_container(m);
807 if (r < 0)
808 return bus_log_create_error(r);
809
810 r = sd_bus_message_close_container(m);
811 if (r < 0)
812 return bus_log_create_error(r);
813
814 r = sd_bus_message_close_container(m);
815 if (r < 0)
816 return bus_log_create_error(r);
817
818 return 1;
819 }
820
821 if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
822 const char *n, *appended;
823
824 if ((n = startswith(eq, "fd:"))) {
825 appended = strjoina(field, "FileDescriptorName");
826 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
827 } else if ((n = startswith(eq, "file:"))) {
828 appended = strjoina(field, "File");
829 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
830 } else
831 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
832
833 if (r < 0)
834 return bus_log_create_error(r);
835
836 return 1;
837 }
838
839 if (streq(field, "StandardInputText")) {
840 _cleanup_free_ char *unescaped = NULL;
841
842 r = cunescape(eq, 0, &unescaped);
843 if (r < 0)
844 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
845
846 if (!strextend(&unescaped, "\n", NULL))
847 return log_oom();
848
849 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
850 * interface anyway */
851
852 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
853 }
854
855 if (streq(field, "StandardInputData")) {
856 _cleanup_free_ void *decoded = NULL;
857 size_t sz;
858
859 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
860 if (r < 0)
861 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
862
863 return bus_append_byte_array(m, field, decoded, sz);
864 }
865
866 rl = rlimit_from_string(field);
867 if (rl >= 0) {
868 const char *sn;
869 struct rlimit l;
870
871 r = rlimit_parse(rl, eq, &l);
872 if (r < 0)
873 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
874
875 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
876 if (r < 0)
877 return bus_log_create_error(r);
878
879 sn = strjoina(field, "Soft");
880 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
881 if (r < 0)
882 return bus_log_create_error(r);
883
884 return 1;
885 }
886
887 if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
888 int ignore = 0;
889 const char *s = eq;
890
891 if (eq[0] == '-') {
892 ignore = 1;
893 s = eq + 1;
894 }
895
896 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
897 if (r < 0)
898 return bus_log_create_error(r);
899
900 return 1;
901 }
902
903 if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
904 uint64_t sum = 0;
905 bool invert = false;
906 const char *p = eq;
907
908 if (*p == '~') {
909 invert = true;
910 p++;
911 }
912
913 r = capability_set_from_string(p, &sum);
914 if (r < 0)
915 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
916
917 sum = invert ? ~sum : sum;
918
919 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
920 if (r < 0)
921 return bus_log_create_error(r);
922
923 return 1;
924 }
925
926 if (streq(field, "CPUAffinity")) {
927 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
928
929 r = parse_cpu_set(eq, &cpuset);
930 if (r < 0)
931 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
932
933 return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
934 }
935
936 if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
937 int whitelist = 1;
938 const char *p = eq;
939
940 if (*p == '~') {
941 whitelist = 0;
942 p++;
943 }
944
945 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
946 if (r < 0)
947 return bus_log_create_error(r);
948
949 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
950 if (r < 0)
951 return bus_log_create_error(r);
952
953 r = sd_bus_message_open_container(m, 'v', "(bas)");
954 if (r < 0)
955 return bus_log_create_error(r);
956
957 r = sd_bus_message_open_container(m, 'r', "bas");
958 if (r < 0)
959 return bus_log_create_error(r);
960
961 r = sd_bus_message_append_basic(m, 'b', &whitelist);
962 if (r < 0)
963 return bus_log_create_error(r);
964
965 r = sd_bus_message_open_container(m, 'a', "s");
966 if (r < 0)
967 return bus_log_create_error(r);
968
969 for (p = eq;;) {
970 _cleanup_free_ char *word = NULL;
971
972 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
973 if (r == 0)
974 break;
975 if (r == -ENOMEM)
976 return log_oom();
977 if (r < 0)
978 return log_error_errno(r, "Invalid syntax: %s", eq);
979
980 r = sd_bus_message_append_basic(m, 's', word);
981 if (r < 0)
982 return bus_log_create_error(r);
983 }
984
985 r = sd_bus_message_close_container(m);
986 if (r < 0)
987 return bus_log_create_error(r);
988
989 r = sd_bus_message_close_container(m);
990 if (r < 0)
991 return bus_log_create_error(r);
992
993 r = sd_bus_message_close_container(m);
994 if (r < 0)
995 return bus_log_create_error(r);
996
997 r = sd_bus_message_close_container(m);
998 if (r < 0)
999 return bus_log_create_error(r);
1000
1001 return 1;
1002 }
1003
1004 if (streq(field, "RestrictNamespaces")) {
1005 bool invert = false;
1006 unsigned long flags = 0;
1007
1008 if (eq[0] == '~') {
1009 invert = true;
1010 eq++;
1011 }
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 r = namespace_flag_from_string_many(eq, &flags);
1020 if (r < 0)
1021 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1022 }
1023
1024 if (invert)
1025 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1026
1027 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1028 if (r < 0)
1029 return bus_log_create_error(r);
1030
1031 return 1;
1032 }
1033
1034 if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1035 const char *p = eq;
1036
1037 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1038 if (r < 0)
1039 return bus_log_create_error(r);
1040
1041 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1042 if (r < 0)
1043 return bus_log_create_error(r);
1044
1045 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1046 if (r < 0)
1047 return bus_log_create_error(r);
1048
1049 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1050 if (r < 0)
1051 return bus_log_create_error(r);
1052
1053 for (;;) {
1054 _cleanup_free_ char *source = NULL, *destination = NULL;
1055 char *s = NULL, *d = NULL;
1056 bool ignore_enoent = false;
1057 uint64_t flags = MS_REC;
1058
1059 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1060 if (r < 0)
1061 return log_error_errno(r, "Failed to parse argument: %m");
1062 if (r == 0)
1063 break;
1064
1065 s = source;
1066 if (s[0] == '-') {
1067 ignore_enoent = true;
1068 s++;
1069 }
1070
1071 if (p && p[-1] == ':') {
1072 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1073 if (r < 0)
1074 return log_error_errno(r, "Failed to parse argument: %m");
1075 if (r == 0) {
1076 log_error("Missing argument after ':': %s", eq);
1077 return -EINVAL;
1078 }
1079
1080 d = destination;
1081
1082 if (p && p[-1] == ':') {
1083 _cleanup_free_ char *options = NULL;
1084
1085 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1086 if (r < 0)
1087 return log_error_errno(r, "Failed to parse argument: %m");
1088
1089 if (isempty(options) || streq(options, "rbind"))
1090 flags = MS_REC;
1091 else if (streq(options, "norbind"))
1092 flags = 0;
1093 else {
1094 log_error("Unknown options: %s", eq);
1095 return -EINVAL;
1096 }
1097 }
1098 } else
1099 d = s;
1100
1101 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1102 if (r < 0)
1103 return bus_log_create_error(r);
1104 }
1105
1106 r = sd_bus_message_close_container(m);
1107 if (r < 0)
1108 return bus_log_create_error(r);
1109
1110 r = sd_bus_message_close_container(m);
1111 if (r < 0)
1112 return bus_log_create_error(r);
1113
1114 r = sd_bus_message_close_container(m);
1115 if (r < 0)
1116 return bus_log_create_error(r);
1117
1118 return 1;
1119 }
1120
1121 if (streq(field, "TemporaryFileSystem")) {
1122 const char *p = eq;
1123
1124 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1125 if (r < 0)
1126 return bus_log_create_error(r);
1127
1128 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1129 if (r < 0)
1130 return bus_log_create_error(r);
1131
1132 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1133 if (r < 0)
1134 return bus_log_create_error(r);
1135
1136 r = sd_bus_message_open_container(m, 'a', "(ss)");
1137 if (r < 0)
1138 return bus_log_create_error(r);
1139
1140 for (;;) {
1141 _cleanup_free_ char *word = NULL, *path = NULL;
1142 const char *w;
1143
1144 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1145 if (r < 0)
1146 return log_error_errno(r, "Failed to parse argument: %m");
1147 if (r == 0)
1148 break;
1149
1150 w = word;
1151 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1152 if (r < 0)
1153 return log_error_errno(r, "Failed to parse argument: %m");
1154 if (r == 0)
1155 return log_error("Failed to parse argument: %m");
1156
1157 r = sd_bus_message_append(m, "(ss)", path, w);
1158 if (r < 0)
1159 return bus_log_create_error(r);
1160 }
1161
1162 r = sd_bus_message_close_container(m);
1163 if (r < 0)
1164 return bus_log_create_error(r);
1165
1166 r = sd_bus_message_close_container(m);
1167 if (r < 0)
1168 return bus_log_create_error(r);
1169
1170 r = sd_bus_message_close_container(m);
1171 if (r < 0)
1172 return bus_log_create_error(r);
1173
1174 return 1;
1175 }
1176
1177 return 0;
1178 }
1179
1180 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
1181
1182 if (streq(field, "KillMode"))
1183
1184 return bus_append_string(m, field, eq);
1185
1186 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
1187
1188 return bus_append_parse_boolean(m, field, eq);
1189
1190 if (streq(field, "KillSignal"))
1191
1192 return bus_append_signal_from_string(m, field, eq);
1193
1194 return 0;
1195 }
1196
1197 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
1198
1199 if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
1200
1201 return bus_append_string(m, field, eq);
1202
1203 if (streq(field, "TimeoutSec"))
1204
1205 return bus_append_parse_sec_rename(m, field, eq);
1206
1207 if (streq(field, "DirectoryMode"))
1208
1209 return bus_append_parse_mode(m, field, eq);
1210
1211 if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1212
1213 return bus_append_parse_boolean(m, field, eq);
1214
1215 return 0;
1216 }
1217
1218 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1219 int r;
1220
1221 if (streq(field, "MakeDirectory"))
1222
1223 return bus_append_parse_boolean(m, field, eq);
1224
1225 if (streq(field, "DirectoryMode"))
1226
1227 return bus_append_parse_mode(m, field, eq);
1228
1229 if (STR_IN_SET(field,
1230 "PathExists", "PathExistsGlob", "PathChanged",
1231 "PathModified", "DirectoryNotEmpty")) {
1232
1233 if (isempty(eq))
1234 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1235 else
1236 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1237 if (r < 0)
1238 return bus_log_create_error(r);
1239
1240 return 1;
1241 }
1242
1243 return 0;
1244 }
1245
1246 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
1247 int r;
1248
1249 if (STR_IN_SET(field,
1250 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1251 "USBFunctionDescriptors", "USBFunctionStrings"))
1252
1253 return bus_append_string(m, field, eq);
1254
1255 if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
1256
1257 return bus_append_parse_boolean(m, field, eq);
1258
1259 if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
1260
1261 return bus_append_parse_sec_rename(m, field, eq);
1262
1263 if (streq(field, "TimeoutSec")) {
1264
1265 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1266 if (r < 0)
1267 return r;
1268
1269 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1270 }
1271
1272 if (streq(field, "FileDescriptorStoreMax"))
1273
1274 return bus_append_safe_atou(m, field, eq);
1275
1276 if (STR_IN_SET(field,
1277 "ExecStartPre", "ExecStart", "ExecStartPost",
1278 "ExecReload", "ExecStop", "ExecStopPost"))
1279
1280 return bus_append_exec_command(m, field, eq);
1281
1282 if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1283 _cleanup_free_ int *status = NULL, *signal = NULL;
1284 size_t sz_status = 0, sz_signal = 0;
1285 const char *p;
1286
1287 for (p = eq;;) {
1288 _cleanup_free_ char *word = NULL;
1289 int val;
1290
1291 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1292 if (r == 0)
1293 break;
1294 if (r == -ENOMEM)
1295 return log_oom();
1296 if (r < 0)
1297 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1298
1299 r = safe_atoi(word, &val);
1300 if (r < 0) {
1301 val = signal_from_string(word);
1302 if (val < 0)
1303 return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
1304
1305 signal = reallocarray(signal, sz_signal + 1, sizeof(int));
1306 if (!signal)
1307 return log_oom();
1308
1309 signal[sz_signal++] = val;
1310 } else {
1311 status = reallocarray(status, sz_status + 1, sizeof(int));
1312 if (!status)
1313 return log_oom();
1314
1315 status[sz_status++] = val;
1316 }
1317 }
1318
1319 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1320 if (r < 0)
1321 return bus_log_create_error(r);
1322
1323 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1324 if (r < 0)
1325 return bus_log_create_error(r);
1326
1327 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1328 if (r < 0)
1329 return bus_log_create_error(r);
1330
1331 r = sd_bus_message_open_container(m, 'r', "aiai");
1332 if (r < 0)
1333 return bus_log_create_error(r);
1334
1335 r = sd_bus_message_append_array(m, 'i', status, sz_status);
1336 if (r < 0)
1337 return bus_log_create_error(r);
1338
1339 r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
1340 if (r < 0)
1341 return bus_log_create_error(r);
1342
1343 r = sd_bus_message_close_container(m);
1344 if (r < 0)
1345 return bus_log_create_error(r);
1346
1347 r = sd_bus_message_close_container(m);
1348 if (r < 0)
1349 return bus_log_create_error(r);
1350
1351 r = sd_bus_message_close_container(m);
1352 if (r < 0)
1353 return bus_log_create_error(r);
1354
1355 return 1;
1356 }
1357
1358 return 0;
1359 }
1360
1361 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1362 int r;
1363
1364 if (STR_IN_SET(field,
1365 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1366 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1367
1368 return bus_append_parse_boolean(m, field, eq);
1369
1370 if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
1371
1372 return bus_append_safe_atoi(m, field, eq);
1373
1374 if (streq(field, "IPTOS"))
1375
1376 return bus_append_ip_tos_from_string(m, field, eq);
1377
1378 if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1379
1380 return bus_append_safe_atou(m, field, eq);
1381
1382 if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
1383
1384 return bus_append_parse_mode(m, field, eq);
1385
1386 if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1387
1388 return bus_append_safe_atoi64(m, field, eq);
1389
1390 if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1391
1392 return bus_append_parse_sec_rename(m, field, eq);
1393
1394 if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1395
1396 return bus_append_parse_size(m, field, eq, 1024);
1397
1398 if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1399
1400 return bus_append_exec_command(m, field, eq);
1401
1402 if (STR_IN_SET(field,
1403 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1404 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1405 "SocketUser", "SocketGroup"))
1406
1407 return bus_append_string(m, field, eq);
1408
1409 if (streq(field, "Symlinks"))
1410
1411 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1412
1413 if (streq(field, "SocketProtocol"))
1414
1415 return bus_append_socket_protocol_from_name(m, field, eq);
1416
1417 if (STR_IN_SET(field,
1418 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1419 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1420
1421 if (isempty(eq))
1422 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1423 else
1424 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
1425 if (r < 0)
1426 return bus_log_create_error(r);
1427
1428 return 1;
1429 }
1430
1431 return 0;
1432 }
1433 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
1434 int r;
1435
1436 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent"))
1437
1438 return bus_append_parse_boolean(m, field, eq);
1439
1440 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1441
1442 return bus_append_parse_sec_rename(m, field, eq);
1443
1444 if (STR_IN_SET(field,
1445 "OnActiveSec", "OnBootSec", "OnStartupSec",
1446 "OnUnitActiveSec","OnUnitInactiveSec")) {
1447
1448 if (isempty(eq))
1449 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
1450 else {
1451 usec_t t;
1452 r = parse_sec(eq, &t);
1453 if (r < 0)
1454 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
1455
1456 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
1457 }
1458 if (r < 0)
1459 return bus_log_create_error(r);
1460
1461 return 1;
1462 }
1463
1464 if (streq(field, "OnCalendar")) {
1465
1466 if (isempty(eq))
1467 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
1468 else
1469 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
1470 if (r < 0)
1471 return bus_log_create_error(r);
1472
1473 return 1;
1474 }
1475
1476 return 0;
1477 }
1478
1479 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
1480 ConditionType t = _CONDITION_TYPE_INVALID;
1481 bool is_condition = false;
1482 int r;
1483
1484 if (STR_IN_SET(field,
1485 "Description", "SourcePath", "OnFailureJobMode",
1486 "JobTimeoutAction", "JobTimeoutRebootArgument",
1487 "StartLimitAction", "FailureAction", "SuccessAction",
1488 "RebootArgument", "CollectMode"))
1489
1490 return bus_append_string(m, field, eq);
1491
1492 if (STR_IN_SET(field,
1493 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1494 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
1495
1496 return bus_append_parse_boolean(m, field, eq);
1497
1498 if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1499
1500 return bus_append_parse_sec_rename(m, field, eq);
1501
1502 if (streq(field, "StartLimitBurst"))
1503
1504 return bus_append_safe_atou(m, field, eq);
1505
1506 if (unit_dependency_from_string(field) >= 0 ||
1507 STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
1508
1509 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1510
1511 t = condition_type_from_string(field);
1512 if (t >= 0)
1513 is_condition = true;
1514 else
1515 t = assert_type_from_string(field);
1516 if (t >= 0) {
1517 if (isempty(eq))
1518 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
1519 else {
1520 const char *p = eq;
1521 int trigger, negate;
1522
1523 trigger = *p == '|';
1524 if (trigger)
1525 p++;
1526
1527 negate = *p == '!';
1528 if (negate)
1529 p++;
1530
1531 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
1532 field, trigger, negate, p);
1533 }
1534 if (r < 0)
1535 return bus_log_create_error(r);
1536
1537 return 1;
1538 }
1539
1540 return 0;
1541 }
1542
1543 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1544 const char *eq, *field;
1545 int r;
1546
1547 assert(m);
1548 assert(assignment);
1549
1550 eq = strchr(assignment, '=');
1551 if (!eq) {
1552 log_error("Not an assignment: %s", assignment);
1553 return -EINVAL;
1554 }
1555
1556 field = strndupa(assignment, eq - assignment);
1557 eq++;
1558
1559 switch (t) {
1560 case UNIT_SERVICE:
1561 r = bus_append_cgroup_property(m, field, eq);
1562 if (r != 0)
1563 return r;
1564
1565 r = bus_append_execute_property(m, field, eq);
1566 if (r != 0)
1567 return r;
1568
1569 r = bus_append_kill_property(m, field, eq);
1570 if (r != 0)
1571 return r;
1572
1573 r = bus_append_service_property(m, field, eq);
1574 if (r != 0)
1575 return r;
1576 break;
1577
1578 case UNIT_SOCKET:
1579 r = bus_append_cgroup_property(m, field, eq);
1580 if (r != 0)
1581 return r;
1582
1583 r = bus_append_execute_property(m, field, eq);
1584 if (r != 0)
1585 return r;
1586
1587 r = bus_append_kill_property(m, field, eq);
1588 if (r != 0)
1589 return r;
1590
1591 r = bus_append_socket_property(m, field, eq);
1592 if (r != 0)
1593 return r;
1594 break;
1595
1596 case UNIT_TIMER:
1597 r = bus_append_timer_property(m, field, eq);
1598 if (r != 0)
1599 return r;
1600 break;
1601
1602 case UNIT_PATH:
1603 r = bus_append_path_property(m, field, eq);
1604 if (r != 0)
1605 return r;
1606 break;
1607
1608 case UNIT_SLICE:
1609 r = bus_append_cgroup_property(m, field, eq);
1610 if (r != 0)
1611 return r;
1612 break;
1613
1614 case UNIT_SCOPE:
1615
1616 if (streq(field, "TimeoutStopSec"))
1617 return bus_append_parse_sec_rename(m, field, eq);
1618
1619 r = bus_append_cgroup_property(m, field, eq);
1620 if (r != 0)
1621 return r;
1622
1623 r = bus_append_kill_property(m, field, eq);
1624 if (r != 0)
1625 return r;
1626 break;
1627
1628 case UNIT_MOUNT:
1629 r = bus_append_cgroup_property(m, field, eq);
1630 if (r != 0)
1631 return r;
1632
1633 r = bus_append_execute_property(m, field, eq);
1634 if (r != 0)
1635 return r;
1636
1637 r = bus_append_kill_property(m, field, eq);
1638 if (r != 0)
1639 return r;
1640
1641 r = bus_append_mount_property(m, field, eq);
1642 if (r != 0)
1643 return r;
1644
1645 break;
1646
1647 case UNIT_AUTOMOUNT:
1648 r = bus_append_automount_property(m, field, eq);
1649 if (r != 0)
1650 return r;
1651
1652 break;
1653
1654 case UNIT_TARGET:
1655 case UNIT_DEVICE:
1656 case UNIT_SWAP:
1657 log_error("Not supported unit type");
1658 return -EINVAL;
1659
1660 default:
1661 log_error("Invalid unit type");
1662 return -EINVAL;
1663 }
1664
1665 r = bus_append_unit_property(m, field, eq);
1666 if (r != 0)
1667 return r;
1668
1669 log_error("Unknown assignment: %s", assignment);
1670 return -EINVAL;
1671 }
1672
1673 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
1674 char **i;
1675 int r;
1676
1677 assert(m);
1678
1679 STRV_FOREACH(i, l) {
1680 r = bus_append_unit_property_assignment(m, t, *i);
1681 if (r < 0)
1682 return r;
1683 }
1684
1685 return 0;
1686 }
1687
1688 typedef struct BusWaitForJobs {
1689 sd_bus *bus;
1690 Set *jobs;
1691
1692 char *name;
1693 char *result;
1694
1695 sd_bus_slot *slot_job_removed;
1696 sd_bus_slot *slot_disconnected;
1697 } BusWaitForJobs;
1698
1699 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1700 assert(m);
1701
1702 log_error("Warning! D-Bus connection terminated.");
1703 sd_bus_close(sd_bus_message_get_bus(m));
1704
1705 return 0;
1706 }
1707
1708 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1709 const char *path, *unit, *result;
1710 BusWaitForJobs *d = userdata;
1711 uint32_t id;
1712 char *found;
1713 int r;
1714
1715 assert(m);
1716 assert(d);
1717
1718 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1719 if (r < 0) {
1720 bus_log_parse_error(r);
1721 return 0;
1722 }
1723
1724 found = set_remove(d->jobs, (char*) path);
1725 if (!found)
1726 return 0;
1727
1728 free(found);
1729
1730 if (!isempty(result))
1731 d->result = strdup(result);
1732
1733 if (!isempty(unit))
1734 d->name = strdup(unit);
1735
1736 return 0;
1737 }
1738
1739 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1740 if (!d)
1741 return;
1742
1743 set_free_free(d->jobs);
1744
1745 sd_bus_slot_unref(d->slot_disconnected);
1746 sd_bus_slot_unref(d->slot_job_removed);
1747
1748 sd_bus_unref(d->bus);
1749
1750 free(d->name);
1751 free(d->result);
1752
1753 free(d);
1754 }
1755
1756 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1757 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1758 int r;
1759
1760 assert(bus);
1761 assert(ret);
1762
1763 d = new0(BusWaitForJobs, 1);
1764 if (!d)
1765 return -ENOMEM;
1766
1767 d->bus = sd_bus_ref(bus);
1768
1769 /* When we are a bus client we match by sender. Direct
1770 * connections OTOH have no initialized sender field, and
1771 * hence we ignore the sender then */
1772 r = sd_bus_match_signal_async(
1773 bus,
1774 &d->slot_job_removed,
1775 bus->bus_client ? "org.freedesktop.systemd1" : NULL,
1776 "/org/freedesktop/systemd1",
1777 "org.freedesktop.systemd1.Manager",
1778 "JobRemoved",
1779 match_job_removed, NULL, d);
1780 if (r < 0)
1781 return r;
1782
1783 r = sd_bus_match_signal_async(
1784 bus,
1785 &d->slot_disconnected,
1786 "org.freedesktop.DBus.Local",
1787 NULL,
1788 "org.freedesktop.DBus.Local",
1789 "Disconnected",
1790 match_disconnected, NULL, d);
1791 if (r < 0)
1792 return r;
1793
1794 *ret = TAKE_PTR(d);
1795
1796 return 0;
1797 }
1798
1799 static int bus_process_wait(sd_bus *bus) {
1800 int r;
1801
1802 for (;;) {
1803 r = sd_bus_process(bus, NULL);
1804 if (r < 0)
1805 return r;
1806 if (r > 0)
1807 return 0;
1808
1809 r = sd_bus_wait(bus, (uint64_t) -1);
1810 if (r < 0)
1811 return r;
1812 }
1813 }
1814
1815 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1816 _cleanup_free_ char *dbus_path = NULL;
1817
1818 assert(d);
1819 assert(d->name);
1820 assert(result);
1821
1822 if (!endswith(d->name, ".service"))
1823 return -EINVAL;
1824
1825 dbus_path = unit_dbus_path_from_name(d->name);
1826 if (!dbus_path)
1827 return -ENOMEM;
1828
1829 return sd_bus_get_property_string(d->bus,
1830 "org.freedesktop.systemd1",
1831 dbus_path,
1832 "org.freedesktop.systemd1.Service",
1833 "Result",
1834 NULL,
1835 result);
1836 }
1837
1838 static const struct {
1839 const char *result, *explanation;
1840 } explanations [] = {
1841 { "resources", "of unavailable resources or another system error" },
1842 { "protocol", "the service did not take the steps required by its unit configuration" },
1843 { "timeout", "a timeout was exceeded" },
1844 { "exit-code", "the control process exited with error code" },
1845 { "signal", "a fatal signal was delivered to the control process" },
1846 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1847 { "watchdog", "the service failed to send watchdog ping" },
1848 { "start-limit", "start of the service was attempted too often" }
1849 };
1850
1851 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1852 _cleanup_free_ char *service_shell_quoted = NULL;
1853 const char *systemctl = "systemctl", *journalctl = "journalctl";
1854
1855 assert(service);
1856
1857 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1858
1859 if (!strv_isempty((char**) extra_args)) {
1860 _cleanup_free_ char *t;
1861
1862 t = strv_join((char**) extra_args, " ");
1863 systemctl = strjoina("systemctl ", t ? : "<args>");
1864 journalctl = strjoina("journalctl ", t ? : "<args>");
1865 }
1866
1867 if (!isempty(result)) {
1868 unsigned i;
1869
1870 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1871 if (streq(result, explanations[i].result))
1872 break;
1873
1874 if (i < ELEMENTSOF(explanations)) {
1875 log_error("Job for %s failed because %s.\n"
1876 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1877 service,
1878 explanations[i].explanation,
1879 systemctl,
1880 service_shell_quoted ?: "<service>",
1881 journalctl);
1882 goto finish;
1883 }
1884 }
1885
1886 log_error("Job for %s failed.\n"
1887 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1888 service,
1889 systemctl,
1890 service_shell_quoted ?: "<service>",
1891 journalctl);
1892
1893 finish:
1894 /* For some results maybe additional explanation is required */
1895 if (streq_ptr(result, "start-limit"))
1896 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1897 "followed by \"%1$s start %2$s\" again.",
1898 systemctl,
1899 service_shell_quoted ?: "<service>");
1900 }
1901
1902 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1903 assert(d->result);
1904
1905 if (!quiet) {
1906 if (streq(d->result, "canceled"))
1907 log_error("Job for %s canceled.", strna(d->name));
1908 else if (streq(d->result, "timeout"))
1909 log_error("Job for %s timed out.", strna(d->name));
1910 else if (streq(d->result, "dependency"))
1911 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1912 else if (streq(d->result, "invalid"))
1913 log_error("%s is not active, cannot reload.", strna(d->name));
1914 else if (streq(d->result, "assert"))
1915 log_error("Assertion failed on job for %s.", strna(d->name));
1916 else if (streq(d->result, "unsupported"))
1917 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1918 else if (streq(d->result, "collected"))
1919 log_error("Queued job for %s was garbage collected.", strna(d->name));
1920 else if (streq(d->result, "once"))
1921 log_error("Unit %s was started already once and can't be started again.", strna(d->name));
1922 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1923 if (d->name) {
1924 _cleanup_free_ char *result = NULL;
1925 int q;
1926
1927 q = bus_job_get_service_result(d, &result);
1928 if (q < 0)
1929 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1930
1931 log_job_error_with_service_result(d->name, result, extra_args);
1932 } else
1933 log_error("Job failed. See \"journalctl -xe\" for details.");
1934 }
1935 }
1936
1937 if (STR_IN_SET(d->result, "canceled", "collected"))
1938 return -ECANCELED;
1939 else if (streq(d->result, "timeout"))
1940 return -ETIME;
1941 else if (streq(d->result, "dependency"))
1942 return -EIO;
1943 else if (streq(d->result, "invalid"))
1944 return -ENOEXEC;
1945 else if (streq(d->result, "assert"))
1946 return -EPROTO;
1947 else if (streq(d->result, "unsupported"))
1948 return -EOPNOTSUPP;
1949 else if (streq(d->result, "once"))
1950 return -ESTALE;
1951 else if (STR_IN_SET(d->result, "done", "skipped"))
1952 return 0;
1953
1954 log_debug("Unexpected job result, assuming server side newer than us: %s", d->result);
1955 return -EIO;
1956 }
1957
1958 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1959 int r = 0;
1960
1961 assert(d);
1962
1963 while (!set_isempty(d->jobs)) {
1964 int q;
1965
1966 q = bus_process_wait(d->bus);
1967 if (q < 0)
1968 return log_error_errno(q, "Failed to wait for response: %m");
1969
1970 if (d->result) {
1971 q = check_wait_response(d, quiet, extra_args);
1972 /* Return the first error as it is most likely to be
1973 * meaningful. */
1974 if (q < 0 && r == 0)
1975 r = q;
1976
1977 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1978 }
1979
1980 d->name = mfree(d->name);
1981 d->result = mfree(d->result);
1982 }
1983
1984 return r;
1985 }
1986
1987 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1988 int r;
1989
1990 assert(d);
1991
1992 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1993 if (r < 0)
1994 return r;
1995
1996 return set_put_strdup(d->jobs, path);
1997 }
1998
1999 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
2000 int r;
2001
2002 r = bus_wait_for_jobs_add(d, path);
2003 if (r < 0)
2004 return log_oom();
2005
2006 return bus_wait_for_jobs(d, quiet, NULL);
2007 }
2008
2009 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
2010 const char *type, *path, *source;
2011 int r;
2012
2013 /* changes is dereferenced when calling unit_file_dump_changes() later,
2014 * so we have to make sure this is not NULL. */
2015 assert(changes);
2016 assert(n_changes);
2017
2018 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2019 if (r < 0)
2020 return bus_log_parse_error(r);
2021
2022 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2023 /* We expect only "success" changes to be sent over the bus.
2024 Hence, reject anything negative. */
2025 UnitFileChangeType ch = unit_file_change_type_from_string(type);
2026
2027 if (ch < 0) {
2028 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
2029 continue;
2030 }
2031
2032 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2033 if (r < 0)
2034 return r;
2035 }
2036 if (r < 0)
2037 return bus_log_parse_error(r);
2038
2039 r = sd_bus_message_exit_container(m);
2040 if (r < 0)
2041 return bus_log_parse_error(r);
2042
2043 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
2044 return 0;
2045 }
2046
2047 struct CGroupInfo {
2048 char *cgroup_path;
2049 bool is_const; /* If false, cgroup_path should be free()'d */
2050
2051 Hashmap *pids; /* PID → process name */
2052 bool done;
2053
2054 struct CGroupInfo *parent;
2055 LIST_FIELDS(struct CGroupInfo, siblings);
2056 LIST_HEAD(struct CGroupInfo, children);
2057 size_t n_children;
2058 };
2059
2060 static bool IS_ROOT(const char *p) {
2061 return isempty(p) || streq(p, "/");
2062 }
2063
2064 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
2065 struct CGroupInfo *parent = NULL, *cg;
2066 int r;
2067
2068 assert(cgroups);
2069 assert(ret);
2070
2071 if (IS_ROOT(path))
2072 path = "/";
2073
2074 cg = hashmap_get(cgroups, path);
2075 if (cg) {
2076 *ret = cg;
2077 return 0;
2078 }
2079
2080 if (!IS_ROOT(path)) {
2081 const char *e, *pp;
2082
2083 e = strrchr(path, '/');
2084 if (!e)
2085 return -EINVAL;
2086
2087 pp = strndupa(path, e - path);
2088 if (!pp)
2089 return -ENOMEM;
2090
2091 r = add_cgroup(cgroups, pp, false, &parent);
2092 if (r < 0)
2093 return r;
2094 }
2095
2096 cg = new0(struct CGroupInfo, 1);
2097 if (!cg)
2098 return -ENOMEM;
2099
2100 if (is_const)
2101 cg->cgroup_path = (char*) path;
2102 else {
2103 cg->cgroup_path = strdup(path);
2104 if (!cg->cgroup_path) {
2105 free(cg);
2106 return -ENOMEM;
2107 }
2108 }
2109
2110 cg->is_const = is_const;
2111 cg->parent = parent;
2112
2113 r = hashmap_put(cgroups, cg->cgroup_path, cg);
2114 if (r < 0) {
2115 if (!is_const)
2116 free(cg->cgroup_path);
2117 free(cg);
2118 return r;
2119 }
2120
2121 if (parent) {
2122 LIST_PREPEND(siblings, parent->children, cg);
2123 parent->n_children++;
2124 }
2125
2126 *ret = cg;
2127 return 1;
2128 }
2129
2130 static int add_process(
2131 Hashmap *cgroups,
2132 const char *path,
2133 pid_t pid,
2134 const char *name) {
2135
2136 struct CGroupInfo *cg;
2137 int r;
2138
2139 assert(cgroups);
2140 assert(name);
2141 assert(pid > 0);
2142
2143 r = add_cgroup(cgroups, path, true, &cg);
2144 if (r < 0)
2145 return r;
2146
2147 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
2148 if (r < 0)
2149 return r;
2150
2151 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
2152 }
2153
2154 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
2155 assert(cgroups);
2156 assert(cg);
2157
2158 while (cg->children)
2159 remove_cgroup(cgroups, cg->children);
2160
2161 hashmap_remove(cgroups, cg->cgroup_path);
2162
2163 if (!cg->is_const)
2164 free(cg->cgroup_path);
2165
2166 hashmap_free(cg->pids);
2167
2168 if (cg->parent)
2169 LIST_REMOVE(siblings, cg->parent->children, cg);
2170
2171 free(cg);
2172 }
2173
2174 static int cgroup_info_compare_func(const void *a, const void *b) {
2175 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
2176
2177 assert(x);
2178 assert(y);
2179
2180 return strcmp(x->cgroup_path, y->cgroup_path);
2181 }
2182
2183 static int dump_processes(
2184 Hashmap *cgroups,
2185 const char *cgroup_path,
2186 const char *prefix,
2187 unsigned n_columns,
2188 OutputFlags flags) {
2189
2190 struct CGroupInfo *cg;
2191 int r;
2192
2193 assert(prefix);
2194
2195 if (IS_ROOT(cgroup_path))
2196 cgroup_path = "/";
2197
2198 cg = hashmap_get(cgroups, cgroup_path);
2199 if (!cg)
2200 return 0;
2201
2202 if (!hashmap_isempty(cg->pids)) {
2203 const char *name;
2204 size_t n = 0, i;
2205 pid_t *pids;
2206 void *pidp;
2207 Iterator j;
2208 int width;
2209
2210 /* Order processes by their PID */
2211 pids = newa(pid_t, hashmap_size(cg->pids));
2212
2213 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
2214 pids[n++] = PTR_TO_PID(pidp);
2215
2216 assert(n == hashmap_size(cg->pids));
2217 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2218
2219 width = DECIMAL_STR_WIDTH(pids[n-1]);
2220
2221 for (i = 0; i < n; i++) {
2222 _cleanup_free_ char *e = NULL;
2223 const char *special;
2224 bool more;
2225
2226 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
2227 assert(name);
2228
2229 if (n_columns != 0) {
2230 unsigned k;
2231
2232 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2233
2234 e = ellipsize(name, k, 100);
2235 if (e)
2236 name = e;
2237 }
2238
2239 more = i+1 < n || cg->children;
2240 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
2241
2242 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
2243 prefix,
2244 special,
2245 width, pids[i],
2246 name);
2247 }
2248 }
2249
2250 if (cg->children) {
2251 struct CGroupInfo **children, *child;
2252 size_t n = 0, i;
2253
2254 /* Order subcgroups by their name */
2255 children = newa(struct CGroupInfo*, cg->n_children);
2256 LIST_FOREACH(siblings, child, cg->children)
2257 children[n++] = child;
2258 assert(n == cg->n_children);
2259 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
2260
2261 if (n_columns != 0)
2262 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
2263
2264 for (i = 0; i < n; i++) {
2265 _cleanup_free_ char *pp = NULL;
2266 const char *name, *special;
2267 bool more;
2268
2269 child = children[i];
2270
2271 name = strrchr(child->cgroup_path, '/');
2272 if (!name)
2273 return -EINVAL;
2274 name++;
2275
2276 more = i+1 < n;
2277 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
2278
2279 fputs(prefix, stdout);
2280 fputs(special, stdout);
2281 fputs(name, stdout);
2282 fputc('\n', stdout);
2283
2284 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
2285
2286 pp = strappend(prefix, special);
2287 if (!pp)
2288 return -ENOMEM;
2289
2290 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
2291 if (r < 0)
2292 return r;
2293 }
2294 }
2295
2296 cg->done = true;
2297 return 0;
2298 }
2299
2300 static int dump_extra_processes(
2301 Hashmap *cgroups,
2302 const char *prefix,
2303 unsigned n_columns,
2304 OutputFlags flags) {
2305
2306 _cleanup_free_ pid_t *pids = NULL;
2307 _cleanup_hashmap_free_ Hashmap *names = NULL;
2308 struct CGroupInfo *cg;
2309 size_t n_allocated = 0, n = 0, k;
2310 Iterator i;
2311 int width, r;
2312
2313 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2314 * combined, sorted, linear list. */
2315
2316 HASHMAP_FOREACH(cg, cgroups, i) {
2317 const char *name;
2318 void *pidp;
2319 Iterator j;
2320
2321 if (cg->done)
2322 continue;
2323
2324 if (hashmap_isempty(cg->pids))
2325 continue;
2326
2327 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
2328 if (r < 0)
2329 return r;
2330
2331 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
2332 return -ENOMEM;
2333
2334 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
2335 pids[n++] = PTR_TO_PID(pidp);
2336
2337 r = hashmap_put(names, pidp, (void*) name);
2338 if (r < 0)
2339 return r;
2340 }
2341 }
2342
2343 if (n == 0)
2344 return 0;
2345
2346 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
2347 width = DECIMAL_STR_WIDTH(pids[n-1]);
2348
2349 for (k = 0; k < n; k++) {
2350 _cleanup_free_ char *e = NULL;
2351 const char *name;
2352
2353 name = hashmap_get(names, PID_TO_PTR(pids[k]));
2354 assert(name);
2355
2356 if (n_columns != 0) {
2357 unsigned z;
2358
2359 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
2360
2361 e = ellipsize(name, z, 100);
2362 if (e)
2363 name = e;
2364 }
2365
2366 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
2367 prefix,
2368 special_glyph(TRIANGULAR_BULLET),
2369 width, pids[k],
2370 name);
2371 }
2372
2373 return 0;
2374 }
2375
2376 int unit_show_processes(
2377 sd_bus *bus,
2378 const char *unit,
2379 const char *cgroup_path,
2380 const char *prefix,
2381 unsigned n_columns,
2382 OutputFlags flags,
2383 sd_bus_error *error) {
2384
2385 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2386 Hashmap *cgroups = NULL;
2387 struct CGroupInfo *cg;
2388 int r;
2389
2390 assert(bus);
2391 assert(unit);
2392
2393 if (flags & OUTPUT_FULL_WIDTH)
2394 n_columns = 0;
2395 else if (n_columns <= 0)
2396 n_columns = columns();
2397
2398 prefix = strempty(prefix);
2399
2400 r = sd_bus_call_method(
2401 bus,
2402 "org.freedesktop.systemd1",
2403 "/org/freedesktop/systemd1",
2404 "org.freedesktop.systemd1.Manager",
2405 "GetUnitProcesses",
2406 error,
2407 &reply,
2408 "s",
2409 unit);
2410 if (r < 0)
2411 return r;
2412
2413 cgroups = hashmap_new(&path_hash_ops);
2414 if (!cgroups)
2415 return -ENOMEM;
2416
2417 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
2418 if (r < 0)
2419 goto finish;
2420
2421 for (;;) {
2422 const char *path = NULL, *name = NULL;
2423 uint32_t pid;
2424
2425 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
2426 if (r < 0)
2427 goto finish;
2428 if (r == 0)
2429 break;
2430
2431 r = add_process(cgroups, path, pid, name);
2432 if (r < 0)
2433 goto finish;
2434 }
2435
2436 r = sd_bus_message_exit_container(reply);
2437 if (r < 0)
2438 goto finish;
2439
2440 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
2441 if (r < 0)
2442 goto finish;
2443
2444 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2445
2446 finish:
2447 while ((cg = hashmap_first(cgroups)))
2448 remove_cgroup(cgroups, cg);
2449
2450 hashmap_free(cgroups);
2451
2452 return r;
2453 }
2454
2455 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2456 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2457 _cleanup_free_ char *path = NULL;
2458 int r;
2459
2460 path = unit_dbus_path_from_name(name);
2461 if (!path)
2462 return log_oom();
2463
2464 /* This function warns on it's own, because otherwise it'd be awkward to pass
2465 * the dbus error message around. */
2466
2467 r = sd_bus_get_property_string(
2468 bus,
2469 "org.freedesktop.systemd1",
2470 path,
2471 "org.freedesktop.systemd1.Unit",
2472 "LoadState",
2473 &error,
2474 load_state);
2475 if (r < 0)
2476 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2477
2478 return 0;
2479 }