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