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