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