]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
build-sys: use #if Y instead of #ifdef Y everywhere
[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 const char *p;
606
607 r = sd_bus_message_open_container(m, 'v', "bas");
608 if (r < 0)
609 return bus_log_create_error(r);
610
611 p = eq;
612 if (*p == '~') {
613 whitelist = 0;
614 p++;
615 } else
616 whitelist = 1;
617
618 r = sd_bus_message_append_basic(m, 'b', &whitelist);
619 if (r < 0)
620 return bus_log_create_error(r);
621
622 r = sd_bus_message_open_container(m, 'a', "s");
623 if (r < 0)
624 return bus_log_create_error(r);
625
626 if (whitelist != 0) {
627 r = sd_bus_message_append_basic(m, 's', "@default");
628 if (r < 0)
629 return bus_log_create_error(r);
630 }
631
632 for (;;) {
633 _cleanup_free_ char *word = NULL;
634
635 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
636 if (r < 0)
637 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
638 if (r == 0)
639 break;
640
641 r = sd_bus_message_append_basic(m, 's', word);
642 if (r < 0)
643 return bus_log_create_error(r);
644 }
645
646 r = sd_bus_message_close_container(m);
647 if (r < 0)
648 return bus_log_create_error(r);
649
650 r = sd_bus_message_close_container(m);
651
652 } else if (streq(field, "SystemCallArchitectures")) {
653 const char *p;
654
655 r = sd_bus_message_open_container(m, 'v', "as");
656 if (r < 0)
657 return bus_log_create_error(r);
658
659 r = sd_bus_message_open_container(m, 'a', "s");
660 if (r < 0)
661 return bus_log_create_error(r);
662
663 for (p = eq;;) {
664 _cleanup_free_ char *word = NULL;
665
666 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
667 if (r < 0)
668 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
669 if (r == 0)
670 break;
671
672 r = sd_bus_message_append_basic(m, 's', word);
673 if (r < 0)
674 return bus_log_create_error(r);
675 }
676
677 r = sd_bus_message_close_container(m);
678 if (r < 0)
679 return bus_log_create_error(r);
680
681 r = sd_bus_message_close_container(m);
682
683 } else if (streq(field, "SystemCallErrorNumber")) {
684 int n;
685
686 n = errno_from_name(eq);
687 if (n < 0)
688 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
689
690 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
691
692 } else if (streq(field, "RestrictAddressFamilies")) {
693 int whitelist;
694 const char *p;
695
696 r = sd_bus_message_open_container(m, 'v', "bas");
697 if (r < 0)
698 return bus_log_create_error(r);
699
700 p = eq;
701 if (*p == '~') {
702 whitelist = 0;
703 p++;
704 } else
705 whitelist = 1;
706
707 r = sd_bus_message_append_basic(m, 'b', &whitelist);
708 if (r < 0)
709 return bus_log_create_error(r);
710
711 r = sd_bus_message_open_container(m, 'a', "s");
712 if (r < 0)
713 return bus_log_create_error(r);
714
715 for (;;) {
716 _cleanup_free_ char *word = NULL;
717
718 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
719 if (r < 0)
720 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
721 if (r == 0)
722 break;
723
724 r = sd_bus_message_append_basic(m, 's', word);
725 if (r < 0)
726 return bus_log_create_error(r);
727 }
728
729 r = sd_bus_message_close_container(m);
730 if (r < 0)
731 return bus_log_create_error(r);
732
733 r = sd_bus_message_close_container(m);
734
735 #endif
736
737 } else if (streq(field, "FileDescriptorStoreMax")) {
738 unsigned u;
739
740 r = safe_atou(eq, &u);
741 if (r < 0)
742 return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq);
743
744 r = sd_bus_message_append(m, "v", "u", (uint32_t) u);
745
746 } else if (streq(field, "IOSchedulingClass")) {
747 int c;
748
749 c = ioprio_class_from_string(eq);
750 if (c < 0)
751 return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq);
752
753 r = sd_bus_message_append(m, "v", "i", (int32_t) c);
754
755 } else if (streq(field, "IOSchedulingPriority")) {
756 int q;
757
758 r = ioprio_parse_priority(eq, &q);
759 if (r < 0)
760 return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq);
761
762 r = sd_bus_message_append(m, "v", "i", (int32_t) q);
763
764 } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
765 const char *p;
766
767 r = sd_bus_message_open_container(m, 'v', "as");
768 if (r < 0)
769 return bus_log_create_error(r);
770
771 r = sd_bus_message_open_container(m, 'a', "s");
772 if (r < 0)
773 return bus_log_create_error(r);
774
775 for (p = eq;;) {
776 _cleanup_free_ char *word = NULL;
777
778 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
779 if (r < 0) {
780 log_error("Failed to parse Environment value %s", eq);
781 return -EINVAL;
782 }
783 if (r == 0)
784 break;
785
786 if (streq(field, "Environment")) {
787 if (!env_assignment_is_valid(word)) {
788 log_error("Invalid environment assignment: %s", word);
789 return -EINVAL;
790 }
791 } else if (streq(field, "UnsetEnvironment")) {
792 if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
793 log_error("Invalid environment name or assignment: %s", word);
794 return -EINVAL;
795 }
796 } else { /* PassEnvironment */
797 if (!env_name_is_valid(word)) {
798 log_error("Invalid environment variable name: %s", word);
799 return -EINVAL;
800 }
801 }
802
803 r = sd_bus_message_append_basic(m, 's', word);
804 if (r < 0)
805 return bus_log_create_error(r);
806 }
807
808 r = sd_bus_message_close_container(m);
809 if (r < 0)
810 return bus_log_create_error(r);
811
812 r = sd_bus_message_close_container(m);
813
814 } else if (streq(field, "KillSignal")) {
815 int sig;
816
817 sig = signal_from_string_try_harder(eq);
818 if (sig < 0) {
819 log_error("Failed to parse %s value %s.", field, eq);
820 return -EINVAL;
821 }
822
823 r = sd_bus_message_append(m, "v", "i", sig);
824
825 } else if (streq(field, "TimerSlackNSec")) {
826 nsec_t n;
827
828 r = parse_nsec(eq, &n);
829 if (r < 0) {
830 log_error("Failed to parse %s value %s", field, eq);
831 return -EINVAL;
832 }
833
834 r = sd_bus_message_append(m, "v", "t", n);
835 } else if (streq(field, "OOMScoreAdjust")) {
836 int oa;
837
838 r = safe_atoi(eq, &oa);
839 if (r < 0) {
840 log_error("Failed to parse %s value %s", field, eq);
841 return -EINVAL;
842 }
843
844 if (!oom_score_adjust_is_valid(oa)) {
845 log_error("OOM score adjust value out of range");
846 return -EINVAL;
847 }
848
849 r = sd_bus_message_append(m, "v", "i", oa);
850 } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
851 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
852 const char *p;
853
854 r = sd_bus_message_open_container(m, 'v', "as");
855 if (r < 0)
856 return bus_log_create_error(r);
857
858 r = sd_bus_message_open_container(m, 'a', "s");
859 if (r < 0)
860 return bus_log_create_error(r);
861
862 for (p = eq;;) {
863 _cleanup_free_ char *word = NULL;
864 size_t offset;
865
866 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
867 if (r < 0) {
868 log_error("Failed to parse %s value %s", field, eq);
869 return -EINVAL;
870 }
871 if (r == 0)
872 break;
873
874 if (!utf8_is_valid(word)) {
875 log_error("Failed to parse %s value %s", field, eq);
876 return -EINVAL;
877 }
878
879 offset = word[0] == '-';
880 offset += word[offset] == '+';
881
882 if (!path_is_absolute(word + offset)) {
883 log_error("Failed to parse %s value %s", field, eq);
884 return -EINVAL;
885 }
886
887 path_kill_slashes(word + offset);
888
889 r = sd_bus_message_append_basic(m, 's', word);
890 if (r < 0)
891 return bus_log_create_error(r);
892 }
893
894 r = sd_bus_message_close_container(m);
895 if (r < 0)
896 return bus_log_create_error(r);
897
898 r = sd_bus_message_close_container(m);
899
900 } else if (streq(field, "SupplementaryGroups")) {
901 const char *p;
902
903 r = sd_bus_message_open_container(m, 'v', "as");
904 if (r < 0)
905 return bus_log_create_error(r);
906
907 r = sd_bus_message_open_container(m, 'a', "s");
908 if (r < 0)
909 return bus_log_create_error(r);
910
911 for (p = eq;;) {
912 _cleanup_free_ char *word = NULL;
913
914 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
915 if (r < 0) {
916 log_error("Failed to parse %s value %s", field, eq);
917 return -EINVAL;
918 }
919 if (r == 0)
920 break;
921
922 if (!valid_user_group_name_or_id(word)) {
923 log_error("Failed to parse %s value %s", field, eq);
924 return -EINVAL;
925 }
926
927 r = sd_bus_message_append_basic(m, 's', word);
928 if (r < 0)
929 return bus_log_create_error(r);
930 }
931
932 r = sd_bus_message_close_container(m);
933 if (r < 0)
934 return bus_log_create_error(r);
935
936 r = sd_bus_message_close_container(m);
937
938 } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
939 mode_t mode;
940
941 r = parse_mode(eq, &mode);
942 if (r < 0)
943 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
944
945 r = sd_bus_message_append(m, "v", "u", mode);
946
947 } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
948 const char *p;
949
950 r = sd_bus_message_open_container(m, 'v', "as");
951 if (r < 0)
952 return bus_log_create_error(r);
953
954 r = sd_bus_message_open_container(m, 'a', "s");
955 if (r < 0)
956 return bus_log_create_error(r);
957
958 for (p = eq;;) {
959 _cleanup_free_ char *word = NULL;
960
961 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
962 if (r == -ENOMEM)
963 return log_oom();
964 if (r < 0)
965 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
966 if (r == 0)
967 break;
968
969 r = sd_bus_message_append_basic(m, 's', word);
970 if (r < 0)
971 return bus_log_create_error(r);
972 }
973
974 r = sd_bus_message_close_container(m);
975 if (r < 0)
976 return bus_log_create_error(r);
977
978 r = sd_bus_message_close_container(m);
979
980 } else if (streq(field, "RestrictNamespaces")) {
981 bool invert = false;
982 unsigned long flags = 0;
983
984 if (eq[0] == '~') {
985 invert = true;
986 eq++;
987 }
988
989 r = parse_boolean(eq);
990 if (r > 0)
991 flags = 0;
992 else if (r == 0)
993 flags = NAMESPACE_FLAGS_ALL;
994 else {
995 r = namespace_flag_from_string_many(eq, &flags);
996 if (r < 0)
997 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
998 }
999
1000 if (invert)
1001 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1002
1003 r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
1004 } else if ((dep = unit_dependency_from_string(field)) >= 0)
1005 r = sd_bus_message_append(m, "v", "as", 1, eq);
1006 else if (streq(field, "MountFlags")) {
1007 unsigned long f;
1008
1009 r = mount_propagation_flags_from_string(eq, &f);
1010 if (r < 0)
1011 return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
1012
1013 r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
1014 } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1015 const char *p = eq;
1016
1017 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1018 if (r < 0)
1019 return r;
1020
1021 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1022 if (r < 0)
1023 return r;
1024
1025 for (;;) {
1026 _cleanup_free_ char *source = NULL, *destination = NULL;
1027 char *s = NULL, *d = NULL;
1028 bool ignore_enoent = false;
1029 uint64_t flags = MS_REC;
1030
1031 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1032 if (r < 0)
1033 return log_error_errno(r, "Failed to parse argument: %m");
1034 if (r == 0)
1035 break;
1036
1037 s = source;
1038 if (s[0] == '-') {
1039 ignore_enoent = true;
1040 s++;
1041 }
1042
1043 if (p && p[-1] == ':') {
1044 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1045 if (r < 0)
1046 return log_error_errno(r, "Failed to parse argument: %m");
1047 if (r == 0) {
1048 log_error("Missing argument after ':': %s", eq);
1049 return -EINVAL;
1050 }
1051
1052 d = destination;
1053
1054 if (p && p[-1] == ':') {
1055 _cleanup_free_ char *options = NULL;
1056
1057 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1058 if (r < 0)
1059 return log_error_errno(r, "Failed to parse argument: %m");
1060
1061 if (isempty(options) || streq(options, "rbind"))
1062 flags = MS_REC;
1063 else if (streq(options, "norbind"))
1064 flags = 0;
1065 else {
1066 log_error("Unknown options: %s", eq);
1067 return -EINVAL;
1068 }
1069 }
1070 } else
1071 d = s;
1072
1073
1074 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1075 if (r < 0)
1076 return r;
1077 }
1078
1079 r = sd_bus_message_close_container(m);
1080 if (r < 0)
1081 return r;
1082
1083 r = sd_bus_message_close_container(m);
1084 } else {
1085 log_error("Unknown assignment %s.", assignment);
1086 return -EINVAL;
1087 }
1088
1089 finish:
1090 if (r < 0)
1091 return bus_log_create_error(r);
1092
1093 r = sd_bus_message_close_container(m);
1094 if (r < 0)
1095 return bus_log_create_error(r);
1096
1097 return 0;
1098 }
1099
1100 int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
1101 char **i;
1102 int r;
1103
1104 assert(m);
1105
1106 STRV_FOREACH(i, l) {
1107 r = bus_append_unit_property_assignment(m, *i);
1108 if (r < 0)
1109 return r;
1110 }
1111
1112 return 0;
1113 }
1114
1115 typedef struct BusWaitForJobs {
1116 sd_bus *bus;
1117 Set *jobs;
1118
1119 char *name;
1120 char *result;
1121
1122 sd_bus_slot *slot_job_removed;
1123 sd_bus_slot *slot_disconnected;
1124 } BusWaitForJobs;
1125
1126 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1127 assert(m);
1128
1129 log_error("Warning! D-Bus connection terminated.");
1130 sd_bus_close(sd_bus_message_get_bus(m));
1131
1132 return 0;
1133 }
1134
1135 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1136 const char *path, *unit, *result;
1137 BusWaitForJobs *d = userdata;
1138 uint32_t id;
1139 char *found;
1140 int r;
1141
1142 assert(m);
1143 assert(d);
1144
1145 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1146 if (r < 0) {
1147 bus_log_parse_error(r);
1148 return 0;
1149 }
1150
1151 found = set_remove(d->jobs, (char*) path);
1152 if (!found)
1153 return 0;
1154
1155 free(found);
1156
1157 if (!isempty(result))
1158 d->result = strdup(result);
1159
1160 if (!isempty(unit))
1161 d->name = strdup(unit);
1162
1163 return 0;
1164 }
1165
1166 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1167 if (!d)
1168 return;
1169
1170 set_free_free(d->jobs);
1171
1172 sd_bus_slot_unref(d->slot_disconnected);
1173 sd_bus_slot_unref(d->slot_job_removed);
1174
1175 sd_bus_unref(d->bus);
1176
1177 free(d->name);
1178 free(d->result);
1179
1180 free(d);
1181 }
1182
1183 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1184 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1185 int r;
1186
1187 assert(bus);
1188 assert(ret);
1189
1190 d = new0(BusWaitForJobs, 1);
1191 if (!d)
1192 return -ENOMEM;
1193
1194 d->bus = sd_bus_ref(bus);
1195
1196 /* When we are a bus client we match by sender. Direct
1197 * connections OTOH have no initialized sender field, and
1198 * hence we ignore the sender then */
1199 r = sd_bus_add_match(
1200 bus,
1201 &d->slot_job_removed,
1202 bus->bus_client ?
1203 "type='signal',"
1204 "sender='org.freedesktop.systemd1',"
1205 "interface='org.freedesktop.systemd1.Manager',"
1206 "member='JobRemoved',"
1207 "path='/org/freedesktop/systemd1'" :
1208 "type='signal',"
1209 "interface='org.freedesktop.systemd1.Manager',"
1210 "member='JobRemoved',"
1211 "path='/org/freedesktop/systemd1'",
1212 match_job_removed, d);
1213 if (r < 0)
1214 return r;
1215
1216 r = sd_bus_add_match(
1217 bus,
1218 &d->slot_disconnected,
1219 "type='signal',"
1220 "sender='org.freedesktop.DBus.Local',"
1221 "interface='org.freedesktop.DBus.Local',"
1222 "member='Disconnected'",
1223 match_disconnected, d);
1224 if (r < 0)
1225 return r;
1226
1227 *ret = d;
1228 d = NULL;
1229
1230 return 0;
1231 }
1232
1233 static int bus_process_wait(sd_bus *bus) {
1234 int r;
1235
1236 for (;;) {
1237 r = sd_bus_process(bus, NULL);
1238 if (r < 0)
1239 return r;
1240 if (r > 0)
1241 return 0;
1242
1243 r = sd_bus_wait(bus, (uint64_t) -1);
1244 if (r < 0)
1245 return r;
1246 }
1247 }
1248
1249 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1250 _cleanup_free_ char *dbus_path = NULL;
1251
1252 assert(d);
1253 assert(d->name);
1254 assert(result);
1255
1256 if (!endswith(d->name, ".service"))
1257 return -EINVAL;
1258
1259 dbus_path = unit_dbus_path_from_name(d->name);
1260 if (!dbus_path)
1261 return -ENOMEM;
1262
1263 return sd_bus_get_property_string(d->bus,
1264 "org.freedesktop.systemd1",
1265 dbus_path,
1266 "org.freedesktop.systemd1.Service",
1267 "Result",
1268 NULL,
1269 result);
1270 }
1271
1272 static const struct {
1273 const char *result, *explanation;
1274 } explanations [] = {
1275 { "resources", "of unavailable resources or another system error" },
1276 { "protocol", "the service did not take the steps required by its unit configuration" },
1277 { "timeout", "a timeout was exceeded" },
1278 { "exit-code", "the control process exited with error code" },
1279 { "signal", "a fatal signal was delivered to the control process" },
1280 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1281 { "watchdog", "the service failed to send watchdog ping" },
1282 { "start-limit", "start of the service was attempted too often" }
1283 };
1284
1285 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1286 _cleanup_free_ char *service_shell_quoted = NULL;
1287 const char *systemctl = "systemctl", *journalctl = "journalctl";
1288
1289 assert(service);
1290
1291 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1292
1293 if (extra_args) {
1294 _cleanup_free_ char *t;
1295
1296 t = strv_join((char**) extra_args, " ");
1297 systemctl = strjoina("systemctl ", t ? : "<args>");
1298 journalctl = strjoina("journalctl ", t ? : "<args>");
1299 }
1300
1301 if (!isempty(result)) {
1302 unsigned i;
1303
1304 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1305 if (streq(result, explanations[i].result))
1306 break;
1307
1308 if (i < ELEMENTSOF(explanations)) {
1309 log_error("Job for %s failed because %s.\n"
1310 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1311 service,
1312 explanations[i].explanation,
1313 systemctl,
1314 service_shell_quoted ?: "<service>",
1315 journalctl);
1316 goto finish;
1317 }
1318 }
1319
1320 log_error("Job for %s failed.\n"
1321 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1322 service,
1323 systemctl,
1324 service_shell_quoted ?: "<service>",
1325 journalctl);
1326
1327 finish:
1328 /* For some results maybe additional explanation is required */
1329 if (streq_ptr(result, "start-limit"))
1330 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1331 "followed by \"%1$s start %2$s\" again.",
1332 systemctl,
1333 service_shell_quoted ?: "<service>");
1334 }
1335
1336 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1337 int r = 0;
1338
1339 assert(d->result);
1340
1341 if (!quiet) {
1342 if (streq(d->result, "canceled"))
1343 log_error("Job for %s canceled.", strna(d->name));
1344 else if (streq(d->result, "timeout"))
1345 log_error("Job for %s timed out.", strna(d->name));
1346 else if (streq(d->result, "dependency"))
1347 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1348 else if (streq(d->result, "invalid"))
1349 log_error("%s is not active, cannot reload.", strna(d->name));
1350 else if (streq(d->result, "assert"))
1351 log_error("Assertion failed on job for %s.", strna(d->name));
1352 else if (streq(d->result, "unsupported"))
1353 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1354 else if (streq(d->result, "collected"))
1355 log_error("Queued job for %s was garbage collected.", strna(d->name));
1356 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1357 if (d->name) {
1358 _cleanup_free_ char *result = NULL;
1359 int q;
1360
1361 q = bus_job_get_service_result(d, &result);
1362 if (q < 0)
1363 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1364
1365 log_job_error_with_service_result(d->name, result, extra_args);
1366 } else
1367 log_error("Job failed. See \"journalctl -xe\" for details.");
1368 }
1369 }
1370
1371 if (STR_IN_SET(d->result, "canceled", "collected"))
1372 r = -ECANCELED;
1373 else if (streq(d->result, "timeout"))
1374 r = -ETIME;
1375 else if (streq(d->result, "dependency"))
1376 r = -EIO;
1377 else if (streq(d->result, "invalid"))
1378 r = -ENOEXEC;
1379 else if (streq(d->result, "assert"))
1380 r = -EPROTO;
1381 else if (streq(d->result, "unsupported"))
1382 r = -EOPNOTSUPP;
1383 else if (!STR_IN_SET(d->result, "done", "skipped"))
1384 r = -EIO;
1385
1386 return r;
1387 }
1388
1389 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1390 int r = 0;
1391
1392 assert(d);
1393
1394 while (!set_isempty(d->jobs)) {
1395 int q;
1396
1397 q = bus_process_wait(d->bus);
1398 if (q < 0)
1399 return log_error_errno(q, "Failed to wait for response: %m");
1400
1401 if (d->result) {
1402 q = check_wait_response(d, quiet, extra_args);
1403 /* Return the first error as it is most likely to be
1404 * meaningful. */
1405 if (q < 0 && r == 0)
1406 r = q;
1407
1408 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1409 }
1410
1411 d->name = mfree(d->name);
1412 d->result = mfree(d->result);
1413 }
1414
1415 return r;
1416 }
1417
1418 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1419 int r;
1420
1421 assert(d);
1422
1423 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1424 if (r < 0)
1425 return r;
1426
1427 return set_put_strdup(d->jobs, path);
1428 }
1429
1430 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1431 int r;
1432
1433 r = bus_wait_for_jobs_add(d, path);
1434 if (r < 0)
1435 return log_oom();
1436
1437 return bus_wait_for_jobs(d, quiet, NULL);
1438 }
1439
1440 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1441 const char *type, *path, *source;
1442 int r;
1443
1444 /* changes is dereferenced when calling unit_file_dump_changes() later,
1445 * so we have to make sure this is not NULL. */
1446 assert(changes);
1447 assert(n_changes);
1448
1449 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1450 if (r < 0)
1451 return bus_log_parse_error(r);
1452
1453 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1454 /* We expect only "success" changes to be sent over the bus.
1455 Hence, reject anything negative. */
1456 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1457
1458 if (ch < 0) {
1459 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1460 continue;
1461 }
1462
1463 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1464 if (r < 0)
1465 return r;
1466 }
1467 if (r < 0)
1468 return bus_log_parse_error(r);
1469
1470 r = sd_bus_message_exit_container(m);
1471 if (r < 0)
1472 return bus_log_parse_error(r);
1473
1474 unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
1475 return 0;
1476 }
1477
1478 struct CGroupInfo {
1479 char *cgroup_path;
1480 bool is_const; /* If false, cgroup_path should be free()'d */
1481
1482 Hashmap *pids; /* PID → process name */
1483 bool done;
1484
1485 struct CGroupInfo *parent;
1486 LIST_FIELDS(struct CGroupInfo, siblings);
1487 LIST_HEAD(struct CGroupInfo, children);
1488 size_t n_children;
1489 };
1490
1491 static bool IS_ROOT(const char *p) {
1492 return isempty(p) || streq(p, "/");
1493 }
1494
1495 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1496 struct CGroupInfo *parent = NULL, *cg;
1497 int r;
1498
1499 assert(cgroups);
1500 assert(ret);
1501
1502 if (IS_ROOT(path))
1503 path = "/";
1504
1505 cg = hashmap_get(cgroups, path);
1506 if (cg) {
1507 *ret = cg;
1508 return 0;
1509 }
1510
1511 if (!IS_ROOT(path)) {
1512 const char *e, *pp;
1513
1514 e = strrchr(path, '/');
1515 if (!e)
1516 return -EINVAL;
1517
1518 pp = strndupa(path, e - path);
1519 if (!pp)
1520 return -ENOMEM;
1521
1522 r = add_cgroup(cgroups, pp, false, &parent);
1523 if (r < 0)
1524 return r;
1525 }
1526
1527 cg = new0(struct CGroupInfo, 1);
1528 if (!cg)
1529 return -ENOMEM;
1530
1531 if (is_const)
1532 cg->cgroup_path = (char*) path;
1533 else {
1534 cg->cgroup_path = strdup(path);
1535 if (!cg->cgroup_path) {
1536 free(cg);
1537 return -ENOMEM;
1538 }
1539 }
1540
1541 cg->is_const = is_const;
1542 cg->parent = parent;
1543
1544 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1545 if (r < 0) {
1546 if (!is_const)
1547 free(cg->cgroup_path);
1548 free(cg);
1549 return r;
1550 }
1551
1552 if (parent) {
1553 LIST_PREPEND(siblings, parent->children, cg);
1554 parent->n_children++;
1555 }
1556
1557 *ret = cg;
1558 return 1;
1559 }
1560
1561 static int add_process(
1562 Hashmap *cgroups,
1563 const char *path,
1564 pid_t pid,
1565 const char *name) {
1566
1567 struct CGroupInfo *cg;
1568 int r;
1569
1570 assert(cgroups);
1571 assert(name);
1572 assert(pid > 0);
1573
1574 r = add_cgroup(cgroups, path, true, &cg);
1575 if (r < 0)
1576 return r;
1577
1578 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1579 if (r < 0)
1580 return r;
1581
1582 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1583 }
1584
1585 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1586 assert(cgroups);
1587 assert(cg);
1588
1589 while (cg->children)
1590 remove_cgroup(cgroups, cg->children);
1591
1592 hashmap_remove(cgroups, cg->cgroup_path);
1593
1594 if (!cg->is_const)
1595 free(cg->cgroup_path);
1596
1597 hashmap_free(cg->pids);
1598
1599 if (cg->parent)
1600 LIST_REMOVE(siblings, cg->parent->children, cg);
1601
1602 free(cg);
1603 }
1604
1605 static int cgroup_info_compare_func(const void *a, const void *b) {
1606 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1607
1608 assert(x);
1609 assert(y);
1610
1611 return strcmp(x->cgroup_path, y->cgroup_path);
1612 }
1613
1614 static int dump_processes(
1615 Hashmap *cgroups,
1616 const char *cgroup_path,
1617 const char *prefix,
1618 unsigned n_columns,
1619 OutputFlags flags) {
1620
1621 struct CGroupInfo *cg;
1622 int r;
1623
1624 assert(prefix);
1625
1626 if (IS_ROOT(cgroup_path))
1627 cgroup_path = "/";
1628
1629 cg = hashmap_get(cgroups, cgroup_path);
1630 if (!cg)
1631 return 0;
1632
1633 if (!hashmap_isempty(cg->pids)) {
1634 const char *name;
1635 size_t n = 0, i;
1636 pid_t *pids;
1637 void *pidp;
1638 Iterator j;
1639 int width;
1640
1641 /* Order processes by their PID */
1642 pids = newa(pid_t, hashmap_size(cg->pids));
1643
1644 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1645 pids[n++] = PTR_TO_PID(pidp);
1646
1647 assert(n == hashmap_size(cg->pids));
1648 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1649
1650 width = DECIMAL_STR_WIDTH(pids[n-1]);
1651
1652 for (i = 0; i < n; i++) {
1653 _cleanup_free_ char *e = NULL;
1654 const char *special;
1655 bool more;
1656
1657 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1658 assert(name);
1659
1660 if (n_columns != 0) {
1661 unsigned k;
1662
1663 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1664
1665 e = ellipsize(name, k, 100);
1666 if (e)
1667 name = e;
1668 }
1669
1670 more = i+1 < n || cg->children;
1671 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1672
1673 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1674 prefix,
1675 special,
1676 width, pids[i],
1677 name);
1678 }
1679 }
1680
1681 if (cg->children) {
1682 struct CGroupInfo **children, *child;
1683 size_t n = 0, i;
1684
1685 /* Order subcgroups by their name */
1686 children = newa(struct CGroupInfo*, cg->n_children);
1687 LIST_FOREACH(siblings, child, cg->children)
1688 children[n++] = child;
1689 assert(n == cg->n_children);
1690 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1691
1692 if (n_columns != 0)
1693 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1694
1695 for (i = 0; i < n; i++) {
1696 _cleanup_free_ char *pp = NULL;
1697 const char *name, *special;
1698 bool more;
1699
1700 child = children[i];
1701
1702 name = strrchr(child->cgroup_path, '/');
1703 if (!name)
1704 return -EINVAL;
1705 name++;
1706
1707 more = i+1 < n;
1708 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1709
1710 fputs(prefix, stdout);
1711 fputs(special, stdout);
1712 fputs(name, stdout);
1713 fputc('\n', stdout);
1714
1715 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1716
1717 pp = strappend(prefix, special);
1718 if (!pp)
1719 return -ENOMEM;
1720
1721 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1722 if (r < 0)
1723 return r;
1724 }
1725 }
1726
1727 cg->done = true;
1728 return 0;
1729 }
1730
1731 static int dump_extra_processes(
1732 Hashmap *cgroups,
1733 const char *prefix,
1734 unsigned n_columns,
1735 OutputFlags flags) {
1736
1737 _cleanup_free_ pid_t *pids = NULL;
1738 _cleanup_hashmap_free_ Hashmap *names = NULL;
1739 struct CGroupInfo *cg;
1740 size_t n_allocated = 0, n = 0, k;
1741 Iterator i;
1742 int width, r;
1743
1744 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1745 * combined, sorted, linear list. */
1746
1747 HASHMAP_FOREACH(cg, cgroups, i) {
1748 const char *name;
1749 void *pidp;
1750 Iterator j;
1751
1752 if (cg->done)
1753 continue;
1754
1755 if (hashmap_isempty(cg->pids))
1756 continue;
1757
1758 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1759 if (r < 0)
1760 return r;
1761
1762 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1763 return -ENOMEM;
1764
1765 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1766 pids[n++] = PTR_TO_PID(pidp);
1767
1768 r = hashmap_put(names, pidp, (void*) name);
1769 if (r < 0)
1770 return r;
1771 }
1772 }
1773
1774 if (n == 0)
1775 return 0;
1776
1777 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1778 width = DECIMAL_STR_WIDTH(pids[n-1]);
1779
1780 for (k = 0; k < n; k++) {
1781 _cleanup_free_ char *e = NULL;
1782 const char *name;
1783
1784 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1785 assert(name);
1786
1787 if (n_columns != 0) {
1788 unsigned z;
1789
1790 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1791
1792 e = ellipsize(name, z, 100);
1793 if (e)
1794 name = e;
1795 }
1796
1797 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1798 prefix,
1799 special_glyph(TRIANGULAR_BULLET),
1800 width, pids[k],
1801 name);
1802 }
1803
1804 return 0;
1805 }
1806
1807 int unit_show_processes(
1808 sd_bus *bus,
1809 const char *unit,
1810 const char *cgroup_path,
1811 const char *prefix,
1812 unsigned n_columns,
1813 OutputFlags flags,
1814 sd_bus_error *error) {
1815
1816 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1817 Hashmap *cgroups = NULL;
1818 struct CGroupInfo *cg;
1819 int r;
1820
1821 assert(bus);
1822 assert(unit);
1823
1824 if (flags & OUTPUT_FULL_WIDTH)
1825 n_columns = 0;
1826 else if (n_columns <= 0)
1827 n_columns = columns();
1828
1829 prefix = strempty(prefix);
1830
1831 r = sd_bus_call_method(
1832 bus,
1833 "org.freedesktop.systemd1",
1834 "/org/freedesktop/systemd1",
1835 "org.freedesktop.systemd1.Manager",
1836 "GetUnitProcesses",
1837 error,
1838 &reply,
1839 "s",
1840 unit);
1841 if (r < 0)
1842 return r;
1843
1844 cgroups = hashmap_new(&string_hash_ops);
1845 if (!cgroups)
1846 return -ENOMEM;
1847
1848 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1849 if (r < 0)
1850 goto finish;
1851
1852 for (;;) {
1853 const char *path = NULL, *name = NULL;
1854 uint32_t pid;
1855
1856 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1857 if (r < 0)
1858 goto finish;
1859 if (r == 0)
1860 break;
1861
1862 r = add_process(cgroups, path, pid, name);
1863 if (r < 0)
1864 goto finish;
1865 }
1866
1867 r = sd_bus_message_exit_container(reply);
1868 if (r < 0)
1869 goto finish;
1870
1871 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1872 if (r < 0)
1873 goto finish;
1874
1875 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1876
1877 finish:
1878 while ((cg = hashmap_first(cgroups)))
1879 remove_cgroup(cgroups, cg);
1880
1881 hashmap_free(cgroups);
1882
1883 return r;
1884 }