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