]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "alloc-util.h"
4 #include "bus-error.h"
5 #include "bus-unit-util.h"
6 #include "bus-util.h"
7 #include "cap-list.h"
8 #include "cgroup-util.h"
9 #include "condition.h"
10 #include "cpu-set-util.h"
11 #include "escape.h"
12 #include "hexdecoct.h"
13 #include "hostname-util.h"
14 #include "in-addr-util.h"
15 #include "ip-protocol-list.h"
16 #include "locale-util.h"
17 #include "log.h"
18 #include "missing_fs.h"
19 #include "mountpoint-util.h"
20 #include "nsflags.h"
21 #include "parse-util.h"
22 #include "process-util.h"
23 #include "rlimit-util.h"
24 #include "securebits-util.h"
25 #include "signal-util.h"
26 #include "socket-util.h"
27 #include "sort-util.h"
28 #include "string-util.h"
29 #include "syslog-util.h"
30 #include "terminal-util.h"
31 #include "unit-def.h"
32 #include "user-util.h"
33 #include "utf8.h"
34
35 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
36 assert(message);
37 assert(u);
38
39 u->machine = NULL;
40
41 return sd_bus_message_read(
42 message,
43 "(ssssssouso)",
44 &u->id,
45 &u->description,
46 &u->load_state,
47 &u->active_state,
48 &u->sub_state,
49 &u->following,
50 &u->unit_path,
51 &u->job_id,
52 &u->job_type,
53 &u->job_path);
54 }
55
56 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
57 static int bus_append_##parse_func( \
58 sd_bus_message *m, \
59 const char *field, \
60 const char *eq) { \
61 type val; \
62 int r; \
63 \
64 r = parse_func(eq, &val); \
65 if (r < 0) \
66 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
67 \
68 r = sd_bus_message_append(m, "(sv)", field, \
69 bus_type, (cast_type) val); \
70 if (r < 0) \
71 return bus_log_create_error(r); \
72 \
73 return 1; \
74 }
75
76 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
77 static int bus_append_##parse_func( \
78 sd_bus_message *m, \
79 const char *field, \
80 const char *eq) { \
81 int r; \
82 \
83 r = parse_func(eq); \
84 if (r < 0) \
85 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
86 \
87 r = sd_bus_message_append(m, "(sv)", field, \
88 bus_type, (int32_t) r); \
89 if (r < 0) \
90 return bus_log_create_error(r); \
91 \
92 return 1; \
93 }
94
95 DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
96 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
97 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
98 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
99 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
100 DEFINE_BUS_APPEND_PARSE("i", parse_errno);
101 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
102 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
103 DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
104 DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
105 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
106 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
107 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
108 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
109 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
110 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
111 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
112 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
113 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
114 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
115 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
116 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
117
118 static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
119 int r;
120
121 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
122 if (r < 0)
123 return bus_log_create_error(r);
124
125 return 1;
126 }
127
128 static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
129 const char *p;
130 int r;
131
132 r = sd_bus_message_open_container(m, 'r', "sv");
133 if (r < 0)
134 return bus_log_create_error(r);
135
136 r = sd_bus_message_append_basic(m, 's', field);
137 if (r < 0)
138 return bus_log_create_error(r);
139
140 r = sd_bus_message_open_container(m, 'v', "as");
141 if (r < 0)
142 return bus_log_create_error(r);
143
144 r = sd_bus_message_open_container(m, 'a', "s");
145 if (r < 0)
146 return bus_log_create_error(r);
147
148 for (p = eq;;) {
149 _cleanup_free_ char *word = NULL;
150
151 r = extract_first_word(&p, &word, NULL, flags);
152 if (r == 0)
153 break;
154 if (r == -ENOMEM)
155 return log_oom();
156 if (r < 0)
157 return log_error_errno(r, "Invalid syntax: %s", eq);
158
159 r = sd_bus_message_append_basic(m, 's', word);
160 if (r < 0)
161 return bus_log_create_error(r);
162 }
163
164 r = sd_bus_message_close_container(m);
165 if (r < 0)
166 return bus_log_create_error(r);
167
168 r = sd_bus_message_close_container(m);
169 if (r < 0)
170 return bus_log_create_error(r);
171
172 r = sd_bus_message_close_container(m);
173 if (r < 0)
174 return bus_log_create_error(r);
175
176 return 1;
177 }
178
179 static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
180 int r;
181
182 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
183 if (r < 0)
184 return bus_log_create_error(r);
185
186 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
187 if (r < 0)
188 return bus_log_create_error(r);
189
190 r = sd_bus_message_open_container(m, 'v', "ay");
191 if (r < 0)
192 return bus_log_create_error(r);
193
194 r = sd_bus_message_append_array(m, 'y', buf, n);
195 if (r < 0)
196 return bus_log_create_error(r);
197
198 r = sd_bus_message_close_container(m);
199 if (r < 0)
200 return bus_log_create_error(r);
201
202 r = sd_bus_message_close_container(m);
203 if (r < 0)
204 return bus_log_create_error(r);
205
206 return 1;
207 }
208
209 static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
210 char *n;
211 usec_t t;
212 size_t l;
213 int r;
214
215 r = parse_sec(eq, &t);
216 if (r < 0)
217 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
218
219 l = strlen(field);
220 n = newa(char, l + 2);
221 /* Change suffix Sec → USec */
222 strcpy(mempcpy(n, field, l - 3), "USec");
223
224 r = sd_bus_message_append(m, "(sv)", n, "t", t);
225 if (r < 0)
226 return bus_log_create_error(r);
227
228 return 1;
229 }
230
231 static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
232 uint64_t v;
233 int r;
234
235 r = parse_size(eq, base, &v);
236 if (r < 0)
237 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
238
239 r = sd_bus_message_append(m, "(sv)", field, "t", v);
240 if (r < 0)
241 return bus_log_create_error(r);
242
243 return 1;
244 }
245
246 static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
247 bool ignore_failure = false, explicit_path = false, done = false;
248 _cleanup_strv_free_ char **l = NULL;
249 _cleanup_free_ char *path = NULL;
250 int r;
251
252 do {
253 switch (*eq) {
254
255 case '-':
256 if (ignore_failure)
257 done = true;
258 else {
259 ignore_failure = true;
260 eq++;
261 }
262 break;
263
264 case '@':
265 if (explicit_path)
266 done = true;
267 else {
268 explicit_path = true;
269 eq++;
270 }
271 break;
272
273 case '+':
274 case '!':
275 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
276 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
277 "Sorry, but +, ! and !! are currently not supported for transient services.");
278
279 default:
280 done = true;
281 break;
282 }
283 } while (!done);
284
285 if (explicit_path) {
286 r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
287 if (r < 0)
288 return log_error_errno(r, "Failed to parse path: %m");
289 }
290
291 r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
292 if (r < 0)
293 return log_error_errno(r, "Failed to parse command line: %m");
294
295 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
296 if (r < 0)
297 return bus_log_create_error(r);
298
299 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
300 if (r < 0)
301 return bus_log_create_error(r);
302
303 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
304 if (r < 0)
305 return bus_log_create_error(r);
306
307 r = sd_bus_message_open_container(m, 'a', "(sasb)");
308 if (r < 0)
309 return bus_log_create_error(r);
310
311 if (!strv_isempty(l)) {
312
313 r = sd_bus_message_open_container(m, 'r', "sasb");
314 if (r < 0)
315 return bus_log_create_error(r);
316
317 r = sd_bus_message_append(m, "s", path ?: l[0]);
318 if (r < 0)
319 return bus_log_create_error(r);
320
321 r = sd_bus_message_append_strv(m, l);
322 if (r < 0)
323 return bus_log_create_error(r);
324
325 r = sd_bus_message_append(m, "b", ignore_failure);
326 if (r < 0)
327 return bus_log_create_error(r);
328
329 r = sd_bus_message_close_container(m);
330 if (r < 0)
331 return bus_log_create_error(r);
332 }
333
334 r = sd_bus_message_close_container(m);
335 if (r < 0)
336 return bus_log_create_error(r);
337
338 r = sd_bus_message_close_container(m);
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 return 1;
347 }
348
349 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
350 int r;
351
352 assert(m);
353 assert(prefix);
354
355 r = sd_bus_message_open_container(m, 'r', "iayu");
356 if (r < 0)
357 return r;
358
359 r = sd_bus_message_append(m, "i", family);
360 if (r < 0)
361 return r;
362
363 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
364 if (r < 0)
365 return r;
366
367 r = sd_bus_message_append(m, "u", prefixlen);
368 if (r < 0)
369 return r;
370
371 return sd_bus_message_close_container(m);
372 }
373
374 static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
375 int r;
376
377 if (STR_IN_SET(field, "DevicePolicy", "Slice"))
378
379 return bus_append_string(m, field, eq);
380
381 if (STR_IN_SET(field,
382 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
383 "TasksAccounting", "IPAccounting"))
384
385 return bus_append_parse_boolean(m, field, eq);
386
387 if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
388
389 return bus_append_cg_weight_parse(m, field, eq);
390
391 if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
392
393 return bus_append_cg_cpu_shares_parse(m, field, eq);
394
395 if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
396
397 return bus_append_cg_blkio_weight_parse(m, field, eq);
398
399 if (streq(field, "Delegate")) {
400
401 r = parse_boolean(eq);
402 if (r < 0)
403 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES);
404
405 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
406 if (r < 0)
407 return bus_log_create_error(r);
408
409 return 1;
410 }
411
412 if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
413
414 if (isempty(eq) || streq(eq, "infinity")) {
415 r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
416 if (r < 0)
417 return bus_log_create_error(r);
418 return 1;
419 }
420
421 r = parse_permille(eq);
422 if (r >= 0) {
423 char *n;
424
425 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
426 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
427 * size can be determined server-side. */
428
429 n = strjoina(field, "Scale");
430 r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
431 if (r < 0)
432 return bus_log_create_error(r);
433
434 return 1;
435 }
436
437 if (streq(field, "TasksMax"))
438 return bus_append_safe_atou64(m, field, eq);
439
440 return bus_append_parse_size(m, field, eq, 1024);
441 }
442
443 if (streq(field, "CPUQuota")) {
444
445 if (isempty(eq))
446 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
447 else {
448 r = parse_permille_unbounded(eq);
449 if (r == 0)
450 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
451 "CPU quota too small.");
452 if (r < 0)
453 return log_error_errno(r, "CPU quota '%s' invalid.", eq);
454
455 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
456 }
457
458 if (r < 0)
459 return bus_log_create_error(r);
460
461 return 1;
462 }
463
464 if (streq(field, "CPUQuotaPeriodSec")) {
465 usec_t u = USEC_INFINITY;
466
467 r = parse_sec_def_infinity(eq, &u);
468 if (r < 0)
469 return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
470
471 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
472 if (r < 0)
473 return bus_log_create_error(r);
474
475 return 1;
476 }
477
478 if (streq(field, "DeviceAllow")) {
479
480 if (isempty(eq))
481 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
482 else {
483 const char *path = eq, *rwm = NULL, *e;
484
485 e = strchr(eq, ' ');
486 if (e) {
487 path = strndupa(eq, e - eq);
488 rwm = e+1;
489 }
490
491 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
492 }
493
494 if (r < 0)
495 return bus_log_create_error(r);
496
497 return 1;
498 }
499
500 if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
501
502 if (isempty(eq))
503 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
504 else {
505 const char *path, *bandwidth, *e;
506 uint64_t bytes;
507
508 e = strchr(eq, ' ');
509 if (!e)
510 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
511 "Failed to parse %s value %s.",
512 field, eq);
513
514 path = strndupa(eq, e - eq);
515 bandwidth = e+1;
516
517 if (streq(bandwidth, "infinity"))
518 bytes = CGROUP_LIMIT_MAX;
519 else {
520 r = parse_size(bandwidth, 1000, &bytes);
521 if (r < 0)
522 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
523 }
524
525 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
526 }
527
528 if (r < 0)
529 return bus_log_create_error(r);
530
531 return 1;
532 }
533
534 if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
535
536 if (isempty(eq))
537 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
538 else {
539 const char *path, *weight, *e;
540 uint64_t u;
541
542 e = strchr(eq, ' ');
543 if (!e)
544 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
545 "Failed to parse %s value %s.",
546 field, eq);
547
548 path = strndupa(eq, e - eq);
549 weight = e+1;
550
551 r = safe_atou64(weight, &u);
552 if (r < 0)
553 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
554
555 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
556 }
557
558 if (r < 0)
559 return bus_log_create_error(r);
560
561 return 1;
562 }
563
564 if (streq(field, "IODeviceLatencyTargetSec")) {
565 const char *field_usec = "IODeviceLatencyTargetUSec";
566
567 if (isempty(eq))
568 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
569 else {
570 const char *path, *target, *e;
571 usec_t usec;
572
573 e = strchr(eq, ' ');
574 if (!e)
575 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
576 "Failed to parse %s value %s.",
577 field, eq);
578
579 path = strndupa(eq, e - eq);
580 target = e+1;
581
582 r = parse_sec(target, &usec);
583 if (r < 0)
584 return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
585
586 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
587 }
588
589 if (r < 0)
590 return bus_log_create_error(r);
591
592 return 1;
593 }
594
595 if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
596 unsigned char prefixlen;
597 union in_addr_union prefix = {};
598 int family;
599
600 if (isempty(eq)) {
601 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
602 if (r < 0)
603 return bus_log_create_error(r);
604
605 return 1;
606 }
607
608 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
609 if (r < 0)
610 return bus_log_create_error(r);
611
612 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
613 if (r < 0)
614 return bus_log_create_error(r);
615
616 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
617 if (r < 0)
618 return bus_log_create_error(r);
619
620 r = sd_bus_message_open_container(m, 'a', "(iayu)");
621 if (r < 0)
622 return bus_log_create_error(r);
623
624 if (streq(eq, "any")) {
625 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
626
627 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
628 if (r < 0)
629 return bus_log_create_error(r);
630
631 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
632 if (r < 0)
633 return bus_log_create_error(r);
634
635 } else if (is_localhost(eq)) {
636 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
637
638 prefix.in.s_addr = htobe32(0x7f000000);
639 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
640 if (r < 0)
641 return bus_log_create_error(r);
642
643 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
644 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
645 if (r < 0)
646 return r;
647
648 } else if (streq(eq, "link-local")) {
649 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
650
651 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
652 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
653 if (r < 0)
654 return bus_log_create_error(r);
655
656 prefix.in6 = (struct in6_addr) {
657 .s6_addr32[0] = htobe32(0xfe800000)
658 };
659 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
660 if (r < 0)
661 return bus_log_create_error(r);
662
663 } else if (streq(eq, "multicast")) {
664 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
665
666 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
667 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
668 if (r < 0)
669 return bus_log_create_error(r);
670
671 prefix.in6 = (struct in6_addr) {
672 .s6_addr32[0] = htobe32(0xff000000)
673 };
674 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
675 if (r < 0)
676 return bus_log_create_error(r);
677
678 } else {
679 for (;;) {
680 _cleanup_free_ char *word = NULL;
681
682 r = extract_first_word(&eq, &word, NULL, 0);
683 if (r == 0)
684 break;
685 if (r == -ENOMEM)
686 return log_oom();
687 if (r < 0)
688 return log_error_errno(r, "Failed to parse %s: %s", field, eq);
689
690 r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
691 if (r < 0)
692 return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
693
694 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
695 if (r < 0)
696 return bus_log_create_error(r);
697 }
698 }
699
700 r = sd_bus_message_close_container(m);
701 if (r < 0)
702 return bus_log_create_error(r);
703
704 r = sd_bus_message_close_container(m);
705 if (r < 0)
706 return bus_log_create_error(r);
707
708 r = sd_bus_message_close_container(m);
709 if (r < 0)
710 return bus_log_create_error(r);
711
712 return 1;
713 }
714
715 return 0;
716 }
717
718 static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
719
720 if (streq(field, "Where"))
721
722 return bus_append_string(m, field, eq);
723
724 if (streq(field, "DirectoryMode"))
725
726 return bus_append_parse_mode(m, field, eq);
727
728 if (streq(field, "TimeoutIdleSec"))
729
730 return bus_append_parse_sec_rename(m, field, eq);
731
732 return 0;
733 }
734
735 static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
736 const char *suffix;
737 int r;
738
739 if (STR_IN_SET(field,
740 "User", "Group",
741 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
742 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
743 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
744 "RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath"))
745
746 return bus_append_string(m, field, eq);
747
748 if (STR_IN_SET(field,
749 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "PrivateTmp",
750 "PrivateDevices", "PrivateNetwork", "PrivateUsers", "PrivateMounts",
751 "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime",
752 "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules",
753 "ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
754 "ProtectHostname", "RestrictSUIDSGID"))
755
756 return bus_append_parse_boolean(m, field, eq);
757
758 if (STR_IN_SET(field,
759 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
760 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
761 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
762 "SupplementaryGroups", "SystemCallArchitectures"))
763
764 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
765
766 if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
767
768 return bus_append_log_level_from_string(m, field, eq);
769
770 if (streq(field, "SyslogFacility"))
771
772 return bus_append_log_facility_unshifted_from_string(m, field, eq);
773
774 if (streq(field, "SecureBits"))
775
776 return bus_append_secure_bits_from_string(m, field, eq);
777
778 if (streq(field, "CPUSchedulingPolicy"))
779
780 return bus_append_sched_policy_from_string(m, field, eq);
781
782 if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
783
784 return bus_append_safe_atoi(m, field, eq);
785
786 if (streq(field, "Nice"))
787
788 return bus_append_parse_nice(m, field, eq);
789
790 if (streq(field, "SystemCallErrorNumber"))
791
792 return bus_append_parse_errno(m, field, eq);
793
794 if (streq(field, "IOSchedulingClass"))
795
796 return bus_append_ioprio_class_from_string(m, field, eq);
797
798 if (streq(field, "IOSchedulingPriority"))
799
800 return bus_append_ioprio_parse_priority(m, field, eq);
801
802 if (STR_IN_SET(field,
803 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
804 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
805
806 return bus_append_parse_mode(m, field, eq);
807
808 if (streq(field, "TimerSlackNSec"))
809
810 return bus_append_parse_nsec(m, field, eq);
811
812 if (streq(field, "LogRateLimitIntervalSec"))
813
814 return bus_append_parse_sec_rename(m, field, eq);
815
816 if (streq(field, "LogRateLimitBurst"))
817
818 return bus_append_safe_atou(m, field, eq);
819
820 if (streq(field, "MountFlags"))
821
822 return bus_append_mount_propagation_flags_from_string(m, field, eq);
823
824 if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
825
826 return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
827
828 if (streq(field, "EnvironmentFile")) {
829
830 if (isempty(eq))
831 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
832 else
833 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
834 eq[0] == '-' ? eq + 1 : eq,
835 eq[0] == '-');
836 if (r < 0)
837 return bus_log_create_error(r);
838
839 return 1;
840 }
841
842 if (streq(field, "LogExtraFields")) {
843
844 r = sd_bus_message_open_container(m, 'r', "sv");
845 if (r < 0)
846 return bus_log_create_error(r);
847
848 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
849 if (r < 0)
850 return bus_log_create_error(r);
851
852 r = sd_bus_message_open_container(m, 'v', "aay");
853 if (r < 0)
854 return bus_log_create_error(r);
855
856 r = sd_bus_message_open_container(m, 'a', "ay");
857 if (r < 0)
858 return bus_log_create_error(r);
859
860 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
861 if (r < 0)
862 return bus_log_create_error(r);
863
864 r = sd_bus_message_close_container(m);
865 if (r < 0)
866 return bus_log_create_error(r);
867
868 r = sd_bus_message_close_container(m);
869 if (r < 0)
870 return bus_log_create_error(r);
871
872 r = sd_bus_message_close_container(m);
873 if (r < 0)
874 return bus_log_create_error(r);
875
876 return 1;
877 }
878
879 if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
880 const char *n, *appended;
881
882 if ((n = startswith(eq, "fd:"))) {
883 appended = strjoina(field, "FileDescriptorName");
884 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
885 } else if ((n = startswith(eq, "file:"))) {
886 appended = strjoina(field, "File");
887 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
888 } else if ((n = startswith(eq, "append:"))) {
889 appended = strjoina(field, "FileToAppend");
890 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
891 } else
892 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
893 if (r < 0)
894 return bus_log_create_error(r);
895
896 return 1;
897 }
898
899 if (streq(field, "StandardInputText")) {
900 _cleanup_free_ char *unescaped = NULL;
901
902 r = cunescape(eq, 0, &unescaped);
903 if (r < 0)
904 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
905
906 if (!strextend(&unescaped, "\n", NULL))
907 return log_oom();
908
909 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
910 * interface anyway */
911
912 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
913 }
914
915 if (streq(field, "StandardInputData")) {
916 _cleanup_free_ void *decoded = NULL;
917 size_t sz;
918
919 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
920 if (r < 0)
921 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
922
923 return bus_append_byte_array(m, field, decoded, sz);
924 }
925
926 if ((suffix = startswith(field, "Limit"))) {
927 int rl;
928
929 rl = rlimit_from_string(suffix);
930 if (rl >= 0) {
931 const char *sn;
932 struct rlimit l;
933
934 r = rlimit_parse(rl, eq, &l);
935 if (r < 0)
936 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
937
938 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
939 if (r < 0)
940 return bus_log_create_error(r);
941
942 sn = strjoina(field, "Soft");
943 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
944 if (r < 0)
945 return bus_log_create_error(r);
946
947 return 1;
948 }
949 }
950
951 if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
952 int ignore = 0;
953 const char *s = eq;
954
955 if (eq[0] == '-') {
956 ignore = 1;
957 s = eq + 1;
958 }
959
960 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
961 if (r < 0)
962 return bus_log_create_error(r);
963
964 return 1;
965 }
966
967 if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) {
968 uint64_t sum = 0;
969 bool invert = false;
970 const char *p = eq;
971
972 if (*p == '~') {
973 invert = true;
974 p++;
975 }
976
977 r = capability_set_from_string(p, &sum);
978 if (r < 0)
979 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
980
981 sum = invert ? ~sum : sum;
982
983 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
984 if (r < 0)
985 return bus_log_create_error(r);
986
987 return 1;
988 }
989
990 if (streq(field, "CPUAffinity")) {
991 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
992
993 r = parse_cpu_set(eq, &cpuset);
994 if (r < 0)
995 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
996
997 return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
998 }
999
1000 if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
1001 int whitelist = 1;
1002 const char *p = eq;
1003
1004 if (*p == '~') {
1005 whitelist = 0;
1006 p++;
1007 }
1008
1009 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1010 if (r < 0)
1011 return bus_log_create_error(r);
1012
1013 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1014 if (r < 0)
1015 return bus_log_create_error(r);
1016
1017 r = sd_bus_message_open_container(m, 'v', "(bas)");
1018 if (r < 0)
1019 return bus_log_create_error(r);
1020
1021 r = sd_bus_message_open_container(m, 'r', "bas");
1022 if (r < 0)
1023 return bus_log_create_error(r);
1024
1025 r = sd_bus_message_append_basic(m, 'b', &whitelist);
1026 if (r < 0)
1027 return bus_log_create_error(r);
1028
1029 r = sd_bus_message_open_container(m, 'a', "s");
1030 if (r < 0)
1031 return bus_log_create_error(r);
1032
1033 for (;;) {
1034 _cleanup_free_ char *word = NULL;
1035
1036 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1037 if (r == 0)
1038 break;
1039 if (r == -ENOMEM)
1040 return log_oom();
1041 if (r < 0)
1042 return log_error_errno(r, "Invalid syntax: %s", eq);
1043
1044 r = sd_bus_message_append_basic(m, 's', word);
1045 if (r < 0)
1046 return bus_log_create_error(r);
1047 }
1048
1049 r = sd_bus_message_close_container(m);
1050 if (r < 0)
1051 return bus_log_create_error(r);
1052
1053 r = sd_bus_message_close_container(m);
1054 if (r < 0)
1055 return bus_log_create_error(r);
1056
1057 r = sd_bus_message_close_container(m);
1058 if (r < 0)
1059 return bus_log_create_error(r);
1060
1061 r = sd_bus_message_close_container(m);
1062 if (r < 0)
1063 return bus_log_create_error(r);
1064
1065 return 1;
1066 }
1067
1068 if (streq(field, "RestrictNamespaces")) {
1069 bool invert = false;
1070 unsigned long flags;
1071
1072 r = parse_boolean(eq);
1073 if (r > 0)
1074 flags = 0;
1075 else if (r == 0)
1076 flags = NAMESPACE_FLAGS_ALL;
1077 else {
1078 if (eq[0] == '~') {
1079 invert = true;
1080 eq++;
1081 }
1082
1083 r = namespace_flags_from_string(eq, &flags);
1084 if (r < 0)
1085 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1086 }
1087
1088 if (invert)
1089 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1090
1091 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1092 if (r < 0)
1093 return bus_log_create_error(r);
1094
1095 return 1;
1096 }
1097
1098 if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
1099 const char *p = eq;
1100
1101 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1102 if (r < 0)
1103 return bus_log_create_error(r);
1104
1105 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1106 if (r < 0)
1107 return bus_log_create_error(r);
1108
1109 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1110 if (r < 0)
1111 return bus_log_create_error(r);
1112
1113 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1114 if (r < 0)
1115 return bus_log_create_error(r);
1116
1117 for (;;) {
1118 _cleanup_free_ char *source = NULL, *destination = NULL;
1119 char *s = NULL, *d = NULL;
1120 bool ignore_enoent = false;
1121 uint64_t flags = MS_REC;
1122
1123 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1124 if (r < 0)
1125 return log_error_errno(r, "Failed to parse argument: %m");
1126 if (r == 0)
1127 break;
1128
1129 s = source;
1130 if (s[0] == '-') {
1131 ignore_enoent = true;
1132 s++;
1133 }
1134
1135 if (p && p[-1] == ':') {
1136 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
1137 if (r < 0)
1138 return log_error_errno(r, "Failed to parse argument: %m");
1139 if (r == 0)
1140 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1141 "Missing argument after ':': %s",
1142 eq);
1143
1144 d = destination;
1145
1146 if (p && p[-1] == ':') {
1147 _cleanup_free_ char *options = NULL;
1148
1149 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
1150 if (r < 0)
1151 return log_error_errno(r, "Failed to parse argument: %m");
1152
1153 if (isempty(options) || streq(options, "rbind"))
1154 flags = MS_REC;
1155 else if (streq(options, "norbind"))
1156 flags = 0;
1157 else
1158 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1159 "Unknown options: %s",
1160 eq);
1161 }
1162 } else
1163 d = s;
1164
1165 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1166 if (r < 0)
1167 return bus_log_create_error(r);
1168 }
1169
1170 r = sd_bus_message_close_container(m);
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 r = sd_bus_message_close_container(m);
1179 if (r < 0)
1180 return bus_log_create_error(r);
1181
1182 return 1;
1183 }
1184
1185 if (streq(field, "TemporaryFileSystem")) {
1186 const char *p = eq;
1187
1188 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1189 if (r < 0)
1190 return bus_log_create_error(r);
1191
1192 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195
1196 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1197 if (r < 0)
1198 return bus_log_create_error(r);
1199
1200 r = sd_bus_message_open_container(m, 'a', "(ss)");
1201 if (r < 0)
1202 return bus_log_create_error(r);
1203
1204 for (;;) {
1205 _cleanup_free_ char *word = NULL, *path = NULL;
1206 const char *w;
1207
1208 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1209 if (r < 0)
1210 return log_error_errno(r, "Failed to parse argument: %m");
1211 if (r == 0)
1212 break;
1213
1214 w = word;
1215 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1216 if (r < 0)
1217 return log_error_errno(r, "Failed to parse argument: %m");
1218 if (r == 0)
1219 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1220 "Failed to parse argument: %s",
1221 p);
1222
1223 r = sd_bus_message_append(m, "(ss)", path, w);
1224 if (r < 0)
1225 return bus_log_create_error(r);
1226 }
1227
1228 r = sd_bus_message_close_container(m);
1229 if (r < 0)
1230 return bus_log_create_error(r);
1231
1232 r = sd_bus_message_close_container(m);
1233 if (r < 0)
1234 return bus_log_create_error(r);
1235
1236 r = sd_bus_message_close_container(m);
1237 if (r < 0)
1238 return bus_log_create_error(r);
1239
1240 return 1;
1241 }
1242
1243 return 0;
1244 }
1245
1246 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
1247
1248 if (streq(field, "KillMode"))
1249
1250 return bus_append_string(m, field, eq);
1251
1252 if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
1253
1254 return bus_append_parse_boolean(m, field, eq);
1255
1256 if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
1257
1258 return bus_append_signal_from_string(m, field, eq);
1259
1260 return 0;
1261 }
1262
1263 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
1264
1265 if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
1266
1267 return bus_append_string(m, field, eq);
1268
1269 if (streq(field, "TimeoutSec"))
1270
1271 return bus_append_parse_sec_rename(m, field, eq);
1272
1273 if (streq(field, "DirectoryMode"))
1274
1275 return bus_append_parse_mode(m, field, eq);
1276
1277 if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1278
1279 return bus_append_parse_boolean(m, field, eq);
1280
1281 return 0;
1282 }
1283
1284 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1285 int r;
1286
1287 if (streq(field, "MakeDirectory"))
1288
1289 return bus_append_parse_boolean(m, field, eq);
1290
1291 if (streq(field, "DirectoryMode"))
1292
1293 return bus_append_parse_mode(m, field, eq);
1294
1295 if (STR_IN_SET(field,
1296 "PathExists", "PathExistsGlob", "PathChanged",
1297 "PathModified", "DirectoryNotEmpty")) {
1298
1299 if (isempty(eq))
1300 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1301 else
1302 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1303 if (r < 0)
1304 return bus_log_create_error(r);
1305
1306 return 1;
1307 }
1308
1309 return 0;
1310 }
1311
1312 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
1313 int r;
1314
1315 if (STR_IN_SET(field,
1316 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1317 "USBFunctionDescriptors", "USBFunctionStrings", "OOMPolicy"))
1318
1319 return bus_append_string(m, field, eq);
1320
1321 if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
1322
1323 return bus_append_parse_boolean(m, field, eq);
1324
1325 if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
1326
1327 return bus_append_parse_sec_rename(m, field, eq);
1328
1329 if (streq(field, "TimeoutSec")) {
1330
1331 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1332 if (r < 0)
1333 return r;
1334
1335 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1336 }
1337
1338 if (streq(field, "FileDescriptorStoreMax"))
1339
1340 return bus_append_safe_atou(m, field, eq);
1341
1342 if (STR_IN_SET(field,
1343 "ExecStartPre", "ExecStart", "ExecStartPost",
1344 "ExecReload", "ExecStop", "ExecStopPost"))
1345
1346 return bus_append_exec_command(m, field, eq);
1347
1348 if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1349 _cleanup_free_ int *status = NULL, *signal = NULL;
1350 size_t sz_status = 0, sz_signal = 0;
1351 const char *p;
1352
1353 for (p = eq;;) {
1354 _cleanup_free_ char *word = NULL;
1355 int val;
1356
1357 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1358 if (r == 0)
1359 break;
1360 if (r == -ENOMEM)
1361 return log_oom();
1362 if (r < 0)
1363 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1364
1365 r = safe_atoi(word, &val);
1366 if (r < 0) {
1367 val = signal_from_string(word);
1368 if (val < 0)
1369 return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
1370
1371 signal = reallocarray(signal, sz_signal + 1, sizeof(int));
1372 if (!signal)
1373 return log_oom();
1374
1375 signal[sz_signal++] = val;
1376 } else {
1377 status = reallocarray(status, sz_status + 1, sizeof(int));
1378 if (!status)
1379 return log_oom();
1380
1381 status[sz_status++] = val;
1382 }
1383 }
1384
1385 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1386 if (r < 0)
1387 return bus_log_create_error(r);
1388
1389 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1390 if (r < 0)
1391 return bus_log_create_error(r);
1392
1393 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1394 if (r < 0)
1395 return bus_log_create_error(r);
1396
1397 r = sd_bus_message_open_container(m, 'r', "aiai");
1398 if (r < 0)
1399 return bus_log_create_error(r);
1400
1401 r = sd_bus_message_append_array(m, 'i', status, sz_status);
1402 if (r < 0)
1403 return bus_log_create_error(r);
1404
1405 r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
1406 if (r < 0)
1407 return bus_log_create_error(r);
1408
1409 r = sd_bus_message_close_container(m);
1410 if (r < 0)
1411 return bus_log_create_error(r);
1412
1413 r = sd_bus_message_close_container(m);
1414 if (r < 0)
1415 return bus_log_create_error(r);
1416
1417 r = sd_bus_message_close_container(m);
1418 if (r < 0)
1419 return bus_log_create_error(r);
1420
1421 return 1;
1422 }
1423
1424 return 0;
1425 }
1426
1427 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1428 int r;
1429
1430 if (STR_IN_SET(field,
1431 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1432 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1433
1434 return bus_append_parse_boolean(m, field, eq);
1435
1436 if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
1437
1438 return bus_append_safe_atoi(m, field, eq);
1439
1440 if (streq(field, "IPTOS"))
1441
1442 return bus_append_ip_tos_from_string(m, field, eq);
1443
1444 if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1445
1446 return bus_append_safe_atou(m, field, eq);
1447
1448 if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
1449
1450 return bus_append_parse_mode(m, field, eq);
1451
1452 if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1453
1454 return bus_append_safe_atoi64(m, field, eq);
1455
1456 if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1457
1458 return bus_append_parse_sec_rename(m, field, eq);
1459
1460 if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1461
1462 return bus_append_parse_size(m, field, eq, 1024);
1463
1464 if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1465
1466 return bus_append_exec_command(m, field, eq);
1467
1468 if (STR_IN_SET(field,
1469 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1470 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1471 "SocketUser", "SocketGroup"))
1472
1473 return bus_append_string(m, field, eq);
1474
1475 if (streq(field, "Symlinks"))
1476
1477 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1478
1479 if (streq(field, "SocketProtocol"))
1480
1481 return bus_append_parse_ip_protocol(m, field, eq);
1482
1483 if (STR_IN_SET(field,
1484 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1485 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1486
1487 if (isempty(eq))
1488 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1489 else
1490 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
1491 if (r < 0)
1492 return bus_log_create_error(r);
1493
1494 return 1;
1495 }
1496
1497 return 0;
1498 }
1499 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
1500 int r;
1501
1502 if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent",
1503 "OnTimezoneChange", "OnClockChange"))
1504
1505 return bus_append_parse_boolean(m, field, eq);
1506
1507 if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
1508
1509 return bus_append_parse_sec_rename(m, field, eq);
1510
1511 if (STR_IN_SET(field,
1512 "OnActiveSec", "OnBootSec", "OnStartupSec",
1513 "OnUnitActiveSec","OnUnitInactiveSec")) {
1514
1515 if (isempty(eq))
1516 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
1517 else {
1518 usec_t t;
1519 r = parse_sec(eq, &t);
1520 if (r < 0)
1521 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
1522
1523 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
1524 }
1525 if (r < 0)
1526 return bus_log_create_error(r);
1527
1528 return 1;
1529 }
1530
1531 if (streq(field, "OnCalendar")) {
1532
1533 if (isempty(eq))
1534 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
1535 else
1536 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
1537 if (r < 0)
1538 return bus_log_create_error(r);
1539
1540 return 1;
1541 }
1542
1543 return 0;
1544 }
1545
1546 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
1547 ConditionType t = _CONDITION_TYPE_INVALID;
1548 bool is_condition = false;
1549 int r;
1550
1551 if (STR_IN_SET(field,
1552 "Description", "SourcePath", "OnFailureJobMode",
1553 "JobTimeoutAction", "JobTimeoutRebootArgument",
1554 "StartLimitAction", "FailureAction", "SuccessAction",
1555 "RebootArgument", "CollectMode"))
1556
1557 return bus_append_string(m, field, eq);
1558
1559 if (STR_IN_SET(field,
1560 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1561 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
1562
1563 return bus_append_parse_boolean(m, field, eq);
1564
1565 if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1566
1567 return bus_append_parse_sec_rename(m, field, eq);
1568
1569 if (streq(field, "StartLimitBurst"))
1570
1571 return bus_append_safe_atou(m, field, eq);
1572
1573 if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) {
1574
1575 if (isempty(eq))
1576 r = sd_bus_message_append(m, "(sv)", field, "i", -1);
1577 else {
1578 uint8_t u;
1579
1580 r = safe_atou8(eq, &u);
1581 if (r < 0)
1582 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
1583
1584 r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
1585 }
1586 if (r < 0)
1587 return bus_log_create_error(r);
1588
1589 return 1;
1590 }
1591
1592 if (unit_dependency_from_string(field) >= 0 ||
1593 STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
1594
1595 return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
1596
1597 t = condition_type_from_string(field);
1598 if (t >= 0)
1599 is_condition = true;
1600 else
1601 t = assert_type_from_string(field);
1602 if (t >= 0) {
1603 if (isempty(eq))
1604 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
1605 else {
1606 const char *p = eq;
1607 int trigger, negate;
1608
1609 trigger = *p == '|';
1610 if (trigger)
1611 p++;
1612
1613 negate = *p == '!';
1614 if (negate)
1615 p++;
1616
1617 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
1618 field, trigger, negate, p);
1619 }
1620 if (r < 0)
1621 return bus_log_create_error(r);
1622
1623 return 1;
1624 }
1625
1626 return 0;
1627 }
1628
1629 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1630 const char *eq, *field;
1631 int r;
1632
1633 assert(m);
1634 assert(assignment);
1635
1636 eq = strchr(assignment, '=');
1637 if (!eq)
1638 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1639 "Not an assignment: %s", assignment);
1640
1641 field = strndupa(assignment, eq - assignment);
1642 eq++;
1643
1644 switch (t) {
1645 case UNIT_SERVICE:
1646 r = bus_append_cgroup_property(m, field, eq);
1647 if (r != 0)
1648 return r;
1649
1650 r = bus_append_execute_property(m, field, eq);
1651 if (r != 0)
1652 return r;
1653
1654 r = bus_append_kill_property(m, field, eq);
1655 if (r != 0)
1656 return r;
1657
1658 r = bus_append_service_property(m, field, eq);
1659 if (r != 0)
1660 return r;
1661 break;
1662
1663 case UNIT_SOCKET:
1664 r = bus_append_cgroup_property(m, field, eq);
1665 if (r != 0)
1666 return r;
1667
1668 r = bus_append_execute_property(m, field, eq);
1669 if (r != 0)
1670 return r;
1671
1672 r = bus_append_kill_property(m, field, eq);
1673 if (r != 0)
1674 return r;
1675
1676 r = bus_append_socket_property(m, field, eq);
1677 if (r != 0)
1678 return r;
1679 break;
1680
1681 case UNIT_TIMER:
1682 r = bus_append_timer_property(m, field, eq);
1683 if (r != 0)
1684 return r;
1685 break;
1686
1687 case UNIT_PATH:
1688 r = bus_append_path_property(m, field, eq);
1689 if (r != 0)
1690 return r;
1691 break;
1692
1693 case UNIT_SLICE:
1694 r = bus_append_cgroup_property(m, field, eq);
1695 if (r != 0)
1696 return r;
1697 break;
1698
1699 case UNIT_SCOPE:
1700
1701 if (streq(field, "TimeoutStopSec"))
1702 return bus_append_parse_sec_rename(m, field, eq);
1703
1704 r = bus_append_cgroup_property(m, field, eq);
1705 if (r != 0)
1706 return r;
1707
1708 r = bus_append_kill_property(m, field, eq);
1709 if (r != 0)
1710 return r;
1711 break;
1712
1713 case UNIT_MOUNT:
1714 r = bus_append_cgroup_property(m, field, eq);
1715 if (r != 0)
1716 return r;
1717
1718 r = bus_append_execute_property(m, field, eq);
1719 if (r != 0)
1720 return r;
1721
1722 r = bus_append_kill_property(m, field, eq);
1723 if (r != 0)
1724 return r;
1725
1726 r = bus_append_mount_property(m, field, eq);
1727 if (r != 0)
1728 return r;
1729
1730 break;
1731
1732 case UNIT_AUTOMOUNT:
1733 r = bus_append_automount_property(m, field, eq);
1734 if (r != 0)
1735 return r;
1736
1737 break;
1738
1739 case UNIT_TARGET:
1740 case UNIT_DEVICE:
1741 case UNIT_SWAP:
1742 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1743 "Not supported unit type");
1744
1745 default:
1746 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1747 "Invalid unit type");
1748 }
1749
1750 r = bus_append_unit_property(m, field, eq);
1751 if (r != 0)
1752 return r;
1753
1754 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1755 "Unknown assignment: %s", assignment);
1756 }
1757
1758 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
1759 char **i;
1760 int r;
1761
1762 assert(m);
1763
1764 STRV_FOREACH(i, l) {
1765 r = bus_append_unit_property_assignment(m, t, *i);
1766 if (r < 0)
1767 return r;
1768 }
1769
1770 return 0;
1771 }
1772
1773 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
1774 const char *type, *path, *source;
1775 int r;
1776
1777 /* changes is dereferenced when calling unit_file_dump_changes() later,
1778 * so we have to make sure this is not NULL. */
1779 assert(changes);
1780 assert(n_changes);
1781
1782 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1783 if (r < 0)
1784 return bus_log_parse_error(r);
1785
1786 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1787 /* We expect only "success" changes to be sent over the bus.
1788 Hence, reject anything negative. */
1789 UnitFileChangeType ch = unit_file_change_type_from_string(type);
1790
1791 if (ch < 0) {
1792 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
1793 continue;
1794 }
1795
1796 r = unit_file_changes_add(changes, n_changes, ch, path, source);
1797 if (r < 0)
1798 return r;
1799 }
1800 if (r < 0)
1801 return bus_log_parse_error(r);
1802
1803 r = sd_bus_message_exit_container(m);
1804 if (r < 0)
1805 return bus_log_parse_error(r);
1806
1807 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
1808 return 0;
1809 }
1810
1811 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
1812 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1813 _cleanup_free_ char *path = NULL;
1814 int r;
1815
1816 path = unit_dbus_path_from_name(name);
1817 if (!path)
1818 return log_oom();
1819
1820 /* This function warns on it's own, because otherwise it'd be awkward to pass
1821 * the dbus error message around. */
1822
1823 r = sd_bus_get_property_string(
1824 bus,
1825 "org.freedesktop.systemd1",
1826 path,
1827 "org.freedesktop.systemd1.Unit",
1828 "LoadState",
1829 &error,
1830 load_state);
1831 if (r < 0)
1832 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
1833
1834 return 0;
1835 }