1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "cap-list.h"
5 #include "conf-parser.h"
6 #include "cpu-set-util.h"
7 #include "hostname-util.h"
8 #include "namespace-util.h"
9 #include "nspawn-network.h"
10 #include "nspawn-settings.h"
11 #include "parse-util.h"
12 #include "process-util.h"
13 #include "rlimit-util.h"
14 #include "socket-util.h"
15 #include "string-table.h"
16 #include "string-util.h"
17 #include "strv.h"
18 #include "user-util.h"
19 #include "util.h"
20 
settings_new(void)21 Settings *settings_new(void) {
22         Settings *s;
23 
24         s = new(Settings, 1);
25         if (!s)
26                 return NULL;
27 
28         *s = (Settings) {
29                 .start_mode = _START_MODE_INVALID,
30                 .ephemeral = -1,
31                 .personality = PERSONALITY_INVALID,
32 
33                 .resolv_conf = _RESOLV_CONF_MODE_INVALID,
34                 .link_journal = _LINK_JOURNAL_INVALID,
35                 .timezone = _TIMEZONE_MODE_INVALID,
36 
37                 .userns_mode = _USER_NAMESPACE_MODE_INVALID,
38                 .userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID,
39                 .uid_shift = UID_INVALID,
40                 .uid_range = UID_INVALID,
41 
42                 .no_new_privileges = -1,
43 
44                 .read_only = -1,
45                 .volatile_mode = _VOLATILE_MODE_INVALID,
46 
47                 .private_network = -1,
48                 .network_veth = -1,
49 
50                 .full_capabilities = CAPABILITY_QUINTET_NULL,
51 
52                 .uid = UID_INVALID,
53                 .gid = GID_INVALID,
54 
55                 .console_mode = _CONSOLE_MODE_INVALID,
56                 .console_width = UINT_MAX,
57                 .console_height = UINT_MAX,
58 
59                 .clone_ns_flags = ULONG_MAX,
60                 .use_cgns = -1,
61 
62                 .notify_ready = -1,
63                 .suppress_sync = -1,
64         };
65 
66         return s;
67 }
68 
settings_load(FILE * f,const char * path,Settings ** ret)69 int settings_load(FILE *f, const char *path, Settings **ret) {
70         _cleanup_(settings_freep) Settings *s = NULL;
71         int r;
72 
73         assert(path);
74         assert(ret);
75 
76         s = settings_new();
77         if (!s)
78                 return -ENOMEM;
79 
80         r = config_parse(NULL, path, f,
81                          "Exec\0"
82                          "Network\0"
83                          "Files\0",
84                          config_item_perf_lookup, nspawn_gperf_lookup,
85                          CONFIG_PARSE_WARN,
86                          s, NULL);
87         if (r < 0)
88                 return r;
89 
90         /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
91          * both fields shall be initialized or neither. */
92         if (s->userns_mode >= 0 && s->userns_ownership < 0)
93                 s->userns_ownership = s->userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
94         if (s->userns_ownership >= 0 && s->userns_mode < 0)
95                 s->userns_mode = USER_NAMESPACE_NO;
96 
97         *ret = TAKE_PTR(s);
98         return 0;
99 }
100 
free_oci_hooks(OciHook * h,size_t n)101 static void free_oci_hooks(OciHook *h, size_t n) {
102         size_t i;
103 
104         assert(h || n == 0);
105 
106         for (i = 0; i < n; i++) {
107                 free(h[i].path);
108                 strv_free(h[i].args);
109                 strv_free(h[i].env);
110         }
111 
112         free(h);
113 }
114 
device_node_array_free(DeviceNode * node,size_t n)115 void device_node_array_free(DeviceNode *node, size_t n) {
116         size_t i;
117 
118         for (i = 0; i < n; i++)
119                 free(node[i].path);
120 
121         free(node);
122 }
123 
settings_free(Settings * s)124 Settings* settings_free(Settings *s) {
125         if (!s)
126                 return NULL;
127 
128         strv_free(s->parameters);
129         strv_free(s->environment);
130         free(s->user);
131         free(s->pivot_root_new);
132         free(s->pivot_root_old);
133         free(s->working_directory);
134         strv_free(s->syscall_allow_list);
135         strv_free(s->syscall_deny_list);
136         rlimit_free_all(s->rlimit);
137         free(s->hostname);
138         cpu_set_reset(&s->cpu_set);
139         strv_free(s->bind_user);
140 
141         strv_free(s->network_interfaces);
142         strv_free(s->network_macvlan);
143         strv_free(s->network_ipvlan);
144         strv_free(s->network_veth_extra);
145         free(s->network_bridge);
146         free(s->network_zone);
147         expose_port_free_all(s->expose_ports);
148 
149         custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
150 
151         free(s->bundle);
152         free(s->root);
153 
154         free_oci_hooks(s->oci_hooks_prestart, s->n_oci_hooks_prestart);
155         free_oci_hooks(s->oci_hooks_poststart, s->n_oci_hooks_poststart);
156         free_oci_hooks(s->oci_hooks_poststop, s->n_oci_hooks_poststop);
157 
158         free(s->slice);
159         sd_bus_message_unref(s->properties);
160 
161         free(s->supplementary_gids);
162         device_node_array_free(s->extra_nodes, s->n_extra_nodes);
163         free(s->network_namespace_path);
164 
165         strv_free(s->sysctl);
166 
167 #if HAVE_SECCOMP
168         seccomp_release(s->seccomp);
169 #endif
170 
171         return mfree(s);
172 }
173 
settings_private_network(Settings * s)174 bool settings_private_network(Settings *s) {
175         assert(s);
176 
177         /* Determines whether we shall open up our own private network */
178 
179         return
180                 s->private_network > 0 ||
181                 s->network_veth > 0 ||
182                 s->network_bridge ||
183                 s->network_zone ||
184                 s->network_interfaces ||
185                 s->network_macvlan ||
186                 s->network_ipvlan ||
187                 s->network_veth_extra;
188 }
189 
settings_network_veth(Settings * s)190 bool settings_network_veth(Settings *s) {
191         assert(s);
192 
193         return
194                 s->network_veth > 0 ||
195                 s->network_bridge ||
196                 s->network_zone;
197 }
198 
settings_network_configured(Settings * s)199 bool settings_network_configured(Settings *s) {
200         assert(s);
201 
202         /* Determines whether any network configuration setting was used. (i.e. in contrast to
203          * settings_private_network() above this might also indicate if private networking was explicitly
204          * turned off.) */
205 
206         return
207                 s->private_network >= 0 ||
208                 s->network_veth >= 0 ||
209                 s->network_bridge ||
210                 s->network_zone ||
211                 s->network_interfaces ||
212                 s->network_macvlan ||
213                 s->network_ipvlan ||
214                 s->network_veth_extra ||
215                 s->network_namespace_path;
216 }
217 
settings_allocate_properties(Settings * s)218 int settings_allocate_properties(Settings *s) {
219         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
220         int r;
221 
222         assert(s);
223 
224         if (s->properties)
225                 return 0;
226 
227         r = sd_bus_default_system(&bus);
228         if (r < 0)
229                 return r;
230 
231         r = sd_bus_message_new(bus, &s->properties, SD_BUS_MESSAGE_METHOD_CALL);
232         if (r < 0)
233                 return r;
234 
235         return 0;
236 }
237 
238 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
239 
config_parse_expose_port(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)240 int config_parse_expose_port(
241                 const char *unit,
242                 const char *filename,
243                 unsigned line,
244                 const char *section,
245                 unsigned section_line,
246                 const char *lvalue,
247                 int ltype,
248                 const char *rvalue,
249                 void *data,
250                 void *userdata) {
251 
252         Settings *s = data;
253         int r;
254 
255         assert(filename);
256         assert(lvalue);
257         assert(rvalue);
258 
259         r = expose_port_parse(&s->expose_ports, rvalue);
260         if (r == -EEXIST)
261                 log_syntax(unit, LOG_WARNING, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
262         else if (r < 0)
263                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse host port %s: %m", rvalue);
264 
265         return 0;
266 }
267 
config_parse_capability(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)268 int config_parse_capability(
269                 const char *unit,
270                 const char *filename,
271                 unsigned line,
272                 const char *section,
273                 unsigned section_line,
274                 const char *lvalue,
275                 int ltype,
276                 const char *rvalue,
277                 void *data,
278                 void *userdata) {
279 
280         uint64_t u = 0, *result = data;
281         int r;
282 
283         assert(filename);
284         assert(lvalue);
285         assert(rvalue);
286 
287         for (;;) {
288                 _cleanup_free_ char *word = NULL;
289 
290                 r = extract_first_word(&rvalue, &word, NULL, 0);
291                 if (r == -ENOMEM)
292                         return log_oom();
293                 if (r < 0) {
294                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
295                         return 0;
296                 }
297                 if (r == 0)
298                         break;
299 
300                 if (streq(word, "all"))
301                         u = UINT64_MAX;
302                 else {
303                         r = capability_from_name(word);
304                         if (r < 0) {
305                                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse capability, ignoring: %s", word);
306                                 continue;
307                         }
308 
309                         u |= UINT64_C(1) << r;
310                 }
311         }
312 
313         *result |= u;
314         return 0;
315 }
316 
config_parse_pivot_root(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)317 int config_parse_pivot_root(
318                 const char *unit,
319                 const char *filename,
320                 unsigned line,
321                 const char *section,
322                 unsigned section_line,
323                 const char *lvalue,
324                 int ltype,
325                 const char *rvalue,
326                 void *data,
327                 void *userdata) {
328 
329         Settings *settings = data;
330         int r;
331 
332         assert(filename);
333         assert(lvalue);
334         assert(rvalue);
335 
336         r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
337         if (r < 0)
338                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
339 
340         return 0;
341 }
342 
config_parse_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)343 int config_parse_bind(
344                 const char *unit,
345                 const char *filename,
346                 unsigned line,
347                 const char *section,
348                 unsigned section_line,
349                 const char *lvalue,
350                 int ltype,
351                 const char *rvalue,
352                 void *data,
353                 void *userdata) {
354 
355         Settings *settings = data;
356         int r;
357 
358         assert(filename);
359         assert(lvalue);
360         assert(rvalue);
361 
362         r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
363         if (r < 0)
364                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
365 
366         return 0;
367 }
368 
config_parse_tmpfs(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)369 int config_parse_tmpfs(
370                 const char *unit,
371                 const char *filename,
372                 unsigned line,
373                 const char *section,
374                 unsigned section_line,
375                 const char *lvalue,
376                 int ltype,
377                 const char *rvalue,
378                 void *data,
379                 void *userdata) {
380 
381         Settings *settings = data;
382         int r;
383 
384         assert(filename);
385         assert(lvalue);
386         assert(rvalue);
387 
388         r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
389         if (r < 0)
390                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
391 
392         return 0;
393 }
394 
config_parse_inaccessible(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)395 int config_parse_inaccessible(
396                 const char *unit,
397                 const char *filename,
398                 unsigned line,
399                 const char *section,
400                 unsigned section_line,
401                 const char *lvalue,
402                 int ltype,
403                 const char *rvalue,
404                 void *data,
405                 void *userdata) {
406 
407         Settings *settings = data;
408         int r;
409 
410         assert(filename);
411         assert(lvalue);
412         assert(rvalue);
413 
414         r = inaccessible_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
415         if (r < 0)
416                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid inaccessible file system specification %s: %m", rvalue);
417 
418         return 0;
419 }
420 
config_parse_overlay(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)421 int config_parse_overlay(
422                 const char *unit,
423                 const char *filename,
424                 unsigned line,
425                 const char *section,
426                 unsigned section_line,
427                 const char *lvalue,
428                 int ltype,
429                 const char *rvalue,
430                 void *data,
431                 void *userdata) {
432 
433         Settings *settings = data;
434         int r;
435 
436         assert(filename);
437         assert(lvalue);
438         assert(rvalue);
439 
440         r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
441         if (r < 0)
442                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
443 
444         return 0;
445 }
446 
config_parse_veth_extra(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)447 int config_parse_veth_extra(
448                 const char *unit,
449                 const char *filename,
450                 unsigned line,
451                 const char *section,
452                 unsigned section_line,
453                 const char *lvalue,
454                 int ltype,
455                 const char *rvalue,
456                 void *data,
457                 void *userdata) {
458 
459         Settings *settings = data;
460         int r;
461 
462         assert(filename);
463         assert(lvalue);
464         assert(rvalue);
465 
466         r = veth_extra_parse(&settings->network_veth_extra, rvalue);
467         if (r < 0)
468                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
469 
470         return 0;
471 }
472 
config_parse_network_zone(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)473 int config_parse_network_zone(
474                 const char *unit,
475                 const char *filename,
476                 unsigned line,
477                 const char *section,
478                 unsigned section_line,
479                 const char *lvalue,
480                 int ltype,
481                 const char *rvalue,
482                 void *data,
483                 void *userdata) {
484 
485         Settings *settings = data;
486         _cleanup_free_ char *j = NULL;
487 
488         assert(filename);
489         assert(lvalue);
490         assert(rvalue);
491 
492         j = strjoin("vz-", rvalue);
493         if (!ifname_valid(j)) {
494                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid network zone name, ignoring: %s", rvalue);
495                 return 0;
496         }
497 
498         return free_and_replace(settings->network_zone, j);
499 }
500 
config_parse_boot(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)501 int config_parse_boot(
502                 const char *unit,
503                 const char *filename,
504                 unsigned line,
505                 const char *section,
506                 unsigned section_line,
507                 const char *lvalue,
508                 int ltype,
509                 const char *rvalue,
510                 void *data,
511                 void *userdata) {
512 
513         Settings *settings = data;
514         int r;
515 
516         assert(filename);
517         assert(lvalue);
518         assert(rvalue);
519 
520         r = parse_boolean(rvalue);
521         if (r < 0) {
522                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
523                 return 0;
524         }
525 
526         if (r) {
527                 if (settings->start_mode == START_PID2)
528                         goto conflict;
529 
530                 settings->start_mode = START_BOOT;
531         } else {
532                 if (settings->start_mode == START_BOOT)
533                         goto conflict;
534 
535                 if (settings->start_mode < 0)
536                         settings->start_mode = START_PID1;
537         }
538 
539         return 0;
540 
541 conflict:
542         log_syntax(unit, LOG_WARNING, filename, line, 0, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
543         return 0;
544 }
545 
config_parse_pid2(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)546 int config_parse_pid2(
547                 const char *unit,
548                 const char *filename,
549                 unsigned line,
550                 const char *section,
551                 unsigned section_line,
552                 const char *lvalue,
553                 int ltype,
554                 const char *rvalue,
555                 void *data,
556                 void *userdata) {
557 
558         Settings *settings = data;
559         int r;
560 
561         assert(filename);
562         assert(lvalue);
563         assert(rvalue);
564 
565         r = parse_boolean(rvalue);
566         if (r < 0) {
567                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
568                 return 0;
569         }
570 
571         if (r) {
572                 if (settings->start_mode == START_BOOT)
573                         goto conflict;
574 
575                 settings->start_mode = START_PID2;
576         } else {
577                 if (settings->start_mode == START_PID2)
578                         goto conflict;
579 
580                 if (settings->start_mode < 0)
581                         settings->start_mode = START_PID1;
582         }
583 
584         return 0;
585 
586 conflict:
587         log_syntax(unit, LOG_WARNING, filename, line, 0, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
588         return 0;
589 }
590 
config_parse_private_users(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)591 int config_parse_private_users(
592                 const char *unit,
593                 const char *filename,
594                 unsigned line,
595                 const char *section,
596                 unsigned section_line,
597                 const char *lvalue,
598                 int ltype,
599                 const char *rvalue,
600                 void *data,
601                 void *userdata) {
602 
603         Settings *settings = data;
604         int r;
605 
606         assert(filename);
607         assert(lvalue);
608         assert(rvalue);
609 
610         r = parse_boolean(rvalue);
611         if (r == 0) {
612                 /* no: User namespacing off */
613                 settings->userns_mode = USER_NAMESPACE_NO;
614                 settings->uid_shift = UID_INVALID;
615                 settings->uid_range = UINT32_C(0x10000);
616         } else if (r > 0) {
617                 /* yes: User namespacing on, UID range is read from root dir */
618                 settings->userns_mode = USER_NAMESPACE_FIXED;
619                 settings->uid_shift = UID_INVALID;
620                 settings->uid_range = UINT32_C(0x10000);
621         } else if (streq(rvalue, "pick")) {
622                 /* pick: User namespacing on, UID range is picked randomly */
623                 settings->userns_mode = USER_NAMESPACE_PICK;
624                 settings->uid_shift = UID_INVALID;
625                 settings->uid_range = UINT32_C(0x10000);
626         } else {
627                 const char *range, *shift;
628                 uid_t sh, rn;
629 
630                 /* anything else: User namespacing on, UID range is explicitly configured */
631 
632                 range = strchr(rvalue, ':');
633                 if (range) {
634                         shift = strndupa_safe(rvalue, range - rvalue);
635                         range++;
636 
637                         r = safe_atou32(range, &rn);
638                         if (r < 0) {
639                                 log_syntax(unit, LOG_WARNING, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
640                                 return 0;
641                         }
642                 } else {
643                         shift = rvalue;
644                         rn = UINT32_C(0x10000);
645                 }
646 
647                 r = parse_uid(shift, &sh);
648                 if (r < 0) {
649                         log_syntax(unit, LOG_WARNING, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
650                         return 0;
651                 }
652 
653                 if (!userns_shift_range_valid(sh, rn)) {
654                         log_syntax(unit, LOG_WARNING, filename, line, 0, "UID/GID shift and range combination invalid, ignoring: %s", range);
655                         return 0;
656                 }
657 
658                 settings->userns_mode = USER_NAMESPACE_FIXED;
659                 settings->uid_shift = sh;
660                 settings->uid_range = rn;
661         }
662 
663         return 0;
664 }
665 
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)666 int config_parse_syscall_filter(
667                 const char *unit,
668                 const char *filename,
669                 unsigned line,
670                 const char *section,
671                 unsigned section_line,
672                 const char *lvalue,
673                 int ltype,
674                 const char *rvalue,
675                 void *data,
676                 void *userdata) {
677 
678         Settings *settings = data;
679         bool negative;
680         const char *items;
681         int r;
682 
683         assert(filename);
684         assert(lvalue);
685         assert(rvalue);
686 
687         negative = rvalue[0] == '~';
688         items = negative ? rvalue + 1 : rvalue;
689 
690         for (;;) {
691                 _cleanup_free_ char *word = NULL;
692 
693                 r = extract_first_word(&items, &word, NULL, 0);
694                 if (r == 0)
695                         return 0;
696                 if (r == -ENOMEM)
697                         return log_oom();
698                 if (r < 0) {
699                         log_syntax(unit, LOG_WARNING, filename, line, r,
700                                    "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
701                         return 0;
702                 }
703 
704                 if (negative)
705                         r = strv_extend(&settings->syscall_deny_list, word);
706                 else
707                         r = strv_extend(&settings->syscall_allow_list, word);
708                 if (r < 0)
709                         return log_oom();
710         }
711 }
712 
config_parse_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)713 int config_parse_oom_score_adjust(
714                 const char *unit,
715                 const char *filename,
716                 unsigned line,
717                 const char *section,
718                 unsigned section_line,
719                 const char *lvalue,
720                 int ltype,
721                 const char *rvalue,
722                 void *data,
723                 void *userdata) {
724 
725         Settings *settings = data;
726         int oa, r;
727 
728         assert(rvalue);
729         assert(settings);
730 
731         if (isempty(rvalue)) {
732                 settings->oom_score_adjust_set = false;
733                 return 0;
734         }
735 
736         r = parse_oom_score_adjust(rvalue, &oa);
737         if (r == -ERANGE) {
738                 log_syntax(unit, LOG_WARNING, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
739                 return 0;
740         }
741         if (r < 0) {
742                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
743                 return 0;
744         }
745 
746         settings->oom_score_adjust = oa;
747         settings->oom_score_adjust_set = true;
748 
749         return 0;
750 }
751 
config_parse_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)752 int config_parse_cpu_affinity(
753                 const char *unit,
754                 const char *filename,
755                 unsigned line,
756                 const char *section,
757                 unsigned section_line,
758                 const char *lvalue,
759                 int ltype,
760                 const char *rvalue,
761                 void *data,
762                 void *userdata) {
763 
764         Settings *settings = data;
765 
766         assert(rvalue);
767         assert(settings);
768 
769         return parse_cpu_set_extend(rvalue, &settings->cpu_set, true, unit, filename, line, lvalue);
770 }
771 
772 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
773 
774 static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = {
775         [RESOLV_CONF_OFF] = "off",
776         [RESOLV_CONF_COPY_HOST] = "copy-host",
777         [RESOLV_CONF_COPY_STATIC] = "copy-static",
778         [RESOLV_CONF_COPY_UPLINK] = "copy-uplink",
779         [RESOLV_CONF_COPY_STUB] = "copy-stub",
780         [RESOLV_CONF_REPLACE_HOST] = "replace-host",
781         [RESOLV_CONF_REPLACE_STATIC] = "replace-static",
782         [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink",
783         [RESOLV_CONF_REPLACE_STUB] = "replace-stub",
784         [RESOLV_CONF_BIND_HOST] = "bind-host",
785         [RESOLV_CONF_BIND_STATIC] = "bind-static",
786         [RESOLV_CONF_BIND_UPLINK] = "bind-uplink",
787         [RESOLV_CONF_BIND_STUB] = "bind-stub",
788         [RESOLV_CONF_DELETE] = "delete",
789         [RESOLV_CONF_AUTO] = "auto",
790 };
791 
792 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode, ResolvConfMode, RESOLV_CONF_AUTO);
793 
parse_link_journal(const char * s,LinkJournal * ret_mode,bool * ret_try)794 int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try) {
795         int r;
796 
797         assert(s);
798         assert(ret_mode);
799         assert(ret_try);
800 
801         if (streq(s, "auto")) {
802                 *ret_mode = LINK_AUTO;
803                 *ret_try = false;
804         } else if (streq(s, "guest")) {
805                 *ret_mode = LINK_GUEST;
806                 *ret_try = false;
807         } else if (streq(s, "host")) {
808                 *ret_mode = LINK_HOST;
809                 *ret_try = false;
810         } else if (streq(s, "try-guest")) {
811                 *ret_mode = LINK_GUEST;
812                 *ret_try = true;
813         } else if (streq(s, "try-host")) {
814                 *ret_mode = LINK_HOST;
815                 *ret_try = true;
816         } else {
817                 /* Also support boolean values, to make things less confusing. */
818                 r = parse_boolean(s);
819                 if (r < 0)
820                         return r;
821 
822                 /* Let's consider "true" to be equivalent to "auto". */
823                 *ret_mode = r ? LINK_AUTO : LINK_NO;
824                 *ret_try = false;
825         }
826 
827         return 0;
828 }
829 
config_parse_link_journal(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)830 int config_parse_link_journal(
831                 const char *unit,
832                 const char *filename,
833                 unsigned line,
834                 const char *section,
835                 unsigned section_line,
836                 const char *lvalue,
837                 int ltype,
838                 const char *rvalue,
839                 void *data,
840                 void *userdata) {
841 
842         Settings *settings = data;
843         int r;
844 
845         assert(rvalue);
846         assert(settings);
847 
848         r = parse_link_journal(rvalue, &settings->link_journal, &settings->link_journal_try);
849         if (r < 0)
850                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse link journal mode, ignoring: %s", rvalue);
851 
852         return 0;
853 }
854 
855 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
856 
857 static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
858         [TIMEZONE_OFF] = "off",
859         [TIMEZONE_COPY] = "copy",
860         [TIMEZONE_BIND] = "bind",
861         [TIMEZONE_SYMLINK] = "symlink",
862         [TIMEZONE_DELETE] = "delete",
863         [TIMEZONE_AUTO] = "auto",
864 };
865 
866 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);
867 
868 DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership, "Failed to parse user namespace ownership mode");
869 
870 static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = {
871         [USER_NAMESPACE_OWNERSHIP_OFF] = "off",
872         [USER_NAMESPACE_OWNERSHIP_CHOWN] = "chown",
873         [USER_NAMESPACE_OWNERSHIP_MAP] = "map",
874         [USER_NAMESPACE_OWNERSHIP_AUTO] = "auto",
875 };
876 
877 DEFINE_STRING_TABLE_LOOKUP(user_namespace_ownership, UserNamespaceOwnership);
878 
config_parse_userns_chown(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)879 int config_parse_userns_chown(
880                 const char *unit,
881                 const char *filename,
882                 unsigned line,
883                 const char *section,
884                 unsigned section_line,
885                 const char *lvalue,
886                 int ltype,
887                 const char *rvalue,
888                 void *data,
889                 void *userdata) {
890 
891         UserNamespaceOwnership *ownership = data;
892         int r;
893 
894         assert(rvalue);
895         assert(ownership);
896 
897         /* Compatibility support for UserNamespaceChown=, whose job has been taken over by UserNamespaceOwnership= */
898 
899         r = parse_boolean(rvalue);
900         if (r < 0) {
901                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse user namespace ownership mode, ignoring: %s", rvalue);
902                 return 0;
903         }
904 
905         *ownership = r ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
906         return 0;
907 }
908 
config_parse_bind_user(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)909 int config_parse_bind_user(
910                 const char *unit,
911                 const char *filename,
912                 unsigned line,
913                 const char *section,
914                 unsigned section_line,
915                 const char *lvalue,
916                 int ltype,
917                 const char *rvalue,
918                 void *data,
919                 void *userdata) {
920 
921         char ***bind_user = data;
922         int r;
923 
924         assert(rvalue);
925         assert(bind_user);
926 
927         if (isempty(rvalue)) {
928                 *bind_user = strv_free(*bind_user);
929                 return 0;
930         }
931 
932         for (const char* p = rvalue;;) {
933                 _cleanup_free_ char *word = NULL;
934 
935                 r = extract_first_word(&p, &word, NULL, 0);
936                 if (r == -ENOMEM)
937                         return log_oom();
938                 if (r < 0) {
939                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse BindUser= list, ignoring: %s", rvalue);
940                         return 0;
941                 }
942                 if (r == 0)
943                         break;
944 
945                 if (!valid_user_group_name(word, 0)) {
946                         log_syntax(unit, LOG_WARNING, filename, line, 0, "User name '%s' not valid, ignoring.", word);
947                         return 0;
948                 }
949 
950                 if (strv_consume(bind_user, TAKE_PTR(word)) < 0)
951                         return log_oom();
952         }
953 
954         return 0;
955 }
956