1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "af-list.h"
4 #include "alloc-util.h"
5 #include "bus-error.h"
6 #include "bus-unit-util.h"
7 #include "bus-util.h"
8 #include "cap-list.h"
9 #include "cgroup-setup.h"
10 #include "cgroup-util.h"
11 #include "condition.h"
12 #include "coredump-util.h"
13 #include "cpu-set-util.h"
14 #include "dissect-image.h"
15 #include "escape.h"
16 #include "exec-util.h"
17 #include "exit-status.h"
18 #include "fileio.h"
19 #include "hexdecoct.h"
20 #include "hostname-util.h"
21 #include "in-addr-util.h"
22 #include "ioprio-util.h"
23 #include "ip-protocol-list.h"
24 #include "libmount-util.h"
25 #include "locale-util.h"
26 #include "log.h"
27 #include "macro.h"
28 #include "missing_fs.h"
29 #include "mountpoint-util.h"
30 #include "nsflags.h"
31 #include "numa-util.h"
32 #include "parse-helpers.h"
33 #include "parse-util.h"
34 #include "path-util.h"
35 #include "percent-util.h"
36 #include "process-util.h"
37 #include "rlimit-util.h"
38 #if HAVE_SECCOMP
39 #include "seccomp-util.h"
40 #endif
41 #include "securebits-util.h"
42 #include "signal-util.h"
43 #include "socket-util.h"
44 #include "sort-util.h"
45 #include "stdio-util.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 
bus_parse_unit_info(sd_bus_message * message,UnitInfo * u)53 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
54         assert(message);
55         assert(u);
56 
57         u->machine = NULL;
58 
59         return sd_bus_message_read(
60                         message,
61                         "(ssssssouso)",
62                         &u->id,
63                         &u->description,
64                         &u->load_state,
65                         &u->active_state,
66                         &u->sub_state,
67                         &u->following,
68                         &u->unit_path,
69                         &u->job_id,
70                         &u->job_type,
71                         &u->job_path);
72 }
73 
74 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
75         static int bus_append_##parse_func(                             \
76                         sd_bus_message *m,                              \
77                         const char *field,                              \
78                         const char *eq) {                               \
79                 type val;                                               \
80                 int r;                                                  \
81                                                                         \
82                 r = parse_func(eq, &val);                               \
83                 if (r < 0)                                              \
84                         return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
85                                                                         \
86                 r = sd_bus_message_append(m, "(sv)", field,             \
87                                           bus_type, (cast_type) val);   \
88                 if (r < 0)                                              \
89                         return bus_log_create_error(r);                 \
90                                                                         \
91                 return 1;                                               \
92         }
93 
94 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func)                   \
95         static int bus_append_##parse_func(                             \
96                         sd_bus_message *m,                              \
97                         const char *field,                              \
98                         const char *eq) {                               \
99                 int r;                                                  \
100                                                                         \
101                 r = parse_func(eq);                                     \
102                 if (r < 0)                                              \
103                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq);                                 \
104                                                                         \
105                 r = sd_bus_message_append(m, "(sv)", field,             \
106                                           bus_type, (int32_t) r);       \
107                 if (r < 0)                                              \
108                         return bus_log_create_error(r);                 \
109                                                                         \
110                 return 1;                                               \
111         }
112 
113 DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
114 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
115 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
116 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
117 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
118 #if !HAVE_SECCOMP
seccomp_parse_errno_or_action(const char * eq)119 static inline int seccomp_parse_errno_or_action(const char *eq) { return -EINVAL; }
120 #endif
121 DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action);
122 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
123 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
124 DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
125 DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
126 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
127 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
128 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
129 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
130 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
131 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
132 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
133 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
134 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
135 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
136 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
137 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
138 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
139 
bus_append_string(sd_bus_message * m,const char * field,const char * eq)140 static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
141         int r;
142 
143         r = sd_bus_message_append(m, "(sv)", field, "s", eq);
144         if (r < 0)
145                 return bus_log_create_error(r);
146 
147         return 1;
148 }
149 
bus_append_strv(sd_bus_message * m,const char * field,const char * eq,ExtractFlags flags)150 static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
151         const char *p;
152         int r;
153 
154         r = sd_bus_message_open_container(m, 'r', "sv");
155         if (r < 0)
156                 return bus_log_create_error(r);
157 
158         r = sd_bus_message_append_basic(m, 's', field);
159         if (r < 0)
160                 return bus_log_create_error(r);
161 
162         r = sd_bus_message_open_container(m, 'v', "as");
163         if (r < 0)
164                 return bus_log_create_error(r);
165 
166         r = sd_bus_message_open_container(m, 'a', "s");
167         if (r < 0)
168                 return bus_log_create_error(r);
169 
170         for (p = eq;;) {
171                 _cleanup_free_ char *word = NULL;
172 
173                 r = extract_first_word(&p, &word, NULL, flags);
174                 if (r == 0)
175                         break;
176                 if (r == -ENOMEM)
177                         return log_oom();
178                 if (r < 0)
179                         return log_error_errno(r, "Invalid syntax: %s", eq);
180 
181                 r = sd_bus_message_append_basic(m, 's', word);
182                 if (r < 0)
183                         return bus_log_create_error(r);
184         }
185 
186         r = sd_bus_message_close_container(m);
187         if (r < 0)
188                 return bus_log_create_error(r);
189 
190         r = sd_bus_message_close_container(m);
191         if (r < 0)
192                 return bus_log_create_error(r);
193 
194         r = sd_bus_message_close_container(m);
195         if (r < 0)
196                 return bus_log_create_error(r);
197 
198         return 1;
199 }
200 
bus_append_byte_array(sd_bus_message * m,const char * field,const void * buf,size_t n)201 static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
202         int r;
203 
204         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
205         if (r < 0)
206                 return bus_log_create_error(r);
207 
208         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
209         if (r < 0)
210                 return bus_log_create_error(r);
211 
212         r = sd_bus_message_open_container(m, 'v', "ay");
213         if (r < 0)
214                 return bus_log_create_error(r);
215 
216         r = sd_bus_message_append_array(m, 'y', buf, n);
217         if (r < 0)
218                 return bus_log_create_error(r);
219 
220         r = sd_bus_message_close_container(m);
221         if (r < 0)
222                 return bus_log_create_error(r);
223 
224         r = sd_bus_message_close_container(m);
225         if (r < 0)
226                 return bus_log_create_error(r);
227 
228         return 1;
229 }
230 
bus_append_parse_sec_rename(sd_bus_message * m,const char * field,const char * eq)231 static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
232         char *n;
233         usec_t t;
234         size_t l;
235         int r;
236 
237         r = parse_sec(eq, &t);
238         if (r < 0)
239                 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
240 
241         l = strlen(field);
242         n = newa(char, l + 2);
243         /* Change suffix Sec → USec */
244         strcpy(mempcpy(n, field, l - 3), "USec");
245 
246         r = sd_bus_message_append(m, "(sv)", n, "t", t);
247         if (r < 0)
248                 return bus_log_create_error(r);
249 
250         return 1;
251 }
252 
bus_append_parse_size(sd_bus_message * m,const char * field,const char * eq,uint64_t base)253 static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
254         uint64_t v;
255         int r;
256 
257         r = parse_size(eq, base, &v);
258         if (r < 0)
259                 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
260 
261         r = sd_bus_message_append(m, "(sv)", field, "t", v);
262         if (r < 0)
263                 return bus_log_create_error(r);
264 
265         return 1;
266 }
267 
bus_append_exec_command(sd_bus_message * m,const char * field,const char * eq)268 static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
269         bool explicit_path = false, done = false;
270         _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
271         _cleanup_free_ char *path = NULL, *upgraded_name = NULL;
272         ExecCommandFlags flags = 0;
273         bool is_ex_prop = endswith(field, "Ex");
274         int r;
275 
276         do {
277                 switch (*eq) {
278 
279                 case '-':
280                         if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
281                                 done = true;
282                         else {
283                                 flags |= EXEC_COMMAND_IGNORE_FAILURE;
284                                 eq++;
285                         }
286                         break;
287 
288                 case '@':
289                         if (explicit_path)
290                                 done = true;
291                         else {
292                                 explicit_path = true;
293                                 eq++;
294                         }
295                         break;
296 
297                 case ':':
298                         if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
299                                 done = true;
300                         else {
301                                 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
302                                 eq++;
303                         }
304                         break;
305 
306                 case '+':
307                         if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
308                                 done = true;
309                         else {
310                                 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
311                                 eq++;
312                         }
313                         break;
314 
315                 case '!':
316                         if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
317                                 done = true;
318                         else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
319                                 flags &= ~EXEC_COMMAND_NO_SETUID;
320                                 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
321                                 eq++;
322                         } else {
323                                 flags |= EXEC_COMMAND_NO_SETUID;
324                                 eq++;
325                         }
326                         break;
327 
328                 default:
329                         done = true;
330                         break;
331                 }
332         } while (!done);
333 
334         if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
335                 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
336                 is_ex_prop = true;
337                 upgraded_name = strjoin(field, "Ex");
338                 if (!upgraded_name)
339                         return log_oom();
340         }
341 
342         if (is_ex_prop) {
343                 r = exec_command_flags_to_strv(flags, &ex_opts);
344                 if (r < 0)
345                         return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
346         }
347 
348         if (explicit_path) {
349                 r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
350                 if (r < 0)
351                         return log_error_errno(r, "Failed to parse path: %m");
352         }
353 
354         r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
355         if (r < 0)
356                 return log_error_errno(r, "Failed to parse command line: %m");
357 
358         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
359         if (r < 0)
360                 return bus_log_create_error(r);
361 
362         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
363         if (r < 0)
364                 return bus_log_create_error(r);
365 
366         r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
367         if (r < 0)
368                 return bus_log_create_error(r);
369 
370         r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
371         if (r < 0)
372                 return bus_log_create_error(r);
373 
374         if (!strv_isempty(l)) {
375 
376                 r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
377                 if (r < 0)
378                         return bus_log_create_error(r);
379 
380                 r = sd_bus_message_append(m, "s", path ?: l[0]);
381                 if (r < 0)
382                         return bus_log_create_error(r);
383 
384                 r = sd_bus_message_append_strv(m, l);
385                 if (r < 0)
386                         return bus_log_create_error(r);
387 
388                 r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
389                 if (r < 0)
390                         return bus_log_create_error(r);
391 
392                 r = sd_bus_message_close_container(m);
393                 if (r < 0)
394                         return bus_log_create_error(r);
395         }
396 
397         r = sd_bus_message_close_container(m);
398         if (r < 0)
399                 return bus_log_create_error(r);
400 
401         r = sd_bus_message_close_container(m);
402         if (r < 0)
403                 return bus_log_create_error(r);
404 
405         r = sd_bus_message_close_container(m);
406         if (r < 0)
407                 return bus_log_create_error(r);
408 
409         return 1;
410 }
411 
bus_append_ip_address_access(sd_bus_message * m,int family,const union in_addr_union * prefix,unsigned char prefixlen)412 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
413         int r;
414 
415         assert(m);
416         assert(prefix);
417 
418         r = sd_bus_message_open_container(m, 'r', "iayu");
419         if (r < 0)
420                 return r;
421 
422         r = sd_bus_message_append(m, "i", family);
423         if (r < 0)
424                 return r;
425 
426         r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
427         if (r < 0)
428                 return r;
429 
430         r = sd_bus_message_append(m, "u", prefixlen);
431         if (r < 0)
432                 return r;
433 
434         return sd_bus_message_close_container(m);
435 }
436 
bus_append_cgroup_property(sd_bus_message * m,const char * field,const char * eq)437 static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
438         int r;
439 
440         if (STR_IN_SET(field, "DevicePolicy",
441                               "Slice",
442                               "ManagedOOMSwap",
443                               "ManagedOOMMemoryPressure",
444                               "ManagedOOMPreference"))
445                 return bus_append_string(m, field, eq);
446 
447         if (STR_IN_SET(field, "ManagedOOMMemoryPressureLimit")) {
448                 r = parse_permyriad(eq);
449                 if (r < 0)
450                         return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
451 
452                 /* Pass around scaled to 2^32-1 == 100% */
453                 r = sd_bus_message_append(m, "(sv)", field, "u", UINT32_SCALE_FROM_PERMYRIAD(r));
454                 if (r < 0)
455                         return bus_log_create_error(r);
456 
457                 return 1;
458         }
459 
460         if (STR_IN_SET(field, "CPUAccounting",
461                               "MemoryAccounting",
462                               "IOAccounting",
463                               "BlockIOAccounting",
464                               "TasksAccounting",
465                               "IPAccounting"))
466                 return bus_append_parse_boolean(m, field, eq);
467 
468         if (STR_IN_SET(field, "CPUWeight",
469                               "StartupCPUWeight",
470                               "IOWeight",
471                               "StartupIOWeight"))
472                 return bus_append_cg_weight_parse(m, field, eq);
473 
474         if (STR_IN_SET(field, "CPUShares",
475                               "StartupCPUShares"))
476                 return bus_append_cg_cpu_shares_parse(m, field, eq);
477 
478         if (STR_IN_SET(field, "AllowedCPUs",
479                               "StartupAllowedCPUs",
480                               "AllowedMemoryNodes",
481                               "StartupAllowedMemoryNodes")) {
482                 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
483                 _cleanup_free_ uint8_t *array = NULL;
484                 size_t allocated;
485 
486                 r = parse_cpu_set(eq, &cpuset);
487                 if (r < 0)
488                         return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
489 
490                 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
491                 if (r < 0)
492                         return log_error_errno(r, "Failed to serialize CPUSet: %m");
493 
494                 return bus_append_byte_array(m, field, array, allocated);
495         }
496 
497         if (STR_IN_SET(field, "BlockIOWeight",
498                               "StartupBlockIOWeight"))
499                 return bus_append_cg_blkio_weight_parse(m, field, eq);
500 
501         if (streq(field, "DisableControllers"))
502                 return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
503 
504         if (streq(field, "Delegate")) {
505                 r = parse_boolean(eq);
506                 if (r < 0)
507                         return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
508 
509                 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
510                 if (r < 0)
511                         return bus_log_create_error(r);
512 
513                 return 1;
514         }
515 
516         if (STR_IN_SET(field, "MemoryMin",
517                               "DefaultMemoryLow",
518                               "DefaultMemoryMin",
519                               "MemoryLow",
520                               "MemoryHigh",
521                               "MemoryMax",
522                               "MemorySwapMax",
523                               "MemoryLimit",
524                               "TasksMax")) {
525 
526                 if (streq(eq, "infinity")) {
527                         r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
528                         if (r < 0)
529                                 return bus_log_create_error(r);
530                         return 1;
531                 } else if (isempty(eq)) {
532                         uint64_t empty_value = STR_IN_SET(field,
533                                                           "DefaultMemoryLow",
534                                                           "DefaultMemoryMin",
535                                                           "MemoryLow",
536                                                           "MemoryMin") ?
537                                                CGROUP_LIMIT_MIN :
538                                                CGROUP_LIMIT_MAX;
539 
540                         r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
541                         if (r < 0)
542                                 return bus_log_create_error(r);
543                         return 1;
544                 }
545 
546                 r = parse_permyriad(eq);
547                 if (r >= 0) {
548                         char *n;
549 
550                         /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
551                          * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
552                          * size can be determined server-side. */
553 
554                         n = strjoina(field, "Scale");
555                         r = sd_bus_message_append(m, "(sv)", n, "u", UINT32_SCALE_FROM_PERMYRIAD(r));
556                         if (r < 0)
557                                 return bus_log_create_error(r);
558 
559                         return 1;
560                 }
561 
562                 if (streq(field, "TasksMax"))
563                         return bus_append_safe_atou64(m, field, eq);
564 
565                 return bus_append_parse_size(m, field, eq, 1024);
566         }
567 
568         if (streq(field, "CPUQuota")) {
569                 if (isempty(eq))
570                         r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
571                 else {
572                         r = parse_permyriad_unbounded(eq);
573                         if (r == 0)
574                                 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
575                                                        "CPU quota too small.");
576                         if (r < 0)
577                                 return log_error_errno(r, "CPU quota '%s' invalid.", eq);
578 
579                         r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 10000U));
580                 }
581 
582                 if (r < 0)
583                         return bus_log_create_error(r);
584 
585                 return 1;
586         }
587 
588         if (streq(field, "CPUQuotaPeriodSec")) {
589                 usec_t u = USEC_INFINITY;
590 
591                 r = parse_sec_def_infinity(eq, &u);
592                 if (r < 0)
593                         return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
594 
595                 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
596                 if (r < 0)
597                         return bus_log_create_error(r);
598 
599                 return 1;
600         }
601 
602         if (streq(field, "DeviceAllow")) {
603                 if (isempty(eq))
604                         r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
605                 else {
606                         const char *path = eq, *rwm = NULL, *e;
607 
608                         e = strchr(eq, ' ');
609                         if (e) {
610                                 path = strndupa_safe(eq, e - eq);
611                                 rwm = e+1;
612                         }
613 
614                         r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
615                 }
616 
617                 if (r < 0)
618                         return bus_log_create_error(r);
619 
620                 return 1;
621         }
622 
623         if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
624                 if (isempty(eq))
625                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
626                 else {
627                         const char *path, *bandwidth, *e;
628                         uint64_t bytes;
629 
630                         e = strchr(eq, ' ');
631                         if (!e)
632                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
633                                                        "Failed to parse %s value %s.",
634                                                        field, eq);
635 
636                         path = strndupa_safe(eq, e - eq);
637                         bandwidth = e+1;
638 
639                         if (streq(bandwidth, "infinity"))
640                                 bytes = CGROUP_LIMIT_MAX;
641                         else {
642                                 r = parse_size(bandwidth, 1000, &bytes);
643                                 if (r < 0)
644                                         return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
645                         }
646 
647                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
648                 }
649 
650                 if (r < 0)
651                         return bus_log_create_error(r);
652 
653                 return 1;
654         }
655 
656         if (STR_IN_SET(field, "IODeviceWeight",
657                               "BlockIODeviceWeight")) {
658                 if (isempty(eq))
659                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
660                 else {
661                         const char *path, *weight, *e;
662                         uint64_t u;
663 
664                         e = strchr(eq, ' ');
665                         if (!e)
666                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
667                                                        "Failed to parse %s value %s.",
668                                                        field, eq);
669 
670                         path = strndupa_safe(eq, e - eq);
671                         weight = e+1;
672 
673                         r = safe_atou64(weight, &u);
674                         if (r < 0)
675                                 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
676 
677                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
678                 }
679 
680                 if (r < 0)
681                         return bus_log_create_error(r);
682 
683                 return 1;
684         }
685 
686         if (streq(field, "IODeviceLatencyTargetSec")) {
687                 const char *field_usec = "IODeviceLatencyTargetUSec";
688 
689                 if (isempty(eq))
690                         r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
691                 else {
692                         const char *path, *target, *e;
693                         usec_t usec;
694 
695                         e = strchr(eq, ' ');
696                         if (!e)
697                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
698                                                        "Failed to parse %s value %s.",
699                                                        field, eq);
700 
701                         path = strndupa_safe(eq, e - eq);
702                         target = e+1;
703 
704                         r = parse_sec(target, &usec);
705                         if (r < 0)
706                                 return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
707 
708                         r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
709                 }
710 
711                 if (r < 0)
712                         return bus_log_create_error(r);
713 
714                 return 1;
715         }
716 
717         if (STR_IN_SET(field, "IPAddressAllow",
718                               "IPAddressDeny")) {
719                 unsigned char prefixlen;
720                 union in_addr_union prefix = {};
721                 int family;
722 
723                 if (isempty(eq)) {
724                         r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
725                         if (r < 0)
726                                 return bus_log_create_error(r);
727 
728                         return 1;
729                 }
730 
731                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
732                 if (r < 0)
733                         return bus_log_create_error(r);
734 
735                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
736                 if (r < 0)
737                         return bus_log_create_error(r);
738 
739                 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
740                 if (r < 0)
741                         return bus_log_create_error(r);
742 
743                 r = sd_bus_message_open_container(m, 'a', "(iayu)");
744                 if (r < 0)
745                         return bus_log_create_error(r);
746 
747                 if (streq(eq, "any")) {
748                         /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
749 
750                         r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
751                         if (r < 0)
752                                 return bus_log_create_error(r);
753 
754                         r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
755                         if (r < 0)
756                                 return bus_log_create_error(r);
757 
758                 } else if (is_localhost(eq)) {
759                         /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
760 
761                         prefix.in.s_addr = htobe32(0x7f000000);
762                         r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
763                         if (r < 0)
764                                 return bus_log_create_error(r);
765 
766                         prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
767                         r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
768                         if (r < 0)
769                                 return r;
770 
771                 } else if (streq(eq, "link-local")) {
772                         /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
773 
774                         prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
775                         r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
776                         if (r < 0)
777                                 return bus_log_create_error(r);
778 
779                         prefix.in6 = (struct in6_addr) {
780                                 .s6_addr32[0] = htobe32(0xfe800000)
781                         };
782                         r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
783                         if (r < 0)
784                                 return bus_log_create_error(r);
785 
786                 } else if (streq(eq, "multicast")) {
787                         /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
788 
789                         prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
790                         r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
791                         if (r < 0)
792                                 return bus_log_create_error(r);
793 
794                         prefix.in6 = (struct in6_addr) {
795                                 .s6_addr32[0] = htobe32(0xff000000)
796                         };
797                         r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
798                         if (r < 0)
799                                 return bus_log_create_error(r);
800 
801                 } else {
802                         for (;;) {
803                                 _cleanup_free_ char *word = NULL;
804 
805                                 r = extract_first_word(&eq, &word, NULL, 0);
806                                 if (r == 0)
807                                         break;
808                                 if (r == -ENOMEM)
809                                         return log_oom();
810                                 if (r < 0)
811                                         return log_error_errno(r, "Failed to parse %s: %s", field, eq);
812 
813                                 r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
814                                 if (r < 0)
815                                         return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
816 
817                                 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
818                                 if (r < 0)
819                                         return bus_log_create_error(r);
820                         }
821                 }
822 
823                 r = sd_bus_message_close_container(m);
824                 if (r < 0)
825                         return bus_log_create_error(r);
826 
827                 r = sd_bus_message_close_container(m);
828                 if (r < 0)
829                         return bus_log_create_error(r);
830 
831                 r = sd_bus_message_close_container(m);
832                 if (r < 0)
833                         return bus_log_create_error(r);
834 
835                 return 1;
836         }
837 
838         if (STR_IN_SET(field, "IPIngressFilterPath",
839                               "IPEgressFilterPath")) {
840                 if (isempty(eq))
841                         r = sd_bus_message_append(m, "(sv)", field, "as", 0);
842                 else
843                         r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
844 
845                 if (r < 0)
846                         return bus_log_create_error(r);
847 
848                 return 1;
849         }
850 
851         if (streq(field, "BPFProgram")) {
852                 if (isempty(eq))
853                         r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
854                 else {
855                         _cleanup_free_ char *word = NULL;
856 
857                         r = extract_first_word(&eq, &word, ":", 0);
858                         if (r == -ENOMEM)
859                                 return log_oom();
860                         if (r < 0)
861                                 return log_error_errno(r, "Failed to parse %s: %m", field);
862 
863                         r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq);
864                 }
865                 if (r < 0)
866                         return bus_log_create_error(r);
867 
868                 return 1;
869         }
870 
871         if (STR_IN_SET(field, "SocketBindAllow",
872                               "SocketBindDeny")) {
873                 if (isempty(eq))
874                         r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 0);
875                 else {
876                         int32_t family, ip_protocol;
877                         uint16_t nr_ports, port_min;
878 
879                         r = parse_socket_bind_item(eq, &family, &ip_protocol, &nr_ports, &port_min);
880                         if (r == -ENOMEM)
881                                 return log_oom();
882                         if (r < 0)
883                                 return log_error_errno(r, "Failed to parse %s", field);
884 
885                         r = sd_bus_message_append(
886                                         m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, nr_ports, port_min);
887                 }
888                 if (r < 0)
889                         return bus_log_create_error(r);
890 
891                 return 1;
892         }
893 
894         return 0;
895 }
896 
bus_append_automount_property(sd_bus_message * m,const char * field,const char * eq)897 static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
898         if (STR_IN_SET(field, "Where",
899                               "ExtraOptions"))
900                 return bus_append_string(m, field, eq);
901 
902         if (streq(field, "DirectoryMode"))
903                 return bus_append_parse_mode(m, field, eq);
904 
905         if (streq(field, "TimeoutIdleSec"))
906                 return bus_append_parse_sec_rename(m, field, eq);
907 
908         return 0;
909 }
910 
bus_append_execute_property(sd_bus_message * m,const char * field,const char * eq)911 static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
912         const char *suffix;
913         int r;
914 
915         if (STR_IN_SET(field, "User",
916                               "Group",
917                               "UtmpIdentifier",
918                               "UtmpMode",
919                               "PAMName",
920                               "TTYPath",
921                               "WorkingDirectory",
922                               "RootDirectory",
923                               "SyslogIdentifier",
924                               "ProtectSystem",
925                               "ProtectHome",
926                               "SELinuxContext",
927                               "RootImage",
928                               "RootVerity",
929                               "RuntimeDirectoryPreserve",
930                               "Personality",
931                               "KeyringMode",
932                               "ProtectProc",
933                               "ProcSubset",
934                               "NetworkNamespacePath",
935                               "IPCNamespacePath",
936                               "LogNamespace"))
937                 return bus_append_string(m, field, eq);
938 
939         if (STR_IN_SET(field, "IgnoreSIGPIPE",
940                               "TTYVHangup",
941                               "TTYReset",
942                               "TTYVTDisallocate",
943                               "PrivateTmp",
944                               "PrivateDevices",
945                               "PrivateNetwork",
946                               "PrivateUsers",
947                               "PrivateMounts",
948                               "PrivateIPC",
949                               "NoNewPrivileges",
950                               "SyslogLevelPrefix",
951                               "MemoryDenyWriteExecute",
952                               "RestrictRealtime",
953                               "DynamicUser",
954                               "RemoveIPC",
955                               "ProtectKernelTunables",
956                               "ProtectKernelModules",
957                               "ProtectKernelLogs",
958                               "ProtectClock",
959                               "ProtectControlGroups",
960                               "MountAPIVFS",
961                               "CPUSchedulingResetOnFork",
962                               "LockPersonality",
963                               "ProtectHostname",
964                               "RestrictSUIDSGID"))
965                 return bus_append_parse_boolean(m, field, eq);
966 
967         if (STR_IN_SET(field, "ReadWriteDirectories",
968                               "ReadOnlyDirectories",
969                               "InaccessibleDirectories",
970                               "ReadWritePaths",
971                               "ReadOnlyPaths",
972                               "InaccessiblePaths",
973                               "ExecPaths",
974                               "NoExecPaths",
975                               "ExecSearchPath",
976                               "ExtensionDirectories",
977                               "ConfigurationDirectory",
978                               "SupplementaryGroups",
979                               "SystemCallArchitectures"))
980                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
981 
982         if (STR_IN_SET(field, "SyslogLevel",
983                               "LogLevelMax"))
984                 return bus_append_log_level_from_string(m, field, eq);
985 
986         if (streq(field, "SyslogFacility"))
987                 return bus_append_log_facility_unshifted_from_string(m, field, eq);
988 
989         if (streq(field, "SecureBits"))
990                 return bus_append_secure_bits_from_string(m, field, eq);
991 
992         if (streq(field, "CPUSchedulingPolicy"))
993                 return bus_append_sched_policy_from_string(m, field, eq);
994 
995         if (STR_IN_SET(field, "CPUSchedulingPriority",
996                               "OOMScoreAdjust"))
997                 return bus_append_safe_atoi(m, field, eq);
998 
999         if (streq(field, "CoredumpFilter"))
1000                 return bus_append_coredump_filter_mask_from_string(m, field, eq);
1001 
1002         if (streq(field, "Nice"))
1003                 return bus_append_parse_nice(m, field, eq);
1004 
1005         if (streq(field, "SystemCallErrorNumber"))
1006                 return bus_append_seccomp_parse_errno_or_action(m, field, eq);
1007 
1008         if (streq(field, "IOSchedulingClass"))
1009                 return bus_append_ioprio_class_from_string(m, field, eq);
1010 
1011         if (streq(field, "IOSchedulingPriority"))
1012                 return bus_append_ioprio_parse_priority(m, field, eq);
1013 
1014         if (STR_IN_SET(field, "RuntimeDirectoryMode",
1015                               "StateDirectoryMode",
1016                               "CacheDirectoryMode",
1017                               "LogsDirectoryMode",
1018                               "ConfigurationDirectoryMode",
1019                               "UMask"))
1020                 return bus_append_parse_mode(m, field, eq);
1021 
1022         if (streq(field, "TimerSlackNSec"))
1023                 return bus_append_parse_nsec(m, field, eq);
1024 
1025         if (streq(field, "LogRateLimitIntervalSec"))
1026                 return bus_append_parse_sec_rename(m, field, eq);
1027 
1028         if (STR_IN_SET(field, "LogRateLimitBurst",
1029                               "TTYRows",
1030                               "TTYColumns"))
1031                 return bus_append_safe_atou(m, field, eq);
1032 
1033         if (streq(field, "MountFlags"))
1034                 return bus_append_mount_propagation_flags_from_string(m, field, eq);
1035 
1036         if (STR_IN_SET(field, "Environment",
1037                               "UnsetEnvironment",
1038                               "PassEnvironment"))
1039                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
1040 
1041         if (streq(field, "EnvironmentFile")) {
1042                 if (isempty(eq))
1043                         r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
1044                 else
1045                         r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
1046                                                   eq[0] == '-' ? eq + 1 : eq,
1047                                                   eq[0] == '-');
1048                 if (r < 0)
1049                         return bus_log_create_error(r);
1050 
1051                 return 1;
1052         }
1053 
1054         if (STR_IN_SET(field, "SetCredential", "SetCredentialEncrypted")) {
1055                 r = sd_bus_message_open_container(m, 'r', "sv");
1056                 if (r < 0)
1057                         return bus_log_create_error(r);
1058 
1059                 r = sd_bus_message_append_basic(m, 's', field);
1060                 if (r < 0)
1061                         return bus_log_create_error(r);
1062 
1063                 r = sd_bus_message_open_container(m, 'v', "a(say)");
1064                 if (r < 0)
1065                         return bus_log_create_error(r);
1066 
1067                 if (isempty(eq))
1068                         r = sd_bus_message_append(m, "a(say)", 0);
1069                 else {
1070                         _cleanup_free_ char *word = NULL;
1071                         const char *p = eq;
1072 
1073                         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1074                         if (r == -ENOMEM)
1075                                 return log_oom();
1076                         if (r < 0)
1077                                 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1078                         if (r == 0 || !p)
1079                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1080 
1081                         r = sd_bus_message_open_container(m, 'a', "(say)");
1082                         if (r < 0)
1083                                 return bus_log_create_error(r);
1084 
1085                         r = sd_bus_message_open_container(m, 'r', "say");
1086                         if (r < 0)
1087                                 return bus_log_create_error(r);
1088 
1089                         r = sd_bus_message_append(m, "s", word);
1090                         if (r < 0)
1091                                 return bus_log_create_error(r);
1092 
1093                         if (streq(field, "SetCredentialEncrypted")) {
1094                                 _cleanup_free_ void *decoded = NULL;
1095                                 size_t decoded_size;
1096 
1097                                 r = unbase64mem(p, SIZE_MAX, &decoded, &decoded_size);
1098                                 if (r < 0)
1099                                         return log_error_errno(r, "Failed to base64 decode encrypted credential: %m");
1100 
1101                                 r = sd_bus_message_append_array(m, 'y', decoded, decoded_size);
1102                         } else {
1103                                 _cleanup_free_ char *unescaped = NULL;
1104                                 ssize_t l;
1105 
1106                                 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
1107                                 if (l < 0)
1108                                         return log_error_errno(l, "Failed to unescape %s= value: %s", field, p);
1109 
1110                                 r = sd_bus_message_append_array(m, 'y', unescaped, l);
1111                         }
1112                         if (r < 0)
1113                                 return bus_log_create_error(r);
1114 
1115                         r = sd_bus_message_close_container(m);
1116                         if (r < 0)
1117                                 return bus_log_create_error(r);
1118 
1119                         r = sd_bus_message_close_container(m);
1120                 }
1121                 if (r < 0)
1122                         return bus_log_create_error(r);
1123 
1124                 r = sd_bus_message_close_container(m);
1125                 if (r < 0)
1126                         return bus_log_create_error(r);
1127 
1128                 r = sd_bus_message_close_container(m);
1129                 if (r < 0)
1130                         return bus_log_create_error(r);
1131 
1132                 return 1;
1133         }
1134 
1135         if (STR_IN_SET(field, "LoadCredential", "LoadCredentialEncrypted")) {
1136                 r = sd_bus_message_open_container(m, 'r', "sv");
1137                 if (r < 0)
1138                         return bus_log_create_error(r);
1139 
1140                 r = sd_bus_message_append_basic(m, 's', field);
1141                 if (r < 0)
1142                         return bus_log_create_error(r);
1143 
1144                 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1145                 if (r < 0)
1146                         return bus_log_create_error(r);
1147 
1148                 if (isempty(eq))
1149                         r = sd_bus_message_append(m, "a(ss)", 0);
1150                 else {
1151                         _cleanup_free_ char *word = NULL;
1152                         const char *p = eq;
1153 
1154                         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1155                         if (r == -ENOMEM)
1156                                 return log_oom();
1157                         if (r < 0)
1158                                 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1159                         if (r == 0)
1160                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1161 
1162                         if (isempty(p)) /* If only one field is specified, then this means "inherit from above" */
1163                                 p = eq;
1164 
1165                         r = sd_bus_message_append(m, "a(ss)", 1, word, p);
1166                 }
1167                 if (r < 0)
1168                         return bus_log_create_error(r);
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                 return 1;
1179         }
1180 
1181         if (streq(field, "LogExtraFields")) {
1182                 r = sd_bus_message_open_container(m, 'r', "sv");
1183                 if (r < 0)
1184                         return bus_log_create_error(r);
1185 
1186                 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
1187                 if (r < 0)
1188                         return bus_log_create_error(r);
1189 
1190                 r = sd_bus_message_open_container(m, 'v', "aay");
1191                 if (r < 0)
1192                         return bus_log_create_error(r);
1193 
1194                 r = sd_bus_message_open_container(m, 'a', "ay");
1195                 if (r < 0)
1196                         return bus_log_create_error(r);
1197 
1198                 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
1199                 if (r < 0)
1200                         return bus_log_create_error(r);
1201 
1202                 r = sd_bus_message_close_container(m);
1203                 if (r < 0)
1204                         return bus_log_create_error(r);
1205 
1206                 r = sd_bus_message_close_container(m);
1207                 if (r < 0)
1208                         return bus_log_create_error(r);
1209 
1210                 r = sd_bus_message_close_container(m);
1211                 if (r < 0)
1212                         return bus_log_create_error(r);
1213 
1214                 return 1;
1215         }
1216 
1217         if (STR_IN_SET(field, "StandardInput",
1218                               "StandardOutput",
1219                               "StandardError")) {
1220                 const char *n, *appended;
1221 
1222                 if ((n = startswith(eq, "fd:"))) {
1223                         appended = strjoina(field, "FileDescriptorName");
1224                         r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1225                 } else if ((n = startswith(eq, "file:"))) {
1226                         appended = strjoina(field, "File");
1227                         r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1228                 } else if ((n = startswith(eq, "append:"))) {
1229                         appended = strjoina(field, "FileToAppend");
1230                         r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1231                 } else if ((n = startswith(eq, "truncate:"))) {
1232                         appended = strjoina(field, "FileToTruncate");
1233                         r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1234                 } else
1235                         r = sd_bus_message_append(m, "(sv)", field, "s", eq);
1236                 if (r < 0)
1237                         return bus_log_create_error(r);
1238 
1239                 return 1;
1240         }
1241 
1242         if (streq(field, "StandardInputText")) {
1243                 _cleanup_free_ char *unescaped = NULL;
1244                 ssize_t l;
1245 
1246                 l = cunescape(eq, 0, &unescaped);
1247                 if (l < 0)
1248                         return log_error_errno(l, "Failed to unescape text '%s': %m", eq);
1249 
1250                 if (!strextend(&unescaped, "\n"))
1251                         return log_oom();
1252 
1253                 /* Note that we don't expand specifiers here, but that should be OK, as this is a
1254                  * programmatic interface anyway */
1255 
1256                 return bus_append_byte_array(m, field, unescaped, l + 1);
1257         }
1258 
1259         if (streq(field, "StandardInputData")) {
1260                 _cleanup_free_ void *decoded = NULL;
1261                 size_t sz;
1262 
1263                 r = unbase64mem(eq, SIZE_MAX, &decoded, &sz);
1264                 if (r < 0)
1265                         return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
1266 
1267                 return bus_append_byte_array(m, field, decoded, sz);
1268         }
1269 
1270         if ((suffix = startswith(field, "Limit"))) {
1271                 int rl;
1272 
1273                 rl = rlimit_from_string(suffix);
1274                 if (rl >= 0) {
1275                         const char *sn;
1276                         struct rlimit l;
1277 
1278                         r = rlimit_parse(rl, eq, &l);
1279                         if (r < 0)
1280                                 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
1281 
1282                         r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
1283                         if (r < 0)
1284                                 return bus_log_create_error(r);
1285 
1286                         sn = strjoina(field, "Soft");
1287                         r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
1288                         if (r < 0)
1289                                 return bus_log_create_error(r);
1290 
1291                         return 1;
1292                 }
1293         }
1294 
1295         if (STR_IN_SET(field, "AppArmorProfile",
1296                               "SmackProcessLabel")) {
1297                 int ignore = 0;
1298                 const char *s = eq;
1299 
1300                 if (eq[0] == '-') {
1301                         ignore = 1;
1302                         s = eq + 1;
1303                 }
1304 
1305                 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
1306                 if (r < 0)
1307                         return bus_log_create_error(r);
1308 
1309                 return 1;
1310         }
1311 
1312         if (STR_IN_SET(field, "CapabilityBoundingSet",
1313                               "AmbientCapabilities")) {
1314                 uint64_t sum = 0;
1315                 bool invert = false;
1316                 const char *p = eq;
1317 
1318                 if (*p == '~') {
1319                         invert = true;
1320                         p++;
1321                 }
1322 
1323                 r = capability_set_from_string(p, &sum);
1324                 if (r < 0)
1325                         return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
1326 
1327                 sum = invert ? ~sum : sum;
1328 
1329                 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
1330                 if (r < 0)
1331                         return bus_log_create_error(r);
1332 
1333                 return 1;
1334         }
1335 
1336         if (streq(field, "CPUAffinity")) {
1337                 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
1338                 _cleanup_free_ uint8_t *array = NULL;
1339                 size_t allocated;
1340 
1341                 if (eq && streq(eq, "numa")) {
1342                         r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
1343                         if (r < 0)
1344                                 return bus_log_create_error(r);
1345                         return r;
1346                 }
1347 
1348                 r = parse_cpu_set(eq, &cpuset);
1349                 if (r < 0)
1350                         return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1351 
1352                 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
1353                 if (r < 0)
1354                         return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
1355 
1356                 return bus_append_byte_array(m, field, array, allocated);
1357         }
1358 
1359         if (streq(field, "NUMAPolicy")) {
1360                 r = mpol_from_string(eq);
1361                 if (r < 0)
1362                         return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1363 
1364                 r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
1365                 if (r < 0)
1366                         return bus_log_create_error(r);
1367 
1368                 return 1;
1369         }
1370 
1371         if (streq(field, "NUMAMask")) {
1372                 _cleanup_(cpu_set_reset) CPUSet nodes = {};
1373                 _cleanup_free_ uint8_t *array = NULL;
1374                 size_t allocated;
1375 
1376                 if (eq && streq(eq, "all")) {
1377                         r = numa_mask_add_all(&nodes);
1378                         if (r < 0)
1379                                 return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
1380                 } else {
1381                         r = parse_cpu_set(eq, &nodes);
1382                         if (r < 0)
1383                                 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1384                 }
1385 
1386                 r = cpu_set_to_dbus(&nodes, &array, &allocated);
1387                 if (r < 0)
1388                         return log_error_errno(r, "Failed to serialize NUMAMask: %m");
1389 
1390                 return bus_append_byte_array(m, field, array, allocated);
1391         }
1392 
1393         if (STR_IN_SET(field, "RestrictAddressFamilies",
1394                               "RestrictFileSystems",
1395                               "SystemCallFilter",
1396                               "SystemCallLog",
1397                               "RestrictNetworkInterfaces")) {
1398                 int allow_list = 1;
1399                 const char *p = eq;
1400 
1401                 if (*p == '~') {
1402                         allow_list = 0;
1403                         p++;
1404                 }
1405 
1406                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1407                 if (r < 0)
1408                         return bus_log_create_error(r);
1409 
1410                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1411                 if (r < 0)
1412                         return bus_log_create_error(r);
1413 
1414                 r = sd_bus_message_open_container(m, 'v', "(bas)");
1415                 if (r < 0)
1416                         return bus_log_create_error(r);
1417 
1418                 r = sd_bus_message_open_container(m, 'r', "bas");
1419                 if (r < 0)
1420                         return bus_log_create_error(r);
1421 
1422                 r = sd_bus_message_append_basic(m, 'b', &allow_list);
1423                 if (r < 0)
1424                         return bus_log_create_error(r);
1425 
1426                 r = sd_bus_message_open_container(m, 'a', "s");
1427                 if (r < 0)
1428                         return bus_log_create_error(r);
1429 
1430                 for (;;) {
1431                         _cleanup_free_ char *word = NULL;
1432 
1433                         r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1434                         if (r == 0)
1435                                 break;
1436                         if (r == -ENOMEM)
1437                                 return log_oom();
1438                         if (r < 0)
1439                                 return log_error_errno(r, "Invalid syntax: %s", eq);
1440 
1441                         r = sd_bus_message_append_basic(m, 's', word);
1442                         if (r < 0)
1443                                 return bus_log_create_error(r);
1444                 }
1445 
1446                 r = sd_bus_message_close_container(m);
1447                 if (r < 0)
1448                         return bus_log_create_error(r);
1449 
1450                 r = sd_bus_message_close_container(m);
1451                 if (r < 0)
1452                         return bus_log_create_error(r);
1453 
1454                 r = sd_bus_message_close_container(m);
1455                 if (r < 0)
1456                         return bus_log_create_error(r);
1457 
1458                 r = sd_bus_message_close_container(m);
1459                 if (r < 0)
1460                         return bus_log_create_error(r);
1461 
1462                 return 1;
1463         }
1464 
1465         if (streq(field, "RestrictNamespaces")) {
1466                 bool invert = false;
1467                 unsigned long flags;
1468 
1469                 r = parse_boolean(eq);
1470                 if (r > 0)
1471                         flags = 0;
1472                 else if (r == 0)
1473                         flags = NAMESPACE_FLAGS_ALL;
1474                 else {
1475                         if (eq[0] == '~') {
1476                                 invert = true;
1477                                 eq++;
1478                         }
1479 
1480                         r = namespace_flags_from_string(eq, &flags);
1481                         if (r < 0)
1482                                 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1483                 }
1484 
1485                 if (invert)
1486                         flags = (~flags) & NAMESPACE_FLAGS_ALL;
1487 
1488                 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1489                 if (r < 0)
1490                         return bus_log_create_error(r);
1491 
1492                 return 1;
1493         }
1494 
1495         if (STR_IN_SET(field, "BindPaths",
1496                               "BindReadOnlyPaths")) {
1497                 const char *p = eq;
1498 
1499                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1500                 if (r < 0)
1501                         return bus_log_create_error(r);
1502 
1503                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1504                 if (r < 0)
1505                         return bus_log_create_error(r);
1506 
1507                 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1508                 if (r < 0)
1509                         return bus_log_create_error(r);
1510 
1511                 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1512                 if (r < 0)
1513                         return bus_log_create_error(r);
1514 
1515                 for (;;) {
1516                         _cleanup_free_ char *source = NULL, *destination = NULL;
1517                         char *s = NULL, *d = NULL;
1518                         bool ignore_enoent = false;
1519                         uint64_t flags = MS_REC;
1520 
1521                         r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1522                         if (r < 0)
1523                                 return log_error_errno(r, "Failed to parse argument: %m");
1524                         if (r == 0)
1525                                 break;
1526 
1527                         s = source;
1528                         if (s[0] == '-') {
1529                                 ignore_enoent = true;
1530                                 s++;
1531                         }
1532 
1533                         if (p && p[-1] == ':') {
1534                                 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1535                                 if (r < 0)
1536                                         return log_error_errno(r, "Failed to parse argument: %m");
1537                                 if (r == 0)
1538                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1539                                                                "Missing argument after ':': %s",
1540                                                                eq);
1541 
1542                                 d = destination;
1543 
1544                                 if (p && p[-1] == ':') {
1545                                         _cleanup_free_ char *options = NULL;
1546 
1547                                         r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
1548                                         if (r < 0)
1549                                                 return log_error_errno(r, "Failed to parse argument: %m");
1550 
1551                                         if (isempty(options) || streq(options, "rbind"))
1552                                                 flags = MS_REC;
1553                                         else if (streq(options, "norbind"))
1554                                                 flags = 0;
1555                                         else
1556                                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1557                                                                        "Unknown options: %s",
1558                                                                        eq);
1559                                 }
1560                         } else
1561                                 d = s;
1562 
1563                         r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1564                         if (r < 0)
1565                                 return bus_log_create_error(r);
1566                 }
1567 
1568                 r = sd_bus_message_close_container(m);
1569                 if (r < 0)
1570                         return bus_log_create_error(r);
1571 
1572                 r = sd_bus_message_close_container(m);
1573                 if (r < 0)
1574                         return bus_log_create_error(r);
1575 
1576                 r = sd_bus_message_close_container(m);
1577                 if (r < 0)
1578                         return bus_log_create_error(r);
1579 
1580                 return 1;
1581         }
1582 
1583         if (streq(field, "TemporaryFileSystem")) {
1584                 const char *p = eq;
1585 
1586                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1587                 if (r < 0)
1588                         return bus_log_create_error(r);
1589 
1590                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1591                 if (r < 0)
1592                         return bus_log_create_error(r);
1593 
1594                 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1595                 if (r < 0)
1596                         return bus_log_create_error(r);
1597 
1598                 r = sd_bus_message_open_container(m, 'a', "(ss)");
1599                 if (r < 0)
1600                         return bus_log_create_error(r);
1601 
1602                 for (;;) {
1603                         _cleanup_free_ char *word = NULL, *path = NULL;
1604                         const char *w;
1605 
1606                         r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1607                         if (r < 0)
1608                                 return log_error_errno(r, "Failed to parse argument: %m");
1609                         if (r == 0)
1610                                 break;
1611 
1612                         w = word;
1613                         r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1614                         if (r < 0)
1615                                 return log_error_errno(r, "Failed to parse argument: %m");
1616                         if (r == 0)
1617                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1618                                                        "Failed to parse argument: %s",
1619                                                        p);
1620 
1621                         r = sd_bus_message_append(m, "(ss)", path, w);
1622                         if (r < 0)
1623                                 return bus_log_create_error(r);
1624                 }
1625 
1626                 r = sd_bus_message_close_container(m);
1627                 if (r < 0)
1628                         return bus_log_create_error(r);
1629 
1630                 r = sd_bus_message_close_container(m);
1631                 if (r < 0)
1632                         return bus_log_create_error(r);
1633 
1634                 r = sd_bus_message_close_container(m);
1635                 if (r < 0)
1636                         return bus_log_create_error(r);
1637 
1638                 return 1;
1639         }
1640 
1641         if (streq(field, "RootHash")) {
1642                 _cleanup_free_ void *roothash_decoded = NULL;
1643                 size_t roothash_decoded_size = 0;
1644 
1645                 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1646                 if (path_is_absolute(eq))
1647                         return bus_append_string(m, "RootHashPath", eq);
1648 
1649                 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1650                 r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
1651                 if (r < 0)
1652                         return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
1653                 if (roothash_decoded_size < sizeof(sd_id128_t))
1654                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
1655 
1656                 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
1657         }
1658 
1659         if (streq(field, "RootHashSignature")) {
1660                 _cleanup_free_ void *roothash_sig_decoded = NULL;
1661                 char *value;
1662                 size_t roothash_sig_decoded_size = 0;
1663 
1664                 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1665                 if (path_is_absolute(eq))
1666                         return bus_append_string(m, "RootHashSignaturePath", eq);
1667 
1668                 if (!(value = startswith(eq, "base64:")))
1669                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
1670 
1671                 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1672                 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1673                 if (r < 0)
1674                         return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
1675 
1676                 return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
1677         }
1678 
1679         if (streq(field, "RootImageOptions")) {
1680                 _cleanup_strv_free_ char **l = NULL;
1681                 const char *p = eq;
1682 
1683                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1684                 if (r < 0)
1685                         return bus_log_create_error(r);
1686 
1687                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1688                 if (r < 0)
1689                         return bus_log_create_error(r);
1690 
1691                 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1692                 if (r < 0)
1693                         return bus_log_create_error(r);
1694 
1695                 r = sd_bus_message_open_container(m, 'a', "(ss)");
1696                 if (r < 0)
1697                         return bus_log_create_error(r);
1698 
1699                 r = strv_split_colon_pairs(&l, p);
1700                 if (r < 0)
1701                         return log_error_errno(r, "Failed to parse argument: %m");
1702 
1703                 STRV_FOREACH_PAIR(first, second, l) {
1704                         r = sd_bus_message_append(m, "(ss)",
1705                                                   !isempty(*second) ? *first : "root",
1706                                                   !isempty(*second) ? *second : *first);
1707                         if (r < 0)
1708                                 return bus_log_create_error(r);
1709                 }
1710 
1711                 r = sd_bus_message_close_container(m);
1712                 if (r < 0)
1713                         return bus_log_create_error(r);
1714 
1715                 r = sd_bus_message_close_container(m);
1716                 if (r < 0)
1717                         return bus_log_create_error(r);
1718 
1719                 r = sd_bus_message_close_container(m);
1720                 if (r < 0)
1721                         return bus_log_create_error(r);
1722 
1723                 return 1;
1724         }
1725 
1726         if (streq(field, "MountImages")) {
1727                 const char *p = eq;
1728 
1729                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1730                 if (r < 0)
1731                         return bus_log_create_error(r);
1732 
1733                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1734                 if (r < 0)
1735                         return bus_log_create_error(r);
1736 
1737                 r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
1738                 if (r < 0)
1739                         return bus_log_create_error(r);
1740 
1741                 r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
1742                 if (r < 0)
1743                         return bus_log_create_error(r);
1744 
1745                 for (;;) {
1746                         _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
1747                         const char *q = NULL, *source = NULL;
1748                         bool permissive = false;
1749 
1750                         r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1751                         if (r < 0)
1752                                 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1753                         if (r == 0)
1754                                 break;
1755 
1756                         q = tuple;
1757                         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
1758                         if (r < 0)
1759                                 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1760                         if (r == 0)
1761                                 continue;
1762 
1763                         source = first;
1764                         if (source[0] == '-') {
1765                                 permissive = true;
1766                                 source++;
1767                         }
1768 
1769                         if (isempty(second))
1770                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1771                                                         "Missing argument after ':': %s",
1772                                                         eq);
1773 
1774                         r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
1775                         if (r < 0)
1776                                 return bus_log_create_error(r);
1777 
1778                         r = sd_bus_message_append(m, "ssb", source, second, permissive);
1779                         if (r < 0)
1780                                 return bus_log_create_error(r);
1781 
1782                         r = sd_bus_message_open_container(m, 'a', "(ss)");
1783                         if (r < 0)
1784                                 return bus_log_create_error(r);
1785 
1786                         for (;;) {
1787                                 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1788 
1789                                 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
1790                                 if (r < 0)
1791                                         return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1792                                 if (r == 0)
1793                                         break;
1794                                 /* Single set of options, applying to the root partition/single filesystem */
1795                                 if (r == 1) {
1796                                         r = sd_bus_message_append(m, "(ss)", "root", partition);
1797                                         if (r < 0)
1798                                                 return bus_log_create_error(r);
1799 
1800                                         break;
1801                                 }
1802 
1803                                 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1804                                 if (r < 0)
1805                                         return bus_log_create_error(r);
1806                         }
1807 
1808                         r = sd_bus_message_close_container(m);
1809                         if (r < 0)
1810                                 return bus_log_create_error(r);
1811 
1812                         r = sd_bus_message_close_container(m);
1813                         if (r < 0)
1814                                 return bus_log_create_error(r);
1815                 }
1816 
1817                 r = sd_bus_message_close_container(m);
1818                 if (r < 0)
1819                         return bus_log_create_error(r);
1820 
1821                 r = sd_bus_message_close_container(m);
1822                 if (r < 0)
1823                         return bus_log_create_error(r);
1824 
1825                 r = sd_bus_message_close_container(m);
1826                 if (r < 0)
1827                         return bus_log_create_error(r);
1828 
1829                 return 1;
1830         }
1831 
1832         if (streq(field, "ExtensionImages")) {
1833                 const char *p = eq;
1834 
1835                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1836                 if (r < 0)
1837                         return bus_log_create_error(r);
1838 
1839                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1840                 if (r < 0)
1841                         return bus_log_create_error(r);
1842 
1843                 r = sd_bus_message_open_container(m, 'v', "a(sba(ss))");
1844                 if (r < 0)
1845                         return bus_log_create_error(r);
1846 
1847                 r = sd_bus_message_open_container(m, 'a', "(sba(ss))");
1848                 if (r < 0)
1849                         return bus_log_create_error(r);
1850 
1851                 for (;;) {
1852                         _cleanup_free_ char *source = NULL, *tuple = NULL;
1853                         const char *q = NULL, *s = NULL;
1854                         bool permissive = false;
1855 
1856                         r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1857                         if (r < 0)
1858                                 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1859                         if (r == 0)
1860                                 break;
1861 
1862                         q = tuple;
1863                         r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
1864                         if (r < 0)
1865                                 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1866                         if (r == 0)
1867                                 continue;
1868 
1869                         s = source;
1870                         if (s[0] == '-') {
1871                                 permissive = true;
1872                                 s++;
1873                         }
1874 
1875                         r = sd_bus_message_open_container(m, 'r', "sba(ss)");
1876                         if (r < 0)
1877                                 return bus_log_create_error(r);
1878 
1879                         r = sd_bus_message_append(m, "sb", s, permissive);
1880                         if (r < 0)
1881                                 return bus_log_create_error(r);
1882 
1883                         r = sd_bus_message_open_container(m, 'a', "(ss)");
1884                         if (r < 0)
1885                                 return bus_log_create_error(r);
1886 
1887                         for (;;) {
1888                                 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1889 
1890                                 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
1891                                 if (r < 0)
1892                                         return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1893                                 if (r == 0)
1894                                         break;
1895                                 /* Single set of options, applying to the root partition/single filesystem */
1896                                 if (r == 1) {
1897                                         r = sd_bus_message_append(m, "(ss)", "root", partition);
1898                                         if (r < 0)
1899                                                 return bus_log_create_error(r);
1900 
1901                                         break;
1902                                 }
1903 
1904                                 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1905                                 if (r < 0)
1906                                         return bus_log_create_error(r);
1907                         }
1908 
1909                         r = sd_bus_message_close_container(m);
1910                         if (r < 0)
1911                                 return bus_log_create_error(r);
1912 
1913                         r = sd_bus_message_close_container(m);
1914                         if (r < 0)
1915                                 return bus_log_create_error(r);
1916                 }
1917 
1918                 r = sd_bus_message_close_container(m);
1919                 if (r < 0)
1920                         return bus_log_create_error(r);
1921 
1922                 r = sd_bus_message_close_container(m);
1923                 if (r < 0)
1924                         return bus_log_create_error(r);
1925 
1926                 r = sd_bus_message_close_container(m);
1927                 if (r < 0)
1928                         return bus_log_create_error(r);
1929 
1930                 return 1;
1931         }
1932 
1933         if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) {
1934                 _cleanup_strv_free_ char **symlinks = NULL, **sources = NULL;
1935                 const char *p = eq;
1936 
1937                 /* Adding new directories is supported from both *DirectorySymlink methods and the
1938                  * older ones, so first parse the input, and if we are given a new-style src:dst
1939                  * tuple use the new method, else use the old one. */
1940 
1941                 for (;;) {
1942                         _cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL;
1943 
1944                         r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE);
1945                         if (r < 0)
1946                                 return log_error_errno(r, "Failed to parse argument: %m");
1947                         if (r == 0)
1948                                 break;
1949 
1950                         const char *t = tuple;
1951                         r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
1952                         if (r <= 0)
1953                                 return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m");
1954 
1955                         path_simplify(source);
1956 
1957                         if (isempty(destination)) {
1958                                 r = strv_consume(&sources, TAKE_PTR(source));
1959                                 if (r < 0)
1960                                         return bus_log_create_error(r);
1961                         } else {
1962                                 path_simplify(destination);
1963 
1964                                 r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(destination));
1965                                 if (r < 0)
1966                                         return log_oom();
1967                         }
1968                 }
1969 
1970                 if (!strv_isempty(sources)) {
1971                         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1972                         if (r < 0)
1973                                 return bus_log_create_error(r);
1974 
1975                         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1976                         if (r < 0)
1977                                 return bus_log_create_error(r);
1978 
1979                         r = sd_bus_message_open_container(m, 'v', "as");
1980                         if (r < 0)
1981                                 return bus_log_create_error(r);
1982 
1983                         r = sd_bus_message_append_strv(m, sources);
1984                         if (r < 0)
1985                                 return bus_log_create_error(r);
1986 
1987                         r = sd_bus_message_close_container(m);
1988                         if (r < 0)
1989                                 return bus_log_create_error(r);
1990 
1991                         r = sd_bus_message_close_container(m);
1992                         if (r < 0)
1993                                 return bus_log_create_error(r);
1994                 }
1995 
1996                 /* For State and Runtime directories we support an optional destination parameter, which
1997                  * will be used to create a symlink to the source. But it is new so we cannot change the
1998                  * old DBUS signatures, so append a new message type. */
1999                 if (!strv_isempty(symlinks)) {
2000                         const char *symlink_field;
2001 
2002                         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2003                         if (r < 0)
2004                                 return bus_log_create_error(r);
2005 
2006                         if (streq(field, "StateDirectory"))
2007                                 symlink_field = "StateDirectorySymlink";
2008                         else if (streq(field, "RuntimeDirectory"))
2009                                 symlink_field = "RuntimeDirectorySymlink";
2010                         else if (streq(field, "CacheDirectory"))
2011                                 symlink_field = "CacheDirectorySymlink";
2012                         else if (streq(field, "LogsDirectory"))
2013                                 symlink_field = "LogsDirectorySymlink";
2014                         else
2015                                 assert_not_reached();
2016 
2017                         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field);
2018                         if (r < 0)
2019                                 return bus_log_create_error(r);
2020 
2021                         r = sd_bus_message_open_container(m, 'v', "a(sst)");
2022                         if (r < 0)
2023                                 return bus_log_create_error(r);
2024 
2025                         r = sd_bus_message_open_container(m, 'a', "(sst)");
2026                         if (r < 0)
2027                                 return bus_log_create_error(r);
2028 
2029                         STRV_FOREACH_PAIR(source, destination, symlinks) {
2030                                 r = sd_bus_message_append(m, "(sst)", *source, *destination, 0);
2031                                 if (r < 0)
2032                                         return bus_log_create_error(r);
2033                         }
2034 
2035                         r = sd_bus_message_close_container(m);
2036                         if (r < 0)
2037                                 return bus_log_create_error(r);
2038 
2039                         r = sd_bus_message_close_container(m);
2040                         if (r < 0)
2041                                 return bus_log_create_error(r);
2042 
2043                         r = sd_bus_message_close_container(m);
2044                         if (r < 0)
2045                                 return bus_log_create_error(r);
2046                 }
2047 
2048                 return 1;
2049         }
2050 
2051         return 0;
2052 }
2053 
bus_append_kill_property(sd_bus_message * m,const char * field,const char * eq)2054 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
2055         if (streq(field, "KillMode"))
2056                 return bus_append_string(m, field, eq);
2057 
2058         if (STR_IN_SET(field, "SendSIGHUP",
2059                               "SendSIGKILL"))
2060                 return bus_append_parse_boolean(m, field, eq);
2061 
2062         if (STR_IN_SET(field, "KillSignal",
2063                               "RestartKillSignal",
2064                               "FinalKillSignal",
2065                               "WatchdogSignal"))
2066                 return bus_append_signal_from_string(m, field, eq);
2067 
2068         return 0;
2069 }
2070 
bus_append_mount_property(sd_bus_message * m,const char * field,const char * eq)2071 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
2072 
2073         if (STR_IN_SET(field, "What",
2074                               "Where",
2075                               "Options",
2076                               "Type"))
2077                 return bus_append_string(m, field, eq);
2078 
2079         if (streq(field, "TimeoutSec"))
2080                 return bus_append_parse_sec_rename(m, field, eq);
2081 
2082         if (streq(field, "DirectoryMode"))
2083                 return bus_append_parse_mode(m, field, eq);
2084 
2085         if (STR_IN_SET(field, "SloppyOptions",
2086                               "LazyUnmount",
2087                               "ForceUnmount",
2088                               "ReadwriteOnly"))
2089                 return bus_append_parse_boolean(m, field, eq);
2090 
2091         return 0;
2092 }
2093 
bus_append_path_property(sd_bus_message * m,const char * field,const char * eq)2094 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
2095         int r;
2096 
2097         if (streq(field, "MakeDirectory"))
2098                 return bus_append_parse_boolean(m, field, eq);
2099 
2100         if (streq(field, "DirectoryMode"))
2101                 return bus_append_parse_mode(m, field, eq);
2102 
2103         if (STR_IN_SET(field, "PathExists",
2104                               "PathExistsGlob",
2105                               "PathChanged",
2106                               "PathModified",
2107                               "DirectoryNotEmpty")) {
2108                 if (isempty(eq))
2109                         r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
2110                 else
2111                         r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
2112                 if (r < 0)
2113                         return bus_log_create_error(r);
2114 
2115                 return 1;
2116         }
2117 
2118         if (streq(field, "TriggerLimitBurst"))
2119                 return bus_append_safe_atou(m, field, eq);
2120 
2121         if (streq(field, "TriggerLimitIntervalSec"))
2122                 return bus_append_parse_sec_rename(m, field, eq);
2123 
2124         return 0;
2125 }
2126 
bus_append_scope_property(sd_bus_message * m,const char * field,const char * eq)2127 static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
2128         if (streq(field, "RuntimeMaxSec"))
2129                 return bus_append_parse_sec_rename(m, field, eq);
2130 
2131         if (streq(field, "RuntimeRandomizedExtraSec"))
2132                 return bus_append_parse_sec_rename(m, field, eq);
2133 
2134         if (streq(field, "TimeoutStopSec"))
2135                 return bus_append_parse_sec_rename(m, field, eq);
2136 
2137         return 0;
2138 }
2139 
bus_append_service_property(sd_bus_message * m,const char * field,const char * eq)2140 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
2141         int r;
2142 
2143         if (STR_IN_SET(field, "PIDFile",
2144                               "Type",
2145                               "ExitType",
2146                               "Restart",
2147                               "BusName",
2148                               "NotifyAccess",
2149                               "USBFunctionDescriptors",
2150                               "USBFunctionStrings",
2151                               "OOMPolicy",
2152                               "TimeoutStartFailureMode",
2153                               "TimeoutStopFailureMode"))
2154                 return bus_append_string(m, field, eq);
2155 
2156         if (STR_IN_SET(field, "PermissionsStartOnly",
2157                               "RootDirectoryStartOnly",
2158                               "RemainAfterExit",
2159                               "GuessMainPID"))
2160                 return bus_append_parse_boolean(m, field, eq);
2161 
2162         if (STR_IN_SET(field, "RestartSec",
2163                               "TimeoutStartSec",
2164                               "TimeoutStopSec",
2165                               "TimeoutAbortSec",
2166                               "RuntimeMaxSec",
2167                               "RuntimeRandomizedExtraSec",
2168                               "WatchdogSec"))
2169                 return bus_append_parse_sec_rename(m, field, eq);
2170 
2171         if (streq(field, "TimeoutSec")) {
2172                 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
2173                 if (r < 0)
2174                         return r;
2175 
2176                 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
2177         }
2178 
2179         if (streq(field, "FileDescriptorStoreMax"))
2180                 return bus_append_safe_atou(m, field, eq);
2181 
2182         if (STR_IN_SET(field, "ExecCondition",
2183                               "ExecStartPre",
2184                               "ExecStart",
2185                               "ExecStartPost",
2186                               "ExecConditionEx",
2187                               "ExecStartPreEx",
2188                               "ExecStartEx",
2189                               "ExecStartPostEx",
2190                               "ExecReload",
2191                               "ExecStop",
2192                               "ExecStopPost",
2193                               "ExecReloadEx",
2194                               "ExecStopEx",
2195                               "ExecStopPostEx"))
2196                 return bus_append_exec_command(m, field, eq);
2197 
2198         if (STR_IN_SET(field, "RestartPreventExitStatus",
2199                               "RestartForceExitStatus",
2200                               "SuccessExitStatus")) {
2201                 _cleanup_free_ int *status = NULL, *signal = NULL;
2202                 size_t n_status = 0, n_signal = 0;
2203                 const char *p;
2204 
2205                 for (p = eq;;) {
2206                         _cleanup_free_ char *word = NULL;
2207 
2208                         r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2209                         if (r == 0)
2210                                 break;
2211                         if (r == -ENOMEM)
2212                                 return log_oom();
2213                         if (r < 0)
2214                                 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
2215 
2216                         /* We need to call exit_status_from_string() first, because we want
2217                          * to parse numbers as exit statuses, not signals. */
2218 
2219                         r = exit_status_from_string(word);
2220                         if (r >= 0) {
2221                                 assert(r >= 0 && r < 256);
2222 
2223                                 status = reallocarray(status, n_status + 1, sizeof(int));
2224                                 if (!status)
2225                                         return log_oom();
2226 
2227                                 status[n_status++] = r;
2228 
2229                         } else if ((r = signal_from_string(word)) >= 0) {
2230                                 signal = reallocarray(signal, n_signal + 1, sizeof(int));
2231                                 if (!signal)
2232                                         return log_oom();
2233 
2234                                 signal[n_signal++] = r;
2235 
2236                         } else
2237                                 /* original r from exit_status_to_string() */
2238                                 return log_error_errno(r, "Invalid status or signal %s in %s: %m",
2239                                                        word, field);
2240                 }
2241 
2242                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2243                 if (r < 0)
2244                         return bus_log_create_error(r);
2245 
2246                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
2247                 if (r < 0)
2248                         return bus_log_create_error(r);
2249 
2250                 r = sd_bus_message_open_container(m, 'v', "(aiai)");
2251                 if (r < 0)
2252                         return bus_log_create_error(r);
2253 
2254                 r = sd_bus_message_open_container(m, 'r', "aiai");
2255                 if (r < 0)
2256                         return bus_log_create_error(r);
2257 
2258                 r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
2259                 if (r < 0)
2260                         return bus_log_create_error(r);
2261 
2262                 r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
2263                 if (r < 0)
2264                         return bus_log_create_error(r);
2265 
2266                 r = sd_bus_message_close_container(m);
2267                 if (r < 0)
2268                         return bus_log_create_error(r);
2269 
2270                 r = sd_bus_message_close_container(m);
2271                 if (r < 0)
2272                         return bus_log_create_error(r);
2273 
2274                 r = sd_bus_message_close_container(m);
2275                 if (r < 0)
2276                         return bus_log_create_error(r);
2277 
2278                 return 1;
2279         }
2280 
2281         return 0;
2282 }
2283 
bus_append_socket_property(sd_bus_message * m,const char * field,const char * eq)2284 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
2285         int r;
2286 
2287         if (STR_IN_SET(field, "Accept",
2288                               "FlushPending",
2289                               "Writable",
2290                               "KeepAlive",
2291                               "NoDelay",
2292                               "FreeBind",
2293                               "Transparent",
2294                               "Broadcast",
2295                               "PassCredentials",
2296                               "PassSecurity",
2297                               "PassPacketInfo",
2298                               "ReusePort",
2299                               "RemoveOnStop",
2300                               "SELinuxContextFromNet"))
2301                 return bus_append_parse_boolean(m, field, eq);
2302 
2303         if (STR_IN_SET(field, "Priority",
2304                               "IPTTL",
2305                               "Mark"))
2306                 return bus_append_safe_atoi(m, field, eq);
2307 
2308         if (streq(field, "IPTOS"))
2309                 return bus_append_ip_tos_from_string(m, field, eq);
2310 
2311         if (STR_IN_SET(field, "Backlog",
2312                               "MaxConnections",
2313                               "MaxConnectionsPerSource",
2314                               "KeepAliveProbes",
2315                               "TriggerLimitBurst"))
2316                 return bus_append_safe_atou(m, field, eq);
2317 
2318         if (STR_IN_SET(field, "SocketMode",
2319                               "DirectoryMode"))
2320                 return bus_append_parse_mode(m, field, eq);
2321 
2322         if (STR_IN_SET(field, "MessageQueueMaxMessages",
2323                               "MessageQueueMessageSize"))
2324                 return bus_append_safe_atoi64(m, field, eq);
2325 
2326         if (STR_IN_SET(field, "TimeoutSec",
2327                               "KeepAliveTimeSec",
2328                               "KeepAliveIntervalSec",
2329                               "DeferAcceptSec",
2330                               "TriggerLimitIntervalSec"))
2331                 return bus_append_parse_sec_rename(m, field, eq);
2332 
2333         if (STR_IN_SET(field, "ReceiveBuffer",
2334                               "SendBuffer",
2335                               "PipeSize"))
2336                 return bus_append_parse_size(m, field, eq, 1024);
2337 
2338         if (STR_IN_SET(field, "ExecStartPre",
2339                               "ExecStartPost",
2340                               "ExecReload",
2341                               "ExecStopPost"))
2342                 return bus_append_exec_command(m, field, eq);
2343 
2344         if (STR_IN_SET(field, "SmackLabel",
2345                               "SmackLabelIPIn",
2346                               "SmackLabelIPOut",
2347                               "TCPCongestion",
2348                               "BindToDevice",
2349                               "BindIPv6Only",
2350                               "FileDescriptorName",
2351                               "SocketUser",
2352                               "SocketGroup",
2353                               "Timestamping"))
2354                 return bus_append_string(m, field, eq);
2355 
2356         if (streq(field, "Symlinks"))
2357                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2358 
2359         if (streq(field, "SocketProtocol"))
2360                 return bus_append_parse_ip_protocol(m, field, eq);
2361 
2362         if (STR_IN_SET(field, "ListenStream",
2363                               "ListenDatagram",
2364                               "ListenSequentialPacket",
2365                               "ListenNetlink",
2366                               "ListenSpecial",
2367                               "ListenMessageQueue",
2368                               "ListenFIFO",
2369                               "ListenUSBFunction")) {
2370                 if (isempty(eq))
2371                         r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
2372                 else
2373                         r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
2374                 if (r < 0)
2375                         return bus_log_create_error(r);
2376 
2377                 return 1;
2378         }
2379 
2380         return 0;
2381 }
bus_append_timer_property(sd_bus_message * m,const char * field,const char * eq)2382 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
2383         int r;
2384 
2385         if (STR_IN_SET(field, "WakeSystem",
2386                               "RemainAfterElapse",
2387                               "Persistent",
2388                               "OnTimezoneChange",
2389                               "OnClockChange",
2390                               "FixedRandomDelay"))
2391                 return bus_append_parse_boolean(m, field, eq);
2392 
2393         if (STR_IN_SET(field, "AccuracySec",
2394                               "RandomizedDelaySec"))
2395                 return bus_append_parse_sec_rename(m, field, eq);
2396 
2397         if (STR_IN_SET(field, "OnActiveSec",
2398                               "OnBootSec",
2399                               "OnStartupSec",
2400                               "OnUnitActiveSec",
2401                               "OnUnitInactiveSec")) {
2402                 if (isempty(eq))
2403                         r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
2404                 else {
2405                         usec_t t;
2406                         r = parse_sec(eq, &t);
2407                         if (r < 0)
2408                                 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
2409 
2410                         r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
2411                 }
2412                 if (r < 0)
2413                         return bus_log_create_error(r);
2414 
2415                 return 1;
2416         }
2417 
2418         if (streq(field, "OnCalendar")) {
2419                 if (isempty(eq))
2420                         r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
2421                 else
2422                         r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
2423                 if (r < 0)
2424                         return bus_log_create_error(r);
2425 
2426                 return 1;
2427         }
2428 
2429         return 0;
2430 }
2431 
bus_append_unit_property(sd_bus_message * m,const char * field,const char * eq)2432 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
2433         ConditionType t = _CONDITION_TYPE_INVALID;
2434         bool is_condition = false;
2435         int r;
2436 
2437         if (STR_IN_SET(field, "Description",
2438                               "SourcePath",
2439                               "OnFailureJobMode",
2440                               "JobTimeoutAction",
2441                               "JobTimeoutRebootArgument",
2442                               "StartLimitAction",
2443                               "FailureAction",
2444                               "SuccessAction",
2445                               "RebootArgument",
2446                               "CollectMode"))
2447                 return bus_append_string(m, field, eq);
2448 
2449         if (STR_IN_SET(field, "StopWhenUnneeded",
2450                               "RefuseManualStart",
2451                               "RefuseManualStop",
2452                               "AllowIsolate",
2453                               "IgnoreOnIsolate",
2454                               "DefaultDependencies"))
2455                 return bus_append_parse_boolean(m, field, eq);
2456 
2457         if (STR_IN_SET(field, "JobTimeoutSec",
2458                               "JobRunningTimeoutSec",
2459                               "StartLimitIntervalSec"))
2460                 return bus_append_parse_sec_rename(m, field, eq);
2461 
2462         if (streq(field, "StartLimitBurst"))
2463                 return bus_append_safe_atou(m, field, eq);
2464 
2465         if (STR_IN_SET(field, "SuccessActionExitStatus",
2466                               "FailureActionExitStatus")) {
2467                 if (isempty(eq))
2468                         r = sd_bus_message_append(m, "(sv)", field, "i", -1);
2469                 else {
2470                         uint8_t u;
2471 
2472                         r = safe_atou8(eq, &u);
2473                         if (r < 0)
2474                                 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
2475 
2476                         r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
2477                 }
2478                 if (r < 0)
2479                         return bus_log_create_error(r);
2480 
2481                 return 1;
2482         }
2483 
2484         if (unit_dependency_from_string(field) >= 0 ||
2485             STR_IN_SET(field, "Documentation",
2486                               "RequiresMountsFor",
2487                               "Markers"))
2488                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2489 
2490         t = condition_type_from_string(field);
2491         if (t >= 0)
2492                 is_condition = true;
2493         else
2494                 t = assert_type_from_string(field);
2495         if (t >= 0) {
2496                 if (isempty(eq))
2497                         r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
2498                 else {
2499                         const char *p = eq;
2500                         int trigger, negate;
2501 
2502                         trigger = *p == '|';
2503                         if (trigger)
2504                                 p++;
2505 
2506                         negate = *p == '!';
2507                         if (negate)
2508                                 p++;
2509 
2510                         r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
2511                                                   field, trigger, negate, p);
2512                 }
2513                 if (r < 0)
2514                         return bus_log_create_error(r);
2515 
2516                 return 1;
2517         }
2518 
2519         return 0;
2520 }
2521 
bus_append_unit_property_assignment(sd_bus_message * m,UnitType t,const char * assignment)2522 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
2523         const char *eq, *field;
2524         int r;
2525 
2526         assert(m);
2527         assert(assignment);
2528 
2529         eq = strchr(assignment, '=');
2530         if (!eq)
2531                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2532                                        "Not an assignment: %s", assignment);
2533 
2534         field = strndupa_safe(assignment, eq - assignment);
2535         eq++;
2536 
2537         switch (t) {
2538         case UNIT_SERVICE:
2539                 r = bus_append_cgroup_property(m, field, eq);
2540                 if (r != 0)
2541                         return r;
2542 
2543                 r = bus_append_execute_property(m, field, eq);
2544                 if (r != 0)
2545                         return r;
2546 
2547                 r = bus_append_kill_property(m, field, eq);
2548                 if (r != 0)
2549                         return r;
2550 
2551                 r = bus_append_service_property(m, field, eq);
2552                 if (r != 0)
2553                         return r;
2554                 break;
2555 
2556         case UNIT_SOCKET:
2557                 r = bus_append_cgroup_property(m, field, eq);
2558                 if (r != 0)
2559                         return r;
2560 
2561                 r = bus_append_execute_property(m, field, eq);
2562                 if (r != 0)
2563                         return r;
2564 
2565                 r = bus_append_kill_property(m, field, eq);
2566                 if (r != 0)
2567                         return r;
2568 
2569                 r = bus_append_socket_property(m, field, eq);
2570                 if (r != 0)
2571                         return r;
2572                 break;
2573 
2574         case UNIT_TIMER:
2575                 r = bus_append_timer_property(m, field, eq);
2576                 if (r != 0)
2577                         return r;
2578                 break;
2579 
2580         case UNIT_PATH:
2581                 r = bus_append_path_property(m, field, eq);
2582                 if (r != 0)
2583                         return r;
2584                 break;
2585 
2586         case UNIT_SLICE:
2587                 r = bus_append_cgroup_property(m, field, eq);
2588                 if (r != 0)
2589                         return r;
2590                 break;
2591 
2592         case UNIT_SCOPE:
2593                 r = bus_append_cgroup_property(m, field, eq);
2594                 if (r != 0)
2595                         return r;
2596 
2597                 r = bus_append_kill_property(m, field, eq);
2598                 if (r != 0)
2599                         return r;
2600 
2601                 r = bus_append_scope_property(m, field, eq);
2602                 if (r != 0)
2603                         return r;
2604                 break;
2605 
2606         case UNIT_MOUNT:
2607                 r = bus_append_cgroup_property(m, field, eq);
2608                 if (r != 0)
2609                         return r;
2610 
2611                 r = bus_append_execute_property(m, field, eq);
2612                 if (r != 0)
2613                         return r;
2614 
2615                 r = bus_append_kill_property(m, field, eq);
2616                 if (r != 0)
2617                         return r;
2618 
2619                 r = bus_append_mount_property(m, field, eq);
2620                 if (r != 0)
2621                         return r;
2622 
2623                 break;
2624 
2625         case UNIT_AUTOMOUNT:
2626                 r = bus_append_automount_property(m, field, eq);
2627                 if (r != 0)
2628                         return r;
2629 
2630                 break;
2631 
2632         case UNIT_TARGET:
2633         case UNIT_DEVICE:
2634         case UNIT_SWAP:
2635                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2636                                        "Not supported unit type");
2637 
2638         default:
2639                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2640                                        "Invalid unit type");
2641         }
2642 
2643         r = bus_append_unit_property(m, field, eq);
2644         if (r != 0)
2645                 return r;
2646 
2647         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2648                                "Unknown assignment: %s", assignment);
2649 }
2650 
bus_append_unit_property_assignment_many(sd_bus_message * m,UnitType t,char ** l)2651 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
2652         int r;
2653 
2654         assert(m);
2655 
2656         STRV_FOREACH(i, l) {
2657                 r = bus_append_unit_property_assignment(m, t, *i);
2658                 if (r < 0)
2659                         return r;
2660         }
2661 
2662         return 0;
2663 }
2664 
bus_deserialize_and_dump_unit_file_changes(sd_bus_message * m,bool quiet,UnitFileChange ** changes,size_t * n_changes)2665 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
2666         const char *type, *path, *source;
2667         int r;
2668 
2669         /* changes is dereferenced when calling unit_file_dump_changes() later,
2670          * so we have to make sure this is not NULL. */
2671         assert(changes);
2672         assert(n_changes);
2673 
2674         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2675         if (r < 0)
2676                 return bus_log_parse_error(r);
2677 
2678         while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2679                 /* We expect only "success" changes to be sent over the bus.
2680                    Hence, reject anything negative. */
2681                 int ch = unit_file_change_type_from_string(type);
2682                 if (ch < 0) {
2683                         log_notice_errno(ch, "Manager reported unknown change type \"%s\" for path \"%s\", ignoring.",
2684                                          type, path);
2685                         continue;
2686                 }
2687 
2688                 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2689                 if (r < 0)
2690                         return r;
2691         }
2692         if (r < 0)
2693                 return bus_log_parse_error(r);
2694 
2695         r = sd_bus_message_exit_container(m);
2696         if (r < 0)
2697                 return bus_log_parse_error(r);
2698 
2699         unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
2700         return 0;
2701 }
2702 
unit_load_state(sd_bus * bus,const char * name,char ** load_state)2703 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2704         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2705         _cleanup_free_ char *path = NULL;
2706         int r;
2707 
2708         path = unit_dbus_path_from_name(name);
2709         if (!path)
2710                 return log_oom();
2711 
2712         /* This function warns on it's own, because otherwise it'd be awkward to pass
2713          * the dbus error message around. */
2714 
2715         r = sd_bus_get_property_string(
2716                         bus,
2717                         "org.freedesktop.systemd1",
2718                         path,
2719                         "org.freedesktop.systemd1.Unit",
2720                         "LoadState",
2721                         &error,
2722                         load_state);
2723         if (r < 0)
2724                 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2725 
2726         return 0;
2727 }
2728 
unit_info_compare(const UnitInfo * a,const UnitInfo * b)2729 int unit_info_compare(const UnitInfo *a, const UnitInfo *b) {
2730         int r;
2731 
2732         /* First, order by machine */
2733         r = strcasecmp_ptr(a->machine, b->machine);
2734         if (r != 0)
2735                 return r;
2736 
2737         /* Second, order by unit type */
2738         r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
2739         if (r != 0)
2740                 return r;
2741 
2742         /* Third, order by name */
2743         return strcasecmp(a->id, b->id);
2744 }
2745