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