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