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