1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3   Copyright © 2012 Holger Hans Peter Freyther
4 ***/
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <linux/fs.h>
9 #include <linux/oom.h>
10 #if HAVE_SECCOMP
11 #include <seccomp.h>
12 #endif
13 #include <sched.h>
14 #include <sys/resource.h>
15 
16 #include "sd-messages.h"
17 
18 #include "af-list.h"
19 #include "all-units.h"
20 #include "alloc-util.h"
21 #include "bpf-firewall.h"
22 #include "bpf-lsm.h"
23 #include "bpf-program.h"
24 #include "bpf-socket-bind.h"
25 #include "bus-error.h"
26 #include "bus-internal.h"
27 #include "bus-util.h"
28 #include "cap-list.h"
29 #include "capability-util.h"
30 #include "cgroup-setup.h"
31 #include "conf-parser.h"
32 #include "core-varlink.h"
33 #include "cpu-set-util.h"
34 #include "creds-util.h"
35 #include "env-util.h"
36 #include "errno-list.h"
37 #include "escape.h"
38 #include "fd-util.h"
39 #include "fileio.h"
40 #include "fs-util.h"
41 #include "hexdecoct.h"
42 #include "io-util.h"
43 #include "ioprio-util.h"
44 #include "ip-protocol-list.h"
45 #include "journal-file.h"
46 #include "limits-util.h"
47 #include "load-fragment.h"
48 #include "log.h"
49 #include "missing_ioprio.h"
50 #include "mountpoint-util.h"
51 #include "nulstr-util.h"
52 #include "parse-helpers.h"
53 #include "parse-util.h"
54 #include "path-util.h"
55 #include "percent-util.h"
56 #include "process-util.h"
57 #if HAVE_SECCOMP
58 #include "seccomp-util.h"
59 #endif
60 #include "securebits-util.h"
61 #include "signal-util.h"
62 #include "socket-netlink.h"
63 #include "specifier.h"
64 #include "stat-util.h"
65 #include "string-util.h"
66 #include "strv.h"
67 #include "syslog-util.h"
68 #include "time-util.h"
69 #include "unit-name.h"
70 #include "unit-printf.h"
71 #include "user-util.h"
72 #include "utf8.h"
73 #include "web-util.h"
74 
parse_socket_protocol(const char * s)75 static int parse_socket_protocol(const char *s) {
76         int r;
77 
78         r = parse_ip_protocol(s);
79         if (r < 0)
80                 return r;
81         if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP))
82                 return -EPROTONOSUPPORT;
83 
84         return r;
85 }
86 
parse_crash_chvt(const char * value,int * data)87 int parse_crash_chvt(const char *value, int *data) {
88         int b;
89 
90         if (safe_atoi(value, data) >= 0)
91                 return 0;
92 
93         b = parse_boolean(value);
94         if (b < 0)
95                 return b;
96 
97         if (b > 0)
98                 *data = 0; /* switch to where kmsg goes */
99         else
100                 *data = -1; /* turn off switching */
101 
102         return 0;
103 }
104 
parse_confirm_spawn(const char * value,char ** console)105 int parse_confirm_spawn(const char *value, char **console) {
106         char *s;
107         int r;
108 
109         r = value ? parse_boolean(value) : 1;
110         if (r == 0) {
111                 *console = NULL;
112                 return 0;
113         } else if (r > 0) /* on with default tty */
114                 s = strdup("/dev/console");
115         else if (is_path(value)) /* on with fully qualified path */
116                 s = strdup(value);
117         else /* on with only a tty file name, not a fully qualified path */
118                 s = path_join("/dev/", value);
119         if (!s)
120                 return -ENOMEM;
121 
122         *console = s;
123         return 0;
124 }
125 
126 DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
127 DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
128 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
129 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
130 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
131 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc, "Failed to parse /proc/ protection mode");
132 DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset, "Failed to parse /proc/ subset mode");
133 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
134 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
135 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
136 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
137 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
138 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
139 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
140 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
141 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
142 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
143 DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
144 DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
145 DEFINE_CONFIG_PARSE_ENUM(config_parse_managed_oom_preference, managed_oom_preference, ManagedOOMPreference, "Failed to parse ManagedOOMPreference=");
146 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
147 DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
148 DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
149 DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
150 DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
151 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
152 DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
153 DEFINE_CONFIG_PARSE_ENUM_FULL(config_parse_socket_timestamping, socket_timestamping_from_string_harder, SocketTimestamping, "Failed to parse timestamping precision");
154 
contains_instance_specifier_superset(const char * s)155 bool contains_instance_specifier_superset(const char *s) {
156         const char *p, *q;
157         bool percent = false;
158 
159         assert(s);
160 
161         p = strchr(s, '@');
162         if (!p)
163                 return false;
164 
165         p++; /* Skip '@' */
166 
167         q = strrchr(p, '.');
168         if (!q)
169                 q = p + strlen(p);
170 
171         /* If the string is just the instance specifier, it's not a superset of the instance. */
172         if (memcmp_nn(p, q - p, "%i", strlen("%i")) == 0)
173                 return false;
174 
175         /* %i, %n and %N all expand to the instance or a superset of it. */
176         for (; p < q; p++) {
177                 if (*p == '%')
178                         percent = !percent;
179                 else if (percent) {
180                         if (IN_SET(*p, 'n', 'N', 'i'))
181                                 return true;
182                         percent = false;
183                 }
184         }
185 
186         return false;
187 }
188 
189 /* `name` is the rendered version of `format` via `unit_printf` or similar functions. */
unit_is_likely_recursive_template_dependency(Unit * u,const char * name,const char * format)190 int unit_is_likely_recursive_template_dependency(Unit *u, const char *name, const char *format) {
191         const char *fragment_path;
192         int r;
193 
194         assert(u);
195         assert(name);
196 
197         /* If a template unit has a direct dependency on itself that includes the unit instance as part of
198          * the template instance via a unit specifier (%i, %n or %N), this will almost certainly lead to
199          * infinite recursion as systemd will keep instantiating new instances of the template unit.
200          * https://github.com/systemd/systemd/issues/17602 shows a good example of how this can happen in
201          * practice. To guard against this, we check for templates that depend on themselves and have the
202          * instantiated unit instance included as part of the template instance of the dependency via a
203          * specifier.
204          *
205          * For example, if systemd-notify@.service depends on systemd-notify@%n.service, this will result in
206          * infinite recursion.
207          */
208 
209         if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
210                 return false;
211 
212         if (!unit_name_prefix_equal(u->id, name))
213                 return false;
214 
215         if (u->type != unit_name_to_type(name))
216                 return false;
217 
218         r = unit_file_find_fragment(u->manager->unit_id_map, u->manager->unit_name_map, name, &fragment_path, NULL);
219         if (r < 0)
220                 return r;
221 
222         /* Fragment paths should also be equal as a custom fragment for a specific template instance
223          * wouldn't necessarily lead to infinite recursion. */
224         if (!path_equal_ptr(u->fragment_path, fragment_path))
225                 return false;
226 
227         if (!contains_instance_specifier_superset(format))
228                 return false;
229 
230         return true;
231 }
232 
config_parse_unit_deps(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)233 int config_parse_unit_deps(
234                 const char *unit,
235                 const char *filename,
236                 unsigned line,
237                 const char *section,
238                 unsigned section_line,
239                 const char *lvalue,
240                 int ltype,
241                 const char *rvalue,
242                 void *data,
243                 void *userdata) {
244 
245         UnitDependency d = ltype;
246         Unit *u = userdata;
247 
248         assert(filename);
249         assert(lvalue);
250         assert(rvalue);
251 
252         for (const char *p = rvalue;;) {
253                 _cleanup_free_ char *word = NULL, *k = NULL;
254                 int r;
255 
256                 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
257                 if (r == 0)
258                         return 0;
259                 if (r == -ENOMEM)
260                         return log_oom();
261                 if (r < 0) {
262                         log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
263                         return 0;
264                 }
265 
266                 r = unit_name_printf(u, word, &k);
267                 if (r < 0) {
268                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
269                         continue;
270                 }
271 
272                 r = unit_is_likely_recursive_template_dependency(u, k, word);
273                 if (r < 0) {
274                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to determine if '%s' is a recursive dependency, ignoring: %m", k);
275                         continue;
276                 }
277                 if (r > 0) {
278                         log_syntax(unit, LOG_DEBUG, filename, line, 0,
279                                    "Dropping dependency %s=%s that likely leads to infinite recursion.",
280                                    unit_dependency_to_string(d), word);
281                         continue;
282                 }
283 
284                 r = unit_add_dependency_by_name(u, d, k, true, UNIT_DEPENDENCY_FILE);
285                 if (r < 0)
286                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
287         }
288 }
289 
config_parse_obsolete_unit_deps(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)290 int config_parse_obsolete_unit_deps(
291                 const char *unit,
292                 const char *filename,
293                 unsigned line,
294                 const char *section,
295                 unsigned section_line,
296                 const char *lvalue,
297                 int ltype,
298                 const char *rvalue,
299                 void *data,
300                 void *userdata) {
301 
302         log_syntax(unit, LOG_WARNING, filename, line, 0,
303                    "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
304 
305         return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
306 }
307 
config_parse_unit_string_printf(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)308 int config_parse_unit_string_printf(
309                 const char *unit,
310                 const char *filename,
311                 unsigned line,
312                 const char *section,
313                 unsigned section_line,
314                 const char *lvalue,
315                 int ltype,
316                 const char *rvalue,
317                 void *data,
318                 void *userdata) {
319 
320         _cleanup_free_ char *k = NULL;
321         const Unit *u = userdata;
322         int r;
323 
324         assert(filename);
325         assert(lvalue);
326         assert(rvalue);
327         assert(u);
328 
329         r = unit_full_printf(u, rvalue, &k);
330         if (r < 0) {
331                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
332                 return 0;
333         }
334 
335         return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
336 }
337 
config_parse_unit_strv_printf(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)338 int config_parse_unit_strv_printf(
339                 const char *unit,
340                 const char *filename,
341                 unsigned line,
342                 const char *section,
343                 unsigned section_line,
344                 const char *lvalue,
345                 int ltype,
346                 const char *rvalue,
347                 void *data,
348                 void *userdata) {
349 
350         const Unit *u = userdata;
351         _cleanup_free_ char *k = NULL;
352         int r;
353 
354         assert(filename);
355         assert(lvalue);
356         assert(rvalue);
357         assert(u);
358 
359         r = unit_full_printf(u, rvalue, &k);
360         if (r < 0) {
361                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
362                 return 0;
363         }
364 
365         return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
366 }
367 
config_parse_unit_path_printf(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)368 int config_parse_unit_path_printf(
369                 const char *unit,
370                 const char *filename,
371                 unsigned line,
372                 const char *section,
373                 unsigned section_line,
374                 const char *lvalue,
375                 int ltype,
376                 const char *rvalue,
377                 void *data,
378                 void *userdata) {
379 
380         _cleanup_free_ char *k = NULL;
381         const Unit *u = userdata;
382         int r;
383         bool fatal = ltype;
384 
385         assert(filename);
386         assert(lvalue);
387         assert(rvalue);
388         assert(u);
389 
390         r = unit_path_printf(u, rvalue, &k);
391         if (r < 0) {
392                 log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, r,
393                            "Failed to resolve unit specifiers in '%s'%s: %m",
394                            rvalue, fatal ? "" : ", ignoring");
395                 return fatal ? -ENOEXEC : 0;
396         }
397 
398         return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
399 }
400 
config_parse_colon_separated_paths(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)401 int config_parse_colon_separated_paths(
402                 const char *unit,
403                 const char *filename,
404                 unsigned line,
405                 const char *section,
406                 unsigned section_line,
407                 const char *lvalue,
408                 int ltype,
409                 const char *rvalue,
410                 void *data,
411                 void *userdata) {
412         char ***sv = data;
413         const Unit *u = userdata;
414         int r;
415 
416         assert(filename);
417         assert(lvalue);
418         assert(rvalue);
419         assert(data);
420 
421         if (isempty(rvalue)) {
422                 /* Empty assignment resets the list */
423                 *sv = strv_free(*sv);
424                 return 0;
425         }
426 
427         for (const char *p = rvalue;;) {
428                 _cleanup_free_ char *word = NULL, *k = NULL;
429 
430                 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
431                 if (r == -ENOMEM)
432                         return log_oom();
433                 if (r < 0) {
434                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
435                         return 0;
436                 }
437                 if (r == 0)
438                         break;
439 
440                 r = unit_path_printf(u, word, &k);
441                 if (r < 0) {
442                         log_syntax(unit, LOG_WARNING, filename, line, r,
443                                    "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
444                         return 0;
445                 }
446 
447                 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
448                 if (r < 0)
449                         return 0;
450 
451                 r = strv_consume(sv, TAKE_PTR(k));
452                 if (r < 0)
453                         return log_oom();
454         }
455 
456         return 0;
457 }
458 
config_parse_unit_path_strv_printf(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)459 int config_parse_unit_path_strv_printf(
460                 const char *unit,
461                 const char *filename,
462                 unsigned line,
463                 const char *section,
464                 unsigned section_line,
465                 const char *lvalue,
466                 int ltype,
467                 const char *rvalue,
468                 void *data,
469                 void *userdata) {
470 
471         char ***x = data;
472         const Unit *u = userdata;
473         int r;
474 
475         assert(filename);
476         assert(lvalue);
477         assert(rvalue);
478         assert(u);
479 
480         if (isempty(rvalue)) {
481                 *x = strv_free(*x);
482                 return 0;
483         }
484 
485         for (const char *p = rvalue;;) {
486                 _cleanup_free_ char *word = NULL, *k = NULL;
487 
488                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
489                 if (r == 0)
490                         return 0;
491                 if (r == -ENOMEM)
492                         return log_oom();
493                 if (r < 0) {
494                         log_syntax(unit, LOG_WARNING, filename, line, r,
495                                    "Invalid syntax, ignoring: %s", rvalue);
496                         return 0;
497                 }
498 
499                 r = unit_path_printf(u, word, &k);
500                 if (r < 0) {
501                         log_syntax(unit, LOG_WARNING, filename, line, r,
502                                    "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
503                         return 0;
504                 }
505 
506                 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
507                 if (r < 0)
508                         return 0;
509 
510                 r = strv_consume(x, TAKE_PTR(k));
511                 if (r < 0)
512                         return log_oom();
513         }
514 }
515 
patch_var_run(const char * unit,const char * filename,unsigned line,const char * lvalue,char ** path)516 static int patch_var_run(
517                 const char *unit,
518                 const char *filename,
519                 unsigned line,
520                 const char *lvalue,
521                 char **path) {
522 
523         const char *e;
524         char *z;
525 
526         e = path_startswith(*path, "/var/run/");
527         if (!e)
528                 return 0;
529 
530         z = path_join("/run/", e);
531         if (!z)
532                 return log_oom();
533 
534         log_syntax(unit, LOG_NOTICE, filename, line, 0,
535                    "%s= references a path below legacy directory /var/run/, updating %s → %s; "
536                    "please update the unit file accordingly.", lvalue, *path, z);
537 
538         free_and_replace(*path, z);
539 
540         return 1;
541 }
542 
config_parse_socket_listen(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)543 int config_parse_socket_listen(
544                 const char *unit,
545                 const char *filename,
546                 unsigned line,
547                 const char *section,
548                 unsigned section_line,
549                 const char *lvalue,
550                 int ltype,
551                 const char *rvalue,
552                 void *data,
553                 void *userdata) {
554 
555         _cleanup_free_ SocketPort *p = NULL;
556         SocketPort *tail;
557         Socket *s;
558         int r;
559 
560         assert(filename);
561         assert(lvalue);
562         assert(rvalue);
563         assert(data);
564 
565         s = SOCKET(data);
566 
567         if (isempty(rvalue)) {
568                 /* An empty assignment removes all ports */
569                 socket_free_ports(s);
570                 return 0;
571         }
572 
573         p = new0(SocketPort, 1);
574         if (!p)
575                 return log_oom();
576 
577         if (ltype != SOCKET_SOCKET) {
578                 _cleanup_free_ char *k = NULL;
579 
580                 r = unit_path_printf(UNIT(s), rvalue, &k);
581                 if (r < 0) {
582                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
583                         return 0;
584                 }
585 
586                 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
587                 if (r < 0)
588                         return 0;
589 
590                 if (ltype == SOCKET_FIFO) {
591                         r = patch_var_run(unit, filename, line, lvalue, &k);
592                         if (r < 0)
593                                 return r;
594                 }
595 
596                 free_and_replace(p->path, k);
597                 p->type = ltype;
598 
599         } else if (streq(lvalue, "ListenNetlink")) {
600                 _cleanup_free_ char  *k = NULL;
601 
602                 r = unit_path_printf(UNIT(s), rvalue, &k);
603                 if (r < 0) {
604                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
605                         return 0;
606                 }
607 
608                 r = socket_address_parse_netlink(&p->address, k);
609                 if (r < 0) {
610                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
611                         return 0;
612                 }
613 
614                 p->type = SOCKET_SOCKET;
615 
616         } else {
617                 _cleanup_free_ char *k = NULL;
618 
619                 r = unit_path_printf(UNIT(s), rvalue, &k);
620                 if (r < 0) {
621                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
622                         return 0;
623                 }
624 
625                 if (k[0] == '/') { /* Only for AF_UNIX file system sockets… */
626                         r = patch_var_run(unit, filename, line, lvalue, &k);
627                         if (r < 0)
628                                 return r;
629                 }
630 
631                 r = socket_address_parse_and_warn(&p->address, k);
632                 if (r < 0) {
633                         if (r != -EAFNOSUPPORT)
634                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
635                         return 0;
636                 }
637 
638                 if (streq(lvalue, "ListenStream"))
639                         p->address.type = SOCK_STREAM;
640                 else if (streq(lvalue, "ListenDatagram"))
641                         p->address.type = SOCK_DGRAM;
642                 else {
643                         assert(streq(lvalue, "ListenSequentialPacket"));
644                         p->address.type = SOCK_SEQPACKET;
645                 }
646 
647                 if (socket_address_family(&p->address) != AF_UNIX && p->address.type == SOCK_SEQPACKET) {
648                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
649                         return 0;
650                 }
651 
652                 p->type = SOCKET_SOCKET;
653         }
654 
655         p->fd = -1;
656         p->auxiliary_fds = NULL;
657         p->n_auxiliary_fds = 0;
658         p->socket = s;
659 
660         LIST_FIND_TAIL(port, s->ports, tail);
661         LIST_INSERT_AFTER(port, s->ports, tail, p);
662 
663         p = NULL;
664 
665         return 0;
666 }
667 
config_parse_exec_nice(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)668 int config_parse_exec_nice(
669                 const char *unit,
670                 const char *filename,
671                 unsigned line,
672                 const char *section,
673                 unsigned section_line,
674                 const char *lvalue,
675                 int ltype,
676                 const char *rvalue,
677                 void *data,
678                 void *userdata) {
679 
680         ExecContext *c = data;
681         int priority, r;
682 
683         assert(filename);
684         assert(lvalue);
685         assert(rvalue);
686         assert(data);
687 
688         if (isempty(rvalue)) {
689                 c->nice_set = false;
690                 return 0;
691         }
692 
693         r = parse_nice(rvalue, &priority);
694         if (r < 0) {
695                 if (r == -ERANGE)
696                         log_syntax(unit, LOG_WARNING, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
697                 else
698                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nice priority '%s', ignoring: %m", rvalue);
699                 return 0;
700         }
701 
702         c->nice = priority;
703         c->nice_set = true;
704 
705         return 0;
706 }
707 
config_parse_exec_oom_score_adjust(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)708 int config_parse_exec_oom_score_adjust(
709                 const char* unit,
710                 const char *filename,
711                 unsigned line,
712                 const char *section,
713                 unsigned section_line,
714                 const char *lvalue,
715                 int ltype,
716                 const char *rvalue,
717                 void *data,
718                 void *userdata) {
719 
720         ExecContext *c = data;
721         int oa, r;
722 
723         assert(filename);
724         assert(lvalue);
725         assert(rvalue);
726         assert(data);
727 
728         if (isempty(rvalue)) {
729                 c->oom_score_adjust_set = false;
730                 return 0;
731         }
732 
733         r = parse_oom_score_adjust(rvalue, &oa);
734         if (r < 0) {
735                 if (r == -ERANGE)
736                         log_syntax(unit, LOG_WARNING, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
737                 else
738                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue);
739                 return 0;
740         }
741 
742         c->oom_score_adjust = oa;
743         c->oom_score_adjust_set = true;
744 
745         return 0;
746 }
747 
config_parse_exec_coredump_filter(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)748 int config_parse_exec_coredump_filter(
749                 const char* unit,
750                 const char *filename,
751                 unsigned line,
752                 const char *section,
753                 unsigned section_line,
754                 const char *lvalue,
755                 int ltype,
756                 const char *rvalue,
757                 void *data,
758                 void *userdata) {
759 
760         ExecContext *c = data;
761         int r;
762 
763         assert(filename);
764         assert(lvalue);
765         assert(rvalue);
766         assert(data);
767 
768         if (isempty(rvalue)) {
769                 c->coredump_filter = 0;
770                 c->coredump_filter_set = false;
771                 return 0;
772         }
773 
774         uint64_t f;
775         r = coredump_filter_mask_from_string(rvalue, &f);
776         if (r < 0) {
777                 log_syntax(unit, LOG_WARNING, filename, line, r,
778                            "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue);
779                 return 0;
780         }
781 
782         c->coredump_filter |= f;
783         c->oom_score_adjust_set = true;
784         return 0;
785 }
786 
config_parse_kill_mode(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)787 int config_parse_kill_mode(
788                 const char* unit,
789                 const char *filename,
790                 unsigned line,
791                 const char *section,
792                 unsigned section_line,
793                 const char *lvalue,
794                 int ltype,
795                 const char *rvalue,
796                 void *data,
797                 void *userdata) {
798 
799         KillMode *k = data, m;
800 
801         assert(filename);
802         assert(lvalue);
803         assert(rvalue);
804         assert(data);
805 
806         if (isempty(rvalue)) {
807                 *k = KILL_CONTROL_GROUP;
808                 return 0;
809         }
810 
811         m = kill_mode_from_string(rvalue);
812         if (m < 0) {
813                 log_syntax(unit, LOG_WARNING, filename, line, m,
814                            "Failed to parse kill mode specification, ignoring: %s", rvalue);
815                 return 0;
816         }
817 
818         if (m == KILL_NONE)
819                 log_syntax(unit, LOG_WARNING, filename, line, 0,
820                            "Unit configured to use KillMode=none. "
821                            "This is unsafe, as it disables systemd's process lifecycle management for the service. "
822                            "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
823                            "Support for KillMode=none is deprecated and will eventually be removed.");
824 
825         *k = m;
826         return 0;
827 }
828 
config_parse_exec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)829 int config_parse_exec(
830                 const char *unit,
831                 const char *filename,
832                 unsigned line,
833                 const char *section,
834                 unsigned section_line,
835                 const char *lvalue,
836                 int ltype,
837                 const char *rvalue,
838                 void *data,
839                 void *userdata) {
840 
841         ExecCommand **e = data;
842         const Unit *u = userdata;
843         const char *p;
844         bool semicolon;
845         int r;
846 
847         assert(filename);
848         assert(lvalue);
849         assert(rvalue);
850         assert(e);
851 
852         e += ltype;
853 
854         if (isempty(rvalue)) {
855                 /* An empty assignment resets the list */
856                 *e = exec_command_free_list(*e);
857                 return 0;
858         }
859 
860         p = rvalue;
861         do {
862                 _cleanup_free_ char *path = NULL, *firstword = NULL;
863                 ExecCommandFlags flags = 0;
864                 bool ignore = false, separate_argv0 = false;
865                 _cleanup_free_ ExecCommand *nce = NULL;
866                 _cleanup_strv_free_ char **n = NULL;
867                 size_t nlen = 0;
868                 const char *f;
869 
870                 semicolon = false;
871 
872                 r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
873                 if (r <= 0)
874                         return 0;
875 
876                 /* A lone ";" is a separator. Let's make sure we don't treat it as an executable name. */
877                 if (streq(firstword, ";")) {
878                         semicolon = true;
879                         continue;
880                 }
881 
882                 f = firstword;
883                 for (;;) {
884                         /* We accept an absolute path as first argument.  If it's prefixed with - and the path doesn't
885                          * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
886                          * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
887                          * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
888                          * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
889                          * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
890                          * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
891                          * other sandboxing, with some special exceptions for changing UID.
892                          *
893                          * The idea is that '!!' may be used to write services that can take benefit of systemd's
894                          * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
895                          * privilege dropping within the daemon if the kernel does not offer that. */
896 
897                         if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
898                                 flags |= EXEC_COMMAND_IGNORE_FAILURE;
899                                 ignore = true;
900                         } else if (*f == '@' && !separate_argv0)
901                                 separate_argv0 = true;
902                         else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
903                                 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
904                         else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
905                                 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
906                         else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
907                                 flags |= EXEC_COMMAND_NO_SETUID;
908                         else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))) {
909                                 flags &= ~EXEC_COMMAND_NO_SETUID;
910                                 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
911                         } else
912                                 break;
913                         f++;
914                 }
915 
916                 r = unit_path_printf(u, f, &path);
917                 if (r < 0) {
918                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
919                                    "Failed to resolve unit specifiers in '%s'%s: %m",
920                                    f, ignore ? ", ignoring" : "");
921                         return ignore ? 0 : -ENOEXEC;
922                 }
923 
924                 if (isempty(path)) {
925                         /* First word is either "-" or "@" with no command. */
926                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
927                                    "Empty path in command line%s: '%s'",
928                                    ignore ? ", ignoring" : "", rvalue);
929                         return ignore ? 0 : -ENOEXEC;
930                 }
931                 if (!string_is_safe(path)) {
932                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
933                                    "Executable name contains special characters%s: %s",
934                                    ignore ? ", ignoring" : "", path);
935                         return ignore ? 0 : -ENOEXEC;
936                 }
937                 if (endswith(path, "/")) {
938                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
939                                    "Executable path specifies a directory%s: %s",
940                                    ignore ? ", ignoring" : "", path);
941                         return ignore ? 0 : -ENOEXEC;
942                 }
943 
944                 if (!(path_is_absolute(path) ? path_is_valid(path) : filename_is_valid(path))) {
945                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
946                                    "Neither a valid executable name nor an absolute path%s: %s",
947                                    ignore ? ", ignoring" : "", path);
948                         return ignore ? 0 : -ENOEXEC;
949                 }
950 
951                 if (!separate_argv0) {
952                         char *w = NULL;
953 
954                         if (!GREEDY_REALLOC0(n, nlen + 2))
955                                 return log_oom();
956 
957                         w = strdup(path);
958                         if (!w)
959                                 return log_oom();
960                         n[nlen++] = w;
961                         n[nlen] = NULL;
962                 }
963 
964                 path_simplify(path);
965 
966                 while (!isempty(p)) {
967                         _cleanup_free_ char *word = NULL, *resolved = NULL;
968 
969                         /* Check explicitly for an unquoted semicolon as
970                          * command separator token.  */
971                         if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
972                                 p++;
973                                 p += strspn(p, WHITESPACE);
974                                 semicolon = true;
975                                 break;
976                         }
977 
978                         /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
979                          * extract_first_word() would return the same for all of those.  */
980                         if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
981                                 char *w;
982 
983                                 p += 2;
984                                 p += strspn(p, WHITESPACE);
985 
986                                 if (!GREEDY_REALLOC0(n, nlen + 2))
987                                         return log_oom();
988 
989                                 w = strdup(";");
990                                 if (!w)
991                                         return log_oom();
992                                 n[nlen++] = w;
993                                 n[nlen] = NULL;
994                                 continue;
995                         }
996 
997                         r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
998                         if (r == 0)
999                                 break;
1000                         if (r < 0)
1001                                 return ignore ? 0 : -ENOEXEC;
1002 
1003                         r = unit_full_printf(u, word, &resolved);
1004                         if (r < 0) {
1005                                 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
1006                                            "Failed to resolve unit specifiers in %s%s: %m",
1007                                            word, ignore ? ", ignoring" : "");
1008                                 return ignore ? 0 : -ENOEXEC;
1009                         }
1010 
1011                         if (!GREEDY_REALLOC(n, nlen + 2))
1012                                 return log_oom();
1013 
1014                         n[nlen++] = TAKE_PTR(resolved);
1015                         n[nlen] = NULL;
1016                 }
1017 
1018                 if (!n || !n[0]) {
1019                         log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
1020                                    "Empty executable name or zeroeth argument%s: %s",
1021                                    ignore ? ", ignoring" : "", rvalue);
1022                         return ignore ? 0 : -ENOEXEC;
1023                 }
1024 
1025                 nce = new0(ExecCommand, 1);
1026                 if (!nce)
1027                         return log_oom();
1028 
1029                 nce->argv = TAKE_PTR(n);
1030                 nce->path = TAKE_PTR(path);
1031                 nce->flags = flags;
1032 
1033                 exec_command_append_list(e, nce);
1034 
1035                 /* Do not _cleanup_free_ these. */
1036                 nce = NULL;
1037 
1038                 rvalue = p;
1039         } while (semicolon);
1040 
1041         return 0;
1042 }
1043 
config_parse_socket_bindtodevice(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1044 int config_parse_socket_bindtodevice(
1045                 const char* unit,
1046                 const char *filename,
1047                 unsigned line,
1048                 const char *section,
1049                 unsigned section_line,
1050                 const char *lvalue,
1051                 int ltype,
1052                 const char *rvalue,
1053                 void *data,
1054                 void *userdata) {
1055 
1056         Socket *s = data;
1057 
1058         assert(filename);
1059         assert(lvalue);
1060         assert(rvalue);
1061         assert(data);
1062 
1063         if (isempty(rvalue) || streq(rvalue, "*")) {
1064                 s->bind_to_device = mfree(s->bind_to_device);
1065                 return 0;
1066         }
1067 
1068         if (!ifname_valid(rvalue)) {
1069                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", rvalue);
1070                 return 0;
1071         }
1072 
1073         return free_and_strdup_warn(&s->bind_to_device, rvalue);
1074 }
1075 
config_parse_exec_input(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1076 int config_parse_exec_input(
1077                 const char *unit,
1078                 const char *filename,
1079                 unsigned line,
1080                 const char *section,
1081                 unsigned section_line,
1082                 const char *lvalue,
1083                 int ltype,
1084                 const char *rvalue,
1085                 void *data,
1086                 void *userdata) {
1087 
1088         ExecContext *c = data;
1089         const Unit *u = userdata;
1090         const char *n;
1091         ExecInput ei;
1092         int r;
1093 
1094         assert(data);
1095         assert(filename);
1096         assert(line);
1097         assert(rvalue);
1098 
1099         n = startswith(rvalue, "fd:");
1100         if (n) {
1101                 _cleanup_free_ char *resolved = NULL;
1102 
1103                 r = unit_fd_printf(u, n, &resolved);
1104                 if (r < 0) {
1105                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
1106                         return 0;
1107                 }
1108 
1109                 if (isempty(resolved))
1110                         resolved = mfree(resolved);
1111                 else if (!fdname_is_valid(resolved)) {
1112                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
1113                         return 0;
1114                 }
1115 
1116                 free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
1117 
1118                 ei = EXEC_INPUT_NAMED_FD;
1119 
1120         } else if ((n = startswith(rvalue, "file:"))) {
1121                 _cleanup_free_ char *resolved = NULL;
1122 
1123                 r = unit_path_printf(u, n, &resolved);
1124                 if (r < 0) {
1125                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
1126                         return 0;
1127                 }
1128 
1129                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1130                 if (r < 0)
1131                         return 0;
1132 
1133                 free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
1134 
1135                 ei = EXEC_INPUT_FILE;
1136 
1137         } else {
1138                 ei = exec_input_from_string(rvalue);
1139                 if (ei < 0) {
1140                         log_syntax(unit, LOG_WARNING, filename, line, ei, "Failed to parse input specifier, ignoring: %s", rvalue);
1141                         return 0;
1142                 }
1143         }
1144 
1145         c->std_input = ei;
1146         return 0;
1147 }
1148 
config_parse_exec_input_text(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1149 int config_parse_exec_input_text(
1150                 const char *unit,
1151                 const char *filename,
1152                 unsigned line,
1153                 const char *section,
1154                 unsigned section_line,
1155                 const char *lvalue,
1156                 int ltype,
1157                 const char *rvalue,
1158                 void *data,
1159                 void *userdata) {
1160 
1161         _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
1162         ExecContext *c = data;
1163         const Unit *u = userdata;
1164         int r;
1165 
1166         assert(data);
1167         assert(filename);
1168         assert(line);
1169         assert(rvalue);
1170 
1171         if (isempty(rvalue)) {
1172                 /* Reset if the empty string is assigned */
1173                 c->stdin_data = mfree(c->stdin_data);
1174                 c->stdin_data_size = 0;
1175                 return 0;
1176         }
1177 
1178         ssize_t l = cunescape(rvalue, 0, &unescaped);
1179         if (l < 0) {
1180                 log_syntax(unit, LOG_WARNING, filename, line, l,
1181                            "Failed to decode C escaped text '%s', ignoring: %m", rvalue);
1182                 return 0;
1183         }
1184 
1185         r = unit_full_printf_full(u, unescaped, EXEC_STDIN_DATA_MAX, &resolved);
1186         if (r < 0) {
1187                 log_syntax(unit, LOG_WARNING, filename, line, r,
1188                            "Failed to resolve unit specifiers in '%s', ignoring: %m", unescaped);
1189                 return 0;
1190         }
1191 
1192         size_t sz = strlen(resolved);
1193         if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
1194             c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
1195                 log_syntax(unit, LOG_WARNING, filename, line, 0,
1196                            "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1197                            c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1198                 return 0;
1199         }
1200 
1201         void *p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
1202         if (!p)
1203                 return log_oom();
1204 
1205         *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
1206 
1207         c->stdin_data = p;
1208         c->stdin_data_size += sz + 1;
1209 
1210         return 0;
1211 }
1212 
config_parse_exec_input_data(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1213 int config_parse_exec_input_data(
1214                 const char *unit,
1215                 const char *filename,
1216                 unsigned line,
1217                 const char *section,
1218                 unsigned section_line,
1219                 const char *lvalue,
1220                 int ltype,
1221                 const char *rvalue,
1222                 void *data,
1223                 void *userdata) {
1224 
1225         _cleanup_free_ void *p = NULL;
1226         ExecContext *c = data;
1227         size_t sz;
1228         void *q;
1229         int r;
1230 
1231         assert(data);
1232         assert(filename);
1233         assert(line);
1234         assert(rvalue);
1235 
1236         if (isempty(rvalue)) {
1237                 /* Reset if the empty string is assigned */
1238                 c->stdin_data = mfree(c->stdin_data);
1239                 c->stdin_data_size = 0;
1240                 return 0;
1241         }
1242 
1243         r = unbase64mem(rvalue, SIZE_MAX, &p, &sz);
1244         if (r < 0) {
1245                 log_syntax(unit, LOG_WARNING, filename, line, r,
1246                            "Failed to decode base64 data, ignoring: %s", rvalue);
1247                 return 0;
1248         }
1249 
1250         assert(sz > 0);
1251 
1252         if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
1253             c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
1254                 log_syntax(unit, LOG_WARNING, filename, line, 0,
1255                            "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1256                            c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1257                 return 0;
1258         }
1259 
1260         q = realloc(c->stdin_data, c->stdin_data_size + sz);
1261         if (!q)
1262                 return log_oom();
1263 
1264         memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
1265 
1266         c->stdin_data = q;
1267         c->stdin_data_size += sz;
1268 
1269         return 0;
1270 }
1271 
config_parse_exec_output(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1272 int config_parse_exec_output(
1273                 const char *unit,
1274                 const char *filename,
1275                 unsigned line,
1276                 const char *section,
1277                 unsigned section_line,
1278                 const char *lvalue,
1279                 int ltype,
1280                 const char *rvalue,
1281                 void *data,
1282                 void *userdata) {
1283 
1284         _cleanup_free_ char *resolved = NULL;
1285         const char *n;
1286         ExecContext *c = data;
1287         const Unit *u = userdata;
1288         bool obsolete = false;
1289         ExecOutput eo;
1290         int r;
1291 
1292         assert(data);
1293         assert(filename);
1294         assert(line);
1295         assert(lvalue);
1296         assert(rvalue);
1297 
1298         n = startswith(rvalue, "fd:");
1299         if (n) {
1300                 r = unit_fd_printf(u, n, &resolved);
1301                 if (r < 0) {
1302                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
1303                         return 0;
1304                 }
1305 
1306                 if (isempty(resolved))
1307                         resolved = mfree(resolved);
1308                 else if (!fdname_is_valid(resolved)) {
1309                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
1310                         return 0;
1311                 }
1312 
1313                 eo = EXEC_OUTPUT_NAMED_FD;
1314 
1315         } else if (streq(rvalue, "syslog")) {
1316                 eo = EXEC_OUTPUT_JOURNAL;
1317                 obsolete = true;
1318 
1319         } else if (streq(rvalue, "syslog+console")) {
1320                 eo = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
1321                 obsolete = true;
1322 
1323         } else if ((n = startswith(rvalue, "file:"))) {
1324 
1325                 r = unit_path_printf(u, n, &resolved);
1326                 if (r < 0) {
1327                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1328                         return 0;
1329                 }
1330 
1331                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1332                 if (r < 0)
1333                         return 0;
1334 
1335                 eo = EXEC_OUTPUT_FILE;
1336 
1337         } else if ((n = startswith(rvalue, "append:"))) {
1338 
1339                 r = unit_path_printf(u, n, &resolved);
1340                 if (r < 0) {
1341                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1342                         return 0;
1343                 }
1344 
1345                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1346                 if (r < 0)
1347                         return 0;
1348 
1349                 eo = EXEC_OUTPUT_FILE_APPEND;
1350 
1351         } else if ((n = startswith(rvalue, "truncate:"))) {
1352 
1353                 r = unit_path_printf(u, n, &resolved);
1354                 if (r < 0) {
1355                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1356                         return 0;
1357                 }
1358 
1359                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1360                 if (r < 0)
1361                         return 0;
1362 
1363                 eo = EXEC_OUTPUT_FILE_TRUNCATE;
1364         } else {
1365                 eo = exec_output_from_string(rvalue);
1366                 if (eo < 0) {
1367                         log_syntax(unit, LOG_WARNING, filename, line, eo, "Failed to parse output specifier, ignoring: %s", rvalue);
1368                         return 0;
1369                 }
1370         }
1371 
1372         if (obsolete)
1373                 log_syntax(unit, LOG_NOTICE, filename, line, 0,
1374                            "Standard output type %s is obsolete, automatically updating to %s. Please update your unit file, and consider removing the setting altogether.",
1375                            rvalue, exec_output_to_string(eo));
1376 
1377         if (streq(lvalue, "StandardOutput")) {
1378                 if (eo == EXEC_OUTPUT_NAMED_FD)
1379                         free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
1380                 else
1381                         free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
1382 
1383                 c->std_output = eo;
1384 
1385         } else {
1386                 assert(streq(lvalue, "StandardError"));
1387 
1388                 if (eo == EXEC_OUTPUT_NAMED_FD)
1389                         free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
1390                 else
1391                         free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
1392 
1393                 c->std_error = eo;
1394         }
1395 
1396         return 0;
1397 }
1398 
config_parse_exec_io_class(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1399 int config_parse_exec_io_class(const char *unit,
1400                                const char *filename,
1401                                unsigned line,
1402                                const char *section,
1403                                unsigned section_line,
1404                                const char *lvalue,
1405                                int ltype,
1406                                const char *rvalue,
1407                                void *data,
1408                                void *userdata) {
1409 
1410         ExecContext *c = data;
1411         int x;
1412 
1413         assert(filename);
1414         assert(lvalue);
1415         assert(rvalue);
1416         assert(data);
1417 
1418         if (isempty(rvalue)) {
1419                 c->ioprio_set = false;
1420                 c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
1421                 return 0;
1422         }
1423 
1424         x = ioprio_class_from_string(rvalue);
1425         if (x < 0) {
1426                 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
1427                 return 0;
1428         }
1429 
1430         c->ioprio = ioprio_normalize(ioprio_prio_value(x, ioprio_prio_data(c->ioprio)));
1431         c->ioprio_set = true;
1432 
1433         return 0;
1434 }
1435 
config_parse_exec_io_priority(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1436 int config_parse_exec_io_priority(const char *unit,
1437                                   const char *filename,
1438                                   unsigned line,
1439                                   const char *section,
1440                                   unsigned section_line,
1441                                   const char *lvalue,
1442                                   int ltype,
1443                                   const char *rvalue,
1444                                   void *data,
1445                                   void *userdata) {
1446 
1447         ExecContext *c = data;
1448         int i, r;
1449 
1450         assert(filename);
1451         assert(lvalue);
1452         assert(rvalue);
1453         assert(data);
1454 
1455         if (isempty(rvalue)) {
1456                 c->ioprio_set = false;
1457                 c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
1458                 return 0;
1459         }
1460 
1461         r = ioprio_parse_priority(rvalue, &i);
1462         if (r < 0) {
1463                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
1464                 return 0;
1465         }
1466 
1467         c->ioprio = ioprio_normalize(ioprio_prio_value(ioprio_prio_class(c->ioprio), i));
1468         c->ioprio_set = true;
1469 
1470         return 0;
1471 }
1472 
config_parse_exec_cpu_sched_policy(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1473 int config_parse_exec_cpu_sched_policy(const char *unit,
1474                                        const char *filename,
1475                                        unsigned line,
1476                                        const char *section,
1477                                        unsigned section_line,
1478                                        const char *lvalue,
1479                                        int ltype,
1480                                        const char *rvalue,
1481                                        void *data,
1482                                        void *userdata) {
1483 
1484         ExecContext *c = data;
1485         int x;
1486 
1487         assert(filename);
1488         assert(lvalue);
1489         assert(rvalue);
1490         assert(data);
1491 
1492         if (isempty(rvalue)) {
1493                 c->cpu_sched_set = false;
1494                 c->cpu_sched_policy = SCHED_OTHER;
1495                 c->cpu_sched_priority = 0;
1496                 return 0;
1497         }
1498 
1499         x = sched_policy_from_string(rvalue);
1500         if (x < 0) {
1501                 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
1502                 return 0;
1503         }
1504 
1505         c->cpu_sched_policy = x;
1506         /* Moving to or from real-time policy? We need to adjust the priority */
1507         c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
1508         c->cpu_sched_set = true;
1509 
1510         return 0;
1511 }
1512 
config_parse_exec_mount_apivfs(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1513 int config_parse_exec_mount_apivfs(const char *unit,
1514                                    const char *filename,
1515                                    unsigned line,
1516                                    const char *section,
1517                                    unsigned section_line,
1518                                    const char *lvalue,
1519                                    int ltype,
1520                                    const char *rvalue,
1521                                    void *data,
1522                                    void *userdata) {
1523 
1524         ExecContext *c = data;
1525         int k;
1526 
1527         assert(filename);
1528         assert(lvalue);
1529         assert(rvalue);
1530         assert(data);
1531 
1532         if (isempty(rvalue)) {
1533                 c->mount_apivfs_set = false;
1534                 c->mount_apivfs = false;
1535                 return 0;
1536         }
1537 
1538         k = parse_boolean(rvalue);
1539         if (k < 0) {
1540                 log_syntax(unit, LOG_WARNING, filename, line, k,
1541                            "Failed to parse boolean value, ignoring: %s",
1542                            rvalue);
1543                 return 0;
1544         }
1545 
1546         c->mount_apivfs_set = true;
1547         c->mount_apivfs = k;
1548         return 0;
1549 }
1550 
config_parse_numa_mask(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1551 int config_parse_numa_mask(const char *unit,
1552                            const char *filename,
1553                            unsigned line,
1554                            const char *section,
1555                            unsigned section_line,
1556                            const char *lvalue,
1557                            int ltype,
1558                            const char *rvalue,
1559                            void *data,
1560                            void *userdata) {
1561         int r;
1562         NUMAPolicy *p = data;
1563 
1564         assert(filename);
1565         assert(lvalue);
1566         assert(rvalue);
1567         assert(data);
1568 
1569         if (streq(rvalue, "all")) {
1570                 r = numa_mask_add_all(&p->nodes);
1571                 if (r < 0)
1572                         log_syntax(unit, LOG_WARNING, filename, line, r,
1573                                    "Failed to create NUMA mask representing \"all\" NUMA nodes, ignoring: %m");
1574         } else {
1575                 r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue);
1576                 if (r < 0)
1577                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue);
1578         }
1579 
1580         return 0;
1581 }
1582 
config_parse_exec_cpu_sched_prio(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1583 int config_parse_exec_cpu_sched_prio(const char *unit,
1584                                      const char *filename,
1585                                      unsigned line,
1586                                      const char *section,
1587                                      unsigned section_line,
1588                                      const char *lvalue,
1589                                      int ltype,
1590                                      const char *rvalue,
1591                                      void *data,
1592                                      void *userdata) {
1593 
1594         ExecContext *c = data;
1595         int i, min, max, r;
1596 
1597         assert(filename);
1598         assert(lvalue);
1599         assert(rvalue);
1600         assert(data);
1601 
1602         r = safe_atoi(rvalue, &i);
1603         if (r < 0) {
1604                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue);
1605                 return 0;
1606         }
1607 
1608         /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1609         min = sched_get_priority_min(c->cpu_sched_policy);
1610         max = sched_get_priority_max(c->cpu_sched_policy);
1611 
1612         if (i < min || i > max) {
1613                 log_syntax(unit, LOG_WARNING, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
1614                 return 0;
1615         }
1616 
1617         c->cpu_sched_priority = i;
1618         c->cpu_sched_set = true;
1619 
1620         return 0;
1621 }
1622 
config_parse_root_image_options(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1623 int config_parse_root_image_options(
1624                 const char *unit,
1625                 const char *filename,
1626                 unsigned line,
1627                 const char *section,
1628                 unsigned section_line,
1629                 const char *lvalue,
1630                 int ltype,
1631                 const char *rvalue,
1632                 void *data,
1633                 void *userdata) {
1634 
1635         _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
1636         _cleanup_strv_free_ char **l = NULL;
1637         ExecContext *c = data;
1638         const Unit *u = userdata;
1639         int r;
1640 
1641         assert(filename);
1642         assert(lvalue);
1643         assert(rvalue);
1644         assert(data);
1645 
1646         if (isempty(rvalue)) {
1647                 c->root_image_options = mount_options_free_all(c->root_image_options);
1648                 return 0;
1649         }
1650 
1651         r = strv_split_colon_pairs(&l, rvalue);
1652         if (r == -ENOMEM)
1653                 return log_oom();
1654         if (r < 0) {
1655                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
1656                 return 0;
1657         }
1658 
1659         STRV_FOREACH_PAIR(first, second, l) {
1660                 MountOptions *o = NULL;
1661                 _cleanup_free_ char *mount_options_resolved = NULL;
1662                 const char *mount_options = NULL, *partition = "root";
1663                 PartitionDesignator partition_designator;
1664 
1665                 /* Format is either 'root:foo' or 'foo' (root is implied) */
1666                 if (!isempty(*second)) {
1667                         partition = *first;
1668                         mount_options = *second;
1669                 } else
1670                         mount_options = *first;
1671 
1672                 partition_designator = partition_designator_from_string(partition);
1673                 if (partition_designator < 0) {
1674                         log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
1675                                    "Invalid partition name %s, ignoring", partition);
1676                         continue;
1677                 }
1678                 r = unit_full_printf(u, mount_options, &mount_options_resolved);
1679                 if (r < 0) {
1680                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
1681                         continue;
1682                 }
1683 
1684                 o = new(MountOptions, 1);
1685                 if (!o)
1686                         return log_oom();
1687                 *o = (MountOptions) {
1688                         .partition_designator = partition_designator,
1689                         .options = TAKE_PTR(mount_options_resolved),
1690                 };
1691                 LIST_APPEND(mount_options, options, TAKE_PTR(o));
1692         }
1693 
1694         /* empty spaces/separators only */
1695         if (LIST_IS_EMPTY(options))
1696                 c->root_image_options = mount_options_free_all(c->root_image_options);
1697         else
1698                 LIST_JOIN(mount_options, c->root_image_options, options);
1699 
1700         return 0;
1701 }
1702 
config_parse_exec_root_hash(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1703 int config_parse_exec_root_hash(
1704                 const char *unit,
1705                 const char *filename,
1706                 unsigned line,
1707                 const char *section,
1708                 unsigned section_line,
1709                 const char *lvalue,
1710                 int ltype,
1711                 const char *rvalue,
1712                 void *data,
1713                 void *userdata) {
1714 
1715         _cleanup_free_ void *roothash_decoded = NULL;
1716         ExecContext *c = data;
1717         size_t roothash_decoded_size = 0;
1718         int r;
1719 
1720         assert(data);
1721         assert(filename);
1722         assert(line);
1723         assert(rvalue);
1724 
1725         if (isempty(rvalue)) {
1726                 /* Reset if the empty string is assigned */
1727                 c->root_hash_path = mfree(c->root_hash_path);
1728                 c->root_hash = mfree(c->root_hash);
1729                 c->root_hash_size = 0;
1730                 return 0;
1731         }
1732 
1733         if (path_is_absolute(rvalue)) {
1734                 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1735                 _cleanup_free_ char *p = NULL;
1736 
1737                 p = strdup(rvalue);
1738                 if (!p)
1739                         return -ENOMEM;
1740 
1741                 free_and_replace(c->root_hash_path, p);
1742                 c->root_hash = mfree(c->root_hash);
1743                 c->root_hash_size = 0;
1744                 return 0;
1745         }
1746 
1747         /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1748         r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
1749         if (r < 0) {
1750                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
1751                 return 0;
1752         }
1753         if (roothash_decoded_size < sizeof(sd_id128_t)) {
1754                 log_syntax(unit, LOG_WARNING, filename, line, 0, "RootHash= is too short, ignoring: %s", rvalue);
1755                 return 0;
1756         }
1757 
1758         free_and_replace(c->root_hash, roothash_decoded);
1759         c->root_hash_size = roothash_decoded_size;
1760         c->root_hash_path = mfree(c->root_hash_path);
1761 
1762         return 0;
1763 }
1764 
config_parse_exec_root_hash_sig(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1765 int config_parse_exec_root_hash_sig(
1766                 const char *unit,
1767                 const char *filename,
1768                 unsigned line,
1769                 const char *section,
1770                 unsigned section_line,
1771                 const char *lvalue,
1772                 int ltype,
1773                 const char *rvalue,
1774                 void *data,
1775                 void *userdata) {
1776 
1777         _cleanup_free_ void *roothash_sig_decoded = NULL;
1778         char *value;
1779         ExecContext *c = data;
1780         size_t roothash_sig_decoded_size = 0;
1781         int r;
1782 
1783         assert(data);
1784         assert(filename);
1785         assert(line);
1786         assert(rvalue);
1787 
1788         if (isempty(rvalue)) {
1789                 /* Reset if the empty string is assigned */
1790                 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1791                 c->root_hash_sig = mfree(c->root_hash_sig);
1792                 c->root_hash_sig_size = 0;
1793                 return 0;
1794         }
1795 
1796         if (path_is_absolute(rvalue)) {
1797                 /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
1798                 _cleanup_free_ char *p = NULL;
1799 
1800                 p = strdup(rvalue);
1801                 if (!p)
1802                         return log_oom();
1803 
1804                 free_and_replace(c->root_hash_sig_path, p);
1805                 c->root_hash_sig = mfree(c->root_hash_sig);
1806                 c->root_hash_sig_size = 0;
1807                 return 0;
1808         }
1809 
1810         if (!(value = startswith(rvalue, "base64:"))) {
1811                 log_syntax(unit, LOG_WARNING, filename, line, 0,
1812                            "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
1813                 return 0;
1814         }
1815 
1816         /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1817         r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1818         if (r < 0) {
1819                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
1820                 return 0;
1821         }
1822 
1823         free_and_replace(c->root_hash_sig, roothash_sig_decoded);
1824         c->root_hash_sig_size = roothash_sig_decoded_size;
1825         c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1826 
1827         return 0;
1828 }
1829 
config_parse_exec_cpu_affinity(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1830 int config_parse_exec_cpu_affinity(
1831                 const char *unit,
1832                 const char *filename,
1833                 unsigned line,
1834                 const char *section,
1835                 unsigned section_line,
1836                 const char *lvalue,
1837                 int ltype,
1838                 const char *rvalue,
1839                 void *data,
1840                 void *userdata) {
1841 
1842         ExecContext *c = data;
1843         const Unit *u = userdata;
1844         _cleanup_free_ char *k = NULL;
1845         int r;
1846 
1847         assert(filename);
1848         assert(lvalue);
1849         assert(rvalue);
1850         assert(data);
1851 
1852         if (streq(rvalue, "numa")) {
1853                 c->cpu_affinity_from_numa = true;
1854                 cpu_set_reset(&c->cpu_set);
1855 
1856                 return 0;
1857         }
1858 
1859         r = unit_full_printf(u, rvalue, &k);
1860         if (r < 0) {
1861                 log_syntax(unit, LOG_WARNING, filename, line, r,
1862                            "Failed to resolve unit specifiers in '%s', ignoring: %m",
1863                            rvalue);
1864                 return 0;
1865         }
1866 
1867         r = parse_cpu_set_extend(k, &c->cpu_set, true, unit, filename, line, lvalue);
1868         if (r >= 0)
1869                 c->cpu_affinity_from_numa = false;
1870 
1871         return 0;
1872 }
1873 
config_parse_capability_set(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1874 int config_parse_capability_set(
1875                 const char *unit,
1876                 const char *filename,
1877                 unsigned line,
1878                 const char *section,
1879                 unsigned section_line,
1880                 const char *lvalue,
1881                 int ltype,
1882                 const char *rvalue,
1883                 void *data,
1884                 void *userdata) {
1885 
1886         uint64_t *capability_set = data;
1887         uint64_t sum = 0, initial = 0;
1888         bool invert = false;
1889         int r;
1890 
1891         assert(filename);
1892         assert(lvalue);
1893         assert(rvalue);
1894         assert(data);
1895 
1896         if (rvalue[0] == '~') {
1897                 invert = true;
1898                 rvalue++;
1899         }
1900 
1901         if (streq(lvalue, "CapabilityBoundingSet"))
1902                 initial = CAP_ALL; /* initialized to all bits on */
1903         /* else "AmbientCapabilities" initialized to all bits off */
1904 
1905         r = capability_set_from_string(rvalue, &sum);
1906         if (r < 0) {
1907                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue);
1908                 return 0;
1909         }
1910 
1911         if (sum == 0 || *capability_set == initial)
1912                 /* "", "~" or uninitialized data -> replace */
1913                 *capability_set = invert ? ~sum : sum;
1914         else {
1915                 /* previous data -> merge */
1916                 if (invert)
1917                         *capability_set &= ~sum;
1918                 else
1919                         *capability_set |= sum;
1920         }
1921 
1922         return 0;
1923 }
1924 
config_parse_exec_selinux_context(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1925 int config_parse_exec_selinux_context(
1926                 const char *unit,
1927                 const char *filename,
1928                 unsigned line,
1929                 const char *section,
1930                 unsigned section_line,
1931                 const char *lvalue,
1932                 int ltype,
1933                 const char *rvalue,
1934                 void *data,
1935                 void *userdata) {
1936 
1937         ExecContext *c = data;
1938         const Unit *u = userdata;
1939         bool ignore;
1940         char *k;
1941         int r;
1942 
1943         assert(filename);
1944         assert(lvalue);
1945         assert(rvalue);
1946         assert(data);
1947 
1948         if (isempty(rvalue)) {
1949                 c->selinux_context = mfree(c->selinux_context);
1950                 c->selinux_context_ignore = false;
1951                 return 0;
1952         }
1953 
1954         if (rvalue[0] == '-') {
1955                 ignore = true;
1956                 rvalue++;
1957         } else
1958                 ignore = false;
1959 
1960         r = unit_full_printf(u, rvalue, &k);
1961         if (r < 0) {
1962                 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
1963                            "Failed to resolve unit specifiers in '%s'%s: %m",
1964                            rvalue, ignore ? ", ignoring" : "");
1965                 return ignore ? 0 : -ENOEXEC;
1966         }
1967 
1968         free_and_replace(c->selinux_context, k);
1969         c->selinux_context_ignore = ignore;
1970 
1971         return 0;
1972 }
1973 
config_parse_exec_apparmor_profile(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1974 int config_parse_exec_apparmor_profile(
1975                 const char *unit,
1976                 const char *filename,
1977                 unsigned line,
1978                 const char *section,
1979                 unsigned section_line,
1980                 const char *lvalue,
1981                 int ltype,
1982                 const char *rvalue,
1983                 void *data,
1984                 void *userdata) {
1985 
1986         ExecContext *c = data;
1987         const Unit *u = userdata;
1988         bool ignore;
1989         char *k;
1990         int r;
1991 
1992         assert(filename);
1993         assert(lvalue);
1994         assert(rvalue);
1995         assert(data);
1996 
1997         if (isempty(rvalue)) {
1998                 c->apparmor_profile = mfree(c->apparmor_profile);
1999                 c->apparmor_profile_ignore = false;
2000                 return 0;
2001         }
2002 
2003         if (rvalue[0] == '-') {
2004                 ignore = true;
2005                 rvalue++;
2006         } else
2007                 ignore = false;
2008 
2009         r = unit_full_printf(u, rvalue, &k);
2010         if (r < 0) {
2011                 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2012                            "Failed to resolve unit specifiers in '%s'%s: %m",
2013                            rvalue, ignore ? ", ignoring" : "");
2014                 return ignore ? 0 : -ENOEXEC;
2015         }
2016 
2017         free_and_replace(c->apparmor_profile, k);
2018         c->apparmor_profile_ignore = ignore;
2019 
2020         return 0;
2021 }
2022 
config_parse_exec_smack_process_label(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2023 int config_parse_exec_smack_process_label(
2024                 const char *unit,
2025                 const char *filename,
2026                 unsigned line,
2027                 const char *section,
2028                 unsigned section_line,
2029                 const char *lvalue,
2030                 int ltype,
2031                 const char *rvalue,
2032                 void *data,
2033                 void *userdata) {
2034 
2035         ExecContext *c = data;
2036         const Unit *u = userdata;
2037         bool ignore;
2038         char *k;
2039         int r;
2040 
2041         assert(filename);
2042         assert(lvalue);
2043         assert(rvalue);
2044         assert(data);
2045 
2046         if (isempty(rvalue)) {
2047                 c->smack_process_label = mfree(c->smack_process_label);
2048                 c->smack_process_label_ignore = false;
2049                 return 0;
2050         }
2051 
2052         if (rvalue[0] == '-') {
2053                 ignore = true;
2054                 rvalue++;
2055         } else
2056                 ignore = false;
2057 
2058         r = unit_full_printf(u, rvalue, &k);
2059         if (r < 0) {
2060                 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2061                            "Failed to resolve unit specifiers in '%s'%s: %m",
2062                            rvalue, ignore ? ", ignoring" : "");
2063                 return ignore ? 0 : -ENOEXEC;
2064         }
2065 
2066         free_and_replace(c->smack_process_label, k);
2067         c->smack_process_label_ignore = ignore;
2068 
2069         return 0;
2070 }
2071 
config_parse_timer(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2072 int config_parse_timer(
2073                 const char *unit,
2074                 const char *filename,
2075                 unsigned line,
2076                 const char *section,
2077                 unsigned section_line,
2078                 const char *lvalue,
2079                 int ltype,
2080                 const char *rvalue,
2081                 void *data,
2082                 void *userdata) {
2083 
2084         _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
2085         _cleanup_free_ char *k = NULL;
2086         const Unit *u = userdata;
2087         Timer *t = data;
2088         usec_t usec = 0;
2089         TimerValue *v;
2090         int r;
2091 
2092         assert(filename);
2093         assert(lvalue);
2094         assert(rvalue);
2095         assert(data);
2096 
2097         if (isempty(rvalue)) {
2098                 /* Empty assignment resets list */
2099                 timer_free_values(t);
2100                 return 0;
2101         }
2102 
2103         r = unit_full_printf(u, rvalue, &k);
2104         if (r < 0) {
2105                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2106                 return 0;
2107         }
2108 
2109         if (ltype == TIMER_CALENDAR) {
2110                 r = calendar_spec_from_string(k, &c);
2111                 if (r < 0) {
2112                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k);
2113                         return 0;
2114                 }
2115         } else {
2116                 r = parse_sec(k, &usec);
2117                 if (r < 0) {
2118                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", k);
2119                         return 0;
2120                 }
2121         }
2122 
2123         v = new(TimerValue, 1);
2124         if (!v)
2125                 return log_oom();
2126 
2127         *v = (TimerValue) {
2128                 .base = ltype,
2129                 .value = usec,
2130                 .calendar_spec = TAKE_PTR(c),
2131         };
2132 
2133         LIST_PREPEND(value, t->values, v);
2134 
2135         return 0;
2136 }
2137 
config_parse_trigger_unit(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2138 int config_parse_trigger_unit(
2139                 const char *unit,
2140                 const char *filename,
2141                 unsigned line,
2142                 const char *section,
2143                 unsigned section_line,
2144                 const char *lvalue,
2145                 int ltype,
2146                 const char *rvalue,
2147                 void *data,
2148                 void *userdata) {
2149 
2150         _cleanup_free_ char *p = NULL;
2151         Unit *u = data;
2152         UnitType type;
2153         int r;
2154 
2155         assert(filename);
2156         assert(lvalue);
2157         assert(rvalue);
2158         assert(data);
2159 
2160         if (UNIT_TRIGGER(u)) {
2161                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
2162                 return 0;
2163         }
2164 
2165         r = unit_name_printf(u, rvalue, &p);
2166         if (r < 0) {
2167                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2168                 return 0;
2169         }
2170 
2171         type = unit_name_to_type(p);
2172         if (type < 0) {
2173                 log_syntax(unit, LOG_WARNING, filename, line, type, "Unit type not valid, ignoring: %s", rvalue);
2174                 return 0;
2175         }
2176         if (unit_has_name(u, p)) {
2177                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Units cannot trigger themselves, ignoring: %s", rvalue);
2178                 return 0;
2179         }
2180 
2181         r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
2182         if (r < 0) {
2183                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
2184                 return 0;
2185         }
2186 
2187         return 0;
2188 }
2189 
config_parse_path_spec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2190 int config_parse_path_spec(const char *unit,
2191                            const char *filename,
2192                            unsigned line,
2193                            const char *section,
2194                            unsigned section_line,
2195                            const char *lvalue,
2196                            int ltype,
2197                            const char *rvalue,
2198                            void *data,
2199                            void *userdata) {
2200 
2201         Path *p = data;
2202         PathSpec *s;
2203         PathType b;
2204         _cleanup_free_ char *k = NULL;
2205         int r;
2206 
2207         assert(filename);
2208         assert(lvalue);
2209         assert(rvalue);
2210         assert(data);
2211 
2212         if (isempty(rvalue)) {
2213                 /* Empty assignment clears list */
2214                 path_free_specs(p);
2215                 return 0;
2216         }
2217 
2218         b = path_type_from_string(lvalue);
2219         if (b < 0) {
2220                 log_syntax(unit, LOG_WARNING, filename, line, b, "Failed to parse path type, ignoring: %s", lvalue);
2221                 return 0;
2222         }
2223 
2224         r = unit_path_printf(UNIT(p), rvalue, &k);
2225         if (r < 0) {
2226                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2227                 return 0;
2228         }
2229 
2230         r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2231         if (r < 0)
2232                 return 0;
2233 
2234         s = new0(PathSpec, 1);
2235         if (!s)
2236                 return log_oom();
2237 
2238         s->unit = UNIT(p);
2239         s->path = TAKE_PTR(k);
2240         s->type = b;
2241         s->inotify_fd = -1;
2242 
2243         LIST_PREPEND(spec, p->specs, s);
2244 
2245         return 0;
2246 }
2247 
config_parse_socket_service(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2248 int config_parse_socket_service(
2249                 const char *unit,
2250                 const char *filename,
2251                 unsigned line,
2252                 const char *section,
2253                 unsigned section_line,
2254                 const char *lvalue,
2255                 int ltype,
2256                 const char *rvalue,
2257                 void *data,
2258                 void *userdata) {
2259 
2260         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2261         _cleanup_free_ char *p = NULL;
2262         Socket *s = data;
2263         Unit *x;
2264         int r;
2265 
2266         assert(filename);
2267         assert(lvalue);
2268         assert(rvalue);
2269         assert(data);
2270 
2271         r = unit_name_printf(UNIT(s), rvalue, &p);
2272         if (r < 0) {
2273                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2274                 return 0;
2275         }
2276 
2277         if (!endswith(p, ".service")) {
2278                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
2279                 return 0;
2280         }
2281 
2282         r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
2283         if (r < 0) {
2284                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
2285                 return 0;
2286         }
2287 
2288         unit_ref_set(&s->service, UNIT(s), x);
2289 
2290         return 0;
2291 }
2292 
config_parse_fdname(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2293 int config_parse_fdname(
2294                 const char *unit,
2295                 const char *filename,
2296                 unsigned line,
2297                 const char *section,
2298                 unsigned section_line,
2299                 const char *lvalue,
2300                 int ltype,
2301                 const char *rvalue,
2302                 void *data,
2303                 void *userdata) {
2304 
2305         _cleanup_free_ char *p = NULL;
2306         Socket *s = data;
2307         int r;
2308 
2309         assert(filename);
2310         assert(lvalue);
2311         assert(rvalue);
2312         assert(data);
2313 
2314         if (isempty(rvalue)) {
2315                 s->fdname = mfree(s->fdname);
2316                 return 0;
2317         }
2318 
2319         r = unit_fd_printf(UNIT(s), rvalue, &p);
2320         if (r < 0) {
2321                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2322                 return 0;
2323         }
2324 
2325         if (!fdname_is_valid(p)) {
2326                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
2327                 return 0;
2328         }
2329 
2330         return free_and_replace(s->fdname, p);
2331 }
2332 
config_parse_service_sockets(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2333 int config_parse_service_sockets(
2334                 const char *unit,
2335                 const char *filename,
2336                 unsigned line,
2337                 const char *section,
2338                 unsigned section_line,
2339                 const char *lvalue,
2340                 int ltype,
2341                 const char *rvalue,
2342                 void *data,
2343                 void *userdata) {
2344 
2345         Service *s = data;
2346         int r;
2347 
2348         assert(filename);
2349         assert(lvalue);
2350         assert(rvalue);
2351         assert(data);
2352 
2353         for (const char *p = rvalue;;) {
2354                 _cleanup_free_ char *word = NULL, *k = NULL;
2355 
2356                 r = extract_first_word(&p, &word, NULL, 0);
2357                 if (r == -ENOMEM)
2358                         return log_oom();
2359                 if (r < 0) {
2360                         log_syntax(unit, LOG_WARNING, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
2361                         return 0;
2362                 }
2363                 if (r == 0)
2364                         return 0;
2365 
2366                 r = unit_name_printf(UNIT(s), word, &k);
2367                 if (r < 0) {
2368                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
2369                         continue;
2370                 }
2371 
2372                 if (!endswith(k, ".socket")) {
2373                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
2374                         continue;
2375                 }
2376 
2377                 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
2378                 if (r < 0)
2379                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2380 
2381                 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
2382                 if (r < 0)
2383                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2384         }
2385 }
2386 
config_parse_bus_name(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2387 int config_parse_bus_name(
2388                 const char *unit,
2389                 const char *filename,
2390                 unsigned line,
2391                 const char *section,
2392                 unsigned section_line,
2393                 const char *lvalue,
2394                 int ltype,
2395                 const char *rvalue,
2396                 void *data,
2397                 void *userdata) {
2398 
2399         _cleanup_free_ char *k = NULL;
2400         const Unit *u = userdata;
2401         int r;
2402 
2403         assert(filename);
2404         assert(lvalue);
2405         assert(rvalue);
2406         assert(u);
2407 
2408         r = unit_full_printf_full(u, rvalue, SD_BUS_MAXIMUM_NAME_LENGTH, &k);
2409         if (r < 0) {
2410                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2411                 return 0;
2412         }
2413 
2414         if (!sd_bus_service_name_is_valid(k)) {
2415                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid bus name, ignoring: %s", k);
2416                 return 0;
2417         }
2418 
2419         return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
2420 }
2421 
config_parse_service_timeout(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2422 int config_parse_service_timeout(
2423                 const char *unit,
2424                 const char *filename,
2425                 unsigned line,
2426                 const char *section,
2427                 unsigned section_line,
2428                 const char *lvalue,
2429                 int ltype,
2430                 const char *rvalue,
2431                 void *data,
2432                 void *userdata) {
2433 
2434         Service *s = userdata;
2435         usec_t usec;
2436         int r;
2437 
2438         assert(filename);
2439         assert(lvalue);
2440         assert(rvalue);
2441         assert(s);
2442 
2443         /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
2444 
2445         /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2446          * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2447          * all other timeouts. */
2448         r = parse_sec_fix_0(rvalue, &usec);
2449         if (r < 0) {
2450                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2451                 return 0;
2452         }
2453 
2454         s->start_timeout_defined = true;
2455         s->timeout_start_usec = usec;
2456 
2457         if (streq(lvalue, "TimeoutSec"))
2458                 s->timeout_stop_usec = usec;
2459 
2460         return 0;
2461 }
2462 
config_parse_timeout_abort(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2463 int config_parse_timeout_abort(
2464                 const char *unit,
2465                 const char *filename,
2466                 unsigned line,
2467                 const char *section,
2468                 unsigned section_line,
2469                 const char *lvalue,
2470                 int ltype,
2471                 const char *rvalue,
2472                 void *data,
2473                 void *userdata) {
2474 
2475         usec_t *ret = data;
2476         int r;
2477 
2478         assert(filename);
2479         assert(lvalue);
2480         assert(rvalue);
2481         assert(ret);
2482 
2483         /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
2484 
2485         if (isempty(rvalue)) {
2486                 *ret = 0;
2487                 return 0; /* "not set" */
2488         }
2489 
2490         r = parse_sec(rvalue, ret);
2491         if (r < 0)
2492                 return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
2493 
2494         return 1; /* "set" */
2495 }
2496 
config_parse_service_timeout_abort(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2497 int config_parse_service_timeout_abort(
2498                 const char *unit,
2499                 const char *filename,
2500                 unsigned line,
2501                 const char *section,
2502                 unsigned section_line,
2503                 const char *lvalue,
2504                 int ltype,
2505                 const char *rvalue,
2506                 void *data,
2507                 void *userdata) {
2508 
2509         Service *s = userdata;
2510         int r;
2511 
2512         assert(s);
2513 
2514         r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
2515                                        &s->timeout_abort_usec, s);
2516         if (r >= 0)
2517                 s->timeout_abort_set = r;
2518         return 0;
2519 }
2520 
config_parse_sec_fix_0(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2521 int config_parse_sec_fix_0(
2522                 const char *unit,
2523                 const char *filename,
2524                 unsigned line,
2525                 const char *section,
2526                 unsigned section_line,
2527                 const char *lvalue,
2528                 int ltype,
2529                 const char *rvalue,
2530                 void *data,
2531                 void *userdata) {
2532 
2533         usec_t *usec = data;
2534         int r;
2535 
2536         assert(filename);
2537         assert(lvalue);
2538         assert(rvalue);
2539         assert(usec);
2540 
2541         /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2542          * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2543          * timeout. */
2544 
2545         r = parse_sec_fix_0(rvalue, usec);
2546         if (r < 0)
2547                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2548 
2549         return 0;
2550 }
2551 
config_parse_user_group_compat(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2552 int config_parse_user_group_compat(
2553                 const char *unit,
2554                 const char *filename,
2555                 unsigned line,
2556                 const char *section,
2557                 unsigned section_line,
2558                 const char *lvalue,
2559                 int ltype,
2560                 const char *rvalue,
2561                 void *data,
2562                 void *userdata) {
2563 
2564         _cleanup_free_ char *k = NULL;
2565         char **user = data;
2566         const Unit *u = userdata;
2567         int r;
2568 
2569         assert(filename);
2570         assert(lvalue);
2571         assert(rvalue);
2572         assert(u);
2573 
2574         if (isempty(rvalue)) {
2575                 *user = mfree(*user);
2576                 return 0;
2577         }
2578 
2579         r = unit_full_printf(u, rvalue, &k);
2580         if (r < 0) {
2581                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2582                 return -ENOEXEC;
2583         }
2584 
2585         if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2586                 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2587                 return -ENOEXEC;
2588         }
2589 
2590         if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
2591                 log_struct(LOG_NOTICE,
2592                            "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
2593                            "UNIT=%s", unit,
2594                            "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
2595                            "OFFENDING_USER=%s", k,
2596                            "CONFIG_FILE=%s", filename,
2597                            "CONFIG_LINE=%u", line);
2598 
2599         return free_and_replace(*user, k);
2600 }
2601 
config_parse_user_group_strv_compat(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2602 int config_parse_user_group_strv_compat(
2603                 const char *unit,
2604                 const char *filename,
2605                 unsigned line,
2606                 const char *section,
2607                 unsigned section_line,
2608                 const char *lvalue,
2609                 int ltype,
2610                 const char *rvalue,
2611                 void *data,
2612                 void *userdata) {
2613 
2614         char ***users = data;
2615         const Unit *u = userdata;
2616         int r;
2617 
2618         assert(filename);
2619         assert(lvalue);
2620         assert(rvalue);
2621         assert(u);
2622 
2623         if (isempty(rvalue)) {
2624                 *users = strv_free(*users);
2625                 return 0;
2626         }
2627 
2628         for (const char *p = rvalue;;) {
2629                 _cleanup_free_ char *word = NULL, *k = NULL;
2630 
2631                 r = extract_first_word(&p, &word, NULL, 0);
2632                 if (r == -ENOMEM)
2633                         return log_oom();
2634                 if (r < 0) {
2635                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2636                         return -ENOEXEC;
2637                 }
2638                 if (r == 0)
2639                         return 0;
2640 
2641                 r = unit_full_printf(u, word, &k);
2642                 if (r < 0) {
2643                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2644                         return -ENOEXEC;
2645                 }
2646 
2647                 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2648                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2649                         return -ENOEXEC;
2650                 }
2651 
2652                 r = strv_push(users, k);
2653                 if (r < 0)
2654                         return log_oom();
2655 
2656                 k = NULL;
2657         }
2658 }
2659 
config_parse_working_directory(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2660 int config_parse_working_directory(
2661                 const char *unit,
2662                 const char *filename,
2663                 unsigned line,
2664                 const char *section,
2665                 unsigned section_line,
2666                 const char *lvalue,
2667                 int ltype,
2668                 const char *rvalue,
2669                 void *data,
2670                 void *userdata) {
2671 
2672         ExecContext *c = data;
2673         const Unit *u = userdata;
2674         bool missing_ok;
2675         int r;
2676 
2677         assert(filename);
2678         assert(lvalue);
2679         assert(rvalue);
2680         assert(c);
2681         assert(u);
2682 
2683         if (isempty(rvalue)) {
2684                 c->working_directory_home = false;
2685                 c->working_directory = mfree(c->working_directory);
2686                 return 0;
2687         }
2688 
2689         if (rvalue[0] == '-') {
2690                 missing_ok = true;
2691                 rvalue++;
2692         } else
2693                 missing_ok = false;
2694 
2695         if (streq(rvalue, "~")) {
2696                 c->working_directory_home = true;
2697                 c->working_directory = mfree(c->working_directory);
2698         } else {
2699                 _cleanup_free_ char *k = NULL;
2700 
2701                 r = unit_path_printf(u, rvalue, &k);
2702                 if (r < 0) {
2703                         log_syntax(unit, missing_ok ? LOG_WARNING : LOG_ERR, filename, line, r,
2704                                    "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2705                                    rvalue, missing_ok ? ", ignoring" : "");
2706                         return missing_ok ? 0 : -ENOEXEC;
2707                 }
2708 
2709                 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue);
2710                 if (r < 0)
2711                         return missing_ok ? 0 : -ENOEXEC;
2712 
2713                 c->working_directory_home = false;
2714                 free_and_replace(c->working_directory, k);
2715         }
2716 
2717         c->working_directory_missing_ok = missing_ok;
2718         return 0;
2719 }
2720 
config_parse_unit_env_file(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2721 int config_parse_unit_env_file(const char *unit,
2722                                const char *filename,
2723                                unsigned line,
2724                                const char *section,
2725                                unsigned section_line,
2726                                const char *lvalue,
2727                                int ltype,
2728                                const char *rvalue,
2729                                void *data,
2730                                void *userdata) {
2731 
2732         char ***env = data;
2733         const Unit *u = userdata;
2734         _cleanup_free_ char *n = NULL;
2735         int r;
2736 
2737         assert(filename);
2738         assert(lvalue);
2739         assert(rvalue);
2740         assert(data);
2741 
2742         if (isempty(rvalue)) {
2743                 /* Empty assignment frees the list */
2744                 *env = strv_free(*env);
2745                 return 0;
2746         }
2747 
2748         r = unit_full_printf_full(u, rvalue, PATH_MAX, &n);
2749         if (r < 0) {
2750                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2751                 return 0;
2752         }
2753 
2754         r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2755         if (r < 0)
2756                 return 0;
2757 
2758         r = strv_push(env, n);
2759         if (r < 0)
2760                 return log_oom();
2761 
2762         n = NULL;
2763 
2764         return 0;
2765 }
2766 
config_parse_environ(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2767 int config_parse_environ(
2768                 const char *unit,
2769                 const char *filename,
2770                 unsigned line,
2771                 const char *section,
2772                 unsigned section_line,
2773                 const char *lvalue,
2774                 int ltype,
2775                 const char *rvalue,
2776                 void *data,
2777                 void *userdata) {
2778 
2779         const Unit *u = userdata;
2780         char ***env = data;
2781         int r;
2782 
2783         assert(filename);
2784         assert(lvalue);
2785         assert(rvalue);
2786         assert(data);
2787 
2788         if (isempty(rvalue)) {
2789                 /* Empty assignment resets the list */
2790                 *env = strv_free(*env);
2791                 return 0;
2792         }
2793 
2794         for (const char *p = rvalue;; ) {
2795                 _cleanup_free_ char *word = NULL, *resolved = NULL;
2796 
2797                 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2798                 if (r == -ENOMEM)
2799                         return log_oom();
2800                 if (r < 0) {
2801                         log_syntax(unit, LOG_WARNING, filename, line, r,
2802                                    "Invalid syntax, ignoring: %s", rvalue);
2803                         return 0;
2804                 }
2805                 if (r == 0)
2806                         return 0;
2807 
2808                 if (u)
2809                         r = unit_env_printf(u, word, &resolved);
2810                 else
2811                         r = specifier_printf(word, sc_arg_max(), system_and_tmp_specifier_table, NULL, NULL, &resolved);
2812                 if (r < 0) {
2813                         log_syntax(unit, LOG_WARNING, filename, line, r,
2814                                    "Failed to resolve specifiers in %s, ignoring: %m", word);
2815                         continue;
2816                 }
2817 
2818                 if (!env_assignment_is_valid(resolved)) {
2819                         log_syntax(unit, LOG_WARNING, filename, line, 0,
2820                                    "Invalid environment assignment, ignoring: %s", resolved);
2821                         continue;
2822                 }
2823 
2824                 r = strv_env_replace_consume(env, TAKE_PTR(resolved));
2825                 if (r < 0)
2826                         return log_error_errno(r, "Failed to update environment: %m");
2827         }
2828 }
2829 
config_parse_pass_environ(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2830 int config_parse_pass_environ(
2831                 const char *unit,
2832                 const char *filename,
2833                 unsigned line,
2834                 const char *section,
2835                 unsigned section_line,
2836                 const char *lvalue,
2837                 int ltype,
2838                 const char *rvalue,
2839                 void *data,
2840                 void *userdata) {
2841 
2842         _cleanup_strv_free_ char **n = NULL;
2843         const Unit *u = userdata;
2844         char*** passenv = data;
2845         size_t nlen = 0;
2846         int r;
2847 
2848         assert(filename);
2849         assert(lvalue);
2850         assert(rvalue);
2851         assert(data);
2852 
2853         if (isempty(rvalue)) {
2854                 /* Empty assignment resets the list */
2855                 *passenv = strv_free(*passenv);
2856                 return 0;
2857         }
2858 
2859         for (const char *p = rvalue;;) {
2860                 _cleanup_free_ char *word = NULL, *k = NULL;
2861 
2862                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2863                 if (r == -ENOMEM)
2864                         return log_oom();
2865                 if (r < 0) {
2866                         log_syntax(unit, LOG_WARNING, filename, line, r,
2867                                    "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2868                         break;
2869                 }
2870                 if (r == 0)
2871                         break;
2872 
2873                 if (u) {
2874                         r = unit_env_printf(u, word, &k);
2875                         if (r < 0) {
2876                                 log_syntax(unit, LOG_WARNING, filename, line, r,
2877                                            "Failed to resolve specifiers in %s, ignoring: %m", word);
2878                                 continue;
2879                         }
2880                 } else
2881                         k = TAKE_PTR(word);
2882 
2883                 if (!env_name_is_valid(k)) {
2884                         log_syntax(unit, LOG_WARNING, filename, line, 0,
2885                                    "Invalid environment name for %s, ignoring: %s", lvalue, k);
2886                         continue;
2887                 }
2888 
2889                 if (!GREEDY_REALLOC(n, nlen + 2))
2890                         return log_oom();
2891 
2892                 n[nlen++] = TAKE_PTR(k);
2893                 n[nlen] = NULL;
2894         }
2895 
2896         if (n) {
2897                 r = strv_extend_strv(passenv, n, true);
2898                 if (r < 0)
2899                         return log_oom();
2900         }
2901 
2902         return 0;
2903 }
2904 
config_parse_unset_environ(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2905 int config_parse_unset_environ(
2906                 const char *unit,
2907                 const char *filename,
2908                 unsigned line,
2909                 const char *section,
2910                 unsigned section_line,
2911                 const char *lvalue,
2912                 int ltype,
2913                 const char *rvalue,
2914                 void *data,
2915                 void *userdata) {
2916 
2917         _cleanup_strv_free_ char **n = NULL;
2918         char*** unsetenv = data;
2919         const Unit *u = userdata;
2920         size_t nlen = 0;
2921         int r;
2922 
2923         assert(filename);
2924         assert(lvalue);
2925         assert(rvalue);
2926         assert(data);
2927 
2928         if (isempty(rvalue)) {
2929                 /* Empty assignment resets the list */
2930                 *unsetenv = strv_free(*unsetenv);
2931                 return 0;
2932         }
2933 
2934         for (const char *p = rvalue;;) {
2935                 _cleanup_free_ char *word = NULL, *k = NULL;
2936 
2937                 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2938                 if (r == -ENOMEM)
2939                         return log_oom();
2940                 if (r < 0) {
2941                         log_syntax(unit, LOG_WARNING, filename, line, r,
2942                                    "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2943                         break;
2944                 }
2945                 if (r == 0)
2946                         break;
2947 
2948                 if (u) {
2949                         r = unit_env_printf(u, word, &k);
2950                         if (r < 0) {
2951                                 log_syntax(unit, LOG_WARNING, filename, line, r,
2952                                            "Failed to resolve unit specifiers in %s, ignoring: %m", word);
2953                                 continue;
2954                         }
2955                 } else
2956                         k = TAKE_PTR(word);
2957 
2958                 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
2959                         log_syntax(unit, LOG_WARNING, filename, line, 0,
2960                                    "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2961                         continue;
2962                 }
2963 
2964                 if (!GREEDY_REALLOC(n, nlen + 2))
2965                         return log_oom();
2966 
2967                 n[nlen++] = TAKE_PTR(k);
2968                 n[nlen] = NULL;
2969         }
2970 
2971         if (n) {
2972                 r = strv_extend_strv(unsetenv, n, true);
2973                 if (r < 0)
2974                         return log_oom();
2975         }
2976 
2977         return 0;
2978 }
2979 
config_parse_log_extra_fields(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)2980 int config_parse_log_extra_fields(
2981                 const char *unit,
2982                 const char *filename,
2983                 unsigned line,
2984                 const char *section,
2985                 unsigned section_line,
2986                 const char *lvalue,
2987                 int ltype,
2988                 const char *rvalue,
2989                 void *data,
2990                 void *userdata) {
2991 
2992         ExecContext *c = data;
2993         const Unit *u = userdata;
2994         int r;
2995 
2996         assert(filename);
2997         assert(lvalue);
2998         assert(rvalue);
2999         assert(c);
3000 
3001         if (isempty(rvalue)) {
3002                 exec_context_free_log_extra_fields(c);
3003                 return 0;
3004         }
3005 
3006         for (const char *p = rvalue;;) {
3007                 _cleanup_free_ char *word = NULL, *k = NULL;
3008                 struct iovec *t;
3009                 const char *eq;
3010 
3011                 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
3012                 if (r == -ENOMEM)
3013                         return log_oom();
3014                 if (r < 0) {
3015                         log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3016                         return 0;
3017                 }
3018                 if (r == 0)
3019                         return 0;
3020 
3021                 r = unit_full_printf(u, word, &k);
3022                 if (r < 0) {
3023                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
3024                         continue;
3025                 }
3026 
3027                 eq = strchr(k, '=');
3028                 if (!eq) {
3029                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k);
3030                         continue;
3031                 }
3032 
3033                 if (!journal_field_valid(k, eq-k, false)) {
3034                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field name is invalid, ignoring: %s", k);
3035                         continue;
3036                 }
3037 
3038                 t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
3039                 if (!t)
3040                         return log_oom();
3041 
3042                 c->log_extra_fields = t;
3043                 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
3044 
3045                 k = NULL;
3046         }
3047 }
3048 
config_parse_log_namespace(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3049 int config_parse_log_namespace(
3050                 const char *unit,
3051                 const char *filename,
3052                 unsigned line,
3053                 const char *section,
3054                 unsigned section_line,
3055                 const char *lvalue,
3056                 int ltype,
3057                 const char *rvalue,
3058                 void *data,
3059                 void *userdata) {
3060 
3061         _cleanup_free_ char *k = NULL;
3062         ExecContext *c = data;
3063         const Unit *u = userdata;
3064         int r;
3065 
3066         assert(filename);
3067         assert(lvalue);
3068         assert(rvalue);
3069         assert(c);
3070 
3071         if (isempty(rvalue)) {
3072                 c->log_namespace = mfree(c->log_namespace);
3073                 return 0;
3074         }
3075 
3076         r = unit_full_printf_full(u, rvalue, NAME_MAX, &k);
3077         if (r < 0) {
3078                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3079                 return 0;
3080         }
3081 
3082         if (!log_namespace_name_valid(k)) {
3083                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified log namespace name is not valid, ignoring: %s", k);
3084                 return 0;
3085         }
3086 
3087         free_and_replace(c->log_namespace, k);
3088         return 0;
3089 }
3090 
config_parse_unit_condition_path(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3091 int config_parse_unit_condition_path(
3092                 const char *unit,
3093                 const char *filename,
3094                 unsigned line,
3095                 const char *section,
3096                 unsigned section_line,
3097                 const char *lvalue,
3098                 int ltype,
3099                 const char *rvalue,
3100                 void *data,
3101                 void *userdata) {
3102 
3103         _cleanup_free_ char *p = NULL;
3104         Condition **list = data, *c;
3105         ConditionType t = ltype;
3106         bool trigger, negate;
3107         const Unit *u = userdata;
3108         int r;
3109 
3110         assert(filename);
3111         assert(lvalue);
3112         assert(rvalue);
3113         assert(data);
3114 
3115         if (isempty(rvalue)) {
3116                 /* Empty assignment resets the list */
3117                 *list = condition_free_list(*list);
3118                 return 0;
3119         }
3120 
3121         trigger = rvalue[0] == '|';
3122         if (trigger)
3123                 rvalue++;
3124 
3125         negate = rvalue[0] == '!';
3126         if (negate)
3127                 rvalue++;
3128 
3129         r = unit_path_printf(u, rvalue, &p);
3130         if (r < 0) {
3131                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3132                 return 0;
3133         }
3134 
3135         r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3136         if (r < 0)
3137                 return 0;
3138 
3139         c = condition_new(t, p, trigger, negate);
3140         if (!c)
3141                 return log_oom();
3142 
3143         LIST_PREPEND(conditions, *list, c);
3144         return 0;
3145 }
3146 
config_parse_unit_condition_string(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3147 int config_parse_unit_condition_string(
3148                 const char *unit,
3149                 const char *filename,
3150                 unsigned line,
3151                 const char *section,
3152                 unsigned section_line,
3153                 const char *lvalue,
3154                 int ltype,
3155                 const char *rvalue,
3156                 void *data,
3157                 void *userdata) {
3158 
3159         _cleanup_free_ char *s = NULL;
3160         Condition **list = data, *c;
3161         ConditionType t = ltype;
3162         bool trigger, negate;
3163         const Unit *u = userdata;
3164         int r;
3165 
3166         assert(filename);
3167         assert(lvalue);
3168         assert(rvalue);
3169         assert(data);
3170 
3171         if (isempty(rvalue)) {
3172                 /* Empty assignment resets the list */
3173                 *list = condition_free_list(*list);
3174                 return 0;
3175         }
3176 
3177         trigger = *rvalue == '|';
3178         if (trigger)
3179                 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3180 
3181         negate = *rvalue == '!';
3182         if (negate)
3183                 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3184 
3185         r = unit_full_printf(u, rvalue, &s);
3186         if (r < 0) {
3187                 log_syntax(unit, LOG_WARNING, filename, line, r,
3188                            "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
3189                 return 0;
3190         }
3191 
3192         c = condition_new(t, s, trigger, negate);
3193         if (!c)
3194                 return log_oom();
3195 
3196         LIST_PREPEND(conditions, *list, c);
3197         return 0;
3198 }
3199 
config_parse_unit_requires_mounts_for(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3200 int config_parse_unit_requires_mounts_for(
3201                 const char *unit,
3202                 const char *filename,
3203                 unsigned line,
3204                 const char *section,
3205                 unsigned section_line,
3206                 const char *lvalue,
3207                 int ltype,
3208                 const char *rvalue,
3209                 void *data,
3210                 void *userdata) {
3211 
3212         Unit *u = userdata;
3213         int r;
3214 
3215         assert(filename);
3216         assert(lvalue);
3217         assert(rvalue);
3218         assert(data);
3219 
3220         for (const char *p = rvalue;;) {
3221                 _cleanup_free_ char *word = NULL, *resolved = NULL;
3222 
3223                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3224                 if (r == -ENOMEM)
3225                         return log_oom();
3226                 if (r < 0) {
3227                         log_syntax(unit, LOG_WARNING, filename, line, r,
3228                                    "Invalid syntax, ignoring: %s", rvalue);
3229                         return 0;
3230                 }
3231                 if (r == 0)
3232                         return 0;
3233 
3234                 r = unit_path_printf(u, word, &resolved);
3235                 if (r < 0) {
3236                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
3237                         continue;
3238                 }
3239 
3240                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3241                 if (r < 0)
3242                         continue;
3243 
3244                 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
3245                 if (r < 0) {
3246                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
3247                         continue;
3248                 }
3249         }
3250 }
3251 
config_parse_documentation(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3252 int config_parse_documentation(
3253                 const char *unit,
3254                 const char *filename,
3255                 unsigned line,
3256                 const char *section,
3257                 unsigned section_line,
3258                 const char *lvalue,
3259                 int ltype,
3260                 const char *rvalue,
3261                 void *data,
3262                 void *userdata) {
3263 
3264         Unit *u = userdata;
3265         int r;
3266         char **a, **b;
3267 
3268         assert(filename);
3269         assert(lvalue);
3270         assert(rvalue);
3271         assert(u);
3272 
3273         if (isempty(rvalue)) {
3274                 /* Empty assignment resets the list */
3275                 u->documentation = strv_free(u->documentation);
3276                 return 0;
3277         }
3278 
3279         r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
3280                                           rvalue, data, userdata);
3281         if (r < 0)
3282                 return r;
3283 
3284         for (a = b = u->documentation; a && *a; a++) {
3285 
3286                 if (documentation_url_is_valid(*a))
3287                         *(b++) = *a;
3288                 else {
3289                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid URL, ignoring: %s", *a);
3290                         free(*a);
3291                 }
3292         }
3293         if (b)
3294                 *b = NULL;
3295 
3296         return 0;
3297 }
3298 
3299 #if HAVE_SECCOMP
config_parse_syscall_filter(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3300 int config_parse_syscall_filter(
3301                 const char *unit,
3302                 const char *filename,
3303                 unsigned line,
3304                 const char *section,
3305                 unsigned section_line,
3306                 const char *lvalue,
3307                 int ltype,
3308                 const char *rvalue,
3309                 void *data,
3310                 void *userdata) {
3311 
3312         ExecContext *c = data;
3313         _unused_ const Unit *u = userdata;
3314         bool invert = false;
3315         int r;
3316 
3317         assert(filename);
3318         assert(lvalue);
3319         assert(rvalue);
3320         assert(u);
3321 
3322         if (isempty(rvalue)) {
3323                 /* Empty assignment resets the list */
3324                 c->syscall_filter = hashmap_free(c->syscall_filter);
3325                 c->syscall_allow_list = false;
3326                 return 0;
3327         }
3328 
3329         if (rvalue[0] == '~') {
3330                 invert = true;
3331                 rvalue++;
3332         }
3333 
3334         if (!c->syscall_filter) {
3335                 c->syscall_filter = hashmap_new(NULL);
3336                 if (!c->syscall_filter)
3337                         return log_oom();
3338 
3339                 if (invert)
3340                         /* Allow everything but the ones listed */
3341                         c->syscall_allow_list = false;
3342                 else {
3343                         /* Allow nothing but the ones listed */
3344                         c->syscall_allow_list = true;
3345 
3346                         /* Accept default syscalls if we are on an allow_list */
3347                         r = seccomp_parse_syscall_filter(
3348                                         "@default", -1, c->syscall_filter,
3349                                         SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
3350                                         unit,
3351                                         NULL, 0);
3352                         if (r < 0)
3353                                 return r;
3354                 }
3355         }
3356 
3357         for (const char *p = rvalue;;) {
3358                 _cleanup_free_ char *word = NULL, *name = NULL;
3359                 int num;
3360 
3361                 r = extract_first_word(&p, &word, NULL, 0);
3362                 if (r == -ENOMEM)
3363                         return log_oom();
3364                 if (r < 0) {
3365                         log_syntax(unit, LOG_WARNING, filename, line, r,
3366                                    "Invalid syntax, ignoring: %s", rvalue);
3367                         return 0;
3368                 }
3369                 if (r == 0)
3370                         return 0;
3371 
3372                 r = parse_syscall_and_errno(word, &name, &num);
3373                 if (r < 0) {
3374                         log_syntax(unit, LOG_WARNING, filename, line, r,
3375                                    "Failed to parse syscall:errno, ignoring: %s", word);
3376                         continue;
3377                 }
3378                 if (!invert && num >= 0) {
3379                         log_syntax(unit, LOG_WARNING, filename, line, 0,
3380                                    "Allow-listed system calls cannot take error number, ignoring: %s", word);
3381                         continue;
3382                 }
3383 
3384                 r = seccomp_parse_syscall_filter(
3385                                 name, num, c->syscall_filter,
3386                                 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3387                                 (invert ? SECCOMP_PARSE_INVERT : 0)|
3388                                 (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3389                                 unit, filename, line);
3390                 if (r < 0)
3391                         return r;
3392         }
3393 }
3394 
config_parse_syscall_log(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3395 int config_parse_syscall_log(
3396                 const char *unit,
3397                 const char *filename,
3398                 unsigned line,
3399                 const char *section,
3400                 unsigned section_line,
3401                 const char *lvalue,
3402                 int ltype,
3403                 const char *rvalue,
3404                 void *data,
3405                 void *userdata) {
3406 
3407         ExecContext *c = data;
3408         _unused_ const Unit *u = userdata;
3409         bool invert = false;
3410         const char *p;
3411         int r;
3412 
3413         assert(filename);
3414         assert(lvalue);
3415         assert(rvalue);
3416         assert(u);
3417 
3418         if (isempty(rvalue)) {
3419                 /* Empty assignment resets the list */
3420                 c->syscall_log = hashmap_free(c->syscall_log);
3421                 c->syscall_log_allow_list = false;
3422                 return 0;
3423         }
3424 
3425         if (rvalue[0] == '~') {
3426                 invert = true;
3427                 rvalue++;
3428         }
3429 
3430         if (!c->syscall_log) {
3431                 c->syscall_log = hashmap_new(NULL);
3432                 if (!c->syscall_log)
3433                         return log_oom();
3434 
3435                 if (invert)
3436                         /* Log everything but the ones listed */
3437                         c->syscall_log_allow_list = false;
3438                 else
3439                         /* Log nothing but the ones listed */
3440                         c->syscall_log_allow_list = true;
3441         }
3442 
3443         p = rvalue;
3444         for (;;) {
3445                 _cleanup_free_ char *word = NULL;
3446 
3447                 r = extract_first_word(&p, &word, NULL, 0);
3448                 if (r == -ENOMEM)
3449                         return log_oom();
3450                 if (r < 0) {
3451                         log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3452                         return 0;
3453                 }
3454                 if (r == 0)
3455                         return 0;
3456 
3457                 r = seccomp_parse_syscall_filter(
3458                                 word, -1, c->syscall_log,
3459                                 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3460                                 (invert ? SECCOMP_PARSE_INVERT : 0)|
3461                                 (c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3462                                 unit, filename, line);
3463                 if (r < 0)
3464                         return r;
3465         }
3466 }
3467 
config_parse_syscall_archs(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3468 int config_parse_syscall_archs(
3469                 const char *unit,
3470                 const char *filename,
3471                 unsigned line,
3472                 const char *section,
3473                 unsigned section_line,
3474                 const char *lvalue,
3475                 int ltype,
3476                 const char *rvalue,
3477                 void *data,
3478                 void *userdata) {
3479 
3480         Set **archs = data;
3481         int r;
3482 
3483         if (isempty(rvalue)) {
3484                 *archs = set_free(*archs);
3485                 return 0;
3486         }
3487 
3488         for (const char *p = rvalue;;) {
3489                 _cleanup_free_ char *word = NULL;
3490                 uint32_t a;
3491 
3492                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3493                 if (r == -ENOMEM)
3494                         return log_oom();
3495                 if (r < 0) {
3496                         log_syntax(unit, LOG_WARNING, filename, line, r,
3497                                    "Invalid syntax, ignoring: %s", rvalue);
3498                         return 0;
3499                 }
3500                 if (r == 0)
3501                         return 0;
3502 
3503                 r = seccomp_arch_from_string(word, &a);
3504                 if (r < 0) {
3505                         log_syntax(unit, LOG_WARNING, filename, line, r,
3506                                    "Failed to parse system call architecture \"%s\", ignoring: %m", word);
3507                         continue;
3508                 }
3509 
3510                 r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
3511                 if (r < 0)
3512                         return log_oom();
3513         }
3514 }
3515 
config_parse_syscall_errno(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3516 int config_parse_syscall_errno(
3517                 const char *unit,
3518                 const char *filename,
3519                 unsigned line,
3520                 const char *section,
3521                 unsigned section_line,
3522                 const char *lvalue,
3523                 int ltype,
3524                 const char *rvalue,
3525                 void *data,
3526                 void *userdata) {
3527 
3528         ExecContext *c = data;
3529         int e;
3530 
3531         assert(filename);
3532         assert(lvalue);
3533         assert(rvalue);
3534 
3535         if (isempty(rvalue) || streq(rvalue, "kill")) {
3536                 /* Empty assignment resets to KILL */
3537                 c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
3538                 return 0;
3539         }
3540 
3541         e = parse_errno(rvalue);
3542         if (e < 0) {
3543                 log_syntax(unit, LOG_WARNING, filename, line, e, "Failed to parse error number, ignoring: %s", rvalue);
3544                 return 0;
3545         }
3546         if (e == 0) {
3547                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid error number, ignoring: %s", rvalue);
3548                 return 0;
3549         }
3550 
3551         c->syscall_errno = e;
3552         return 0;
3553 }
3554 
config_parse_address_families(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3555 int config_parse_address_families(
3556                 const char *unit,
3557                 const char *filename,
3558                 unsigned line,
3559                 const char *section,
3560                 unsigned section_line,
3561                 const char *lvalue,
3562                 int ltype,
3563                 const char *rvalue,
3564                 void *data,
3565                 void *userdata) {
3566 
3567         ExecContext *c = data;
3568         bool invert = false;
3569         int r;
3570 
3571         assert(filename);
3572         assert(lvalue);
3573         assert(rvalue);
3574 
3575         if (isempty(rvalue)) {
3576                 /* Empty assignment resets the list */
3577                 c->address_families = set_free(c->address_families);
3578                 c->address_families_allow_list = false;
3579                 return 0;
3580         }
3581 
3582         if (streq(rvalue, "none")) {
3583                 /* Forbid all address families. */
3584                 c->address_families = set_free(c->address_families);
3585                 c->address_families_allow_list = true;
3586                 return 0;
3587         }
3588 
3589         if (rvalue[0] == '~') {
3590                 invert = true;
3591                 rvalue++;
3592         }
3593 
3594         if (!c->address_families) {
3595                 c->address_families = set_new(NULL);
3596                 if (!c->address_families)
3597                         return log_oom();
3598 
3599                 c->address_families_allow_list = !invert;
3600         }
3601 
3602         for (const char *p = rvalue;;) {
3603                 _cleanup_free_ char *word = NULL;
3604                 int af;
3605 
3606                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3607                 if (r == -ENOMEM)
3608                         return log_oom();
3609                 if (r < 0) {
3610                         log_syntax(unit, LOG_WARNING, filename, line, r,
3611                                    "Invalid syntax, ignoring: %s", rvalue);
3612                         return 0;
3613                 }
3614                 if (r == 0)
3615                         return 0;
3616 
3617                 af = af_from_name(word);
3618                 if (af < 0) {
3619                         log_syntax(unit, LOG_WARNING, filename, line, af,
3620                                    "Failed to parse address family, ignoring: %s", word);
3621                         continue;
3622                 }
3623 
3624                 /* If we previously wanted to forbid an address family and now
3625                  * we want to allow it, then just remove it from the list.
3626                  */
3627                 if (!invert == c->address_families_allow_list)  {
3628                         r = set_put(c->address_families, INT_TO_PTR(af));
3629                         if (r < 0)
3630                                 return log_oom();
3631                 } else
3632                         set_remove(c->address_families, INT_TO_PTR(af));
3633         }
3634 }
3635 
config_parse_restrict_namespaces(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3636 int config_parse_restrict_namespaces(
3637                 const char *unit,
3638                 const char *filename,
3639                 unsigned line,
3640                 const char *section,
3641                 unsigned section_line,
3642                 const char *lvalue,
3643                 int ltype,
3644                 const char *rvalue,
3645                 void *data,
3646                 void *userdata) {
3647 
3648         ExecContext *c = data;
3649         unsigned long flags;
3650         bool invert = false;
3651         int r;
3652 
3653         if (isempty(rvalue)) {
3654                 /* Reset to the default. */
3655                 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3656                 return 0;
3657         }
3658 
3659         /* Boolean parameter ignores the previous settings */
3660         r = parse_boolean(rvalue);
3661         if (r > 0) {
3662                 c->restrict_namespaces = 0;
3663                 return 0;
3664         } else if (r == 0) {
3665                 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3666                 return 0;
3667         }
3668 
3669         if (rvalue[0] == '~') {
3670                 invert = true;
3671                 rvalue++;
3672         }
3673 
3674         /* Not a boolean argument, in this case it's a list of namespace types. */
3675         r = namespace_flags_from_string(rvalue, &flags);
3676         if (r < 0) {
3677                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3678                 return 0;
3679         }
3680 
3681         if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3682                 /* Initial assignment. Just set the value. */
3683                 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3684         else
3685                 /* Merge the value with the previous one. */
3686                 SET_FLAG(c->restrict_namespaces, flags, !invert);
3687 
3688         return 0;
3689 }
3690 #endif
3691 
config_parse_restrict_filesystems(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3692 int config_parse_restrict_filesystems(
3693                 const char *unit,
3694                 const char *filename,
3695                 unsigned line,
3696                 const char *section,
3697                 unsigned section_line,
3698                 const char *lvalue,
3699                 int ltype,
3700                 const char *rvalue,
3701                 void *data,
3702                 void *userdata) {
3703         ExecContext *c = data;
3704         bool invert = false;
3705         int r;
3706 
3707         assert(filename);
3708         assert(lvalue);
3709         assert(rvalue);
3710         assert(data);
3711 
3712         if (isempty(rvalue)) {
3713                 /* Empty assignment resets the list */
3714                 c->restrict_filesystems = set_free(c->restrict_filesystems);
3715                 c->restrict_filesystems_allow_list = false;
3716                 return 0;
3717         }
3718 
3719         if (rvalue[0] == '~') {
3720                 invert = true;
3721                 rvalue++;
3722         }
3723 
3724         if (!c->restrict_filesystems) {
3725                 if (invert)
3726                         /* Allow everything but the ones listed */
3727                         c->restrict_filesystems_allow_list = false;
3728                 else
3729                         /* Allow nothing but the ones listed */
3730                         c->restrict_filesystems_allow_list = true;
3731         }
3732 
3733         for (const char *p = rvalue;;) {
3734                 _cleanup_free_ char *word = NULL;
3735 
3736                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3737                 if (r == 0)
3738                         break;
3739                 if (r == -ENOMEM)
3740                         return log_oom();
3741                 if (r < 0) {
3742                         log_syntax(unit, LOG_WARNING, filename, line, r,
3743                                    "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
3744                         break;
3745                 }
3746 
3747                 r = lsm_bpf_parse_filesystem(
3748                               word,
3749                               &c->restrict_filesystems,
3750                               FILESYSTEM_PARSE_LOG|
3751                               (invert ? FILESYSTEM_PARSE_INVERT : 0)|
3752                               (c->restrict_filesystems_allow_list ? FILESYSTEM_PARSE_ALLOW_LIST : 0),
3753                               unit, filename, line);
3754 
3755                 if (r < 0)
3756                         return r;
3757         }
3758 
3759         return 0;
3760 }
3761 
config_parse_unit_slice(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3762 int config_parse_unit_slice(
3763                 const char *unit,
3764                 const char *filename,
3765                 unsigned line,
3766                 const char *section,
3767                 unsigned section_line,
3768                 const char *lvalue,
3769                 int ltype,
3770                 const char *rvalue,
3771                 void *data,
3772                 void *userdata) {
3773 
3774         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3775         _cleanup_free_ char *k = NULL;
3776         Unit *u = userdata, *slice;
3777         int r;
3778 
3779         assert(filename);
3780         assert(lvalue);
3781         assert(rvalue);
3782         assert(u);
3783 
3784         r = unit_name_printf(u, rvalue, &k);
3785         if (r < 0) {
3786                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3787                 return 0;
3788         }
3789 
3790         r = manager_load_unit(u->manager, k, NULL, &error, &slice);
3791         if (r < 0) {
3792                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
3793                 return 0;
3794         }
3795 
3796         r = unit_set_slice(u, slice);
3797         if (r < 0) {
3798                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
3799                 return 0;
3800         }
3801 
3802         return 0;
3803 }
3804 
config_parse_cpu_quota(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3805 int config_parse_cpu_quota(
3806                 const char *unit,
3807                 const char *filename,
3808                 unsigned line,
3809                 const char *section,
3810                 unsigned section_line,
3811                 const char *lvalue,
3812                 int ltype,
3813                 const char *rvalue,
3814                 void *data,
3815                 void *userdata) {
3816 
3817         CGroupContext *c = data;
3818         int r;
3819 
3820         assert(filename);
3821         assert(lvalue);
3822         assert(rvalue);
3823 
3824         if (isempty(rvalue)) {
3825                 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3826                 return 0;
3827         }
3828 
3829         r = parse_permyriad_unbounded(rvalue);
3830         if (r <= 0) {
3831                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
3832                 return 0;
3833         }
3834 
3835         c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 10000U;
3836         return 0;
3837 }
3838 
config_parse_allowed_cpuset(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3839 int config_parse_allowed_cpuset(
3840                 const char *unit,
3841                 const char *filename,
3842                 unsigned line,
3843                 const char *section,
3844                 unsigned section_line,
3845                 const char *lvalue,
3846                 int ltype,
3847                 const char *rvalue,
3848                 void *data,
3849                 void *userdata) {
3850 
3851         CPUSet *c = data;
3852 
3853         (void) parse_cpu_set_extend(rvalue, c, true, unit, filename, line, lvalue);
3854         return 0;
3855 }
3856 
config_parse_memory_limit(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3857 int config_parse_memory_limit(
3858                 const char *unit,
3859                 const char *filename,
3860                 unsigned line,
3861                 const char *section,
3862                 unsigned section_line,
3863                 const char *lvalue,
3864                 int ltype,
3865                 const char *rvalue,
3866                 void *data,
3867                 void *userdata) {
3868 
3869         CGroupContext *c = data;
3870         uint64_t bytes = CGROUP_LIMIT_MAX;
3871         int r;
3872 
3873         if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
3874                                                   "DefaultMemoryMin",
3875                                                   "MemoryLow",
3876                                                   "MemoryMin"))
3877                 bytes = CGROUP_LIMIT_MIN;
3878         else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3879 
3880                 r = parse_permyriad(rvalue);
3881                 if (r < 0) {
3882                         r = parse_size(rvalue, 1024, &bytes);
3883                         if (r < 0) {
3884                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
3885                                 return 0;
3886                         }
3887                 } else
3888                         bytes = physical_memory_scale(r, 10000U);
3889 
3890                 if (bytes >= UINT64_MAX ||
3891                     (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
3892                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
3893                         return 0;
3894                 }
3895         }
3896 
3897         if (streq(lvalue, "DefaultMemoryLow")) {
3898                 c->default_memory_low = bytes;
3899                 c->default_memory_low_set = true;
3900         } else if (streq(lvalue, "DefaultMemoryMin")) {
3901                 c->default_memory_min = bytes;
3902                 c->default_memory_min_set = true;
3903         } else if (streq(lvalue, "MemoryMin")) {
3904                 c->memory_min = bytes;
3905                 c->memory_min_set = true;
3906         } else if (streq(lvalue, "MemoryLow")) {
3907                 c->memory_low = bytes;
3908                 c->memory_low_set = true;
3909         } else if (streq(lvalue, "MemoryHigh"))
3910                 c->memory_high = bytes;
3911         else if (streq(lvalue, "MemoryMax"))
3912                 c->memory_max = bytes;
3913         else if (streq(lvalue, "MemorySwapMax"))
3914                 c->memory_swap_max = bytes;
3915         else if (streq(lvalue, "MemoryLimit"))
3916                 c->memory_limit = bytes;
3917         else
3918                 return -EINVAL;
3919 
3920         return 0;
3921 }
3922 
config_parse_tasks_max(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3923 int config_parse_tasks_max(
3924                 const char *unit,
3925                 const char *filename,
3926                 unsigned line,
3927                 const char *section,
3928                 unsigned section_line,
3929                 const char *lvalue,
3930                 int ltype,
3931                 const char *rvalue,
3932                 void *data,
3933                 void *userdata) {
3934 
3935         const Unit *u = userdata;
3936         TasksMax *tasks_max = data;
3937         uint64_t v;
3938         int r;
3939 
3940         if (isempty(rvalue)) {
3941                 *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
3942                 return 0;
3943         }
3944 
3945         if (streq(rvalue, "infinity")) {
3946                 *tasks_max = TASKS_MAX_UNSET;
3947                 return 0;
3948         }
3949 
3950         r = parse_permyriad(rvalue);
3951         if (r >= 0)
3952                 *tasks_max = (TasksMax) { r, 10000U }; /* r‱ */
3953         else {
3954                 r = safe_atou64(rvalue, &v);
3955                 if (r < 0) {
3956                         log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
3957                         return 0;
3958                 }
3959 
3960                 if (v <= 0 || v >= UINT64_MAX) {
3961                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
3962                         return 0;
3963                 }
3964 
3965                 *tasks_max = (TasksMax) { v };
3966         }
3967 
3968         return 0;
3969 }
3970 
config_parse_delegate(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)3971 int config_parse_delegate(
3972                 const char *unit,
3973                 const char *filename,
3974                 unsigned line,
3975                 const char *section,
3976                 unsigned section_line,
3977                 const char *lvalue,
3978                 int ltype,
3979                 const char *rvalue,
3980                 void *data,
3981                 void *userdata) {
3982 
3983         CGroupContext *c = data;
3984         UnitType t;
3985         int r;
3986 
3987         t = unit_name_to_type(unit);
3988         assert(t != _UNIT_TYPE_INVALID);
3989 
3990         if (!unit_vtable[t]->can_delegate) {
3991                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring.");
3992                 return 0;
3993         }
3994 
3995         /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3996          * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3997          * mask to delegate. */
3998 
3999         if (isempty(rvalue)) {
4000                 /* An empty string resets controllers and set Delegate=yes. */
4001                 c->delegate = true;
4002                 c->delegate_controllers = 0;
4003                 return 0;
4004         }
4005 
4006         r = parse_boolean(rvalue);
4007         if (r < 0) {
4008                 CGroupMask mask = 0;
4009 
4010                 for (const char *p = rvalue;;) {
4011                         _cleanup_free_ char *word = NULL;
4012                         CGroupController cc;
4013 
4014                         r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4015                         if (r == -ENOMEM)
4016                                 return log_oom();
4017                         if (r < 0) {
4018                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4019                                 return 0;
4020                         }
4021                         if (r == 0)
4022                                 break;
4023 
4024                         cc = cgroup_controller_from_string(word);
4025                         if (cc < 0) {
4026                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid controller name '%s', ignoring", word);
4027                                 continue;
4028                         }
4029 
4030                         mask |= CGROUP_CONTROLLER_TO_MASK(cc);
4031                 }
4032 
4033                 c->delegate = true;
4034                 c->delegate_controllers |= mask;
4035 
4036         } else if (r > 0) {
4037                 c->delegate = true;
4038                 c->delegate_controllers = _CGROUP_MASK_ALL;
4039         } else {
4040                 c->delegate = false;
4041                 c->delegate_controllers = 0;
4042         }
4043 
4044         return 0;
4045 }
4046 
config_parse_managed_oom_mode(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4047 int config_parse_managed_oom_mode(
4048                 const char *unit,
4049                 const char *filename,
4050                 unsigned line,
4051                 const char *section,
4052                 unsigned section_line,
4053                 const char *lvalue,
4054                 int ltype,
4055                 const char *rvalue,
4056                 void *data,
4057                 void *userdata) {
4058 
4059         ManagedOOMMode *mode = data, m;
4060         UnitType t;
4061 
4062         t = unit_name_to_type(unit);
4063         assert(t != _UNIT_TYPE_INVALID);
4064 
4065         if (!unit_vtable[t]->can_set_managed_oom)
4066                 return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
4067 
4068         if (isempty(rvalue)) {
4069                 *mode = MANAGED_OOM_AUTO;
4070                 return 0;
4071         }
4072 
4073         m = managed_oom_mode_from_string(rvalue);
4074         if (m < 0) {
4075                 log_syntax(unit, LOG_WARNING, filename, line, m, "Invalid syntax, ignoring: %s", rvalue);
4076                 return 0;
4077         }
4078 
4079         *mode = m;
4080         return 0;
4081 }
4082 
config_parse_managed_oom_mem_pressure_limit(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4083 int config_parse_managed_oom_mem_pressure_limit(
4084                 const char *unit,
4085                 const char *filename,
4086                 unsigned line,
4087                 const char *section,
4088                 unsigned section_line,
4089                 const char *lvalue,
4090                 int ltype,
4091                 const char *rvalue,
4092                 void *data,
4093                 void *userdata) {
4094 
4095         uint32_t *limit = data;
4096         UnitType t;
4097         int r;
4098 
4099         t = unit_name_to_type(unit);
4100         assert(t != _UNIT_TYPE_INVALID);
4101 
4102         if (!unit_vtable[t]->can_set_managed_oom)
4103                 return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
4104 
4105         if (isempty(rvalue)) {
4106                 *limit = 0;
4107                 return 0;
4108         }
4109 
4110         r = parse_permyriad(rvalue);
4111         if (r < 0) {
4112                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse memory pressure limit value, ignoring: %s", rvalue);
4113                 return 0;
4114         }
4115 
4116         /* Normalize to 2^32-1 == 100% */
4117         *limit = UINT32_SCALE_FROM_PERMYRIAD(r);
4118         return 0;
4119 }
4120 
config_parse_device_allow(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4121 int config_parse_device_allow(
4122                 const char *unit,
4123                 const char *filename,
4124                 unsigned line,
4125                 const char *section,
4126                 unsigned section_line,
4127                 const char *lvalue,
4128                 int ltype,
4129                 const char *rvalue,
4130                 void *data,
4131                 void *userdata) {
4132 
4133         _cleanup_free_ char *path = NULL, *resolved = NULL;
4134         CGroupContext *c = data;
4135         const char *p = rvalue;
4136         int r;
4137 
4138         if (isempty(rvalue)) {
4139                 while (c->device_allow)
4140                         cgroup_context_free_device_allow(c, c->device_allow);
4141 
4142                 return 0;
4143         }
4144 
4145         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4146         if (r == -ENOMEM)
4147                 return log_oom();
4148         if (r <= 0) {
4149                 log_syntax(unit, LOG_WARNING, filename, line, r,
4150                            "Failed to extract device path and rights from '%s', ignoring.", rvalue);
4151                 return 0;
4152         }
4153 
4154         r = unit_path_printf(userdata, path, &resolved);
4155         if (r < 0) {
4156                 log_syntax(unit, LOG_WARNING, filename, line, r,
4157                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4158                 return 0;
4159         }
4160 
4161         if (!STARTSWITH_SET(resolved, "block-", "char-")) {
4162 
4163                 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4164                 if (r < 0)
4165                         return 0;
4166 
4167                 if (!valid_device_node_path(resolved)) {
4168                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved);
4169                         return 0;
4170                 }
4171         }
4172 
4173         if (!isempty(p) && !in_charset(p, "rwm")) {
4174                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
4175                 return 0;
4176         }
4177 
4178         return cgroup_add_device_allow(c, resolved, p);
4179 }
4180 
config_parse_io_device_weight(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4181 int config_parse_io_device_weight(
4182                 const char *unit,
4183                 const char *filename,
4184                 unsigned line,
4185                 const char *section,
4186                 unsigned section_line,
4187                 const char *lvalue,
4188                 int ltype,
4189                 const char *rvalue,
4190                 void *data,
4191                 void *userdata) {
4192 
4193         _cleanup_free_ char *path = NULL, *resolved = NULL;
4194         CGroupIODeviceWeight *w;
4195         CGroupContext *c = data;
4196         const char *p = rvalue;
4197         uint64_t u;
4198         int r;
4199 
4200         assert(filename);
4201         assert(lvalue);
4202         assert(rvalue);
4203 
4204         if (isempty(rvalue)) {
4205                 while (c->io_device_weights)
4206                         cgroup_context_free_io_device_weight(c, c->io_device_weights);
4207 
4208                 return 0;
4209         }
4210 
4211         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4212         if (r == -ENOMEM)
4213                 return log_oom();
4214         if (r <= 0 || isempty(p)) {
4215                 log_syntax(unit, LOG_WARNING, filename, line, r,
4216                            "Failed to extract device path and weight from '%s', ignoring.", rvalue);
4217                 return 0;
4218         }
4219 
4220         r = unit_path_printf(userdata, path, &resolved);
4221         if (r < 0) {
4222                 log_syntax(unit, LOG_WARNING, filename, line, r,
4223                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4224                 return 0;
4225         }
4226 
4227         r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4228         if (r < 0)
4229                 return 0;
4230 
4231         r = cg_weight_parse(p, &u);
4232         if (r < 0) {
4233                 log_syntax(unit, LOG_WARNING, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p);
4234                 return 0;
4235         }
4236 
4237         assert(u != CGROUP_WEIGHT_INVALID);
4238 
4239         w = new0(CGroupIODeviceWeight, 1);
4240         if (!w)
4241                 return log_oom();
4242 
4243         w->path = TAKE_PTR(resolved);
4244         w->weight = u;
4245 
4246         LIST_PREPEND(device_weights, c->io_device_weights, w);
4247         return 0;
4248 }
4249 
config_parse_io_device_latency(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4250 int config_parse_io_device_latency(
4251                 const char *unit,
4252                 const char *filename,
4253                 unsigned line,
4254                 const char *section,
4255                 unsigned section_line,
4256                 const char *lvalue,
4257                 int ltype,
4258                 const char *rvalue,
4259                 void *data,
4260                 void *userdata) {
4261 
4262         _cleanup_free_ char *path = NULL, *resolved = NULL;
4263         CGroupIODeviceLatency *l;
4264         CGroupContext *c = data;
4265         const char *p = rvalue;
4266         usec_t usec;
4267         int r;
4268 
4269         assert(filename);
4270         assert(lvalue);
4271         assert(rvalue);
4272 
4273         if (isempty(rvalue)) {
4274                 while (c->io_device_latencies)
4275                         cgroup_context_free_io_device_latency(c, c->io_device_latencies);
4276 
4277                 return 0;
4278         }
4279 
4280         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4281         if (r == -ENOMEM)
4282                 return log_oom();
4283         if (r <= 0 || isempty(p)) {
4284                 log_syntax(unit, LOG_WARNING, filename, line, r,
4285                            "Failed to extract device path and latency from '%s', ignoring.", rvalue);
4286                 return 0;
4287         }
4288 
4289         r = unit_path_printf(userdata, path, &resolved);
4290         if (r < 0) {
4291                 log_syntax(unit, LOG_WARNING, filename, line, r,
4292                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4293                 return 0;
4294         }
4295 
4296         r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4297         if (r < 0)
4298                 return 0;
4299 
4300         r = parse_sec(p, &usec);
4301         if (r < 0) {
4302                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", p);
4303                 return 0;
4304         }
4305 
4306         l = new0(CGroupIODeviceLatency, 1);
4307         if (!l)
4308                 return log_oom();
4309 
4310         l->path = TAKE_PTR(resolved);
4311         l->target_usec = usec;
4312 
4313         LIST_PREPEND(device_latencies, c->io_device_latencies, l);
4314         return 0;
4315 }
4316 
config_parse_io_limit(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4317 int config_parse_io_limit(
4318                 const char *unit,
4319                 const char *filename,
4320                 unsigned line,
4321                 const char *section,
4322                 unsigned section_line,
4323                 const char *lvalue,
4324                 int ltype,
4325                 const char *rvalue,
4326                 void *data,
4327                 void *userdata) {
4328 
4329         _cleanup_free_ char *path = NULL, *resolved = NULL;
4330         CGroupIODeviceLimit *l = NULL;
4331         CGroupContext *c = data;
4332         CGroupIOLimitType type;
4333         const char *p = rvalue;
4334         uint64_t num;
4335         int r;
4336 
4337         assert(filename);
4338         assert(lvalue);
4339         assert(rvalue);
4340 
4341         type = cgroup_io_limit_type_from_string(lvalue);
4342         assert(type >= 0);
4343 
4344         if (isempty(rvalue)) {
4345                 LIST_FOREACH(device_limits, t, c->io_device_limits)
4346                         t->limits[type] = cgroup_io_limit_defaults[type];
4347                 return 0;
4348         }
4349 
4350         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4351         if (r == -ENOMEM)
4352                 return log_oom();
4353         if (r <= 0 || isempty(p)) {
4354                 log_syntax(unit, LOG_WARNING, filename, line, r,
4355                            "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4356                 return 0;
4357         }
4358 
4359         r = unit_path_printf(userdata, path, &resolved);
4360         if (r < 0) {
4361                 log_syntax(unit, LOG_WARNING, filename, line, r,
4362                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4363                 return 0;
4364         }
4365 
4366         r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4367         if (r < 0)
4368                 return 0;
4369 
4370         if (streq("infinity", p))
4371                 num = CGROUP_LIMIT_MAX;
4372         else {
4373                 r = parse_size(p, 1000, &num);
4374                 if (r < 0 || num <= 0) {
4375                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid IO limit '%s', ignoring.", p);
4376                         return 0;
4377                 }
4378         }
4379 
4380         LIST_FOREACH(device_limits, t, c->io_device_limits)
4381                 if (path_equal(resolved, t->path)) {
4382                         l = t;
4383                         break;
4384                 }
4385 
4386         if (!l) {
4387                 CGroupIOLimitType ttype;
4388 
4389                 l = new0(CGroupIODeviceLimit, 1);
4390                 if (!l)
4391                         return log_oom();
4392 
4393                 l->path = TAKE_PTR(resolved);
4394                 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
4395                         l->limits[ttype] = cgroup_io_limit_defaults[ttype];
4396 
4397                 LIST_PREPEND(device_limits, c->io_device_limits, l);
4398         }
4399 
4400         l->limits[type] = num;
4401 
4402         return 0;
4403 }
4404 
config_parse_blockio_device_weight(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4405 int config_parse_blockio_device_weight(
4406                 const char *unit,
4407                 const char *filename,
4408                 unsigned line,
4409                 const char *section,
4410                 unsigned section_line,
4411                 const char *lvalue,
4412                 int ltype,
4413                 const char *rvalue,
4414                 void *data,
4415                 void *userdata) {
4416 
4417         _cleanup_free_ char *path = NULL, *resolved = NULL;
4418         CGroupBlockIODeviceWeight *w;
4419         CGroupContext *c = data;
4420         const char *p = rvalue;
4421         uint64_t u;
4422         int r;
4423 
4424         assert(filename);
4425         assert(lvalue);
4426         assert(rvalue);
4427 
4428         if (isempty(rvalue)) {
4429                 while (c->blockio_device_weights)
4430                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
4431 
4432                 return 0;
4433         }
4434 
4435         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4436         if (r == -ENOMEM)
4437                 return log_oom();
4438         if (r <= 0 || isempty(p)) {
4439                 log_syntax(unit, LOG_WARNING, filename, line, r,
4440                            "Failed to extract device node and weight from '%s', ignoring.", rvalue);
4441                 return 0;
4442         }
4443 
4444         r = unit_path_printf(userdata, path, &resolved);
4445         if (r < 0) {
4446                 log_syntax(unit, LOG_WARNING, filename, line, r,
4447                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4448                 return 0;
4449         }
4450 
4451         r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4452         if (r < 0)
4453                 return 0;
4454 
4455         r = cg_blkio_weight_parse(p, &u);
4456         if (r < 0) {
4457                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p);
4458                 return 0;
4459         }
4460 
4461         assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
4462 
4463         w = new0(CGroupBlockIODeviceWeight, 1);
4464         if (!w)
4465                 return log_oom();
4466 
4467         w->path = TAKE_PTR(resolved);
4468         w->weight = u;
4469 
4470         LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4471         return 0;
4472 }
4473 
config_parse_blockio_bandwidth(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4474 int config_parse_blockio_bandwidth(
4475                 const char *unit,
4476                 const char *filename,
4477                 unsigned line,
4478                 const char *section,
4479                 unsigned section_line,
4480                 const char *lvalue,
4481                 int ltype,
4482                 const char *rvalue,
4483                 void *data,
4484                 void *userdata) {
4485 
4486         _cleanup_free_ char *path = NULL, *resolved = NULL;
4487         CGroupBlockIODeviceBandwidth *b = NULL;
4488         CGroupContext *c = data;
4489         const char *p = rvalue;
4490         uint64_t bytes;
4491         bool read;
4492         int r;
4493 
4494         assert(filename);
4495         assert(lvalue);
4496         assert(rvalue);
4497 
4498         read = streq("BlockIOReadBandwidth", lvalue);
4499 
4500         if (isempty(rvalue)) {
4501                 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
4502                         t->rbps = CGROUP_LIMIT_MAX;
4503                         t->wbps = CGROUP_LIMIT_MAX;
4504                 }
4505                 return 0;
4506         }
4507 
4508         r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4509         if (r == -ENOMEM)
4510                 return log_oom();
4511         if (r <= 0 || isempty(p)) {
4512                 log_syntax(unit, LOG_WARNING, filename, line, r,
4513                            "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4514                 return 0;
4515         }
4516 
4517         r = unit_path_printf(userdata, path, &resolved);
4518         if (r < 0) {
4519                 log_syntax(unit, LOG_WARNING, filename, line, r,
4520                            "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4521                 return 0;
4522         }
4523 
4524         r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4525         if (r < 0)
4526                 return 0;
4527 
4528         r = parse_size(p, 1000, &bytes);
4529         if (r < 0 || bytes <= 0) {
4530                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p);
4531                 return 0;
4532         }
4533 
4534         LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths)
4535                 if (path_equal(resolved, t->path)) {
4536                         b = t;
4537                         break;
4538                 }
4539 
4540         if (!b) {
4541                 b = new0(CGroupBlockIODeviceBandwidth, 1);
4542                 if (!b)
4543                         return log_oom();
4544 
4545                 b->path = TAKE_PTR(resolved);
4546                 b->rbps = CGROUP_LIMIT_MAX;
4547                 b->wbps = CGROUP_LIMIT_MAX;
4548 
4549                 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4550         }
4551 
4552         if (read)
4553                 b->rbps = bytes;
4554         else
4555                 b->wbps = bytes;
4556 
4557         return 0;
4558 }
4559 
config_parse_job_mode_isolate(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4560 int config_parse_job_mode_isolate(
4561                 const char *unit,
4562                 const char *filename,
4563                 unsigned line,
4564                 const char *section,
4565                 unsigned section_line,
4566                 const char *lvalue,
4567                 int ltype,
4568                 const char *rvalue,
4569                 void *data,
4570                 void *userdata) {
4571 
4572         JobMode *m = data;
4573         int r;
4574 
4575         assert(filename);
4576         assert(lvalue);
4577         assert(rvalue);
4578 
4579         r = parse_boolean(rvalue);
4580         if (r < 0) {
4581                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
4582                 return 0;
4583         }
4584 
4585         log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
4586 
4587         *m = r ? JOB_ISOLATE : JOB_REPLACE;
4588         return 0;
4589 }
4590 
config_parse_exec_directories(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4591 int config_parse_exec_directories(
4592                 const char *unit,
4593                 const char *filename,
4594                 unsigned line,
4595                 const char *section,
4596                 unsigned section_line,
4597                 const char *lvalue,
4598                 int ltype,
4599                 const char *rvalue,
4600                 void *data,
4601                 void *userdata) {
4602 
4603         ExecDirectory *ed = data;
4604         const Unit *u = userdata;
4605         int r;
4606 
4607         assert(filename);
4608         assert(lvalue);
4609         assert(rvalue);
4610         assert(data);
4611 
4612         if (isempty(rvalue)) {
4613                 /* Empty assignment resets the list */
4614                 exec_directory_done(ed);
4615                 return 0;
4616         }
4617 
4618         for (const char *p = rvalue;;) {
4619                 _cleanup_free_ char *tuple = NULL;
4620 
4621                 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
4622                 if (r == -ENOMEM)
4623                         return log_oom();
4624                 if (r < 0) {
4625                         log_syntax(unit, LOG_WARNING, filename, line, r,
4626                                    "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
4627                         return 0;
4628                 }
4629                 if (r == 0)
4630                         return 0;
4631 
4632                 _cleanup_free_ char *src = NULL, *dest = NULL;
4633                 const char *q = tuple;
4634                 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &src, &dest, NULL);
4635                 if (r == -ENOMEM)
4636                         return log_oom();
4637                 if (r <= 0) {
4638                         log_syntax(unit, LOG_WARNING, filename, line, r ?: SYNTHETIC_ERRNO(EINVAL),
4639                                    "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
4640                         return 0;
4641                 }
4642 
4643                 _cleanup_free_ char *sresolved = NULL;
4644                 r = unit_path_printf(u, src, &sresolved);
4645                 if (r < 0) {
4646                         log_syntax(unit, LOG_WARNING, filename, line, r,
4647                                    "Failed to resolve unit specifiers in \"%s\", ignoring: %m", src);
4648                         continue;
4649                 }
4650 
4651                 r = path_simplify_and_warn(sresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4652                 if (r < 0)
4653                         continue;
4654 
4655                 if (path_startswith(sresolved, "private")) {
4656                         log_syntax(unit, LOG_WARNING, filename, line, 0,
4657                                    "%s= path can't be 'private', ignoring assignment: %s", lvalue, tuple);
4658                         continue;
4659                 }
4660 
4661                 /* For State and Runtime directories we support an optional destination parameter, which
4662                  * will be used to create a symlink to the source. */
4663                 _cleanup_strv_free_ char **symlinks = NULL;
4664                 if (!isempty(dest)) {
4665                         _cleanup_free_ char *dresolved = NULL;
4666 
4667                         if (streq(lvalue, "ConfigurationDirectory")) {
4668                                 log_syntax(unit, LOG_WARNING, filename, line, 0,
4669                                            "Destination parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
4670                                 continue;
4671                         }
4672 
4673                         r = unit_path_printf(u, dest, &dresolved);
4674                         if (r < 0) {
4675                                 log_syntax(unit, LOG_WARNING, filename, line, r,
4676                                         "Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
4677                                 continue;
4678                         }
4679 
4680                         r = path_simplify_and_warn(dresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4681                         if (r < 0)
4682                                 continue;
4683 
4684                         r = strv_consume(&symlinks, TAKE_PTR(dresolved));
4685                         if (r < 0)
4686                                 return log_oom();
4687                 }
4688 
4689                 r = exec_directory_add(&ed->items, &ed->n_items, sresolved, symlinks);
4690                 if (r < 0)
4691                         return log_oom();
4692         }
4693 }
4694 
config_parse_set_credential(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4695 int config_parse_set_credential(
4696                 const char *unit,
4697                 const char *filename,
4698                 unsigned line,
4699                 const char *section,
4700                 unsigned section_line,
4701                 const char *lvalue,
4702                 int ltype,
4703                 const char *rvalue,
4704                 void *data,
4705                 void *userdata) {
4706 
4707         _cleanup_free_ char *word = NULL, *k = NULL;
4708         _cleanup_free_ void *d = NULL;
4709         ExecContext *context = data;
4710         ExecSetCredential *old;
4711         Unit *u = userdata;
4712         bool encrypted = ltype;
4713         const char *p = rvalue;
4714         size_t size;
4715         int r;
4716 
4717         assert(filename);
4718         assert(lvalue);
4719         assert(rvalue);
4720         assert(context);
4721 
4722         if (isempty(rvalue)) {
4723                 /* Empty assignment resets the list */
4724                 context->set_credentials = hashmap_free(context->set_credentials);
4725                 return 0;
4726         }
4727 
4728         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4729         if (r == -ENOMEM)
4730                 return log_oom();
4731         if (r <= 0 || !p) {
4732                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4733                 return 0;
4734         }
4735 
4736         r = unit_cred_printf(u, word, &k);
4737         if (r < 0) {
4738                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4739                 return 0;
4740         }
4741         if (!credential_name_valid(k)) {
4742                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4743                 return 0;
4744         }
4745 
4746         if (encrypted) {
4747                 r = unbase64mem_full(p, SIZE_MAX, true, &d, &size);
4748                 if (r < 0) {
4749                         log_syntax(unit, LOG_WARNING, filename, line, r, "Encrypted credential data not valid Base64 data, ignoring.");
4750                         return 0;
4751                 }
4752         } else {
4753                 char *unescaped;
4754                 ssize_t l;
4755 
4756                 /* We support escape codes here, so that users can insert trailing \n if they like */
4757                 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
4758                 if (l < 0) {
4759                         log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
4760                         return 0;
4761                 }
4762 
4763                 d = unescaped;
4764                 size = l;
4765         }
4766 
4767         old = hashmap_get(context->set_credentials, k);
4768         if (old) {
4769                 free_and_replace(old->data, d);
4770                 old->size = size;
4771                 old->encrypted = encrypted;
4772         } else {
4773                 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
4774 
4775                 sc = new(ExecSetCredential, 1);
4776                 if (!sc)
4777                         return log_oom();
4778 
4779                 *sc = (ExecSetCredential) {
4780                         .id = TAKE_PTR(k),
4781                         .data = TAKE_PTR(d),
4782                         .size = size,
4783                         .encrypted = encrypted,
4784                 };
4785 
4786                 r = hashmap_ensure_put(&context->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
4787                 if (r == -ENOMEM)
4788                         return log_oom();
4789                 if (r < 0) {
4790                         log_syntax(unit, LOG_WARNING, filename, line, r,
4791                                    "Duplicated credential value '%s', ignoring assignment: %s", sc->id, rvalue);
4792                         return 0;
4793                 }
4794 
4795                 TAKE_PTR(sc);
4796         }
4797 
4798         return 0;
4799 }
4800 
config_parse_load_credential(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4801 int config_parse_load_credential(
4802                 const char *unit,
4803                 const char *filename,
4804                 unsigned line,
4805                 const char *section,
4806                 unsigned section_line,
4807                 const char *lvalue,
4808                 int ltype,
4809                 const char *rvalue,
4810                 void *data,
4811                 void *userdata) {
4812 
4813         _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
4814         ExecContext *context = data;
4815         ExecLoadCredential *old;
4816         bool encrypted = ltype;
4817         Unit *u = userdata;
4818         const char *p;
4819         int r;
4820 
4821         assert(filename);
4822         assert(lvalue);
4823         assert(rvalue);
4824         assert(context);
4825 
4826         if (isempty(rvalue)) {
4827                 /* Empty assignment resets the list */
4828                 context->load_credentials = hashmap_free(context->load_credentials);
4829                 return 0;
4830         }
4831 
4832         p = rvalue;
4833         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4834         if (r == -ENOMEM)
4835                 return log_oom();
4836         if (r <= 0) {
4837                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4838                 return 0;
4839         }
4840 
4841         r = unit_cred_printf(u, word, &k);
4842         if (r < 0) {
4843                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4844                 return 0;
4845         }
4846         if (!credential_name_valid(k)) {
4847                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4848                 return 0;
4849         }
4850 
4851         if (isempty(p)) {
4852                 /* If only one field is specified take it as shortcut for inheriting a credential named
4853                  * the same way from our parent */
4854                 q = strdup(k);
4855                 if (!q)
4856                         return log_oom();
4857         } else {
4858                 r = unit_path_printf(u, p, &q);
4859                 if (r < 0) {
4860                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", p);
4861                         return 0;
4862                 }
4863                 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
4864                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential source \"%s\" not valid, ignoring.", q);
4865                         return 0;
4866                 }
4867         }
4868 
4869         old = hashmap_get(context->load_credentials, k);
4870         if (old) {
4871                 free_and_replace(old->path, q);
4872                 old->encrypted = encrypted;
4873         } else {
4874                 _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
4875 
4876                 lc = new(ExecLoadCredential, 1);
4877                 if (!lc)
4878                         return log_oom();
4879 
4880                 *lc = (ExecLoadCredential) {
4881                         .id = TAKE_PTR(k),
4882                         .path = TAKE_PTR(q),
4883                         .encrypted = encrypted,
4884                 };
4885 
4886                 r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
4887                 if (r == -ENOMEM)
4888                         return log_oom();
4889                 if (r < 0) {
4890                         log_syntax(unit, LOG_WARNING, filename, line, r,
4891                                    "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
4892                         return 0;
4893                 }
4894 
4895                 TAKE_PTR(lc);
4896         }
4897 
4898         return 0;
4899 }
4900 
config_parse_set_status(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4901 int config_parse_set_status(
4902                 const char *unit,
4903                 const char *filename,
4904                 unsigned line,
4905                 const char *section,
4906                 unsigned section_line,
4907                 const char *lvalue,
4908                 int ltype,
4909                 const char *rvalue,
4910                 void *data,
4911                 void *userdata) {
4912 
4913         ExitStatusSet *status_set = data;
4914         int r;
4915 
4916         assert(filename);
4917         assert(lvalue);
4918         assert(rvalue);
4919         assert(status_set);
4920 
4921         /* Empty assignment resets the list */
4922         if (isempty(rvalue)) {
4923                 exit_status_set_free(status_set);
4924                 return 0;
4925         }
4926 
4927         for (const char *p = rvalue;;) {
4928                 _cleanup_free_ char *word = NULL;
4929                 Bitmap *bitmap;
4930 
4931                 r = extract_first_word(&p, &word, NULL, 0);
4932                 if (r == -ENOMEM)
4933                         return log_oom();
4934                 if (r < 0) {
4935                         log_syntax(unit, LOG_WARNING, filename, line, r,
4936                                    "Failed to parse %s=%s, ignoring: %m", lvalue, rvalue);
4937                         return 0;
4938                 }
4939                 if (r == 0)
4940                         return 0;
4941 
4942                 /* We need to call exit_status_from_string() first, because we want
4943                  * to parse numbers as exit statuses, not signals. */
4944 
4945                 r = exit_status_from_string(word);
4946                 if (r >= 0) {
4947                         assert(r >= 0 && r < 256);
4948                         bitmap = &status_set->status;
4949                 } else {
4950                         r = signal_from_string(word);
4951                         if (r < 0) {
4952                                 log_syntax(unit, LOG_WARNING, filename, line, r,
4953                                            "Failed to parse value, ignoring: %s", word);
4954                                 continue;
4955                         }
4956                         bitmap = &status_set->signal;
4957                 }
4958 
4959                 r = bitmap_set(bitmap, r);
4960                 if (r < 0)
4961                         log_syntax(unit, LOG_WARNING, filename, line, r,
4962                                    "Failed to set signal or status %s, ignoring: %m", word);
4963         }
4964 }
4965 
config_parse_namespace_path_strv(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)4966 int config_parse_namespace_path_strv(
4967                 const char *unit,
4968                 const char *filename,
4969                 unsigned line,
4970                 const char *section,
4971                 unsigned section_line,
4972                 const char *lvalue,
4973                 int ltype,
4974                 const char *rvalue,
4975                 void *data,
4976                 void *userdata) {
4977 
4978         const Unit *u = userdata;
4979         char*** sv = data;
4980         int r;
4981 
4982         assert(filename);
4983         assert(lvalue);
4984         assert(rvalue);
4985         assert(data);
4986 
4987         if (isempty(rvalue)) {
4988                 /* Empty assignment resets the list */
4989                 *sv = strv_free(*sv);
4990                 return 0;
4991         }
4992 
4993         for (const char *p = rvalue;;) {
4994                 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
4995                 const char *w;
4996                 bool ignore_enoent = false, shall_prefix = false;
4997 
4998                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4999                 if (r == -ENOMEM)
5000                         return log_oom();
5001                 if (r < 0) {
5002                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
5003                         return 0;
5004                 }
5005                 if (r == 0)
5006                         break;
5007 
5008                 w = word;
5009                 if (startswith(w, "-")) {
5010                         ignore_enoent = true;
5011                         w++;
5012                 }
5013                 if (startswith(w, "+")) {
5014                         shall_prefix = true;
5015                         w++;
5016                 }
5017 
5018                 r = unit_path_printf(u, w, &resolved);
5019                 if (r < 0) {
5020                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
5021                         continue;
5022                 }
5023 
5024                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5025                 if (r < 0)
5026                         continue;
5027 
5028                 joined = strjoin(ignore_enoent ? "-" : "",
5029                                  shall_prefix ? "+" : "",
5030                                  resolved);
5031 
5032                 r = strv_push(sv, joined);
5033                 if (r < 0)
5034                         return log_oom();
5035 
5036                 joined = NULL;
5037         }
5038 
5039         return 0;
5040 }
5041 
config_parse_temporary_filesystems(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5042 int config_parse_temporary_filesystems(
5043                 const char *unit,
5044                 const char *filename,
5045                 unsigned line,
5046                 const char *section,
5047                 unsigned section_line,
5048                 const char *lvalue,
5049                 int ltype,
5050                 const char *rvalue,
5051                 void *data,
5052                 void *userdata) {
5053 
5054         const Unit *u = userdata;
5055         ExecContext *c = data;
5056         int r;
5057 
5058         assert(filename);
5059         assert(lvalue);
5060         assert(rvalue);
5061         assert(data);
5062 
5063         if (isempty(rvalue)) {
5064                 /* Empty assignment resets the list */
5065                 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
5066                 c->temporary_filesystems = NULL;
5067                 c->n_temporary_filesystems = 0;
5068                 return 0;
5069         }
5070 
5071         for (const char *p = rvalue;;) {
5072                 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
5073                 const char *w;
5074 
5075                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5076                 if (r == -ENOMEM)
5077                         return log_oom();
5078                 if (r < 0) {
5079                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
5080                         return 0;
5081                 }
5082                 if (r == 0)
5083                         return 0;
5084 
5085                 w = word;
5086                 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
5087                 if (r == -ENOMEM)
5088                         return log_oom();
5089                 if (r < 0) {
5090                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", word);
5091                         continue;
5092                 }
5093                 if (r == 0) {
5094                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", word);
5095                         continue;
5096                 }
5097 
5098                 r = unit_path_printf(u, path, &resolved);
5099                 if (r < 0) {
5100                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
5101                         continue;
5102                 }
5103 
5104                 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5105                 if (r < 0)
5106                         continue;
5107 
5108                 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
5109                 if (r < 0)
5110                         return log_oom();
5111         }
5112 }
5113 
config_parse_bind_paths(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5114 int config_parse_bind_paths(
5115                 const char *unit,
5116                 const char *filename,
5117                 unsigned line,
5118                 const char *section,
5119                 unsigned section_line,
5120                 const char *lvalue,
5121                 int ltype,
5122                 const char *rvalue,
5123                 void *data,
5124                 void *userdata) {
5125 
5126         ExecContext *c = data;
5127         const Unit *u = userdata;
5128         int r;
5129 
5130         assert(filename);
5131         assert(lvalue);
5132         assert(rvalue);
5133         assert(data);
5134 
5135         if (isempty(rvalue)) {
5136                 /* Empty assignment resets the list */
5137                 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
5138                 c->bind_mounts = NULL;
5139                 c->n_bind_mounts = 0;
5140                 return 0;
5141         }
5142 
5143         for (const char *p = rvalue;;) {
5144                 _cleanup_free_ char *source = NULL, *destination = NULL;
5145                 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5146                 char *s = NULL, *d = NULL;
5147                 bool rbind = true, ignore_enoent = false;
5148 
5149                 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5150                 if (r == -ENOMEM)
5151                         return log_oom();
5152                 if (r < 0) {
5153                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5154                         return 0;
5155                 }
5156                 if (r == 0)
5157                         break;
5158 
5159                 r = unit_full_printf_full(u, source, PATH_MAX, &sresolved);
5160                 if (r < 0) {
5161                         log_syntax(unit, LOG_WARNING, filename, line, r,
5162                                    "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
5163                         continue;
5164                 }
5165 
5166                 s = sresolved;
5167                 if (s[0] == '-') {
5168                         ignore_enoent = true;
5169                         s++;
5170                 }
5171 
5172                 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5173                 if (r < 0)
5174                         continue;
5175 
5176                 /* Optionally, the destination is specified. */
5177                 if (p && p[-1] == ':') {
5178                         r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5179                         if (r == -ENOMEM)
5180                                 return log_oom();
5181                         if (r < 0) {
5182                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5183                                 return 0;
5184                         }
5185                         if (r == 0) {
5186                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
5187                                 continue;
5188                         }
5189 
5190                         r = unit_path_printf(u, destination, &dresolved);
5191                         if (r < 0) {
5192                                 log_syntax(unit, LOG_WARNING, filename, line, r,
5193                                            "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
5194                                 continue;
5195                         }
5196 
5197                         r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5198                         if (r < 0)
5199                                 continue;
5200 
5201                         d = dresolved;
5202 
5203                         /* Optionally, there's also a short option string specified */
5204                         if (p && p[-1] == ':') {
5205                                 _cleanup_free_ char *options = NULL;
5206 
5207                                 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
5208                                 if (r == -ENOMEM)
5209                                         return log_oom();
5210                                 if (r < 0) {
5211                                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
5212                                         return 0;
5213                                 }
5214 
5215                                 if (isempty(options) || streq(options, "rbind"))
5216                                         rbind = true;
5217                                 else if (streq(options, "norbind"))
5218                                         rbind = false;
5219                                 else {
5220                                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
5221                                         continue;
5222                                 }
5223                         }
5224                 } else
5225                         d = s;
5226 
5227                 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
5228                                    &(BindMount) {
5229                                            .source = s,
5230                                            .destination = d,
5231                                            .read_only = !!strstr(lvalue, "ReadOnly"),
5232                                            .recursive = rbind,
5233                                            .ignore_enoent = ignore_enoent,
5234                                    });
5235                 if (r < 0)
5236                         return log_oom();
5237         }
5238 
5239         return 0;
5240 }
5241 
config_parse_mount_images(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5242 int config_parse_mount_images(
5243                 const char *unit,
5244                 const char *filename,
5245                 unsigned line,
5246                 const char *section,
5247                 unsigned section_line,
5248                 const char *lvalue,
5249                 int ltype,
5250                 const char *rvalue,
5251                 void *data,
5252                 void *userdata) {
5253 
5254         ExecContext *c = data;
5255         const Unit *u = userdata;
5256         int r;
5257 
5258         assert(filename);
5259         assert(lvalue);
5260         assert(rvalue);
5261         assert(data);
5262 
5263         if (isempty(rvalue)) {
5264                 /* Empty assignment resets the list */
5265                 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
5266                 return 0;
5267         }
5268 
5269         for (const char *p = rvalue;;) {
5270                 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5271                 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
5272                 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5273                 const char *q = NULL;
5274                 char *s = NULL;
5275                 bool permissive = false;
5276 
5277                 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5278                 if (r == -ENOMEM)
5279                         return log_oom();
5280                 if (r < 0) {
5281                         log_syntax(unit, LOG_WARNING, filename, line, r,
5282                                    "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5283                         return 0;
5284                 }
5285                 if (r == 0)
5286                         return 0;
5287 
5288                 q = tuple;
5289                 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
5290                 if (r == -ENOMEM)
5291                         return log_oom();
5292                 if (r < 0) {
5293                         log_syntax(unit, LOG_WARNING, filename, line, r,
5294                                    "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5295                         return 0;
5296                 }
5297                 if (r == 0)
5298                         continue;
5299 
5300                 s = first;
5301                 if (s[0] == '-') {
5302                         permissive = true;
5303                         s++;
5304                 }
5305 
5306                 r = unit_path_printf(u, s, &sresolved);
5307                 if (r < 0) {
5308                         log_syntax(unit, LOG_WARNING, filename, line, r,
5309                                    "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5310                         continue;
5311                 }
5312 
5313                 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5314                 if (r < 0)
5315                         continue;
5316 
5317                 if (isempty(second)) {
5318                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
5319                         continue;
5320                 }
5321 
5322                 r = unit_path_printf(u, second, &dresolved);
5323                 if (r < 0) {
5324                         log_syntax(unit, LOG_WARNING, filename, line, r,
5325                                         "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
5326                         continue;
5327                 }
5328 
5329                 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5330                 if (r < 0)
5331                         continue;
5332 
5333                 for (;;) {
5334                         _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5335                         MountOptions *o = NULL;
5336                         PartitionDesignator partition_designator;
5337 
5338                         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5339                         if (r == -ENOMEM)
5340                                 return log_oom();
5341                         if (r < 0) {
5342                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5343                                 return 0;
5344                         }
5345                         if (r == 0)
5346                                 break;
5347                         /* Single set of options, applying to the root partition/single filesystem */
5348                         if (r == 1) {
5349                                 r = unit_full_printf(u, partition, &mount_options_resolved);
5350                                 if (r < 0) {
5351                                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
5352                                         continue;
5353                                 }
5354 
5355                                 o = new(MountOptions, 1);
5356                                 if (!o)
5357                                         return log_oom();
5358                                 *o = (MountOptions) {
5359                                         .partition_designator = PARTITION_ROOT,
5360                                         .options = TAKE_PTR(mount_options_resolved),
5361                                 };
5362                                 LIST_APPEND(mount_options, options, o);
5363 
5364                                 break;
5365                         }
5366 
5367                         partition_designator = partition_designator_from_string(partition);
5368                         if (partition_designator < 0) {
5369                                 log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
5370                                            "Invalid partition name %s, ignoring", partition);
5371                                 continue;
5372                         }
5373                         r = unit_full_printf(u, mount_options, &mount_options_resolved);
5374                         if (r < 0) {
5375                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5376                                 continue;
5377                         }
5378 
5379                         o = new(MountOptions, 1);
5380                         if (!o)
5381                                 return log_oom();
5382                         *o = (MountOptions) {
5383                                 .partition_designator = partition_designator,
5384                                 .options = TAKE_PTR(mount_options_resolved),
5385                         };
5386                         LIST_APPEND(mount_options, options, o);
5387                 }
5388 
5389                 r = mount_image_add(&c->mount_images, &c->n_mount_images,
5390                                     &(MountImage) {
5391                                             .source = sresolved,
5392                                             .destination = dresolved,
5393                                             .mount_options = options,
5394                                             .ignore_enoent = permissive,
5395                                             .type = MOUNT_IMAGE_DISCRETE,
5396                                     });
5397                 if (r < 0)
5398                         return log_oom();
5399         }
5400 }
5401 
config_parse_extension_images(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5402 int config_parse_extension_images(
5403                 const char *unit,
5404                 const char *filename,
5405                 unsigned line,
5406                 const char *section,
5407                 unsigned section_line,
5408                 const char *lvalue,
5409                 int ltype,
5410                 const char *rvalue,
5411                 void *data,
5412                 void *userdata) {
5413 
5414         ExecContext *c = data;
5415         const Unit *u = userdata;
5416         int r;
5417 
5418         assert(filename);
5419         assert(lvalue);
5420         assert(rvalue);
5421         assert(data);
5422 
5423         if (isempty(rvalue)) {
5424                 /* Empty assignment resets the list */
5425                 c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
5426                 return 0;
5427         }
5428 
5429         for (const char *p = rvalue;;) {
5430                 _cleanup_free_ char *source = NULL, *tuple = NULL, *sresolved = NULL;
5431                 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5432                 bool permissive = false;
5433                 const char *q = NULL;
5434                 char *s = NULL;
5435 
5436                 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5437                 if (r == -ENOMEM)
5438                         return log_oom();
5439                 if (r < 0) {
5440                         log_syntax(unit, LOG_WARNING, filename, line, r,
5441                                    "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5442                         return 0;
5443                 }
5444                 if (r == 0)
5445                         return 0;
5446 
5447                 q = tuple;
5448                 r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
5449                 if (r == -ENOMEM)
5450                         return log_oom();
5451                 if (r < 0) {
5452                         log_syntax(unit, LOG_WARNING, filename, line, r,
5453                                    "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5454                         return 0;
5455                 }
5456                 if (r == 0)
5457                         continue;
5458 
5459                 s = source;
5460                 if (s[0] == '-') {
5461                         permissive = true;
5462                         s++;
5463                 }
5464 
5465                 r = unit_path_printf(u, s, &sresolved);
5466                 if (r < 0) {
5467                         log_syntax(unit, LOG_WARNING, filename, line, r,
5468                                    "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5469                         continue;
5470                 }
5471 
5472                 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5473                 if (r < 0)
5474                         continue;
5475 
5476                 for (;;) {
5477                         _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5478                         MountOptions *o = NULL;
5479                         PartitionDesignator partition_designator;
5480 
5481                         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5482                         if (r == -ENOMEM)
5483                                 return log_oom();
5484                         if (r < 0) {
5485                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5486                                 return 0;
5487                         }
5488                         if (r == 0)
5489                                 break;
5490                         /* Single set of options, applying to the root partition/single filesystem */
5491                         if (r == 1) {
5492                                 r = unit_full_printf(u, partition, &mount_options_resolved);
5493                                 if (r < 0) {
5494                                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", partition);
5495                                         continue;
5496                                 }
5497 
5498                                 o = new(MountOptions, 1);
5499                                 if (!o)
5500                                         return log_oom();
5501                                 *o = (MountOptions) {
5502                                         .partition_designator = PARTITION_ROOT,
5503                                         .options = TAKE_PTR(mount_options_resolved),
5504                                 };
5505                                 LIST_APPEND(mount_options, options, o);
5506 
5507                                 break;
5508                         }
5509 
5510                         partition_designator = partition_designator_from_string(partition);
5511                         if (partition_designator < 0) {
5512                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
5513                                 continue;
5514                         }
5515                         r = unit_full_printf(u, mount_options, &mount_options_resolved);
5516                         if (r < 0) {
5517                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5518                                 continue;
5519                         }
5520 
5521                         o = new(MountOptions, 1);
5522                         if (!o)
5523                                 return log_oom();
5524                         *o = (MountOptions) {
5525                                 .partition_designator = partition_designator,
5526                                 .options = TAKE_PTR(mount_options_resolved),
5527                         };
5528                         LIST_APPEND(mount_options, options, o);
5529                 }
5530 
5531                 r = mount_image_add(&c->extension_images, &c->n_extension_images,
5532                                     &(MountImage) {
5533                                             .source = sresolved,
5534                                             .mount_options = options,
5535                                             .ignore_enoent = permissive,
5536                                             .type = MOUNT_IMAGE_EXTENSION,
5537                                     });
5538                 if (r < 0)
5539                         return log_oom();
5540         }
5541 }
5542 
config_parse_job_timeout_sec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5543 int config_parse_job_timeout_sec(
5544                 const char* unit,
5545                 const char *filename,
5546                 unsigned line,
5547                 const char *section,
5548                 unsigned section_line,
5549                 const char *lvalue,
5550                 int ltype,
5551                 const char *rvalue,
5552                 void *data,
5553                 void *userdata) {
5554 
5555         Unit *u = data;
5556         usec_t usec;
5557         int r;
5558 
5559         assert(filename);
5560         assert(lvalue);
5561         assert(rvalue);
5562         assert(u);
5563 
5564         r = parse_sec_fix_0(rvalue, &usec);
5565         if (r < 0) {
5566                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
5567                 return 0;
5568         }
5569 
5570         /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
5571          * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
5572          * count. */
5573 
5574         if (!u->job_running_timeout_set)
5575                 u->job_running_timeout = usec;
5576 
5577         u->job_timeout = usec;
5578 
5579         return 0;
5580 }
5581 
config_parse_job_running_timeout_sec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5582 int config_parse_job_running_timeout_sec(
5583                 const char* unit,
5584                 const char *filename,
5585                 unsigned line,
5586                 const char *section,
5587                 unsigned section_line,
5588                 const char *lvalue,
5589                 int ltype,
5590                 const char *rvalue,
5591                 void *data,
5592                 void *userdata) {
5593 
5594         Unit *u = data;
5595         usec_t usec;
5596         int r;
5597 
5598         assert(filename);
5599         assert(lvalue);
5600         assert(rvalue);
5601         assert(u);
5602 
5603         r = parse_sec_fix_0(rvalue, &usec);
5604         if (r < 0) {
5605                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
5606                 return 0;
5607         }
5608 
5609         u->job_running_timeout = usec;
5610         u->job_running_timeout_set = true;
5611 
5612         return 0;
5613 }
5614 
config_parse_emergency_action(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5615 int config_parse_emergency_action(
5616                 const char* unit,
5617                 const char *filename,
5618                 unsigned line,
5619                 const char *section,
5620                 unsigned section_line,
5621                 const char *lvalue,
5622                 int ltype,
5623                 const char *rvalue,
5624                 void *data,
5625                 void *userdata) {
5626 
5627         Manager *m = NULL;
5628         EmergencyAction *x = data;
5629         int r;
5630 
5631         assert(filename);
5632         assert(lvalue);
5633         assert(rvalue);
5634         assert(data);
5635 
5636         if (unit)
5637                 m = ((Unit*) userdata)->manager;
5638         else
5639                 m = data;
5640 
5641         r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
5642         if (r < 0) {
5643                 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
5644                         /* Compat mode: remove for systemd 241. */
5645 
5646                         log_syntax(unit, LOG_INFO, filename, line, r,
5647                                    "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
5648                                    lvalue, rvalue);
5649                         *x = EMERGENCY_ACTION_EXIT_FORCE;
5650                         return 0;
5651                 }
5652 
5653                 if (r == -EOPNOTSUPP)
5654                         log_syntax(unit, LOG_WARNING, filename, line, r,
5655                                    "%s= specified as %s mode action, ignoring: %s",
5656                                    lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
5657                 else
5658                         log_syntax(unit, LOG_WARNING, filename, line, r,
5659                                    "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5660                 return 0;
5661         }
5662 
5663         return 0;
5664 }
5665 
config_parse_pid_file(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5666 int config_parse_pid_file(
5667                 const char *unit,
5668                 const char *filename,
5669                 unsigned line,
5670                 const char *section,
5671                 unsigned section_line,
5672                 const char *lvalue,
5673                 int ltype,
5674                 const char *rvalue,
5675                 void *data,
5676                 void *userdata) {
5677 
5678         _cleanup_free_ char *k = NULL, *n = NULL;
5679         const Unit *u = userdata;
5680         char **s = data;
5681         int r;
5682 
5683         assert(filename);
5684         assert(lvalue);
5685         assert(rvalue);
5686         assert(u);
5687 
5688         if (isempty(rvalue)) {
5689                 /* An empty assignment removes already set value. */
5690                 *s = mfree(*s);
5691                 return 0;
5692         }
5693 
5694         r = unit_path_printf(u, rvalue, &k);
5695         if (r < 0) {
5696                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5697                 return 0;
5698         }
5699 
5700         /* If this is a relative path make it absolute by prefixing the /run */
5701         n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
5702         if (!n)
5703                 return log_oom();
5704 
5705         /* Check that the result is a sensible path */
5706         r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5707         if (r < 0)
5708                 return r;
5709 
5710         r = patch_var_run(unit, filename, line, lvalue, &n);
5711         if (r < 0)
5712                 return r;
5713 
5714         free_and_replace(*s, n);
5715         return 0;
5716 }
5717 
config_parse_exit_status(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5718 int config_parse_exit_status(
5719                 const char *unit,
5720                 const char *filename,
5721                 unsigned line,
5722                 const char *section,
5723                 unsigned section_line,
5724                 const char *lvalue,
5725                 int ltype,
5726                 const char *rvalue,
5727                 void *data,
5728                 void *userdata) {
5729 
5730         int *exit_status = data, r;
5731         uint8_t u;
5732 
5733         assert(filename);
5734         assert(lvalue);
5735         assert(rvalue);
5736         assert(exit_status);
5737 
5738         if (isempty(rvalue)) {
5739                 *exit_status = -1;
5740                 return 0;
5741         }
5742 
5743         r = safe_atou8(rvalue, &u);
5744         if (r < 0) {
5745                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
5746                 return 0;
5747         }
5748 
5749         *exit_status = u;
5750         return 0;
5751 }
5752 
config_parse_disable_controllers(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5753 int config_parse_disable_controllers(
5754                 const char *unit,
5755                 const char *filename,
5756                 unsigned line,
5757                 const char *section,
5758                 unsigned section_line,
5759                 const char *lvalue,
5760                 int ltype,
5761                 const char *rvalue,
5762                 void *data,
5763                 void *userdata) {
5764 
5765         int r;
5766         CGroupContext *c = data;
5767         CGroupMask disabled_mask;
5768 
5769         /* 1. If empty, make all controllers eligible for use again.
5770          * 2. If non-empty, merge all listed controllers, space separated. */
5771 
5772         if (isempty(rvalue)) {
5773                 c->disable_controllers = 0;
5774                 return 0;
5775         }
5776 
5777         r = cg_mask_from_string(rvalue, &disabled_mask);
5778         if (r < 0 || disabled_mask <= 0) {
5779                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
5780                 return 0;
5781         }
5782 
5783         c->disable_controllers |= disabled_mask;
5784 
5785         return 0;
5786 }
5787 
config_parse_ip_filter_bpf_progs(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5788 int config_parse_ip_filter_bpf_progs(
5789                 const char *unit,
5790                 const char *filename,
5791                 unsigned line,
5792                 const char *section,
5793                 unsigned section_line,
5794                 const char *lvalue,
5795                 int ltype,
5796                 const char *rvalue,
5797                 void *data,
5798                 void *userdata) {
5799 
5800         _cleanup_free_ char *resolved = NULL;
5801         const Unit *u = userdata;
5802         char ***paths = data;
5803         int r;
5804 
5805         assert(filename);
5806         assert(lvalue);
5807         assert(rvalue);
5808         assert(paths);
5809 
5810         if (isempty(rvalue)) {
5811                 *paths = strv_free(*paths);
5812                 return 0;
5813         }
5814 
5815         r = unit_path_printf(u, rvalue, &resolved);
5816         if (r < 0) {
5817                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5818                 return 0;
5819         }
5820 
5821         r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5822         if (r < 0)
5823                 return 0;
5824 
5825         if (strv_contains(*paths, resolved))
5826                 return 0;
5827 
5828         r = strv_extend(paths, resolved);
5829         if (r < 0)
5830                 return log_oom();
5831 
5832         r = bpf_firewall_supported();
5833         if (r < 0)
5834                 return r;
5835         if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
5836                 static bool warned = false;
5837 
5838                 log_full(warned ? LOG_DEBUG : LOG_WARNING,
5839                          "File %s:%u configures an IP firewall with BPF programs (%s=%s), but the local system does not support BPF/cgroup based firewalling with multiple filters.\n"
5840                          "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
5841 
5842                 warned = true;
5843         }
5844 
5845         return 0;
5846 }
5847 
config_parse_bpf_foreign_program(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5848 int config_parse_bpf_foreign_program(
5849                 const char *unit,
5850                 const char *filename,
5851                 unsigned line,
5852                 const char *section,
5853                 unsigned section_line,
5854                 const char *lvalue,
5855                 int ltype,
5856                 const char *rvalue,
5857                 void *data,
5858                 void *userdata) {
5859         _cleanup_free_ char *resolved = NULL, *word = NULL;
5860         CGroupContext *c = data;
5861         Unit *u = userdata;
5862         int attach_type, r;
5863 
5864         assert(filename);
5865         assert(lvalue);
5866         assert(rvalue);
5867 
5868         if (isempty(rvalue)) {
5869                 while (c->bpf_foreign_programs)
5870                         cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
5871 
5872                 return 0;
5873         }
5874 
5875         r = extract_first_word(&rvalue, &word, ":", 0);
5876         if (r == -ENOMEM)
5877                 return log_oom();
5878         if (r <= 0 || isempty(rvalue)) {
5879                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue);
5880                 return 0;
5881         }
5882 
5883         attach_type = bpf_cgroup_attach_type_from_string(word);
5884         if (attach_type < 0) {
5885                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue);
5886                 return 0;
5887         }
5888 
5889         r = unit_path_printf(u, rvalue, &resolved);
5890         if (r < 0) {
5891                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5892                 return 0;
5893         }
5894 
5895         r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5896         if (r < 0)
5897                 return 0;
5898 
5899         r = cgroup_add_bpf_foreign_program(c, attach_type, resolved);
5900         if (r < 0)
5901                 return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m");
5902 
5903         return 0;
5904 }
5905 
config_parse_cgroup_socket_bind(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5906 int config_parse_cgroup_socket_bind(
5907                 const char *unit,
5908                 const char *filename,
5909                 unsigned line,
5910                 const char *section,
5911                 unsigned section_line,
5912                 const char *lvalue,
5913                 int ltype,
5914                 const char *rvalue,
5915                 void *data,
5916                 void *userdata) {
5917         _cleanup_free_ CGroupSocketBindItem *item = NULL;
5918         CGroupSocketBindItem **head = data;
5919         uint16_t nr_ports, port_min;
5920         int af, ip_protocol, r;
5921 
5922         if (isempty(rvalue)) {
5923                 cgroup_context_remove_socket_bind(head);
5924                 return 0;
5925         }
5926 
5927         r = parse_socket_bind_item(rvalue, &af, &ip_protocol, &nr_ports, &port_min);
5928         if (r == -ENOMEM)
5929                 return log_oom();
5930         if (r < 0) {
5931                 log_syntax(unit, LOG_WARNING, filename, line, r,
5932                            "Unable to parse %s= assignment, ignoring: %s", lvalue, rvalue);
5933                 return 0;
5934         }
5935 
5936         item = new(CGroupSocketBindItem, 1);
5937         if (!item)
5938                 return log_oom();
5939         *item = (CGroupSocketBindItem) {
5940                 .address_family = af,
5941                 .ip_protocol = ip_protocol,
5942                 .nr_ports = nr_ports,
5943                 .port_min = port_min,
5944         };
5945 
5946         LIST_PREPEND(socket_bind_items, *head, TAKE_PTR(item));
5947 
5948         return 0;
5949 }
5950 
config_parse_restrict_network_interfaces(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)5951 int config_parse_restrict_network_interfaces(
5952                 const char *unit,
5953                 const char *filename,
5954                 unsigned line,
5955                 const char *section,
5956                 unsigned section_line,
5957                 const char *lvalue,
5958                 int ltype,
5959                 const char *rvalue,
5960                 void *data,
5961                 void *userdata) {
5962         CGroupContext *c = data;
5963         bool is_allow_rule = true;
5964         int r;
5965 
5966         assert(filename);
5967         assert(lvalue);
5968         assert(rvalue);
5969         assert(data);
5970 
5971         if (isempty(rvalue)) {
5972                 /* Empty assignment resets the list */
5973                 c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
5974                 return 0;
5975         }
5976 
5977         if (rvalue[0] == '~') {
5978                 is_allow_rule = false;
5979                 rvalue++;
5980         }
5981 
5982         if (set_isempty(c->restrict_network_interfaces))
5983                 /* Only initialize this when creating the set */
5984                 c->restrict_network_interfaces_is_allow_list = is_allow_rule;
5985 
5986         for (const char *p = rvalue;;) {
5987                 _cleanup_free_ char *word = NULL;
5988 
5989                 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5990                 if (r == 0)
5991                         break;
5992                 if (r == -ENOMEM)
5993                         return log_oom();
5994                 if (r < 0) {
5995                         log_syntax(unit, LOG_WARNING, filename, line, r,
5996                                    "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
5997                         break;
5998                 }
5999 
6000                 if (!ifname_valid(word)) {
6001                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
6002                         continue;
6003                 }
6004 
6005                 if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
6006                         free(set_remove(c->restrict_network_interfaces, word));
6007                 else {
6008                         r = set_put_strdup(&c->restrict_network_interfaces, word);
6009                         if (r < 0)
6010                                 return log_oom();
6011                 }
6012         }
6013 
6014         return 0;
6015 }
6016 
merge_by_names(Unit ** u,Set * names,const char * id)6017 static int merge_by_names(Unit **u, Set *names, const char *id) {
6018         char *k;
6019         int r;
6020 
6021         assert(u);
6022         assert(*u);
6023 
6024         /* Let's try to add in all names that are aliases of this unit */
6025         while ((k = set_steal_first(names))) {
6026                 _cleanup_free_ _unused_ char *free_k = k;
6027 
6028                 /* First try to merge in the other name into our unit */
6029                 r = unit_merge_by_name(*u, k);
6030                 if (r < 0) {
6031                         Unit *other;
6032 
6033                         /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
6034                          * round. */
6035 
6036                         other = manager_get_unit((*u)->manager, k);
6037                         if (!other)
6038                                 return r; /* return previous failure */
6039 
6040                         r = unit_merge(other, *u);
6041                         if (r < 0)
6042                                 return r;
6043 
6044                         *u = other;
6045                         return merge_by_names(u, names, NULL);
6046                 }
6047 
6048                 if (streq_ptr(id, k))
6049                         unit_choose_id(*u, id);
6050         }
6051 
6052         return 0;
6053 }
6054 
unit_load_fragment(Unit * u)6055 int unit_load_fragment(Unit *u) {
6056         const char *fragment;
6057         _cleanup_set_free_free_ Set *names = NULL;
6058         int r;
6059 
6060         assert(u);
6061         assert(u->load_state == UNIT_STUB);
6062         assert(u->id);
6063 
6064         if (u->transient) {
6065                 u->load_state = UNIT_LOADED;
6066                 return 0;
6067         }
6068 
6069         /* Possibly rebuild the fragment map to catch new units */
6070         r = unit_file_build_name_map(&u->manager->lookup_paths,
6071                                      &u->manager->unit_cache_timestamp_hash,
6072                                      &u->manager->unit_id_map,
6073                                      &u->manager->unit_name_map,
6074                                      &u->manager->unit_path_cache);
6075         if (r < 0)
6076                 return log_error_errno(r, "Failed to rebuild name map: %m");
6077 
6078         r = unit_file_find_fragment(u->manager->unit_id_map,
6079                                     u->manager->unit_name_map,
6080                                     u->id,
6081                                     &fragment,
6082                                     &names);
6083         if (r < 0 && r != -ENOENT)
6084                 return r;
6085 
6086         if (fragment) {
6087                 /* Open the file, check if this is a mask, otherwise read. */
6088                 _cleanup_fclose_ FILE *f = NULL;
6089                 struct stat st;
6090 
6091                 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
6092                  * expect that all symlinks within the lookup paths have been already resolved, but we don't
6093                  * verify this here. */
6094                 f = fopen(fragment, "re");
6095                 if (!f)
6096                         return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
6097 
6098                 if (fstat(fileno(f), &st) < 0)
6099                         return -errno;
6100 
6101                 r = free_and_strdup(&u->fragment_path, fragment);
6102                 if (r < 0)
6103                         return r;
6104 
6105                 if (null_or_empty(&st)) {
6106                         /* Unit file is masked */
6107 
6108                         u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
6109                         u->fragment_mtime = 0;
6110                 } else {
6111                         u->load_state = UNIT_LOADED;
6112                         u->fragment_mtime = timespec_load(&st.st_mtim);
6113 
6114                         /* Now, parse the file contents */
6115                         r = config_parse(u->id, fragment, f,
6116                                          UNIT_VTABLE(u)->sections,
6117                                          config_item_perf_lookup, load_fragment_gperf_lookup,
6118                                          0,
6119                                          u,
6120                                          NULL);
6121                         if (r == -ENOEXEC)
6122                                 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
6123                         if (r < 0)
6124                                 return r;
6125                 }
6126         }
6127 
6128         /* Call merge_by_names with the name derived from the fragment path as the preferred name.
6129          *
6130          * We do the merge dance here because for some unit types, the unit might have aliases which are not
6131          * declared in the file system. In particular, this is true (and frequent) for device and swap units.
6132          */
6133         const char *id = u->id;
6134         _cleanup_free_ char *free_id = NULL;
6135 
6136         if (fragment) {
6137                 id = basename(fragment);
6138                 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
6139                         assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
6140                                               * this must be set. */
6141 
6142                         r = unit_name_replace_instance(id, u->instance, &free_id);
6143                         if (r < 0)
6144                                 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
6145                         id = free_id;
6146                 }
6147         }
6148 
6149         Unit *merged = u;
6150         r = merge_by_names(&merged, names, id);
6151         if (r < 0)
6152                 return r;
6153 
6154         if (merged != u)
6155                 u->load_state = UNIT_MERGED;
6156 
6157         return 0;
6158 }
6159 
unit_dump_config_items(FILE * f)6160 void unit_dump_config_items(FILE *f) {
6161         static const struct {
6162                 const ConfigParserCallback callback;
6163                 const char *rvalue;
6164         } table[] = {
6165                 { config_parse_warn_compat,           "NOTSUPPORTED" },
6166                 { config_parse_int,                   "INTEGER" },
6167                 { config_parse_unsigned,              "UNSIGNED" },
6168                 { config_parse_iec_size,              "SIZE" },
6169                 { config_parse_iec_uint64,            "SIZE" },
6170                 { config_parse_si_uint64,             "SIZE" },
6171                 { config_parse_bool,                  "BOOLEAN" },
6172                 { config_parse_string,                "STRING" },
6173                 { config_parse_path,                  "PATH" },
6174                 { config_parse_unit_path_printf,      "PATH" },
6175                 { config_parse_colon_separated_paths, "PATH" },
6176                 { config_parse_strv,                  "STRING [...]" },
6177                 { config_parse_exec_nice,             "NICE" },
6178                 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
6179                 { config_parse_exec_io_class,         "IOCLASS" },
6180                 { config_parse_exec_io_priority,      "IOPRIORITY" },
6181                 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
6182                 { config_parse_exec_cpu_sched_prio,   "CPUSCHEDPRIO" },
6183                 { config_parse_exec_cpu_affinity,     "CPUAFFINITY" },
6184                 { config_parse_mode,                  "MODE" },
6185                 { config_parse_unit_env_file,         "FILE" },
6186                 { config_parse_exec_output,           "OUTPUT" },
6187                 { config_parse_exec_input,            "INPUT" },
6188                 { config_parse_log_facility,          "FACILITY" },
6189                 { config_parse_log_level,             "LEVEL" },
6190                 { config_parse_exec_secure_bits,      "SECUREBITS" },
6191                 { config_parse_capability_set,        "BOUNDINGSET" },
6192                 { config_parse_rlimit,                "LIMIT" },
6193                 { config_parse_unit_deps,             "UNIT [...]" },
6194                 { config_parse_exec,                  "PATH [ARGUMENT [...]]" },
6195                 { config_parse_service_type,          "SERVICETYPE" },
6196                 { config_parse_service_exit_type,     "SERVICEEXITTYPE" },
6197                 { config_parse_service_restart,       "SERVICERESTART" },
6198                 { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
6199                 { config_parse_kill_mode,             "KILLMODE" },
6200                 { config_parse_signal,                "SIGNAL" },
6201                 { config_parse_socket_listen,         "SOCKET [...]" },
6202                 { config_parse_socket_bind,           "SOCKETBIND" },
6203                 { config_parse_socket_bindtodevice,   "NETWORKINTERFACE" },
6204                 { config_parse_sec,                   "SECONDS" },
6205                 { config_parse_nsec,                  "NANOSECONDS" },
6206                 { config_parse_namespace_path_strv,   "PATH [...]" },
6207                 { config_parse_bind_paths,            "PATH[:PATH[:OPTIONS]] [...]" },
6208                 { config_parse_unit_requires_mounts_for, "PATH [...]" },
6209                 { config_parse_exec_mount_flags,      "MOUNTFLAG [...]" },
6210                 { config_parse_unit_string_printf,    "STRING" },
6211                 { config_parse_trigger_unit,          "UNIT" },
6212                 { config_parse_timer,                 "TIMER" },
6213                 { config_parse_path_spec,             "PATH" },
6214                 { config_parse_notify_access,         "ACCESS" },
6215                 { config_parse_ip_tos,                "TOS" },
6216                 { config_parse_unit_condition_path,   "CONDITION" },
6217                 { config_parse_unit_condition_string, "CONDITION" },
6218                 { config_parse_unit_slice,            "SLICE" },
6219                 { config_parse_documentation,         "URL" },
6220                 { config_parse_service_timeout,       "SECONDS" },
6221                 { config_parse_emergency_action,      "ACTION" },
6222                 { config_parse_set_status,            "STATUS" },
6223                 { config_parse_service_sockets,       "SOCKETS" },
6224                 { config_parse_environ,               "ENVIRON" },
6225 #if HAVE_SECCOMP
6226                 { config_parse_syscall_filter,        "SYSCALLS" },
6227                 { config_parse_syscall_archs,         "ARCHS" },
6228                 { config_parse_syscall_errno,         "ERRNO" },
6229                 { config_parse_syscall_log,           "SYSCALLS" },
6230                 { config_parse_address_families,      "FAMILIES" },
6231                 { config_parse_restrict_namespaces,   "NAMESPACES"  },
6232 #endif
6233                 { config_parse_restrict_filesystems,  "FILESYSTEMS"  },
6234                 { config_parse_cpu_shares,            "SHARES" },
6235                 { config_parse_cg_weight,             "WEIGHT" },
6236                 { config_parse_memory_limit,          "LIMIT" },
6237                 { config_parse_device_allow,          "DEVICE" },
6238                 { config_parse_device_policy,         "POLICY" },
6239                 { config_parse_io_limit,              "LIMIT" },
6240                 { config_parse_io_device_weight,      "DEVICEWEIGHT" },
6241                 { config_parse_io_device_latency,     "DEVICELATENCY" },
6242                 { config_parse_blockio_bandwidth,     "BANDWIDTH" },
6243                 { config_parse_blockio_weight,        "WEIGHT" },
6244                 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
6245                 { config_parse_long,                  "LONG" },
6246                 { config_parse_socket_service,        "SERVICE" },
6247 #if HAVE_SELINUX
6248                 { config_parse_exec_selinux_context,  "LABEL" },
6249 #endif
6250                 { config_parse_job_mode,              "MODE" },
6251                 { config_parse_job_mode_isolate,      "BOOLEAN" },
6252                 { config_parse_personality,           "PERSONALITY" },
6253         };
6254 
6255         const char *prev = NULL;
6256         const char *i;
6257 
6258         assert(f);
6259 
6260         NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
6261                 const char *rvalue = "OTHER", *lvalue;
6262                 const ConfigPerfItem *p;
6263                 const char *dot;
6264 
6265                 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
6266 
6267                 /* Hide legacy settings */
6268                 if (p->parse == config_parse_warn_compat &&
6269                     p->ltype == DISABLED_LEGACY)
6270                         continue;
6271 
6272                 for (size_t j = 0; j < ELEMENTSOF(table); j++)
6273                         if (p->parse == table[j].callback) {
6274                                 rvalue = table[j].rvalue;
6275                                 break;
6276                         }
6277 
6278                 dot = strchr(i, '.');
6279                 lvalue = dot ? dot + 1 : i;
6280 
6281                 if (dot) {
6282                         size_t prefix_len = dot - i;
6283 
6284                         if (!prev || !strneq(prev, i, prefix_len+1)) {
6285                                 if (prev)
6286                                         fputc('\n', f);
6287 
6288                                 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
6289                         }
6290                 }
6291 
6292                 fprintf(f, "%s=%s\n", lvalue, rvalue);
6293                 prev = i;
6294         }
6295 }
6296 
config_parse_cpu_affinity2(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6297 int config_parse_cpu_affinity2(
6298                 const char *unit,
6299                 const char *filename,
6300                 unsigned line,
6301                 const char *section,
6302                 unsigned section_line,
6303                 const char *lvalue,
6304                 int ltype,
6305                 const char *rvalue,
6306                 void *data,
6307                 void *userdata) {
6308 
6309         CPUSet *affinity = data;
6310 
6311         assert(affinity);
6312 
6313         (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
6314 
6315         return 0;
6316 }
6317 
config_parse_show_status(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6318 int config_parse_show_status(
6319                 const char* unit,
6320                 const char *filename,
6321                 unsigned line,
6322                 const char *section,
6323                 unsigned section_line,
6324                 const char *lvalue,
6325                 int ltype,
6326                 const char *rvalue,
6327                 void *data,
6328                 void *userdata) {
6329 
6330         int k;
6331         ShowStatus *b = data;
6332 
6333         assert(filename);
6334         assert(lvalue);
6335         assert(rvalue);
6336         assert(data);
6337 
6338         k = parse_show_status(rvalue, b);
6339         if (k < 0)
6340                 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
6341 
6342         return 0;
6343 }
6344 
config_parse_output_restricted(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6345 int config_parse_output_restricted(
6346                 const char* unit,
6347                 const char *filename,
6348                 unsigned line,
6349                 const char *section,
6350                 unsigned section_line,
6351                 const char *lvalue,
6352                 int ltype,
6353                 const char *rvalue,
6354                 void *data,
6355                 void *userdata) {
6356 
6357         ExecOutput t, *eo = data;
6358         bool obsolete = false;
6359 
6360         assert(filename);
6361         assert(lvalue);
6362         assert(rvalue);
6363         assert(data);
6364 
6365         if (streq(rvalue, "syslog")) {
6366                 t = EXEC_OUTPUT_JOURNAL;
6367                 obsolete = true;
6368         } else if (streq(rvalue, "syslog+console")) {
6369                 t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
6370                 obsolete = true;
6371         } else {
6372                 t = exec_output_from_string(rvalue);
6373                 if (t < 0) {
6374                         log_syntax(unit, LOG_WARNING, filename, line, t, "Failed to parse output type, ignoring: %s", rvalue);
6375                         return 0;
6376                 }
6377 
6378                 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND, EXEC_OUTPUT_FILE_TRUNCATE)) {
6379                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append:, truncate: are not supported as defaults, ignoring: %s", rvalue);
6380                         return 0;
6381                 }
6382         }
6383 
6384         if (obsolete)
6385                 log_syntax(unit, LOG_NOTICE, filename, line, 0,
6386                            "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
6387                            rvalue, exec_output_to_string(t));
6388 
6389         *eo = t;
6390         return 0;
6391 }
6392 
config_parse_crash_chvt(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6393 int config_parse_crash_chvt(
6394                 const char* unit,
6395                 const char *filename,
6396                 unsigned line,
6397                 const char *section,
6398                 unsigned section_line,
6399                 const char *lvalue,
6400                 int ltype,
6401                 const char *rvalue,
6402                 void *data,
6403                 void *userdata) {
6404 
6405         int r;
6406 
6407         assert(filename);
6408         assert(lvalue);
6409         assert(rvalue);
6410         assert(data);
6411 
6412         r = parse_crash_chvt(rvalue, data);
6413         if (r < 0)
6414                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
6415 
6416         return 0;
6417 }
6418 
config_parse_swap_priority(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6419 int config_parse_swap_priority(
6420                 const char *unit,
6421                 const char *filename,
6422                 unsigned line,
6423                 const char *section,
6424                 unsigned section_line,
6425                 const char *lvalue,
6426                 int ltype,
6427                 const char *rvalue,
6428                 void *data,
6429                 void *userdata) {
6430 
6431         Swap *s = userdata;
6432         int r, priority;
6433 
6434         assert(s);
6435         assert(filename);
6436         assert(lvalue);
6437         assert(rvalue);
6438         assert(data);
6439 
6440         if (isempty(rvalue)) {
6441                 s->parameters_fragment.priority = -1;
6442                 s->parameters_fragment.priority_set = false;
6443                 return 0;
6444         }
6445 
6446         r = safe_atoi(rvalue, &priority);
6447         if (r < 0) {
6448                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
6449                 return 0;
6450         }
6451 
6452         if (priority < -1) {
6453                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
6454                 return 0;
6455         }
6456 
6457         if (priority > 32767) {
6458                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
6459                 return 0;
6460         }
6461 
6462         s->parameters_fragment.priority = priority;
6463         s->parameters_fragment.priority_set = true;
6464         return 0;
6465 }
6466 
config_parse_watchdog_sec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6467 int config_parse_watchdog_sec(
6468                 const char *unit,
6469                 const char *filename,
6470                 unsigned line,
6471                 const char *section,
6472                 unsigned section_line,
6473                 const char *lvalue,
6474                 int ltype,
6475                 const char *rvalue,
6476                 void *data,
6477                 void *userdata) {
6478 
6479         usec_t *usec = data;
6480 
6481         assert(filename);
6482         assert(lvalue);
6483         assert(rvalue);
6484 
6485         /* This is called for {Runtime,Reboot,KExec}WatchdogSec= where "default" maps to
6486          * USEC_INFINITY internally. */
6487 
6488         if (streq(rvalue, "default"))
6489                 *usec = USEC_INFINITY;
6490         else if (streq(rvalue, "off"))
6491                 *usec = 0;
6492         else
6493                 return config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6494 
6495         return 0;
6496 }
6497 
config_parse_tty_size(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)6498 int config_parse_tty_size(
6499                 const char *unit,
6500                 const char *filename,
6501                 unsigned line,
6502                 const char *section,
6503                 unsigned section_line,
6504                 const char *lvalue,
6505                 int ltype,
6506                 const char *rvalue,
6507                 void *data,
6508                 void *userdata) {
6509 
6510         unsigned *sz = data;
6511 
6512         assert(filename);
6513         assert(lvalue);
6514         assert(rvalue);
6515 
6516         if (isempty(rvalue)) {
6517                 *sz = UINT_MAX;
6518                 return 0;
6519         }
6520 
6521         return config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6522 }
6523