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