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