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