]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
bus-unit-util: use STR_IN_SET() where appropriate
[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", "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 if (!endswith(d->name, ".service"))
1131 return -EINVAL;
1132
1133 dbus_path = unit_dbus_path_from_name(d->name);
1134 if (!dbus_path)
1135 return -ENOMEM;
1136
1137 return sd_bus_get_property_string(d->bus,
1138 "org.freedesktop.systemd1",
1139 dbus_path,
1140 "org.freedesktop.systemd1.Service",
1141 "Result",
1142 NULL,
1143 result);
1144 }
1145
1146 static const struct {
1147 const char *result, *explanation;
1148 } explanations [] = {
1149 { "resources", "of unavailable resources or another system error" },
1150 { "protocol", "the service did not take the steps required by its unit configuration" },
1151 { "timeout", "a timeout was exceeded" },
1152 { "exit-code", "the control process exited with error code" },
1153 { "signal", "a fatal signal was delivered to the control process" },
1154 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1155 { "watchdog", "the service failed to send watchdog ping" },
1156 { "start-limit", "start of the service was attempted too often" }
1157 };
1158
1159 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1160 _cleanup_free_ char *service_shell_quoted = NULL;
1161 const char *systemctl = "systemctl", *journalctl = "journalctl";
1162
1163 assert(service);
1164
1165 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1166
1167 if (extra_args) {
1168 _cleanup_free_ char *t;
1169
1170 t = strv_join((char**) extra_args, " ");
1171 systemctl = strjoina("systemctl ", t ? : "<args>");
1172 journalctl = strjoina("journalctl ", t ? : "<args>");
1173 }
1174
1175 if (!isempty(result)) {
1176 unsigned i;
1177
1178 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1179 if (streq(result, explanations[i].result))
1180 break;
1181
1182 if (i < ELEMENTSOF(explanations)) {
1183 log_error("Job for %s failed because %s.\n"
1184 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1185 service,
1186 explanations[i].explanation,
1187 systemctl,
1188 service_shell_quoted ?: "<service>",
1189 journalctl);
1190 goto finish;
1191 }
1192 }
1193
1194 log_error("Job for %s failed.\n"
1195 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1196 service,
1197 systemctl,
1198 service_shell_quoted ?: "<service>",
1199 journalctl);
1200
1201 finish:
1202 /* For some results maybe additional explanation is required */
1203 if (streq_ptr(result, "start-limit"))
1204 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1205 "followed by \"%1$s start %2$s\" again.",
1206 systemctl,
1207 service_shell_quoted ?: "<service>");
1208 }
1209
1210 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1211 int r = 0;
1212
1213 assert(d->result);
1214
1215 if (!quiet) {
1216 if (streq(d->result, "canceled"))
1217 log_error("Job for %s canceled.", strna(d->name));
1218 else if (streq(d->result, "timeout"))
1219 log_error("Job for %s timed out.", strna(d->name));
1220 else if (streq(d->result, "dependency"))
1221 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1222 else if (streq(d->result, "invalid"))
1223 log_error("%s is not active, cannot reload.", strna(d->name));
1224 else if (streq(d->result, "assert"))
1225 log_error("Assertion failed on job for %s.", strna(d->name));
1226 else if (streq(d->result, "unsupported"))
1227 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1228 else if (streq(d->result, "collected"))
1229 log_error("Queued job for %s was garbage collected.", strna(d->name));
1230 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1231 if (d->name) {
1232 _cleanup_free_ char *result = NULL;
1233 int q;
1234
1235 q = bus_job_get_service_result(d, &result);
1236 if (q < 0)
1237 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1238
1239 log_job_error_with_service_result(d->name, result, extra_args);
1240 } else
1241 log_error("Job failed. See \"journalctl -xe\" for details.");
1242 }
1243 }
1244
1245 if (STR_IN_SET(d->result, "canceled", "collected"))
1246 r = -ECANCELED;
1247 else if (streq(d->result, "timeout"))
1248 r = -ETIME;
1249 else if (streq(d->result, "dependency"))
1250 r = -EIO;
1251 else if (streq(d->result, "invalid"))
1252 r = -ENOEXEC;
1253 else if (streq(d->result, "assert"))
1254 r = -EPROTO;
1255 else if (streq(d->result, "unsupported"))
1256 r = -EOPNOTSUPP;
1257 else if (!STR_IN_SET(d->result, "done", "skipped"))
1258 r = -EIO;
1259
1260 return r;
1261 }
1262
1263 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1264 int r = 0;
1265
1266 assert(d);
1267
1268 while (!set_isempty(d->jobs)) {
1269 int q;
1270
1271 q = bus_process_wait(d->bus);
1272 if (q < 0)
1273 return log_error_errno(q, "Failed to wait for response: %m");
1274
1275 if (d->result) {
1276 q = check_wait_response(d, quiet, extra_args);
1277 /* Return the first error as it is most likely to be
1278 * meaningful. */
1279 if (q < 0 && r == 0)
1280 r = q;
1281
1282 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1283 }
1284
1285 d->name = mfree(d->name);
1286 d->result = mfree(d->result);
1287 }
1288
1289 return r;
1290 }
1291
1292 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1293 int r;
1294
1295 assert(d);
1296
1297 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1298 if (r < 0)
1299 return r;
1300
1301 return set_put_strdup(d->jobs, path);
1302 }
1303
1304 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1305 int r;
1306
1307 r = bus_wait_for_jobs_add(d, path);
1308 if (r < 0)
1309 return log_oom();
1310
1311 return bus_wait_for_jobs(d, quiet, NULL);
1312 }
1313
1314 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1315 const char *type, *path, *source;
1316 int r;
1317
1318 /* changes is dereferenced when calling unit_file_dump_changes() later,
1319 * so we have to make sure this is not NULL. */
1320 assert(changes);
1321 assert(n_changes);
1322
1323 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1324 if (r < 0)
1325 return bus_log_parse_error(r);
1326
1327 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1328 /* We expect only "success" changes to be sent over the bus.
1329 Hence, reject anything negative. */
1330 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1331
1332 if (ch < 0) {
1333 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1334 continue;
1335 }
1336
1337 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1338 if (r < 0)
1339 return r;
1340 }
1341 if (r < 0)
1342 return bus_log_parse_error(r);
1343
1344 r = sd_bus_message_exit_container(m);
1345 if (r < 0)
1346 return bus_log_parse_error(r);
1347
1348 unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
1349 return 0;
1350 }
1351
1352 struct CGroupInfo {
1353 char *cgroup_path;
1354 bool is_const; /* If false, cgroup_path should be free()'d */
1355
1356 Hashmap *pids; /* PID → process name */
1357 bool done;
1358
1359 struct CGroupInfo *parent;
1360 LIST_FIELDS(struct CGroupInfo, siblings);
1361 LIST_HEAD(struct CGroupInfo, children);
1362 size_t n_children;
1363 };
1364
1365 static bool IS_ROOT(const char *p) {
1366 return isempty(p) || streq(p, "/");
1367 }
1368
1369 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1370 struct CGroupInfo *parent = NULL, *cg;
1371 int r;
1372
1373 assert(cgroups);
1374 assert(ret);
1375
1376 if (IS_ROOT(path))
1377 path = "/";
1378
1379 cg = hashmap_get(cgroups, path);
1380 if (cg) {
1381 *ret = cg;
1382 return 0;
1383 }
1384
1385 if (!IS_ROOT(path)) {
1386 const char *e, *pp;
1387
1388 e = strrchr(path, '/');
1389 if (!e)
1390 return -EINVAL;
1391
1392 pp = strndupa(path, e - path);
1393 if (!pp)
1394 return -ENOMEM;
1395
1396 r = add_cgroup(cgroups, pp, false, &parent);
1397 if (r < 0)
1398 return r;
1399 }
1400
1401 cg = new0(struct CGroupInfo, 1);
1402 if (!cg)
1403 return -ENOMEM;
1404
1405 if (is_const)
1406 cg->cgroup_path = (char*) path;
1407 else {
1408 cg->cgroup_path = strdup(path);
1409 if (!cg->cgroup_path) {
1410 free(cg);
1411 return -ENOMEM;
1412 }
1413 }
1414
1415 cg->is_const = is_const;
1416 cg->parent = parent;
1417
1418 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1419 if (r < 0) {
1420 if (!is_const)
1421 free(cg->cgroup_path);
1422 free(cg);
1423 return r;
1424 }
1425
1426 if (parent) {
1427 LIST_PREPEND(siblings, parent->children, cg);
1428 parent->n_children++;
1429 }
1430
1431 *ret = cg;
1432 return 1;
1433 }
1434
1435 static int add_process(
1436 Hashmap *cgroups,
1437 const char *path,
1438 pid_t pid,
1439 const char *name) {
1440
1441 struct CGroupInfo *cg;
1442 int r;
1443
1444 assert(cgroups);
1445 assert(name);
1446 assert(pid > 0);
1447
1448 r = add_cgroup(cgroups, path, true, &cg);
1449 if (r < 0)
1450 return r;
1451
1452 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1453 if (r < 0)
1454 return r;
1455
1456 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1457 }
1458
1459 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1460 assert(cgroups);
1461 assert(cg);
1462
1463 while (cg->children)
1464 remove_cgroup(cgroups, cg->children);
1465
1466 hashmap_remove(cgroups, cg->cgroup_path);
1467
1468 if (!cg->is_const)
1469 free(cg->cgroup_path);
1470
1471 hashmap_free(cg->pids);
1472
1473 if (cg->parent)
1474 LIST_REMOVE(siblings, cg->parent->children, cg);
1475
1476 free(cg);
1477 }
1478
1479 static int cgroup_info_compare_func(const void *a, const void *b) {
1480 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1481
1482 assert(x);
1483 assert(y);
1484
1485 return strcmp(x->cgroup_path, y->cgroup_path);
1486 }
1487
1488 static int dump_processes(
1489 Hashmap *cgroups,
1490 const char *cgroup_path,
1491 const char *prefix,
1492 unsigned n_columns,
1493 OutputFlags flags) {
1494
1495 struct CGroupInfo *cg;
1496 int r;
1497
1498 assert(prefix);
1499
1500 if (IS_ROOT(cgroup_path))
1501 cgroup_path = "/";
1502
1503 cg = hashmap_get(cgroups, cgroup_path);
1504 if (!cg)
1505 return 0;
1506
1507 if (!hashmap_isempty(cg->pids)) {
1508 const char *name;
1509 size_t n = 0, i;
1510 pid_t *pids;
1511 void *pidp;
1512 Iterator j;
1513 int width;
1514
1515 /* Order processes by their PID */
1516 pids = newa(pid_t, hashmap_size(cg->pids));
1517
1518 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1519 pids[n++] = PTR_TO_PID(pidp);
1520
1521 assert(n == hashmap_size(cg->pids));
1522 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1523
1524 width = DECIMAL_STR_WIDTH(pids[n-1]);
1525
1526 for (i = 0; i < n; i++) {
1527 _cleanup_free_ char *e = NULL;
1528 const char *special;
1529 bool more;
1530
1531 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1532 assert(name);
1533
1534 if (n_columns != 0) {
1535 unsigned k;
1536
1537 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1538
1539 e = ellipsize(name, k, 100);
1540 if (e)
1541 name = e;
1542 }
1543
1544 more = i+1 < n || cg->children;
1545 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1546
1547 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1548 prefix,
1549 special,
1550 width, pids[i],
1551 name);
1552 }
1553 }
1554
1555 if (cg->children) {
1556 struct CGroupInfo **children, *child;
1557 size_t n = 0, i;
1558
1559 /* Order subcgroups by their name */
1560 children = newa(struct CGroupInfo*, cg->n_children);
1561 LIST_FOREACH(siblings, child, cg->children)
1562 children[n++] = child;
1563 assert(n == cg->n_children);
1564 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1565
1566 if (n_columns != 0)
1567 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1568
1569 for (i = 0; i < n; i++) {
1570 _cleanup_free_ char *pp = NULL;
1571 const char *name, *special;
1572 bool more;
1573
1574 child = children[i];
1575
1576 name = strrchr(child->cgroup_path, '/');
1577 if (!name)
1578 return -EINVAL;
1579 name++;
1580
1581 more = i+1 < n;
1582 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1583
1584 fputs(prefix, stdout);
1585 fputs(special, stdout);
1586 fputs(name, stdout);
1587 fputc('\n', stdout);
1588
1589 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1590
1591 pp = strappend(prefix, special);
1592 if (!pp)
1593 return -ENOMEM;
1594
1595 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1596 if (r < 0)
1597 return r;
1598 }
1599 }
1600
1601 cg->done = true;
1602 return 0;
1603 }
1604
1605 static int dump_extra_processes(
1606 Hashmap *cgroups,
1607 const char *prefix,
1608 unsigned n_columns,
1609 OutputFlags flags) {
1610
1611 _cleanup_free_ pid_t *pids = NULL;
1612 _cleanup_hashmap_free_ Hashmap *names = NULL;
1613 struct CGroupInfo *cg;
1614 size_t n_allocated = 0, n = 0, k;
1615 Iterator i;
1616 int width, r;
1617
1618 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1619 * combined, sorted, linear list. */
1620
1621 HASHMAP_FOREACH(cg, cgroups, i) {
1622 const char *name;
1623 void *pidp;
1624 Iterator j;
1625
1626 if (cg->done)
1627 continue;
1628
1629 if (hashmap_isempty(cg->pids))
1630 continue;
1631
1632 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1633 if (r < 0)
1634 return r;
1635
1636 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1637 return -ENOMEM;
1638
1639 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1640 pids[n++] = PTR_TO_PID(pidp);
1641
1642 r = hashmap_put(names, pidp, (void*) name);
1643 if (r < 0)
1644 return r;
1645 }
1646 }
1647
1648 if (n == 0)
1649 return 0;
1650
1651 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1652 width = DECIMAL_STR_WIDTH(pids[n-1]);
1653
1654 for (k = 0; k < n; k++) {
1655 _cleanup_free_ char *e = NULL;
1656 const char *name;
1657
1658 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1659 assert(name);
1660
1661 if (n_columns != 0) {
1662 unsigned z;
1663
1664 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1665
1666 e = ellipsize(name, z, 100);
1667 if (e)
1668 name = e;
1669 }
1670
1671 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1672 prefix,
1673 special_glyph(TRIANGULAR_BULLET),
1674 width, pids[k],
1675 name);
1676 }
1677
1678 return 0;
1679 }
1680
1681 int unit_show_processes(
1682 sd_bus *bus,
1683 const char *unit,
1684 const char *cgroup_path,
1685 const char *prefix,
1686 unsigned n_columns,
1687 OutputFlags flags,
1688 sd_bus_error *error) {
1689
1690 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1691 Hashmap *cgroups = NULL;
1692 struct CGroupInfo *cg;
1693 int r;
1694
1695 assert(bus);
1696 assert(unit);
1697
1698 if (flags & OUTPUT_FULL_WIDTH)
1699 n_columns = 0;
1700 else if (n_columns <= 0)
1701 n_columns = columns();
1702
1703 prefix = strempty(prefix);
1704
1705 r = sd_bus_call_method(
1706 bus,
1707 "org.freedesktop.systemd1",
1708 "/org/freedesktop/systemd1",
1709 "org.freedesktop.systemd1.Manager",
1710 "GetUnitProcesses",
1711 error,
1712 &reply,
1713 "s",
1714 unit);
1715 if (r < 0)
1716 return r;
1717
1718 cgroups = hashmap_new(&string_hash_ops);
1719 if (!cgroups)
1720 return -ENOMEM;
1721
1722 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1723 if (r < 0)
1724 goto finish;
1725
1726 for (;;) {
1727 const char *path = NULL, *name = NULL;
1728 uint32_t pid;
1729
1730 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1731 if (r < 0)
1732 goto finish;
1733 if (r == 0)
1734 break;
1735
1736 r = add_process(cgroups, path, pid, name);
1737 if (r < 0)
1738 goto finish;
1739 }
1740
1741 r = sd_bus_message_exit_container(reply);
1742 if (r < 0)
1743 goto finish;
1744
1745 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1746 if (r < 0)
1747 goto finish;
1748
1749 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1750
1751 finish:
1752 while ((cg = hashmap_first(cgroups)))
1753 remove_cgroup(cgroups, cg);
1754
1755 hashmap_free(cgroups);
1756
1757 return r;
1758 }