]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
NEWS: add some clarifications
[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>
3efd4195 36
e8e581bf
ZJS
37#include <systemd/sd-messages.h>
38
87f0e418 39#include "unit.h"
3efd4195
LP
40#include "strv.h"
41#include "conf-parser.h"
42#include "load-fragment.h"
16354eff 43#include "log.h"
9eba9da4 44#include "ioprio.h"
94f04347
LP
45#include "securebits.h"
46#include "missing.h"
9e2f7c11 47#include "unit-name.h"
41f9172f 48#include "unit-printf.h"
449101fc 49#include "dbus-common.h"
7f110ff9 50#include "utf8.h"
9eb977db 51#include "path-util.h"
8351ceae 52#include "syscall-list.h"
853b8397 53#include "env-util.h"
4ad49000 54#include "cgroup.h"
3efd4195 55
07459bb6 56#ifndef HAVE_SYSV_COMPAT
e8e581bf
ZJS
57int config_parse_warn_compat(const char *unit,
58 const char *filename,
59 unsigned line,
60 const char *section,
61 const char *lvalue,
62 int ltype,
63 const char *rvalue,
64 void *data,
65 void *userdata) {
66
67 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
68 "Support for option %s= has been disabled at compile time and is ignored",
69 lvalue);
07459bb6
FF
70 return 0;
71}
72#endif
73
e8e581bf
ZJS
74int config_parse_unit_deps(const char* unit,
75 const char *filename,
76 unsigned line,
77 const char *section,
78 const char *lvalue,
79 int ltype,
80 const char *rvalue,
81 void *data,
82 void *userdata) {
3efd4195 83
f975e971 84 UnitDependency d = ltype;
87f0e418 85 Unit *u = userdata;
3efd4195
LP
86 char *w;
87 size_t l;
88 char *state;
89
90 assert(filename);
91 assert(lvalue);
92 assert(rvalue);
3efd4195 93
f60f22df 94 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 95 _cleanup_free_ char *t = NULL, *k = NULL;
3efd4195 96 int r;
3efd4195 97
57020a3a
LP
98 t = strndup(w, l);
99 if (!t)
74051b9b 100 return log_oom();
3efd4195 101
19f6d710
LP
102 r = unit_name_printf(u, t, &k);
103 if (r < 0) {
104 log_syntax(unit, LOG_ERR, filename, line, -r,
105 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
106 continue;
107 }
9e2f7c11 108
701cc384 109 r = unit_add_dependency_by_name(u, d, k, NULL, true);
57020a3a 110 if (r < 0)
e8e581bf
ZJS
111 log_syntax(unit, LOG_ERR, filename, line, -r,
112 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
3efd4195
LP
113 }
114
115 return 0;
116}
117
e8e581bf
ZJS
118int config_parse_unit_string_printf(const char *unit,
119 const char *filename,
120 unsigned line,
121 const char *section,
122 const char *lvalue,
123 int ltype,
124 const char *rvalue,
125 void *data,
126 void *userdata) {
932921b5
LP
127
128 Unit *u = userdata;
74051b9b 129 _cleanup_free_ char *k = NULL;
19f6d710 130 int r;
932921b5
LP
131
132 assert(filename);
133 assert(lvalue);
134 assert(rvalue);
f2d3769a 135 assert(u);
932921b5 136
19f6d710
LP
137 r = unit_full_printf(u, rvalue, &k);
138 if (r < 0)
139 log_syntax(unit, LOG_ERR, filename, line, -r,
140 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
932921b5 141
e8e581bf
ZJS
142 return config_parse_string(unit, filename, line, section, lvalue, ltype,
143 k ? k : rvalue, data, userdata);
932921b5
LP
144}
145
e8e581bf
ZJS
146int config_parse_unit_strv_printf(const char *unit,
147 const char *filename,
148 unsigned line,
149 const char *section,
150 const char *lvalue,
151 int ltype,
152 const char *rvalue,
153 void *data,
154 void *userdata) {
8fef7659
LP
155
156 Unit *u = userdata;
74051b9b 157 _cleanup_free_ char *k = NULL;
19f6d710 158 int r;
8fef7659
LP
159
160 assert(filename);
161 assert(lvalue);
162 assert(rvalue);
163 assert(u);
164
19f6d710
LP
165 r = unit_full_printf(u, rvalue, &k);
166 if (r < 0)
167 log_syntax(unit, LOG_ERR, filename, line, -r,
168 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
8fef7659 169
e8e581bf
ZJS
170 return config_parse_strv(unit, filename, line, section, lvalue, ltype,
171 k ? k : rvalue, data, userdata);
8fef7659
LP
172}
173
e8e581bf
ZJS
174int config_parse_unit_path_printf(const char *unit,
175 const char *filename,
176 unsigned line,
177 const char *section,
178 const char *lvalue,
179 int ltype,
180 const char *rvalue,
181 void *data,
182 void *userdata) {
6ea832a2
LP
183
184 Unit *u = userdata;
74051b9b 185 _cleanup_free_ char *k = NULL;
19f6d710 186 int r;
6ea832a2
LP
187
188 assert(filename);
189 assert(lvalue);
190 assert(rvalue);
6ea832a2
LP
191 assert(u);
192
19f6d710
LP
193 r = unit_full_printf(u, rvalue, &k);
194 if (r < 0)
195 log_syntax(unit, LOG_ERR, filename, line, -r,
196 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
6ea832a2 197
e8e581bf
ZJS
198 return config_parse_path(unit, filename, line, section, lvalue, ltype,
199 k ? k : rvalue, data, userdata);
6ea832a2
LP
200}
201
e8e581bf
ZJS
202int config_parse_socket_listen(const char *unit,
203 const char *filename,
204 unsigned line,
205 const char *section,
206 const char *lvalue,
207 int ltype,
208 const char *rvalue,
209 void *data,
210 void *userdata) {
42f4e3c4 211
49f91047 212 SocketPort *p, *tail;
542563ba 213 Socket *s;
19f6d710 214 int r;
16354eff 215
42f4e3c4
LP
216 assert(filename);
217 assert(lvalue);
218 assert(rvalue);
219 assert(data);
220
595ed347 221 s = SOCKET(data);
542563ba 222
74051b9b
LP
223 if (isempty(rvalue)) {
224 /* An empty assignment removes all ports */
225 socket_free_ports(s);
226 return 0;
227 }
228
7f110ff9
LP
229 p = new0(SocketPort, 1);
230 if (!p)
74051b9b 231 return log_oom();
916abb21 232
74051b9b 233 if (ltype != SOCKET_SOCKET) {
916abb21 234
74051b9b 235 p->type = ltype;
19f6d710
LP
236 r = unit_full_printf(UNIT(s), rvalue, &p->path);
237 if (r < 0) {
487060c2
LP
238 p->path = strdup(rvalue);
239 if (!p->path) {
240 free(p);
241 return log_oom();
242 } else
19f6d710
LP
243 log_syntax(unit, LOG_ERR, filename, line, -r,
244 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
916abb21
LP
245 }
246
247 path_kill_slashes(p->path);
248
7a22745a 249 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 250 _cleanup_free_ char *k = NULL;
1fd45a90 251
7a22745a 252 p->type = SOCKET_SOCKET;
19f6d710
LP
253 r = unit_full_printf(UNIT(s), rvalue, &k);
254 if (r < 0)
255 log_syntax(unit, LOG_ERR, filename, line, -r,
256 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
7a22745a 257
487060c2 258 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
1fd45a90 259 if (r < 0) {
19f6d710 260 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 261 "Failed to parse address value, ignoring: %s", rvalue);
7a22745a
LP
262 free(p);
263 return 0;
264 }
265
542563ba 266 } else {
74051b9b 267 _cleanup_free_ char *k = NULL;
1fd45a90 268
542563ba 269 p->type = SOCKET_SOCKET;
19f6d710
LP
270 r = unit_full_printf(UNIT(s), rvalue, &k);
271 if (r < 0)
272 log_syntax(unit, LOG_ERR, filename, line, -r,
273 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
542563ba 274
487060c2 275 r = socket_address_parse(&p->address, k ? k : rvalue);
1fd45a90 276 if (r < 0) {
19f6d710 277 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 278 "Failed to parse address value, ignoring: %s", rvalue);
542563ba 279 free(p);
c0b34696 280 return 0;
542563ba
LP
281 }
282
283 if (streq(lvalue, "ListenStream"))
284 p->address.type = SOCK_STREAM;
285 else if (streq(lvalue, "ListenDatagram"))
286 p->address.type = SOCK_DGRAM;
287 else {
288 assert(streq(lvalue, "ListenSequentialPacket"));
289 p->address.type = SOCK_SEQPACKET;
290 }
291
292 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
e8e581bf
ZJS
293 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
294 "Address family not supported, ignoring: %s", rvalue);
542563ba 295 free(p);
c0b34696 296 return 0;
542563ba 297 }
16354eff
LP
298 }
299
542563ba 300 p->fd = -1;
49f91047
LP
301
302 if (s->ports) {
303 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
304 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
305 } else
306 LIST_PREPEND(SocketPort, port, s->ports, p);
542563ba 307
16354eff 308 return 0;
42f4e3c4
LP
309}
310
e8e581bf
ZJS
311int config_parse_socket_bind(const char *unit,
312 const char *filename,
313 unsigned line,
314 const char *section,
315 const char *lvalue,
316 int ltype,
317 const char *rvalue,
318 void *data,
319 void *userdata) {
42f4e3c4 320
542563ba 321 Socket *s;
c0120d99 322 SocketAddressBindIPv6Only b;
42f4e3c4
LP
323
324 assert(filename);
325 assert(lvalue);
326 assert(rvalue);
327 assert(data);
328
595ed347 329 s = SOCKET(data);
542563ba 330
5198dabc
LP
331 b = socket_address_bind_ipv6_only_from_string(rvalue);
332 if (b < 0) {
c0120d99
LP
333 int r;
334
5198dabc
LP
335 r = parse_boolean(rvalue);
336 if (r < 0) {
e8e581bf
ZJS
337 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
338 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
c0b34696 339 return 0;
c0120d99 340 }
42f4e3c4 341
c0120d99
LP
342 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
343 } else
344 s->bind_ipv6_only = b;
542563ba 345
42f4e3c4
LP
346 return 0;
347}
348
e8e581bf
ZJS
349int config_parse_exec_nice(const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 const char *lvalue,
354 int ltype,
355 const char *rvalue,
356 void *data,
357 void *userdata) {
034c6ed7 358
fb33a393 359 ExecContext *c = data;
e8e581bf 360 int priority, r;
034c6ed7
LP
361
362 assert(filename);
363 assert(lvalue);
364 assert(rvalue);
365 assert(data);
366
e8e581bf
ZJS
367 r = safe_atoi(rvalue, &priority);
368 if (r < 0) {
369 log_syntax(unit, LOG_ERR, filename, line, -r,
370 "Failed to parse nice priority, ignoring: %s. ", rvalue);
c0b34696 371 return 0;
034c6ed7
LP
372 }
373
374 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
e8e581bf
ZJS
375 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
376 "Nice priority out of range, ignoring: %s", rvalue);
c0b34696 377 return 0;
034c6ed7
LP
378 }
379
fb33a393 380 c->nice = priority;
71155933 381 c->nice_set = true;
fb33a393 382
034c6ed7
LP
383 return 0;
384}
385
e8e581bf
ZJS
386int config_parse_exec_oom_score_adjust(const char* unit,
387 const char *filename,
388 unsigned line,
389 const char *section,
390 const char *lvalue,
391 int ltype,
392 const char *rvalue,
393 void *data,
394 void *userdata) {
034c6ed7 395
fb33a393 396 ExecContext *c = data;
e8e581bf 397 int oa, r;
034c6ed7
LP
398
399 assert(filename);
400 assert(lvalue);
401 assert(rvalue);
402 assert(data);
403
e8e581bf
ZJS
404 r = safe_atoi(rvalue, &oa);
405 if (r < 0) {
406 log_syntax(unit, LOG_ERR, filename, line, -r,
407 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
c0b34696 408 return 0;
034c6ed7
LP
409 }
410
dd6c17b1 411 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
e8e581bf
ZJS
412 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
413 "OOM score adjust value out of range, ignoring: %s", rvalue);
c0b34696 414 return 0;
034c6ed7
LP
415 }
416
dd6c17b1
LP
417 c->oom_score_adjust = oa;
418 c->oom_score_adjust_set = true;
fb33a393 419
034c6ed7
LP
420 return 0;
421}
422
e8e581bf
ZJS
423int config_parse_exec(const char *unit,
424 const char *filename,
425 unsigned line,
426 const char *section,
427 const char *lvalue,
428 int ltype,
429 const char *rvalue,
430 void *data,
431 void *userdata) {
034c6ed7 432
61e5d8ed
LP
433 ExecCommand **e = data, *nce;
434 char *path, **n;
034c6ed7 435 unsigned k;
7f110ff9 436 int r;
034c6ed7
LP
437
438 assert(filename);
439 assert(lvalue);
440 assert(rvalue);
61e5d8ed 441 assert(e);
034c6ed7 442
74051b9b
LP
443 e += ltype;
444
445 if (isempty(rvalue)) {
446 /* An empty assignment resets the list */
447 exec_command_free_list(*e);
448 *e = NULL;
449 return 0;
450 }
451
6c666e26
LP
452 /* We accept an absolute path as first argument, or
453 * alternatively an absolute prefixed with @ to allow
454 * overriding of argv[0]. */
61e5d8ed 455 for (;;) {
0f67f1ef 456 int i;
61e5d8ed
LP
457 char *w;
458 size_t l;
459 char *state;
b708e7ce 460 bool honour_argv0 = false, ignore = false;
6c666e26 461
61e5d8ed
LP
462 path = NULL;
463 nce = NULL;
464 n = NULL;
6c666e26 465
61e5d8ed 466 rvalue += strspn(rvalue, WHITESPACE);
034c6ed7 467
61e5d8ed
LP
468 if (rvalue[0] == 0)
469 break;
034c6ed7 470
0f67f1ef
ZJS
471 for (i = 0; i < 2; i++) {
472 if (rvalue[0] == '-' && !ignore) {
473 ignore = true;
474 rvalue ++;
475 }
b708e7ce 476
0f67f1ef
ZJS
477 if (rvalue[0] == '@' && !honour_argv0) {
478 honour_argv0 = true;
479 rvalue ++;
480 }
b708e7ce 481 }
61e5d8ed 482
b708e7ce 483 if (*rvalue != '/') {
e8e581bf
ZJS
484 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
485 "Executable path is not absolute, ignoring: %s", rvalue);
c0b34696 486 return 0;
6c666e26 487 }
034c6ed7 488
61e5d8ed
LP
489 k = 0;
490 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 491 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 492 break;
034c6ed7 493
61e5d8ed
LP
494 k++;
495 }
034c6ed7 496
7f110ff9
LP
497 n = new(char*, k + !honour_argv0);
498 if (!n)
74051b9b 499 return log_oom();
61e5d8ed
LP
500
501 k = 0;
61e5d8ed 502 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 503 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 504 break;
641906e9 505 else if (strneq(w, "\\;", MAX(l, 1U)))
7e1a84f5 506 w ++;
61e5d8ed 507
b708e7ce
LP
508 if (honour_argv0 && w == rvalue) {
509 assert(!path);
7f110ff9
LP
510
511 path = strndup(w, l);
512 if (!path) {
74051b9b 513 r = log_oom();
7f110ff9
LP
514 goto fail;
515 }
516
517 if (!utf8_is_valid(path)) {
e8e581bf
ZJS
518 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
519 "Path is not UTF-8 clean, ignoring assignment: %s",
520 rvalue);
7f110ff9 521 r = 0;
61e5d8ed 522 goto fail;
7f110ff9
LP
523 }
524
61e5d8ed 525 } else {
7f110ff9
LP
526 char *c;
527
528 c = n[k++] = cunescape_length(w, l);
529 if (!c) {
74051b9b 530 r = log_oom();
61e5d8ed 531 goto fail;
7f110ff9
LP
532 }
533
534 if (!utf8_is_valid(c)) {
e8e581bf
ZJS
535 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
536 "Path is not UTF-8 clean, ignoring assignment: %s",
537 rvalue);
7f110ff9
LP
538 r = 0;
539 goto fail;
540 }
61e5d8ed
LP
541 }
542 }
543
544 n[k] = NULL;
545
546 if (!n[0]) {
e8e581bf
ZJS
547 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
548 "Invalid command line, ignoring: %s", rvalue);
7f110ff9
LP
549 r = 0;
550 goto fail;
61e5d8ed
LP
551 }
552
7f110ff9
LP
553 if (!path) {
554 path = strdup(n[0]);
555 if (!path) {
74051b9b 556 r = log_oom();
61e5d8ed 557 goto fail;
7f110ff9
LP
558 }
559 }
6c666e26 560
61e5d8ed 561 assert(path_is_absolute(path));
6c666e26 562
7f110ff9
LP
563 nce = new0(ExecCommand, 1);
564 if (!nce) {
74051b9b 565 r = log_oom();
61e5d8ed 566 goto fail;
7f110ff9 567 }
61e5d8ed
LP
568
569 nce->argv = n;
570 nce->path = path;
b708e7ce 571 nce->ignore = ignore;
034c6ed7 572
61e5d8ed 573 path_kill_slashes(nce->path);
034c6ed7 574
61e5d8ed 575 exec_command_append_list(e, nce);
01f78473 576
61e5d8ed
LP
577 rvalue = state;
578 }
034c6ed7
LP
579
580 return 0;
581
582fail:
6c666e26
LP
583 n[k] = NULL;
584 strv_free(n);
585 free(path);
034c6ed7
LP
586 free(nce);
587
7f110ff9 588 return r;
034c6ed7
LP
589}
590
f975e971
LP
591DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
592DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 593
e8e581bf
ZJS
594int config_parse_socket_bindtodevice(const char* unit,
595 const char *filename,
596 unsigned line,
597 const char *section,
598 const char *lvalue,
599 int ltype,
600 const char *rvalue,
601 void *data,
602 void *userdata) {
acbb0225
LP
603
604 Socket *s = data;
605 char *n;
606
607 assert(filename);
608 assert(lvalue);
609 assert(rvalue);
610 assert(data);
611
612 if (rvalue[0] && !streq(rvalue, "*")) {
74051b9b
LP
613 n = strdup(rvalue);
614 if (!n)
615 return log_oom();
acbb0225
LP
616 } else
617 n = NULL;
618
619 free(s->bind_to_device);
620 s->bind_to_device = n;
621
622 return 0;
623}
624
f975e971
LP
625DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
626DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 627
e8e581bf
ZJS
628int config_parse_exec_io_class(const char *unit,
629 const char *filename,
630 unsigned line,
631 const char *section,
632 const char *lvalue,
633 int ltype,
634 const char *rvalue,
635 void *data,
636 void *userdata) {
94f04347
LP
637
638 ExecContext *c = data;
639 int x;
640
641 assert(filename);
642 assert(lvalue);
643 assert(rvalue);
644 assert(data);
645
f8b69d1d
MS
646 x = ioprio_class_from_string(rvalue);
647 if (x < 0) {
e8e581bf
ZJS
648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
649 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 650 return 0;
0d87eb42 651 }
94f04347
LP
652
653 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
654 c->ioprio_set = true;
655
656 return 0;
657}
658
e8e581bf
ZJS
659int config_parse_exec_io_priority(const char *unit,
660 const char *filename,
661 unsigned line,
662 const char *section,
663 const char *lvalue,
664 int ltype,
665 const char *rvalue,
666 void *data,
667 void *userdata) {
94f04347
LP
668
669 ExecContext *c = data;
e8e581bf 670 int i, r;
94f04347
LP
671
672 assert(filename);
673 assert(lvalue);
674 assert(rvalue);
675 assert(data);
676
e8e581bf
ZJS
677 r = safe_atoi(rvalue, &i);
678 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
679 log_syntax(unit, LOG_ERR, filename, line, -r,
680 "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 681 return 0;
071830ff
LP
682 }
683
94f04347
LP
684 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
685 c->ioprio_set = true;
686
071830ff
LP
687 return 0;
688}
689
e8e581bf
ZJS
690int config_parse_exec_cpu_sched_policy(const char *unit,
691 const char *filename,
692 unsigned line,
693 const char *section,
694 const char *lvalue,
695 int ltype,
696 const char *rvalue,
697 void *data,
698 void *userdata) {
9eba9da4 699
94f04347
LP
700
701 ExecContext *c = data;
702 int x;
703
704 assert(filename);
705 assert(lvalue);
706 assert(rvalue);
707 assert(data);
708
f8b69d1d
MS
709 x = sched_policy_from_string(rvalue);
710 if (x < 0) {
e8e581bf
ZJS
711 log_syntax(unit, LOG_ERR, filename, line, -x,
712 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 713 return 0;
0d87eb42 714 }
94f04347
LP
715
716 c->cpu_sched_policy = x;
bb112710
HHPF
717 /* Moving to or from real-time policy? We need to adjust the priority */
718 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
719 c->cpu_sched_set = true;
720
721 return 0;
722}
723
e8e581bf
ZJS
724int config_parse_exec_cpu_sched_prio(const char *unit,
725 const char *filename,
726 unsigned line,
727 const char *section,
728 const char *lvalue,
729 int ltype,
730 const char *rvalue,
731 void *data,
732 void *userdata) {
9eba9da4
LP
733
734 ExecContext *c = data;
e8e581bf 735 int i, min, max, r;
9eba9da4
LP
736
737 assert(filename);
738 assert(lvalue);
739 assert(rvalue);
740 assert(data);
741
e8e581bf
ZJS
742 r = safe_atoi(rvalue, &i);
743 if (r < 0) {
744 log_syntax(unit, LOG_ERR, filename, line, -r,
745 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 746 return 0;
94f04347 747 }
9eba9da4 748
bb112710
HHPF
749 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
750 min = sched_get_priority_min(c->cpu_sched_policy);
751 max = sched_get_priority_max(c->cpu_sched_policy);
752
753 if (i < min || i > max) {
e8e581bf
ZJS
754 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
755 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
756 return 0;
757 }
758
94f04347
LP
759 c->cpu_sched_priority = i;
760 c->cpu_sched_set = true;
761
762 return 0;
763}
764
e8e581bf
ZJS
765int config_parse_exec_cpu_affinity(const char *unit,
766 const char *filename,
767 unsigned line,
768 const char *section,
769 const char *lvalue,
770 int ltype,
771 const char *rvalue,
772 void *data,
773 void *userdata) {
94f04347
LP
774
775 ExecContext *c = data;
776 char *w;
777 size_t l;
778 char *state;
779
780 assert(filename);
781 assert(lvalue);
782 assert(rvalue);
783 assert(data);
784
74051b9b
LP
785 if (isempty(rvalue)) {
786 /* An empty assignment resets the CPU list */
787 if (c->cpuset)
788 CPU_FREE(c->cpuset);
789 c->cpuset = NULL;
790 return 0;
791 }
792
f60f22df 793 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 794 _cleanup_free_ char *t = NULL;
94f04347
LP
795 int r;
796 unsigned cpu;
797
c040936b
ZJS
798 t = strndup(w, l);
799 if (!t)
74051b9b 800 return log_oom();
94f04347 801
487393e9 802 r = safe_atou(t, &cpu);
487393e9 803
c040936b
ZJS
804 if (!c->cpuset) {
805 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
806 if (!c->cpuset)
74051b9b 807 return log_oom();
c040936b 808 }
82c121a4 809
82c121a4 810 if (r < 0 || cpu >= c->cpuset_ncpus) {
e8e581bf
ZJS
811 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
812 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
c0b34696 813 return 0;
9eba9da4 814 }
94f04347 815
82c121a4 816 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
9eba9da4
LP
817 }
818
94f04347
LP
819 return 0;
820}
821
e8e581bf
ZJS
822int config_parse_exec_capabilities(const char *unit,
823 const char *filename,
824 unsigned line,
825 const char *section,
826 const char *lvalue,
827 int ltype,
828 const char *rvalue,
829 void *data,
830 void *userdata) {
94f04347
LP
831
832 ExecContext *c = data;
833 cap_t cap;
834
835 assert(filename);
836 assert(lvalue);
837 assert(rvalue);
838 assert(data);
839
74051b9b
LP
840 cap = cap_from_text(rvalue);
841 if (!cap) {
e8e581bf
ZJS
842 log_syntax(unit, LOG_ERR, filename, line, errno,
843 "Failed to parse capabilities, ignoring: %s", rvalue);
c0b34696 844 return 0;
94f04347
LP
845 }
846
847 if (c->capabilities)
848 cap_free(c->capabilities);
849 c->capabilities = cap;
850
851 return 0;
852}
853
e8e581bf
ZJS
854int config_parse_exec_secure_bits(const char *unit,
855 const char *filename,
856 unsigned line,
857 const char *section,
858 const char *lvalue,
859 int ltype,
860 const char *rvalue,
861 void *data,
862 void *userdata) {
94f04347
LP
863
864 ExecContext *c = data;
865 char *w;
866 size_t l;
867 char *state;
868
869 assert(filename);
870 assert(lvalue);
871 assert(rvalue);
872 assert(data);
873
74051b9b
LP
874 if (isempty(rvalue)) {
875 /* An empty assignment resets the field */
876 c->secure_bits = 0;
877 return 0;
878 }
879
f60f22df 880 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347 881 if (first_word(w, "keep-caps"))
cbb21cca 882 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
94f04347 883 else if (first_word(w, "keep-caps-locked"))
cbb21cca 884 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
94f04347 885 else if (first_word(w, "no-setuid-fixup"))
cbb21cca 886 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
94f04347 887 else if (first_word(w, "no-setuid-fixup-locked"))
cbb21cca 888 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
94f04347 889 else if (first_word(w, "noroot"))
cbb21cca 890 c->secure_bits |= 1<<SECURE_NOROOT;
94f04347 891 else if (first_word(w, "noroot-locked"))
cbb21cca 892 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
9eba9da4 893 else {
e8e581bf
ZJS
894 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
895 "Failed to parse secure bits, ignoring: %s", rvalue);
c0b34696 896 return 0;
9eba9da4
LP
897 }
898 }
899
94f04347
LP
900 return 0;
901}
902
e8e581bf
ZJS
903int config_parse_bounding_set(const char *unit,
904 const char *filename,
905 unsigned line,
906 const char *section,
907 const char *lvalue,
908 int ltype,
909 const char *rvalue,
910 void *data,
911 void *userdata) {
94f04347 912
ec8927ca 913 uint64_t *capability_bounding_set_drop = data;
94f04347
LP
914 char *w;
915 size_t l;
916 char *state;
260abb78
LP
917 bool invert = false;
918 uint64_t sum = 0;
94f04347
LP
919
920 assert(filename);
921 assert(lvalue);
922 assert(rvalue);
923 assert(data);
924
260abb78
LP
925 if (rvalue[0] == '~') {
926 invert = true;
927 rvalue++;
928 }
929
930 /* Note that we store this inverted internally, since the
931 * kernel wants it like this. But we actually expose it
932 * non-inverted everywhere to have a fully normalized
933 * interface. */
934
f60f22df 935 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 936 _cleanup_free_ char *t = NULL;
94f04347
LP
937 int r;
938 cap_value_t cap;
939
ec8927ca
LP
940 t = strndup(w, l);
941 if (!t)
74051b9b 942 return log_oom();
94f04347
LP
943
944 r = cap_from_name(t, &cap);
94f04347 945 if (r < 0) {
e8e581bf
ZJS
946 log_syntax(unit, LOG_ERR, filename, line, errno,
947 "Failed to parse capability in bounding set, ignoring: %s", t);
8351ceae 948 continue;
94f04347
LP
949 }
950
260abb78 951 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
94f04347 952 }
9eba9da4 953
260abb78 954 if (invert)
ec8927ca 955 *capability_bounding_set_drop |= sum;
260abb78 956 else
ec8927ca 957 *capability_bounding_set_drop |= ~sum;
260abb78 958
9eba9da4
LP
959 return 0;
960}
961
e8e581bf
ZJS
962int config_parse_limit(const char *unit,
963 const char *filename,
964 unsigned line,
965 const char *section,
966 const char *lvalue,
967 int ltype,
968 const char *rvalue,
969 void *data,
970 void *userdata) {
94f04347
LP
971
972 struct rlimit **rl = data;
973 unsigned long long u;
94f04347
LP
974
975 assert(filename);
976 assert(lvalue);
977 assert(rvalue);
978 assert(data);
979
f975e971
LP
980 rl += ltype;
981
3d57c6ab
LP
982 if (streq(rvalue, "infinity"))
983 u = (unsigned long long) RLIM_INFINITY;
e8e581bf
ZJS
984 else {
985 int r;
986
987 r = safe_atollu(rvalue, &u);
988 if (r < 0) {
989 log_syntax(unit, LOG_ERR, filename, line, -r,
990 "Failed to parse resource value, ignoring: %s", rvalue);
991 return 0;
992 }
94f04347
LP
993 }
994
74051b9b
LP
995 if (!*rl) {
996 *rl = new(struct rlimit, 1);
997 if (!*rl)
998 return log_oom();
999 }
9eba9da4 1000
94f04347 1001 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
1002 return 0;
1003}
1004
07459bb6 1005#ifdef HAVE_SYSV_COMPAT
e8e581bf
ZJS
1006int config_parse_sysv_priority(const char *unit,
1007 const char *filename,
1008 unsigned line,
1009 const char *section,
1010 const char *lvalue,
1011 int ltype,
1012 const char *rvalue,
1013 void *data,
1014 void *userdata) {
a9a1e00a
LP
1015
1016 int *priority = data;
e8e581bf 1017 int i, r;
a9a1e00a
LP
1018
1019 assert(filename);
1020 assert(lvalue);
1021 assert(rvalue);
1022 assert(data);
1023
e8e581bf
ZJS
1024 r = safe_atoi(rvalue, &i);
1025 if (r < 0 || i < 0) {
1026 log_syntax(unit, LOG_ERR, filename, line, -r,
1027 "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1028 return 0;
a9a1e00a
LP
1029 }
1030
1031 *priority = (int) i;
1032 return 0;
1033}
07459bb6 1034#endif
a9a1e00a 1035
e8e581bf
ZJS
1036int config_parse_fsck_passno(const char *unit,
1037 const char *filename,
1038 unsigned line,
1039 const char *section,
1040 const char *lvalue,
1041 int ltype,
1042 const char *rvalue,
1043 void *data,
1044 void *userdata) {
2ba545f1
LP
1045
1046 int *passno = data;
e8e581bf 1047 int i, r;
2ba545f1
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 || i < 0) {
1056 log_syntax(unit, LOG_ERR, filename, line, -r,
1057 "Failed to parse fsck pass number, ignoring: %s", rvalue);
2ba545f1
LP
1058 return 0;
1059 }
1060
1061 *passno = (int) i;
1062 return 0;
1063}
1064
f975e971 1065DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1066
e8e581bf
ZJS
1067int config_parse_kill_signal(const char *unit,
1068 const char *filename,
1069 unsigned line,
1070 const char *section,
1071 const char *lvalue,
1072 int ltype,
1073 const char *rvalue,
1074 void *data,
1075 void *userdata) {
2e22afe9
LP
1076
1077 int *sig = data;
1078 int r;
1079
1080 assert(filename);
1081 assert(lvalue);
1082 assert(rvalue);
1083 assert(sig);
1084
74051b9b
LP
1085 r = signal_from_string_try_harder(rvalue);
1086 if (r <= 0) {
e8e581bf
ZJS
1087 log_syntax(unit, LOG_ERR, filename, line, -r,
1088 "Failed to parse kill signal, ignoring: %s", rvalue);
c0b34696 1089 return 0;
2e22afe9
LP
1090 }
1091
1092 *sig = r;
1093 return 0;
1094}
1095
e8e581bf
ZJS
1096int config_parse_exec_mount_flags(const char *unit,
1097 const char *filename,
1098 unsigned line,
1099 const char *section,
1100 const char *lvalue,
1101 int ltype,
1102 const char *rvalue,
1103 void *data,
1104 void *userdata) {
15ae422b
LP
1105
1106 ExecContext *c = data;
1107 char *w;
1108 size_t l;
1109 char *state;
1110 unsigned long flags = 0;
1111
1112 assert(filename);
1113 assert(lvalue);
1114 assert(rvalue);
1115 assert(data);
1116
ac97e2c5 1117 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
7fd1b19b 1118 _cleanup_free_ char *t;
ac97e2c5
ZJS
1119
1120 t = strndup(w, l);
1121 if (!t)
74051b9b 1122 return log_oom();
ac97e2c5
ZJS
1123
1124 if (streq(t, "shared"))
15ae422b 1125 flags |= MS_SHARED;
ac97e2c5 1126 else if (streq(t, "slave"))
15ae422b 1127 flags |= MS_SLAVE;
ac97e2c5 1128 else if (streq(w, "private"))
15ae422b
LP
1129 flags |= MS_PRIVATE;
1130 else {
e8e581bf
ZJS
1131 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1132 "Failed to parse mount flag %s, ignoring: %s",
1133 t, rvalue);
c0b34696 1134 return 0;
15ae422b
LP
1135 }
1136 }
1137
1138 c->mount_flags = flags;
1139 return 0;
1140}
1141
e8e581bf
ZJS
1142int config_parse_timer(const char *unit,
1143 const char *filename,
1144 unsigned line,
1145 const char *section,
1146 const char *lvalue,
1147 int ltype,
1148 const char *rvalue,
1149 void *data,
1150 void *userdata) {
871d7de4
LP
1151
1152 Timer *t = data;
36697dc0 1153 usec_t u = 0;
871d7de4
LP
1154 TimerValue *v;
1155 TimerBase b;
36697dc0
LP
1156 CalendarSpec *c = NULL;
1157 clockid_t id;
871d7de4
LP
1158
1159 assert(filename);
1160 assert(lvalue);
1161 assert(rvalue);
1162 assert(data);
1163
74051b9b
LP
1164 if (isempty(rvalue)) {
1165 /* Empty assignment resets list */
1166 timer_free_values(t);
1167 return 0;
1168 }
1169
36697dc0
LP
1170 b = timer_base_from_string(lvalue);
1171 if (b < 0) {
e8e581bf
ZJS
1172 log_syntax(unit, LOG_ERR, filename, line, -b,
1173 "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1174 return 0;
871d7de4
LP
1175 }
1176
36697dc0
LP
1177 if (b == TIMER_CALENDAR) {
1178 if (calendar_spec_from_string(rvalue, &c) < 0) {
e8e581bf
ZJS
1179 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1180 "Failed to parse calendar specification, ignoring: %s",
1181 rvalue);
36697dc0
LP
1182 return 0;
1183 }
1184
1185 id = CLOCK_REALTIME;
1186 } else {
7f602784 1187 if (parse_sec(rvalue, &u) < 0) {
e8e581bf
ZJS
1188 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1189 "Failed to parse timer value, ignoring: %s",
1190 rvalue);
36697dc0
LP
1191 return 0;
1192 }
1193
1194 id = CLOCK_MONOTONIC;
871d7de4
LP
1195 }
1196
36697dc0
LP
1197 v = new0(TimerValue, 1);
1198 if (!v)
74051b9b 1199 return log_oom();
871d7de4
LP
1200
1201 v->base = b;
36697dc0 1202 v->clock_id = id;
871d7de4 1203 v->value = u;
36697dc0 1204 v->calendar_spec = c;
871d7de4
LP
1205
1206 LIST_PREPEND(TimerValue, value, t->values, v);
1207
1208 return 0;
1209}
1210
3ecaa09b
LP
1211int config_parse_trigger_unit(
1212 const char *unit,
1213 const char *filename,
1214 unsigned line,
1215 const char *section,
1216 const char *lvalue,
1217 int ltype,
1218 const char *rvalue,
1219 void *data,
1220 void *userdata) {
871d7de4 1221
74051b9b 1222 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1223 Unit *u = data;
1224 UnitType type;
1225 int r;
398ef8ba
LP
1226
1227 assert(filename);
1228 assert(lvalue);
1229 assert(rvalue);
1230 assert(data);
1231
3ecaa09b
LP
1232 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1233 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1234 "Multiple units to trigger specified, ignoring: %s", rvalue);
1235 return 0;
1236 }
871d7de4 1237
19f6d710
LP
1238 r = unit_name_printf(u, rvalue, &p);
1239 if (r < 0)
1240 log_syntax(unit, LOG_ERR, filename, line, -r,
1241 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
74051b9b 1242
19f6d710 1243 type = unit_name_to_type(p ?: rvalue);
3ecaa09b 1244 if (type < 0) {
e8e581bf 1245 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3ecaa09b 1246 "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1247 return 0;
871d7de4
LP
1248 }
1249
3ecaa09b
LP
1250 if (type == u->type) {
1251 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1252 "Trigger cannot be of same type, ignoring: %s", rvalue);
1253 return 0;
1254 }
1255
19f6d710 1256 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
57020a3a 1257 if (r < 0) {
e8e581bf 1258 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1259 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
c0b34696 1260 return 0;
871d7de4
LP
1261 }
1262
1263 return 0;
1264}
1265
e8e581bf
ZJS
1266int config_parse_path_spec(const char *unit,
1267 const char *filename,
1268 unsigned line,
1269 const char *section,
1270 const char *lvalue,
1271 int ltype,
1272 const char *rvalue,
1273 void *data,
1274 void *userdata) {
01f78473
LP
1275
1276 Path *p = data;
1277 PathSpec *s;
1278 PathType b;
7fd1b19b 1279 _cleanup_free_ char *k = NULL;
19f6d710 1280 int r;
01f78473
LP
1281
1282 assert(filename);
1283 assert(lvalue);
1284 assert(rvalue);
1285 assert(data);
1286
74051b9b
LP
1287 if (isempty(rvalue)) {
1288 /* Empty assignment clears list */
1289 path_free_specs(p);
1290 return 0;
1291 }
1292
93e4c84b
LP
1293 b = path_type_from_string(lvalue);
1294 if (b < 0) {
e8e581bf
ZJS
1295 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1296 "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1297 return 0;
01f78473
LP
1298 }
1299
19f6d710
LP
1300 r = unit_full_printf(UNIT(p), rvalue, &k);
1301 if (r < 0) {
487060c2
LP
1302 k = strdup(rvalue);
1303 if (!k)
1304 return log_oom();
1305 else
19f6d710 1306 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf
ZJS
1307 "Failed to resolve unit specifiers on %s. Ignoring.",
1308 rvalue);
487060c2 1309 }
93e4c84b
LP
1310
1311 if (!path_is_absolute(k)) {
e8e581bf
ZJS
1312 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1313 "Path is not absolute, ignoring: %s", k);
c0b34696 1314 return 0;
01f78473
LP
1315 }
1316
93e4c84b 1317 s = new0(PathSpec, 1);
543295ad 1318 if (!s)
93e4c84b 1319 return log_oom();
01f78473 1320
93e4c84b 1321 s->path = path_kill_slashes(k);
543295ad 1322 k = NULL;
01f78473
LP
1323 s->type = b;
1324 s->inotify_fd = -1;
1325
1326 LIST_PREPEND(PathSpec, spec, p->specs, s);
1327
1328 return 0;
1329}
1330
e8e581bf
ZJS
1331int config_parse_socket_service(const char *unit,
1332 const char *filename,
1333 unsigned line,
1334 const char *section,
1335 const char *lvalue,
1336 int ltype,
1337 const char *rvalue,
1338 void *data,
1339 void *userdata) {
d9ff321a
LP
1340
1341 Socket *s = data;
1342 int r;
1343 DBusError error;
4ff77f66 1344 Unit *x;
74051b9b 1345 _cleanup_free_ char *p = NULL;
d9ff321a
LP
1346
1347 assert(filename);
1348 assert(lvalue);
1349 assert(rvalue);
1350 assert(data);
1351
1352 dbus_error_init(&error);
1353
19f6d710
LP
1354 r = unit_name_printf(UNIT(s), rvalue, &p);
1355 if (r < 0)
1356 log_syntax(unit, LOG_ERR, filename, line, -r,
1357 "Failed to resolve specifiers, ignoring: %s", rvalue);
74051b9b 1358
19f6d710 1359 if (!endswith(p ?: rvalue, ".service")) {
e8e581bf
ZJS
1360 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1361 "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1362 return 0;
1363 }
1364
19f6d710 1365 r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
4ff77f66 1366 if (r < 0) {
19f6d710 1367 log_syntax(unit, LOG_ERR, filename, line, r,
e8e581bf
ZJS
1368 "Failed to load unit %s, ignoring: %s",
1369 rvalue, bus_error(&error, r));
d9ff321a
LP
1370 dbus_error_free(&error);
1371 return 0;
1372 }
1373
4ff77f66
LP
1374 unit_ref_set(&s->service, x);
1375
d9ff321a
LP
1376 return 0;
1377}
1378
e8e581bf
ZJS
1379int config_parse_service_sockets(const char *unit,
1380 const char *filename,
1381 unsigned line,
1382 const char *section,
1383 const char *lvalue,
1384 int ltype,
1385 const char *rvalue,
1386 void *data,
1387 void *userdata) {
f976f3f6
LP
1388
1389 Service *s = data;
1390 int r;
f976f3f6
LP
1391 char *state, *w;
1392 size_t l;
1393
1394 assert(filename);
1395 assert(lvalue);
1396 assert(rvalue);
1397 assert(data);
1398
f976f3f6 1399 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 1400 _cleanup_free_ char *t = NULL, *k = NULL;
f976f3f6 1401
57020a3a
LP
1402 t = strndup(w, l);
1403 if (!t)
74051b9b 1404 return log_oom();
f976f3f6 1405
19f6d710
LP
1406 r = unit_name_printf(UNIT(s), t, &k);
1407 if (r < 0)
1408 log_syntax(unit, LOG_ERR, filename, line, -r,
1409 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
57020a3a 1410
19f6d710 1411 if (!endswith(k ?: t, ".socket")) {
e8e581bf 1412 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1413 "Unit must be of type socket, ignoring: %s", k ?: t);
f976f3f6
LP
1414 continue;
1415 }
1416
19f6d710 1417 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
57020a3a 1418 if (r < 0)
e8e581bf
ZJS
1419 log_syntax(unit, LOG_ERR, filename, line, -r,
1420 "Failed to add dependency on %s, ignoring: %s",
19f6d710 1421 k ?: t, strerror(-r));
f976f3f6 1422
19f6d710 1423 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
57020a3a 1424 if (r < 0)
f976f3f6
LP
1425 return r;
1426 }
1427
1428 return 0;
1429}
1430
e8e581bf
ZJS
1431int config_parse_service_timeout(const char *unit,
1432 const char *filename,
1433 unsigned line,
1434 const char *section,
1435 const char *lvalue,
1436 int ltype,
1437 const char *rvalue,
1438 void *data,
1439 void *userdata) {
98709151
LN
1440
1441 Service *s = userdata;
1442 int r;
1443
1444 assert(filename);
1445 assert(lvalue);
1446 assert(rvalue);
1447 assert(s);
1448
e8e581bf
ZJS
1449 r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
1450 rvalue, data, userdata);
74051b9b 1451 if (r < 0)
d568a335 1452 return r;
98709151 1453
d568a335
MS
1454 if (streq(lvalue, "TimeoutSec")) {
1455 s->start_timeout_defined = true;
1456 s->timeout_stop_usec = s->timeout_start_usec;
1457 } else if (streq(lvalue, "TimeoutStartSec"))
1458 s->start_timeout_defined = true;
1459
1460 return 0;
98709151
LN
1461}
1462
e8e581bf
ZJS
1463int config_parse_unit_env_file(const char *unit,
1464 const char *filename,
1465 unsigned line,
1466 const char *section,
1467 const char *lvalue,
1468 int ltype,
1469 const char *rvalue,
1470 void *data,
1471 void *userdata) {
ddb26e18 1472
853b8397 1473 char ***env = data;
8fef7659 1474 Unit *u = userdata;
19f6d710
LP
1475 _cleanup_free_ char *n = NULL;
1476 const char *s;
853b8397 1477 int r;
ddb26e18
LP
1478
1479 assert(filename);
1480 assert(lvalue);
1481 assert(rvalue);
1482 assert(data);
1483
74051b9b
LP
1484 if (isempty(rvalue)) {
1485 /* Empty assignment frees the list */
74051b9b
LP
1486 strv_free(*env);
1487 *env = NULL;
1488 return 0;
1489 }
1490
19f6d710
LP
1491 r = unit_full_printf(u, rvalue, &n);
1492 if (r < 0)
1493 log_syntax(unit, LOG_ERR, filename, line, r,
1494 "Failed to resolve specifiers, ignoring: %s", rvalue);
8fef7659 1495
19f6d710 1496 s = n ?: rvalue;
8fef7659 1497 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
e8e581bf
ZJS
1498 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1499 "Path '%s' is not absolute, ignoring.", s);
afe4bfe2
LP
1500 return 0;
1501 }
1502
853b8397
LP
1503 r = strv_extend(env, s);
1504 if (r < 0)
1505 return log_oom();
1506
1507 return 0;
1508}
1509
e8e581bf
ZJS
1510int config_parse_environ(const char *unit,
1511 const char *filename,
1512 unsigned line,
1513 const char *section,
1514 const char *lvalue,
1515 int ltype,
1516 const char *rvalue,
1517 void *data,
1518 void *userdata) {
853b8397
LP
1519
1520 Unit *u = userdata;
1521 char*** env = data, *w, *state;
1522 size_t l;
1523 _cleanup_free_ char *k = NULL;
19f6d710 1524 int r;
853b8397
LP
1525
1526 assert(filename);
1527 assert(lvalue);
1528 assert(rvalue);
97d0e5f8 1529 assert(data);
853b8397
LP
1530
1531 if (isempty(rvalue)) {
1532 /* Empty assignment resets the list */
1533 strv_free(*env);
1534 *env = NULL;
1535 return 0;
1536 }
1537
19f6d710
LP
1538 if (u) {
1539 r = unit_full_printf(u, rvalue, &k);
1540 if (r < 0)
1541 log_syntax(unit, LOG_ERR, filename, line, -r,
1542 "Failed to resolve specifiers, ignoring: %s", rvalue);
1543 }
97d0e5f8 1544
19f6d710
LP
1545 if (!k)
1546 k = strdup(rvalue);
8fef7659 1547 if (!k)
74051b9b 1548 return log_oom();
ddb26e18 1549
853b8397
LP
1550 FOREACH_WORD_QUOTED(w, l, k, state) {
1551 _cleanup_free_ char *n;
1552 char **x;
1553
1554 n = cunescape_length(w, l);
1555 if (!n)
1556 return log_oom();
1557
1558 if (!env_assignment_is_valid(n)) {
e8e581bf
ZJS
1559 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1560 "Invalid environment assignment, ignoring: %s", rvalue);
853b8397
LP
1561 continue;
1562 }
1563
1564 x = strv_env_set(*env, n);
1565 if (!x)
1566 return log_oom();
1567
1568 strv_free(*env);
1569 *env = x;
1570 }
ddb26e18 1571
8c7be95e 1572 return 0;
ddb26e18
LP
1573}
1574
e8e581bf
ZJS
1575int config_parse_ip_tos(const char *unit,
1576 const char *filename,
1577 unsigned line,
1578 const char *section,
1579 const char *lvalue,
1580 int ltype,
1581 const char *rvalue,
1582 void *data,
1583 void *userdata) {
4fd5948e
LP
1584
1585 int *ip_tos = data, x;
4fd5948e
LP
1586
1587 assert(filename);
1588 assert(lvalue);
1589 assert(rvalue);
1590 assert(data);
1591
f8b69d1d
MS
1592 x = ip_tos_from_string(rvalue);
1593 if (x < 0) {
e8e581bf
ZJS
1594 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1595 "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
1596 return 0;
1597 }
4fd5948e
LP
1598
1599 *ip_tos = x;
1600 return 0;
1601}
1602
e8e581bf
ZJS
1603int config_parse_unit_condition_path(const char *unit,
1604 const char *filename,
1605 unsigned line,
1606 const char *section,
1607 const char *lvalue,
1608 int ltype,
1609 const char *rvalue,
1610 void *data,
1611 void *userdata) {
52661efd 1612
2b583ce6 1613 ConditionType cond = ltype;
52661efd 1614 Unit *u = data;
267632f0 1615 bool trigger, negate;
52661efd 1616 Condition *c;
2fbe635a 1617 _cleanup_free_ char *p = NULL;
19f6d710 1618 int r;
52661efd
LP
1619
1620 assert(filename);
1621 assert(lvalue);
1622 assert(rvalue);
1623 assert(data);
1624
74051b9b
LP
1625 if (isempty(rvalue)) {
1626 /* Empty assignment resets the list */
1627 condition_free_list(u->conditions);
1628 u->conditions = NULL;
1629 return 0;
1630 }
1631
ab7f148f
LP
1632 trigger = rvalue[0] == '|';
1633 if (trigger)
267632f0
LP
1634 rvalue++;
1635
ab7f148f
LP
1636 negate = rvalue[0] == '!';
1637 if (negate)
52661efd
LP
1638 rvalue++;
1639
19f6d710
LP
1640 r = unit_full_printf(u, rvalue, &p);
1641 if (r < 0)
1642 log_syntax(unit, LOG_ERR, filename, line, -r,
1643 "Failed to resolve specifiers, ignoring: %s", rvalue);
1644 if (!p) {
1645 p = strdup(rvalue);
1646 if (!p)
1647 return log_oom();
1648 }
095b2d7a
AK
1649
1650 if (!path_is_absolute(p)) {
e8e581bf
ZJS
1651 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1652 "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
1653 return 0;
1654 }
1655
095b2d7a 1656 c = condition_new(cond, p, trigger, negate);
ab7f148f 1657 if (!c)
74051b9b 1658 return log_oom();
52661efd 1659
ac155bb8 1660 LIST_PREPEND(Condition, conditions, u->conditions, c);
52661efd
LP
1661 return 0;
1662}
1663
e8e581bf
ZJS
1664int config_parse_unit_condition_string(const char *unit,
1665 const char *filename,
1666 unsigned line,
1667 const char *section,
1668 const char *lvalue,
1669 int ltype,
1670 const char *rvalue,
1671 void *data,
1672 void *userdata) {
039655a4 1673
41584525 1674 ConditionType cond = ltype;
039655a4 1675 Unit *u = data;
267632f0 1676 bool trigger, negate;
039655a4 1677 Condition *c;
2fbe635a 1678 _cleanup_free_ char *s = NULL;
19f6d710 1679 int r;
039655a4
LP
1680
1681 assert(filename);
1682 assert(lvalue);
1683 assert(rvalue);
1684 assert(data);
1685
74051b9b
LP
1686 if (isempty(rvalue)) {
1687 /* Empty assignment resets the list */
1688 condition_free_list(u->conditions);
1689 u->conditions = NULL;
1690 return 0;
1691 }
1692
c0d6e764
LP
1693 trigger = rvalue[0] == '|';
1694 if (trigger)
267632f0
LP
1695 rvalue++;
1696
c0d6e764
LP
1697 negate = rvalue[0] == '!';
1698 if (negate)
039655a4
LP
1699 rvalue++;
1700
19f6d710
LP
1701 r = unit_full_printf(u, rvalue, &s);
1702 if (r < 0)
1703 log_syntax(unit, LOG_ERR, filename, line, -r,
1704 "Failed to resolve specifiers, ignoring: %s", rvalue);
1705 if (!s) {
1706 s = strdup(rvalue);
1707 if (!s)
1708 return log_oom();
1709 }
095b2d7a
AK
1710
1711 c = condition_new(cond, s, trigger, negate);
c0d6e764
LP
1712 if (!c)
1713 return log_oom();
039655a4 1714
ac155bb8 1715 LIST_PREPEND(Condition, conditions, u->conditions, c);
039655a4
LP
1716 return 0;
1717}
1718
e8e581bf
ZJS
1719int config_parse_unit_condition_null(const char *unit,
1720 const char *filename,
1721 unsigned line,
1722 const char *section,
1723 const char *lvalue,
1724 int ltype,
1725 const char *rvalue,
1726 void *data,
1727 void *userdata) {
d257ddef
LP
1728
1729 Unit *u = data;
1730 Condition *c;
267632f0 1731 bool trigger, negate;
d257ddef
LP
1732 int b;
1733
1734 assert(filename);
1735 assert(lvalue);
1736 assert(rvalue);
1737 assert(data);
1738
74051b9b
LP
1739 if (isempty(rvalue)) {
1740 /* Empty assignment resets the list */
1741 condition_free_list(u->conditions);
1742 u->conditions = NULL;
1743 return 0;
1744 }
1745
1746 trigger = rvalue[0] == '|';
1747 if (trigger)
267632f0
LP
1748 rvalue++;
1749
74051b9b
LP
1750 negate = rvalue[0] == '!';
1751 if (negate)
d257ddef
LP
1752 rvalue++;
1753
74051b9b
LP
1754 b = parse_boolean(rvalue);
1755 if (b < 0) {
e8e581bf
ZJS
1756 log_syntax(unit, LOG_ERR, filename, line, -b,
1757 "Failed to parse boolean value in condition, ignoring: %s",
1758 rvalue);
d257ddef
LP
1759 return 0;
1760 }
1761
1762 if (!b)
1763 negate = !negate;
1764
74051b9b
LP
1765 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1766 if (!c)
1767 return log_oom();
d257ddef 1768
ac155bb8 1769 LIST_PREPEND(Condition, conditions, u->conditions, c);
d257ddef
LP
1770 return 0;
1771}
1772
f975e971 1773DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
4b939747 1774DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
c952c6ec 1775
e8e581bf
ZJS
1776int config_parse_unit_requires_mounts_for(const char *unit,
1777 const char *filename,
1778 unsigned line,
1779 const char *section,
1780 const char *lvalue,
1781 int ltype,
1782 const char *rvalue,
1783 void *data,
1784 void *userdata) {
7c8fa05c
LP
1785
1786 Unit *u = userdata;
1787 int r;
1788 bool empty_before;
1789
1790 assert(filename);
1791 assert(lvalue);
1792 assert(rvalue);
1793 assert(data);
1794
1795 empty_before = !u->requires_mounts_for;
1796
e8e581bf
ZJS
1797 r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
1798 rvalue, data, userdata);
7c8fa05c
LP
1799
1800 /* Make it easy to find units with requires_mounts set */
1801 if (empty_before && u->requires_mounts_for)
1802 LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
1803
1804 return r;
1805}
9e372868 1806
e8e581bf
ZJS
1807int config_parse_documentation(const char *unit,
1808 const char *filename,
1809 unsigned line,
1810 const char *section,
1811 const char *lvalue,
1812 int ltype,
1813 const char *rvalue,
1814 void *data,
1815 void *userdata) {
49dbfa7b
LP
1816
1817 Unit *u = userdata;
1818 int r;
1819 char **a, **b;
1820
1821 assert(filename);
1822 assert(lvalue);
1823 assert(rvalue);
1824 assert(u);
1825
74051b9b
LP
1826 if (isempty(rvalue)) {
1827 /* Empty assignment resets the list */
1828 strv_free(u->documentation);
1829 u->documentation = NULL;
1830 return 0;
1831 }
1832
e8e581bf
ZJS
1833 r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
1834 rvalue, data, userdata);
49dbfa7b
LP
1835 if (r < 0)
1836 return r;
1837
1838 for (a = b = u->documentation; a && *a; a++) {
1839
1840 if (is_valid_documentation_url(*a))
1841 *(b++) = *a;
1842 else {
e8e581bf
ZJS
1843 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1844 "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
1845 free(*a);
1846 }
1847 }
1848 *b = NULL;
1849
1850 return r;
1851}
1852
8351ceae 1853static void syscall_set(uint32_t *p, int nr) {
843fc7f7 1854 nr = SYSCALL_TO_INDEX(nr);
8351ceae
LP
1855 p[nr >> 4] |= 1 << (nr & 31);
1856}
1857
1858static void syscall_unset(uint32_t *p, int nr) {
843fc7f7 1859 nr = SYSCALL_TO_INDEX(nr);
8351ceae
LP
1860 p[nr >> 4] &= ~(1 << (nr & 31));
1861}
1862
e8e581bf
ZJS
1863int config_parse_syscall_filter(const char *unit,
1864 const char *filename,
1865 unsigned line,
1866 const char *section,
1867 const char *lvalue,
1868 int ltype,
1869 const char *rvalue,
1870 void *data,
1871 void *userdata) {
8351ceae
LP
1872
1873 ExecContext *c = data;
1874 Unit *u = userdata;
b5fb3789 1875 bool invert = false;
8351ceae
LP
1876 char *w;
1877 size_t l;
1878 char *state;
1879
1880 assert(filename);
1881 assert(lvalue);
1882 assert(rvalue);
1883 assert(u);
1884
74051b9b
LP
1885 if (isempty(rvalue)) {
1886 /* Empty assignment resets the list */
1887 free(c->syscall_filter);
1888 c->syscall_filter = NULL;
1889 return 0;
1890 }
1891
8351ceae
LP
1892 if (rvalue[0] == '~') {
1893 invert = true;
1894 rvalue++;
1895 }
1896
1897 if (!c->syscall_filter) {
1898 size_t n;
1899
1900 n = (syscall_max() + 31) >> 4;
1901 c->syscall_filter = new(uint32_t, n);
1902 if (!c->syscall_filter)
74051b9b 1903 return log_oom();
8351ceae
LP
1904
1905 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1906
1907 /* Add these by default */
1908 syscall_set(c->syscall_filter, __NR_execve);
1909 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
1910#ifdef __NR_sigreturn
1911 syscall_set(c->syscall_filter, __NR_sigreturn);
1912#endif
1913 syscall_set(c->syscall_filter, __NR_exit_group);
1914 syscall_set(c->syscall_filter, __NR_exit);
1915 }
1916
1917 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1918 int id;
7fd1b19b 1919 _cleanup_free_ char *t = NULL;
8351ceae
LP
1920
1921 t = strndup(w, l);
1922 if (!t)
74051b9b 1923 return log_oom();
8351ceae
LP
1924
1925 id = syscall_from_name(t);
8351ceae 1926 if (id < 0) {
e8e581bf
ZJS
1927 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1928 "Failed to parse syscall, ignoring: %s", t);
8351ceae
LP
1929 continue;
1930 }
1931
1932 if (invert)
1933 syscall_unset(c->syscall_filter, id);
1934 else
1935 syscall_set(c->syscall_filter, id);
1936 }
1937
1938 c->no_new_privileges = true;
1939
1940 return 0;
1941}
1942
a016b922
LP
1943int config_parse_unit_slice(
1944 const char *unit,
1945 const char *filename,
1946 unsigned line,
1947 const char *section,
1948 const char *lvalue,
1949 int ltype,
1950 const char *rvalue,
1951 void *data,
1952 void *userdata) {
1953
1954 _cleanup_free_ char *k = NULL;
1955 Unit *u = userdata, *slice;
1956 int r;
1957
1958 assert(filename);
1959 assert(lvalue);
1960 assert(rvalue);
1961 assert(u);
1962
19f6d710
LP
1963 r = unit_name_printf(u, rvalue, &k);
1964 if (r < 0)
1965 log_syntax(unit, LOG_ERR, filename, line, -r,
a016b922 1966 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
19f6d710
LP
1967 if (!k) {
1968 k = strdup(rvalue);
1969 if (!k)
1970 return log_oom();
1971 }
a016b922 1972
19f6d710 1973 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922
LP
1974 if (r < 0) {
1975 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1976 "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
1977 return 0;
1978 }
1979
1980 if (slice->type != UNIT_SLICE) {
1981 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1982 "Slice unit %s is not a slice. Ignoring.", k);
a016b922
LP
1983 return 0;
1984 }
1985
1986 unit_ref_set(&u->slice, slice);
1987 return 0;
1988}
1989
4ad49000
LP
1990DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
1991
1992int config_parse_cpu_shares(
1993 const char *unit,
1994 const char *filename,
1995 unsigned line,
1996 const char *section,
1997 const char *lvalue,
1998 int ltype,
1999 const char *rvalue,
2000 void *data,
2001 void *userdata) {
2002
2003 CGroupContext *c = data;
2004 unsigned long lu;
2005 int r;
2006
2007 assert(filename);
2008 assert(lvalue);
2009 assert(rvalue);
2010
2011 if (isempty(rvalue)) {
2012 c->cpu_shares = 1024;
2013 return 0;
2014 }
2015
2016 r = safe_atolu(rvalue, &lu);
2017 if (r < 0 || lu <= 0) {
2018 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2019 "CPU shares '%s' invalid. Ignoring.", rvalue);
2020 return 0;
2021 }
2022
2023 c->cpu_shares = lu;
2024 return 0;
2025}
2026
2027int config_parse_memory_limit(
2028 const char *unit,
2029 const char *filename,
2030 unsigned line,
2031 const char *section,
2032 const char *lvalue,
2033 int ltype,
2034 const char *rvalue,
2035 void *data,
2036 void *userdata) {
2037
2038 CGroupContext *c = data;
2039 uint64_t *limit;
2040 off_t bytes;
2041 int r;
2042
2043 limit = streq(lvalue, "MemoryLimit") ? &c->memory_limit : &c->memory_soft_limit;
2044
2045 if (isempty(rvalue)) {
2046 *limit = (uint64_t) -1;
2047 return 0;
2048 }
2049
2050 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2051
2052 r = parse_bytes(rvalue, &bytes);
2053 if (r < 0) {
2054 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2055 "Memory limit '%s' invalid. Ignoring.", rvalue);
2056 return 0;
2057 }
2058
2059 *limit = (uint64_t) bytes;
2060 return 0;
2061}
2062
2063int config_parse_device_allow(
2064 const char *unit,
2065 const char *filename,
2066 unsigned line,
2067 const char *section,
2068 const char *lvalue,
2069 int ltype,
2070 const char *rvalue,
2071 void *data,
2072 void *userdata) {
2073
2074 _cleanup_free_ char *path = NULL;
2075 CGroupContext *c = data;
2076 CGroupDeviceAllow *a;
2077 const char *m;
2078 size_t n;
2079
2080 if (isempty(rvalue)) {
2081 while (c->device_allow)
2082 cgroup_context_free_device_allow(c, c->device_allow);
2083
2084 return 0;
2085 }
2086
2087 n = strcspn(rvalue, WHITESPACE);
2088 path = strndup(rvalue, n);
2089 if (!path)
2090 return log_oom();
2091
2092 if (!path_startswith(path, "/dev")) {
2093 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2094 "Invalid device node path '%s'. Ignoring.", path);
2095 return 0;
2096 }
2097
2098 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2099 if (isempty(m))
2100 m = "rwm";
2101
2102 if (!in_charset(m, "rwm")) {
2103 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2104 "Invalid device rights '%s'. Ignoring.", m);
2105 return 0;
2106 }
2107
2108 a = new0(CGroupDeviceAllow, 1);
2109 if (!a)
2110 return log_oom();
2111
2112 a->path = path;
2113 path = NULL;
2114 a->r = !!strchr(m, 'r');
2115 a->w = !!strchr(m, 'w');
2116 a->m = !!strchr(m, 'm');
2117
2118 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
2119 return 0;
2120}
2121
2122int config_parse_blockio_weight(
2123 const char *unit,
2124 const char *filename,
2125 unsigned line,
2126 const char *section,
2127 const char *lvalue,
2128 int ltype,
2129 const char *rvalue,
2130 void *data,
2131 void *userdata) {
2132
8e7076ca
LP
2133 CGroupContext *c = data;
2134 unsigned long lu;
2135 int r;
2136
2137 assert(filename);
2138 assert(lvalue);
2139 assert(rvalue);
2140
2141 if (isempty(rvalue)) {
2142 c->blockio_weight = 1000;
2143 return 0;
2144 }
2145
2146 r = safe_atolu(rvalue, &lu);
2147 if (r < 0 || lu < 10 || lu > 1000) {
2148 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2149 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2150 return 0;
2151 }
2152
2153 c->blockio_weight = lu;
2154
2155 return 0;
2156}
2157
2158int config_parse_blockio_device_weight(
2159 const char *unit,
2160 const char *filename,
2161 unsigned line,
2162 const char *section,
2163 const char *lvalue,
2164 int ltype,
2165 const char *rvalue,
2166 void *data,
2167 void *userdata) {
2168
4ad49000 2169 _cleanup_free_ char *path = NULL;
8e7076ca 2170 CGroupBlockIODeviceWeight *w;
4ad49000
LP
2171 CGroupContext *c = data;
2172 unsigned long lu;
2173 const char *weight;
2174 size_t n;
2175 int r;
2176
2177 assert(filename);
2178 assert(lvalue);
2179 assert(rvalue);
2180
2181 if (isempty(rvalue)) {
4ad49000
LP
2182 while (c->blockio_device_weights)
2183 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2184
2185 return 0;
2186 }
2187
2188 n = strcspn(rvalue, WHITESPACE);
2189 weight = rvalue + n;
8e7076ca
LP
2190 if (!*weight) {
2191 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2192 "Expected block device and device weight. Ignoring.");
2193 return 0;
2194 }
4ad49000 2195
8e7076ca
LP
2196 path = strndup(rvalue, n);
2197 if (!path)
2198 return log_oom();
4ad49000 2199
8e7076ca
LP
2200 if (!path_startswith(path, "/dev")) {
2201 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2202 "Invalid device node path '%s'. Ignoring.", path);
2203 return 0;
2204 }
4ad49000 2205
8e7076ca 2206 weight += strspn(weight, WHITESPACE);
4ad49000
LP
2207 r = safe_atolu(weight, &lu);
2208 if (r < 0 || lu < 10 || lu > 1000) {
2209 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2210 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2211 return 0;
2212 }
2213
4ad49000 2214
8e7076ca
LP
2215 w = new0(CGroupBlockIODeviceWeight, 1);
2216 if (!w)
2217 return log_oom();
4ad49000 2218
8e7076ca
LP
2219 w->path = path;
2220 path = NULL;
4ad49000 2221
8e7076ca 2222 w->weight = lu;
4ad49000 2223
8e7076ca 2224 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
4ad49000
LP
2225 return 0;
2226}
2227
2228int config_parse_blockio_bandwidth(
2229 const char *unit,
2230 const char *filename,
2231 unsigned line,
2232 const char *section,
2233 const char *lvalue,
2234 int ltype,
2235 const char *rvalue,
2236 void *data,
2237 void *userdata) {
2238
2239 _cleanup_free_ char *path = NULL;
2240 CGroupBlockIODeviceBandwidth *b;
2241 CGroupContext *c = data;
2242 const char *bandwidth;
2243 off_t bytes;
47c0980d 2244 bool read;
4ad49000
LP
2245 size_t n;
2246 int r;
2247
2248 assert(filename);
2249 assert(lvalue);
2250 assert(rvalue);
2251
47c0980d
G
2252 read = streq("BlockIOReadBandwidth", lvalue);
2253
4ad49000 2254 if (isempty(rvalue)) {
47c0980d
G
2255 CGroupBlockIODeviceBandwidth *next;
2256
2257 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2258 if (b->read == read)
2259 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
2260
2261 return 0;
2262 }
2263
2264 n = strcspn(rvalue, WHITESPACE);
2265 bandwidth = rvalue + n;
2266 bandwidth += strspn(bandwidth, WHITESPACE);
2267
2268 if (!*bandwidth) {
2269 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2270 "Expected space separated pair of device node and bandwidth. Ignoring.");
2271 return 0;
2272 }
2273
2274 path = strndup(rvalue, n);
2275 if (!path)
2276 return log_oom();
2277
2278 if (!path_startswith(path, "/dev")) {
2279 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2280 "Invalid device node path '%s'. Ignoring.", path);
2281 return 0;
2282 }
2283
2284 r = parse_bytes(bandwidth, &bytes);
2285 if (r < 0 || bytes <= 0) {
2286 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2287 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2288 return 0;
2289 }
2290
2291 b = new0(CGroupBlockIODeviceBandwidth, 1);
2292 if (!b)
2293 return log_oom();
2294
2295 b->path = path;
2296 path = NULL;
2297 b->bandwidth = (uint64_t) bytes;
47c0980d 2298 b->read = read;
4ad49000
LP
2299
2300 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
2301
2302 return 0;
2303}
2304
071830ff 2305#define FOLLOW_MAX 8
87f0e418 2306
9e2f7c11 2307static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 2308 unsigned c = 0;
87f0e418
LP
2309 int fd, r;
2310 FILE *f;
0301abf4 2311 char *id = NULL;
87f0e418
LP
2312
2313 assert(filename);
2314 assert(*filename);
2315 assert(_f);
2316 assert(names);
2317
0301abf4
LP
2318 /* This will update the filename pointer if the loaded file is
2319 * reached by a symlink. The old string will be freed. */
87f0e418 2320
0301abf4 2321 for (;;) {
2c7108c4 2322 char *target, *name;
87f0e418 2323
0301abf4
LP
2324 if (c++ >= FOLLOW_MAX)
2325 return -ELOOP;
2326
b08d03ff
LP
2327 path_kill_slashes(*filename);
2328
87f0e418 2329 /* Add the file name we are currently looking at to
8f05424d
LP
2330 * the names of this unit, but only if it is a valid
2331 * unit name. */
9eb977db 2332 name = path_get_file_name(*filename);
87f0e418 2333
15e11d81 2334 if (unit_name_is_valid(name, true)) {
8f05424d 2335
15e11d81
LP
2336 id = set_get(names, name);
2337 if (!id) {
2338 id = strdup(name);
2339 if (!id)
8f05424d 2340 return -ENOMEM;
87f0e418 2341
ef42202a
ZJS
2342 r = set_consume(names, id);
2343 if (r < 0)
8f05424d 2344 return r;
87f0e418 2345 }
87f0e418
LP
2346 }
2347
0301abf4 2348 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
2349 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2350 if (fd >= 0)
87f0e418
LP
2351 break;
2352
0301abf4
LP
2353 if (errno != ELOOP)
2354 return -errno;
2355
87f0e418 2356 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
2357 r = readlink_and_make_absolute(*filename, &target);
2358 if (r < 0)
0301abf4 2359 return r;
87f0e418 2360
0301abf4 2361 free(*filename);
2c7108c4 2362 *filename = target;
87f0e418
LP
2363 }
2364
9946996c
LP
2365 f = fdopen(fd, "re");
2366 if (!f) {
87f0e418 2367 r = -errno;
9e2f7c11 2368 close_nointr_nofail(fd);
0301abf4 2369 return r;
87f0e418
LP
2370 }
2371
2372 *_f = f;
9e2f7c11 2373 *_final = id;
0301abf4 2374 return 0;
87f0e418
LP
2375}
2376
23a177ef
LP
2377static int merge_by_names(Unit **u, Set *names, const char *id) {
2378 char *k;
2379 int r;
2380
2381 assert(u);
2382 assert(*u);
2383 assert(names);
2384
2385 /* Let's try to add in all symlink names we found */
2386 while ((k = set_steal_first(names))) {
2387
2388 /* First try to merge in the other name into our
2389 * unit */
9946996c
LP
2390 r = unit_merge_by_name(*u, k);
2391 if (r < 0) {
23a177ef
LP
2392 Unit *other;
2393
2394 /* Hmm, we couldn't merge the other unit into
2395 * ours? Then let's try it the other way
2396 * round */
2397
ac155bb8 2398 other = manager_get_unit((*u)->manager, k);
23a177ef
LP
2399 free(k);
2400
9946996c
LP
2401 if (other) {
2402 r = unit_merge(other, *u);
2403 if (r >= 0) {
23a177ef
LP
2404 *u = other;
2405 return merge_by_names(u, names, NULL);
2406 }
9946996c 2407 }
23a177ef
LP
2408
2409 return r;
2410 }
2411
2412 if (id == k)
2413 unit_choose_id(*u, id);
2414
2415 free(k);
2416 }
2417
2418 return 0;
2419}
2420
e537352b 2421static int load_from_path(Unit *u, const char *path) {
0301abf4 2422 int r;
87f0e418 2423 Set *symlink_names;
23a177ef
LP
2424 FILE *f = NULL;
2425 char *filename = NULL, *id = NULL;
2426 Unit *merged;
45fb0699 2427 struct stat st;
23a177ef
LP
2428
2429 assert(u);
e537352b 2430 assert(path);
3efd4195 2431
f975e971
LP
2432 symlink_names = set_new(string_hash_func, string_compare_func);
2433 if (!symlink_names)
87f0e418 2434 return -ENOMEM;
3efd4195 2435
036643a2
LP
2436 if (path_is_absolute(path)) {
2437
9946996c
LP
2438 filename = strdup(path);
2439 if (!filename) {
036643a2
LP
2440 r = -ENOMEM;
2441 goto finish;
2442 }
2443
9946996c
LP
2444 r = open_follow(&filename, &f, symlink_names, &id);
2445 if (r < 0) {
036643a2
LP
2446 free(filename);
2447 filename = NULL;
2448
2449 if (r != -ENOENT)
2450 goto finish;
2451 }
2452
2453 } else {
2454 char **p;
2455
ac155bb8 2456 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
036643a2
LP
2457
2458 /* Instead of opening the path right away, we manually
2459 * follow all symlinks and add their name to our unit
2460 * name set while doing so */
9946996c
LP
2461 filename = path_make_absolute(path, *p);
2462 if (!filename) {
036643a2
LP
2463 r = -ENOMEM;
2464 goto finish;
2465 }
2466
ac155bb8
MS
2467 if (u->manager->unit_path_cache &&
2468 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
2469 r = -ENOENT;
2470 else
2471 r = open_follow(&filename, &f, symlink_names, &id);
2472
2473 if (r < 0) {
036643a2
LP
2474 free(filename);
2475 filename = NULL;
2476
2477 if (r != -ENOENT)
2478 goto finish;
2479
2480 /* Empty the symlink names for the next run */
9946996c 2481 set_clear_free(symlink_names);
036643a2
LP
2482 continue;
2483 }
2484
2485 break;
2486 }
2487 }
034c6ed7 2488
036643a2 2489 if (!filename) {
8f05424d 2490 /* Hmm, no suitable file found? */
23a177ef 2491 r = 0;
0301abf4
LP
2492 goto finish;
2493 }
87f0e418 2494
23a177ef 2495 merged = u;
9946996c
LP
2496 r = merge_by_names(&merged, symlink_names, id);
2497 if (r < 0)
0301abf4 2498 goto finish;
87f0e418 2499
23a177ef 2500 if (merged != u) {
ac155bb8 2501 u->load_state = UNIT_MERGED;
23a177ef
LP
2502 r = 0;
2503 goto finish;
034c6ed7
LP
2504 }
2505
45fb0699
LP
2506 if (fstat(fileno(f), &st) < 0) {
2507 r = -errno;
2508 goto finish;
2509 }
2510
00dc5d76 2511 if (null_or_empty(&st))
ac155bb8 2512 u->load_state = UNIT_MASKED;
00dc5d76 2513 else {
c2756a68
LP
2514 u->load_state = UNIT_LOADED;
2515
00dc5d76 2516 /* Now, parse the file contents */
e8e581bf
ZJS
2517 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2518 config_item_perf_lookup,
db5c0122 2519 (void*) load_fragment_gperf_lookup, false, true, u);
f975e971 2520 if (r < 0)
00dc5d76 2521 goto finish;
00dc5d76 2522 }
b08d03ff 2523
ac155bb8
MS
2524 free(u->fragment_path);
2525 u->fragment_path = filename;
0301abf4 2526 filename = NULL;
87f0e418 2527
ac155bb8 2528 u->fragment_mtime = timespec_load(&st.st_mtim);
45fb0699 2529
1b64d026
LP
2530 if (u->source_path) {
2531 if (stat(u->source_path, &st) >= 0)
2532 u->source_mtime = timespec_load(&st.st_mtim);
2533 else
2534 u->source_mtime = 0;
2535 }
2536
23a177ef 2537 r = 0;
87f0e418
LP
2538
2539finish:
53ec43c6 2540 set_free_free(symlink_names);
0301abf4
LP
2541 free(filename);
2542
23a177ef
LP
2543 if (f)
2544 fclose(f);
2545
0301abf4
LP
2546 return r;
2547}
2548
e537352b 2549int unit_load_fragment(Unit *u) {
23a177ef 2550 int r;
294d81f1
LP
2551 Iterator i;
2552 const char *t;
0301abf4
LP
2553
2554 assert(u);
ac155bb8
MS
2555 assert(u->load_state == UNIT_STUB);
2556 assert(u->id);
23a177ef 2557
294d81f1
LP
2558 /* First, try to find the unit under its id. We always look
2559 * for unit files in the default directories, to make it easy
2560 * to override things by placing things in /etc/systemd/system */
9946996c
LP
2561 r = load_from_path(u, u->id);
2562 if (r < 0)
294d81f1
LP
2563 return r;
2564
2565 /* Try to find an alias we can load this with */
ac155bb8
MS
2566 if (u->load_state == UNIT_STUB)
2567 SET_FOREACH(t, u->names, i) {
294d81f1 2568
ac155bb8 2569 if (t == u->id)
294d81f1
LP
2570 continue;
2571
9946996c
LP
2572 r = load_from_path(u, t);
2573 if (r < 0)
294d81f1
LP
2574 return r;
2575
ac155bb8 2576 if (u->load_state != UNIT_STUB)
294d81f1
LP
2577 break;
2578 }
23a177ef 2579
294d81f1 2580 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 2581 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 2582
9946996c
LP
2583 r = load_from_path(u, u->fragment_path);
2584 if (r < 0)
23a177ef 2585 return r;
0301abf4 2586
ac155bb8 2587 if (u->load_state == UNIT_STUB) {
6ccb1b44
LP
2588 /* Hmm, this didn't work? Then let's get rid
2589 * of the fragment path stored for us, so that
2590 * we don't point to an invalid location. */
ac155bb8
MS
2591 free(u->fragment_path);
2592 u->fragment_path = NULL;
6ccb1b44
LP
2593 }
2594 }
2595
294d81f1 2596 /* Look for a template */
ac155bb8 2597 if (u->load_state == UNIT_STUB && u->instance) {
294d81f1
LP
2598 char *k;
2599
9946996c
LP
2600 k = unit_name_template(u->id);
2601 if (!k)
294d81f1
LP
2602 return -ENOMEM;
2603
2604 r = load_from_path(u, k);
2605 free(k);
0301abf4 2606
294d81f1 2607 if (r < 0)
9e2f7c11 2608 return r;
890f434c 2609
ac155bb8
MS
2610 if (u->load_state == UNIT_STUB)
2611 SET_FOREACH(t, u->names, i) {
87f0e418 2612
ac155bb8 2613 if (t == u->id)
23a177ef 2614 continue;
071830ff 2615
9946996c
LP
2616 k = unit_name_template(t);
2617 if (!k)
294d81f1
LP
2618 return -ENOMEM;
2619
2620 r = load_from_path(u, k);
2621 free(k);
2622
2623 if (r < 0)
23a177ef 2624 return r;
890f434c 2625
ac155bb8 2626 if (u->load_state != UNIT_STUB)
23a177ef
LP
2627 break;
2628 }
071830ff
LP
2629 }
2630
23a177ef 2631 return 0;
3efd4195 2632}
e537352b
LP
2633
2634void unit_dump_config_items(FILE *f) {
f975e971
LP
2635 static const struct {
2636 const ConfigParserCallback callback;
2637 const char *rvalue;
2638 } table[] = {
2639 { config_parse_int, "INTEGER" },
2640 { config_parse_unsigned, "UNSIGNED" },
9ba1a159 2641 { config_parse_bytes_size, "SIZE" },
f975e971
LP
2642 { config_parse_bool, "BOOLEAN" },
2643 { config_parse_string, "STRING" },
2644 { config_parse_path, "PATH" },
2645 { config_parse_unit_path_printf, "PATH" },
2646 { config_parse_strv, "STRING [...]" },
2647 { config_parse_exec_nice, "NICE" },
2648 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2649 { config_parse_exec_io_class, "IOCLASS" },
2650 { config_parse_exec_io_priority, "IOPRIORITY" },
2651 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2652 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2653 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2654 { config_parse_mode, "MODE" },
2655 { config_parse_unit_env_file, "FILE" },
2656 { config_parse_output, "OUTPUT" },
2657 { config_parse_input, "INPUT" },
2658 { config_parse_facility, "FACILITY" },
2659 { config_parse_level, "LEVEL" },
2660 { config_parse_exec_capabilities, "CAPABILITIES" },
2661 { config_parse_exec_secure_bits, "SECUREBITS" },
ec8927ca 2662 { config_parse_bounding_set, "BOUNDINGSET" },
f975e971 2663 { config_parse_limit, "LIMIT" },
f975e971 2664 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
2665 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2666 { config_parse_service_type, "SERVICETYPE" },
2667 { config_parse_service_restart, "SERVICERESTART" },
2668#ifdef HAVE_SYSV_COMPAT
2669 { config_parse_sysv_priority, "SYSVPRIORITY" },
2670#else
2671 { config_parse_warn_compat, "NOTSUPPORTED" },
2672#endif
2673 { config_parse_kill_mode, "KILLMODE" },
2674 { config_parse_kill_signal, "SIGNAL" },
2675 { config_parse_socket_listen, "SOCKET [...]" },
2676 { config_parse_socket_bind, "SOCKETBIND" },
2677 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 2678 { config_parse_sec, "SECONDS" },
d88a251b 2679 { config_parse_nsec, "NANOSECONDS" },
f975e971 2680 { config_parse_path_strv, "PATH [...]" },
7c8fa05c 2681 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
2682 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2683 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 2684 { config_parse_trigger_unit, "UNIT" },
f975e971 2685 { config_parse_timer, "TIMER" },
f975e971 2686 { config_parse_path_spec, "PATH" },
f975e971
LP
2687 { config_parse_notify_access, "ACCESS" },
2688 { config_parse_ip_tos, "TOS" },
2689 { config_parse_unit_condition_path, "CONDITION" },
2690 { config_parse_unit_condition_string, "CONDITION" },
2691 { config_parse_unit_condition_null, "CONDITION" },
a016b922 2692 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
2693 { config_parse_documentation, "URL" },
2694 { config_parse_service_timeout, "SECONDS" },
2695 { config_parse_start_limit_action, "ACTION" },
2696 { config_parse_set_status, "STATUS" },
2697 { config_parse_service_sockets, "SOCKETS" },
2698 { config_parse_fsck_passno, "PASSNO" },
2699 { config_parse_environ, "ENVIRON" },
2700 { config_parse_syscall_filter, "SYSCALL" },
2701 { config_parse_cpu_shares, "SHARES" },
2702 { config_parse_memory_limit, "LIMIT" },
2703 { config_parse_device_allow, "DEVICE" },
2704 { config_parse_device_policy, "POLICY" },
2705 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2706 { config_parse_blockio_weight, "WEIGHT" },
2707 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2708 { config_parse_long, "LONG" },
2709 { config_parse_socket_service, "SERVICE" },
f975e971
LP
2710 };
2711
2712 const char *prev = NULL;
2713 const char *i;
2714
2715 assert(f);
e537352b 2716
f975e971
LP
2717 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2718 const char *rvalue = "OTHER", *lvalue;
2719 unsigned j;
2720 size_t prefix_len;
2721 const char *dot;
2722 const ConfigPerfItem *p;
2723
2724 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2725
2726 dot = strchr(i, '.');
2727 lvalue = dot ? dot + 1 : i;
2728 prefix_len = dot-i;
2729
2730 if (dot)
641906e9 2731 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
2732 if (prev)
2733 fputc('\n', f);
2734
2735 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2736 }
2737
2738 for (j = 0; j < ELEMENTSOF(table); j++)
2739 if (p->parse == table[j].callback) {
2740 rvalue = table[j].rvalue;
2741 break;
2742 }
2743
2744 fprintf(f, "%s=%s\n", lvalue, rvalue);
2745 prev = i;
2746 }
e537352b 2747}