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