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