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