]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
Merge pull request #7395 from poettering/nametohandleat-loop
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "alloc-util.h"
22 #include "bus-internal.h"
23 #include "bus-unit-util.h"
24 #include "bus-util.h"
25 #include "cap-list.h"
26 #include "cgroup-util.h"
27 #include "cpu-set-util.h"
28 #include "env-util.h"
29 #include "errno-list.h"
30 #include "escape.h"
31 #include "hashmap.h"
32 #include "hexdecoct.h"
33 #include "hostname-util.h"
34 #include "in-addr-util.h"
35 #include "list.h"
36 #include "locale-util.h"
37 #include "mount-util.h"
38 #include "nsflags.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "rlimit-util.h"
43 #include "securebits-util.h"
44 #include "signal-util.h"
45 #include "string-util.h"
46 #include "syslog-util.h"
47 #include "terminal-util.h"
48 #include "user-util.h"
49 #include "utf8.h"
50 #include "util.h"
51
52 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
53 assert(message);
54 assert(u);
55
56 u->machine = NULL;
57
58 return sd_bus_message_read(
59 message,
60 "(ssssssouso)",
61 &u->id,
62 &u->description,
63 &u->load_state,
64 &u->active_state,
65 &u->sub_state,
66 &u->following,
67 &u->unit_path,
68 &u->job_id,
69 &u->job_type,
70 &u->job_path);
71 }
72
73 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
74 int r;
75
76 assert(m);
77 assert(prefix);
78
79 r = sd_bus_message_open_container(m, 'r', "iayu");
80 if (r < 0)
81 return r;
82
83 r = sd_bus_message_append(m, "i", family);
84 if (r < 0)
85 return r;
86
87 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
88 if (r < 0)
89 return r;
90
91 r = sd_bus_message_append(m, "u", prefixlen);
92 if (r < 0)
93 return r;
94
95 return sd_bus_message_close_container(m);
96 }
97
98 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
99 const char *eq, *field;
100 UnitDependency dep;
101 int r, rl;
102
103 assert(m);
104 assert(assignment);
105
106 eq = strchr(assignment, '=');
107 if (!eq) {
108 log_error("Not an assignment: %s", assignment);
109 return -EINVAL;
110 }
111
112 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
113 if (r < 0)
114 return bus_log_create_error(r);
115
116 field = strndupa(assignment, eq - assignment);
117 eq++;
118
119 if (streq(field, "CPUQuota")) {
120
121 if (isempty(eq))
122 r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
123 else {
124 r = parse_percent_unbounded(eq);
125 if (r <= 0) {
126 log_error_errno(r, "CPU quota '%s' invalid.", eq);
127 return -EINVAL;
128 }
129
130 r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
131 }
132
133 goto finish;
134
135 } else if (streq(field, "EnvironmentFile")) {
136
137 r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
138 eq[0] == '-' ? eq + 1 : eq,
139 eq[0] == '-');
140 goto finish;
141
142 } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
143 char *n;
144 usec_t t;
145 size_t l;
146
147 r = parse_sec(eq, &t);
148 if (r < 0)
149 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
150
151 l = strlen(field);
152 n = newa(char, l + 2);
153 if (!n)
154 return log_oom();
155
156 /* Change suffix Sec → USec */
157 strcpy(mempcpy(n, field, l - 3), "USec");
158 r = sd_bus_message_append(m, "sv", n, "t", t);
159 goto finish;
160
161 } else if (streq(field, "LogExtraFields")) {
162
163 r = sd_bus_message_append(m, "s", "LogExtraFields");
164 if (r < 0)
165 goto finish;
166
167 r = sd_bus_message_open_container(m, 'v', "aay");
168 if (r < 0)
169 goto finish;
170
171 r = sd_bus_message_open_container(m, 'a', "ay");
172 if (r < 0)
173 goto finish;
174
175 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
176 if (r < 0)
177 goto finish;
178
179 r = sd_bus_message_close_container(m);
180 if (r < 0)
181 goto finish;
182
183 r = sd_bus_message_close_container(m);
184 goto finish;
185
186 } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
187 uint64_t bytes;
188
189 if (isempty(eq) || streq(eq, "infinity"))
190 bytes = CGROUP_LIMIT_MAX;
191 else {
192 r = parse_percent(eq);
193 if (r >= 0) {
194 char *n;
195
196 /* When this is a percentage we'll convert this into a relative value in the range
197 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
198 * ones). This way the physical memory size can be determined server-side */
199
200 n = strjoina(field, "Scale");
201 r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
202 goto finish;
203
204 } else {
205 r = parse_size(eq, 1024, &bytes);
206 if (r < 0)
207 return log_error_errno(r, "Failed to parse bytes specification %s", assignment);
208 }
209 }
210
211 r = sd_bus_message_append(m, "sv", field, "t", bytes);
212 goto finish;
213
214 } else if (streq(field, "Delegate")) {
215
216 r = parse_boolean(eq);
217 if (r < 0) {
218 const char *p = eq;
219
220 r = sd_bus_message_append(m, "s", "DelegateControllers");
221 if (r < 0)
222 goto finish;
223
224 r = sd_bus_message_open_container(m, 'v', "as");
225 if (r < 0)
226 goto finish;
227
228 r = sd_bus_message_open_container(m, 'a', "s");
229 if (r < 0)
230 goto finish;
231
232 for (;;) {
233 _cleanup_free_ char *word = NULL;
234
235 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
236 if (r == 0)
237 break;
238 if (r == -ENOMEM)
239 return log_oom();
240 if (r < 0)
241 return log_error_errno(r, "Invalid syntax: %s", eq);
242
243 r = sd_bus_message_append(m, "s", word);
244 if (r < 0)
245 goto finish;
246 }
247
248 r = sd_bus_message_close_container(m);
249 if (r < 0)
250 goto finish;
251
252 r = sd_bus_message_close_container(m);
253 } else
254 r = sd_bus_message_append(m, "sv", "Delegate", "b", r);
255
256 goto finish;
257
258 } else if (streq(field, "TasksMax")) {
259 uint64_t t;
260
261 if (isempty(eq) || streq(eq, "infinity"))
262 t = (uint64_t) -1;
263 else {
264 r = parse_percent(eq);
265 if (r >= 0) {
266 r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
267 goto finish;
268 } else {
269 r = safe_atou64(eq, &t);
270 if (r < 0)
271 return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment);
272 }
273
274 }
275
276 r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
277 goto finish;
278
279 } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
280 const char *n, *appended;
281
282 n = startswith(eq, "fd:");
283 if (n) {
284 appended = strjoina(field, "FileDescriptorName");
285 r = sd_bus_message_append(m, "sv", appended, "s", n);
286
287 } else if ((n = startswith(eq, "file:"))) {
288 appended = strjoina(field, "File");
289 r = sd_bus_message_append(m, "sv", appended, "s", n);
290 } else
291 r = sd_bus_message_append(m, "sv", field, "s", eq);
292
293 goto finish;
294
295 } else if (streq(field, "StandardInputText")) {
296 _cleanup_free_ char *unescaped = NULL;
297
298 r = cunescape(eq, 0, &unescaped);
299 if (r < 0)
300 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
301
302 if (!strextend(&unescaped, "\n", NULL))
303 return log_oom();
304
305 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
306 * interface anyway */
307
308 r = sd_bus_message_append(m, "s", "StandardInputData");
309 if (r < 0)
310 return bus_log_create_error(r);
311
312 r = sd_bus_message_open_container(m, 'v', "ay");
313 if (r < 0)
314 return bus_log_create_error(r);
315
316 r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped));
317 if (r < 0)
318 return bus_log_create_error(r);
319
320 r = sd_bus_message_close_container(m);
321 goto finish;
322 }
323
324 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
325 if (r < 0)
326 return bus_log_create_error(r);
327
328 rl = rlimit_from_string(field);
329 if (rl >= 0) {
330 const char *sn;
331 struct rlimit l;
332
333 r = rlimit_parse(rl, eq, &l);
334 if (r < 0)
335 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
336
337 r = sd_bus_message_append(m, "v", "t", l.rlim_max);
338 if (r < 0)
339 return bus_log_create_error(r);
340
341 r = sd_bus_message_close_container(m);
342 if (r < 0)
343 return bus_log_create_error(r);
344
345 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
346 if (r < 0)
347 return bus_log_create_error(r);
348
349 sn = strjoina(field, "Soft");
350 r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
351
352 } else if (STR_IN_SET(field,
353 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
354 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
355 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
356 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
357 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
358 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
359 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
360 "CPUSchedulingResetOnFork", "LockPersonality")) {
361
362 r = parse_boolean(eq);
363 if (r < 0)
364 return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
365
366 r = sd_bus_message_append(m, "v", "b", r);
367
368 } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) {
369 uint64_t u;
370
371 r = cg_weight_parse(eq, &u);
372 if (r < 0)
373 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
374
375 r = sd_bus_message_append(m, "v", "t", u);
376
377 } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
378 uint64_t u;
379
380 r = cg_cpu_shares_parse(eq, &u);
381 if (r < 0)
382 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
383
384 r = sd_bus_message_append(m, "v", "t", u);
385
386 } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) {
387 uint64_t u;
388
389 r = cg_weight_parse(eq, &u);
390 if (r < 0)
391 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
392
393 r = sd_bus_message_append(m, "v", "t", u);
394
395 } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
396 uint64_t u;
397
398 r = cg_blkio_weight_parse(eq, &u);
399 if (r < 0)
400 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
401
402 r = sd_bus_message_append(m, "v", "t", u);
403
404 } else if (STR_IN_SET(field,
405 "User", "Group", "DevicePolicy", "KillMode",
406 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
407 "Description", "Slice", "Type", "WorkingDirectory",
408 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
409 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
410 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
411 "KeyringMode", "CollectMode", "FailureAction", "SuccessAction"))
412 r = sd_bus_message_append(m, "v", "s", eq);
413
414 else if (streq(field, "StandardInputData")) {
415 _cleanup_free_ char *cleaned = NULL;
416 _cleanup_free_ void *decoded = NULL;
417 size_t sz;
418
419 cleaned = strdup(eq);
420 if (!cleaned)
421 return log_oom();
422
423 delete_chars(cleaned, WHITESPACE);
424
425 r = unbase64mem(cleaned, (size_t) -1, &decoded, &sz);
426 if (r < 0)
427 return log_error_errno(r, "Failed to decode base64 data '%s': %m", cleaned);
428
429 r = sd_bus_message_open_container(m, 'v', "ay");
430 if (r < 0)
431 return bus_log_create_error(r);
432
433 r = sd_bus_message_append_array(m, 'y', decoded, sz);
434 if (r < 0)
435 return bus_log_create_error(r);
436
437 r = sd_bus_message_close_container(m);
438
439 } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
440 bool ignore;
441 const char *s;
442
443 if (eq[0] == '-') {
444 ignore = true;
445 s = eq + 1;
446 } else {
447 ignore = false;
448 s = eq;
449 }
450
451 r = sd_bus_message_append(m, "v", "(bs)", ignore, s);
452
453 } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) {
454 int level;
455
456 level = log_level_from_string(eq);
457 if (level < 0) {
458 log_error("Failed to parse %s value %s.", field, eq);
459 return -EINVAL;
460 }
461
462 r = sd_bus_message_append(m, "v", "i", level);
463
464 } else if (streq(field, "SyslogFacility")) {
465 int facility;
466
467 facility = log_facility_unshifted_from_string(eq);
468 if (facility < 0) {
469 log_error("Failed to parse %s value %s.", field, eq);
470 return -EINVAL;
471 }
472
473 r = sd_bus_message_append(m, "v", "i", facility);
474
475 } else if (streq(field, "SecureBits")) {
476
477 r = secure_bits_from_string(eq);
478 if (r < 0)
479 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
480
481 r = sd_bus_message_append(m, "v", "i", r);
482
483 } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
484 uint64_t sum = 0;
485 bool invert = false;
486 const char *p;
487
488 p = eq;
489 if (*p == '~') {
490 invert = true;
491 p++;
492 }
493
494 r = capability_set_from_string(p, &sum);
495 if (r < 0)
496 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
497
498 sum = invert ? ~sum : sum;
499
500 r = sd_bus_message_append(m, "v", "t", sum);
501
502 } else if (streq(field, "DeviceAllow")) {
503
504 if (isempty(eq))
505 r = sd_bus_message_append(m, "v", "a(ss)", 0);
506 else {
507 const char *path, *rwm, *e;
508
509 e = strchr(eq, ' ');
510 if (e) {
511 path = strndupa(eq, e - eq);
512 rwm = e+1;
513 } else {
514 path = eq;
515 rwm = "";
516 }
517
518 if (!is_deviceallow_pattern(path)) {
519 log_error("%s is not a device file in /dev.", path);
520 return -EINVAL;
521 }
522
523 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
524 }
525
526 } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
527
528 if (isempty(eq))
529 r = sd_bus_message_append(m, "v", "a(st)", 0);
530 else {
531 const char *path, *bandwidth, *e;
532 uint64_t bytes;
533
534 e = strchr(eq, ' ');
535 if (e) {
536 path = strndupa(eq, e - eq);
537 bandwidth = e+1;
538 } else {
539 log_error("Failed to parse %s value %s.", field, eq);
540 return -EINVAL;
541 }
542
543 if (!path_startswith(path, "/dev")) {
544 log_error("%s is not a device file in /dev.", path);
545 return -EINVAL;
546 }
547
548 if (streq(bandwidth, "infinity")) {
549 bytes = CGROUP_LIMIT_MAX;
550 } else {
551 r = parse_size(bandwidth, 1000, &bytes);
552 if (r < 0)
553 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
554 }
555
556 r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
557 }
558
559 } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
560
561 if (isempty(eq))
562 r = sd_bus_message_append(m, "v", "a(st)", 0);
563 else {
564 const char *path, *weight, *e;
565 uint64_t u;
566
567 e = strchr(eq, ' ');
568 if (e) {
569 path = strndupa(eq, e - eq);
570 weight = e+1;
571 } else {
572 log_error("Failed to parse %s value %s.", field, eq);
573 return -EINVAL;
574 }
575
576 if (!path_startswith(path, "/dev")) {
577 log_error("%s is not a device file in /dev.", path);
578 return -EINVAL;
579 }
580
581 r = safe_atou64(weight, &u);
582 if (r < 0)
583 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
584
585 r = sd_bus_message_append(m, "v", "a(st)", 1, path, u);
586 }
587
588 } else if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
589
590 if (isempty(eq))
591 r = sd_bus_message_append(m, "v", "a(iayu)", 0);
592 else {
593 unsigned char prefixlen;
594 union in_addr_union prefix = {};
595 int family;
596
597 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
598 if (r < 0)
599 return bus_log_create_error(r);
600
601 r = sd_bus_message_open_container(m, 'a', "(iayu)");
602 if (r < 0)
603 return bus_log_create_error(r);
604
605 if (streq(eq, "any")) {
606 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
607
608 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
609 if (r < 0)
610 return bus_log_create_error(r);
611
612 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
613 if (r < 0)
614 return bus_log_create_error(r);
615
616 } else if (is_localhost(eq)) {
617 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
618
619 prefix.in.s_addr = htobe32(0x7f000000);
620 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
621 if (r < 0)
622 return bus_log_create_error(r);
623
624 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
625 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
626 if (r < 0)
627 return r;
628
629 } else if (streq(eq, "link-local")) {
630
631 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
632
633 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
634 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
635 if (r < 0)
636 return bus_log_create_error(r);
637
638 prefix.in6 = (struct in6_addr) {
639 .s6_addr32[0] = htobe32(0xfe800000)
640 };
641 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
642 if (r < 0)
643 return bus_log_create_error(r);
644
645 } else if (streq(eq, "multicast")) {
646
647 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
648
649 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
650 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
651 if (r < 0)
652 return bus_log_create_error(r);
653
654 prefix.in6 = (struct in6_addr) {
655 .s6_addr32[0] = htobe32(0xff000000)
656 };
657 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
658 if (r < 0)
659 return bus_log_create_error(r);
660
661 } else {
662 r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
663 if (r < 0)
664 return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
665
666 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
667 if (r < 0)
668 return bus_log_create_error(r);
669 }
670
671 r = sd_bus_message_close_container(m);
672 if (r < 0)
673 return bus_log_create_error(r);
674
675 r = sd_bus_message_close_container(m);
676 if (r < 0)
677 return bus_log_create_error(r);
678 }
679
680 } else if (streq(field, "CPUSchedulingPolicy")) {
681 int n;
682
683 n = sched_policy_from_string(eq);
684 if (n < 0)
685 return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq);
686
687 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
688
689 } else if (streq(field, "CPUSchedulingPriority")) {
690 int n;
691
692 r = safe_atoi(eq, &n);
693 if (r < 0)
694 return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq);
695 if (!sched_priority_is_valid(n))
696 return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq);
697
698 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
699
700 } else if (streq(field, "CPUAffinity")) {
701 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
702 int ncpus;
703
704 ncpus = parse_cpu_set(eq, &cpuset);
705 if (ncpus < 0)
706 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
707
708 r = sd_bus_message_open_container(m, 'v', "ay");
709 if (r < 0)
710 return bus_log_create_error(r);
711
712 if (cpuset)
713 sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus));
714
715 r = sd_bus_message_close_container(m);
716
717 } else if (streq(field, "Nice")) {
718 int n;
719
720 r = parse_nice(eq, &n);
721 if (r < 0)
722 return log_error_errno(r, "Failed to parse nice value: %s", eq);
723
724 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
725
726 } else if (streq(field, "SystemCallFilter")) {
727 int whitelist;
728 _cleanup_strv_free_ char **l = NULL;
729 const char *p;
730
731 p = eq;
732 if (*p == '~') {
733 whitelist = 0;
734 p++;
735 } else
736 whitelist = 1;
737
738 if (whitelist != 0) {
739 r = strv_extend(&l, "@default");
740 if (r < 0)
741 return log_oom();
742 }
743
744 for (;;) {
745 _cleanup_free_ char *word = NULL;
746
747 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
748 if (r < 0)
749 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
750 if (r == 0)
751 break;
752
753 r = strv_extend(&l, word);
754 if (r < 0)
755 return log_oom();
756 }
757
758 r = sd_bus_message_open_container(m, 'v', "(bas)");
759 if (r < 0)
760 return bus_log_create_error(r);
761
762 r = sd_bus_message_open_container(m, 'r', "bas");
763 if (r < 0)
764 return bus_log_create_error(r);
765
766 r = sd_bus_message_append_basic(m, 'b', &whitelist);
767 if (r < 0)
768 return bus_log_create_error(r);
769
770 r = sd_bus_message_append_strv(m, l);
771 if (r < 0)
772 return bus_log_create_error(r);
773
774 r = sd_bus_message_close_container(m);
775 if (r < 0)
776 return bus_log_create_error(r);
777
778 r = sd_bus_message_close_container(m);
779 if (r < 0)
780 return bus_log_create_error(r);
781
782 } else if (streq(field, "SystemCallArchitectures")) {
783 const char *p;
784
785 r = sd_bus_message_open_container(m, 'v', "as");
786 if (r < 0)
787 return bus_log_create_error(r);
788
789 r = sd_bus_message_open_container(m, 'a', "s");
790 if (r < 0)
791 return bus_log_create_error(r);
792
793 for (p = eq;;) {
794 _cleanup_free_ char *word = NULL;
795
796 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
797 if (r < 0)
798 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
799 if (r == 0)
800 break;
801
802 r = sd_bus_message_append_basic(m, 's', word);
803 if (r < 0)
804 return bus_log_create_error(r);
805 }
806
807 r = sd_bus_message_close_container(m);
808 if (r < 0)
809 return bus_log_create_error(r);
810
811 r = sd_bus_message_close_container(m);
812
813 } else if (streq(field, "SystemCallErrorNumber")) {
814 int n;
815
816 n = parse_errno(eq);
817 if (n <= 0)
818 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
819
820 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
821
822 } else if (streq(field, "RestrictAddressFamilies")) {
823 int whitelist;
824 _cleanup_strv_free_ char **l = NULL;
825 const char *p = eq;
826
827 if (*p == '~') {
828 whitelist = 0;
829 p++;
830 } else
831 whitelist = 1;
832
833 for (;;) {
834 _cleanup_free_ char *word = NULL;
835
836 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
837 if (r < 0)
838 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
839 if (r == 0)
840 break;
841
842 r = strv_extend(&l, word);
843 if (r < 0)
844 return log_oom();
845 }
846
847 r = sd_bus_message_open_container(m, 'v', "(bas)");
848 if (r < 0)
849 return bus_log_create_error(r);
850
851 r = sd_bus_message_open_container(m, 'r', "bas");
852 if (r < 0)
853 return bus_log_create_error(r);
854
855 r = sd_bus_message_append_basic(m, 'b', &whitelist);
856 if (r < 0)
857 return bus_log_create_error(r);
858
859 r = sd_bus_message_append_strv(m, l);
860 if (r < 0)
861 return bus_log_create_error(r);
862
863 r = sd_bus_message_close_container(m);
864 if (r < 0)
865 return bus_log_create_error(r);
866
867 r = sd_bus_message_close_container(m);
868 if (r < 0)
869 return bus_log_create_error(r);
870
871 } else if (streq(field, "FileDescriptorStoreMax")) {
872 unsigned u;
873
874 r = safe_atou(eq, &u);
875 if (r < 0)
876 return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq);
877
878 r = sd_bus_message_append(m, "v", "u", (uint32_t) u);
879
880 } else if (streq(field, "IOSchedulingClass")) {
881 int c;
882
883 c = ioprio_class_from_string(eq);
884 if (c < 0)
885 return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq);
886
887 r = sd_bus_message_append(m, "v", "i", (int32_t) c);
888
889 } else if (streq(field, "IOSchedulingPriority")) {
890 int q;
891
892 r = ioprio_parse_priority(eq, &q);
893 if (r < 0)
894 return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq);
895
896 r = sd_bus_message_append(m, "v", "i", (int32_t) q);
897
898 } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
899 const char *p;
900
901 r = sd_bus_message_open_container(m, 'v', "as");
902 if (r < 0)
903 return bus_log_create_error(r);
904
905 r = sd_bus_message_open_container(m, 'a', "s");
906 if (r < 0)
907 return bus_log_create_error(r);
908
909 for (p = eq;;) {
910 _cleanup_free_ char *word = NULL;
911
912 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
913 if (r < 0)
914 return log_error_errno(r, "Failed to parse Environment value %s: %m", eq);
915 if (r == 0)
916 break;
917
918 if (streq(field, "Environment")) {
919 if (!env_assignment_is_valid(word)) {
920 log_error("Invalid environment assignment: %s", word);
921 return -EINVAL;
922 }
923 } else if (streq(field, "UnsetEnvironment")) {
924 if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
925 log_error("Invalid environment name or assignment: %s", word);
926 return -EINVAL;
927 }
928 } else { /* PassEnvironment */
929 if (!env_name_is_valid(word)) {
930 log_error("Invalid environment variable name: %s", word);
931 return -EINVAL;
932 }
933 }
934
935 r = sd_bus_message_append_basic(m, 's', word);
936 if (r < 0)
937 return bus_log_create_error(r);
938 }
939
940 r = sd_bus_message_close_container(m);
941 if (r < 0)
942 return bus_log_create_error(r);
943
944 r = sd_bus_message_close_container(m);
945
946 } else if (streq(field, "KillSignal")) {
947 int sig;
948
949 sig = signal_from_string_try_harder(eq);
950 if (sig < 0) {
951 log_error("Failed to parse %s value %s.", field, eq);
952 return -EINVAL;
953 }
954
955 r = sd_bus_message_append(m, "v", "i", sig);
956
957 } else if (streq(field, "TimerSlackNSec")) {
958 nsec_t n;
959
960 r = parse_nsec(eq, &n);
961 if (r < 0)
962 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
963
964 r = sd_bus_message_append(m, "v", "t", n);
965 } else if (streq(field, "OOMScoreAdjust")) {
966 int oa;
967
968 r = safe_atoi(eq, &oa);
969 if (r < 0)
970 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
971
972 if (!oom_score_adjust_is_valid(oa)) {
973 log_error("OOM score adjust value out of range");
974 return -EINVAL;
975 }
976
977 r = sd_bus_message_append(m, "v", "i", oa);
978 } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
979 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
980 const char *p;
981
982 r = sd_bus_message_open_container(m, 'v', "as");
983 if (r < 0)
984 return bus_log_create_error(r);
985
986 r = sd_bus_message_open_container(m, 'a', "s");
987 if (r < 0)
988 return bus_log_create_error(r);
989
990 for (p = eq;;) {
991 _cleanup_free_ char *word = NULL;
992 size_t offset;
993
994 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
995 if (r < 0)
996 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
997 if (r == 0)
998 break;
999
1000 if (!utf8_is_valid(word)) {
1001 log_error("Failed to parse %s value %s", field, eq);
1002 return -EINVAL;
1003 }
1004
1005 offset = word[0] == '-';
1006 offset += word[offset] == '+';
1007
1008 if (!path_is_absolute(word + offset)) {
1009 log_error("Failed to parse %s value %s", field, eq);
1010 return -EINVAL;
1011 }
1012
1013 path_kill_slashes(word + offset);
1014
1015 r = sd_bus_message_append_basic(m, 's', word);
1016 if (r < 0)
1017 return bus_log_create_error(r);
1018 }
1019
1020 r = sd_bus_message_close_container(m);
1021 if (r < 0)
1022 return bus_log_create_error(r);
1023
1024 r = sd_bus_message_close_container(m);
1025
1026 } else if (streq(field, "SupplementaryGroups")) {
1027 const char *p;
1028
1029 r = sd_bus_message_open_container(m, 'v', "as");
1030 if (r < 0)
1031 return bus_log_create_error(r);
1032
1033 r = sd_bus_message_open_container(m, 'a', "s");
1034 if (r < 0)
1035 return bus_log_create_error(r);
1036
1037 for (p = eq;;) {
1038 _cleanup_free_ char *word = NULL;
1039
1040 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1041 if (r < 0)
1042 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
1043 if (r == 0)
1044 break;
1045
1046 if (!valid_user_group_name_or_id(word)) {
1047 log_error("Failed to parse %s value %s", field, eq);
1048 return -EINVAL;
1049 }
1050
1051 r = sd_bus_message_append_basic(m, 's', word);
1052 if (r < 0)
1053 return bus_log_create_error(r);
1054 }
1055
1056 r = sd_bus_message_close_container(m);
1057 if (r < 0)
1058 return bus_log_create_error(r);
1059
1060 r = sd_bus_message_close_container(m);
1061
1062 } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1063 mode_t mode;
1064
1065 r = parse_mode(eq, &mode);
1066 if (r < 0)
1067 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
1068
1069 r = sd_bus_message_append(m, "v", "u", mode);
1070
1071 } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1072 const char *p;
1073
1074 r = sd_bus_message_open_container(m, 'v', "as");
1075 if (r < 0)
1076 return bus_log_create_error(r);
1077
1078 r = sd_bus_message_open_container(m, 'a', "s");
1079 if (r < 0)
1080 return bus_log_create_error(r);
1081
1082 for (p = eq;;) {
1083 _cleanup_free_ char *word = NULL;
1084
1085 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1086 if (r == -ENOMEM)
1087 return log_oom();
1088 if (r < 0)
1089 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
1090 if (r == 0)
1091 break;
1092
1093 r = sd_bus_message_append_basic(m, 's', word);
1094 if (r < 0)
1095 return bus_log_create_error(r);
1096 }
1097
1098 r = sd_bus_message_close_container(m);
1099 if (r < 0)
1100 return bus_log_create_error(r);
1101
1102 r = sd_bus_message_close_container(m);
1103
1104 } else if (streq(field, "RestrictNamespaces")) {
1105 bool invert = false;
1106 unsigned long flags = 0;
1107
1108 if (eq[0] == '~') {
1109 invert = true;
1110 eq++;
1111 }
1112
1113 r = parse_boolean(eq);
1114 if (r > 0)
1115 flags = 0;
1116 else if (r == 0)
1117 flags = NAMESPACE_FLAGS_ALL;
1118 else {
1119 r = namespace_flag_from_string_many(eq, &flags);
1120 if (r < 0)
1121 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1122 }
1123
1124 if (invert)
1125 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1126
1127 r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
1128 } else if ((dep = unit_dependency_from_string(field)) >= 0)
1129 r = sd_bus_message_append(m, "v", "as", 1, eq);
1130 else if (streq(field, "MountFlags")) {
1131 unsigned long f;
1132
1133 r = mount_propagation_flags_from_string(eq, &f);
1134 if (r < 0)
1135 return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
1136
1137 r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
1138 } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1139 const char *p = eq;
1140
1141 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1142 if (r < 0)
1143 return r;
1144
1145 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1146 if (r < 0)
1147 return r;
1148
1149 for (;;) {
1150 _cleanup_free_ char *source = NULL, *destination = NULL;
1151 char *s = NULL, *d = NULL;
1152 bool ignore_enoent = false;
1153 uint64_t flags = MS_REC;
1154
1155 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1156 if (r < 0)
1157 return log_error_errno(r, "Failed to parse argument: %m");
1158 if (r == 0)
1159 break;
1160
1161 s = source;
1162 if (s[0] == '-') {
1163 ignore_enoent = true;
1164 s++;
1165 }
1166
1167 if (p && p[-1] == ':') {
1168 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1169 if (r < 0)
1170 return log_error_errno(r, "Failed to parse argument: %m");
1171 if (r == 0) {
1172 log_error("Missing argument after ':': %s", eq);
1173 return -EINVAL;
1174 }
1175
1176 d = destination;
1177
1178 if (p && p[-1] == ':') {
1179 _cleanup_free_ char *options = NULL;
1180
1181 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1182 if (r < 0)
1183 return log_error_errno(r, "Failed to parse argument: %m");
1184
1185 if (isempty(options) || streq(options, "rbind"))
1186 flags = MS_REC;
1187 else if (streq(options, "norbind"))
1188 flags = 0;
1189 else {
1190 log_error("Unknown options: %s", eq);
1191 return -EINVAL;
1192 }
1193 }
1194 } else
1195 d = s;
1196
1197
1198 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1199 if (r < 0)
1200 return r;
1201 }
1202
1203 r = sd_bus_message_close_container(m);
1204 if (r < 0)
1205 return r;
1206
1207 r = sd_bus_message_close_container(m);
1208 } else {
1209 log_error("Unknown assignment %s.", assignment);
1210 return -EINVAL;
1211 }
1212
1213 finish:
1214 if (r < 0)
1215 return bus_log_create_error(r);
1216
1217 r = sd_bus_message_close_container(m);
1218 if (r < 0)
1219 return bus_log_create_error(r);
1220
1221 return 0;
1222 }
1223
1224 int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
1225 char **i;
1226 int r;
1227
1228 assert(m);
1229
1230 STRV_FOREACH(i, l) {
1231 r = bus_append_unit_property_assignment(m, *i);
1232 if (r < 0)
1233 return r;
1234 }
1235
1236 return 0;
1237 }
1238
1239 typedef struct BusWaitForJobs {
1240 sd_bus *bus;
1241 Set *jobs;
1242
1243 char *name;
1244 char *result;
1245
1246 sd_bus_slot *slot_job_removed;
1247 sd_bus_slot *slot_disconnected;
1248 } BusWaitForJobs;
1249
1250 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1251 assert(m);
1252
1253 log_error("Warning! D-Bus connection terminated.");
1254 sd_bus_close(sd_bus_message_get_bus(m));
1255
1256 return 0;
1257 }
1258
1259 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1260 const char *path, *unit, *result;
1261 BusWaitForJobs *d = userdata;
1262 uint32_t id;
1263 char *found;
1264 int r;
1265
1266 assert(m);
1267 assert(d);
1268
1269 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1270 if (r < 0) {
1271 bus_log_parse_error(r);
1272 return 0;
1273 }
1274
1275 found = set_remove(d->jobs, (char*) path);
1276 if (!found)
1277 return 0;
1278
1279 free(found);
1280
1281 if (!isempty(result))
1282 d->result = strdup(result);
1283
1284 if (!isempty(unit))
1285 d->name = strdup(unit);
1286
1287 return 0;
1288 }
1289
1290 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1291 if (!d)
1292 return;
1293
1294 set_free_free(d->jobs);
1295
1296 sd_bus_slot_unref(d->slot_disconnected);
1297 sd_bus_slot_unref(d->slot_job_removed);
1298
1299 sd_bus_unref(d->bus);
1300
1301 free(d->name);
1302 free(d->result);
1303
1304 free(d);
1305 }
1306
1307 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1308 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1309 int r;
1310
1311 assert(bus);
1312 assert(ret);
1313
1314 d = new0(BusWaitForJobs, 1);
1315 if (!d)
1316 return -ENOMEM;
1317
1318 d->bus = sd_bus_ref(bus);
1319
1320 /* When we are a bus client we match by sender. Direct
1321 * connections OTOH have no initialized sender field, and
1322 * hence we ignore the sender then */
1323 r = sd_bus_add_match(
1324 bus,
1325 &d->slot_job_removed,
1326 bus->bus_client ?
1327 "type='signal',"
1328 "sender='org.freedesktop.systemd1',"
1329 "interface='org.freedesktop.systemd1.Manager',"
1330 "member='JobRemoved',"
1331 "path='/org/freedesktop/systemd1'" :
1332 "type='signal',"
1333 "interface='org.freedesktop.systemd1.Manager',"
1334 "member='JobRemoved',"
1335 "path='/org/freedesktop/systemd1'",
1336 match_job_removed, d);
1337 if (r < 0)
1338 return r;
1339
1340 r = sd_bus_add_match(
1341 bus,
1342 &d->slot_disconnected,
1343 "type='signal',"
1344 "sender='org.freedesktop.DBus.Local',"
1345 "interface='org.freedesktop.DBus.Local',"
1346 "member='Disconnected'",
1347 match_disconnected, d);
1348 if (r < 0)
1349 return r;
1350
1351 *ret = d;
1352 d = NULL;
1353
1354 return 0;
1355 }
1356
1357 static int bus_process_wait(sd_bus *bus) {
1358 int r;
1359
1360 for (;;) {
1361 r = sd_bus_process(bus, NULL);
1362 if (r < 0)
1363 return r;
1364 if (r > 0)
1365 return 0;
1366
1367 r = sd_bus_wait(bus, (uint64_t) -1);
1368 if (r < 0)
1369 return r;
1370 }
1371 }
1372
1373 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1374 _cleanup_free_ char *dbus_path = NULL;
1375
1376 assert(d);
1377 assert(d->name);
1378 assert(result);
1379
1380 if (!endswith(d->name, ".service"))
1381 return -EINVAL;
1382
1383 dbus_path = unit_dbus_path_from_name(d->name);
1384 if (!dbus_path)
1385 return -ENOMEM;
1386
1387 return sd_bus_get_property_string(d->bus,
1388 "org.freedesktop.systemd1",
1389 dbus_path,
1390 "org.freedesktop.systemd1.Service",
1391 "Result",
1392 NULL,
1393 result);
1394 }
1395
1396 static const struct {
1397 const char *result, *explanation;
1398 } explanations [] = {
1399 { "resources", "of unavailable resources or another system error" },
1400 { "protocol", "the service did not take the steps required by its unit configuration" },
1401 { "timeout", "a timeout was exceeded" },
1402 { "exit-code", "the control process exited with error code" },
1403 { "signal", "a fatal signal was delivered to the control process" },
1404 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1405 { "watchdog", "the service failed to send watchdog ping" },
1406 { "start-limit", "start of the service was attempted too often" }
1407 };
1408
1409 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
1410 _cleanup_free_ char *service_shell_quoted = NULL;
1411 const char *systemctl = "systemctl", *journalctl = "journalctl";
1412
1413 assert(service);
1414
1415 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
1416
1417 if (!strv_isempty((char**) extra_args)) {
1418 _cleanup_free_ char *t;
1419
1420 t = strv_join((char**) extra_args, " ");
1421 systemctl = strjoina("systemctl ", t ? : "<args>");
1422 journalctl = strjoina("journalctl ", t ? : "<args>");
1423 }
1424
1425 if (!isempty(result)) {
1426 unsigned i;
1427
1428 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1429 if (streq(result, explanations[i].result))
1430 break;
1431
1432 if (i < ELEMENTSOF(explanations)) {
1433 log_error("Job for %s failed because %s.\n"
1434 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1435 service,
1436 explanations[i].explanation,
1437 systemctl,
1438 service_shell_quoted ?: "<service>",
1439 journalctl);
1440 goto finish;
1441 }
1442 }
1443
1444 log_error("Job for %s failed.\n"
1445 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1446 service,
1447 systemctl,
1448 service_shell_quoted ?: "<service>",
1449 journalctl);
1450
1451 finish:
1452 /* For some results maybe additional explanation is required */
1453 if (streq_ptr(result, "start-limit"))
1454 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1455 "followed by \"%1$s start %2$s\" again.",
1456 systemctl,
1457 service_shell_quoted ?: "<service>");
1458 }
1459
1460 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1461 int r = 0;
1462
1463 assert(d->result);
1464
1465 if (!quiet) {
1466 if (streq(d->result, "canceled"))
1467 log_error("Job for %s canceled.", strna(d->name));
1468 else if (streq(d->result, "timeout"))
1469 log_error("Job for %s timed out.", strna(d->name));
1470 else if (streq(d->result, "dependency"))
1471 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
1472 else if (streq(d->result, "invalid"))
1473 log_error("%s is not active, cannot reload.", strna(d->name));
1474 else if (streq(d->result, "assert"))
1475 log_error("Assertion failed on job for %s.", strna(d->name));
1476 else if (streq(d->result, "unsupported"))
1477 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
1478 else if (streq(d->result, "collected"))
1479 log_error("Queued job for %s was garbage collected.", strna(d->name));
1480 else if (!STR_IN_SET(d->result, "done", "skipped")) {
1481 if (d->name) {
1482 _cleanup_free_ char *result = NULL;
1483 int q;
1484
1485 q = bus_job_get_service_result(d, &result);
1486 if (q < 0)
1487 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
1488
1489 log_job_error_with_service_result(d->name, result, extra_args);
1490 } else
1491 log_error("Job failed. See \"journalctl -xe\" for details.");
1492 }
1493 }
1494
1495 if (STR_IN_SET(d->result, "canceled", "collected"))
1496 r = -ECANCELED;
1497 else if (streq(d->result, "timeout"))
1498 r = -ETIME;
1499 else if (streq(d->result, "dependency"))
1500 r = -EIO;
1501 else if (streq(d->result, "invalid"))
1502 r = -ENOEXEC;
1503 else if (streq(d->result, "assert"))
1504 r = -EPROTO;
1505 else if (streq(d->result, "unsupported"))
1506 r = -EOPNOTSUPP;
1507 else if (!STR_IN_SET(d->result, "done", "skipped"))
1508 r = -EIO;
1509
1510 return r;
1511 }
1512
1513 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
1514 int r = 0;
1515
1516 assert(d);
1517
1518 while (!set_isempty(d->jobs)) {
1519 int q;
1520
1521 q = bus_process_wait(d->bus);
1522 if (q < 0)
1523 return log_error_errno(q, "Failed to wait for response: %m");
1524
1525 if (d->result) {
1526 q = check_wait_response(d, quiet, extra_args);
1527 /* Return the first error as it is most likely to be
1528 * meaningful. */
1529 if (q < 0 && r == 0)
1530 r = q;
1531
1532 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
1533 }
1534
1535 d->name = mfree(d->name);
1536 d->result = mfree(d->result);
1537 }
1538
1539 return r;
1540 }
1541
1542 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
1543 int r;
1544
1545 assert(d);
1546
1547 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
1548 if (r < 0)
1549 return r;
1550
1551 return set_put_strdup(d->jobs, path);
1552 }
1553
1554 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
1555 int r;
1556
1557 r = bus_wait_for_jobs_add(d, path);
1558 if (r < 0)
1559 return log_oom();
1560
1561 return bus_wait_for_jobs(d, quiet, NULL);
1562 }
1563
1564 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
1565 const char *type, *path, *source;
1566 int r;
1567
1568 /* changes is dereferenced when calling unit_file_dump_changes() later,
1569 * so we have to make sure this is not NULL. */
1570 assert(changes);
1571 assert(n_changes);
1572
1573 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1574 if (r < 0)
1575 return bus_log_parse_error(r);
1576
1577 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1578 /* We expect only "success" changes to be sent over the bus.
1579 Hence, reject anything negative. */
1580 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1581
1582 if (ch < 0) {
1583 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1584 continue;
1585 }
1586
1587 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1588 if (r < 0)
1589 return r;
1590 }
1591 if (r < 0)
1592 return bus_log_parse_error(r);
1593
1594 r = sd_bus_message_exit_container(m);
1595 if (r < 0)
1596 return bus_log_parse_error(r);
1597
1598 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
1599 return 0;
1600 }
1601
1602 struct CGroupInfo {
1603 char *cgroup_path;
1604 bool is_const; /* If false, cgroup_path should be free()'d */
1605
1606 Hashmap *pids; /* PID → process name */
1607 bool done;
1608
1609 struct CGroupInfo *parent;
1610 LIST_FIELDS(struct CGroupInfo, siblings);
1611 LIST_HEAD(struct CGroupInfo, children);
1612 size_t n_children;
1613 };
1614
1615 static bool IS_ROOT(const char *p) {
1616 return isempty(p) || streq(p, "/");
1617 }
1618
1619 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
1620 struct CGroupInfo *parent = NULL, *cg;
1621 int r;
1622
1623 assert(cgroups);
1624 assert(ret);
1625
1626 if (IS_ROOT(path))
1627 path = "/";
1628
1629 cg = hashmap_get(cgroups, path);
1630 if (cg) {
1631 *ret = cg;
1632 return 0;
1633 }
1634
1635 if (!IS_ROOT(path)) {
1636 const char *e, *pp;
1637
1638 e = strrchr(path, '/');
1639 if (!e)
1640 return -EINVAL;
1641
1642 pp = strndupa(path, e - path);
1643 if (!pp)
1644 return -ENOMEM;
1645
1646 r = add_cgroup(cgroups, pp, false, &parent);
1647 if (r < 0)
1648 return r;
1649 }
1650
1651 cg = new0(struct CGroupInfo, 1);
1652 if (!cg)
1653 return -ENOMEM;
1654
1655 if (is_const)
1656 cg->cgroup_path = (char*) path;
1657 else {
1658 cg->cgroup_path = strdup(path);
1659 if (!cg->cgroup_path) {
1660 free(cg);
1661 return -ENOMEM;
1662 }
1663 }
1664
1665 cg->is_const = is_const;
1666 cg->parent = parent;
1667
1668 r = hashmap_put(cgroups, cg->cgroup_path, cg);
1669 if (r < 0) {
1670 if (!is_const)
1671 free(cg->cgroup_path);
1672 free(cg);
1673 return r;
1674 }
1675
1676 if (parent) {
1677 LIST_PREPEND(siblings, parent->children, cg);
1678 parent->n_children++;
1679 }
1680
1681 *ret = cg;
1682 return 1;
1683 }
1684
1685 static int add_process(
1686 Hashmap *cgroups,
1687 const char *path,
1688 pid_t pid,
1689 const char *name) {
1690
1691 struct CGroupInfo *cg;
1692 int r;
1693
1694 assert(cgroups);
1695 assert(name);
1696 assert(pid > 0);
1697
1698 r = add_cgroup(cgroups, path, true, &cg);
1699 if (r < 0)
1700 return r;
1701
1702 r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
1703 if (r < 0)
1704 return r;
1705
1706 return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1707 }
1708
1709 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1710 assert(cgroups);
1711 assert(cg);
1712
1713 while (cg->children)
1714 remove_cgroup(cgroups, cg->children);
1715
1716 hashmap_remove(cgroups, cg->cgroup_path);
1717
1718 if (!cg->is_const)
1719 free(cg->cgroup_path);
1720
1721 hashmap_free(cg->pids);
1722
1723 if (cg->parent)
1724 LIST_REMOVE(siblings, cg->parent->children, cg);
1725
1726 free(cg);
1727 }
1728
1729 static int cgroup_info_compare_func(const void *a, const void *b) {
1730 const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1731
1732 assert(x);
1733 assert(y);
1734
1735 return strcmp(x->cgroup_path, y->cgroup_path);
1736 }
1737
1738 static int dump_processes(
1739 Hashmap *cgroups,
1740 const char *cgroup_path,
1741 const char *prefix,
1742 unsigned n_columns,
1743 OutputFlags flags) {
1744
1745 struct CGroupInfo *cg;
1746 int r;
1747
1748 assert(prefix);
1749
1750 if (IS_ROOT(cgroup_path))
1751 cgroup_path = "/";
1752
1753 cg = hashmap_get(cgroups, cgroup_path);
1754 if (!cg)
1755 return 0;
1756
1757 if (!hashmap_isempty(cg->pids)) {
1758 const char *name;
1759 size_t n = 0, i;
1760 pid_t *pids;
1761 void *pidp;
1762 Iterator j;
1763 int width;
1764
1765 /* Order processes by their PID */
1766 pids = newa(pid_t, hashmap_size(cg->pids));
1767
1768 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1769 pids[n++] = PTR_TO_PID(pidp);
1770
1771 assert(n == hashmap_size(cg->pids));
1772 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1773
1774 width = DECIMAL_STR_WIDTH(pids[n-1]);
1775
1776 for (i = 0; i < n; i++) {
1777 _cleanup_free_ char *e = NULL;
1778 const char *special;
1779 bool more;
1780
1781 name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1782 assert(name);
1783
1784 if (n_columns != 0) {
1785 unsigned k;
1786
1787 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1788
1789 e = ellipsize(name, k, 100);
1790 if (e)
1791 name = e;
1792 }
1793
1794 more = i+1 < n || cg->children;
1795 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1796
1797 fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1798 prefix,
1799 special,
1800 width, pids[i],
1801 name);
1802 }
1803 }
1804
1805 if (cg->children) {
1806 struct CGroupInfo **children, *child;
1807 size_t n = 0, i;
1808
1809 /* Order subcgroups by their name */
1810 children = newa(struct CGroupInfo*, cg->n_children);
1811 LIST_FOREACH(siblings, child, cg->children)
1812 children[n++] = child;
1813 assert(n == cg->n_children);
1814 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1815
1816 if (n_columns != 0)
1817 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1818
1819 for (i = 0; i < n; i++) {
1820 _cleanup_free_ char *pp = NULL;
1821 const char *name, *special;
1822 bool more;
1823
1824 child = children[i];
1825
1826 name = strrchr(child->cgroup_path, '/');
1827 if (!name)
1828 return -EINVAL;
1829 name++;
1830
1831 more = i+1 < n;
1832 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1833
1834 fputs(prefix, stdout);
1835 fputs(special, stdout);
1836 fputs(name, stdout);
1837 fputc('\n', stdout);
1838
1839 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1840
1841 pp = strappend(prefix, special);
1842 if (!pp)
1843 return -ENOMEM;
1844
1845 r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1846 if (r < 0)
1847 return r;
1848 }
1849 }
1850
1851 cg->done = true;
1852 return 0;
1853 }
1854
1855 static int dump_extra_processes(
1856 Hashmap *cgroups,
1857 const char *prefix,
1858 unsigned n_columns,
1859 OutputFlags flags) {
1860
1861 _cleanup_free_ pid_t *pids = NULL;
1862 _cleanup_hashmap_free_ Hashmap *names = NULL;
1863 struct CGroupInfo *cg;
1864 size_t n_allocated = 0, n = 0, k;
1865 Iterator i;
1866 int width, r;
1867
1868 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1869 * combined, sorted, linear list. */
1870
1871 HASHMAP_FOREACH(cg, cgroups, i) {
1872 const char *name;
1873 void *pidp;
1874 Iterator j;
1875
1876 if (cg->done)
1877 continue;
1878
1879 if (hashmap_isempty(cg->pids))
1880 continue;
1881
1882 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1883 if (r < 0)
1884 return r;
1885
1886 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1887 return -ENOMEM;
1888
1889 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1890 pids[n++] = PTR_TO_PID(pidp);
1891
1892 r = hashmap_put(names, pidp, (void*) name);
1893 if (r < 0)
1894 return r;
1895 }
1896 }
1897
1898 if (n == 0)
1899 return 0;
1900
1901 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1902 width = DECIMAL_STR_WIDTH(pids[n-1]);
1903
1904 for (k = 0; k < n; k++) {
1905 _cleanup_free_ char *e = NULL;
1906 const char *name;
1907
1908 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1909 assert(name);
1910
1911 if (n_columns != 0) {
1912 unsigned z;
1913
1914 z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1915
1916 e = ellipsize(name, z, 100);
1917 if (e)
1918 name = e;
1919 }
1920
1921 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1922 prefix,
1923 special_glyph(TRIANGULAR_BULLET),
1924 width, pids[k],
1925 name);
1926 }
1927
1928 return 0;
1929 }
1930
1931 int unit_show_processes(
1932 sd_bus *bus,
1933 const char *unit,
1934 const char *cgroup_path,
1935 const char *prefix,
1936 unsigned n_columns,
1937 OutputFlags flags,
1938 sd_bus_error *error) {
1939
1940 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1941 Hashmap *cgroups = NULL;
1942 struct CGroupInfo *cg;
1943 int r;
1944
1945 assert(bus);
1946 assert(unit);
1947
1948 if (flags & OUTPUT_FULL_WIDTH)
1949 n_columns = 0;
1950 else if (n_columns <= 0)
1951 n_columns = columns();
1952
1953 prefix = strempty(prefix);
1954
1955 r = sd_bus_call_method(
1956 bus,
1957 "org.freedesktop.systemd1",
1958 "/org/freedesktop/systemd1",
1959 "org.freedesktop.systemd1.Manager",
1960 "GetUnitProcesses",
1961 error,
1962 &reply,
1963 "s",
1964 unit);
1965 if (r < 0)
1966 return r;
1967
1968 cgroups = hashmap_new(&string_hash_ops);
1969 if (!cgroups)
1970 return -ENOMEM;
1971
1972 r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1973 if (r < 0)
1974 goto finish;
1975
1976 for (;;) {
1977 const char *path = NULL, *name = NULL;
1978 uint32_t pid;
1979
1980 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1981 if (r < 0)
1982 goto finish;
1983 if (r == 0)
1984 break;
1985
1986 r = add_process(cgroups, path, pid, name);
1987 if (r < 0)
1988 goto finish;
1989 }
1990
1991 r = sd_bus_message_exit_container(reply);
1992 if (r < 0)
1993 goto finish;
1994
1995 r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1996 if (r < 0)
1997 goto finish;
1998
1999 r = dump_extra_processes(cgroups, prefix, n_columns, flags);
2000
2001 finish:
2002 while ((cg = hashmap_first(cgroups)))
2003 remove_cgroup(cgroups, cg);
2004
2005 hashmap_free(cgroups);
2006
2007 return r;
2008 }