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