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