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