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