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