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