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