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