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