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