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