chiark / gitweb /
implement trivial socket activated logger daemon
[elogind.git] / load-fragment.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <linux/oom.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9
10 #include "unit.h"
11 #include "strv.h"
12 #include "conf-parser.h"
13 #include "load-fragment.h"
14 #include "log.h"
15
16 static int config_parse_deps(
17                 const char *filename,
18                 unsigned line,
19                 const char *section,
20                 const char *lvalue,
21                 const char *rvalue,
22                 void *data,
23                 void *userdata) {
24
25         UnitDependency d = PTR_TO_UINT(data);
26         Unit *u = userdata;
27         char *w;
28         size_t l;
29         char *state;
30
31         assert(filename);
32         assert(lvalue);
33         assert(rvalue);
34
35         FOREACH_WORD(w, &l, rvalue, state) {
36                 char *t;
37                 int r;
38                 Unit *other;
39
40                 if (!(t = strndup(w, l)))
41                         return -ENOMEM;
42
43                 r = manager_load_unit(u->meta.manager, t, &other);
44                 free(t);
45
46                 if (r < 0)
47                         return r;
48
49                 if ((r = unit_add_dependency(u, d, other)) < 0)
50                         return r;
51         }
52
53         return 0;
54 }
55
56 static int config_parse_names(
57                 const char *filename,
58                 unsigned line,
59                 const char *section,
60                 const char *lvalue,
61                 const char *rvalue,
62                 void *data,
63                 void *userdata) {
64
65         Unit *u = userdata;
66         char *w;
67         size_t l;
68         char *state;
69
70         assert(filename);
71         assert(lvalue);
72         assert(rvalue);
73         assert(data);
74
75         FOREACH_WORD(w, &l, rvalue, state) {
76                 char *t;
77                 int r;
78                 Unit *other;
79
80                 if (!(t = strndup(w, l)))
81                         return -ENOMEM;
82
83                 other = manager_get_unit(u->meta.manager, t);
84
85                 if (other) {
86
87                         if (other != u) {
88
89                                 if (other->meta.load_state != UNIT_STUB) {
90                                         free(t);
91                                         return -EEXIST;
92                                 }
93
94                                 if ((r = unit_merge(u, other)) < 0) {
95                                         free(t);
96                                         return r;
97                                 }
98                         }
99
100                 } else {
101                         if ((r = unit_add_name(u, t)) < 0) {
102                                 free(t);
103                                 return r;
104                         }
105                 }
106
107                 free(t);
108         }
109
110         return 0;
111 }
112
113 static int config_parse_listen(
114                 const char *filename,
115                 unsigned line,
116                 const char *section,
117                 const char *lvalue,
118                 const char *rvalue,
119                 void *data,
120                 void *userdata) {
121
122         int r;
123         SocketPort *p;
124         Socket *s;
125
126         assert(filename);
127         assert(lvalue);
128         assert(rvalue);
129         assert(data);
130
131         s = (Socket*) data;
132
133         if (!(p = new0(SocketPort, 1)))
134                 return -ENOMEM;
135
136         if (streq(lvalue, "ListenFIFO")) {
137                 p->type = SOCKET_FIFO;
138
139                 if (!(p->path = strdup(rvalue))) {
140                         free(p);
141                         return -ENOMEM;
142                 }
143         } else {
144                 p->type = SOCKET_SOCKET;
145
146                 if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
147                         log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
148                         free(p);
149                         return r;
150                 }
151
152                 if (streq(lvalue, "ListenStream"))
153                         p->address.type = SOCK_STREAM;
154                 else if (streq(lvalue, "ListenDatagram"))
155                         p->address.type = SOCK_DGRAM;
156                 else {
157                         assert(streq(lvalue, "ListenSequentialPacket"));
158                         p->address.type = SOCK_SEQPACKET;
159                 }
160
161                 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
162                         free(p);
163                         return -EPROTONOSUPPORT;
164                 }
165         }
166
167         p->fd = -1;
168         LIST_PREPEND(SocketPort, port, s->ports, p);
169
170         return 0;
171 }
172
173 static int config_parse_socket_bind(
174                 const char *filename,
175                 unsigned line,
176                 const char *section,
177                 const char *lvalue,
178                 const char *rvalue,
179                 void *data,
180                 void *userdata) {
181
182         int r;
183         Socket *s;
184
185         assert(filename);
186         assert(lvalue);
187         assert(rvalue);
188         assert(data);
189
190         s = (Socket*) data;
191
192         if ((r = parse_boolean(rvalue)) < 0) {
193                 log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue);
194                 return r;
195         }
196
197         s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
198
199         return 0;
200 }
201
202 static int config_parse_nice(
203                 const char *filename,
204                 unsigned line,
205                 const char *section,
206                 const char *lvalue,
207                 const char *rvalue,
208                 void *data,
209                 void *userdata) {
210
211         int *i = data, priority, r;
212
213         assert(filename);
214         assert(lvalue);
215         assert(rvalue);
216         assert(data);
217
218         if ((r = safe_atoi(rvalue, &priority)) < 0) {
219                 log_error("[%s:%u] Failed to parse nice priority: %s", filename, line, rvalue);
220                 return r;
221         }
222
223         if (priority < PRIO_MIN || priority >= PRIO_MAX) {
224                 log_error("[%s:%u] Nice priority out of range: %s", filename, line, rvalue);
225                 return -ERANGE;
226         }
227
228         *i = priority;
229         return 0;
230 }
231
232 static int config_parse_oom_adjust(
233                 const char *filename,
234                 unsigned line,
235                 const char *section,
236                 const char *lvalue,
237                 const char *rvalue,
238                 void *data,
239                 void *userdata) {
240
241         int *i = data, oa, r;
242
243         assert(filename);
244         assert(lvalue);
245         assert(rvalue);
246         assert(data);
247
248         if ((r = safe_atoi(rvalue, &oa)) < 0) {
249                 log_error("[%s:%u] Failed to parse OOM adjust value: %s", filename, line, rvalue);
250                 return r;
251         }
252
253         if (oa < OOM_DISABLE || oa > OOM_ADJUST_MAX) {
254                 log_error("[%s:%u] OOM adjust value out of range: %s", filename, line, rvalue);
255                 return -ERANGE;
256         }
257
258         *i = oa;
259         return 0;
260 }
261
262 static int config_parse_umask(
263                 const char *filename,
264                 unsigned line,
265                 const char *section,
266                 const char *lvalue,
267                 const char *rvalue,
268                 void *data,
269                 void *userdata) {
270
271         mode_t *m = data;
272         long l;
273         char *x = NULL;
274
275         assert(filename);
276         assert(lvalue);
277         assert(rvalue);
278         assert(data);
279
280         errno = 0;
281         l = strtol(rvalue, &x, 8);
282         if (!x || *x || errno) {
283                 log_error("[%s:%u] Failed to parse umask value: %s", filename, line, rvalue);
284                 return errno ? -errno : -EINVAL;
285         }
286
287         if (l < 0000 || l > 0777) {
288                 log_error("[%s:%u] umask value out of range: %s", filename, line, rvalue);
289                 return -ERANGE;
290         }
291
292         *m = (mode_t) l;
293         return 0;
294 }
295
296 static int config_parse_exec(
297                 const char *filename,
298                 unsigned line,
299                 const char *section,
300                 const char *lvalue,
301                 const char *rvalue,
302                 void *data,
303                 void *userdata) {
304
305         ExecCommand **e = data, *ee, *nce = NULL;
306         char **n;
307         char *w;
308         unsigned k;
309         size_t l;
310         char *state;
311
312         assert(filename);
313         assert(lvalue);
314         assert(rvalue);
315         assert(data);
316
317         k = 0;
318         FOREACH_WORD_QUOTED(w, l, rvalue, state)
319                 k++;
320
321         if (!(n = new(char*, k+1)))
322                 return -ENOMEM;
323
324         k = 0;
325         FOREACH_WORD_QUOTED(w, l, rvalue, state)
326                 if (!(n[k++] = strndup(w, l)))
327                         goto fail;
328
329         n[k] = NULL;
330
331         if (!n[0] || !path_is_absolute(n[0])) {
332                 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
333                 strv_free(n);
334                 return -EINVAL;
335         }
336
337         if (!(nce = new0(ExecCommand, 1)))
338                 goto fail;
339
340         nce->argv = n;
341         if (!(nce->path = strdup(n[0])))
342                 goto fail;
343
344         if (*e) {
345                 /* It's kinda important that we keep the order here */
346                 LIST_FIND_TAIL(ExecCommand, command, *e, ee);
347                 LIST_INSERT_AFTER(ExecCommand, command, *e, ee, nce);
348         } else
349                 *e = nce;
350
351         return 0;
352
353 fail:
354         for (; k > 0; k--)
355                 free(n[k-1]);
356         free(n);
357
358         free(nce);
359
360         return -ENOMEM;
361 }
362
363 static int config_parse_usec(
364                 const char *filename,
365                 unsigned line,
366                 const char *section,
367                 const char *lvalue,
368                 const char *rvalue,
369                 void *data,
370                 void *userdata) {
371
372         usec_t *usec = data;
373         unsigned long long u;
374         int r;
375
376         assert(filename);
377         assert(lvalue);
378         assert(rvalue);
379         assert(data);
380
381         if ((r = safe_atollu(rvalue, &u)) < 0) {
382                 log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue);
383                 return r;
384         }
385
386         /* We actually assume the user configures seconds. Later on we
387          * might choose to support suffixes for time values, to
388          * configure bigger or smaller units */
389
390         *usec = u * USEC_PER_SEC;
391
392         return 0;
393 }
394
395 static int config_parse_service_type(
396                 const char *filename,
397                 unsigned line,
398                 const char *section,
399                 const char *lvalue,
400                 const char *rvalue,
401                 void *data,
402                 void *userdata) {
403
404         Service *s = data;
405
406         assert(filename);
407         assert(lvalue);
408         assert(rvalue);
409         assert(data);
410
411         if (streq(rvalue, "forking"))
412                 s->type = SERVICE_FORKING;
413         else if (streq(rvalue, "simple"))
414                 s->type = SERVICE_SIMPLE;
415         else {
416                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
417                 return -EBADMSG;
418         }
419
420         return 0;
421 }
422
423 static int config_parse_service_restart(
424                 const char *filename,
425                 unsigned line,
426                 const char *section,
427                 const char *lvalue,
428                 const char *rvalue,
429                 void *data,
430                 void *userdata) {
431
432         Service *s = data;
433
434         assert(filename);
435         assert(lvalue);
436         assert(rvalue);
437         assert(data);
438
439         if (streq(rvalue, "once"))
440                 s->restart = SERVICE_ONCE;
441         else if (streq(rvalue, "on-success"))
442                 s->type = SERVICE_RESTART_ON_SUCCESS;
443         else if (streq(rvalue, "always"))
444                 s->type = SERVICE_RESTART_ALWAYS;
445         else {
446                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
447                 return -EBADMSG;
448         }
449
450         return 0;
451 }
452
453 int config_parse_bindtodevice(
454                 const char *filename,
455                 unsigned line,
456                 const char *section,
457                 const char *lvalue,
458                 const char *rvalue,
459                 void *data,
460                 void *userdata) {
461
462         Socket *s = data;
463         char *n;
464
465         assert(filename);
466         assert(lvalue);
467         assert(rvalue);
468         assert(data);
469
470         if (rvalue[0] && !streq(rvalue, "*")) {
471                 if (!(n = strdup(rvalue)))
472                         return -ENOMEM;
473         } else
474                 n = NULL;
475
476         free(s->bind_to_device);
477         s->bind_to_device = n;
478
479         return 0;
480 }
481
482 #define FOLLOW_MAX 8
483
484
485 static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
486         unsigned c = 0;
487         int fd, r;
488         FILE *f;
489         char *id = NULL;
490
491         assert(filename);
492         assert(*filename);
493         assert(_f);
494         assert(names);
495
496         /* This will update the filename pointer if the loaded file is
497          * reached by a symlink. The old string will be freed. */
498
499         for (;;) {
500                 char *target, *k, *name;
501
502                 if (c++ >= FOLLOW_MAX)
503                         return -ELOOP;
504
505                 /* Add the file name we are currently looking at to
506                  * the names of this unit */
507                 name = file_name_from_path(*filename);
508                 if (!(id = set_get(names, name))) {
509
510                         if (!(id = strdup(name)))
511                                 return -ENOMEM;
512
513                         if ((r = set_put(names, id)) < 0) {
514                                 free(id);
515                                 return r;
516                         }
517                 }
518
519                 /* Try to open the file name, but don't if its a symlink */
520                 if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
521                         break;
522
523                 if (errno != ELOOP)
524                         return -errno;
525
526                 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
527                 if ((r = readlink_malloc(*filename, &target)) < 0)
528                         return r;
529
530                 k = file_in_same_dir(*filename, target);
531                 free(target);
532
533                 if (!k)
534                         return -ENOMEM;
535
536                 free(*filename);
537                 *filename = k;
538         }
539
540         if (!(f = fdopen(fd, "r"))) {
541                 r = -errno;
542                 assert(close_nointr(fd) == 0);
543                 return r;
544         }
545
546         *_f = f;
547         *_id = id;
548         return 0;
549 }
550
551 static int load_from_path(Unit *u, const char *path) {
552
553         static const char* const section_table[_UNIT_TYPE_MAX] = {
554                 [UNIT_SERVICE]   = "Service",
555                 [UNIT_TIMER]     = "Timer",
556                 [UNIT_SOCKET]    = "Socket",
557                 [UNIT_TARGET]    = "Target",
558                 [UNIT_DEVICE]    = "Device",
559                 [UNIT_MOUNT]     = "Mount",
560                 [UNIT_AUTOMOUNT] = "Automount",
561                 [UNIT_SNAPSHOT]  = "Snapshot"
562         };
563
564 #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
565                 { "Directory",              config_parse_path,            &(context).directory,                            section   }, \
566                 { "User",                   config_parse_string,          &(context).user,                                 section   }, \
567                 { "Group",                  config_parse_string,          &(context).group,                                section   }, \
568                 { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                 section   }, \
569                 { "Nice",                   config_parse_nice,            &(context).nice,                                 section   }, \
570                 { "OOMAdjust",              config_parse_oom_adjust,      &(context).oom_adjust,                           section   }, \
571                 { "UMask",                  config_parse_umask,           &(context).umask,                                section   }, \
572                 { "Environment",            config_parse_strv,            &(context).environment,                          section   }
573
574         const ConfigItem items[] = {
575                 { "Names",                  config_parse_names,           u,                                               "Meta"    },
576                 { "Description",            config_parse_string,          &u->meta.description,                            "Meta"    },
577                 { "Requires",               config_parse_deps,            UINT_TO_PTR(UNIT_REQUIRES),                      "Meta"    },
578                 { "SoftRequires",           config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUIRES),                 "Meta"    },
579                 { "Wants",                  config_parse_deps,            UINT_TO_PTR(UNIT_WANTS),                         "Meta"    },
580                 { "Requisite",              config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE),                     "Meta"    },
581                 { "SoftRequisite",          config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUISITE),                "Meta"    },
582                 { "Conflicts",              config_parse_deps,            UINT_TO_PTR(UNIT_CONFLICTS),                     "Meta"    },
583                 { "Before",                 config_parse_deps,            UINT_TO_PTR(UNIT_BEFORE),                        "Meta"    },
584                 { "After",                  config_parse_deps,            UINT_TO_PTR(UNIT_AFTER),                         "Meta"    },
585
586                 { "PIDFile",                config_parse_path,            &u->service.pid_file,                            "Service" },
587                 { "ExecStartPre",           config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START_PRE,  "Service" },
588                 { "ExecStart",              config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START,      "Service" },
589                 { "ExecStartPost",          config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
590                 { "ExecReload",             config_parse_exec,            u->service.exec_command+SERVICE_EXEC_RELOAD,     "Service" },
591                 { "ExecStop",               config_parse_exec,            u->service.exec_command+SERVICE_EXEC_STOP,       "Service" },
592                 { "ExecStopPost",           config_parse_exec,            u->service.exec_command+SERVICE_EXEC_STOP_POST,  "Service" },
593                 { "RestartSec",             config_parse_usec,            &u->service.restart_usec,                        "Service" },
594                 { "TimeoutSec",             config_parse_usec,            &u->service.timeout_usec,                        "Service" },
595                 { "Type",                   config_parse_service_type,    &u->service,                                     "Service" },
596                 { "Restart",                config_parse_service_restart, &u->service,                                     "Service" },
597                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
598
599                 { "ListenStream",           config_parse_listen,          &u->socket,                                      "Socket"  },
600                 { "ListenDatagram",         config_parse_listen,          &u->socket,                                      "Socket"  },
601                 { "ListenSequentialPacket", config_parse_listen,          &u->socket,                                      "Socket"  },
602                 { "ListenFIFO",             config_parse_listen,          &u->socket,                                      "Socket"  },
603                 { "BindIPv6Only",           config_parse_socket_bind,     &u->socket,                                      "Socket"  },
604                 { "Backlog",                config_parse_unsigned,        &u->socket.backlog,                              "Socket"  },
605                 { "BindToDevice",           config_parse_bindtodevice,    &u->socket,                                      "Socket"  },
606                 { "ExecStartPre",           config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_START_PRE,    "Socket"  },
607                 { "ExecStartPost",          config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_START_POST,   "Socket"  },
608                 { "ExecStopPre",            config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_STOP_PRE,     "Socket"  },
609                 { "ExecStopPost",           config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_STOP_POST,    "Socket"  },
610                 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
611
612                 EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
613
614                 { NULL, NULL, NULL, NULL }
615         };
616
617 #undef EXEC_CONTEXT_CONFIG_ITEMS
618
619         const char *sections[3];
620         char *k;
621         int r;
622         Set *symlink_names;
623         FILE *f;
624         char *filename, *id;
625
626         sections[0] = "Meta";
627         sections[1] = section_table[u->meta.type];
628         sections[2] = NULL;
629
630         if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
631                 return -ENOMEM;
632
633         /* Instead of opening the path right away, we manually
634          * follow all symlinks and add their name to our unit
635          * name set while doing so */
636         if (!(filename = path_make_absolute(path, unit_path()))) {
637                 r = -ENOMEM;
638                 goto finish;
639         }
640
641         if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
642                 if (r == -ENOENT)
643                         r = 0; /* returning 0 means: no suitable config file found */
644
645                 goto finish;
646         }
647
648         /* Now, parse the file contents */
649         r = config_parse(filename, f, sections, items, u);
650         if (r < 0)
651                 goto finish;
652
653         /* Let's try to add in all symlink names we found */
654         while ((k = set_steal_first(symlink_names))) {
655                 if ((r = unit_add_name(u, k)) < 0)
656                         goto finish;
657
658                 if (id == k)
659                         assert_se(u->meta.id = set_get(u->meta.names, k));
660
661                 free(k);
662         }
663
664         free(u->meta.load_path);
665         u->meta.load_path = filename;
666         filename = NULL;
667
668         r = 1; /* returning 1 means: suitable config file found and loaded */
669
670 finish:
671         while ((k = set_steal_first(symlink_names)))
672                 free(k);
673         set_free(symlink_names);
674         free(filename);
675
676         return r;
677 }
678
679 int unit_load_fragment(Unit *u) {
680         int r = -ENOENT;
681
682         assert(u);
683         assert(u->meta.load_state == UNIT_STUB);
684
685         if (u->meta.load_path)
686                 r = load_from_path(u, u->meta.load_path);
687         else {
688                 Iterator i;
689                 char *t;
690
691                 /* Try to find a name we can load this with */
692                 SET_FOREACH(t, u->meta.names, i)
693                         if ((r = load_from_path(u, t)) != 0)
694                                 return r;
695         }
696
697         return r;
698 }