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