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