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