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