]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
core: make sure we always write changed cgroup attributes to the cgroupfs
[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
7f8aa671 67#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !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"))
c2c13f2d 1128 flags = MS_SHARED;
ac97e2c5 1129 else if (streq(t, "slave"))
c2c13f2d 1130 flags = MS_SLAVE;
ac97e2c5 1131 else if (streq(w, "private"))
c2c13f2d 1132 flags = MS_PRIVATE;
15ae422b 1133 else {
c2c13f2d 1134 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
c0b34696 1135 return 0;
15ae422b
LP
1136 }
1137 }
1138
1139 c->mount_flags = flags;
1140 return 0;
1141}
1142
5f8640fb
LP
1143int config_parse_exec_selinux_context(
1144 const char *unit,
1145 const char *filename,
1146 unsigned line,
1147 const char *section,
1148 unsigned section_line,
1149 const char *lvalue,
1150 int ltype,
1151 const char *rvalue,
1152 void *data,
1153 void *userdata) {
1154
1155 ExecContext *c = data;
1156 Unit *u = userdata;
1157 bool ignore;
1158 char *k;
1159 int r;
1160
1161 assert(filename);
1162 assert(lvalue);
1163 assert(rvalue);
1164 assert(data);
1165
1166 if (isempty(rvalue)) {
1167 free(c->selinux_context);
1168 c->selinux_context = NULL;
1169 c->selinux_context_ignore = false;
1170 return 0;
1171 }
1172
1173 if (rvalue[0] == '-') {
1174 ignore = true;
1175 rvalue++;
1176 } else
1177 ignore = false;
1178
1179 r = unit_name_printf(u, rvalue, &k);
1180 if (r < 0) {
1181 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1182 return 0;
1183 }
1184
1185 free(c->selinux_context);
1186 c->selinux_context = k;
1187 c->selinux_context_ignore = ignore;
1188
1189 return 0;
1190}
1191
eef65bf3
MS
1192int config_parse_exec_apparmor_profile(
1193 const char *unit,
1194 const char *filename,
1195 unsigned line,
1196 const char *section,
1197 unsigned section_line,
1198 const char *lvalue,
1199 int ltype,
1200 const char *rvalue,
1201 void *data,
1202 void *userdata) {
1203
1204 ExecContext *c = data;
1205 Unit *u = userdata;
1206 bool ignore;
1207 char *k;
1208 int r;
1209
1210 assert(filename);
1211 assert(lvalue);
1212 assert(rvalue);
1213 assert(data);
1214
1215 if (isempty(rvalue)) {
1216 free(c->apparmor_profile);
1217 c->apparmor_profile = NULL;
1218 c->apparmor_profile_ignore = false;
1219 return 0;
1220 }
1221
1222 if (rvalue[0] == '-') {
1223 ignore = true;
1224 rvalue++;
1225 } else
1226 ignore = false;
1227
1228 r = unit_name_printf(u, rvalue, &k);
1229 if (r < 0) {
1230 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1231 return 0;
1232 }
1233
1234 free(c->apparmor_profile);
1235 c->apparmor_profile = k;
1236 c->apparmor_profile_ignore = ignore;
1237
1238 return 0;
1239}
1240
e8e581bf
ZJS
1241int config_parse_timer(const char *unit,
1242 const char *filename,
1243 unsigned line,
1244 const char *section,
71a61510 1245 unsigned section_line,
e8e581bf
ZJS
1246 const char *lvalue,
1247 int ltype,
1248 const char *rvalue,
1249 void *data,
1250 void *userdata) {
871d7de4
LP
1251
1252 Timer *t = data;
36697dc0 1253 usec_t u = 0;
871d7de4
LP
1254 TimerValue *v;
1255 TimerBase b;
36697dc0 1256 CalendarSpec *c = NULL;
871d7de4
LP
1257
1258 assert(filename);
1259 assert(lvalue);
1260 assert(rvalue);
1261 assert(data);
1262
74051b9b
LP
1263 if (isempty(rvalue)) {
1264 /* Empty assignment resets list */
1265 timer_free_values(t);
1266 return 0;
1267 }
1268
36697dc0
LP
1269 b = timer_base_from_string(lvalue);
1270 if (b < 0) {
e8e581bf
ZJS
1271 log_syntax(unit, LOG_ERR, filename, line, -b,
1272 "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1273 return 0;
871d7de4
LP
1274 }
1275
36697dc0
LP
1276 if (b == TIMER_CALENDAR) {
1277 if (calendar_spec_from_string(rvalue, &c) < 0) {
e8e581bf
ZJS
1278 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1279 "Failed to parse calendar specification, ignoring: %s",
1280 rvalue);
36697dc0
LP
1281 return 0;
1282 }
36697dc0 1283 } else {
7f602784 1284 if (parse_sec(rvalue, &u) < 0) {
e8e581bf
ZJS
1285 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1286 "Failed to parse timer value, ignoring: %s",
1287 rvalue);
36697dc0
LP
1288 return 0;
1289 }
871d7de4
LP
1290 }
1291
36697dc0
LP
1292 v = new0(TimerValue, 1);
1293 if (!v)
74051b9b 1294 return log_oom();
871d7de4
LP
1295
1296 v->base = b;
1297 v->value = u;
36697dc0 1298 v->calendar_spec = c;
871d7de4 1299
71fda00f 1300 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1301
1302 return 0;
1303}
1304
3ecaa09b
LP
1305int config_parse_trigger_unit(
1306 const char *unit,
1307 const char *filename,
1308 unsigned line,
1309 const char *section,
71a61510 1310 unsigned section_line,
3ecaa09b
LP
1311 const char *lvalue,
1312 int ltype,
1313 const char *rvalue,
1314 void *data,
1315 void *userdata) {
871d7de4 1316
74051b9b 1317 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1318 Unit *u = data;
1319 UnitType type;
1320 int r;
398ef8ba
LP
1321
1322 assert(filename);
1323 assert(lvalue);
1324 assert(rvalue);
1325 assert(data);
1326
3ecaa09b
LP
1327 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1328 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1329 "Multiple units to trigger specified, ignoring: %s", rvalue);
1330 return 0;
1331 }
871d7de4 1332
19f6d710
LP
1333 r = unit_name_printf(u, rvalue, &p);
1334 if (r < 0)
1335 log_syntax(unit, LOG_ERR, filename, line, -r,
1336 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
74051b9b 1337
19f6d710 1338 type = unit_name_to_type(p ?: rvalue);
3ecaa09b 1339 if (type < 0) {
e8e581bf 1340 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3ecaa09b 1341 "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1342 return 0;
871d7de4
LP
1343 }
1344
3ecaa09b
LP
1345 if (type == u->type) {
1346 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1347 "Trigger cannot be of same type, ignoring: %s", rvalue);
1348 return 0;
1349 }
1350
19f6d710 1351 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
57020a3a 1352 if (r < 0) {
e8e581bf 1353 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1354 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
c0b34696 1355 return 0;
871d7de4
LP
1356 }
1357
1358 return 0;
1359}
1360
e8e581bf
ZJS
1361int config_parse_path_spec(const char *unit,
1362 const char *filename,
1363 unsigned line,
1364 const char *section,
71a61510 1365 unsigned section_line,
e8e581bf
ZJS
1366 const char *lvalue,
1367 int ltype,
1368 const char *rvalue,
1369 void *data,
1370 void *userdata) {
01f78473
LP
1371
1372 Path *p = data;
1373 PathSpec *s;
1374 PathType b;
7fd1b19b 1375 _cleanup_free_ char *k = NULL;
19f6d710 1376 int r;
01f78473
LP
1377
1378 assert(filename);
1379 assert(lvalue);
1380 assert(rvalue);
1381 assert(data);
1382
74051b9b
LP
1383 if (isempty(rvalue)) {
1384 /* Empty assignment clears list */
1385 path_free_specs(p);
1386 return 0;
1387 }
1388
93e4c84b
LP
1389 b = path_type_from_string(lvalue);
1390 if (b < 0) {
e8e581bf
ZJS
1391 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1392 "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1393 return 0;
01f78473
LP
1394 }
1395
19f6d710
LP
1396 r = unit_full_printf(UNIT(p), rvalue, &k);
1397 if (r < 0) {
487060c2
LP
1398 k = strdup(rvalue);
1399 if (!k)
1400 return log_oom();
1401 else
19f6d710 1402 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf
ZJS
1403 "Failed to resolve unit specifiers on %s. Ignoring.",
1404 rvalue);
487060c2 1405 }
93e4c84b
LP
1406
1407 if (!path_is_absolute(k)) {
e8e581bf
ZJS
1408 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1409 "Path is not absolute, ignoring: %s", k);
c0b34696 1410 return 0;
01f78473
LP
1411 }
1412
93e4c84b 1413 s = new0(PathSpec, 1);
543295ad 1414 if (!s)
93e4c84b 1415 return log_oom();
01f78473 1416
718db961 1417 s->unit = UNIT(p);
93e4c84b 1418 s->path = path_kill_slashes(k);
543295ad 1419 k = NULL;
01f78473
LP
1420 s->type = b;
1421 s->inotify_fd = -1;
1422
71fda00f 1423 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1424
1425 return 0;
1426}
1427
e8e581bf
ZJS
1428int config_parse_socket_service(const char *unit,
1429 const char *filename,
1430 unsigned line,
1431 const char *section,
71a61510 1432 unsigned section_line,
e8e581bf
ZJS
1433 const char *lvalue,
1434 int ltype,
1435 const char *rvalue,
1436 void *data,
1437 void *userdata) {
d9ff321a 1438
718db961 1439 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d9ff321a
LP
1440 Socket *s = data;
1441 int r;
4ff77f66 1442 Unit *x;
74051b9b 1443 _cleanup_free_ char *p = NULL;
d9ff321a
LP
1444
1445 assert(filename);
1446 assert(lvalue);
1447 assert(rvalue);
1448 assert(data);
1449
19f6d710 1450 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1451 if (r < 0) {
718db961 1452 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
613b411c
LP
1453 return 0;
1454 }
74051b9b 1455
613b411c 1456 if (!endswith(p, ".service")) {
718db961 1457 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1458 return 0;
1459 }
1460
613b411c 1461 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1462 if (r < 0) {
718db961 1463 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
d9ff321a
LP
1464 return 0;
1465 }
1466
4ff77f66
LP
1467 unit_ref_set(&s->service, x);
1468
d9ff321a
LP
1469 return 0;
1470}
1471
e8e581bf
ZJS
1472int config_parse_service_sockets(const char *unit,
1473 const char *filename,
1474 unsigned line,
1475 const char *section,
71a61510 1476 unsigned section_line,
e8e581bf
ZJS
1477 const char *lvalue,
1478 int ltype,
1479 const char *rvalue,
1480 void *data,
1481 void *userdata) {
f976f3f6
LP
1482
1483 Service *s = data;
1484 int r;
f976f3f6
LP
1485 char *state, *w;
1486 size_t l;
1487
1488 assert(filename);
1489 assert(lvalue);
1490 assert(rvalue);
1491 assert(data);
1492
f976f3f6 1493 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 1494 _cleanup_free_ char *t = NULL, *k = NULL;
f976f3f6 1495
57020a3a
LP
1496 t = strndup(w, l);
1497 if (!t)
74051b9b 1498 return log_oom();
f976f3f6 1499
19f6d710
LP
1500 r = unit_name_printf(UNIT(s), t, &k);
1501 if (r < 0)
1502 log_syntax(unit, LOG_ERR, filename, line, -r,
1503 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
57020a3a 1504
19f6d710 1505 if (!endswith(k ?: t, ".socket")) {
e8e581bf 1506 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1507 "Unit must be of type socket, ignoring: %s", k ?: t);
f976f3f6
LP
1508 continue;
1509 }
1510
19f6d710 1511 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
57020a3a 1512 if (r < 0)
e8e581bf
ZJS
1513 log_syntax(unit, LOG_ERR, filename, line, -r,
1514 "Failed to add dependency on %s, ignoring: %s",
19f6d710 1515 k ?: t, strerror(-r));
f976f3f6 1516
19f6d710 1517 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
57020a3a 1518 if (r < 0)
f976f3f6
LP
1519 return r;
1520 }
1521
1522 return 0;
1523}
1524
e8e581bf
ZJS
1525int config_parse_service_timeout(const char *unit,
1526 const char *filename,
1527 unsigned line,
1528 const char *section,
71a61510 1529 unsigned section_line,
e8e581bf
ZJS
1530 const char *lvalue,
1531 int ltype,
1532 const char *rvalue,
1533 void *data,
1534 void *userdata) {
98709151
LN
1535
1536 Service *s = userdata;
1537 int r;
1538
1539 assert(filename);
1540 assert(lvalue);
1541 assert(rvalue);
1542 assert(s);
1543
71a61510 1544 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 1545 rvalue, data, userdata);
74051b9b 1546 if (r < 0)
d568a335 1547 return r;
98709151 1548
d568a335
MS
1549 if (streq(lvalue, "TimeoutSec")) {
1550 s->start_timeout_defined = true;
1551 s->timeout_stop_usec = s->timeout_start_usec;
1552 } else if (streq(lvalue, "TimeoutStartSec"))
1553 s->start_timeout_defined = true;
1554
1555 return 0;
98709151
LN
1556}
1557
e821075a
LP
1558int config_parse_busname_service(
1559 const char *unit,
1560 const char *filename,
1561 unsigned line,
1562 const char *section,
1563 unsigned section_line,
1564 const char *lvalue,
1565 int ltype,
1566 const char *rvalue,
1567 void *data,
1568 void *userdata) {
1569
1570 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1571 BusName *n = data;
1572 int r;
1573 Unit *x;
1574 _cleanup_free_ char *p = NULL;
1575
1576 assert(filename);
1577 assert(lvalue);
1578 assert(rvalue);
1579 assert(data);
1580
1581 r = unit_name_printf(UNIT(n), rvalue, &p);
1582 if (r < 0) {
1583 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1584 return 0;
1585 }
1586
1587 if (!endswith(p, ".service")) {
1588 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1589 return 0;
1590 }
1591
1592 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1593 if (r < 0) {
1594 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1595 return 0;
1596 }
1597
1598 unit_ref_set(&n->service, x);
1599
1600 return 0;
1601}
1602
54d76c92
DM
1603int config_parse_bus_policy(
1604 const char *unit,
1605 const char *filename,
1606 unsigned line,
1607 const char *section,
1608 unsigned section_line,
1609 const char *lvalue,
1610 int ltype,
1611 const char *rvalue,
1612 void *data,
1613 void *userdata) {
1614
1615 _cleanup_free_ BusNamePolicy *p = NULL;
1616 _cleanup_free_ char *id_str = NULL;
1617 BusName *busname = data;
1618 char *access_str;
1619 int r;
1620
1621 assert(filename);
1622 assert(lvalue);
1623 assert(rvalue);
1624 assert(data);
1625
1626 p = new0(BusNamePolicy, 1);
1627 if (!p)
1628 return log_oom();
1629
1630 if (streq(lvalue, "AllowUser"))
1631 p->type = BUSNAME_POLICY_TYPE_USER;
1632 else if (streq(lvalue, "AllowGroup"))
1633 p->type = BUSNAME_POLICY_TYPE_GROUP;
1634 else if (streq(lvalue, "AllowWorld"))
1635 p->type = BUSNAME_POLICY_TYPE_WORLD;
1636 else
1637 assert_not_reached("Unknown lvalue");
1638
1639 id_str = strdup(rvalue);
1640 if (!id_str)
1641 return log_oom();
1642
1643 if (p->type != BUSNAME_POLICY_TYPE_WORLD) {
1644 access_str = strchr(id_str, ' ');
1645 if (!access_str) {
1646 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy value '%s'", rvalue);
1647 return 0;
1648 }
1649
1650 *access_str = '\0';
1651 access_str++;
1652
1653 if (p->type == BUSNAME_POLICY_TYPE_USER) {
1654 const char *user = id_str;
1655
1656 r = get_user_creds(&user, &p->uid, NULL, NULL, NULL);
1657 if (r < 0) {
1658 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to parse uid from '%s'", id_str);
1659 return 0;
1660 }
1661 } else {
1662 const char *group = id_str;
1663
1664 r = get_group_creds(&group, &p->gid);
1665 if (r < 0) {
1666 log_syntax(unit, LOG_ERR, filename, line, -errno, "Unable to parse gid from '%s'", id_str);
1667 return 0;
1668 }
1669 }
1670 } else {
1671 access_str = id_str;
1672 }
1673
1674 p->access = busname_policy_access_from_string(access_str);
1675 if (p->access < 0) {
1676 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy access type '%s'", access_str);
1677 return 0;
1678 }
1679
1680 LIST_PREPEND(policy, busname->policy, p);
1681 p = NULL;
1682
1683 return 0;
1684}
1685
e8e581bf
ZJS
1686int config_parse_unit_env_file(const char *unit,
1687 const char *filename,
1688 unsigned line,
1689 const char *section,
71a61510 1690 unsigned section_line,
e8e581bf
ZJS
1691 const char *lvalue,
1692 int ltype,
1693 const char *rvalue,
1694 void *data,
1695 void *userdata) {
ddb26e18 1696
853b8397 1697 char ***env = data;
8fef7659 1698 Unit *u = userdata;
19f6d710
LP
1699 _cleanup_free_ char *n = NULL;
1700 const char *s;
853b8397 1701 int r;
ddb26e18
LP
1702
1703 assert(filename);
1704 assert(lvalue);
1705 assert(rvalue);
1706 assert(data);
1707
74051b9b
LP
1708 if (isempty(rvalue)) {
1709 /* Empty assignment frees the list */
74051b9b
LP
1710 strv_free(*env);
1711 *env = NULL;
1712 return 0;
1713 }
1714
19f6d710
LP
1715 r = unit_full_printf(u, rvalue, &n);
1716 if (r < 0)
1717 log_syntax(unit, LOG_ERR, filename, line, r,
1718 "Failed to resolve specifiers, ignoring: %s", rvalue);
8fef7659 1719
19f6d710 1720 s = n ?: rvalue;
8fef7659 1721 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
e8e581bf
ZJS
1722 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1723 "Path '%s' is not absolute, ignoring.", s);
afe4bfe2
LP
1724 return 0;
1725 }
1726
853b8397
LP
1727 r = strv_extend(env, s);
1728 if (r < 0)
1729 return log_oom();
1730
1731 return 0;
1732}
1733
e8e581bf
ZJS
1734int config_parse_environ(const char *unit,
1735 const char *filename,
1736 unsigned line,
1737 const char *section,
71a61510 1738 unsigned section_line,
e8e581bf
ZJS
1739 const char *lvalue,
1740 int ltype,
1741 const char *rvalue,
1742 void *data,
1743 void *userdata) {
853b8397
LP
1744
1745 Unit *u = userdata;
1746 char*** env = data, *w, *state;
1747 size_t l;
1748 _cleanup_free_ char *k = NULL;
19f6d710 1749 int r;
853b8397
LP
1750
1751 assert(filename);
1752 assert(lvalue);
1753 assert(rvalue);
97d0e5f8 1754 assert(data);
853b8397
LP
1755
1756 if (isempty(rvalue)) {
1757 /* Empty assignment resets the list */
1758 strv_free(*env);
1759 *env = NULL;
1760 return 0;
1761 }
1762
19f6d710
LP
1763 if (u) {
1764 r = unit_full_printf(u, rvalue, &k);
1765 if (r < 0)
1766 log_syntax(unit, LOG_ERR, filename, line, -r,
1767 "Failed to resolve specifiers, ignoring: %s", rvalue);
1768 }
97d0e5f8 1769
19f6d710
LP
1770 if (!k)
1771 k = strdup(rvalue);
8fef7659 1772 if (!k)
74051b9b 1773 return log_oom();
ddb26e18 1774
853b8397
LP
1775 FOREACH_WORD_QUOTED(w, l, k, state) {
1776 _cleanup_free_ char *n;
1777 char **x;
1778
1779 n = cunescape_length(w, l);
1780 if (!n)
1781 return log_oom();
1782
1783 if (!env_assignment_is_valid(n)) {
e8e581bf
ZJS
1784 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1785 "Invalid environment assignment, ignoring: %s", rvalue);
853b8397
LP
1786 continue;
1787 }
1788
1789 x = strv_env_set(*env, n);
1790 if (!x)
1791 return log_oom();
1792
1793 strv_free(*env);
1794 *env = x;
1795 }
ddb26e18 1796
8c7be95e 1797 return 0;
ddb26e18
LP
1798}
1799
e8e581bf
ZJS
1800int config_parse_ip_tos(const char *unit,
1801 const char *filename,
1802 unsigned line,
1803 const char *section,
71a61510 1804 unsigned section_line,
e8e581bf
ZJS
1805 const char *lvalue,
1806 int ltype,
1807 const char *rvalue,
1808 void *data,
1809 void *userdata) {
4fd5948e
LP
1810
1811 int *ip_tos = data, x;
4fd5948e
LP
1812
1813 assert(filename);
1814 assert(lvalue);
1815 assert(rvalue);
1816 assert(data);
1817
f8b69d1d
MS
1818 x = ip_tos_from_string(rvalue);
1819 if (x < 0) {
e8e581bf
ZJS
1820 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1821 "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
1822 return 0;
1823 }
4fd5948e
LP
1824
1825 *ip_tos = x;
1826 return 0;
1827}
1828
e8e581bf
ZJS
1829int config_parse_unit_condition_path(const char *unit,
1830 const char *filename,
1831 unsigned line,
1832 const char *section,
71a61510 1833 unsigned section_line,
e8e581bf
ZJS
1834 const char *lvalue,
1835 int ltype,
1836 const char *rvalue,
1837 void *data,
1838 void *userdata) {
52661efd 1839
2b583ce6 1840 ConditionType cond = ltype;
52661efd 1841 Unit *u = data;
267632f0 1842 bool trigger, negate;
52661efd 1843 Condition *c;
2fbe635a 1844 _cleanup_free_ char *p = NULL;
19f6d710 1845 int r;
52661efd
LP
1846
1847 assert(filename);
1848 assert(lvalue);
1849 assert(rvalue);
1850 assert(data);
1851
74051b9b
LP
1852 if (isempty(rvalue)) {
1853 /* Empty assignment resets the list */
1854 condition_free_list(u->conditions);
1855 u->conditions = NULL;
1856 return 0;
1857 }
1858
ab7f148f
LP
1859 trigger = rvalue[0] == '|';
1860 if (trigger)
267632f0
LP
1861 rvalue++;
1862
ab7f148f
LP
1863 negate = rvalue[0] == '!';
1864 if (negate)
52661efd
LP
1865 rvalue++;
1866
19f6d710
LP
1867 r = unit_full_printf(u, rvalue, &p);
1868 if (r < 0)
1869 log_syntax(unit, LOG_ERR, filename, line, -r,
1870 "Failed to resolve specifiers, ignoring: %s", rvalue);
1871 if (!p) {
1872 p = strdup(rvalue);
1873 if (!p)
1874 return log_oom();
1875 }
095b2d7a
AK
1876
1877 if (!path_is_absolute(p)) {
e8e581bf
ZJS
1878 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1879 "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
1880 return 0;
1881 }
1882
095b2d7a 1883 c = condition_new(cond, p, trigger, negate);
ab7f148f 1884 if (!c)
74051b9b 1885 return log_oom();
52661efd 1886
71fda00f 1887 LIST_PREPEND(conditions, u->conditions, c);
52661efd
LP
1888 return 0;
1889}
1890
e8e581bf
ZJS
1891int config_parse_unit_condition_string(const char *unit,
1892 const char *filename,
1893 unsigned line,
1894 const char *section,
71a61510 1895 unsigned section_line,
e8e581bf
ZJS
1896 const char *lvalue,
1897 int ltype,
1898 const char *rvalue,
1899 void *data,
1900 void *userdata) {
039655a4 1901
41584525 1902 ConditionType cond = ltype;
039655a4 1903 Unit *u = data;
267632f0 1904 bool trigger, negate;
039655a4 1905 Condition *c;
2fbe635a 1906 _cleanup_free_ char *s = NULL;
19f6d710 1907 int r;
039655a4
LP
1908
1909 assert(filename);
1910 assert(lvalue);
1911 assert(rvalue);
1912 assert(data);
1913
74051b9b
LP
1914 if (isempty(rvalue)) {
1915 /* Empty assignment resets the list */
1916 condition_free_list(u->conditions);
1917 u->conditions = NULL;
1918 return 0;
1919 }
1920
c0d6e764
LP
1921 trigger = rvalue[0] == '|';
1922 if (trigger)
267632f0
LP
1923 rvalue++;
1924
c0d6e764
LP
1925 negate = rvalue[0] == '!';
1926 if (negate)
039655a4
LP
1927 rvalue++;
1928
19f6d710
LP
1929 r = unit_full_printf(u, rvalue, &s);
1930 if (r < 0)
1931 log_syntax(unit, LOG_ERR, filename, line, -r,
1932 "Failed to resolve specifiers, ignoring: %s", rvalue);
1933 if (!s) {
1934 s = strdup(rvalue);
1935 if (!s)
1936 return log_oom();
1937 }
095b2d7a
AK
1938
1939 c = condition_new(cond, s, trigger, negate);
c0d6e764
LP
1940 if (!c)
1941 return log_oom();
039655a4 1942
71fda00f 1943 LIST_PREPEND(conditions, u->conditions, c);
039655a4
LP
1944 return 0;
1945}
1946
e8e581bf
ZJS
1947int config_parse_unit_condition_null(const char *unit,
1948 const char *filename,
1949 unsigned line,
1950 const char *section,
71a61510 1951 unsigned section_line,
e8e581bf
ZJS
1952 const char *lvalue,
1953 int ltype,
1954 const char *rvalue,
1955 void *data,
1956 void *userdata) {
d257ddef
LP
1957
1958 Unit *u = data;
1959 Condition *c;
267632f0 1960 bool trigger, negate;
d257ddef
LP
1961 int b;
1962
1963 assert(filename);
1964 assert(lvalue);
1965 assert(rvalue);
1966 assert(data);
1967
74051b9b
LP
1968 if (isempty(rvalue)) {
1969 /* Empty assignment resets the list */
1970 condition_free_list(u->conditions);
1971 u->conditions = NULL;
1972 return 0;
1973 }
1974
1975 trigger = rvalue[0] == '|';
1976 if (trigger)
267632f0
LP
1977 rvalue++;
1978
74051b9b
LP
1979 negate = rvalue[0] == '!';
1980 if (negate)
d257ddef
LP
1981 rvalue++;
1982
74051b9b
LP
1983 b = parse_boolean(rvalue);
1984 if (b < 0) {
e8e581bf
ZJS
1985 log_syntax(unit, LOG_ERR, filename, line, -b,
1986 "Failed to parse boolean value in condition, ignoring: %s",
1987 rvalue);
d257ddef
LP
1988 return 0;
1989 }
1990
1991 if (!b)
1992 negate = !negate;
1993
74051b9b
LP
1994 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1995 if (!c)
1996 return log_oom();
d257ddef 1997
71fda00f 1998 LIST_PREPEND(conditions, u->conditions, c);
d257ddef
LP
1999 return 0;
2000}
2001
f975e971 2002DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
bf500566 2003DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier");
c952c6ec 2004
a57f7e2c
LP
2005int config_parse_unit_requires_mounts_for(
2006 const char *unit,
2007 const char *filename,
2008 unsigned line,
2009 const char *section,
71a61510 2010 unsigned section_line,
a57f7e2c
LP
2011 const char *lvalue,
2012 int ltype,
2013 const char *rvalue,
2014 void *data,
2015 void *userdata) {
7c8fa05c
LP
2016
2017 Unit *u = userdata;
a57f7e2c
LP
2018 char *state;
2019 size_t l;
2020 char *w;
7c8fa05c
LP
2021
2022 assert(filename);
2023 assert(lvalue);
2024 assert(rvalue);
2025 assert(data);
2026
a57f7e2c 2027 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
8a7935a2 2028 int r;
a57f7e2c
LP
2029 _cleanup_free_ char *n;
2030
2031 n = strndup(w, l);
2032 if (!n)
2033 return log_oom();
7c8fa05c 2034
a57f7e2c 2035 if (!utf8_is_valid(n)) {
b5d74213 2036 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
a57f7e2c
LP
2037 continue;
2038 }
7c8fa05c 2039
a57f7e2c
LP
2040 r = unit_require_mounts_for(u, n);
2041 if (r < 0) {
2042 log_syntax(unit, LOG_ERR, filename, line, r,
2043 "Failed to add required mount for, ignoring: %s", rvalue);
2044 continue;
2045 }
2046 }
7c8fa05c 2047
8a7935a2 2048 return 0;
7c8fa05c 2049}
9e372868 2050
e8e581bf
ZJS
2051int config_parse_documentation(const char *unit,
2052 const char *filename,
2053 unsigned line,
2054 const char *section,
71a61510 2055 unsigned section_line,
e8e581bf
ZJS
2056 const char *lvalue,
2057 int ltype,
2058 const char *rvalue,
2059 void *data,
2060 void *userdata) {
49dbfa7b
LP
2061
2062 Unit *u = userdata;
2063 int r;
2064 char **a, **b;
2065
2066 assert(filename);
2067 assert(lvalue);
2068 assert(rvalue);
2069 assert(u);
2070
74051b9b
LP
2071 if (isempty(rvalue)) {
2072 /* Empty assignment resets the list */
2073 strv_free(u->documentation);
2074 u->documentation = NULL;
2075 return 0;
2076 }
2077
71a61510 2078 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2079 rvalue, data, userdata);
49dbfa7b
LP
2080 if (r < 0)
2081 return r;
2082
2083 for (a = b = u->documentation; a && *a; a++) {
2084
2085 if (is_valid_documentation_url(*a))
2086 *(b++) = *a;
2087 else {
e8e581bf
ZJS
2088 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2089 "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2090 free(*a);
2091 }
2092 }
f6d2d421
ZJS
2093 if (b)
2094 *b = NULL;
49dbfa7b
LP
2095
2096 return r;
2097}
2098
c0467cf3 2099#ifdef HAVE_SECCOMP
17df7223
LP
2100int config_parse_syscall_filter(
2101 const char *unit,
2102 const char *filename,
2103 unsigned line,
2104 const char *section,
2105 unsigned section_line,
2106 const char *lvalue,
2107 int ltype,
2108 const char *rvalue,
2109 void *data,
2110 void *userdata) {
2111
2112 static const char default_syscalls[] =
2113 "execve\0"
2114 "exit\0"
2115 "exit_group\0"
2116 "rt_sigreturn\0"
2117 "sigreturn\0";
2118
8351ceae
LP
2119 ExecContext *c = data;
2120 Unit *u = userdata;
b5fb3789 2121 bool invert = false;
17df7223 2122 char *w, *state;
8351ceae 2123 size_t l;
17df7223 2124 int r;
8351ceae
LP
2125
2126 assert(filename);
2127 assert(lvalue);
2128 assert(rvalue);
2129 assert(u);
2130
74051b9b
LP
2131 if (isempty(rvalue)) {
2132 /* Empty assignment resets the list */
17df7223
LP
2133 set_free(c->syscall_filter);
2134 c->syscall_filter = NULL;
2135 c->syscall_whitelist = false;
74051b9b
LP
2136 return 0;
2137 }
2138
8351ceae
LP
2139 if (rvalue[0] == '~') {
2140 invert = true;
2141 rvalue++;
2142 }
2143
17df7223
LP
2144 if (!c->syscall_filter) {
2145 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
2146 if (!c->syscall_filter)
2147 return log_oom();
2148
c0467cf3 2149 if (invert)
17df7223
LP
2150 /* Allow everything but the ones listed */
2151 c->syscall_whitelist = false;
c0467cf3 2152 else {
17df7223
LP
2153 const char *i;
2154
2155 /* Allow nothing but the ones listed */
2156 c->syscall_whitelist = true;
8351ceae 2157
17df7223
LP
2158 /* Accept default syscalls if we are on a whitelist */
2159 NULSTR_FOREACH(i, default_syscalls) {
2160 int id;
8351ceae 2161
17df7223 2162 id = seccomp_syscall_resolve_name(i);
c0467cf3
RC
2163 if (id < 0)
2164 continue;
8351ceae 2165
17df7223
LP
2166 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2167 if (r == -EEXIST)
2168 continue;
2169 if (r < 0)
2170 return log_oom();
c0467cf3
RC
2171 }
2172 }
8351ceae
LP
2173 }
2174
2175 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 2176 _cleanup_free_ char *t = NULL;
17df7223 2177 int id;
8351ceae
LP
2178
2179 t = strndup(w, l);
2180 if (!t)
74051b9b 2181 return log_oom();
8351ceae 2182
c0467cf3 2183 id = seccomp_syscall_resolve_name(t);
8351ceae 2184 if (id < 0) {
17df7223 2185 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
8351ceae
LP
2186 continue;
2187 }
2188
17df7223
LP
2189 /* If we previously wanted to forbid a syscall and now
2190 * we want to allow it, then remove it from the list
c0467cf3 2191 */
17df7223
LP
2192 if (!invert == c->syscall_whitelist) {
2193 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2194 if (r == -EEXIST)
2195 continue;
2196 if (r < 0)
2197 return log_oom();
2198 } else
2199 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
c0467cf3
RC
2200 }
2201
760b9d7c
LP
2202 /* Turn on NNP, but only if it wasn't configured explicitly
2203 * before, and only if we are in user mode. */
2204 if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER)
2205 c->no_new_privileges = true;
17df7223
LP
2206
2207 return 0;
2208}
2209
57183d11
LP
2210int config_parse_syscall_archs(
2211 const char *unit,
2212 const char *filename,
2213 unsigned line,
2214 const char *section,
2215 unsigned section_line,
2216 const char *lvalue,
2217 int ltype,
2218 const char *rvalue,
2219 void *data,
2220 void *userdata) {
2221
d3b1c508 2222 Set **archs = data;
57183d11
LP
2223 char *w, *state;
2224 size_t l;
2225 int r;
2226
2227 if (isempty(rvalue)) {
d3b1c508
LP
2228 set_free(*archs);
2229 *archs = NULL;
57183d11
LP
2230 return 0;
2231 }
2232
d3b1c508 2233 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
57183d11
LP
2234 if (r < 0)
2235 return log_oom();
2236
2237 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2238 _cleanup_free_ char *t = NULL;
2239 uint32_t a;
2240
2241 t = strndup(w, l);
2242 if (!t)
2243 return log_oom();
2244
2245 r = seccomp_arch_from_string(t, &a);
2246 if (r < 0) {
2247 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2248 continue;
2249 }
2250
d3b1c508 2251 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2252 if (r == -EEXIST)
2253 continue;
2254 if (r < 0)
2255 return log_oom();
2256 }
2257
2258 return 0;
2259}
2260
17df7223
LP
2261int config_parse_syscall_errno(
2262 const char *unit,
2263 const char *filename,
2264 unsigned line,
2265 const char *section,
2266 unsigned section_line,
2267 const char *lvalue,
2268 int ltype,
2269 const char *rvalue,
2270 void *data,
2271 void *userdata) {
2272
2273 ExecContext *c = data;
2274 int e;
2275
2276 assert(filename);
2277 assert(lvalue);
2278 assert(rvalue);
2279
2280 if (isempty(rvalue)) {
2281 /* Empty assignment resets to KILL */
2282 c->syscall_errno = 0;
2283 return 0;
8351ceae
LP
2284 }
2285
17df7223
LP
2286 e = errno_from_name(rvalue);
2287 if (e < 0) {
2288 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2289 return 0;
2290 }
8351ceae 2291
17df7223 2292 c->syscall_errno = e;
8351ceae
LP
2293 return 0;
2294}
4298d0b5
LP
2295
2296int config_parse_address_families(
2297 const char *unit,
2298 const char *filename,
2299 unsigned line,
2300 const char *section,
2301 unsigned section_line,
2302 const char *lvalue,
2303 int ltype,
2304 const char *rvalue,
2305 void *data,
2306 void *userdata) {
2307
2308 ExecContext *c = data;
2309 Unit *u = userdata;
2310 bool invert = false;
2311 char *w, *state;
2312 size_t l;
2313 int r;
2314
2315 assert(filename);
2316 assert(lvalue);
2317 assert(rvalue);
2318 assert(u);
2319
2320 if (isempty(rvalue)) {
2321 /* Empty assignment resets the list */
2322 set_free(c->address_families);
2323 c->address_families = NULL;
2324 c->address_families_whitelist = false;
2325 return 0;
2326 }
2327
2328 if (rvalue[0] == '~') {
2329 invert = true;
2330 rvalue++;
2331 }
2332
2333 if (!c->address_families) {
2334 c->address_families = set_new(trivial_hash_func, trivial_compare_func);
2335 if (!c->address_families)
2336 return log_oom();
2337
2338 c->address_families_whitelist = !invert;
2339 }
2340
2341 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2342 _cleanup_free_ char *t = NULL;
2343 int af;
2344
2345 t = strndup(w, l);
2346 if (!t)
2347 return log_oom();
2348
2349 af = af_from_name(t);
2350 if (af <= 0) {
2351 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse address family, ignoring: %s", t);
2352 continue;
2353 }
2354
2355 /* If we previously wanted to forbid an address family and now
2356 * we want to allow it, then remove it from the list
2357 */
2358 if (!invert == c->address_families_whitelist) {
2359 r = set_put(c->address_families, INT_TO_PTR(af));
2360 if (r == -EEXIST)
2361 continue;
2362 if (r < 0)
2363 return log_oom();
2364 } else
2365 set_remove(c->address_families, INT_TO_PTR(af));
2366 }
2367
2368 return 0;
2369}
c0467cf3 2370#endif
8351ceae 2371
a016b922
LP
2372int config_parse_unit_slice(
2373 const char *unit,
2374 const char *filename,
2375 unsigned line,
2376 const char *section,
71a61510 2377 unsigned section_line,
a016b922
LP
2378 const char *lvalue,
2379 int ltype,
2380 const char *rvalue,
2381 void *data,
2382 void *userdata) {
2383
2384 _cleanup_free_ char *k = NULL;
2385 Unit *u = userdata, *slice;
2386 int r;
2387
2388 assert(filename);
2389 assert(lvalue);
2390 assert(rvalue);
2391 assert(u);
2392
19f6d710
LP
2393 r = unit_name_printf(u, rvalue, &k);
2394 if (r < 0)
2395 log_syntax(unit, LOG_ERR, filename, line, -r,
a016b922 2396 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
19f6d710
LP
2397 if (!k) {
2398 k = strdup(rvalue);
2399 if (!k)
2400 return log_oom();
2401 }
a016b922 2402
19f6d710 2403 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922
LP
2404 if (r < 0) {
2405 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 2406 "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
2407 return 0;
2408 }
2409
2410 if (slice->type != UNIT_SLICE) {
2411 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 2412 "Slice unit %s is not a slice. Ignoring.", k);
a016b922
LP
2413 return 0;
2414 }
2415
2416 unit_ref_set(&u->slice, slice);
2417 return 0;
2418}
2419
4ad49000
LP
2420DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2421
2422int config_parse_cpu_shares(
2423 const char *unit,
2424 const char *filename,
2425 unsigned line,
2426 const char *section,
71a61510 2427 unsigned section_line,
4ad49000
LP
2428 const char *lvalue,
2429 int ltype,
2430 const char *rvalue,
2431 void *data,
2432 void *userdata) {
2433
2434 CGroupContext *c = data;
2435 unsigned long lu;
2436 int r;
2437
2438 assert(filename);
2439 assert(lvalue);
2440 assert(rvalue);
2441
2442 if (isempty(rvalue)) {
2443 c->cpu_shares = 1024;
2444 return 0;
2445 }
2446
2447 r = safe_atolu(rvalue, &lu);
2448 if (r < 0 || lu <= 0) {
2449 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2450 "CPU shares '%s' invalid. Ignoring.", rvalue);
2451 return 0;
2452 }
2453
2454 c->cpu_shares = lu;
2455 return 0;
2456}
2457
2458int config_parse_memory_limit(
2459 const char *unit,
2460 const char *filename,
2461 unsigned line,
2462 const char *section,
71a61510 2463 unsigned section_line,
4ad49000
LP
2464 const char *lvalue,
2465 int ltype,
2466 const char *rvalue,
2467 void *data,
2468 void *userdata) {
2469
2470 CGroupContext *c = data;
4ad49000
LP
2471 off_t bytes;
2472 int r;
2473
4ad49000 2474 if (isempty(rvalue)) {
ddca82ac 2475 c->memory_limit = (uint64_t) -1;
4ad49000
LP
2476 return 0;
2477 }
2478
2479 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2480
5556b5fe 2481 r = parse_size(rvalue, 1024, &bytes);
4ad49000 2482 if (r < 0) {
5556b5fe 2483 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2484 return 0;
2485 }
2486
ddca82ac 2487 c->memory_limit = (uint64_t) bytes;
4ad49000
LP
2488 return 0;
2489}
2490
2491int config_parse_device_allow(
2492 const char *unit,
2493 const char *filename,
2494 unsigned line,
2495 const char *section,
71a61510 2496 unsigned section_line,
4ad49000
LP
2497 const char *lvalue,
2498 int ltype,
2499 const char *rvalue,
2500 void *data,
2501 void *userdata) {
2502
2503 _cleanup_free_ char *path = NULL;
2504 CGroupContext *c = data;
2505 CGroupDeviceAllow *a;
2506 const char *m;
2507 size_t n;
2508
2509 if (isempty(rvalue)) {
2510 while (c->device_allow)
2511 cgroup_context_free_device_allow(c, c->device_allow);
2512
2513 return 0;
2514 }
2515
2516 n = strcspn(rvalue, WHITESPACE);
2517 path = strndup(rvalue, n);
2518 if (!path)
2519 return log_oom();
2520
90060676
LP
2521 if (!startswith(path, "/dev/") &&
2522 !startswith(path, "block-") &&
2523 !startswith(path, "char-")) {
2524 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
2525 return 0;
2526 }
2527
2528 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2529 if (isempty(m))
2530 m = "rwm";
2531
2532 if (!in_charset(m, "rwm")) {
90060676 2533 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
2534 return 0;
2535 }
2536
2537 a = new0(CGroupDeviceAllow, 1);
2538 if (!a)
2539 return log_oom();
2540
2541 a->path = path;
2542 path = NULL;
2543 a->r = !!strchr(m, 'r');
2544 a->w = !!strchr(m, 'w');
2545 a->m = !!strchr(m, 'm');
2546
71fda00f 2547 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
2548 return 0;
2549}
2550
2551int config_parse_blockio_weight(
2552 const char *unit,
2553 const char *filename,
2554 unsigned line,
2555 const char *section,
71a61510 2556 unsigned section_line,
4ad49000
LP
2557 const char *lvalue,
2558 int ltype,
2559 const char *rvalue,
2560 void *data,
2561 void *userdata) {
2562
8e7076ca
LP
2563 CGroupContext *c = data;
2564 unsigned long lu;
2565 int r;
2566
2567 assert(filename);
2568 assert(lvalue);
2569 assert(rvalue);
2570
2571 if (isempty(rvalue)) {
2572 c->blockio_weight = 1000;
2573 return 0;
2574 }
2575
2576 r = safe_atolu(rvalue, &lu);
2577 if (r < 0 || lu < 10 || lu > 1000) {
2578 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2579 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2580 return 0;
2581 }
2582
2583 c->blockio_weight = lu;
2584
2585 return 0;
2586}
2587
2588int config_parse_blockio_device_weight(
2589 const char *unit,
2590 const char *filename,
2591 unsigned line,
2592 const char *section,
71a61510 2593 unsigned section_line,
8e7076ca
LP
2594 const char *lvalue,
2595 int ltype,
2596 const char *rvalue,
2597 void *data,
2598 void *userdata) {
2599
4ad49000 2600 _cleanup_free_ char *path = NULL;
8e7076ca 2601 CGroupBlockIODeviceWeight *w;
4ad49000
LP
2602 CGroupContext *c = data;
2603 unsigned long lu;
2604 const char *weight;
2605 size_t n;
2606 int r;
2607
2608 assert(filename);
2609 assert(lvalue);
2610 assert(rvalue);
2611
2612 if (isempty(rvalue)) {
4ad49000
LP
2613 while (c->blockio_device_weights)
2614 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2615
2616 return 0;
2617 }
2618
2619 n = strcspn(rvalue, WHITESPACE);
2620 weight = rvalue + n;
8e7076ca
LP
2621 if (!*weight) {
2622 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2623 "Expected block device and device weight. Ignoring.");
2624 return 0;
2625 }
4ad49000 2626
8e7076ca
LP
2627 path = strndup(rvalue, n);
2628 if (!path)
2629 return log_oom();
4ad49000 2630
8e7076ca
LP
2631 if (!path_startswith(path, "/dev")) {
2632 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2633 "Invalid device node path '%s'. Ignoring.", path);
2634 return 0;
2635 }
4ad49000 2636
8e7076ca 2637 weight += strspn(weight, WHITESPACE);
4ad49000
LP
2638 r = safe_atolu(weight, &lu);
2639 if (r < 0 || lu < 10 || lu > 1000) {
2640 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2641 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2642 return 0;
2643 }
2644
4ad49000 2645
8e7076ca
LP
2646 w = new0(CGroupBlockIODeviceWeight, 1);
2647 if (!w)
2648 return log_oom();
4ad49000 2649
8e7076ca
LP
2650 w->path = path;
2651 path = NULL;
4ad49000 2652
8e7076ca 2653 w->weight = lu;
4ad49000 2654
71fda00f 2655 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
2656 return 0;
2657}
2658
2659int config_parse_blockio_bandwidth(
2660 const char *unit,
2661 const char *filename,
2662 unsigned line,
2663 const char *section,
71a61510 2664 unsigned section_line,
4ad49000
LP
2665 const char *lvalue,
2666 int ltype,
2667 const char *rvalue,
2668 void *data,
2669 void *userdata) {
2670
2671 _cleanup_free_ char *path = NULL;
2672 CGroupBlockIODeviceBandwidth *b;
2673 CGroupContext *c = data;
2674 const char *bandwidth;
2675 off_t bytes;
47c0980d 2676 bool read;
4ad49000
LP
2677 size_t n;
2678 int r;
2679
2680 assert(filename);
2681 assert(lvalue);
2682 assert(rvalue);
2683
47c0980d
G
2684 read = streq("BlockIOReadBandwidth", lvalue);
2685
4ad49000 2686 if (isempty(rvalue)) {
47c0980d
G
2687 CGroupBlockIODeviceBandwidth *next;
2688
2689 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2690 if (b->read == read)
2691 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
2692
2693 return 0;
2694 }
2695
2696 n = strcspn(rvalue, WHITESPACE);
2697 bandwidth = rvalue + n;
2698 bandwidth += strspn(bandwidth, WHITESPACE);
2699
2700 if (!*bandwidth) {
2701 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2702 "Expected space separated pair of device node and bandwidth. Ignoring.");
2703 return 0;
2704 }
2705
2706 path = strndup(rvalue, n);
2707 if (!path)
2708 return log_oom();
2709
2710 if (!path_startswith(path, "/dev")) {
2711 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2712 "Invalid device node path '%s'. Ignoring.", path);
2713 return 0;
2714 }
2715
5556b5fe 2716 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 2717 if (r < 0 || bytes <= 0) {
5556b5fe 2718 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2719 return 0;
2720 }
2721
2722 b = new0(CGroupBlockIODeviceBandwidth, 1);
2723 if (!b)
2724 return log_oom();
2725
2726 b->path = path;
2727 path = NULL;
2728 b->bandwidth = (uint64_t) bytes;
47c0980d 2729 b->read = read;
4ad49000 2730
71fda00f 2731 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
2732
2733 return 0;
2734}
2735
d420282b
LP
2736DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2737
2738int config_parse_job_mode_isolate(
2739 const char *unit,
2740 const char *filename,
2741 unsigned line,
2742 const char *section,
2743 unsigned section_line,
2744 const char *lvalue,
2745 int ltype,
2746 const char *rvalue,
2747 void *data,
2748 void *userdata) {
2749
2750 JobMode *m = data;
2751 int r;
2752
2753 assert(filename);
2754 assert(lvalue);
2755 assert(rvalue);
2756
2757 r = parse_boolean(rvalue);
2758 if (r < 0) {
2759 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2760 return 0;
2761 }
2762
2763 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2764 return 0;
2765}
2766
ac45f971
LP
2767int config_parse_personality(
2768 const char *unit,
2769 const char *filename,
2770 unsigned line,
2771 const char *section,
2772 unsigned section_line,
2773 const char *lvalue,
2774 int ltype,
2775 const char *rvalue,
2776 void *data,
2777 void *userdata) {
2778
2779 unsigned long *personality = data, p;
2780
2781 assert(filename);
2782 assert(lvalue);
2783 assert(rvalue);
2784 assert(personality);
2785
2786 p = personality_from_string(rvalue);
2787 if (p == 0xffffffffUL) {
2788 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2789 "Failed to parse personality, ignoring: %s", rvalue);
2790 return 0;
2791 }
2792
2793 *personality = p;
2794 return 0;
2795}
2796
e66cf1a3
LP
2797int config_parse_runtime_directory(
2798 const char *unit,
2799 const char *filename,
2800 unsigned line,
2801 const char *section,
2802 unsigned section_line,
2803 const char *lvalue,
2804 int ltype,
2805 const char *rvalue,
2806 void *data,
2807 void *userdata) {
2808
2809 char***rt = data, *w, *state;
2810 size_t l;
2811 int r;
2812
2813 assert(filename);
2814 assert(lvalue);
2815 assert(rvalue);
2816 assert(data);
2817
2818 if (isempty(rvalue)) {
2819 /* Empty assignment resets the list */
2820 strv_free(*rt);
2821 *rt = NULL;
2822 return 0;
2823 }
2824
2825 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2826 _cleanup_free_ char *n;
2827
2828 n = strndup(w, l);
2829 if (!n)
2830 return log_oom();
2831
2832 if (!filename_is_safe(n)) {
2833 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
2834 continue;
2835 }
2836
2837 r = strv_push(rt, n);
2838 if (r < 0)
2839 return log_oom();
2840
2841 n = NULL;
2842 }
2843
2844 return 0;
2845}
2846
3af00fb8
LP
2847int config_parse_set_status(
2848 const char *unit,
2849 const char *filename,
2850 unsigned line,
2851 const char *section,
2852 unsigned section_line,
2853 const char *lvalue,
2854 int ltype,
2855 const char *rvalue,
2856 void *data,
2857 void *userdata) {
2858
2859 char *w;
2860 size_t l;
2861 char *state;
2862 int r;
2863 ExitStatusSet *status_set = data;
2864
2865 assert(filename);
2866 assert(lvalue);
2867 assert(rvalue);
2868 assert(data);
2869
2870 if (isempty(rvalue)) {
2871 /* Empty assignment resets the list */
2872
2873 set_free(status_set->signal);
2874 set_free(status_set->code);
2875
2876 status_set->signal = status_set->code = NULL;
2877 return 0;
2878 }
2879
2880 FOREACH_WORD(w, l, rvalue, state) {
2881 _cleanup_free_ char *temp;
2882 int val;
2883
2884 temp = strndup(w, l);
2885 if (!temp)
2886 return log_oom();
2887
2888 r = safe_atoi(temp, &val);
2889 if (r < 0) {
2890 val = signal_from_string_try_harder(temp);
2891
2892 if (val > 0) {
2893 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
2894 if (r < 0)
2895 return log_oom();
2896
2897 r = set_put(status_set->signal, INT_TO_PTR(val));
2898 if (r < 0) {
2899 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2900 return r;
2901 }
2902 } else {
2903 log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w);
2904 return 0;
2905 }
2906 } else {
2907 if (val < 0 || val > 255)
2908 log_syntax(unit, LOG_ERR, filename, line, ERANGE, "Value %d is outside range 0-255, ignoring", val);
2909 else {
2910 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
2911 if (r < 0)
2912 return log_oom();
2913
2914 r = set_put(status_set->code, INT_TO_PTR(val));
2915 if (r < 0) {
2916 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2917 return r;
2918 }
2919 }
2920 }
2921 }
2922
2923 return 0;
2924}
2925
94828d2d
LP
2926int config_parse_namespace_path_strv(
2927 const char *unit,
2928 const char *filename,
2929 unsigned line,
2930 const char *section,
2931 unsigned section_line,
2932 const char *lvalue,
2933 int ltype,
2934 const char *rvalue,
2935 void *data,
2936 void *userdata) {
2937
2938 char*** sv = data, *w, *state;
2939 size_t l;
2940 int r;
2941
2942 assert(filename);
2943 assert(lvalue);
2944 assert(rvalue);
2945 assert(data);
2946
2947 if (isempty(rvalue)) {
2948 /* Empty assignment resets the list */
2949 strv_free(*sv);
2950 *sv = NULL;
2951 return 0;
2952 }
2953
2954 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2955 _cleanup_free_ char *n;
2956 int offset;
2957
2958 n = strndup(w, l);
2959 if (!n)
2960 return log_oom();
2961
2962 if (!utf8_is_valid(n)) {
b5d74213 2963 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
94828d2d
LP
2964 continue;
2965 }
2966
2967 offset = n[0] == '-';
2968 if (!path_is_absolute(n + offset)) {
2969 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
2970 continue;
2971 }
2972
2973 path_kill_slashes(n);
2974
2975 r = strv_push(sv, n);
2976 if (r < 0)
2977 return log_oom();
2978
2979 n = NULL;
2980 }
2981
2982 return 0;
2983}
2984
760b9d7c
LP
2985int config_parse_no_new_priviliges(
2986 const char* unit,
2987 const char *filename,
2988 unsigned line,
2989 const char *section,
2990 unsigned section_line,
2991 const char *lvalue,
2992 int ltype,
2993 const char *rvalue,
2994 void *data,
2995 void *userdata) {
2996
2997 ExecContext *c = data;
2998 int k;
2999
3000 assert(filename);
3001 assert(lvalue);
3002 assert(rvalue);
3003 assert(data);
3004
3005 k = parse_boolean(rvalue);
3006 if (k < 0) {
3007 log_syntax(unit, LOG_ERR, filename, line, -k, "Failed to parse boolean value, ignoring: %s", rvalue);
3008 return 0;
3009 }
3010
3011 c->no_new_privileges = !!k;
3012 c->no_new_privileges_set = true;
3013
3014 return 0;
3015}
3016
071830ff 3017#define FOLLOW_MAX 8
87f0e418 3018
9e2f7c11 3019static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 3020 unsigned c = 0;
87f0e418
LP
3021 int fd, r;
3022 FILE *f;
0301abf4 3023 char *id = NULL;
87f0e418
LP
3024
3025 assert(filename);
3026 assert(*filename);
3027 assert(_f);
3028 assert(names);
3029
0301abf4
LP
3030 /* This will update the filename pointer if the loaded file is
3031 * reached by a symlink. The old string will be freed. */
87f0e418 3032
0301abf4 3033 for (;;) {
2c7108c4 3034 char *target, *name;
87f0e418 3035
0301abf4
LP
3036 if (c++ >= FOLLOW_MAX)
3037 return -ELOOP;
3038
b08d03ff
LP
3039 path_kill_slashes(*filename);
3040
87f0e418 3041 /* Add the file name we are currently looking at to
8f05424d
LP
3042 * the names of this unit, but only if it is a valid
3043 * unit name. */
2b6bf07d 3044 name = basename(*filename);
87f0e418 3045
f78e6385 3046 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
8f05424d 3047
15e11d81
LP
3048 id = set_get(names, name);
3049 if (!id) {
3050 id = strdup(name);
3051 if (!id)
8f05424d 3052 return -ENOMEM;
87f0e418 3053
ef42202a
ZJS
3054 r = set_consume(names, id);
3055 if (r < 0)
8f05424d 3056 return r;
87f0e418 3057 }
87f0e418
LP
3058 }
3059
0301abf4 3060 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
3061 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3062 if (fd >= 0)
87f0e418
LP
3063 break;
3064
0301abf4
LP
3065 if (errno != ELOOP)
3066 return -errno;
3067
87f0e418 3068 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
3069 r = readlink_and_make_absolute(*filename, &target);
3070 if (r < 0)
0301abf4 3071 return r;
87f0e418 3072
0301abf4 3073 free(*filename);
2c7108c4 3074 *filename = target;
87f0e418
LP
3075 }
3076
9946996c
LP
3077 f = fdopen(fd, "re");
3078 if (!f) {
87f0e418 3079 r = -errno;
03e334a1 3080 safe_close(fd);
0301abf4 3081 return r;
87f0e418
LP
3082 }
3083
3084 *_f = f;
9e2f7c11 3085 *_final = id;
0301abf4 3086 return 0;
87f0e418
LP
3087}
3088
23a177ef
LP
3089static int merge_by_names(Unit **u, Set *names, const char *id) {
3090 char *k;
3091 int r;
3092
3093 assert(u);
3094 assert(*u);
3095 assert(names);
3096
3097 /* Let's try to add in all symlink names we found */
3098 while ((k = set_steal_first(names))) {
3099
3100 /* First try to merge in the other name into our
3101 * unit */
9946996c
LP
3102 r = unit_merge_by_name(*u, k);
3103 if (r < 0) {
23a177ef
LP
3104 Unit *other;
3105
3106 /* Hmm, we couldn't merge the other unit into
3107 * ours? Then let's try it the other way
3108 * round */
3109
ac155bb8 3110 other = manager_get_unit((*u)->manager, k);
23a177ef
LP
3111 free(k);
3112
9946996c
LP
3113 if (other) {
3114 r = unit_merge(other, *u);
3115 if (r >= 0) {
23a177ef
LP
3116 *u = other;
3117 return merge_by_names(u, names, NULL);
3118 }
9946996c 3119 }
23a177ef
LP
3120
3121 return r;
3122 }
3123
3124 if (id == k)
3125 unit_choose_id(*u, id);
3126
3127 free(k);
3128 }
3129
3130 return 0;
3131}
3132
e537352b 3133static int load_from_path(Unit *u, const char *path) {
0301abf4 3134 int r;
e48614c4
ZJS
3135 _cleanup_set_free_free_ Set *symlink_names = NULL;
3136 _cleanup_fclose_ FILE *f = NULL;
3137 _cleanup_free_ char *filename = NULL;
3138 char *id = NULL;
23a177ef 3139 Unit *merged;
45fb0699 3140 struct stat st;
23a177ef
LP
3141
3142 assert(u);
e537352b 3143 assert(path);
3efd4195 3144
f975e971
LP
3145 symlink_names = set_new(string_hash_func, string_compare_func);
3146 if (!symlink_names)
87f0e418 3147 return -ENOMEM;
3efd4195 3148
036643a2
LP
3149 if (path_is_absolute(path)) {
3150
9946996c 3151 filename = strdup(path);
e48614c4
ZJS
3152 if (!filename)
3153 return -ENOMEM;
036643a2 3154
9946996c
LP
3155 r = open_follow(&filename, &f, symlink_names, &id);
3156 if (r < 0) {
036643a2
LP
3157 free(filename);
3158 filename = NULL;
3159
3160 if (r != -ENOENT)
e48614c4 3161 return r;
036643a2
LP
3162 }
3163
3164 } else {
3165 char **p;
3166
ac155bb8 3167 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
036643a2
LP
3168
3169 /* Instead of opening the path right away, we manually
3170 * follow all symlinks and add their name to our unit
3171 * name set while doing so */
9946996c 3172 filename = path_make_absolute(path, *p);
e48614c4
ZJS
3173 if (!filename)
3174 return -ENOMEM;
036643a2 3175
ac155bb8
MS
3176 if (u->manager->unit_path_cache &&
3177 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
3178 r = -ENOENT;
3179 else
3180 r = open_follow(&filename, &f, symlink_names, &id);
3181
3182 if (r < 0) {
036643a2
LP
3183 free(filename);
3184 filename = NULL;
3185
3186 if (r != -ENOENT)
e48614c4 3187 return r;
036643a2
LP
3188
3189 /* Empty the symlink names for the next run */
9946996c 3190 set_clear_free(symlink_names);
036643a2
LP
3191 continue;
3192 }
3193
3194 break;
3195 }
3196 }
034c6ed7 3197
e48614c4 3198 if (!filename)
8f05424d 3199 /* Hmm, no suitable file found? */
e48614c4 3200 return 0;
87f0e418 3201
23a177ef 3202 merged = u;
9946996c
LP
3203 r = merge_by_names(&merged, symlink_names, id);
3204 if (r < 0)
e48614c4 3205 return r;
87f0e418 3206
23a177ef 3207 if (merged != u) {
ac155bb8 3208 u->load_state = UNIT_MERGED;
e48614c4 3209 return 0;
034c6ed7
LP
3210 }
3211
e48614c4
ZJS
3212 if (fstat(fileno(f), &st) < 0)
3213 return -errno;
45fb0699 3214
00dc5d76 3215 if (null_or_empty(&st))
ac155bb8 3216 u->load_state = UNIT_MASKED;
00dc5d76 3217 else {
c2756a68
LP
3218 u->load_state = UNIT_LOADED;
3219
00dc5d76 3220 /* Now, parse the file contents */
e8e581bf
ZJS
3221 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
3222 config_item_perf_lookup,
db5c0122 3223 (void*) load_fragment_gperf_lookup, false, true, u);
f975e971 3224 if (r < 0)
e48614c4 3225 return r;
00dc5d76 3226 }
b08d03ff 3227
ac155bb8
MS
3228 free(u->fragment_path);
3229 u->fragment_path = filename;
0301abf4 3230 filename = NULL;
87f0e418 3231
ac155bb8 3232 u->fragment_mtime = timespec_load(&st.st_mtim);
45fb0699 3233
1b64d026
LP
3234 if (u->source_path) {
3235 if (stat(u->source_path, &st) >= 0)
3236 u->source_mtime = timespec_load(&st.st_mtim);
3237 else
3238 u->source_mtime = 0;
3239 }
3240
e48614c4 3241 return 0;
0301abf4
LP
3242}
3243
e537352b 3244int unit_load_fragment(Unit *u) {
23a177ef 3245 int r;
294d81f1
LP
3246 Iterator i;
3247 const char *t;
0301abf4
LP
3248
3249 assert(u);
ac155bb8
MS
3250 assert(u->load_state == UNIT_STUB);
3251 assert(u->id);
23a177ef 3252
294d81f1
LP
3253 /* First, try to find the unit under its id. We always look
3254 * for unit files in the default directories, to make it easy
3255 * to override things by placing things in /etc/systemd/system */
9946996c
LP
3256 r = load_from_path(u, u->id);
3257 if (r < 0)
294d81f1
LP
3258 return r;
3259
3260 /* Try to find an alias we can load this with */
ac155bb8
MS
3261 if (u->load_state == UNIT_STUB)
3262 SET_FOREACH(t, u->names, i) {
294d81f1 3263
ac155bb8 3264 if (t == u->id)
294d81f1
LP
3265 continue;
3266
9946996c
LP
3267 r = load_from_path(u, t);
3268 if (r < 0)
294d81f1
LP
3269 return r;
3270
ac155bb8 3271 if (u->load_state != UNIT_STUB)
294d81f1
LP
3272 break;
3273 }
23a177ef 3274
294d81f1 3275 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 3276 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 3277
9946996c
LP
3278 r = load_from_path(u, u->fragment_path);
3279 if (r < 0)
23a177ef 3280 return r;
0301abf4 3281
ac155bb8 3282 if (u->load_state == UNIT_STUB) {
6ccb1b44
LP
3283 /* Hmm, this didn't work? Then let's get rid
3284 * of the fragment path stored for us, so that
3285 * we don't point to an invalid location. */
ac155bb8
MS
3286 free(u->fragment_path);
3287 u->fragment_path = NULL;
6ccb1b44
LP
3288 }
3289 }
3290
294d81f1 3291 /* Look for a template */
ac155bb8 3292 if (u->load_state == UNIT_STUB && u->instance) {
bc9fd78c 3293 _cleanup_free_ char *k;
294d81f1 3294
9946996c
LP
3295 k = unit_name_template(u->id);
3296 if (!k)
294d81f1
LP
3297 return -ENOMEM;
3298
3299 r = load_from_path(u, k);
294d81f1 3300 if (r < 0)
9e2f7c11 3301 return r;
890f434c 3302
ac155bb8
MS
3303 if (u->load_state == UNIT_STUB)
3304 SET_FOREACH(t, u->names, i) {
bc9fd78c 3305 _cleanup_free_ char *z = NULL;
87f0e418 3306
ac155bb8 3307 if (t == u->id)
23a177ef 3308 continue;
071830ff 3309
bc9fd78c
LP
3310 z = unit_name_template(t);
3311 if (!z)
294d81f1
LP
3312 return -ENOMEM;
3313
bc9fd78c 3314 r = load_from_path(u, z);
294d81f1 3315 if (r < 0)
23a177ef 3316 return r;
890f434c 3317
ac155bb8 3318 if (u->load_state != UNIT_STUB)
23a177ef
LP
3319 break;
3320 }
071830ff
LP
3321 }
3322
23a177ef 3323 return 0;
3efd4195 3324}
e537352b
LP
3325
3326void unit_dump_config_items(FILE *f) {
f975e971
LP
3327 static const struct {
3328 const ConfigParserCallback callback;
3329 const char *rvalue;
3330 } table[] = {
7f8aa671 3331#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
3332 { config_parse_warn_compat, "NOTSUPPORTED" },
3333#endif
f975e971
LP
3334 { config_parse_int, "INTEGER" },
3335 { config_parse_unsigned, "UNSIGNED" },
5556b5fe
LP
3336 { config_parse_iec_size, "SIZE" },
3337 { config_parse_iec_off, "SIZE" },
3338 { config_parse_si_size, "SIZE" },
f975e971
LP
3339 { config_parse_bool, "BOOLEAN" },
3340 { config_parse_string, "STRING" },
3341 { config_parse_path, "PATH" },
3342 { config_parse_unit_path_printf, "PATH" },
3343 { config_parse_strv, "STRING [...]" },
3344 { config_parse_exec_nice, "NICE" },
3345 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3346 { config_parse_exec_io_class, "IOCLASS" },
3347 { config_parse_exec_io_priority, "IOPRIORITY" },
3348 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3349 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3350 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3351 { config_parse_mode, "MODE" },
3352 { config_parse_unit_env_file, "FILE" },
3353 { config_parse_output, "OUTPUT" },
3354 { config_parse_input, "INPUT" },
ca37242e
LP
3355 { config_parse_log_facility, "FACILITY" },
3356 { config_parse_log_level, "LEVEL" },
f975e971
LP
3357 { config_parse_exec_capabilities, "CAPABILITIES" },
3358 { config_parse_exec_secure_bits, "SECUREBITS" },
ec8927ca 3359 { config_parse_bounding_set, "BOUNDINGSET" },
f975e971 3360 { config_parse_limit, "LIMIT" },
f975e971 3361 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
3362 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3363 { config_parse_service_type, "SERVICETYPE" },
3364 { config_parse_service_restart, "SERVICERESTART" },
3365#ifdef HAVE_SYSV_COMPAT
3366 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
3367#endif
3368 { config_parse_kill_mode, "KILLMODE" },
3369 { config_parse_kill_signal, "SIGNAL" },
3370 { config_parse_socket_listen, "SOCKET [...]" },
3371 { config_parse_socket_bind, "SOCKETBIND" },
3372 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 3373 { config_parse_sec, "SECONDS" },
d88a251b 3374 { config_parse_nsec, "NANOSECONDS" },
94828d2d 3375 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 3376 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
3377 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3378 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 3379 { config_parse_trigger_unit, "UNIT" },
f975e971 3380 { config_parse_timer, "TIMER" },
f975e971 3381 { config_parse_path_spec, "PATH" },
f975e971
LP
3382 { config_parse_notify_access, "ACCESS" },
3383 { config_parse_ip_tos, "TOS" },
3384 { config_parse_unit_condition_path, "CONDITION" },
3385 { config_parse_unit_condition_string, "CONDITION" },
3386 { config_parse_unit_condition_null, "CONDITION" },
a016b922 3387 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
3388 { config_parse_documentation, "URL" },
3389 { config_parse_service_timeout, "SECONDS" },
bf500566 3390 { config_parse_failure_action, "ACTION" },
7f0386f6
LP
3391 { config_parse_set_status, "STATUS" },
3392 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 3393 { config_parse_environ, "ENVIRON" },
c0467cf3 3394#ifdef HAVE_SECCOMP
17df7223 3395 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 3396 { config_parse_syscall_archs, "ARCHS" },
17df7223 3397 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 3398 { config_parse_address_families, "FAMILIES" },
c0467cf3 3399#endif
7f0386f6
LP
3400 { config_parse_cpu_shares, "SHARES" },
3401 { config_parse_memory_limit, "LIMIT" },
3402 { config_parse_device_allow, "DEVICE" },
3403 { config_parse_device_policy, "POLICY" },
3404 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3405 { config_parse_blockio_weight, "WEIGHT" },
3406 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3407 { config_parse_long, "LONG" },
3408 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
3409#ifdef HAVE_SELINUX
3410 { config_parse_exec_selinux_context, "LABEL" },
3411#endif
3412 { config_parse_job_mode, "MODE" },
3413 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 3414 { config_parse_personality, "PERSONALITY" },
f975e971
LP
3415 };
3416
3417 const char *prev = NULL;
3418 const char *i;
3419
3420 assert(f);
e537352b 3421
f975e971
LP
3422 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3423 const char *rvalue = "OTHER", *lvalue;
3424 unsigned j;
3425 size_t prefix_len;
3426 const char *dot;
3427 const ConfigPerfItem *p;
3428
3429 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3430
3431 dot = strchr(i, '.');
3432 lvalue = dot ? dot + 1 : i;
3433 prefix_len = dot-i;
3434
3435 if (dot)
641906e9 3436 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
3437 if (prev)
3438 fputc('\n', f);
3439
3440 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3441 }
3442
3443 for (j = 0; j < ELEMENTSOF(table); j++)
3444 if (p->parse == table[j].callback) {
3445 rvalue = table[j].rvalue;
3446 break;
3447 }
3448
3449 fprintf(f, "%s=%s\n", lvalue, rvalue);
3450 prev = i;
3451 }
e537352b 3452}