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