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