]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
Merge pull request #6387 from keszybz/fix-timeout-0
[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", "RuntimeDirectoryPreserve"))
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, "RuntimeDirectoryMode")) {
552 mode_t mode;
553
554 r = parse_mode(eq, &mode);
555 if (r < 0)
556 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
557
558 r = sd_bus_message_append(m, "v", "u", mode);
559
560 } else if (streq(field, "RuntimeDirectory")) {
561 const char *p;
562
563 r = sd_bus_message_open_container(m, 'v', "as");
564 if (r < 0)
565 return bus_log_create_error(r);
566
567 r = sd_bus_message_open_container(m, 'a', "s");
568 if (r < 0)
569 return bus_log_create_error(r);
570
571 for (p = eq;;) {
572 _cleanup_free_ char *word = NULL;
573
574 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
575 if (r < 0)
576 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
577
578 if (r == 0)
579 break;
580
581 r = sd_bus_message_append_basic(m, 's', word);
582 if (r < 0)
583 return bus_log_create_error(r);
584 }
585
586 r = sd_bus_message_close_container(m);
587 if (r < 0)
588 return bus_log_create_error(r);
589
590 r = sd_bus_message_close_container(m);
591
592 } else if (streq(field, "RestrictNamespaces")) {
593 bool invert = false;
594 unsigned long flags = 0;
595
596 if (eq[0] == '~') {
597 invert = true;
598 eq++;
599 }
600
601 r = parse_boolean(eq);
602 if (r > 0)
603 flags = 0;
604 else if (r == 0)
605 flags = NAMESPACE_FLAGS_ALL;
606 else {
607 r = namespace_flag_from_string_many(eq, &flags);
608 if (r < 0)
609 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
610 }
611
612 if (invert)
613 flags = (~flags) & NAMESPACE_FLAGS_ALL;
614
615 r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
616 } else if ((dep = unit_dependency_from_string(field)) >= 0)
617 r = sd_bus_message_append(m, "v", "as", 1, eq);
618 else if (streq(field, "MountFlags")) {
619 unsigned long f;
620
621 r = mount_propagation_flags_from_string(eq, &f);
622 if (r < 0)
623 return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
624
625 r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
626 } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
627 const char *p = eq;
628
629 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
630 if (r < 0)
631 return r;
632
633 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
634 if (r < 0)
635 return r;
636
637 for (;;) {
638 _cleanup_free_ char *source = NULL, *destination = NULL;
639 char *s = NULL, *d = NULL;
640 bool ignore_enoent = false;
641 uint64_t flags = MS_REC;
642
643 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
644 if (r < 0)
645 return log_error_errno(r, "Failed to parse argument: %m");
646 if (r == 0)
647 break;
648
649 s = source;
650 if (s[0] == '-') {
651 ignore_enoent = true;
652 s++;
653 }
654
655 if (p && p[-1] == ':') {
656 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
657 if (r < 0)
658 return log_error_errno(r, "Failed to parse argument: %m");
659 if (r == 0) {
660 log_error("Missing argument after ':': %s", eq);
661 return -EINVAL;
662 }
663
664 d = destination;
665
666 if (p && p[-1] == ':') {
667 _cleanup_free_ char *options = NULL;
668
669 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
670 if (r < 0)
671 return log_error_errno(r, "Failed to parse argument: %m");
672
673 if (isempty(options) || streq(options, "rbind"))
674 flags = MS_REC;
675 else if (streq(options, "norbind"))
676 flags = 0;
677 else {
678 log_error("Unknown options: %s", eq);
679 return -EINVAL;
680 }
681 }
682 } else
683 d = s;
684
685
686 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
687 if (r < 0)
688 return r;
689 }
690
691 r = sd_bus_message_close_container(m);
692 if (r < 0)
693 return r;
694
695 r = sd_bus_message_close_container(m);
696 } else {
697 log_error("Unknown assignment %s.", assignment);
698 return -EINVAL;
699 }
700
701 finish:
702 if (r < 0)
703 return bus_log_create_error(r);
704
705 r = sd_bus_message_close_container(m);
706 if (r < 0)
707 return bus_log_create_error(r);
708
709 return 0;
710 }
711
712 int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
713 char **i;
714 int r;
715
716 assert(m);
717
718 STRV_FOREACH(i, l) {
719 r = bus_append_unit_property_assignment(m, *i);
720 if (r < 0)
721 return r;
722 }
723
724 return 0;
725 }
726
727 typedef struct BusWaitForJobs {
728 sd_bus *bus;
729 Set *jobs;
730
731 char *name;
732 char *result;
733
734 sd_bus_slot *slot_job_removed;
735 sd_bus_slot *slot_disconnected;
736 } BusWaitForJobs;
737
738 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
739 assert(m);
740
741 log_error("Warning! D-Bus connection terminated.");
742 sd_bus_close(sd_bus_message_get_bus(m));
743
744 return 0;
745 }
746
747 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
748 const char *path, *unit, *result;
749 BusWaitForJobs *d = userdata;
750 uint32_t id;
751 char *found;
752 int r;
753
754 assert(m);
755 assert(d);
756
757 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
758 if (r < 0) {
759 bus_log_parse_error(r);
760 return 0;
761 }
762
763 found = set_remove(d->jobs, (char*) path);
764 if (!found)
765 return 0;
766
767 free(found);
768
769 if (!isempty(result))
770 d->result = strdup(result);
771
772 if (!isempty(unit))
773 d->name = strdup(unit);
774
775 return 0;
776 }
777
778 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
779 if (!d)
780 return;
781
782 set_free_free(d->jobs);
783
784 sd_bus_slot_unref(d->slot_disconnected);
785 sd_bus_slot_unref(d->slot_job_removed);
786
787 sd_bus_unref(d->bus);
788
789 free(d->name);
790 free(d->result);
791
792 free(d);
793 }
794
795 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
796 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
797 int r;
798
799 assert(bus);
800 assert(ret);
801
802 d = new0(BusWaitForJobs, 1);
803 if (!d)
804 return -ENOMEM;
805
806 d->bus = sd_bus_ref(bus);
807
808 /* When we are a bus client we match by sender. Direct
809 * connections OTOH have no initialized sender field, and
810 * hence we ignore the sender then */
811 r = sd_bus_add_match(
812 bus,
813 &d->slot_job_removed,
814 bus->bus_client ?
815 "type='signal',"
816 "sender='org.freedesktop.systemd1',"
817 "interface='org.freedesktop.systemd1.Manager',"
818 "member='JobRemoved',"
819 "path='/org/freedesktop/systemd1'" :
820 "type='signal',"
821 "interface='org.freedesktop.systemd1.Manager',"
822 "member='JobRemoved',"
823 "path='/org/freedesktop/systemd1'",
824 match_job_removed, d);
825 if (r < 0)
826 return r;
827
828 r = sd_bus_add_match(
829 bus,
830 &d->slot_disconnected,
831 "type='signal',"
832 "sender='org.freedesktop.DBus.Local',"
833 "interface='org.freedesktop.DBus.Local',"
834 "member='Disconnected'",
835 match_disconnected, d);
836 if (r < 0)
837 return r;
838
839 *ret = d;
840 d = NULL;
841
842 return 0;
843 }
844
845 static int bus_process_wait(sd_bus *bus) {
846 int r;
847
848 for (;;) {
849 r = sd_bus_process(bus, NULL);
850 if (r < 0)
851 return r;
852 if (r > 0)
853 return 0;
854
855 r = sd_bus_wait(bus, (uint64_t) -1);
856 if (r < 0)
857 return r;
858 }
859 }
860
861 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
862 _cleanup_free_ char *dbus_path = NULL;
863
864 assert(d);
865 assert(d->name);
866 assert(result);
867
868 dbus_path = unit_dbus_path_from_name(d->name);
869 if (!dbus_path)
870 return -ENOMEM;
871
872 return sd_bus_get_property_string(d->bus,
873 "org.freedesktop.systemd1",
874 dbus_path,
875 "org.freedesktop.systemd1.Service",
876 "Result",
877 NULL,
878 result);
879 }
880
881 static const struct {
882 const char *result, *explanation;
883 } explanations [] = {
884 { "resources", "of unavailable resources or another system error" },
885 { "protocol", "the service did not take the steps required by its unit configuration" },
886 { "timeout", "a timeout was exceeded" },
887 { "exit-code", "the control process exited with error code" },
888 { "signal", "a fatal signal was delivered to the control process" },
889 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
890 { "watchdog", "the service failed to send watchdog ping" },
891 { "start-limit", "start of the service was attempted too often" }
892 };
893
894 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
895 _cleanup_free_ char *service_shell_quoted = NULL;
896 const char *systemctl = "systemctl", *journalctl = "journalctl";
897
898 assert(service);
899
900 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
901
902 if (extra_args) {
903 _cleanup_free_ char *t;
904
905 t = strv_join((char**) extra_args, " ");
906 systemctl = strjoina("systemctl ", t ? : "<args>");
907 journalctl = strjoina("journalctl ", t ? : "<args>");
908 }
909
910 if (!isempty(result)) {
911 unsigned i;
912
913 for (i = 0; i < ELEMENTSOF(explanations); ++i)
914 if (streq(result, explanations[i].result))
915 break;
916
917 if (i < ELEMENTSOF(explanations)) {
918 log_error("Job for %s failed because %s.\n"
919 "See \"%s status %s\" and \"%s -xe\" for details.\n",
920 service,
921 explanations[i].explanation,
922 systemctl,
923 service_shell_quoted ?: "<service>",
924 journalctl);
925 goto finish;
926 }
927 }
928
929 log_error("Job for %s failed.\n"
930 "See \"%s status %s\" and \"%s -xe\" for details.\n",
931 service,
932 systemctl,
933 service_shell_quoted ?: "<service>",
934 journalctl);
935
936 finish:
937 /* For some results maybe additional explanation is required */
938 if (streq_ptr(result, "start-limit"))
939 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
940 "followed by \"%1$s start %2$s\" again.",
941 systemctl,
942 service_shell_quoted ?: "<service>");
943 }
944
945 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
946 int r = 0;
947
948 assert(d->result);
949
950 if (!quiet) {
951 if (streq(d->result, "canceled"))
952 log_error("Job for %s canceled.", strna(d->name));
953 else if (streq(d->result, "timeout"))
954 log_error("Job for %s timed out.", strna(d->name));
955 else if (streq(d->result, "dependency"))
956 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
957 else if (streq(d->result, "invalid"))
958 log_error("%s is not active, cannot reload.", strna(d->name));
959 else if (streq(d->result, "assert"))
960 log_error("Assertion failed on job for %s.", strna(d->name));
961 else if (streq(d->result, "unsupported"))
962 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
963 else if (streq(d->result, "collected"))
964 log_error("Queued job for %s was garbage collected.", strna(d->name));
965 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
966 if (d->name) {
967 int q;
968 _cleanup_free_ char *result = NULL;
969
970 q = bus_job_get_service_result(d, &result);
971 if (q < 0)
972 log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
973
974 log_job_error_with_service_result(d->name, result, extra_args);
975 } else
976 log_error("Job failed. See \"journalctl -xe\" for details.");
977 }
978 }
979
980 if (STR_IN_SET(d->result, "canceled", "collected"))
981 r = -ECANCELED;
982 else if (streq(d->result, "timeout"))
983 r = -ETIME;
984 else if (streq(d->result, "dependency"))
985 r = -EIO;
986 else if (streq(d->result, "invalid"))
987 r = -ENOEXEC;
988 else if (streq(d->result, "assert"))
989 r = -EPROTO;
990 else if (streq(d->result, "unsupported"))
991 r = -EOPNOTSUPP;
992 else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
993 r = -EIO;
994
995 return r;
996 }
997
998 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
999 int r = 0;
1000
1001 assert(d);
1002
1003 while (!set_isempty(d->jobs)) {
1004 int q;
1005
1006 q = bus_process_wait(d->bus);
1007 if (q < 0)
1008 return log_error_errno(q, "Failed to wait for response: %m");
1009
1010 if (d->result) {
1011 q = check_wait_response(d, quiet, extra_args);
1012 /* Return the first error as it is most likely to be
1013 * meaningful. */
1014 if (q < 0 && r == 0)
1015 r = q;
1016
1017 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1018 }
1019
1020 d->name = mfree(d->name);
1021 d->result = mfree(d->result);
1022 }
1023
1024 return r;
1025 }
1026
1027 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1028 int r;
1029
1030 assert(d);
1031
1032 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1033 if (r < 0)
1034 return r;
1035
1036 return set_put_strdup(d->jobs, path);
1037 }
1038
1039 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1040 int r;
1041
1042 r = bus_wait_for_jobs_add(d, path);
1043 if (r < 0)
1044 return log_oom();
1045
1046 return bus_wait_for_jobs(d, quiet, NULL);
1047 }
1048
1049 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1050 const char *type, *path, *source;
1051 int r;
1052
1053 /* changes is dereferenced when calling unit_file_dump_changes() later,
1054 * so we have to make sure this is not NULL. */
1055 assert(changes);
1056 assert(n_changes);
1057
1058 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1059 if (r < 0)
1060 return bus_log_parse_error(r);
1061
1062 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1063 /* We expect only "success" changes to be sent over the bus.
1064 Hence, reject anything negative. */
1065 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1066
1067 if (ch < 0) {
1068 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1069 continue;
1070 }
1071
1072 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1073 if (r < 0)
1074 return r;
1075 }
1076 if (r < 0)
1077 return bus_log_parse_error(r);
1078
1079 r = sd_bus_message_exit_container(m);
1080 if (r < 0)
1081 return bus_log_parse_error(r);
1082
1083 unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
1084 return 0;
1085 }
1086
1087 struct CGroupInfo {
1088 char *cgroup_path;
1089 bool is_const; /* If false, cgroup_path should be free()'d */
1090
1091 Hashmap *pids; /* PID → process name */
1092 bool done;
1093
1094 struct CGroupInfo *parent;
1095 LIST_FIELDS(struct CGroupInfo, siblings);
1096 LIST_HEAD(struct CGroupInfo, children);
1097 size_t n_children;
1098 };
1099
1100 static bool IS_ROOT(const char *p) {
1101 return isempty(p) || streq(p, "/");
1102 }
1103
1104 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1105 struct CGroupInfo *parent = NULL, *cg;
1106 int r;
1107
1108 assert(cgroups);
1109 assert(ret);
1110
1111 if (IS_ROOT(path))
1112 path = "/";
1113
1114 cg = hashmap_get(cgroups, path);
1115 if (cg) {
1116 *ret = cg;
1117 return 0;
1118 }
1119
1120 if (!IS_ROOT(path)) {
1121 const char *e, *pp;
1122
1123 e = strrchr(path, '/');
1124 if (!e)
1125 return -EINVAL;
1126
1127 pp = strndupa(path, e - path);
1128 if (!pp)
1129 return -ENOMEM;
1130
1131 r = add_cgroup(cgroups, pp, false, &parent);
1132 if (r < 0)
1133 return r;
1134 }
1135
1136 cg = new0(struct CGroupInfo, 1);
1137 if (!cg)
1138 return -ENOMEM;
1139
1140 if (is_const)
1141 cg->cgroup_path = (char*) path;
1142 else {
1143 cg->cgroup_path = strdup(path);
1144 if (!cg->cgroup_path) {
1145 free(cg);
1146 return -ENOMEM;
1147 }
1148 }
1149
1150 cg->is_const = is_const;
1151 cg->parent = parent;
1152
1153 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1154 if (r < 0) {
1155 if (!is_const)
1156 free(cg->cgroup_path);
1157 free(cg);
1158 return r;
1159 }
1160
1161 if (parent) {
1162 LIST_PREPEND(siblings, parent->children, cg);
1163 parent->n_children++;
1164 }
1165
1166 *ret = cg;
1167 return 1;
1168 }
1169
1170 static int add_process(
1171 Hashmap *cgroups,
1172 const char *path,
1173 pid_t pid,
1174 const char *name) {
1175
1176 struct CGroupInfo *cg;
1177 int r;
1178
1179 assert(cgroups);
1180 assert(name);
1181 assert(pid > 0);
1182
1183 r = add_cgroup(cgroups, path, true, &cg);
1184 if (r < 0)
1185 return r;
1186
1187 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1188 if (r < 0)
1189 return r;
1190
1191 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1192 }
1193
1194 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1195 assert(cgroups);
1196 assert(cg);
1197
1198 while (cg->children)
1199 remove_cgroup(cgroups, cg->children);
1200
1201 hashmap_remove(cgroups, cg->cgroup_path);
1202
1203 if (!cg->is_const)
1204 free(cg->cgroup_path);
1205
1206 hashmap_free(cg->pids);
1207
1208 if (cg->parent)
1209 LIST_REMOVE(siblings, cg->parent->children, cg);
1210
1211 free(cg);
1212 }
1213
1214 static int cgroup_info_compare_func(const void *a, const void *b) {
1215 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1216
1217 assert(x);
1218 assert(y);
1219
1220 return strcmp(x->cgroup_path, y->cgroup_path);
1221 }
1222
1223 static int dump_processes(
1224 Hashmap *cgroups,
1225 const char *cgroup_path,
1226 const char *prefix,
1227 unsigned n_columns,
1228 OutputFlags flags) {
1229
1230 struct CGroupInfo *cg;
1231 int r;
1232
1233 assert(prefix);
1234
1235 if (IS_ROOT(cgroup_path))
1236 cgroup_path = "/";
1237
1238 cg = hashmap_get(cgroups, cgroup_path);
1239 if (!cg)
1240 return 0;
1241
1242 if (!hashmap_isempty(cg->pids)) {
1243 const char *name;
1244 size_t n = 0, i;
1245 pid_t *pids;
1246 void *pidp;
1247 Iterator j;
1248 int width;
1249
1250 /* Order processes by their PID */
1251 pids = newa(pid_t, hashmap_size(cg->pids));
1252
1253 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1254 pids[n++] = PTR_TO_PID(pidp);
1255
1256 assert(n == hashmap_size(cg->pids));
1257 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1258
1259 width = DECIMAL_STR_WIDTH(pids[n-1]);
1260
1261 for (i = 0; i < n; i++) {
1262 _cleanup_free_ char *e = NULL;
1263 const char *special;
1264 bool more;
1265
1266 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1267 assert(name);
1268
1269 if (n_columns != 0) {
1270 unsigned k;
1271
1272 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1273
1274 e = ellipsize(name, k, 100);
1275 if (e)
1276 name = e;
1277 }
1278
1279 more = i+1 < n || cg->children;
1280 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1281
1282 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1283 prefix,
1284 special,
1285 width, pids[i],
1286 name);
1287 }
1288 }
1289
1290 if (cg->children) {
1291 struct CGroupInfo **children, *child;
1292 size_t n = 0, i;
1293
1294 /* Order subcgroups by their name */
1295 children = newa(struct CGroupInfo*, cg->n_children);
1296 LIST_FOREACH(siblings, child, cg->children)
1297 children[n++] = child;
1298 assert(n == cg->n_children);
1299 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1300
1301 if (n_columns != 0)
1302 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1303
1304 for (i = 0; i < n; i++) {
1305 _cleanup_free_ char *pp = NULL;
1306 const char *name, *special;
1307 bool more;
1308
1309 child = children[i];
1310
1311 name = strrchr(child->cgroup_path, '/');
1312 if (!name)
1313 return -EINVAL;
1314 name++;
1315
1316 more = i+1 < n;
1317 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1318
1319 fputs(prefix, stdout);
1320 fputs(special, stdout);
1321 fputs(name, stdout);
1322 fputc('\n', stdout);
1323
1324 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1325
1326 pp = strappend(prefix, special);
1327 if (!pp)
1328 return -ENOMEM;
1329
1330 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1331 if (r < 0)
1332 return r;
1333 }
1334 }
1335
1336 cg->done = true;
1337 return 0;
1338 }
1339
1340 static int dump_extra_processes(
1341 Hashmap *cgroups,
1342 const char *prefix,
1343 unsigned n_columns,
1344 OutputFlags flags) {
1345
1346 _cleanup_free_ pid_t *pids = NULL;
1347 _cleanup_hashmap_free_ Hashmap *names = NULL;
1348 struct CGroupInfo *cg;
1349 size_t n_allocated = 0, n = 0, k;
1350 Iterator i;
1351 int width, r;
1352
1353 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1354 * combined, sorted, linear list. */
1355
1356 HASHMAP_FOREACH(cg, cgroups, i) {
1357 const char *name;
1358 void *pidp;
1359 Iterator j;
1360
1361 if (cg->done)
1362 continue;
1363
1364 if (hashmap_isempty(cg->pids))
1365 continue;
1366
1367 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1368 if (r < 0)
1369 return r;
1370
1371 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1372 return -ENOMEM;
1373
1374 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1375 pids[n++] = PTR_TO_PID(pidp);
1376
1377 r = hashmap_put(names, pidp, (void*) name);
1378 if (r < 0)
1379 return r;
1380 }
1381 }
1382
1383 if (n == 0)
1384 return 0;
1385
1386 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1387 width = DECIMAL_STR_WIDTH(pids[n-1]);
1388
1389 for (k = 0; k < n; k++) {
1390 _cleanup_free_ char *e = NULL;
1391 const char *name;
1392
1393 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1394 assert(name);
1395
1396 if (n_columns != 0) {
1397 unsigned z;
1398
1399 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1400
1401 e = ellipsize(name, z, 100);
1402 if (e)
1403 name = e;
1404 }
1405
1406 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1407 prefix,
1408 special_glyph(TRIANGULAR_BULLET),
1409 width, pids[k],
1410 name);
1411 }
1412
1413 return 0;
1414 }
1415
1416 int unit_show_processes(
1417 sd_bus *bus,
1418 const char *unit,
1419 const char *cgroup_path,
1420 const char *prefix,
1421 unsigned n_columns,
1422 OutputFlags flags,
1423 sd_bus_error *error) {
1424
1425 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1426 Hashmap *cgroups = NULL;
1427 struct CGroupInfo *cg;
1428 int r;
1429
1430 assert(bus);
1431 assert(unit);
1432
1433 if (flags & OUTPUT_FULL_WIDTH)
1434 n_columns = 0;
1435 else if (n_columns <= 0)
1436 n_columns = columns();
1437
1438 prefix = strempty(prefix);
1439
1440 r = sd_bus_call_method(
1441 bus,
1442 "org.freedesktop.systemd1",
1443 "/org/freedesktop/systemd1",
1444 "org.freedesktop.systemd1.Manager",
1445 "GetUnitProcesses",
1446 error,
1447 &reply,
1448 "s",
1449 unit);
1450 if (r < 0)
1451 return r;
1452
1453 cgroups = hashmap_new(&string_hash_ops);
1454 if (!cgroups)
1455 return -ENOMEM;
1456
1457 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1458 if (r < 0)
1459 goto finish;
1460
1461 for (;;) {
1462 const char *path = NULL, *name = NULL;
1463 uint32_t pid;
1464
1465 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1466 if (r < 0)
1467 goto finish;
1468 if (r == 0)
1469 break;
1470
1471 r = add_process(cgroups, path, pid, name);
1472 if (r < 0)
1473 goto finish;
1474 }
1475
1476 r = sd_bus_message_exit_container(reply);
1477 if (r < 0)
1478 goto finish;
1479
1480 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1481 if (r < 0)
1482 goto finish;
1483
1484 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1485
1486 finish:
1487 while ((cg = hashmap_first(cgroups)))
1488 remove_cgroup(cgroups, cg);
1489
1490 hashmap_free(cgroups);
1491
1492 return r;
1493 }