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