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