]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-unit-util.c
core: implement transient socket unit
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
291d565a
LP
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"
cffaed83 25#include "cap-list.h"
291d565a 26#include "cgroup-util.h"
cffaed83 27#include "cpu-set-util.h"
291d565a 28#include "env-util.h"
cffaed83 29#include "errno-list.h"
291d565a
LP
30#include "escape.h"
31#include "hashmap.h"
08f3be7a 32#include "hexdecoct.h"
3dc5ca97
LP
33#include "hostname-util.h"
34#include "in-addr-util.h"
291d565a
LP
35#include "list.h"
36#include "locale-util.h"
83555251 37#include "mount-util.h"
add00535 38#include "nsflags.h"
291d565a
LP
39#include "parse-util.h"
40#include "path-util.h"
41#include "process-util.h"
42#include "rlimit-util.h"
cffaed83 43#include "securebits-util.h"
291d565a
LP
44#include "signal-util.h"
45#include "string-util.h"
46#include "syslog-util.h"
47#include "terminal-util.h"
cffaed83 48#include "user-util.h"
291d565a
LP
49#include "utf8.h"
50#include "util.h"
51
20b16441
LP
52int 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
3dc5ca97
LP
73static 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
20b16441
LP
98int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
99 const char *eq, *field;
289188ca 100 UnitDependency dep;
20b16441
LP
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);
9184ca48 123 else {
5124866d 124 r = parse_percent_unbounded(eq);
9184ca48
LP
125 if (r <= 0) {
126 log_error_errno(r, "CPU quota '%s' invalid.", eq);
20b16441
LP
127 return -EINVAL;
128 }
129
9184ca48 130 r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
20b16441
LP
131 }
132
133 goto finish;
134
135 } else if (streq(field, "EnvironmentFile")) {
136
57169651
LP
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] == '-');
20b16441
LP
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;
9184ca48 149
20b16441
LP
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);
20b16441
LP
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;
d58d600e 161
d3070fbd
LP
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
147fe484 187 } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) {
d58d600e
LP
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
f7903e8d 198 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
d58d600e
LP
199 * ones). This way the physical memory size can be determined server-side */
200
f7903e8d 201 n = strjoina(field, "Scale");
d58d600e
LP
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;
02638280
LP
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
83f8e808
LP
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;
08f3be7a 279
2038c3f5
LP
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
08f3be7a
LP
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;
20b16441
LP
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,
3dc5ca97
LP
354 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
355 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
356 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
357 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
268833ed 358 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent",
3dc5ca97
LP
359 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
360 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
535e0d19 361 "CPUSchedulingResetOnFork", "LockPersonality", "MakeDirectory")) {
20b16441
LP
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
66ebf6c0
TH
369 } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) {
370 uint64_t u;
371
372 r = cg_weight_parse(eq, &u);
6bbfdc67
LP
373 if (r < 0)
374 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
66ebf6c0
TH
375
376 r = sd_bus_message_append(m, "v", "t", u);
377
20b16441
LP
378 } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
379 uint64_t u;
380
381 r = cg_cpu_shares_parse(eq, &u);
6bbfdc67
LP
382 if (r < 0)
383 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441
LP
384
385 r = sd_bus_message_append(m, "v", "t", u);
386
13c31542
TH
387 } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) {
388 uint64_t u;
389
390 r = cg_weight_parse(eq, &u);
6bbfdc67
LP
391 if (r < 0)
392 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
13c31542
TH
393
394 r = sd_bus_message_append(m, "v", "t", u);
395
20b16441
LP
396 } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
397 uint64_t u;
398
3fdf9ad7 399 r = cg_blkio_weight_parse(eq, &u);
6bbfdc67
LP
400 if (r < 0)
401 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441
LP
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",
20b16441
LP
408 "Description", "Slice", "Type", "WorkingDirectory",
409 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
9efb9df9 410 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
b1edf445 411 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
268833ed
YW
412 "KeyringMode", "CollectMode", "FailureAction", "SuccessAction",
413 "OnCalendar"))
414
20b16441
LP
415 r = sd_bus_message_append(m, "v", "s", eq);
416
08f3be7a 417 else if (streq(field, "StandardInputData")) {
08f3be7a
LP
418 _cleanup_free_ void *decoded = NULL;
419 size_t sz;
420
081f36d8 421 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
08f3be7a 422 if (r < 0)
081f36d8 423 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
08f3be7a
LP
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")) {
cffaed83
YW
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
d3070fbd 449 } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) {
20b16441
LP
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
cffaed83
YW
471 } else if (streq(field, "SecureBits")) {
472
473 r = secure_bits_from_string(eq);
6bbfdc67
LP
474 if (r < 0)
475 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
cffaed83
YW
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);
6bbfdc67
LP
491 if (r < 0)
492 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
cffaed83
YW
493
494 sum = invert ? ~sum : sum;
495
496 r = sd_bus_message_append(m, "v", "t", sum);
497
20b16441
LP
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
3ccb8862 514 if (!is_deviceallow_pattern(path)) {
20b16441
LP
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
9be57249 522 } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
20b16441
LP
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
e57c9ce1 544 if (streq(bandwidth, "infinity")) {
13c31542
TH
545 bytes = CGROUP_LIMIT_MAX;
546 } else {
547 r = parse_size(bandwidth, 1000, &bytes);
6bbfdc67
LP
548 if (r < 0)
549 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
20b16441
LP
550 }
551
552 r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
553 }
554
13c31542 555 } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
20b16441
LP
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);
6bbfdc67
LP
578 if (r < 0)
579 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
580
096a4d53 581 r = sd_bus_message_append(m, "v", "a(st)", 1, path, u);
20b16441
LP
582 }
583
3dc5ca97
LP
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) {
5a10b4d6 635 .s6_addr32[0] = htobe32(0xfe800000)
3dc5ca97
LP
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) {
5a10b4d6 651 .s6_addr32[0] = htobe32(0xff000000)
3dc5ca97
LP
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
cffaed83
YW
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
2d966746
YW
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);
cffaed83
YW
711
712 r = sd_bus_message_close_container(m);
713
20b16441 714 } else if (streq(field, "Nice")) {
41bf0590 715 int n;
20b16441 716
41bf0590
LP
717 r = parse_nice(eq, &n);
718 if (r < 0)
719 return log_error_errno(r, "Failed to parse nice value: %s", eq);
20b16441 720
41bf0590 721 r = sd_bus_message_append(m, "v", "i", (int32_t) n);
20b16441 722
cffaed83
YW
723 } else if (streq(field, "SystemCallFilter")) {
724 int whitelist;
3f856a28 725 _cleanup_strv_free_ char **l = NULL;
cffaed83
YW
726 const char *p;
727
cffaed83
YW
728 p = eq;
729 if (*p == '~') {
730 whitelist = 0;
731 p++;
732 } else
733 whitelist = 1;
734
cffaed83 735 if (whitelist != 0) {
3f856a28 736 r = strv_extend(&l, "@default");
cffaed83 737 if (r < 0)
3f856a28 738 return log_oom();
cffaed83
YW
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
3f856a28 750 r = strv_extend(&l, word);
cffaed83 751 if (r < 0)
3f856a28 752 return log_oom();
cffaed83
YW
753 }
754
3f856a28
YW
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
cffaed83
YW
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);
3f856a28
YW
776 if (r < 0)
777 return bus_log_create_error(r);
cffaed83
YW
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
3df90f24
YW
813 n = parse_errno(eq);
814 if (n <= 0)
cffaed83
YW
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;
3f856a28
YW
821 _cleanup_strv_free_ char **l = NULL;
822 const char *p = eq;
cffaed83 823
cffaed83
YW
824 if (*p == '~') {
825 whitelist = 0;
826 p++;
827 } else
828 whitelist = 1;
829
cffaed83
YW
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
3f856a28 839 r = strv_extend(&l, word);
cffaed83 840 if (r < 0)
3f856a28 841 return log_oom();
cffaed83
YW
842 }
843
3f856a28
YW
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);
cffaed83
YW
857 if (r < 0)
858 return bus_log_create_error(r);
859
860 r = sd_bus_message_close_container(m);
3f856a28
YW
861 if (r < 0)
862 return bus_log_create_error(r);
cffaed83 863
3f856a28
YW
864 r = sd_bus_message_close_container(m);
865 if (r < 0)
866 return bus_log_create_error(r);
9f617cd0 867
9efb9df9
LP
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
7f452159
LP
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
00819cc1 895 } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
20b16441
LP
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
c58bd76a 906 for (p = eq;;) {
20b16441
LP
907 _cleanup_free_ char *word = NULL;
908
909 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
6bbfdc67
LP
910 if (r < 0)
911 return log_error_errno(r, "Failed to parse Environment value %s: %m", eq);
20b16441
LP
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 }
00819cc1
LP
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 }
20b16441
LP
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);
6bbfdc67
LP
958 if (r < 0)
959 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441
LP
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);
6bbfdc67
LP
966 if (r < 0)
967 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441
LP
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);
afcb1cd3 975
2a624c36
AP
976 } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
977 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
20b16441
LP
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
c58bd76a 988 for (p = eq;;) {
20b16441 989 _cleanup_free_ char *word = NULL;
20b7a007 990 size_t offset;
20b16441
LP
991
992 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
6bbfdc67
LP
993 if (r < 0)
994 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
20b16441
LP
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] == '-';
20b7a007
LP
1004 offset += word[offset] == '+';
1005
20b16441 1006 if (!path_is_absolute(word + offset)) {
afcb1cd3 1007 log_error("Path specified by %s is not absolute: %s", field, eq);
20b16441
LP
1008 return -EINVAL;
1009 }
1010
20b16441
LP
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
cffaed83
YW
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);
6bbfdc67
LP
1037 if (r < 0)
1038 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
cffaed83
YW
1039 if (r == 0)
1040 break;
1041
1042 if (!valid_user_group_name_or_id(word)) {
afcb1cd3 1043 log_error("Invalid group name or id is specified by %s: %s", field, eq);
cffaed83
YW
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
535e0d19
YW
1058 } else if (STR_IN_SET(field,
1059 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
1060 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask",
1061 "DirectoryMode")) {
53f47dfc
YW
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
3536f49e 1070 } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
20b16441
LP
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
c58bd76a 1081 for (p = eq;;) {
20b16441
LP
1082 _cleanup_free_ char *word = NULL;
1083
1084 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
091e9efe
LP
1085 if (r == -ENOMEM)
1086 return log_oom();
20b16441
LP
1087 if (r < 0)
1088 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
20b16441
LP
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
add00535
LP
1103 } else if (streq(field, "RestrictNamespaces")) {
1104 bool invert = false;
0d7578dc 1105 unsigned long flags = 0;
add00535
LP
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
0d7578dc 1126 r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
afcb1cd3 1127
289188ca 1128 } else if ((dep = unit_dependency_from_string(field)) >= 0)
afcb1cd3 1129
289188ca 1130 r = sd_bus_message_append(m, "v", "as", 1, eq);
afcb1cd3 1131
83555251
LP
1132 else if (streq(field, "MountFlags")) {
1133 unsigned long f;
1134
c7383828
ZJS
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);
83555251 1138
b9e2d822 1139 r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
afcb1cd3 1140
d2d6c096
LP
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);
f6c66be1
LP
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 }
508f63b4 1250 } while (!done);
f6c66be1
LP
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
58629001 1270 if (!strv_isempty(l)) {
f6c66be1
LP
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
268833ed
YW
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
535e0d19
YW
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
83555251 1321 } else {
c2288ade 1322 log_error("Unknown assignment: %s", assignment);
20b16441
LP
1323 return -EINVAL;
1324 }
1325
1326finish:
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
8673cf13
LP
1337int 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
20b16441
LP
1352typedef 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
1363static 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
1372static 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
1403void 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
1420int 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
1470static 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
1486static 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
bd5a1c91
LP
1493 if (!endswith(d->name, ".service"))
1494 return -EINVAL;
1495
20b16441
LP
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
1509static const struct {
1510 const char *result, *explanation;
1511} explanations [] = {
0b2de9d9 1512 { "resources", "of unavailable resources or another system error" },
7ed0a4c5 1513 { "protocol", "the service did not take the steps required by its unit configuration" },
20b16441
LP
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
1522static 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
804ee07c 1528 service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
20b16441 1529
3a58ca47 1530 if (!strv_isempty((char**) extra_args)) {
20b16441
LP
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
1564finish:
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
1573static 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));
c5a97ed1
LP
1591 else if (streq(d->result, "collected"))
1592 log_error("Queued job for %s was garbage collected.", strna(d->name));
ae835bc7 1593 else if (!STR_IN_SET(d->result, "done", "skipped")) {
20b16441 1594 if (d->name) {
20b16441 1595 _cleanup_free_ char *result = NULL;
bd5a1c91 1596 int q;
20b16441
LP
1597
1598 q = bus_job_get_service_result(d, &result);
1599 if (q < 0)
bd5a1c91 1600 log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
20b16441
LP
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
c5a97ed1 1608 if (STR_IN_SET(d->result, "canceled", "collected"))
20b16441
LP
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;
ae835bc7 1620 else if (!STR_IN_SET(d->result, "done", "skipped"))
20b16441
LP
1621 r = -EIO;
1622
1623 return r;
1624}
1625
1626int 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
1655int 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
1667int 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
1677int 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
acc0269c
CH
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
20b16441
LP
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
35d379b2 1711 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
20b16441
LP
1712 return 0;
1713}
1714
291d565a
LP
1715struct 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
1728static bool IS_ROOT(const char *p) {
1729 return isempty(p) || streq(p, "/");
1730}
1731
1732static 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
1798static 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
1822static 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
1842static 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
1851static 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;
323b7dc9 1908 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
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
7351ded5
IL
1929 if (n_columns != 0)
1930 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
291d565a
LP
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;
323b7dc9 1945 special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
291d565a
LP
1946
1947 fputs(prefix, stdout);
1948 fputs(special, stdout);
1949 fputs(name, stdout);
1950 fputc('\n', stdout);
1951
323b7dc9 1952 special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
291d565a
LP
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
1968static 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,
323b7dc9 2036 special_glyph(TRIANGULAR_BULLET),
291d565a
LP
2037 width, pids[k],
2038 name);
2039 }
2040
2041 return 0;
2042}
2043
2044int 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
2114finish:
2115 while ((cg = hashmap_first(cgroups)))
2116 remove_cgroup(cgroups, cg);
2117
2118 hashmap_free(cgroups);
2119
2120 return r;
2121}