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