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