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