]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
core: support IP firewalling to be configured for transient units
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-unit-util.h"
23 #include "bus-util.h"
24 #include "cap-list.h"
25 #include "cgroup-util.h"
26 #include "cpu-set-util.h"
27 #include "env-util.h"
28 #include "errno-list.h"
29 #include "escape.h"
30 #include "hashmap.h"
31 #include "hostname-util.h"
32 #include "in-addr-util.h"
33 #include "list.h"
34 #include "locale-util.h"
35 #include "mount-util.h"
36 #include "nsflags.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
40 #include "rlimit-util.h"
41 #include "securebits-util.h"
42 #include "signal-util.h"
43 #include "string-util.h"
44 #include "syslog-util.h"
45 #include "terminal-util.h"
46 #include "user-util.h"
47 #include "utf8.h"
48 #include "util.h"
49
50 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
51 assert(message);
52 assert(u);
53
54 u->machine = NULL;
55
56 return sd_bus_message_read(
57 message,
58 "(ssssssouso)",
59 &u->id,
60 &u->description,
61 &u->load_state,
62 &u->active_state,
63 &u->sub_state,
64 &u->following,
65 &u->unit_path,
66 &u->job_id,
67 &u->job_type,
68 &u->job_path);
69 }
70
71 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
72 int r;
73
74 assert(m);
75 assert(prefix);
76
77 r = sd_bus_message_open_container(m, 'r', "iayu");
78 if (r < 0)
79 return r;
80
81 r = sd_bus_message_append(m, "i", family);
82 if (r < 0)
83 return r;
84
85 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
86 if (r < 0)
87 return r;
88
89 r = sd_bus_message_append(m, "u", prefixlen);
90 if (r < 0)
91 return r;
92
93 return sd_bus_message_close_container(m);
94 }
95
96 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
97 const char *eq, *field;
98 UnitDependency dep;
99 int r, rl;
100
101 assert(m);
102 assert(assignment);
103
104 eq = strchr(assignment, '=');
105 if (!eq) {
106 log_error("Not an assignment: %s", assignment);
107 return -EINVAL;
108 }
109
110 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
111 if (r < 0)
112 return bus_log_create_error(r);
113
114 field = strndupa(assignment, eq - assignment);
115 eq++;
116
117 if (streq(field, "CPUQuota")) {
118
119 if (isempty(eq))
120 r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
121 else {
122 r = parse_percent_unbounded(eq);
123 if (r <= 0) {
124 log_error_errno(r, "CPU quota '%s' invalid.", eq);
125 return -EINVAL;
126 }
127
128 r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
129 }
130
131 goto finish;
132
133 } else if (streq(field, "EnvironmentFile")) {
134
135 r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
136 eq[0] == '-' ? eq + 1 : eq,
137 eq[0] == '-');
138 goto finish;
139
140 } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
141 char *n;
142 usec_t t;
143 size_t l;
144
145 r = parse_sec(eq, &t);
146 if (r < 0)
147 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
148
149 l = strlen(field);
150 n = newa(char, l + 2);
151 if (!n)
152 return log_oom();
153
154 /* Change suffix Sec → USec */
155 strcpy(mempcpy(n, field, l - 3), "USec");
156 r = sd_bus_message_append(m, "sv", n, "t", t);
157 goto finish;
158
159 } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
160 uint64_t bytes;
161
162 if (isempty(eq) || streq(eq, "infinity"))
163 bytes = CGROUP_LIMIT_MAX;
164 else {
165 r = parse_percent(eq);
166 if (r >= 0) {
167 char *n;
168
169 /* When this is a percentage we'll convert this into a relative value in the range
170 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
171 * ones). This way the physical memory size can be determined server-side */
172
173 n = strjoina(field, "Scale");
174 r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
175 goto finish;
176
177 } else {
178 r = parse_size(eq, 1024, &bytes);
179 if (r < 0)
180 return log_error_errno(r, "Failed to parse bytes specification %s", assignment);
181 }
182 }
183
184 r = sd_bus_message_append(m, "sv", field, "t", bytes);
185 goto finish;
186 } else if (streq(field, "TasksMax")) {
187 uint64_t t;
188
189 if (isempty(eq) || streq(eq, "infinity"))
190 t = (uint64_t) -1;
191 else {
192 r = parse_percent(eq);
193 if (r >= 0) {
194 r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
195 goto finish;
196 } else {
197 r = safe_atou64(eq, &t);
198 if (r < 0)
199 return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment);
200 }
201
202 }
203
204 r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
205 goto finish;
206 }
207
208 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
209 if (r < 0)
210 return bus_log_create_error(r);
211
212 rl = rlimit_from_string(field);
213 if (rl >= 0) {
214 const char *sn;
215 struct rlimit l;
216
217 r = rlimit_parse(rl, eq, &l);
218 if (r < 0)
219 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
220
221 r = sd_bus_message_append(m, "v", "t", l.rlim_max);
222 if (r < 0)
223 return bus_log_create_error(r);
224
225 r = sd_bus_message_close_container(m);
226 if (r < 0)
227 return bus_log_create_error(r);
228
229 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
230 if (r < 0)
231 return bus_log_create_error(r);
232
233 sn = strjoina(field, "Soft");
234 r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
235
236 } else if (STR_IN_SET(field,
237 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
238 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
239 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
240 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
241 "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse",
242 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
243 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
244 "CPUSchedulingResetOnFork", "LockPersonality")) {
245
246 r = parse_boolean(eq);
247 if (r < 0)
248 return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
249
250 r = sd_bus_message_append(m, "v", "b", r);
251
252 } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) {
253 uint64_t u;
254
255 r = cg_weight_parse(eq, &u);
256 if (r < 0) {
257 log_error("Failed to parse %s value %s.", field, eq);
258 return -EINVAL;
259 }
260
261 r = sd_bus_message_append(m, "v", "t", u);
262
263 } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
264 uint64_t u;
265
266 r = cg_cpu_shares_parse(eq, &u);
267 if (r < 0) {
268 log_error("Failed to parse %s value %s.", field, eq);
269 return -EINVAL;
270 }
271
272 r = sd_bus_message_append(m, "v", "t", u);
273
274 } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) {
275 uint64_t u;
276
277 r = cg_weight_parse(eq, &u);
278 if (r < 0) {
279 log_error("Failed to parse %s value %s.", field, eq);
280 return -EINVAL;
281 }
282
283 r = sd_bus_message_append(m, "v", "t", u);
284
285 } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
286 uint64_t u;
287
288 r = cg_blkio_weight_parse(eq, &u);
289 if (r < 0) {
290 log_error("Failed to parse %s value %s.", field, eq);
291 return -EINVAL;
292 }
293
294 r = sd_bus_message_append(m, "v", "t", u);
295
296 } else if (STR_IN_SET(field,
297 "User", "Group", "DevicePolicy", "KillMode",
298 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
299 "StandardInput", "StandardOutput", "StandardError",
300 "Description", "Slice", "Type", "WorkingDirectory",
301 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
302 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
303 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
304 "KeyringMode"))
305 r = sd_bus_message_append(m, "v", "s", eq);
306
307 else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
308 bool ignore;
309 const char *s;
310
311 if (eq[0] == '-') {
312 ignore = true;
313 s = eq + 1;
314 } else {
315 ignore = false;
316 s = eq;
317 }
318
319 r = sd_bus_message_append(m, "v", "(bs)", ignore, s);
320
321 } else if (streq(field, "SyslogLevel")) {
322 int level;
323
324 level = log_level_from_string(eq);
325 if (level < 0) {
326 log_error("Failed to parse %s value %s.", field, eq);
327 return -EINVAL;
328 }
329
330 r = sd_bus_message_append(m, "v", "i", level);
331
332 } else if (streq(field, "SyslogFacility")) {
333 int facility;
334
335 facility = log_facility_unshifted_from_string(eq);
336 if (facility < 0) {
337 log_error("Failed to parse %s value %s.", field, eq);
338 return -EINVAL;
339 }
340
341 r = sd_bus_message_append(m, "v", "i", facility);
342
343 } else if (streq(field, "SecureBits")) {
344
345 r = secure_bits_from_string(eq);
346 if (r < 0) {
347 log_error("Failed to parse %s value %s.", field, eq);
348 return -EINVAL;
349 }
350
351 r = sd_bus_message_append(m, "v", "i", r);
352
353 } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
354 uint64_t sum = 0;
355 bool invert = false;
356 const char *p;
357
358 p = eq;
359 if (*p == '~') {
360 invert = true;
361 p++;
362 }
363
364 r = capability_set_from_string(p, &sum);
365 if (r < 0) {
366 log_error("Failed to parse %s value %s.", field, eq);
367 return -EINVAL;
368 }
369
370 sum = invert ? ~sum : sum;
371
372 r = sd_bus_message_append(m, "v", "t", sum);
373
374 } else if (streq(field, "DeviceAllow")) {
375
376 if (isempty(eq))
377 r = sd_bus_message_append(m, "v", "a(ss)", 0);
378 else {
379 const char *path, *rwm, *e;
380
381 e = strchr(eq, ' ');
382 if (e) {
383 path = strndupa(eq, e - eq);
384 rwm = e+1;
385 } else {
386 path = eq;
387 rwm = "";
388 }
389
390 if (!is_deviceallow_pattern(path)) {
391 log_error("%s is not a device file in /dev.", path);
392 return -EINVAL;
393 }
394
395 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
396 }
397
398 } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
399
400 if (isempty(eq))
401 r = sd_bus_message_append(m, "v", "a(st)", 0);
402 else {
403 const char *path, *bandwidth, *e;
404 uint64_t bytes;
405
406 e = strchr(eq, ' ');
407 if (e) {
408 path = strndupa(eq, e - eq);
409 bandwidth = e+1;
410 } else {
411 log_error("Failed to parse %s value %s.", field, eq);
412 return -EINVAL;
413 }
414
415 if (!path_startswith(path, "/dev")) {
416 log_error("%s is not a device file in /dev.", path);
417 return -EINVAL;
418 }
419
420 if (streq(bandwidth, "infinity")) {
421 bytes = CGROUP_LIMIT_MAX;
422 } else {
423 r = parse_size(bandwidth, 1000, &bytes);
424 if (r < 0) {
425 log_error("Failed to parse byte value %s.", bandwidth);
426 return -EINVAL;
427 }
428 }
429
430 r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
431 }
432
433 } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
434
435 if (isempty(eq))
436 r = sd_bus_message_append(m, "v", "a(st)", 0);
437 else {
438 const char *path, *weight, *e;
439 uint64_t u;
440
441 e = strchr(eq, ' ');
442 if (e) {
443 path = strndupa(eq, e - eq);
444 weight = e+1;
445 } else {
446 log_error("Failed to parse %s value %s.", field, eq);
447 return -EINVAL;
448 }
449
450 if (!path_startswith(path, "/dev")) {
451 log_error("%s is not a device file in /dev.", path);
452 return -EINVAL;
453 }
454
455 r = safe_atou64(weight, &u);
456 if (r < 0) {
457 log_error("Failed to parse %s value %s.", field, weight);
458 return -EINVAL;
459 }
460 r = sd_bus_message_append(m, "v", "a(st)", 1, path, u);
461 }
462
463 } else if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
464
465 if (isempty(eq))
466 r = sd_bus_message_append(m, "v", "a(iayu)", 0);
467 else {
468 unsigned char prefixlen;
469 union in_addr_union prefix = {};
470 int family;
471
472 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
473 if (r < 0)
474 return bus_log_create_error(r);
475
476 r = sd_bus_message_open_container(m, 'a', "(iayu)");
477 if (r < 0)
478 return bus_log_create_error(r);
479
480 if (streq(eq, "any")) {
481 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
482
483 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
484 if (r < 0)
485 return bus_log_create_error(r);
486
487 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
488 if (r < 0)
489 return bus_log_create_error(r);
490
491 } else if (is_localhost(eq)) {
492 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
493
494 prefix.in.s_addr = htobe32(0x7f000000);
495 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
496 if (r < 0)
497 return bus_log_create_error(r);
498
499 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
500 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
501 if (r < 0)
502 return r;
503
504 } else if (streq(eq, "link-local")) {
505
506 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
507
508 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
509 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
510 if (r < 0)
511 return bus_log_create_error(r);
512
513 prefix.in6 = (struct in6_addr) {
514 .__in6_u.__u6_addr32[0] = htobe32(0xfe800000)
515 };
516 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
517 if (r < 0)
518 return bus_log_create_error(r);
519
520 } else if (streq(eq, "multicast")) {
521
522 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
523
524 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
525 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
526 if (r < 0)
527 return bus_log_create_error(r);
528
529 prefix.in6 = (struct in6_addr) {
530 .__in6_u.__u6_addr32[0] = htobe32(0xff000000)
531 };
532 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
533 if (r < 0)
534 return bus_log_create_error(r);
535
536 } else {
537 r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
538 if (r < 0)
539 return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
540
541 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
542 if (r < 0)
543 return bus_log_create_error(r);
544 }
545
546 r = sd_bus_message_close_container(m);
547 if (r < 0)
548 return bus_log_create_error(r);
549
550 r = sd_bus_message_close_container(m);
551 if (r < 0)
552 return bus_log_create_error(r);
553 }
554
555 } else if (streq(field, "CPUSchedulingPolicy")) {
556 int n;
557
558 n = sched_policy_from_string(eq);
559 if (n < 0)
560 return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq);
561
562 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
563
564 } else if (streq(field, "CPUSchedulingPriority")) {
565 int n;
566
567 r = safe_atoi(eq, &n);
568 if (r < 0)
569 return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq);
570 if (!sched_priority_is_valid(n))
571 return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq);
572
573 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
574
575 } else if (streq(field, "CPUAffinity")) {
576 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
577 int ncpus;
578
579 ncpus = parse_cpu_set(eq, &cpuset);
580 if (ncpus < 0)
581 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
582
583 r = sd_bus_message_open_container(m, 'v', "ay");
584 if (r < 0)
585 return bus_log_create_error(r);
586
587 if (cpuset)
588 sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus));
589
590 r = sd_bus_message_close_container(m);
591
592 } else if (streq(field, "Nice")) {
593 int n;
594
595 r = parse_nice(eq, &n);
596 if (r < 0)
597 return log_error_errno(r, "Failed to parse nice value: %s", eq);
598
599 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
600
601 #ifdef HAVE_SECCOMP
602
603 } else if (streq(field, "SystemCallFilter")) {
604 int whitelist;
605 const char *p;
606
607 r = sd_bus_message_open_container(m, 'v', "bas");
608 if (r < 0)
609 return bus_log_create_error(r);
610
611 p = eq;
612 if (*p == '~') {
613 whitelist = 0;
614 p++;
615 } else
616 whitelist = 1;
617
618 r = sd_bus_message_append_basic(m, 'b', &whitelist);
619 if (r < 0)
620 return bus_log_create_error(r);
621
622 r = sd_bus_message_open_container(m, 'a', "s");
623 if (r < 0)
624 return bus_log_create_error(r);
625
626 if (whitelist != 0) {
627 r = sd_bus_message_append_basic(m, 's', "@default");
628 if (r < 0)
629 return bus_log_create_error(r);
630 }
631
632 for (;;) {
633 _cleanup_free_ char *word = NULL;
634
635 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
636 if (r < 0)
637 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
638 if (r == 0)
639 break;
640
641 r = sd_bus_message_append_basic(m, 's', word);
642 if (r < 0)
643 return bus_log_create_error(r);
644 }
645
646 r = sd_bus_message_close_container(m);
647 if (r < 0)
648 return bus_log_create_error(r);
649
650 r = sd_bus_message_close_container(m);
651
652 } else if (streq(field, "SystemCallArchitectures")) {
653 const char *p;
654
655 r = sd_bus_message_open_container(m, 'v', "as");
656 if (r < 0)
657 return bus_log_create_error(r);
658
659 r = sd_bus_message_open_container(m, 'a', "s");
660 if (r < 0)
661 return bus_log_create_error(r);
662
663 for (p = eq;;) {
664 _cleanup_free_ char *word = NULL;
665
666 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
667 if (r < 0)
668 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
669 if (r == 0)
670 break;
671
672 r = sd_bus_message_append_basic(m, 's', word);
673 if (r < 0)
674 return bus_log_create_error(r);
675 }
676
677 r = sd_bus_message_close_container(m);
678 if (r < 0)
679 return bus_log_create_error(r);
680
681 r = sd_bus_message_close_container(m);
682
683 } else if (streq(field, "SystemCallErrorNumber")) {
684 int n;
685
686 n = errno_from_name(eq);
687 if (n < 0)
688 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
689
690 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
691
692 } else if (streq(field, "RestrictAddressFamilies")) {
693 int whitelist;
694 const char *p;
695
696 r = sd_bus_message_open_container(m, 'v', "bas");
697 if (r < 0)
698 return bus_log_create_error(r);
699
700 p = eq;
701 if (*p == '~') {
702 whitelist = 0;
703 p++;
704 } else
705 whitelist = 1;
706
707 r = sd_bus_message_append_basic(m, 'b', &whitelist);
708 if (r < 0)
709 return bus_log_create_error(r);
710
711 r = sd_bus_message_open_container(m, 'a', "s");
712 if (r < 0)
713 return bus_log_create_error(r);
714
715 for (;;) {
716 _cleanup_free_ char *word = NULL;
717
718 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
719 if (r < 0)
720 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
721 if (r == 0)
722 break;
723
724 r = sd_bus_message_append_basic(m, 's', word);
725 if (r < 0)
726 return bus_log_create_error(r);
727 }
728
729 r = sd_bus_message_close_container(m);
730 if (r < 0)
731 return bus_log_create_error(r);
732
733 r = sd_bus_message_close_container(m);
734
735 #endif
736
737 } else if (streq(field, "FileDescriptorStoreMax")) {
738 unsigned u;
739
740 r = safe_atou(eq, &u);
741 if (r < 0)
742 return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq);
743
744 r = sd_bus_message_append(m, "v", "u", (uint32_t) u);
745
746 } else if (streq(field, "IOSchedulingClass")) {
747 int c;
748
749 c = ioprio_class_from_string(eq);
750 if (c < 0)
751 return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq);
752
753 r = sd_bus_message_append(m, "v", "i", (int32_t) c);
754
755 } else if (streq(field, "IOSchedulingPriority")) {
756 int q;
757
758 r = ioprio_parse_priority(eq, &q);
759 if (r < 0)
760 return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq);
761
762 r = sd_bus_message_append(m, "v", "i", (int32_t) q);
763
764 } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
765 const char *p;
766
767 r = sd_bus_message_open_container(m, 'v', "as");
768 if (r < 0)
769 return bus_log_create_error(r);
770
771 r = sd_bus_message_open_container(m, 'a', "s");
772 if (r < 0)
773 return bus_log_create_error(r);
774
775 for (p = eq;;) {
776 _cleanup_free_ char *word = NULL;
777
778 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
779 if (r < 0) {
780 log_error("Failed to parse Environment value %s", eq);
781 return -EINVAL;
782 }
783 if (r == 0)
784 break;
785
786 if (streq(field, "Environment")) {
787 if (!env_assignment_is_valid(word)) {
788 log_error("Invalid environment assignment: %s", word);
789 return -EINVAL;
790 }
791 } else if (streq(field, "UnsetEnvironment")) {
792 if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
793 log_error("Invalid environment name or assignment: %s", word);
794 return -EINVAL;
795 }
796 } else { /* PassEnvironment */
797 if (!env_name_is_valid(word)) {
798 log_error("Invalid environment variable name: %s", word);
799 return -EINVAL;
800 }
801 }
802
803 r = sd_bus_message_append_basic(m, 's', word);
804 if (r < 0)
805 return bus_log_create_error(r);
806 }
807
808 r = sd_bus_message_close_container(m);
809 if (r < 0)
810 return bus_log_create_error(r);
811
812 r = sd_bus_message_close_container(m);
813
814 } else if (streq(field, "KillSignal")) {
815 int sig;
816
817 sig = signal_from_string_try_harder(eq);
818 if (sig < 0) {
819 log_error("Failed to parse %s value %s.", field, eq);
820 return -EINVAL;
821 }
822
823 r = sd_bus_message_append(m, "v", "i", sig);
824
825 } else if (streq(field, "TimerSlackNSec")) {
826 nsec_t n;
827
828 r = parse_nsec(eq, &n);
829 if (r < 0) {
830 log_error("Failed to parse %s value %s", field, eq);
831 return -EINVAL;
832 }
833
834 r = sd_bus_message_append(m, "v", "t", n);
835 } else if (streq(field, "OOMScoreAdjust")) {
836 int oa;
837
838 r = safe_atoi(eq, &oa);
839 if (r < 0) {
840 log_error("Failed to parse %s value %s", field, eq);
841 return -EINVAL;
842 }
843
844 if (!oom_score_adjust_is_valid(oa)) {
845 log_error("OOM score adjust value out of range");
846 return -EINVAL;
847 }
848
849 r = sd_bus_message_append(m, "v", "i", oa);
850 } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
851 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
852 const char *p;
853
854 r = sd_bus_message_open_container(m, 'v', "as");
855 if (r < 0)
856 return bus_log_create_error(r);
857
858 r = sd_bus_message_open_container(m, 'a', "s");
859 if (r < 0)
860 return bus_log_create_error(r);
861
862 for (p = eq;;) {
863 _cleanup_free_ char *word = NULL;
864 size_t offset;
865
866 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
867 if (r < 0) {
868 log_error("Failed to parse %s value %s", field, eq);
869 return -EINVAL;
870 }
871 if (r == 0)
872 break;
873
874 if (!utf8_is_valid(word)) {
875 log_error("Failed to parse %s value %s", field, eq);
876 return -EINVAL;
877 }
878
879 offset = word[0] == '-';
880 offset += word[offset] == '+';
881
882 if (!path_is_absolute(word + offset)) {
883 log_error("Failed to parse %s value %s", field, eq);
884 return -EINVAL;
885 }
886
887 path_kill_slashes(word + offset);
888
889 r = sd_bus_message_append_basic(m, 's', word);
890 if (r < 0)
891 return bus_log_create_error(r);
892 }
893
894 r = sd_bus_message_close_container(m);
895 if (r < 0)
896 return bus_log_create_error(r);
897
898 r = sd_bus_message_close_container(m);
899
900 } else if (streq(field, "SupplementaryGroups")) {
901 const char *p;
902
903 r = sd_bus_message_open_container(m, 'v', "as");
904 if (r < 0)
905 return bus_log_create_error(r);
906
907 r = sd_bus_message_open_container(m, 'a', "s");
908 if (r < 0)
909 return bus_log_create_error(r);
910
911 for (p = eq;;) {
912 _cleanup_free_ char *word = NULL;
913
914 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
915 if (r < 0) {
916 log_error("Failed to parse %s value %s", field, eq);
917 return -EINVAL;
918 }
919 if (r == 0)
920 break;
921
922 if (!valid_user_group_name_or_id(word)) {
923 log_error("Failed to parse %s value %s", field, eq);
924 return -EINVAL;
925 }
926
927 r = sd_bus_message_append_basic(m, 's', word);
928 if (r < 0)
929 return bus_log_create_error(r);
930 }
931
932 r = sd_bus_message_close_container(m);
933 if (r < 0)
934 return bus_log_create_error(r);
935
936 r = sd_bus_message_close_container(m);
937
938 } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
939 mode_t mode;
940
941 r = parse_mode(eq, &mode);
942 if (r < 0)
943 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
944
945 r = sd_bus_message_append(m, "v", "u", mode);
946
947 } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
948 const char *p;
949
950 r = sd_bus_message_open_container(m, 'v', "as");
951 if (r < 0)
952 return bus_log_create_error(r);
953
954 r = sd_bus_message_open_container(m, 'a', "s");
955 if (r < 0)
956 return bus_log_create_error(r);
957
958 for (p = eq;;) {
959 _cleanup_free_ char *word = NULL;
960
961 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
962 if (r < 0)
963 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
964
965 if (r == 0)
966 break;
967
968 r = sd_bus_message_append_basic(m, 's', word);
969 if (r < 0)
970 return bus_log_create_error(r);
971 }
972
973 r = sd_bus_message_close_container(m);
974 if (r < 0)
975 return bus_log_create_error(r);
976
977 r = sd_bus_message_close_container(m);
978
979 } else if (streq(field, "RestrictNamespaces")) {
980 bool invert = false;
981 unsigned long flags = 0;
982
983 if (eq[0] == '~') {
984 invert = true;
985 eq++;
986 }
987
988 r = parse_boolean(eq);
989 if (r > 0)
990 flags = 0;
991 else if (r == 0)
992 flags = NAMESPACE_FLAGS_ALL;
993 else {
994 r = namespace_flag_from_string_many(eq, &flags);
995 if (r < 0)
996 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
997 }
998
999 if (invert)
1000 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1001
1002 r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
1003 } else if ((dep = unit_dependency_from_string(field)) >= 0)
1004 r = sd_bus_message_append(m, "v", "as", 1, eq);
1005 else if (streq(field, "MountFlags")) {
1006 unsigned long f;
1007
1008 r = mount_propagation_flags_from_string(eq, &f);
1009 if (r < 0)
1010 return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
1011
1012 r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
1013 } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1014 const char *p = eq;
1015
1016 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1017 if (r < 0)
1018 return r;
1019
1020 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1021 if (r < 0)
1022 return r;
1023
1024 for (;;) {
1025 _cleanup_free_ char *source = NULL, *destination = NULL;
1026 char *s = NULL, *d = NULL;
1027 bool ignore_enoent = false;
1028 uint64_t flags = MS_REC;
1029
1030 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1031 if (r < 0)
1032 return log_error_errno(r, "Failed to parse argument: %m");
1033 if (r == 0)
1034 break;
1035
1036 s = source;
1037 if (s[0] == '-') {
1038 ignore_enoent = true;
1039 s++;
1040 }
1041
1042 if (p && p[-1] == ':') {
1043 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1044 if (r < 0)
1045 return log_error_errno(r, "Failed to parse argument: %m");
1046 if (r == 0) {
1047 log_error("Missing argument after ':': %s", eq);
1048 return -EINVAL;
1049 }
1050
1051 d = destination;
1052
1053 if (p && p[-1] == ':') {
1054 _cleanup_free_ char *options = NULL;
1055
1056 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1057 if (r < 0)
1058 return log_error_errno(r, "Failed to parse argument: %m");
1059
1060 if (isempty(options) || streq(options, "rbind"))
1061 flags = MS_REC;
1062 else if (streq(options, "norbind"))
1063 flags = 0;
1064 else {
1065 log_error("Unknown options: %s", eq);
1066 return -EINVAL;
1067 }
1068 }
1069 } else
1070 d = s;
1071
1072
1073 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1074 if (r < 0)
1075 return r;
1076 }
1077
1078 r = sd_bus_message_close_container(m);
1079 if (r < 0)
1080 return r;
1081
1082 r = sd_bus_message_close_container(m);
1083 } else {
1084 log_error("Unknown assignment %s.", assignment);
1085 return -EINVAL;
1086 }
1087
1088 finish:
1089 if (r < 0)
1090 return bus_log_create_error(r);
1091
1092 r = sd_bus_message_close_container(m);
1093 if (r < 0)
1094 return bus_log_create_error(r);
1095
1096 return 0;
1097 }
1098
1099 int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
1100 char **i;
1101 int r;
1102
1103 assert(m);
1104
1105 STRV_FOREACH(i, l) {
1106 r = bus_append_unit_property_assignment(m, *i);
1107 if (r < 0)
1108 return r;
1109 }
1110
1111 return 0;
1112 }
1113
1114 typedef struct BusWaitForJobs {
1115 sd_bus *bus;
1116 Set *jobs;
1117
1118 char *name;
1119 char *result;
1120
1121 sd_bus_slot *slot_job_removed;
1122 sd_bus_slot *slot_disconnected;
1123 } BusWaitForJobs;
1124
1125 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1126 assert(m);
1127
1128 log_error("Warning! D-Bus connection terminated.");
1129 sd_bus_close(sd_bus_message_get_bus(m));
1130
1131 return 0;
1132 }
1133
1134 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1135 const char *path, *unit, *result;
1136 BusWaitForJobs *d = userdata;
1137 uint32_t id;
1138 char *found;
1139 int r;
1140
1141 assert(m);
1142 assert(d);
1143
1144 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1145 if (r < 0) {
1146 bus_log_parse_error(r);
1147 return 0;
1148 }
1149
1150 found = set_remove(d->jobs, (char*) path);
1151 if (!found)
1152 return 0;
1153
1154 free(found);
1155
1156 if (!isempty(result))
1157 d->result = strdup(result);
1158
1159 if (!isempty(unit))
1160 d->name = strdup(unit);
1161
1162 return 0;
1163 }
1164
1165 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1166 if (!d)
1167 return;
1168
1169 set_free_free(d->jobs);
1170
1171 sd_bus_slot_unref(d->slot_disconnected);
1172 sd_bus_slot_unref(d->slot_job_removed);
1173
1174 sd_bus_unref(d->bus);
1175
1176 free(d->name);
1177 free(d->result);
1178
1179 free(d);
1180 }
1181
1182 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1183 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1184 int r;
1185
1186 assert(bus);
1187 assert(ret);
1188
1189 d = new0(BusWaitForJobs, 1);
1190 if (!d)
1191 return -ENOMEM;
1192
1193 d->bus = sd_bus_ref(bus);
1194
1195 /* When we are a bus client we match by sender. Direct
1196 * connections OTOH have no initialized sender field, and
1197 * hence we ignore the sender then */
1198 r = sd_bus_add_match(
1199 bus,
1200 &d->slot_job_removed,
1201 bus->bus_client ?
1202 "type='signal',"
1203 "sender='org.freedesktop.systemd1',"
1204 "interface='org.freedesktop.systemd1.Manager',"
1205 "member='JobRemoved',"
1206 "path='/org/freedesktop/systemd1'" :
1207 "type='signal',"
1208 "interface='org.freedesktop.systemd1.Manager',"
1209 "member='JobRemoved',"
1210 "path='/org/freedesktop/systemd1'",
1211 match_job_removed, d);
1212 if (r < 0)
1213 return r;
1214
1215 r = sd_bus_add_match(
1216 bus,
1217 &d->slot_disconnected,
1218 "type='signal',"
1219 "sender='org.freedesktop.DBus.Local',"
1220 "interface='org.freedesktop.DBus.Local',"
1221 "member='Disconnected'",
1222 match_disconnected, d);
1223 if (r < 0)
1224 return r;
1225
1226 *ret = d;
1227 d = NULL;
1228
1229 return 0;
1230 }
1231
1232 static int bus_process_wait(sd_bus *bus) {
1233 int r;
1234
1235 for (;;) {
1236 r = sd_bus_process(bus, NULL);
1237 if (r < 0)
1238 return r;
1239 if (r > 0)
1240 return 0;
1241
1242 r = sd_bus_wait(bus, (uint64_t) -1);
1243 if (r < 0)
1244 return r;
1245 }
1246 }
1247
1248 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1249 _cleanup_free_ char *dbus_path = NULL;
1250
1251 assert(d);
1252 assert(d->name);
1253 assert(result);
1254
1255 if (!endswith(d->name, ".service"))
1256 return -EINVAL;
1257
1258 dbus_path = unit_dbus_path_from_name(d->name);
1259 if (!dbus_path)
1260 return -ENOMEM;
1261
1262 return sd_bus_get_property_string(d->bus,
1263 "org.freedesktop.systemd1",
1264 dbus_path,
1265 "org.freedesktop.systemd1.Service",
1266 "Result",
1267 NULL,
1268 result);
1269 }
1270
1271 static const struct {
1272 const char *result, *explanation;
1273 } explanations [] = {
1274 { "resources", "of unavailable resources or another system error" },
1275 { "protocol", "the service did not take the steps required by its unit configuration" },
1276 { "timeout", "a timeout was exceeded" },
1277 { "exit-code", "the control process exited with error code" },
1278 { "signal", "a fatal signal was delivered to the control process" },
1279 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1280 { "watchdog", "the service failed to send watchdog ping" },
1281 { "start-limit", "start of the service was attempted too often" }
1282 };
1283
1284 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1285 _cleanup_free_ char *service_shell_quoted = NULL;
1286 const char *systemctl = "systemctl", *journalctl = "journalctl";
1287
1288 assert(service);
1289
1290 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1291
1292 if (extra_args) {
1293 _cleanup_free_ char *t;
1294
1295 t = strv_join((char**) extra_args, " ");
1296 systemctl = strjoina("systemctl ", t ? : "<args>");
1297 journalctl = strjoina("journalctl ", t ? : "<args>");
1298 }
1299
1300 if (!isempty(result)) {
1301 unsigned i;
1302
1303 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1304 if (streq(result, explanations[i].result))
1305 break;
1306
1307 if (i < ELEMENTSOF(explanations)) {
1308 log_error("Job for %s failed because %s.\n"
1309 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1310 service,
1311 explanations[i].explanation,
1312 systemctl,
1313 service_shell_quoted ?: "<service>",
1314 journalctl);
1315 goto finish;
1316 }
1317 }
1318
1319 log_error("Job for %s failed.\n"
1320 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1321 service,
1322 systemctl,
1323 service_shell_quoted ?: "<service>",
1324 journalctl);
1325
1326 finish:
1327 /* For some results maybe additional explanation is required */
1328 if (streq_ptr(result, "start-limit"))
1329 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1330 "followed by \"%1$s start %2$s\" again.",
1331 systemctl,
1332 service_shell_quoted ?: "<service>");
1333 }
1334
1335 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1336 int r = 0;
1337
1338 assert(d->result);
1339
1340 if (!quiet) {
1341 if (streq(d->result, "canceled"))
1342 log_error("Job for %s canceled.", strna(d->name));
1343 else if (streq(d->result, "timeout"))
1344 log_error("Job for %s timed out.", strna(d->name));
1345 else if (streq(d->result, "dependency"))
1346 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1347 else if (streq(d->result, "invalid"))
1348 log_error("%s is not active, cannot reload.", strna(d->name));
1349 else if (streq(d->result, "assert"))
1350 log_error("Assertion failed on job for %s.", strna(d->name));
1351 else if (streq(d->result, "unsupported"))
1352 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1353 else if (streq(d->result, "collected"))
1354 log_error("Queued job for %s was garbage collected.", strna(d->name));
1355 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1356 if (d->name) {
1357 _cleanup_free_ char *result = NULL;
1358 int q;
1359
1360 q = bus_job_get_service_result(d, &result);
1361 if (q < 0)
1362 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1363
1364 log_job_error_with_service_result(d->name, result, extra_args);
1365 } else
1366 log_error("Job failed. See \"journalctl -xe\" for details.");
1367 }
1368 }
1369
1370 if (STR_IN_SET(d->result, "canceled", "collected"))
1371 r = -ECANCELED;
1372 else if (streq(d->result, "timeout"))
1373 r = -ETIME;
1374 else if (streq(d->result, "dependency"))
1375 r = -EIO;
1376 else if (streq(d->result, "invalid"))
1377 r = -ENOEXEC;
1378 else if (streq(d->result, "assert"))
1379 r = -EPROTO;
1380 else if (streq(d->result, "unsupported"))
1381 r = -EOPNOTSUPP;
1382 else if (!STR_IN_SET(d->result, "done", "skipped"))
1383 r = -EIO;
1384
1385 return r;
1386 }
1387
1388 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1389 int r = 0;
1390
1391 assert(d);
1392
1393 while (!set_isempty(d->jobs)) {
1394 int q;
1395
1396 q = bus_process_wait(d->bus);
1397 if (q < 0)
1398 return log_error_errno(q, "Failed to wait for response: %m");
1399
1400 if (d->result) {
1401 q = check_wait_response(d, quiet, extra_args);
1402 /* Return the first error as it is most likely to be
1403 * meaningful. */
1404 if (q < 0 && r == 0)
1405 r = q;
1406
1407 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1408 }
1409
1410 d->name = mfree(d->name);
1411 d->result = mfree(d->result);
1412 }
1413
1414 return r;
1415 }
1416
1417 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1418 int r;
1419
1420 assert(d);
1421
1422 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1423 if (r < 0)
1424 return r;
1425
1426 return set_put_strdup(d->jobs, path);
1427 }
1428
1429 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1430 int r;
1431
1432 r = bus_wait_for_jobs_add(d, path);
1433 if (r < 0)
1434 return log_oom();
1435
1436 return bus_wait_for_jobs(d, quiet, NULL);
1437 }
1438
1439 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1440 const char *type, *path, *source;
1441 int r;
1442
1443 /* changes is dereferenced when calling unit_file_dump_changes() later,
1444 * so we have to make sure this is not NULL. */
1445 assert(changes);
1446 assert(n_changes);
1447
1448 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1449 if (r < 0)
1450 return bus_log_parse_error(r);
1451
1452 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1453 /* We expect only "success" changes to be sent over the bus.
1454 Hence, reject anything negative. */
1455 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1456
1457 if (ch < 0) {
1458 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1459 continue;
1460 }
1461
1462 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1463 if (r < 0)
1464 return r;
1465 }
1466 if (r < 0)
1467 return bus_log_parse_error(r);
1468
1469 r = sd_bus_message_exit_container(m);
1470 if (r < 0)
1471 return bus_log_parse_error(r);
1472
1473 unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
1474 return 0;
1475 }
1476
1477 struct CGroupInfo {
1478 char *cgroup_path;
1479 bool is_const; /* If false, cgroup_path should be free()'d */
1480
1481 Hashmap *pids; /* PID → process name */
1482 bool done;
1483
1484 struct CGroupInfo *parent;
1485 LIST_FIELDS(struct CGroupInfo, siblings);
1486 LIST_HEAD(struct CGroupInfo, children);
1487 size_t n_children;
1488 };
1489
1490 static bool IS_ROOT(const char *p) {
1491 return isempty(p) || streq(p, "/");
1492 }
1493
1494 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1495 struct CGroupInfo *parent = NULL, *cg;
1496 int r;
1497
1498 assert(cgroups);
1499 assert(ret);
1500
1501 if (IS_ROOT(path))
1502 path = "/";
1503
1504 cg = hashmap_get(cgroups, path);
1505 if (cg) {
1506 *ret = cg;
1507 return 0;
1508 }
1509
1510 if (!IS_ROOT(path)) {
1511 const char *e, *pp;
1512
1513 e = strrchr(path, '/');
1514 if (!e)
1515 return -EINVAL;
1516
1517 pp = strndupa(path, e - path);
1518 if (!pp)
1519 return -ENOMEM;
1520
1521 r = add_cgroup(cgroups, pp, false, &parent);
1522 if (r < 0)
1523 return r;
1524 }
1525
1526 cg = new0(struct CGroupInfo, 1);
1527 if (!cg)
1528 return -ENOMEM;
1529
1530 if (is_const)
1531 cg->cgroup_path = (char*) path;
1532 else {
1533 cg->cgroup_path = strdup(path);
1534 if (!cg->cgroup_path) {
1535 free(cg);
1536 return -ENOMEM;
1537 }
1538 }
1539
1540 cg->is_const = is_const;
1541 cg->parent = parent;
1542
1543 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1544 if (r < 0) {
1545 if (!is_const)
1546 free(cg->cgroup_path);
1547 free(cg);
1548 return r;
1549 }
1550
1551 if (parent) {
1552 LIST_PREPEND(siblings, parent->children, cg);
1553 parent->n_children++;
1554 }
1555
1556 *ret = cg;
1557 return 1;
1558 }
1559
1560 static int add_process(
1561 Hashmap *cgroups,
1562 const char *path,
1563 pid_t pid,
1564 const char *name) {
1565
1566 struct CGroupInfo *cg;
1567 int r;
1568
1569 assert(cgroups);
1570 assert(name);
1571 assert(pid > 0);
1572
1573 r = add_cgroup(cgroups, path, true, &cg);
1574 if (r < 0)
1575 return r;
1576
1577 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1578 if (r < 0)
1579 return r;
1580
1581 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1582 }
1583
1584 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1585 assert(cgroups);
1586 assert(cg);
1587
1588 while (cg->children)
1589 remove_cgroup(cgroups, cg->children);
1590
1591 hashmap_remove(cgroups, cg->cgroup_path);
1592
1593 if (!cg->is_const)
1594 free(cg->cgroup_path);
1595
1596 hashmap_free(cg->pids);
1597
1598 if (cg->parent)
1599 LIST_REMOVE(siblings, cg->parent->children, cg);
1600
1601 free(cg);
1602 }
1603
1604 static int cgroup_info_compare_func(const void *a, const void *b) {
1605 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1606
1607 assert(x);
1608 assert(y);
1609
1610 return strcmp(x->cgroup_path, y->cgroup_path);
1611 }
1612
1613 static int dump_processes(
1614 Hashmap *cgroups,
1615 const char *cgroup_path,
1616 const char *prefix,
1617 unsigned n_columns,
1618 OutputFlags flags) {
1619
1620 struct CGroupInfo *cg;
1621 int r;
1622
1623 assert(prefix);
1624
1625 if (IS_ROOT(cgroup_path))
1626 cgroup_path = "/";
1627
1628 cg = hashmap_get(cgroups, cgroup_path);
1629 if (!cg)
1630 return 0;
1631
1632 if (!hashmap_isempty(cg->pids)) {
1633 const char *name;
1634 size_t n = 0, i;
1635 pid_t *pids;
1636 void *pidp;
1637 Iterator j;
1638 int width;
1639
1640 /* Order processes by their PID */
1641 pids = newa(pid_t, hashmap_size(cg->pids));
1642
1643 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1644 pids[n++] = PTR_TO_PID(pidp);
1645
1646 assert(n == hashmap_size(cg->pids));
1647 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1648
1649 width = DECIMAL_STR_WIDTH(pids[n-1]);
1650
1651 for (i = 0; i < n; i++) {
1652 _cleanup_free_ char *e = NULL;
1653 const char *special;
1654 bool more;
1655
1656 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1657 assert(name);
1658
1659 if (n_columns != 0) {
1660 unsigned k;
1661
1662 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1663
1664 e = ellipsize(name, k, 100);
1665 if (e)
1666 name = e;
1667 }
1668
1669 more = i+1 < n || cg->children;
1670 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1671
1672 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1673 prefix,
1674 special,
1675 width, pids[i],
1676 name);
1677 }
1678 }
1679
1680 if (cg->children) {
1681 struct CGroupInfo **children, *child;
1682 size_t n = 0, i;
1683
1684 /* Order subcgroups by their name */
1685 children = newa(struct CGroupInfo*, cg->n_children);
1686 LIST_FOREACH(siblings, child, cg->children)
1687 children[n++] = child;
1688 assert(n == cg->n_children);
1689 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1690
1691 if (n_columns != 0)
1692 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1693
1694 for (i = 0; i < n; i++) {
1695 _cleanup_free_ char *pp = NULL;
1696 const char *name, *special;
1697 bool more;
1698
1699 child = children[i];
1700
1701 name = strrchr(child->cgroup_path, '/');
1702 if (!name)
1703 return -EINVAL;
1704 name++;
1705
1706 more = i+1 < n;
1707 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1708
1709 fputs(prefix, stdout);
1710 fputs(special, stdout);
1711 fputs(name, stdout);
1712 fputc('\n', stdout);
1713
1714 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1715
1716 pp = strappend(prefix, special);
1717 if (!pp)
1718 return -ENOMEM;
1719
1720 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1721 if (r < 0)
1722 return r;
1723 }
1724 }
1725
1726 cg->done = true;
1727 return 0;
1728 }
1729
1730 static int dump_extra_processes(
1731 Hashmap *cgroups,
1732 const char *prefix,
1733 unsigned n_columns,
1734 OutputFlags flags) {
1735
1736 _cleanup_free_ pid_t *pids = NULL;
1737 _cleanup_hashmap_free_ Hashmap *names = NULL;
1738 struct CGroupInfo *cg;
1739 size_t n_allocated = 0, n = 0, k;
1740 Iterator i;
1741 int width, r;
1742
1743 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1744 * combined, sorted, linear list. */
1745
1746 HASHMAP_FOREACH(cg, cgroups, i) {
1747 const char *name;
1748 void *pidp;
1749 Iterator j;
1750
1751 if (cg->done)
1752 continue;
1753
1754 if (hashmap_isempty(cg->pids))
1755 continue;
1756
1757 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1758 if (r < 0)
1759 return r;
1760
1761 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1762 return -ENOMEM;
1763
1764 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1765 pids[n++] = PTR_TO_PID(pidp);
1766
1767 r = hashmap_put(names, pidp, (void*) name);
1768 if (r < 0)
1769 return r;
1770 }
1771 }
1772
1773 if (n == 0)
1774 return 0;
1775
1776 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1777 width = DECIMAL_STR_WIDTH(pids[n-1]);
1778
1779 for (k = 0; k < n; k++) {
1780 _cleanup_free_ char *e = NULL;
1781 const char *name;
1782
1783 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1784 assert(name);
1785
1786 if (n_columns != 0) {
1787 unsigned z;
1788
1789 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1790
1791 e = ellipsize(name, z, 100);
1792 if (e)
1793 name = e;
1794 }
1795
1796 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1797 prefix,
1798 special_glyph(TRIANGULAR_BULLET),
1799 width, pids[k],
1800 name);
1801 }
1802
1803 return 0;
1804 }
1805
1806 int unit_show_processes(
1807 sd_bus *bus,
1808 const char *unit,
1809 const char *cgroup_path,
1810 const char *prefix,
1811 unsigned n_columns,
1812 OutputFlags flags,
1813 sd_bus_error *error) {
1814
1815 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1816 Hashmap *cgroups = NULL;
1817 struct CGroupInfo *cg;
1818 int r;
1819
1820 assert(bus);
1821 assert(unit);
1822
1823 if (flags & OUTPUT_FULL_WIDTH)
1824 n_columns = 0;
1825 else if (n_columns <= 0)
1826 n_columns = columns();
1827
1828 prefix = strempty(prefix);
1829
1830 r = sd_bus_call_method(
1831 bus,
1832 "org.freedesktop.systemd1",
1833 "/org/freedesktop/systemd1",
1834 "org.freedesktop.systemd1.Manager",
1835 "GetUnitProcesses",
1836 error,
1837 &reply,
1838 "s",
1839 unit);
1840 if (r < 0)
1841 return r;
1842
1843 cgroups = hashmap_new(&string_hash_ops);
1844 if (!cgroups)
1845 return -ENOMEM;
1846
1847 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1848 if (r < 0)
1849 goto finish;
1850
1851 for (;;) {
1852 const char *path = NULL, *name = NULL;
1853 uint32_t pid;
1854
1855 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1856 if (r < 0)
1857 goto finish;
1858 if (r == 0)
1859 break;
1860
1861 r = add_process(cgroups, path, pid, name);
1862 if (r < 0)
1863 goto finish;
1864 }
1865
1866 r = sd_bus_message_exit_container(reply);
1867 if (r < 0)
1868 goto finish;
1869
1870 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1871 if (r < 0)
1872 goto finish;
1873
1874 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1875
1876 finish:
1877 while ((cg = hashmap_first(cgroups)))
1878 remove_cgroup(cgroups, cg);
1879
1880 hashmap_free(cgroups);
1881
1882 return r;
1883 }