]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
update TODO
[thirdparty/systemd.git] / src / core / load-fragment.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3efd4195 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
bb112710 7 Copyright 2012 Holger Hans Peter Freyther
a7334b09
LP
8
9 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 17 Lesser General Public License for more details.
a7334b09 18
5430f7f2 19 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
87f0e418 23#include <linux/oom.h>
3efd4195
LP
24#include <assert.h>
25#include <errno.h>
26#include <string.h>
87f0e418
LP
27#include <unistd.h>
28#include <fcntl.h>
94f04347
LP
29#include <sched.h>
30#include <sys/prctl.h>
15ae422b 31#include <sys/mount.h>
25e870b5 32#include <linux/fs.h>
45fb0699 33#include <sys/stat.h>
3d57c6ab
LP
34#include <sys/time.h>
35#include <sys/resource.h>
54d76c92
DM
36#include <sys/types.h>
37#include <grp.h>
17df7223 38
c0467cf3
RC
39#ifdef HAVE_SECCOMP
40#include <seccomp.h>
c0467cf3 41#endif
3efd4195 42
84f6181c 43#include "sd-messages.h"
87f0e418 44#include "unit.h"
3efd4195
LP
45#include "strv.h"
46#include "conf-parser.h"
47#include "load-fragment.h"
16354eff 48#include "log.h"
9eba9da4 49#include "ioprio.h"
94f04347
LP
50#include "securebits.h"
51#include "missing.h"
9e2f7c11 52#include "unit-name.h"
41f9172f 53#include "unit-printf.h"
7f110ff9 54#include "utf8.h"
9eb977db 55#include "path-util.h"
853b8397 56#include "env-util.h"
4ad49000 57#include "cgroup.h"
718db961
LP
58#include "bus-util.h"
59#include "bus-error.h"
17df7223 60#include "errno-list.h"
4298d0b5 61#include "af-list.h"
3efd4195 62
57183d11
LP
63#ifdef HAVE_SECCOMP
64#include "seccomp-util.h"
65#endif
66
eef65bf3 67#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
68int config_parse_warn_compat(
69 const char *unit,
70 const char *filename,
71 unsigned line,
72 const char *section,
73 unsigned section_line,
74 const char *lvalue,
75 int ltype,
76 const char *rvalue,
77 void *data,
78 void *userdata) {
e8e581bf
ZJS
79
80 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
81 "Support for option %s= has been disabled at compile time and is ignored",
82 lvalue);
07459bb6
FF
83 return 0;
84}
85#endif
86
e8e581bf
ZJS
87int config_parse_unit_deps(const char* unit,
88 const char *filename,
89 unsigned line,
90 const char *section,
71a61510 91 unsigned section_line,
e8e581bf
ZJS
92 const char *lvalue,
93 int ltype,
94 const char *rvalue,
95 void *data,
96 void *userdata) {
3efd4195 97
f975e971 98 UnitDependency d = ltype;
87f0e418 99 Unit *u = userdata;
3efd4195
LP
100 char *w;
101 size_t l;
102 char *state;
103
104 assert(filename);
105 assert(lvalue);
106 assert(rvalue);
3efd4195 107
f60f22df 108 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 109 _cleanup_free_ char *t = NULL, *k = NULL;
3efd4195 110 int r;
3efd4195 111
57020a3a
LP
112 t = strndup(w, l);
113 if (!t)
74051b9b 114 return log_oom();
3efd4195 115
19f6d710
LP
116 r = unit_name_printf(u, t, &k);
117 if (r < 0) {
118 log_syntax(unit, LOG_ERR, filename, line, -r,
119 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
120 continue;
121 }
9e2f7c11 122
701cc384 123 r = unit_add_dependency_by_name(u, d, k, NULL, true);
57020a3a 124 if (r < 0)
e8e581bf
ZJS
125 log_syntax(unit, LOG_ERR, filename, line, -r,
126 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
3efd4195
LP
127 }
128
129 return 0;
130}
131
e8e581bf
ZJS
132int config_parse_unit_string_printf(const char *unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
71a61510 136 unsigned section_line,
e8e581bf
ZJS
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *data,
141 void *userdata) {
932921b5
LP
142
143 Unit *u = userdata;
74051b9b 144 _cleanup_free_ char *k = NULL;
19f6d710 145 int r;
932921b5
LP
146
147 assert(filename);
148 assert(lvalue);
149 assert(rvalue);
f2d3769a 150 assert(u);
932921b5 151
19f6d710
LP
152 r = unit_full_printf(u, rvalue, &k);
153 if (r < 0)
154 log_syntax(unit, LOG_ERR, filename, line, -r,
155 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
932921b5 156
71a61510 157 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 158 k ? k : rvalue, data, userdata);
932921b5
LP
159}
160
e8e581bf
ZJS
161int config_parse_unit_strv_printf(const char *unit,
162 const char *filename,
163 unsigned line,
164 const char *section,
71a61510 165 unsigned section_line,
e8e581bf
ZJS
166 const char *lvalue,
167 int ltype,
168 const char *rvalue,
169 void *data,
170 void *userdata) {
8fef7659
LP
171
172 Unit *u = userdata;
74051b9b 173 _cleanup_free_ char *k = NULL;
19f6d710 174 int r;
8fef7659
LP
175
176 assert(filename);
177 assert(lvalue);
178 assert(rvalue);
179 assert(u);
180
19f6d710
LP
181 r = unit_full_printf(u, rvalue, &k);
182 if (r < 0)
183 log_syntax(unit, LOG_ERR, filename, line, -r,
184 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
8fef7659 185
71a61510 186 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 187 k ? k : rvalue, data, userdata);
8fef7659
LP
188}
189
e8e581bf
ZJS
190int config_parse_unit_path_printf(const char *unit,
191 const char *filename,
192 unsigned line,
193 const char *section,
71a61510 194 unsigned section_line,
e8e581bf
ZJS
195 const char *lvalue,
196 int ltype,
197 const char *rvalue,
198 void *data,
199 void *userdata) {
6ea832a2
LP
200
201 Unit *u = userdata;
74051b9b 202 _cleanup_free_ char *k = NULL;
19f6d710 203 int r;
6ea832a2
LP
204
205 assert(filename);
206 assert(lvalue);
207 assert(rvalue);
6ea832a2
LP
208 assert(u);
209
19f6d710
LP
210 r = unit_full_printf(u, rvalue, &k);
211 if (r < 0)
212 log_syntax(unit, LOG_ERR, filename, line, -r,
213 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
6ea832a2 214
71a61510 215 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 216 k ? k : rvalue, data, userdata);
6ea832a2
LP
217}
218
e8e581bf
ZJS
219int config_parse_socket_listen(const char *unit,
220 const char *filename,
221 unsigned line,
222 const char *section,
71a61510 223 unsigned section_line,
e8e581bf
ZJS
224 const char *lvalue,
225 int ltype,
226 const char *rvalue,
227 void *data,
228 void *userdata) {
42f4e3c4 229
49f91047 230 SocketPort *p, *tail;
542563ba 231 Socket *s;
19f6d710 232 int r;
16354eff 233
42f4e3c4
LP
234 assert(filename);
235 assert(lvalue);
236 assert(rvalue);
237 assert(data);
238
595ed347 239 s = SOCKET(data);
542563ba 240
74051b9b
LP
241 if (isempty(rvalue)) {
242 /* An empty assignment removes all ports */
243 socket_free_ports(s);
244 return 0;
245 }
246
7f110ff9
LP
247 p = new0(SocketPort, 1);
248 if (!p)
74051b9b 249 return log_oom();
916abb21 250
74051b9b 251 if (ltype != SOCKET_SOCKET) {
916abb21 252
74051b9b 253 p->type = ltype;
19f6d710
LP
254 r = unit_full_printf(UNIT(s), rvalue, &p->path);
255 if (r < 0) {
487060c2
LP
256 p->path = strdup(rvalue);
257 if (!p->path) {
258 free(p);
259 return log_oom();
260 } else
19f6d710
LP
261 log_syntax(unit, LOG_ERR, filename, line, -r,
262 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
916abb21
LP
263 }
264
265 path_kill_slashes(p->path);
266
7a22745a 267 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 268 _cleanup_free_ char *k = NULL;
1fd45a90 269
7a22745a 270 p->type = SOCKET_SOCKET;
19f6d710
LP
271 r = unit_full_printf(UNIT(s), rvalue, &k);
272 if (r < 0)
273 log_syntax(unit, LOG_ERR, filename, line, -r,
274 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
7a22745a 275
487060c2 276 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
1fd45a90 277 if (r < 0) {
19f6d710 278 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 279 "Failed to parse address value, ignoring: %s", rvalue);
7a22745a
LP
280 free(p);
281 return 0;
282 }
283
542563ba 284 } else {
74051b9b 285 _cleanup_free_ char *k = NULL;
1fd45a90 286
542563ba 287 p->type = SOCKET_SOCKET;
19f6d710
LP
288 r = unit_full_printf(UNIT(s), rvalue, &k);
289 if (r < 0)
290 log_syntax(unit, LOG_ERR, filename, line, -r,
291 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
542563ba 292
487060c2 293 r = socket_address_parse(&p->address, k ? k : rvalue);
1fd45a90 294 if (r < 0) {
19f6d710 295 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 296 "Failed to parse address value, ignoring: %s", rvalue);
542563ba 297 free(p);
c0b34696 298 return 0;
542563ba
LP
299 }
300
301 if (streq(lvalue, "ListenStream"))
302 p->address.type = SOCK_STREAM;
303 else if (streq(lvalue, "ListenDatagram"))
304 p->address.type = SOCK_DGRAM;
305 else {
306 assert(streq(lvalue, "ListenSequentialPacket"));
307 p->address.type = SOCK_SEQPACKET;
308 }
309
310 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
e8e581bf
ZJS
311 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
312 "Address family not supported, ignoring: %s", rvalue);
542563ba 313 free(p);
c0b34696 314 return 0;
542563ba 315 }
16354eff
LP
316 }
317
542563ba 318 p->fd = -1;
2e41a51e 319 p->socket = s;
49f91047
LP
320
321 if (s->ports) {
71fda00f
LP
322 LIST_FIND_TAIL(port, s->ports, tail);
323 LIST_INSERT_AFTER(port, s->ports, tail, p);
49f91047 324 } else
71fda00f 325 LIST_PREPEND(port, s->ports, p);
542563ba 326
16354eff 327 return 0;
42f4e3c4
LP
328}
329
e8e581bf
ZJS
330int config_parse_socket_bind(const char *unit,
331 const char *filename,
332 unsigned line,
333 const char *section,
71a61510 334 unsigned section_line,
e8e581bf
ZJS
335 const char *lvalue,
336 int ltype,
337 const char *rvalue,
338 void *data,
339 void *userdata) {
42f4e3c4 340
542563ba 341 Socket *s;
c0120d99 342 SocketAddressBindIPv6Only b;
42f4e3c4
LP
343
344 assert(filename);
345 assert(lvalue);
346 assert(rvalue);
347 assert(data);
348
595ed347 349 s = SOCKET(data);
542563ba 350
5198dabc
LP
351 b = socket_address_bind_ipv6_only_from_string(rvalue);
352 if (b < 0) {
c0120d99
LP
353 int r;
354
5198dabc
LP
355 r = parse_boolean(rvalue);
356 if (r < 0) {
e8e581bf
ZJS
357 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
358 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
c0b34696 359 return 0;
c0120d99 360 }
42f4e3c4 361
c0120d99
LP
362 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
363 } else
364 s->bind_ipv6_only = b;
542563ba 365
42f4e3c4
LP
366 return 0;
367}
368
e8e581bf
ZJS
369int config_parse_exec_nice(const char *unit,
370 const char *filename,
371 unsigned line,
372 const char *section,
71a61510 373 unsigned section_line,
e8e581bf
ZJS
374 const char *lvalue,
375 int ltype,
376 const char *rvalue,
377 void *data,
378 void *userdata) {
034c6ed7 379
fb33a393 380 ExecContext *c = data;
e8e581bf 381 int priority, r;
034c6ed7
LP
382
383 assert(filename);
384 assert(lvalue);
385 assert(rvalue);
386 assert(data);
387
e8e581bf
ZJS
388 r = safe_atoi(rvalue, &priority);
389 if (r < 0) {
390 log_syntax(unit, LOG_ERR, filename, line, -r,
391 "Failed to parse nice priority, ignoring: %s. ", rvalue);
c0b34696 392 return 0;
034c6ed7
LP
393 }
394
395 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
e8e581bf
ZJS
396 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
397 "Nice priority out of range, ignoring: %s", rvalue);
c0b34696 398 return 0;
034c6ed7
LP
399 }
400
fb33a393 401 c->nice = priority;
71155933 402 c->nice_set = true;
fb33a393 403
034c6ed7
LP
404 return 0;
405}
406
e8e581bf
ZJS
407int config_parse_exec_oom_score_adjust(const char* unit,
408 const char *filename,
409 unsigned line,
410 const char *section,
71a61510 411 unsigned section_line,
e8e581bf
ZJS
412 const char *lvalue,
413 int ltype,
414 const char *rvalue,
415 void *data,
416 void *userdata) {
034c6ed7 417
fb33a393 418 ExecContext *c = data;
e8e581bf 419 int oa, r;
034c6ed7
LP
420
421 assert(filename);
422 assert(lvalue);
423 assert(rvalue);
424 assert(data);
425
e8e581bf
ZJS
426 r = safe_atoi(rvalue, &oa);
427 if (r < 0) {
428 log_syntax(unit, LOG_ERR, filename, line, -r,
429 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
c0b34696 430 return 0;
034c6ed7
LP
431 }
432
dd6c17b1 433 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
e8e581bf
ZJS
434 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
435 "OOM score adjust value out of range, ignoring: %s", rvalue);
c0b34696 436 return 0;
034c6ed7
LP
437 }
438
dd6c17b1
LP
439 c->oom_score_adjust = oa;
440 c->oom_score_adjust_set = true;
fb33a393 441
034c6ed7
LP
442 return 0;
443}
444
e8e581bf
ZJS
445int config_parse_exec(const char *unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
71a61510 449 unsigned section_line,
e8e581bf
ZJS
450 const char *lvalue,
451 int ltype,
452 const char *rvalue,
453 void *data,
454 void *userdata) {
034c6ed7 455
61e5d8ed
LP
456 ExecCommand **e = data, *nce;
457 char *path, **n;
034c6ed7 458 unsigned k;
7f110ff9 459 int r;
034c6ed7
LP
460
461 assert(filename);
462 assert(lvalue);
463 assert(rvalue);
61e5d8ed 464 assert(e);
034c6ed7 465
74051b9b
LP
466 e += ltype;
467
468 if (isempty(rvalue)) {
469 /* An empty assignment resets the list */
470 exec_command_free_list(*e);
471 *e = NULL;
472 return 0;
473 }
474
6c666e26
LP
475 /* We accept an absolute path as first argument, or
476 * alternatively an absolute prefixed with @ to allow
477 * overriding of argv[0]. */
61e5d8ed 478 for (;;) {
0f67f1ef 479 int i;
61e5d8ed
LP
480 char *w;
481 size_t l;
482 char *state;
b708e7ce 483 bool honour_argv0 = false, ignore = false;
6c666e26 484
61e5d8ed
LP
485 path = NULL;
486 nce = NULL;
487 n = NULL;
6c666e26 488
61e5d8ed 489 rvalue += strspn(rvalue, WHITESPACE);
034c6ed7 490
61e5d8ed
LP
491 if (rvalue[0] == 0)
492 break;
034c6ed7 493
0f67f1ef
ZJS
494 for (i = 0; i < 2; i++) {
495 if (rvalue[0] == '-' && !ignore) {
496 ignore = true;
497 rvalue ++;
498 }
b708e7ce 499
0f67f1ef
ZJS
500 if (rvalue[0] == '@' && !honour_argv0) {
501 honour_argv0 = true;
502 rvalue ++;
503 }
b708e7ce 504 }
61e5d8ed 505
b708e7ce 506 if (*rvalue != '/') {
e8e581bf
ZJS
507 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
508 "Executable path is not absolute, ignoring: %s", rvalue);
c0b34696 509 return 0;
6c666e26 510 }
034c6ed7 511
61e5d8ed
LP
512 k = 0;
513 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 514 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 515 break;
034c6ed7 516
61e5d8ed
LP
517 k++;
518 }
034c6ed7 519
7f110ff9
LP
520 n = new(char*, k + !honour_argv0);
521 if (!n)
74051b9b 522 return log_oom();
61e5d8ed
LP
523
524 k = 0;
61e5d8ed 525 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 526 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 527 break;
641906e9 528 else if (strneq(w, "\\;", MAX(l, 1U)))
7e1a84f5 529 w ++;
61e5d8ed 530
b708e7ce
LP
531 if (honour_argv0 && w == rvalue) {
532 assert(!path);
7f110ff9
LP
533
534 path = strndup(w, l);
535 if (!path) {
74051b9b 536 r = log_oom();
7f110ff9
LP
537 goto fail;
538 }
539
540 if (!utf8_is_valid(path)) {
b5d74213 541 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
7f110ff9 542 r = 0;
61e5d8ed 543 goto fail;
7f110ff9
LP
544 }
545
61e5d8ed 546 } else {
7f110ff9
LP
547 char *c;
548
549 c = n[k++] = cunescape_length(w, l);
550 if (!c) {
74051b9b 551 r = log_oom();
61e5d8ed 552 goto fail;
7f110ff9
LP
553 }
554
555 if (!utf8_is_valid(c)) {
b5d74213 556 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
7f110ff9
LP
557 r = 0;
558 goto fail;
559 }
61e5d8ed
LP
560 }
561 }
562
563 n[k] = NULL;
564
565 if (!n[0]) {
e8e581bf
ZJS
566 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
567 "Invalid command line, ignoring: %s", rvalue);
7f110ff9
LP
568 r = 0;
569 goto fail;
61e5d8ed
LP
570 }
571
7f110ff9
LP
572 if (!path) {
573 path = strdup(n[0]);
574 if (!path) {
74051b9b 575 r = log_oom();
61e5d8ed 576 goto fail;
7f110ff9
LP
577 }
578 }
6c666e26 579
61e5d8ed 580 assert(path_is_absolute(path));
6c666e26 581
7f110ff9
LP
582 nce = new0(ExecCommand, 1);
583 if (!nce) {
74051b9b 584 r = log_oom();
61e5d8ed 585 goto fail;
7f110ff9 586 }
61e5d8ed
LP
587
588 nce->argv = n;
589 nce->path = path;
b708e7ce 590 nce->ignore = ignore;
034c6ed7 591
61e5d8ed 592 path_kill_slashes(nce->path);
034c6ed7 593
61e5d8ed 594 exec_command_append_list(e, nce);
01f78473 595
61e5d8ed
LP
596 rvalue = state;
597 }
034c6ed7
LP
598
599 return 0;
600
601fail:
6c666e26
LP
602 n[k] = NULL;
603 strv_free(n);
604 free(path);
034c6ed7
LP
605 free(nce);
606
7f110ff9 607 return r;
034c6ed7
LP
608}
609
f975e971
LP
610DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
611DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 612
e8e581bf
ZJS
613int config_parse_socket_bindtodevice(const char* unit,
614 const char *filename,
615 unsigned line,
616 const char *section,
71a61510 617 unsigned section_line,
e8e581bf
ZJS
618 const char *lvalue,
619 int ltype,
620 const char *rvalue,
621 void *data,
622 void *userdata) {
acbb0225
LP
623
624 Socket *s = data;
625 char *n;
626
627 assert(filename);
628 assert(lvalue);
629 assert(rvalue);
630 assert(data);
631
632 if (rvalue[0] && !streq(rvalue, "*")) {
74051b9b
LP
633 n = strdup(rvalue);
634 if (!n)
635 return log_oom();
acbb0225
LP
636 } else
637 n = NULL;
638
639 free(s->bind_to_device);
640 s->bind_to_device = n;
641
642 return 0;
643}
644
f975e971
LP
645DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
646DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 647
e8e581bf
ZJS
648int config_parse_exec_io_class(const char *unit,
649 const char *filename,
650 unsigned line,
651 const char *section,
71a61510 652 unsigned section_line,
e8e581bf
ZJS
653 const char *lvalue,
654 int ltype,
655 const char *rvalue,
656 void *data,
657 void *userdata) {
94f04347
LP
658
659 ExecContext *c = data;
660 int x;
661
662 assert(filename);
663 assert(lvalue);
664 assert(rvalue);
665 assert(data);
666
f8b69d1d
MS
667 x = ioprio_class_from_string(rvalue);
668 if (x < 0) {
e8e581bf
ZJS
669 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
670 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 671 return 0;
0d87eb42 672 }
94f04347
LP
673
674 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
675 c->ioprio_set = true;
676
677 return 0;
678}
679
e8e581bf
ZJS
680int config_parse_exec_io_priority(const char *unit,
681 const char *filename,
682 unsigned line,
683 const char *section,
71a61510 684 unsigned section_line,
e8e581bf
ZJS
685 const char *lvalue,
686 int ltype,
687 const char *rvalue,
688 void *data,
689 void *userdata) {
94f04347
LP
690
691 ExecContext *c = data;
e8e581bf 692 int i, r;
94f04347
LP
693
694 assert(filename);
695 assert(lvalue);
696 assert(rvalue);
697 assert(data);
698
e8e581bf
ZJS
699 r = safe_atoi(rvalue, &i);
700 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
701 log_syntax(unit, LOG_ERR, filename, line, -r,
702 "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 703 return 0;
071830ff
LP
704 }
705
94f04347
LP
706 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
707 c->ioprio_set = true;
708
071830ff
LP
709 return 0;
710}
711
e8e581bf
ZJS
712int config_parse_exec_cpu_sched_policy(const char *unit,
713 const char *filename,
714 unsigned line,
715 const char *section,
71a61510 716 unsigned section_line,
e8e581bf
ZJS
717 const char *lvalue,
718 int ltype,
719 const char *rvalue,
720 void *data,
721 void *userdata) {
9eba9da4 722
94f04347
LP
723
724 ExecContext *c = data;
725 int x;
726
727 assert(filename);
728 assert(lvalue);
729 assert(rvalue);
730 assert(data);
731
f8b69d1d
MS
732 x = sched_policy_from_string(rvalue);
733 if (x < 0) {
e8e581bf
ZJS
734 log_syntax(unit, LOG_ERR, filename, line, -x,
735 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 736 return 0;
0d87eb42 737 }
94f04347
LP
738
739 c->cpu_sched_policy = x;
bb112710
HHPF
740 /* Moving to or from real-time policy? We need to adjust the priority */
741 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
742 c->cpu_sched_set = true;
743
744 return 0;
745}
746
e8e581bf
ZJS
747int config_parse_exec_cpu_sched_prio(const char *unit,
748 const char *filename,
749 unsigned line,
750 const char *section,
71a61510 751 unsigned section_line,
e8e581bf
ZJS
752 const char *lvalue,
753 int ltype,
754 const char *rvalue,
755 void *data,
756 void *userdata) {
9eba9da4
LP
757
758 ExecContext *c = data;
e8e581bf 759 int i, min, max, r;
9eba9da4
LP
760
761 assert(filename);
762 assert(lvalue);
763 assert(rvalue);
764 assert(data);
765
e8e581bf
ZJS
766 r = safe_atoi(rvalue, &i);
767 if (r < 0) {
768 log_syntax(unit, LOG_ERR, filename, line, -r,
769 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 770 return 0;
94f04347 771 }
9eba9da4 772
bb112710
HHPF
773 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
774 min = sched_get_priority_min(c->cpu_sched_policy);
775 max = sched_get_priority_max(c->cpu_sched_policy);
776
777 if (i < min || i > max) {
e8e581bf
ZJS
778 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
779 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
780 return 0;
781 }
782
94f04347
LP
783 c->cpu_sched_priority = i;
784 c->cpu_sched_set = true;
785
786 return 0;
787}
788
e8e581bf
ZJS
789int config_parse_exec_cpu_affinity(const char *unit,
790 const char *filename,
791 unsigned line,
792 const char *section,
71a61510 793 unsigned section_line,
e8e581bf
ZJS
794 const char *lvalue,
795 int ltype,
796 const char *rvalue,
797 void *data,
798 void *userdata) {
94f04347
LP
799
800 ExecContext *c = data;
801 char *w;
802 size_t l;
803 char *state;
804
805 assert(filename);
806 assert(lvalue);
807 assert(rvalue);
808 assert(data);
809
74051b9b
LP
810 if (isempty(rvalue)) {
811 /* An empty assignment resets the CPU list */
812 if (c->cpuset)
813 CPU_FREE(c->cpuset);
814 c->cpuset = NULL;
815 return 0;
816 }
817
f60f22df 818 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 819 _cleanup_free_ char *t = NULL;
94f04347
LP
820 int r;
821 unsigned cpu;
822
c040936b
ZJS
823 t = strndup(w, l);
824 if (!t)
74051b9b 825 return log_oom();
94f04347 826
487393e9 827 r = safe_atou(t, &cpu);
487393e9 828
c040936b
ZJS
829 if (!c->cpuset) {
830 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
831 if (!c->cpuset)
74051b9b 832 return log_oom();
c040936b 833 }
82c121a4 834
82c121a4 835 if (r < 0 || cpu >= c->cpuset_ncpus) {
e8e581bf
ZJS
836 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
837 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
c0b34696 838 return 0;
9eba9da4 839 }
94f04347 840
82c121a4 841 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
9eba9da4
LP
842 }
843
94f04347
LP
844 return 0;
845}
846
e8e581bf
ZJS
847int config_parse_exec_capabilities(const char *unit,
848 const char *filename,
849 unsigned line,
850 const char *section,
71a61510 851 unsigned section_line,
e8e581bf
ZJS
852 const char *lvalue,
853 int ltype,
854 const char *rvalue,
855 void *data,
856 void *userdata) {
94f04347
LP
857
858 ExecContext *c = data;
859 cap_t cap;
860
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
865
74051b9b
LP
866 cap = cap_from_text(rvalue);
867 if (!cap) {
e8e581bf
ZJS
868 log_syntax(unit, LOG_ERR, filename, line, errno,
869 "Failed to parse capabilities, ignoring: %s", rvalue);
c0b34696 870 return 0;
94f04347
LP
871 }
872
873 if (c->capabilities)
874 cap_free(c->capabilities);
875 c->capabilities = cap;
876
877 return 0;
878}
879
e8e581bf
ZJS
880int config_parse_exec_secure_bits(const char *unit,
881 const char *filename,
882 unsigned line,
883 const char *section,
71a61510 884 unsigned section_line,
e8e581bf
ZJS
885 const char *lvalue,
886 int ltype,
887 const char *rvalue,
888 void *data,
889 void *userdata) {
94f04347
LP
890
891 ExecContext *c = data;
892 char *w;
893 size_t l;
894 char *state;
895
896 assert(filename);
897 assert(lvalue);
898 assert(rvalue);
899 assert(data);
900
74051b9b
LP
901 if (isempty(rvalue)) {
902 /* An empty assignment resets the field */
903 c->secure_bits = 0;
904 return 0;
905 }
906
f60f22df 907 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347 908 if (first_word(w, "keep-caps"))
cbb21cca 909 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
94f04347 910 else if (first_word(w, "keep-caps-locked"))
cbb21cca 911 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
94f04347 912 else if (first_word(w, "no-setuid-fixup"))
cbb21cca 913 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
94f04347 914 else if (first_word(w, "no-setuid-fixup-locked"))
cbb21cca 915 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
94f04347 916 else if (first_word(w, "noroot"))
cbb21cca 917 c->secure_bits |= 1<<SECURE_NOROOT;
94f04347 918 else if (first_word(w, "noroot-locked"))
cbb21cca 919 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
9eba9da4 920 else {
e8e581bf
ZJS
921 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
922 "Failed to parse secure bits, ignoring: %s", rvalue);
c0b34696 923 return 0;
9eba9da4
LP
924 }
925 }
926
94f04347
LP
927 return 0;
928}
929
e8e581bf
ZJS
930int config_parse_bounding_set(const char *unit,
931 const char *filename,
932 unsigned line,
933 const char *section,
71a61510 934 unsigned section_line,
e8e581bf
ZJS
935 const char *lvalue,
936 int ltype,
937 const char *rvalue,
938 void *data,
939 void *userdata) {
94f04347 940
ec8927ca 941 uint64_t *capability_bounding_set_drop = data;
94f04347
LP
942 char *w;
943 size_t l;
944 char *state;
260abb78
LP
945 bool invert = false;
946 uint64_t sum = 0;
94f04347
LP
947
948 assert(filename);
949 assert(lvalue);
950 assert(rvalue);
951 assert(data);
952
260abb78
LP
953 if (rvalue[0] == '~') {
954 invert = true;
955 rvalue++;
956 }
957
958 /* Note that we store this inverted internally, since the
959 * kernel wants it like this. But we actually expose it
960 * non-inverted everywhere to have a fully normalized
961 * interface. */
962
f60f22df 963 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 964 _cleanup_free_ char *t = NULL;
94f04347
LP
965 int r;
966 cap_value_t cap;
967
ec8927ca
LP
968 t = strndup(w, l);
969 if (!t)
74051b9b 970 return log_oom();
94f04347
LP
971
972 r = cap_from_name(t, &cap);
94f04347 973 if (r < 0) {
e8e581bf
ZJS
974 log_syntax(unit, LOG_ERR, filename, line, errno,
975 "Failed to parse capability in bounding set, ignoring: %s", t);
8351ceae 976 continue;
94f04347
LP
977 }
978
260abb78 979 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
94f04347 980 }
9eba9da4 981
260abb78 982 if (invert)
ec8927ca 983 *capability_bounding_set_drop |= sum;
260abb78 984 else
ec8927ca 985 *capability_bounding_set_drop |= ~sum;
260abb78 986
9eba9da4
LP
987 return 0;
988}
989
e8e581bf
ZJS
990int config_parse_limit(const char *unit,
991 const char *filename,
992 unsigned line,
993 const char *section,
71a61510 994 unsigned section_line,
e8e581bf
ZJS
995 const char *lvalue,
996 int ltype,
997 const char *rvalue,
998 void *data,
999 void *userdata) {
94f04347
LP
1000
1001 struct rlimit **rl = data;
1002 unsigned long long u;
94f04347
LP
1003
1004 assert(filename);
1005 assert(lvalue);
1006 assert(rvalue);
1007 assert(data);
1008
f975e971
LP
1009 rl += ltype;
1010
3d57c6ab
LP
1011 if (streq(rvalue, "infinity"))
1012 u = (unsigned long long) RLIM_INFINITY;
e8e581bf
ZJS
1013 else {
1014 int r;
1015
1016 r = safe_atollu(rvalue, &u);
1017 if (r < 0) {
1018 log_syntax(unit, LOG_ERR, filename, line, -r,
1019 "Failed to parse resource value, ignoring: %s", rvalue);
1020 return 0;
1021 }
94f04347
LP
1022 }
1023
74051b9b
LP
1024 if (!*rl) {
1025 *rl = new(struct rlimit, 1);
1026 if (!*rl)
1027 return log_oom();
1028 }
9eba9da4 1029
94f04347 1030 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
1031 return 0;
1032}
1033
07459bb6 1034#ifdef HAVE_SYSV_COMPAT
e8e581bf
ZJS
1035int config_parse_sysv_priority(const char *unit,
1036 const char *filename,
1037 unsigned line,
1038 const char *section,
71a61510 1039 unsigned section_line,
e8e581bf
ZJS
1040 const char *lvalue,
1041 int ltype,
1042 const char *rvalue,
1043 void *data,
1044 void *userdata) {
a9a1e00a
LP
1045
1046 int *priority = data;
e8e581bf 1047 int i, r;
a9a1e00a
LP
1048
1049 assert(filename);
1050 assert(lvalue);
1051 assert(rvalue);
1052 assert(data);
1053
e8e581bf
ZJS
1054 r = safe_atoi(rvalue, &i);
1055 if (r < 0 || i < 0) {
1056 log_syntax(unit, LOG_ERR, filename, line, -r,
1057 "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1058 return 0;
a9a1e00a
LP
1059 }
1060
1061 *priority = (int) i;
1062 return 0;
1063}
07459bb6 1064#endif
a9a1e00a 1065
f975e971 1066DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1067
e8e581bf
ZJS
1068int config_parse_kill_signal(const char *unit,
1069 const char *filename,
1070 unsigned line,
1071 const char *section,
71a61510 1072 unsigned section_line,
e8e581bf
ZJS
1073 const char *lvalue,
1074 int ltype,
1075 const char *rvalue,
1076 void *data,
1077 void *userdata) {
2e22afe9
LP
1078
1079 int *sig = data;
1080 int r;
1081
1082 assert(filename);
1083 assert(lvalue);
1084 assert(rvalue);
1085 assert(sig);
1086
74051b9b
LP
1087 r = signal_from_string_try_harder(rvalue);
1088 if (r <= 0) {
e8e581bf
ZJS
1089 log_syntax(unit, LOG_ERR, filename, line, -r,
1090 "Failed to parse kill signal, ignoring: %s", rvalue);
c0b34696 1091 return 0;
2e22afe9
LP
1092 }
1093
1094 *sig = r;
1095 return 0;
1096}
1097
e8e581bf
ZJS
1098int config_parse_exec_mount_flags(const char *unit,
1099 const char *filename,
1100 unsigned line,
1101 const char *section,
71a61510 1102 unsigned section_line,
e8e581bf
ZJS
1103 const char *lvalue,
1104 int ltype,
1105 const char *rvalue,
1106 void *data,
1107 void *userdata) {
15ae422b
LP
1108
1109 ExecContext *c = data;
1110 char *w;
1111 size_t l;
1112 char *state;
1113 unsigned long flags = 0;
1114
1115 assert(filename);
1116 assert(lvalue);
1117 assert(rvalue);
1118 assert(data);
1119
ac97e2c5 1120 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
7fd1b19b 1121 _cleanup_free_ char *t;
ac97e2c5
ZJS
1122
1123 t = strndup(w, l);
1124 if (!t)
74051b9b 1125 return log_oom();
ac97e2c5
ZJS
1126
1127 if (streq(t, "shared"))
15ae422b 1128 flags |= MS_SHARED;
ac97e2c5 1129 else if (streq(t, "slave"))
15ae422b 1130 flags |= MS_SLAVE;
ac97e2c5 1131 else if (streq(w, "private"))
15ae422b
LP
1132 flags |= MS_PRIVATE;
1133 else {
e8e581bf
ZJS
1134 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1135 "Failed to parse mount flag %s, ignoring: %s",
1136 t, rvalue);
c0b34696 1137 return 0;
15ae422b
LP
1138 }
1139 }
1140
1141 c->mount_flags = flags;
1142 return 0;
1143}
1144
5f8640fb
LP
1145int config_parse_exec_selinux_context(
1146 const char *unit,
1147 const char *filename,
1148 unsigned line,
1149 const char *section,
1150 unsigned section_line,
1151 const char *lvalue,
1152 int ltype,
1153 const char *rvalue,
1154 void *data,
1155 void *userdata) {
1156
1157 ExecContext *c = data;
1158 Unit *u = userdata;
1159 bool ignore;
1160 char *k;
1161 int r;
1162
1163 assert(filename);
1164 assert(lvalue);
1165 assert(rvalue);
1166 assert(data);
1167
1168 if (isempty(rvalue)) {
1169 free(c->selinux_context);
1170 c->selinux_context = NULL;
1171 c->selinux_context_ignore = false;
1172 return 0;
1173 }
1174
1175 if (rvalue[0] == '-') {
1176 ignore = true;
1177 rvalue++;
1178 } else
1179 ignore = false;
1180
1181 r = unit_name_printf(u, rvalue, &k);
1182 if (r < 0) {
1183 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1184 return 0;
1185 }
1186
1187 free(c->selinux_context);
1188 c->selinux_context = k;
1189 c->selinux_context_ignore = ignore;
1190
1191 return 0;
1192}
1193
eef65bf3
MS
1194int config_parse_exec_apparmor_profile(
1195 const char *unit,
1196 const char *filename,
1197 unsigned line,
1198 const char *section,
1199 unsigned section_line,
1200 const char *lvalue,
1201 int ltype,
1202 const char *rvalue,
1203 void *data,
1204 void *userdata) {
1205
1206 ExecContext *c = data;
1207 Unit *u = userdata;
1208 bool ignore;
1209 char *k;
1210 int r;
1211
1212 assert(filename);
1213 assert(lvalue);
1214 assert(rvalue);
1215 assert(data);
1216
1217 if (isempty(rvalue)) {
1218 free(c->apparmor_profile);
1219 c->apparmor_profile = NULL;
1220 c->apparmor_profile_ignore = false;
1221 return 0;
1222 }
1223
1224 if (rvalue[0] == '-') {
1225 ignore = true;
1226 rvalue++;
1227 } else
1228 ignore = false;
1229
1230 r = unit_name_printf(u, rvalue, &k);
1231 if (r < 0) {
1232 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1233 return 0;
1234 }
1235
1236 free(c->apparmor_profile);
1237 c->apparmor_profile = k;
1238 c->apparmor_profile_ignore = ignore;
1239
1240 return 0;
1241}
1242
e8e581bf
ZJS
1243int config_parse_timer(const char *unit,
1244 const char *filename,
1245 unsigned line,
1246 const char *section,
71a61510 1247 unsigned section_line,
e8e581bf
ZJS
1248 const char *lvalue,
1249 int ltype,
1250 const char *rvalue,
1251 void *data,
1252 void *userdata) {
871d7de4
LP
1253
1254 Timer *t = data;
36697dc0 1255 usec_t u = 0;
871d7de4
LP
1256 TimerValue *v;
1257 TimerBase b;
36697dc0
LP
1258 CalendarSpec *c = NULL;
1259 clockid_t id;
871d7de4
LP
1260
1261 assert(filename);
1262 assert(lvalue);
1263 assert(rvalue);
1264 assert(data);
1265
74051b9b
LP
1266 if (isempty(rvalue)) {
1267 /* Empty assignment resets list */
1268 timer_free_values(t);
1269 return 0;
1270 }
1271
36697dc0
LP
1272 b = timer_base_from_string(lvalue);
1273 if (b < 0) {
e8e581bf
ZJS
1274 log_syntax(unit, LOG_ERR, filename, line, -b,
1275 "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1276 return 0;
871d7de4
LP
1277 }
1278
36697dc0
LP
1279 if (b == TIMER_CALENDAR) {
1280 if (calendar_spec_from_string(rvalue, &c) < 0) {
e8e581bf
ZJS
1281 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1282 "Failed to parse calendar specification, ignoring: %s",
1283 rvalue);
36697dc0
LP
1284 return 0;
1285 }
1286
1287 id = CLOCK_REALTIME;
1288 } else {
7f602784 1289 if (parse_sec(rvalue, &u) < 0) {
e8e581bf
ZJS
1290 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1291 "Failed to parse timer value, ignoring: %s",
1292 rvalue);
36697dc0
LP
1293 return 0;
1294 }
1295
1296 id = CLOCK_MONOTONIC;
871d7de4
LP
1297 }
1298
36697dc0
LP
1299 v = new0(TimerValue, 1);
1300 if (!v)
74051b9b 1301 return log_oom();
871d7de4
LP
1302
1303 v->base = b;
36697dc0 1304 v->clock_id = id;
871d7de4 1305 v->value = u;
36697dc0 1306 v->calendar_spec = c;
871d7de4 1307
71fda00f 1308 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1309
1310 return 0;
1311}
1312
3ecaa09b
LP
1313int config_parse_trigger_unit(
1314 const char *unit,
1315 const char *filename,
1316 unsigned line,
1317 const char *section,
71a61510 1318 unsigned section_line,
3ecaa09b
LP
1319 const char *lvalue,
1320 int ltype,
1321 const char *rvalue,
1322 void *data,
1323 void *userdata) {
871d7de4 1324
74051b9b 1325 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1326 Unit *u = data;
1327 UnitType type;
1328 int r;
398ef8ba
LP
1329
1330 assert(filename);
1331 assert(lvalue);
1332 assert(rvalue);
1333 assert(data);
1334
3ecaa09b
LP
1335 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1336 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1337 "Multiple units to trigger specified, ignoring: %s", rvalue);
1338 return 0;
1339 }
871d7de4 1340
19f6d710
LP
1341 r = unit_name_printf(u, rvalue, &p);
1342 if (r < 0)
1343 log_syntax(unit, LOG_ERR, filename, line, -r,
1344 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
74051b9b 1345
19f6d710 1346 type = unit_name_to_type(p ?: rvalue);
3ecaa09b 1347 if (type < 0) {
e8e581bf 1348 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3ecaa09b 1349 "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1350 return 0;
871d7de4
LP
1351 }
1352
3ecaa09b
LP
1353 if (type == u->type) {
1354 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1355 "Trigger cannot be of same type, ignoring: %s", rvalue);
1356 return 0;
1357 }
1358
19f6d710 1359 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
57020a3a 1360 if (r < 0) {
e8e581bf 1361 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1362 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
c0b34696 1363 return 0;
871d7de4
LP
1364 }
1365
1366 return 0;
1367}
1368
e8e581bf
ZJS
1369int config_parse_path_spec(const char *unit,
1370 const char *filename,
1371 unsigned line,
1372 const char *section,
71a61510 1373 unsigned section_line,
e8e581bf
ZJS
1374 const char *lvalue,
1375 int ltype,
1376 const char *rvalue,
1377 void *data,
1378 void *userdata) {
01f78473
LP
1379
1380 Path *p = data;
1381 PathSpec *s;
1382 PathType b;
7fd1b19b 1383 _cleanup_free_ char *k = NULL;
19f6d710 1384 int r;
01f78473
LP
1385
1386 assert(filename);
1387 assert(lvalue);
1388 assert(rvalue);
1389 assert(data);
1390
74051b9b
LP
1391 if (isempty(rvalue)) {
1392 /* Empty assignment clears list */
1393 path_free_specs(p);
1394 return 0;
1395 }
1396
93e4c84b
LP
1397 b = path_type_from_string(lvalue);
1398 if (b < 0) {
e8e581bf
ZJS
1399 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1400 "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1401 return 0;
01f78473
LP
1402 }
1403
19f6d710
LP
1404 r = unit_full_printf(UNIT(p), rvalue, &k);
1405 if (r < 0) {
487060c2
LP
1406 k = strdup(rvalue);
1407 if (!k)
1408 return log_oom();
1409 else
19f6d710 1410 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf
ZJS
1411 "Failed to resolve unit specifiers on %s. Ignoring.",
1412 rvalue);
487060c2 1413 }
93e4c84b
LP
1414
1415 if (!path_is_absolute(k)) {
e8e581bf
ZJS
1416 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1417 "Path is not absolute, ignoring: %s", k);
c0b34696 1418 return 0;
01f78473
LP
1419 }
1420
93e4c84b 1421 s = new0(PathSpec, 1);
543295ad 1422 if (!s)
93e4c84b 1423 return log_oom();
01f78473 1424
718db961 1425 s->unit = UNIT(p);
93e4c84b 1426 s->path = path_kill_slashes(k);
543295ad 1427 k = NULL;
01f78473
LP
1428 s->type = b;
1429 s->inotify_fd = -1;
1430
71fda00f 1431 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1432
1433 return 0;
1434}
1435
e8e581bf
ZJS
1436int config_parse_socket_service(const char *unit,
1437 const char *filename,
1438 unsigned line,
1439 const char *section,
71a61510 1440 unsigned section_line,
e8e581bf
ZJS
1441 const char *lvalue,
1442 int ltype,
1443 const char *rvalue,
1444 void *data,
1445 void *userdata) {
d9ff321a 1446
718db961 1447 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d9ff321a
LP
1448 Socket *s = data;
1449 int r;
4ff77f66 1450 Unit *x;
74051b9b 1451 _cleanup_free_ char *p = NULL;
d9ff321a
LP
1452
1453 assert(filename);
1454 assert(lvalue);
1455 assert(rvalue);
1456 assert(data);
1457
19f6d710 1458 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1459 if (r < 0) {
718db961 1460 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
613b411c
LP
1461 return 0;
1462 }
74051b9b 1463
613b411c 1464 if (!endswith(p, ".service")) {
718db961 1465 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1466 return 0;
1467 }
1468
613b411c 1469 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1470 if (r < 0) {
718db961 1471 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
d9ff321a
LP
1472 return 0;
1473 }
1474
4ff77f66
LP
1475 unit_ref_set(&s->service, x);
1476
d9ff321a
LP
1477 return 0;
1478}
1479
e8e581bf
ZJS
1480int config_parse_service_sockets(const char *unit,
1481 const char *filename,
1482 unsigned line,
1483 const char *section,
71a61510 1484 unsigned section_line,
e8e581bf
ZJS
1485 const char *lvalue,
1486 int ltype,
1487 const char *rvalue,
1488 void *data,
1489 void *userdata) {
f976f3f6
LP
1490
1491 Service *s = data;
1492 int r;
f976f3f6
LP
1493 char *state, *w;
1494 size_t l;
1495
1496 assert(filename);
1497 assert(lvalue);
1498 assert(rvalue);
1499 assert(data);
1500
f976f3f6 1501 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 1502 _cleanup_free_ char *t = NULL, *k = NULL;
f976f3f6 1503
57020a3a
LP
1504 t = strndup(w, l);
1505 if (!t)
74051b9b 1506 return log_oom();
f976f3f6 1507
19f6d710
LP
1508 r = unit_name_printf(UNIT(s), t, &k);
1509 if (r < 0)
1510 log_syntax(unit, LOG_ERR, filename, line, -r,
1511 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
57020a3a 1512
19f6d710 1513 if (!endswith(k ?: t, ".socket")) {
e8e581bf 1514 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1515 "Unit must be of type socket, ignoring: %s", k ?: t);
f976f3f6
LP
1516 continue;
1517 }
1518
19f6d710 1519 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
57020a3a 1520 if (r < 0)
e8e581bf
ZJS
1521 log_syntax(unit, LOG_ERR, filename, line, -r,
1522 "Failed to add dependency on %s, ignoring: %s",
19f6d710 1523 k ?: t, strerror(-r));
f976f3f6 1524
19f6d710 1525 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
57020a3a 1526 if (r < 0)
f976f3f6
LP
1527 return r;
1528 }
1529
1530 return 0;
1531}
1532
e8e581bf
ZJS
1533int config_parse_service_timeout(const char *unit,
1534 const char *filename,
1535 unsigned line,
1536 const char *section,
71a61510 1537 unsigned section_line,
e8e581bf
ZJS
1538 const char *lvalue,
1539 int ltype,
1540 const char *rvalue,
1541 void *data,
1542 void *userdata) {
98709151
LN
1543
1544 Service *s = userdata;
1545 int r;
1546
1547 assert(filename);
1548 assert(lvalue);
1549 assert(rvalue);
1550 assert(s);
1551
71a61510 1552 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 1553 rvalue, data, userdata);
74051b9b 1554 if (r < 0)
d568a335 1555 return r;
98709151 1556
d568a335
MS
1557 if (streq(lvalue, "TimeoutSec")) {
1558 s->start_timeout_defined = true;
1559 s->timeout_stop_usec = s->timeout_start_usec;
1560 } else if (streq(lvalue, "TimeoutStartSec"))
1561 s->start_timeout_defined = true;
1562
1563 return 0;
98709151
LN
1564}
1565
e821075a
LP
1566int config_parse_busname_service(
1567 const char *unit,
1568 const char *filename,
1569 unsigned line,
1570 const char *section,
1571 unsigned section_line,
1572 const char *lvalue,
1573 int ltype,
1574 const char *rvalue,
1575 void *data,
1576 void *userdata) {
1577
1578 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1579 BusName *n = data;
1580 int r;
1581 Unit *x;
1582 _cleanup_free_ char *p = NULL;
1583
1584 assert(filename);
1585 assert(lvalue);
1586 assert(rvalue);
1587 assert(data);
1588
1589 r = unit_name_printf(UNIT(n), rvalue, &p);
1590 if (r < 0) {
1591 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1592 return 0;
1593 }
1594
1595 if (!endswith(p, ".service")) {
1596 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1597 return 0;
1598 }
1599
1600 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1601 if (r < 0) {
1602 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1603 return 0;
1604 }
1605
1606 unit_ref_set(&n->service, x);
1607
1608 return 0;
1609}
1610
54d76c92
DM
1611int config_parse_bus_policy(
1612 const char *unit,
1613 const char *filename,
1614 unsigned line,
1615 const char *section,
1616 unsigned section_line,
1617 const char *lvalue,
1618 int ltype,
1619 const char *rvalue,
1620 void *data,
1621 void *userdata) {
1622
1623 _cleanup_free_ BusNamePolicy *p = NULL;
1624 _cleanup_free_ char *id_str = NULL;
1625 BusName *busname = data;
1626 char *access_str;
1627 int r;
1628
1629 assert(filename);
1630 assert(lvalue);
1631 assert(rvalue);
1632 assert(data);
1633
1634 p = new0(BusNamePolicy, 1);
1635 if (!p)
1636 return log_oom();
1637
1638 if (streq(lvalue, "AllowUser"))
1639 p->type = BUSNAME_POLICY_TYPE_USER;
1640 else if (streq(lvalue, "AllowGroup"))
1641 p->type = BUSNAME_POLICY_TYPE_GROUP;
1642 else if (streq(lvalue, "AllowWorld"))
1643 p->type = BUSNAME_POLICY_TYPE_WORLD;
1644 else
1645 assert_not_reached("Unknown lvalue");
1646
1647 id_str = strdup(rvalue);
1648 if (!id_str)
1649 return log_oom();
1650
1651 if (p->type != BUSNAME_POLICY_TYPE_WORLD) {
1652 access_str = strchr(id_str, ' ');
1653 if (!access_str) {
1654 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy value '%s'", rvalue);
1655 return 0;
1656 }
1657
1658 *access_str = '\0';
1659 access_str++;
1660
1661 if (p->type == BUSNAME_POLICY_TYPE_USER) {
1662 const char *user = id_str;
1663
1664 r = get_user_creds(&user, &p->uid, NULL, NULL, NULL);
1665 if (r < 0) {
1666 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to parse uid from '%s'", id_str);
1667 return 0;
1668 }
1669 } else {
1670 const char *group = id_str;
1671
1672 r = get_group_creds(&group, &p->gid);
1673 if (r < 0) {
1674 log_syntax(unit, LOG_ERR, filename, line, -errno, "Unable to parse gid from '%s'", id_str);
1675 return 0;
1676 }
1677 }
1678 } else {
1679 access_str = id_str;
1680 }
1681
1682 p->access = busname_policy_access_from_string(access_str);
1683 if (p->access < 0) {
1684 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy access type '%s'", access_str);
1685 return 0;
1686 }
1687
1688 LIST_PREPEND(policy, busname->policy, p);
1689 p = NULL;
1690
1691 return 0;
1692}
1693
e8e581bf
ZJS
1694int config_parse_unit_env_file(const char *unit,
1695 const char *filename,
1696 unsigned line,
1697 const char *section,
71a61510 1698 unsigned section_line,
e8e581bf
ZJS
1699 const char *lvalue,
1700 int ltype,
1701 const char *rvalue,
1702 void *data,
1703 void *userdata) {
ddb26e18 1704
853b8397 1705 char ***env = data;
8fef7659 1706 Unit *u = userdata;
19f6d710
LP
1707 _cleanup_free_ char *n = NULL;
1708 const char *s;
853b8397 1709 int r;
ddb26e18
LP
1710
1711 assert(filename);
1712 assert(lvalue);
1713 assert(rvalue);
1714 assert(data);
1715
74051b9b
LP
1716 if (isempty(rvalue)) {
1717 /* Empty assignment frees the list */
74051b9b
LP
1718 strv_free(*env);
1719 *env = NULL;
1720 return 0;
1721 }
1722
19f6d710
LP
1723 r = unit_full_printf(u, rvalue, &n);
1724 if (r < 0)
1725 log_syntax(unit, LOG_ERR, filename, line, r,
1726 "Failed to resolve specifiers, ignoring: %s", rvalue);
8fef7659 1727
19f6d710 1728 s = n ?: rvalue;
8fef7659 1729 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
e8e581bf
ZJS
1730 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1731 "Path '%s' is not absolute, ignoring.", s);
afe4bfe2
LP
1732 return 0;
1733 }
1734
853b8397
LP
1735 r = strv_extend(env, s);
1736 if (r < 0)
1737 return log_oom();
1738
1739 return 0;
1740}
1741
e8e581bf
ZJS
1742int config_parse_environ(const char *unit,
1743 const char *filename,
1744 unsigned line,
1745 const char *section,
71a61510 1746 unsigned section_line,
e8e581bf
ZJS
1747 const char *lvalue,
1748 int ltype,
1749 const char *rvalue,
1750 void *data,
1751 void *userdata) {
853b8397
LP
1752
1753 Unit *u = userdata;
1754 char*** env = data, *w, *state;
1755 size_t l;
1756 _cleanup_free_ char *k = NULL;
19f6d710 1757 int r;
853b8397
LP
1758
1759 assert(filename);
1760 assert(lvalue);
1761 assert(rvalue);
97d0e5f8 1762 assert(data);
853b8397
LP
1763
1764 if (isempty(rvalue)) {
1765 /* Empty assignment resets the list */
1766 strv_free(*env);
1767 *env = NULL;
1768 return 0;
1769 }
1770
19f6d710
LP
1771 if (u) {
1772 r = unit_full_printf(u, rvalue, &k);
1773 if (r < 0)
1774 log_syntax(unit, LOG_ERR, filename, line, -r,
1775 "Failed to resolve specifiers, ignoring: %s", rvalue);
1776 }
97d0e5f8 1777
19f6d710
LP
1778 if (!k)
1779 k = strdup(rvalue);
8fef7659 1780 if (!k)
74051b9b 1781 return log_oom();
ddb26e18 1782
853b8397
LP
1783 FOREACH_WORD_QUOTED(w, l, k, state) {
1784 _cleanup_free_ char *n;
1785 char **x;
1786
1787 n = cunescape_length(w, l);
1788 if (!n)
1789 return log_oom();
1790
1791 if (!env_assignment_is_valid(n)) {
e8e581bf
ZJS
1792 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1793 "Invalid environment assignment, ignoring: %s", rvalue);
853b8397
LP
1794 continue;
1795 }
1796
1797 x = strv_env_set(*env, n);
1798 if (!x)
1799 return log_oom();
1800
1801 strv_free(*env);
1802 *env = x;
1803 }
ddb26e18 1804
8c7be95e 1805 return 0;
ddb26e18
LP
1806}
1807
e8e581bf
ZJS
1808int config_parse_ip_tos(const char *unit,
1809 const char *filename,
1810 unsigned line,
1811 const char *section,
71a61510 1812 unsigned section_line,
e8e581bf
ZJS
1813 const char *lvalue,
1814 int ltype,
1815 const char *rvalue,
1816 void *data,
1817 void *userdata) {
4fd5948e
LP
1818
1819 int *ip_tos = data, x;
4fd5948e
LP
1820
1821 assert(filename);
1822 assert(lvalue);
1823 assert(rvalue);
1824 assert(data);
1825
f8b69d1d
MS
1826 x = ip_tos_from_string(rvalue);
1827 if (x < 0) {
e8e581bf
ZJS
1828 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1829 "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
1830 return 0;
1831 }
4fd5948e
LP
1832
1833 *ip_tos = x;
1834 return 0;
1835}
1836
e8e581bf
ZJS
1837int config_parse_unit_condition_path(const char *unit,
1838 const char *filename,
1839 unsigned line,
1840 const char *section,
71a61510 1841 unsigned section_line,
e8e581bf
ZJS
1842 const char *lvalue,
1843 int ltype,
1844 const char *rvalue,
1845 void *data,
1846 void *userdata) {
52661efd 1847
2b583ce6 1848 ConditionType cond = ltype;
52661efd 1849 Unit *u = data;
267632f0 1850 bool trigger, negate;
52661efd 1851 Condition *c;
2fbe635a 1852 _cleanup_free_ char *p = NULL;
19f6d710 1853 int r;
52661efd
LP
1854
1855 assert(filename);
1856 assert(lvalue);
1857 assert(rvalue);
1858 assert(data);
1859
74051b9b
LP
1860 if (isempty(rvalue)) {
1861 /* Empty assignment resets the list */
1862 condition_free_list(u->conditions);
1863 u->conditions = NULL;
1864 return 0;
1865 }
1866
ab7f148f
LP
1867 trigger = rvalue[0] == '|';
1868 if (trigger)
267632f0
LP
1869 rvalue++;
1870
ab7f148f
LP
1871 negate = rvalue[0] == '!';
1872 if (negate)
52661efd
LP
1873 rvalue++;
1874
19f6d710
LP
1875 r = unit_full_printf(u, rvalue, &p);
1876 if (r < 0)
1877 log_syntax(unit, LOG_ERR, filename, line, -r,
1878 "Failed to resolve specifiers, ignoring: %s", rvalue);
1879 if (!p) {
1880 p = strdup(rvalue);
1881 if (!p)
1882 return log_oom();
1883 }
095b2d7a
AK
1884
1885 if (!path_is_absolute(p)) {
e8e581bf
ZJS
1886 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1887 "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
1888 return 0;
1889 }
1890
095b2d7a 1891 c = condition_new(cond, p, trigger, negate);
ab7f148f 1892 if (!c)
74051b9b 1893 return log_oom();
52661efd 1894
71fda00f 1895 LIST_PREPEND(conditions, u->conditions, c);
52661efd
LP
1896 return 0;
1897}
1898
e8e581bf
ZJS
1899int config_parse_unit_condition_string(const char *unit,
1900 const char *filename,
1901 unsigned line,
1902 const char *section,
71a61510 1903 unsigned section_line,
e8e581bf
ZJS
1904 const char *lvalue,
1905 int ltype,
1906 const char *rvalue,
1907 void *data,
1908 void *userdata) {
039655a4 1909
41584525 1910 ConditionType cond = ltype;
039655a4 1911 Unit *u = data;
267632f0 1912 bool trigger, negate;
039655a4 1913 Condition *c;
2fbe635a 1914 _cleanup_free_ char *s = NULL;
19f6d710 1915 int r;
039655a4
LP
1916
1917 assert(filename);
1918 assert(lvalue);
1919 assert(rvalue);
1920 assert(data);
1921
74051b9b
LP
1922 if (isempty(rvalue)) {
1923 /* Empty assignment resets the list */
1924 condition_free_list(u->conditions);
1925 u->conditions = NULL;
1926 return 0;
1927 }
1928
c0d6e764
LP
1929 trigger = rvalue[0] == '|';
1930 if (trigger)
267632f0
LP
1931 rvalue++;
1932
c0d6e764
LP
1933 negate = rvalue[0] == '!';
1934 if (negate)
039655a4
LP
1935 rvalue++;
1936
19f6d710
LP
1937 r = unit_full_printf(u, rvalue, &s);
1938 if (r < 0)
1939 log_syntax(unit, LOG_ERR, filename, line, -r,
1940 "Failed to resolve specifiers, ignoring: %s", rvalue);
1941 if (!s) {
1942 s = strdup(rvalue);
1943 if (!s)
1944 return log_oom();
1945 }
095b2d7a
AK
1946
1947 c = condition_new(cond, s, trigger, negate);
c0d6e764
LP
1948 if (!c)
1949 return log_oom();
039655a4 1950
71fda00f 1951 LIST_PREPEND(conditions, u->conditions, c);
039655a4
LP
1952 return 0;
1953}
1954
e8e581bf
ZJS
1955int config_parse_unit_condition_null(const char *unit,
1956 const char *filename,
1957 unsigned line,
1958 const char *section,
71a61510 1959 unsigned section_line,
e8e581bf
ZJS
1960 const char *lvalue,
1961 int ltype,
1962 const char *rvalue,
1963 void *data,
1964 void *userdata) {
d257ddef
LP
1965
1966 Unit *u = data;
1967 Condition *c;
267632f0 1968 bool trigger, negate;
d257ddef
LP
1969 int b;
1970
1971 assert(filename);
1972 assert(lvalue);
1973 assert(rvalue);
1974 assert(data);
1975
74051b9b
LP
1976 if (isempty(rvalue)) {
1977 /* Empty assignment resets the list */
1978 condition_free_list(u->conditions);
1979 u->conditions = NULL;
1980 return 0;
1981 }
1982
1983 trigger = rvalue[0] == '|';
1984 if (trigger)
267632f0
LP
1985 rvalue++;
1986
74051b9b
LP
1987 negate = rvalue[0] == '!';
1988 if (negate)
d257ddef
LP
1989 rvalue++;
1990
74051b9b
LP
1991 b = parse_boolean(rvalue);
1992 if (b < 0) {
e8e581bf
ZJS
1993 log_syntax(unit, LOG_ERR, filename, line, -b,
1994 "Failed to parse boolean value in condition, ignoring: %s",
1995 rvalue);
d257ddef
LP
1996 return 0;
1997 }
1998
1999 if (!b)
2000 negate = !negate;
2001
74051b9b
LP
2002 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2003 if (!c)
2004 return log_oom();
d257ddef 2005
71fda00f 2006 LIST_PREPEND(conditions, u->conditions, c);
d257ddef
LP
2007 return 0;
2008}
2009
f975e971 2010DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
4b939747 2011DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
c952c6ec 2012
a57f7e2c
LP
2013int config_parse_unit_requires_mounts_for(
2014 const char *unit,
2015 const char *filename,
2016 unsigned line,
2017 const char *section,
71a61510 2018 unsigned section_line,
a57f7e2c
LP
2019 const char *lvalue,
2020 int ltype,
2021 const char *rvalue,
2022 void *data,
2023 void *userdata) {
7c8fa05c
LP
2024
2025 Unit *u = userdata;
a57f7e2c
LP
2026 char *state;
2027 size_t l;
2028 char *w;
7c8fa05c
LP
2029
2030 assert(filename);
2031 assert(lvalue);
2032 assert(rvalue);
2033 assert(data);
2034
a57f7e2c 2035 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
8a7935a2 2036 int r;
a57f7e2c
LP
2037 _cleanup_free_ char *n;
2038
2039 n = strndup(w, l);
2040 if (!n)
2041 return log_oom();
7c8fa05c 2042
a57f7e2c 2043 if (!utf8_is_valid(n)) {
b5d74213 2044 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
a57f7e2c
LP
2045 continue;
2046 }
7c8fa05c 2047
a57f7e2c
LP
2048 r = unit_require_mounts_for(u, n);
2049 if (r < 0) {
2050 log_syntax(unit, LOG_ERR, filename, line, r,
2051 "Failed to add required mount for, ignoring: %s", rvalue);
2052 continue;
2053 }
2054 }
7c8fa05c 2055
8a7935a2 2056 return 0;
7c8fa05c 2057}
9e372868 2058
e8e581bf
ZJS
2059int config_parse_documentation(const char *unit,
2060 const char *filename,
2061 unsigned line,
2062 const char *section,
71a61510 2063 unsigned section_line,
e8e581bf
ZJS
2064 const char *lvalue,
2065 int ltype,
2066 const char *rvalue,
2067 void *data,
2068 void *userdata) {
49dbfa7b
LP
2069
2070 Unit *u = userdata;
2071 int r;
2072 char **a, **b;
2073
2074 assert(filename);
2075 assert(lvalue);
2076 assert(rvalue);
2077 assert(u);
2078
74051b9b
LP
2079 if (isempty(rvalue)) {
2080 /* Empty assignment resets the list */
2081 strv_free(u->documentation);
2082 u->documentation = NULL;
2083 return 0;
2084 }
2085
71a61510 2086 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2087 rvalue, data, userdata);
49dbfa7b
LP
2088 if (r < 0)
2089 return r;
2090
2091 for (a = b = u->documentation; a && *a; a++) {
2092
2093 if (is_valid_documentation_url(*a))
2094 *(b++) = *a;
2095 else {
e8e581bf
ZJS
2096 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2097 "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2098 free(*a);
2099 }
2100 }
f6d2d421
ZJS
2101 if (b)
2102 *b = NULL;
49dbfa7b
LP
2103
2104 return r;
2105}
2106
c0467cf3 2107#ifdef HAVE_SECCOMP
17df7223
LP
2108int config_parse_syscall_filter(
2109 const char *unit,
2110 const char *filename,
2111 unsigned line,
2112 const char *section,
2113 unsigned section_line,
2114 const char *lvalue,
2115 int ltype,
2116 const char *rvalue,
2117 void *data,
2118 void *userdata) {
2119
2120 static const char default_syscalls[] =
2121 "execve\0"
2122 "exit\0"
2123 "exit_group\0"
2124 "rt_sigreturn\0"
2125 "sigreturn\0";
2126
8351ceae
LP
2127 ExecContext *c = data;
2128 Unit *u = userdata;
b5fb3789 2129 bool invert = false;
17df7223 2130 char *w, *state;
8351ceae 2131 size_t l;
17df7223 2132 int r;
8351ceae
LP
2133
2134 assert(filename);
2135 assert(lvalue);
2136 assert(rvalue);
2137 assert(u);
2138
74051b9b
LP
2139 if (isempty(rvalue)) {
2140 /* Empty assignment resets the list */
17df7223
LP
2141 set_free(c->syscall_filter);
2142 c->syscall_filter = NULL;
2143 c->syscall_whitelist = false;
74051b9b
LP
2144 return 0;
2145 }
2146
8351ceae
LP
2147 if (rvalue[0] == '~') {
2148 invert = true;
2149 rvalue++;
2150 }
2151
17df7223
LP
2152 if (!c->syscall_filter) {
2153 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
2154 if (!c->syscall_filter)
2155 return log_oom();
2156
c0467cf3 2157 if (invert)
17df7223
LP
2158 /* Allow everything but the ones listed */
2159 c->syscall_whitelist = false;
c0467cf3 2160 else {
17df7223
LP
2161 const char *i;
2162
2163 /* Allow nothing but the ones listed */
2164 c->syscall_whitelist = true;
8351ceae 2165
17df7223
LP
2166 /* Accept default syscalls if we are on a whitelist */
2167 NULSTR_FOREACH(i, default_syscalls) {
2168 int id;
8351ceae 2169
17df7223 2170 id = seccomp_syscall_resolve_name(i);
c0467cf3
RC
2171 if (id < 0)
2172 continue;
8351ceae 2173
17df7223
LP
2174 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2175 if (r == -EEXIST)
2176 continue;
2177 if (r < 0)
2178 return log_oom();
c0467cf3
RC
2179 }
2180 }
8351ceae
LP
2181 }
2182
2183 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 2184 _cleanup_free_ char *t = NULL;
17df7223 2185 int id;
8351ceae
LP
2186
2187 t = strndup(w, l);
2188 if (!t)
74051b9b 2189 return log_oom();
8351ceae 2190
c0467cf3 2191 id = seccomp_syscall_resolve_name(t);
8351ceae 2192 if (id < 0) {
17df7223 2193 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
8351ceae
LP
2194 continue;
2195 }
2196
17df7223
LP
2197 /* If we previously wanted to forbid a syscall and now
2198 * we want to allow it, then remove it from the list
c0467cf3 2199 */
17df7223
LP
2200 if (!invert == c->syscall_whitelist) {
2201 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2202 if (r == -EEXIST)
2203 continue;
2204 if (r < 0)
2205 return log_oom();
2206 } else
2207 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
c0467cf3
RC
2208 }
2209
760b9d7c
LP
2210 /* Turn on NNP, but only if it wasn't configured explicitly
2211 * before, and only if we are in user mode. */
2212 if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER)
2213 c->no_new_privileges = true;
17df7223
LP
2214
2215 return 0;
2216}
2217
57183d11
LP
2218int config_parse_syscall_archs(
2219 const char *unit,
2220 const char *filename,
2221 unsigned line,
2222 const char *section,
2223 unsigned section_line,
2224 const char *lvalue,
2225 int ltype,
2226 const char *rvalue,
2227 void *data,
2228 void *userdata) {
2229
d3b1c508 2230 Set **archs = data;
57183d11
LP
2231 char *w, *state;
2232 size_t l;
2233 int r;
2234
2235 if (isempty(rvalue)) {
d3b1c508
LP
2236 set_free(*archs);
2237 *archs = NULL;
57183d11
LP
2238 return 0;
2239 }
2240
d3b1c508 2241 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
57183d11
LP
2242 if (r < 0)
2243 return log_oom();
2244
2245 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2246 _cleanup_free_ char *t = NULL;
2247 uint32_t a;
2248
2249 t = strndup(w, l);
2250 if (!t)
2251 return log_oom();
2252
2253 r = seccomp_arch_from_string(t, &a);
2254 if (r < 0) {
2255 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2256 continue;
2257 }
2258
d3b1c508 2259 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2260 if (r == -EEXIST)
2261 continue;
2262 if (r < 0)
2263 return log_oom();
2264 }
2265
2266 return 0;
2267}
2268
17df7223
LP
2269int config_parse_syscall_errno(
2270 const char *unit,
2271 const char *filename,
2272 unsigned line,
2273 const char *section,
2274 unsigned section_line,
2275 const char *lvalue,
2276 int ltype,
2277 const char *rvalue,
2278 void *data,
2279 void *userdata) {
2280
2281 ExecContext *c = data;
2282 int e;
2283
2284 assert(filename);
2285 assert(lvalue);
2286 assert(rvalue);
2287
2288 if (isempty(rvalue)) {
2289 /* Empty assignment resets to KILL */
2290 c->syscall_errno = 0;
2291 return 0;
8351ceae
LP
2292 }
2293
17df7223
LP
2294 e = errno_from_name(rvalue);
2295 if (e < 0) {
2296 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2297 return 0;
2298 }
8351ceae 2299
17df7223 2300 c->syscall_errno = e;
8351ceae
LP
2301 return 0;
2302}
4298d0b5
LP
2303
2304int config_parse_address_families(
2305 const char *unit,
2306 const char *filename,
2307 unsigned line,
2308 const char *section,
2309 unsigned section_line,
2310 const char *lvalue,
2311 int ltype,
2312 const char *rvalue,
2313 void *data,
2314 void *userdata) {
2315
2316 ExecContext *c = data;
2317 Unit *u = userdata;
2318 bool invert = false;
2319 char *w, *state;
2320 size_t l;
2321 int r;
2322
2323 assert(filename);
2324 assert(lvalue);
2325 assert(rvalue);
2326 assert(u);
2327
2328 if (isempty(rvalue)) {
2329 /* Empty assignment resets the list */
2330 set_free(c->address_families);
2331 c->address_families = NULL;
2332 c->address_families_whitelist = false;
2333 return 0;
2334 }
2335
2336 if (rvalue[0] == '~') {
2337 invert = true;
2338 rvalue++;
2339 }
2340
2341 if (!c->address_families) {
2342 c->address_families = set_new(trivial_hash_func, trivial_compare_func);
2343 if (!c->address_families)
2344 return log_oom();
2345
2346 c->address_families_whitelist = !invert;
2347 }
2348
2349 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2350 _cleanup_free_ char *t = NULL;
2351 int af;
2352
2353 t = strndup(w, l);
2354 if (!t)
2355 return log_oom();
2356
2357 af = af_from_name(t);
2358 if (af <= 0) {
2359 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse address family, ignoring: %s", t);
2360 continue;
2361 }
2362
2363 /* If we previously wanted to forbid an address family and now
2364 * we want to allow it, then remove it from the list
2365 */
2366 if (!invert == c->address_families_whitelist) {
2367 r = set_put(c->address_families, INT_TO_PTR(af));
2368 if (r == -EEXIST)
2369 continue;
2370 if (r < 0)
2371 return log_oom();
2372 } else
2373 set_remove(c->address_families, INT_TO_PTR(af));
2374 }
2375
2376 return 0;
2377}
c0467cf3 2378#endif
8351ceae 2379
a016b922
LP
2380int config_parse_unit_slice(
2381 const char *unit,
2382 const char *filename,
2383 unsigned line,
2384 const char *section,
71a61510 2385 unsigned section_line,
a016b922
LP
2386 const char *lvalue,
2387 int ltype,
2388 const char *rvalue,
2389 void *data,
2390 void *userdata) {
2391
2392 _cleanup_free_ char *k = NULL;
2393 Unit *u = userdata, *slice;
2394 int r;
2395
2396 assert(filename);
2397 assert(lvalue);
2398 assert(rvalue);
2399 assert(u);
2400
19f6d710
LP
2401 r = unit_name_printf(u, rvalue, &k);
2402 if (r < 0)
2403 log_syntax(unit, LOG_ERR, filename, line, -r,
a016b922 2404 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
19f6d710
LP
2405 if (!k) {
2406 k = strdup(rvalue);
2407 if (!k)
2408 return log_oom();
2409 }
a016b922 2410
19f6d710 2411 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922
LP
2412 if (r < 0) {
2413 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 2414 "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
2415 return 0;
2416 }
2417
2418 if (slice->type != UNIT_SLICE) {
2419 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 2420 "Slice unit %s is not a slice. Ignoring.", k);
a016b922
LP
2421 return 0;
2422 }
2423
2424 unit_ref_set(&u->slice, slice);
2425 return 0;
2426}
2427
4ad49000
LP
2428DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2429
2430int config_parse_cpu_shares(
2431 const char *unit,
2432 const char *filename,
2433 unsigned line,
2434 const char *section,
71a61510 2435 unsigned section_line,
4ad49000
LP
2436 const char *lvalue,
2437 int ltype,
2438 const char *rvalue,
2439 void *data,
2440 void *userdata) {
2441
2442 CGroupContext *c = data;
2443 unsigned long lu;
2444 int r;
2445
2446 assert(filename);
2447 assert(lvalue);
2448 assert(rvalue);
2449
2450 if (isempty(rvalue)) {
2451 c->cpu_shares = 1024;
2452 return 0;
2453 }
2454
2455 r = safe_atolu(rvalue, &lu);
2456 if (r < 0 || lu <= 0) {
2457 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2458 "CPU shares '%s' invalid. Ignoring.", rvalue);
2459 return 0;
2460 }
2461
2462 c->cpu_shares = lu;
2463 return 0;
2464}
2465
2466int config_parse_memory_limit(
2467 const char *unit,
2468 const char *filename,
2469 unsigned line,
2470 const char *section,
71a61510 2471 unsigned section_line,
4ad49000
LP
2472 const char *lvalue,
2473 int ltype,
2474 const char *rvalue,
2475 void *data,
2476 void *userdata) {
2477
2478 CGroupContext *c = data;
4ad49000
LP
2479 off_t bytes;
2480 int r;
2481
4ad49000 2482 if (isempty(rvalue)) {
ddca82ac 2483 c->memory_limit = (uint64_t) -1;
4ad49000
LP
2484 return 0;
2485 }
2486
2487 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2488
5556b5fe 2489 r = parse_size(rvalue, 1024, &bytes);
4ad49000 2490 if (r < 0) {
5556b5fe 2491 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2492 return 0;
2493 }
2494
ddca82ac 2495 c->memory_limit = (uint64_t) bytes;
4ad49000
LP
2496 return 0;
2497}
2498
2499int config_parse_device_allow(
2500 const char *unit,
2501 const char *filename,
2502 unsigned line,
2503 const char *section,
71a61510 2504 unsigned section_line,
4ad49000
LP
2505 const char *lvalue,
2506 int ltype,
2507 const char *rvalue,
2508 void *data,
2509 void *userdata) {
2510
2511 _cleanup_free_ char *path = NULL;
2512 CGroupContext *c = data;
2513 CGroupDeviceAllow *a;
2514 const char *m;
2515 size_t n;
2516
2517 if (isempty(rvalue)) {
2518 while (c->device_allow)
2519 cgroup_context_free_device_allow(c, c->device_allow);
2520
2521 return 0;
2522 }
2523
2524 n = strcspn(rvalue, WHITESPACE);
2525 path = strndup(rvalue, n);
2526 if (!path)
2527 return log_oom();
2528
90060676
LP
2529 if (!startswith(path, "/dev/") &&
2530 !startswith(path, "block-") &&
2531 !startswith(path, "char-")) {
2532 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
2533 return 0;
2534 }
2535
2536 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2537 if (isempty(m))
2538 m = "rwm";
2539
2540 if (!in_charset(m, "rwm")) {
90060676 2541 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
2542 return 0;
2543 }
2544
2545 a = new0(CGroupDeviceAllow, 1);
2546 if (!a)
2547 return log_oom();
2548
2549 a->path = path;
2550 path = NULL;
2551 a->r = !!strchr(m, 'r');
2552 a->w = !!strchr(m, 'w');
2553 a->m = !!strchr(m, 'm');
2554
71fda00f 2555 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
2556 return 0;
2557}
2558
2559int config_parse_blockio_weight(
2560 const char *unit,
2561 const char *filename,
2562 unsigned line,
2563 const char *section,
71a61510 2564 unsigned section_line,
4ad49000
LP
2565 const char *lvalue,
2566 int ltype,
2567 const char *rvalue,
2568 void *data,
2569 void *userdata) {
2570
8e7076ca
LP
2571 CGroupContext *c = data;
2572 unsigned long lu;
2573 int r;
2574
2575 assert(filename);
2576 assert(lvalue);
2577 assert(rvalue);
2578
2579 if (isempty(rvalue)) {
2580 c->blockio_weight = 1000;
2581 return 0;
2582 }
2583
2584 r = safe_atolu(rvalue, &lu);
2585 if (r < 0 || lu < 10 || lu > 1000) {
2586 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2587 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2588 return 0;
2589 }
2590
2591 c->blockio_weight = lu;
2592
2593 return 0;
2594}
2595
2596int config_parse_blockio_device_weight(
2597 const char *unit,
2598 const char *filename,
2599 unsigned line,
2600 const char *section,
71a61510 2601 unsigned section_line,
8e7076ca
LP
2602 const char *lvalue,
2603 int ltype,
2604 const char *rvalue,
2605 void *data,
2606 void *userdata) {
2607
4ad49000 2608 _cleanup_free_ char *path = NULL;
8e7076ca 2609 CGroupBlockIODeviceWeight *w;
4ad49000
LP
2610 CGroupContext *c = data;
2611 unsigned long lu;
2612 const char *weight;
2613 size_t n;
2614 int r;
2615
2616 assert(filename);
2617 assert(lvalue);
2618 assert(rvalue);
2619
2620 if (isempty(rvalue)) {
4ad49000
LP
2621 while (c->blockio_device_weights)
2622 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2623
2624 return 0;
2625 }
2626
2627 n = strcspn(rvalue, WHITESPACE);
2628 weight = rvalue + n;
8e7076ca
LP
2629 if (!*weight) {
2630 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2631 "Expected block device and device weight. Ignoring.");
2632 return 0;
2633 }
4ad49000 2634
8e7076ca
LP
2635 path = strndup(rvalue, n);
2636 if (!path)
2637 return log_oom();
4ad49000 2638
8e7076ca
LP
2639 if (!path_startswith(path, "/dev")) {
2640 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2641 "Invalid device node path '%s'. Ignoring.", path);
2642 return 0;
2643 }
4ad49000 2644
8e7076ca 2645 weight += strspn(weight, WHITESPACE);
4ad49000
LP
2646 r = safe_atolu(weight, &lu);
2647 if (r < 0 || lu < 10 || lu > 1000) {
2648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2649 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2650 return 0;
2651 }
2652
4ad49000 2653
8e7076ca
LP
2654 w = new0(CGroupBlockIODeviceWeight, 1);
2655 if (!w)
2656 return log_oom();
4ad49000 2657
8e7076ca
LP
2658 w->path = path;
2659 path = NULL;
4ad49000 2660
8e7076ca 2661 w->weight = lu;
4ad49000 2662
71fda00f 2663 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
2664 return 0;
2665}
2666
2667int config_parse_blockio_bandwidth(
2668 const char *unit,
2669 const char *filename,
2670 unsigned line,
2671 const char *section,
71a61510 2672 unsigned section_line,
4ad49000
LP
2673 const char *lvalue,
2674 int ltype,
2675 const char *rvalue,
2676 void *data,
2677 void *userdata) {
2678
2679 _cleanup_free_ char *path = NULL;
2680 CGroupBlockIODeviceBandwidth *b;
2681 CGroupContext *c = data;
2682 const char *bandwidth;
2683 off_t bytes;
47c0980d 2684 bool read;
4ad49000
LP
2685 size_t n;
2686 int r;
2687
2688 assert(filename);
2689 assert(lvalue);
2690 assert(rvalue);
2691
47c0980d
G
2692 read = streq("BlockIOReadBandwidth", lvalue);
2693
4ad49000 2694 if (isempty(rvalue)) {
47c0980d
G
2695 CGroupBlockIODeviceBandwidth *next;
2696
2697 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2698 if (b->read == read)
2699 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
2700
2701 return 0;
2702 }
2703
2704 n = strcspn(rvalue, WHITESPACE);
2705 bandwidth = rvalue + n;
2706 bandwidth += strspn(bandwidth, WHITESPACE);
2707
2708 if (!*bandwidth) {
2709 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2710 "Expected space separated pair of device node and bandwidth. Ignoring.");
2711 return 0;
2712 }
2713
2714 path = strndup(rvalue, n);
2715 if (!path)
2716 return log_oom();
2717
2718 if (!path_startswith(path, "/dev")) {
2719 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2720 "Invalid device node path '%s'. Ignoring.", path);
2721 return 0;
2722 }
2723
5556b5fe 2724 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 2725 if (r < 0 || bytes <= 0) {
5556b5fe 2726 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2727 return 0;
2728 }
2729
2730 b = new0(CGroupBlockIODeviceBandwidth, 1);
2731 if (!b)
2732 return log_oom();
2733
2734 b->path = path;
2735 path = NULL;
2736 b->bandwidth = (uint64_t) bytes;
47c0980d 2737 b->read = read;
4ad49000 2738
71fda00f 2739 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
2740
2741 return 0;
2742}
2743
d420282b
LP
2744DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2745
2746int config_parse_job_mode_isolate(
2747 const char *unit,
2748 const char *filename,
2749 unsigned line,
2750 const char *section,
2751 unsigned section_line,
2752 const char *lvalue,
2753 int ltype,
2754 const char *rvalue,
2755 void *data,
2756 void *userdata) {
2757
2758 JobMode *m = data;
2759 int r;
2760
2761 assert(filename);
2762 assert(lvalue);
2763 assert(rvalue);
2764
2765 r = parse_boolean(rvalue);
2766 if (r < 0) {
2767 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2768 return 0;
2769 }
2770
2771 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2772 return 0;
2773}
2774
ac45f971
LP
2775int config_parse_personality(
2776 const char *unit,
2777 const char *filename,
2778 unsigned line,
2779 const char *section,
2780 unsigned section_line,
2781 const char *lvalue,
2782 int ltype,
2783 const char *rvalue,
2784 void *data,
2785 void *userdata) {
2786
2787 unsigned long *personality = data, p;
2788
2789 assert(filename);
2790 assert(lvalue);
2791 assert(rvalue);
2792 assert(personality);
2793
2794 p = personality_from_string(rvalue);
2795 if (p == 0xffffffffUL) {
2796 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2797 "Failed to parse personality, ignoring: %s", rvalue);
2798 return 0;
2799 }
2800
2801 *personality = p;
2802 return 0;
2803}
2804
e66cf1a3
LP
2805int config_parse_runtime_directory(
2806 const char *unit,
2807 const char *filename,
2808 unsigned line,
2809 const char *section,
2810 unsigned section_line,
2811 const char *lvalue,
2812 int ltype,
2813 const char *rvalue,
2814 void *data,
2815 void *userdata) {
2816
2817 char***rt = data, *w, *state;
2818 size_t l;
2819 int r;
2820
2821 assert(filename);
2822 assert(lvalue);
2823 assert(rvalue);
2824 assert(data);
2825
2826 if (isempty(rvalue)) {
2827 /* Empty assignment resets the list */
2828 strv_free(*rt);
2829 *rt = NULL;
2830 return 0;
2831 }
2832
2833 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2834 _cleanup_free_ char *n;
2835
2836 n = strndup(w, l);
2837 if (!n)
2838 return log_oom();
2839
2840 if (!filename_is_safe(n)) {
2841 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
2842 continue;
2843 }
2844
2845 r = strv_push(rt, n);
2846 if (r < 0)
2847 return log_oom();
2848
2849 n = NULL;
2850 }
2851
2852 return 0;
2853}
2854
3af00fb8
LP
2855int config_parse_set_status(
2856 const char *unit,
2857 const char *filename,
2858 unsigned line,
2859 const char *section,
2860 unsigned section_line,
2861 const char *lvalue,
2862 int ltype,
2863 const char *rvalue,
2864 void *data,
2865 void *userdata) {
2866
2867 char *w;
2868 size_t l;
2869 char *state;
2870 int r;
2871 ExitStatusSet *status_set = data;
2872
2873 assert(filename);
2874 assert(lvalue);
2875 assert(rvalue);
2876 assert(data);
2877
2878 if (isempty(rvalue)) {
2879 /* Empty assignment resets the list */
2880
2881 set_free(status_set->signal);
2882 set_free(status_set->code);
2883
2884 status_set->signal = status_set->code = NULL;
2885 return 0;
2886 }
2887
2888 FOREACH_WORD(w, l, rvalue, state) {
2889 _cleanup_free_ char *temp;
2890 int val;
2891
2892 temp = strndup(w, l);
2893 if (!temp)
2894 return log_oom();
2895
2896 r = safe_atoi(temp, &val);
2897 if (r < 0) {
2898 val = signal_from_string_try_harder(temp);
2899
2900 if (val > 0) {
2901 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
2902 if (r < 0)
2903 return log_oom();
2904
2905 r = set_put(status_set->signal, INT_TO_PTR(val));
2906 if (r < 0) {
2907 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2908 return r;
2909 }
2910 } else {
2911 log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w);
2912 return 0;
2913 }
2914 } else {
2915 if (val < 0 || val > 255)
2916 log_syntax(unit, LOG_ERR, filename, line, ERANGE, "Value %d is outside range 0-255, ignoring", val);
2917 else {
2918 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
2919 if (r < 0)
2920 return log_oom();
2921
2922 r = set_put(status_set->code, INT_TO_PTR(val));
2923 if (r < 0) {
2924 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2925 return r;
2926 }
2927 }
2928 }
2929 }
2930
2931 return 0;
2932}
2933
94828d2d
LP
2934int config_parse_namespace_path_strv(
2935 const char *unit,
2936 const char *filename,
2937 unsigned line,
2938 const char *section,
2939 unsigned section_line,
2940 const char *lvalue,
2941 int ltype,
2942 const char *rvalue,
2943 void *data,
2944 void *userdata) {
2945
2946 char*** sv = data, *w, *state;
2947 size_t l;
2948 int r;
2949
2950 assert(filename);
2951 assert(lvalue);
2952 assert(rvalue);
2953 assert(data);
2954
2955 if (isempty(rvalue)) {
2956 /* Empty assignment resets the list */
2957 strv_free(*sv);
2958 *sv = NULL;
2959 return 0;
2960 }
2961
2962 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2963 _cleanup_free_ char *n;
2964 int offset;
2965
2966 n = strndup(w, l);
2967 if (!n)
2968 return log_oom();
2969
2970 if (!utf8_is_valid(n)) {
b5d74213 2971 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
94828d2d
LP
2972 continue;
2973 }
2974
2975 offset = n[0] == '-';
2976 if (!path_is_absolute(n + offset)) {
2977 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
2978 continue;
2979 }
2980
2981 path_kill_slashes(n);
2982
2983 r = strv_push(sv, n);
2984 if (r < 0)
2985 return log_oom();
2986
2987 n = NULL;
2988 }
2989
2990 return 0;
2991}
2992
760b9d7c
LP
2993int config_parse_no_new_priviliges(
2994 const char* unit,
2995 const char *filename,
2996 unsigned line,
2997 const char *section,
2998 unsigned section_line,
2999 const char *lvalue,
3000 int ltype,
3001 const char *rvalue,
3002 void *data,
3003 void *userdata) {
3004
3005 ExecContext *c = data;
3006 int k;
3007
3008 assert(filename);
3009 assert(lvalue);
3010 assert(rvalue);
3011 assert(data);
3012
3013 k = parse_boolean(rvalue);
3014 if (k < 0) {
3015 log_syntax(unit, LOG_ERR, filename, line, -k, "Failed to parse boolean value, ignoring: %s", rvalue);
3016 return 0;
3017 }
3018
3019 c->no_new_privileges = !!k;
3020 c->no_new_privileges_set = true;
3021
3022 return 0;
3023}
3024
071830ff 3025#define FOLLOW_MAX 8
87f0e418 3026
9e2f7c11 3027static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 3028 unsigned c = 0;
87f0e418
LP
3029 int fd, r;
3030 FILE *f;
0301abf4 3031 char *id = NULL;
87f0e418
LP
3032
3033 assert(filename);
3034 assert(*filename);
3035 assert(_f);
3036 assert(names);
3037
0301abf4
LP
3038 /* This will update the filename pointer if the loaded file is
3039 * reached by a symlink. The old string will be freed. */
87f0e418 3040
0301abf4 3041 for (;;) {
2c7108c4 3042 char *target, *name;
87f0e418 3043
0301abf4
LP
3044 if (c++ >= FOLLOW_MAX)
3045 return -ELOOP;
3046
b08d03ff
LP
3047 path_kill_slashes(*filename);
3048
87f0e418 3049 /* Add the file name we are currently looking at to
8f05424d
LP
3050 * the names of this unit, but only if it is a valid
3051 * unit name. */
2b6bf07d 3052 name = basename(*filename);
87f0e418 3053
f78e6385 3054 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
8f05424d 3055
15e11d81
LP
3056 id = set_get(names, name);
3057 if (!id) {
3058 id = strdup(name);
3059 if (!id)
8f05424d 3060 return -ENOMEM;
87f0e418 3061
ef42202a
ZJS
3062 r = set_consume(names, id);
3063 if (r < 0)
8f05424d 3064 return r;
87f0e418 3065 }
87f0e418
LP
3066 }
3067
0301abf4 3068 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
3069 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3070 if (fd >= 0)
87f0e418
LP
3071 break;
3072
0301abf4
LP
3073 if (errno != ELOOP)
3074 return -errno;
3075
87f0e418 3076 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
3077 r = readlink_and_make_absolute(*filename, &target);
3078 if (r < 0)
0301abf4 3079 return r;
87f0e418 3080
0301abf4 3081 free(*filename);
2c7108c4 3082 *filename = target;
87f0e418
LP
3083 }
3084
9946996c
LP
3085 f = fdopen(fd, "re");
3086 if (!f) {
87f0e418 3087 r = -errno;
03e334a1 3088 safe_close(fd);
0301abf4 3089 return r;
87f0e418
LP
3090 }
3091
3092 *_f = f;
9e2f7c11 3093 *_final = id;
0301abf4 3094 return 0;
87f0e418
LP
3095}
3096
23a177ef
LP
3097static int merge_by_names(Unit **u, Set *names, const char *id) {
3098 char *k;
3099 int r;
3100
3101 assert(u);
3102 assert(*u);
3103 assert(names);
3104
3105 /* Let's try to add in all symlink names we found */
3106 while ((k = set_steal_first(names))) {
3107
3108 /* First try to merge in the other name into our
3109 * unit */
9946996c
LP
3110 r = unit_merge_by_name(*u, k);
3111 if (r < 0) {
23a177ef
LP
3112 Unit *other;
3113
3114 /* Hmm, we couldn't merge the other unit into
3115 * ours? Then let's try it the other way
3116 * round */
3117
ac155bb8 3118 other = manager_get_unit((*u)->manager, k);
23a177ef
LP
3119 free(k);
3120
9946996c
LP
3121 if (other) {
3122 r = unit_merge(other, *u);
3123 if (r >= 0) {
23a177ef
LP
3124 *u = other;
3125 return merge_by_names(u, names, NULL);
3126 }
9946996c 3127 }
23a177ef
LP
3128
3129 return r;
3130 }
3131
3132 if (id == k)
3133 unit_choose_id(*u, id);
3134
3135 free(k);
3136 }
3137
3138 return 0;
3139}
3140
e537352b 3141static int load_from_path(Unit *u, const char *path) {
0301abf4 3142 int r;
e48614c4
ZJS
3143 _cleanup_set_free_free_ Set *symlink_names = NULL;
3144 _cleanup_fclose_ FILE *f = NULL;
3145 _cleanup_free_ char *filename = NULL;
3146 char *id = NULL;
23a177ef 3147 Unit *merged;
45fb0699 3148 struct stat st;
23a177ef
LP
3149
3150 assert(u);
e537352b 3151 assert(path);
3efd4195 3152
f975e971
LP
3153 symlink_names = set_new(string_hash_func, string_compare_func);
3154 if (!symlink_names)
87f0e418 3155 return -ENOMEM;
3efd4195 3156
036643a2
LP
3157 if (path_is_absolute(path)) {
3158
9946996c 3159 filename = strdup(path);
e48614c4
ZJS
3160 if (!filename)
3161 return -ENOMEM;
036643a2 3162
9946996c
LP
3163 r = open_follow(&filename, &f, symlink_names, &id);
3164 if (r < 0) {
036643a2
LP
3165 free(filename);
3166 filename = NULL;
3167
3168 if (r != -ENOENT)
e48614c4 3169 return r;
036643a2
LP
3170 }
3171
3172 } else {
3173 char **p;
3174
ac155bb8 3175 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
036643a2
LP
3176
3177 /* Instead of opening the path right away, we manually
3178 * follow all symlinks and add their name to our unit
3179 * name set while doing so */
9946996c 3180 filename = path_make_absolute(path, *p);
e48614c4
ZJS
3181 if (!filename)
3182 return -ENOMEM;
036643a2 3183
ac155bb8
MS
3184 if (u->manager->unit_path_cache &&
3185 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
3186 r = -ENOENT;
3187 else
3188 r = open_follow(&filename, &f, symlink_names, &id);
3189
3190 if (r < 0) {
036643a2
LP
3191 free(filename);
3192 filename = NULL;
3193
3194 if (r != -ENOENT)
e48614c4 3195 return r;
036643a2
LP
3196
3197 /* Empty the symlink names for the next run */
9946996c 3198 set_clear_free(symlink_names);
036643a2
LP
3199 continue;
3200 }
3201
3202 break;
3203 }
3204 }
034c6ed7 3205
e48614c4 3206 if (!filename)
8f05424d 3207 /* Hmm, no suitable file found? */
e48614c4 3208 return 0;
87f0e418 3209
23a177ef 3210 merged = u;
9946996c
LP
3211 r = merge_by_names(&merged, symlink_names, id);
3212 if (r < 0)
e48614c4 3213 return r;
87f0e418 3214
23a177ef 3215 if (merged != u) {
ac155bb8 3216 u->load_state = UNIT_MERGED;
e48614c4 3217 return 0;
034c6ed7
LP
3218 }
3219
e48614c4
ZJS
3220 if (fstat(fileno(f), &st) < 0)
3221 return -errno;
45fb0699 3222
00dc5d76 3223 if (null_or_empty(&st))
ac155bb8 3224 u->load_state = UNIT_MASKED;
00dc5d76 3225 else {
c2756a68
LP
3226 u->load_state = UNIT_LOADED;
3227
00dc5d76 3228 /* Now, parse the file contents */
e8e581bf
ZJS
3229 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
3230 config_item_perf_lookup,
db5c0122 3231 (void*) load_fragment_gperf_lookup, false, true, u);
f975e971 3232 if (r < 0)
e48614c4 3233 return r;
00dc5d76 3234 }
b08d03ff 3235
ac155bb8
MS
3236 free(u->fragment_path);
3237 u->fragment_path = filename;
0301abf4 3238 filename = NULL;
87f0e418 3239
ac155bb8 3240 u->fragment_mtime = timespec_load(&st.st_mtim);
45fb0699 3241
1b64d026
LP
3242 if (u->source_path) {
3243 if (stat(u->source_path, &st) >= 0)
3244 u->source_mtime = timespec_load(&st.st_mtim);
3245 else
3246 u->source_mtime = 0;
3247 }
3248
e48614c4 3249 return 0;
0301abf4
LP
3250}
3251
e537352b 3252int unit_load_fragment(Unit *u) {
23a177ef 3253 int r;
294d81f1
LP
3254 Iterator i;
3255 const char *t;
0301abf4
LP
3256
3257 assert(u);
ac155bb8
MS
3258 assert(u->load_state == UNIT_STUB);
3259 assert(u->id);
23a177ef 3260
294d81f1
LP
3261 /* First, try to find the unit under its id. We always look
3262 * for unit files in the default directories, to make it easy
3263 * to override things by placing things in /etc/systemd/system */
9946996c
LP
3264 r = load_from_path(u, u->id);
3265 if (r < 0)
294d81f1
LP
3266 return r;
3267
3268 /* Try to find an alias we can load this with */
ac155bb8
MS
3269 if (u->load_state == UNIT_STUB)
3270 SET_FOREACH(t, u->names, i) {
294d81f1 3271
ac155bb8 3272 if (t == u->id)
294d81f1
LP
3273 continue;
3274
9946996c
LP
3275 r = load_from_path(u, t);
3276 if (r < 0)
294d81f1
LP
3277 return r;
3278
ac155bb8 3279 if (u->load_state != UNIT_STUB)
294d81f1
LP
3280 break;
3281 }
23a177ef 3282
294d81f1 3283 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 3284 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 3285
9946996c
LP
3286 r = load_from_path(u, u->fragment_path);
3287 if (r < 0)
23a177ef 3288 return r;
0301abf4 3289
ac155bb8 3290 if (u->load_state == UNIT_STUB) {
6ccb1b44
LP
3291 /* Hmm, this didn't work? Then let's get rid
3292 * of the fragment path stored for us, so that
3293 * we don't point to an invalid location. */
ac155bb8
MS
3294 free(u->fragment_path);
3295 u->fragment_path = NULL;
6ccb1b44
LP
3296 }
3297 }
3298
294d81f1 3299 /* Look for a template */
ac155bb8 3300 if (u->load_state == UNIT_STUB && u->instance) {
bc9fd78c 3301 _cleanup_free_ char *k;
294d81f1 3302
9946996c
LP
3303 k = unit_name_template(u->id);
3304 if (!k)
294d81f1
LP
3305 return -ENOMEM;
3306
3307 r = load_from_path(u, k);
294d81f1 3308 if (r < 0)
9e2f7c11 3309 return r;
890f434c 3310
ac155bb8
MS
3311 if (u->load_state == UNIT_STUB)
3312 SET_FOREACH(t, u->names, i) {
bc9fd78c 3313 _cleanup_free_ char *z = NULL;
87f0e418 3314
ac155bb8 3315 if (t == u->id)
23a177ef 3316 continue;
071830ff 3317
bc9fd78c
LP
3318 z = unit_name_template(t);
3319 if (!z)
294d81f1
LP
3320 return -ENOMEM;
3321
bc9fd78c 3322 r = load_from_path(u, z);
294d81f1 3323 if (r < 0)
23a177ef 3324 return r;
890f434c 3325
ac155bb8 3326 if (u->load_state != UNIT_STUB)
23a177ef
LP
3327 break;
3328 }
071830ff
LP
3329 }
3330
23a177ef 3331 return 0;
3efd4195 3332}
e537352b
LP
3333
3334void unit_dump_config_items(FILE *f) {
f975e971
LP
3335 static const struct {
3336 const ConfigParserCallback callback;
3337 const char *rvalue;
3338 } table[] = {
eef65bf3 3339#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
3340 { config_parse_warn_compat, "NOTSUPPORTED" },
3341#endif
f975e971
LP
3342 { config_parse_int, "INTEGER" },
3343 { config_parse_unsigned, "UNSIGNED" },
5556b5fe
LP
3344 { config_parse_iec_size, "SIZE" },
3345 { config_parse_iec_off, "SIZE" },
3346 { config_parse_si_size, "SIZE" },
f975e971
LP
3347 { config_parse_bool, "BOOLEAN" },
3348 { config_parse_string, "STRING" },
3349 { config_parse_path, "PATH" },
3350 { config_parse_unit_path_printf, "PATH" },
3351 { config_parse_strv, "STRING [...]" },
3352 { config_parse_exec_nice, "NICE" },
3353 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3354 { config_parse_exec_io_class, "IOCLASS" },
3355 { config_parse_exec_io_priority, "IOPRIORITY" },
3356 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3357 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3358 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3359 { config_parse_mode, "MODE" },
3360 { config_parse_unit_env_file, "FILE" },
3361 { config_parse_output, "OUTPUT" },
3362 { config_parse_input, "INPUT" },
ca37242e
LP
3363 { config_parse_log_facility, "FACILITY" },
3364 { config_parse_log_level, "LEVEL" },
f975e971
LP
3365 { config_parse_exec_capabilities, "CAPABILITIES" },
3366 { config_parse_exec_secure_bits, "SECUREBITS" },
ec8927ca 3367 { config_parse_bounding_set, "BOUNDINGSET" },
f975e971 3368 { config_parse_limit, "LIMIT" },
f975e971 3369 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
3370 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3371 { config_parse_service_type, "SERVICETYPE" },
3372 { config_parse_service_restart, "SERVICERESTART" },
3373#ifdef HAVE_SYSV_COMPAT
3374 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
3375#endif
3376 { config_parse_kill_mode, "KILLMODE" },
3377 { config_parse_kill_signal, "SIGNAL" },
3378 { config_parse_socket_listen, "SOCKET [...]" },
3379 { config_parse_socket_bind, "SOCKETBIND" },
3380 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 3381 { config_parse_sec, "SECONDS" },
d88a251b 3382 { config_parse_nsec, "NANOSECONDS" },
94828d2d 3383 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 3384 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
3385 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3386 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 3387 { config_parse_trigger_unit, "UNIT" },
f975e971 3388 { config_parse_timer, "TIMER" },
f975e971 3389 { config_parse_path_spec, "PATH" },
f975e971
LP
3390 { config_parse_notify_access, "ACCESS" },
3391 { config_parse_ip_tos, "TOS" },
3392 { config_parse_unit_condition_path, "CONDITION" },
3393 { config_parse_unit_condition_string, "CONDITION" },
3394 { config_parse_unit_condition_null, "CONDITION" },
a016b922 3395 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
3396 { config_parse_documentation, "URL" },
3397 { config_parse_service_timeout, "SECONDS" },
3398 { config_parse_start_limit_action, "ACTION" },
3399 { config_parse_set_status, "STATUS" },
3400 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 3401 { config_parse_environ, "ENVIRON" },
c0467cf3 3402#ifdef HAVE_SECCOMP
17df7223 3403 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 3404 { config_parse_syscall_archs, "ARCHS" },
17df7223 3405 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 3406 { config_parse_address_families, "FAMILIES" },
c0467cf3 3407#endif
7f0386f6
LP
3408 { config_parse_cpu_shares, "SHARES" },
3409 { config_parse_memory_limit, "LIMIT" },
3410 { config_parse_device_allow, "DEVICE" },
3411 { config_parse_device_policy, "POLICY" },
3412 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3413 { config_parse_blockio_weight, "WEIGHT" },
3414 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3415 { config_parse_long, "LONG" },
3416 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
3417#ifdef HAVE_SELINUX
3418 { config_parse_exec_selinux_context, "LABEL" },
3419#endif
3420 { config_parse_job_mode, "MODE" },
3421 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 3422 { config_parse_personality, "PERSONALITY" },
f975e971
LP
3423 };
3424
3425 const char *prev = NULL;
3426 const char *i;
3427
3428 assert(f);
e537352b 3429
f975e971
LP
3430 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3431 const char *rvalue = "OTHER", *lvalue;
3432 unsigned j;
3433 size_t prefix_len;
3434 const char *dot;
3435 const ConfigPerfItem *p;
3436
3437 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3438
3439 dot = strchr(i, '.');
3440 lvalue = dot ? dot + 1 : i;
3441 prefix_len = dot-i;
3442
3443 if (dot)
641906e9 3444 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
3445 if (prev)
3446 fputc('\n', f);
3447
3448 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3449 }
3450
3451 for (j = 0; j < ELEMENTSOF(table); j++)
3452 if (p->parse == table[j].callback) {
3453 rvalue = table[j].rvalue;
3454 break;
3455 }
3456
3457 fprintf(f, "%s=%s\n", lvalue, rvalue);
3458 prev = i;
3459 }
e537352b 3460}