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