chiark / gitweb /
various cleanups
[elogind.git] / load-fragment.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <linux/oom.h>
7
8 #include "name.h"
9 #include "strv.h"
10 #include "conf-parser.h"
11 #include "load-fragment.h"
12 #include "log.h"
13
14 static int config_parse_deps(
15                 const char *filename,
16                 unsigned line,
17                 const char *section,
18                 const char *lvalue,
19                 const char *rvalue,
20                 void *data,
21                 void *userdata) {
22
23         NameDependency d = PTR_TO_UINT(data);
24         Name *name = userdata;
25         char *w;
26         size_t l;
27         char *state;
28
29         assert(filename);
30         assert(lvalue);
31         assert(rvalue);
32
33         FOREACH_WORD(w, &l, rvalue, state) {
34                 char *t;
35                 int r;
36                 Name *other;
37
38                 if (!(t = strndup(w, l)))
39                         return -ENOMEM;
40
41                 r = manager_load_name(name->meta.manager, t, &other);
42                 free(t);
43
44                 if (r < 0)
45                         return r;
46
47                 if ((r = name_add_dependency(name, d, other)) < 0)
48                         return r;
49         }
50
51         return 0;
52 }
53
54 static int config_parse_names(
55                 const char *filename,
56                 unsigned line,
57                 const char *section,
58                 const char *lvalue,
59                 const char *rvalue,
60                 void *data,
61                 void *userdata) {
62
63         Set **set = data;
64         Name *name = userdata;
65         char *w;
66         size_t l;
67         char *state;
68
69         assert(filename);
70         assert(lvalue);
71         assert(rvalue);
72         assert(data);
73
74         FOREACH_WORD(w, &l, rvalue, state) {
75                 char *t;
76                 int r;
77                 Name *other;
78
79                 if (!(t = strndup(w, l)))
80                         return -ENOMEM;
81
82                 other = manager_get_name(name->meta.manager, t);
83
84                 if (other) {
85
86                         if (other != name) {
87
88                                 if (other->meta.load_state != NAME_STUB) {
89                                         free(t);
90                                         return -EEXIST;
91                                 }
92
93                                 if ((r = name_merge(name, other)) < 0) {
94                                         free(t);
95                                         return r;
96                                 }
97                         }
98
99                 } else {
100
101                         if (!*set)
102                                 if (!(*set = set_new(trivial_hash_func, trivial_compare_func))) {
103                                         free(t);
104                                         return -ENOMEM;
105                                 }
106
107                         if ((r = set_put(*set, t)) < 0) {
108                                 free(t);
109                                 return r;
110                         }
111
112                         t = NULL;
113                 }
114
115                 free(t);
116         }
117
118         return 0;
119 }
120
121 static int config_parse_listen(
122                 const char *filename,
123                 unsigned line,
124                 const char *section,
125                 const char *lvalue,
126                 const char *rvalue,
127                 void *data,
128                 void *userdata) {
129
130         int r;
131         SocketPort *p;
132         Socket *s;
133
134         assert(filename);
135         assert(lvalue);
136         assert(rvalue);
137         assert(data);
138
139         s = (Socket*) data;
140
141         if (!(p = new0(SocketPort, 1)))
142                 return -ENOMEM;
143
144         if (streq(lvalue, "ListenFIFO")) {
145                 p->type = SOCKET_FIFO;
146
147                 if (!(p->path = strdup(rvalue))) {
148                         free(p);
149                         return -ENOMEM;
150                 }
151         } else {
152                 p->type = SOCKET_SOCKET;
153
154                 if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
155                         log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
156                         free(p);
157                         return r;
158                 }
159
160                 if (streq(lvalue, "ListenStream"))
161                         p->address.type = SOCK_STREAM;
162                 else if (streq(lvalue, "ListenDatagram"))
163                         p->address.type = SOCK_DGRAM;
164                 else {
165                         assert(streq(lvalue, "ListenSequentialPacket"));
166                         p->address.type = SOCK_SEQPACKET;
167                 }
168
169                 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
170                         free(p);
171                         return -EPROTONOSUPPORT;
172                 }
173         }
174
175         p->fd = -1;
176         LIST_PREPEND(SocketPort, port, s->ports, p);
177
178         return 0;
179 }
180
181 static int config_parse_socket_bind(
182                 const char *filename,
183                 unsigned line,
184                 const char *section,
185                 const char *lvalue,
186                 const char *rvalue,
187                 void *data,
188                 void *userdata) {
189
190         int r;
191         Socket *s;
192
193         assert(filename);
194         assert(lvalue);
195         assert(rvalue);
196         assert(data);
197
198         s = (Socket*) data;
199
200         if ((r = parse_boolean(rvalue)) < 0) {
201                 log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue);
202                 return r;
203         }
204
205         s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
206
207         return 0;
208 }
209
210 static int config_parse_nice(
211                 const char *filename,
212                 unsigned line,
213                 const char *section,
214                 const char *lvalue,
215                 const char *rvalue,
216                 void *data,
217                 void *userdata) {
218
219         int *i = data, priority, r;
220
221         assert(filename);
222         assert(lvalue);
223         assert(rvalue);
224         assert(data);
225
226         if ((r = safe_atoi(rvalue, &priority)) < 0) {
227                 log_error("[%s:%u] Failed to parse nice priority: %s", filename, line, rvalue);
228                 return r;
229         }
230
231         if (priority < PRIO_MIN || priority >= PRIO_MAX) {
232                 log_error("[%s:%u] Nice priority out of range: %s", filename, line, rvalue);
233                 return -ERANGE;
234         }
235
236         *i = priority;
237         return 0;
238 }
239
240 static int config_parse_oom_adjust(
241                 const char *filename,
242                 unsigned line,
243                 const char *section,
244                 const char *lvalue,
245                 const char *rvalue,
246                 void *data,
247                 void *userdata) {
248
249         int *i = data, oa, r;
250
251         assert(filename);
252         assert(lvalue);
253         assert(rvalue);
254         assert(data);
255
256         if ((r = safe_atoi(rvalue, &oa)) < 0) {
257                 log_error("[%s:%u] Failed to parse OOM adjust value: %s", filename, line, rvalue);
258                 return r;
259         }
260
261         if (oa < OOM_DISABLE || oa > OOM_ADJUST_MAX) {
262                 log_error("[%s:%u] OOM adjust value out of range: %s", filename, line, rvalue);
263                 return -ERANGE;
264         }
265
266         *i = oa;
267         return 0;
268 }
269
270 static int config_parse_umask(
271                 const char *filename,
272                 unsigned line,
273                 const char *section,
274                 const char *lvalue,
275                 const char *rvalue,
276                 void *data,
277                 void *userdata) {
278
279         mode_t *m = data;
280         long l;
281         char *x = NULL;
282
283         assert(filename);
284         assert(lvalue);
285         assert(rvalue);
286         assert(data);
287
288         errno = 0;
289         l = strtol(rvalue, &x, 8);
290         if (!x || *x || errno) {
291                 log_error("[%s:%u] Failed to parse umask value: %s", filename, line, rvalue);
292                 return errno ? -errno : -EINVAL;
293         }
294
295         if (l < 0000 || l > 0777) {
296                 log_error("[%s:%u] umask value out of range: %s", filename, line, rvalue);
297                 return -ERANGE;
298         }
299
300         *m = (mode_t) l;
301         return 0;
302 }
303
304 static int config_parse_exec(
305                 const char *filename,
306                 unsigned line,
307                 const char *section,
308                 const char *lvalue,
309                 const char *rvalue,
310                 void *data,
311                 void *userdata) {
312
313         ExecCommand **e = data, *ee, *nce = NULL;
314         char **n;
315         char *w;
316         unsigned k;
317         size_t l;
318         char *state;
319
320         assert(filename);
321         assert(lvalue);
322         assert(rvalue);
323         assert(data);
324
325         k = 0;
326         FOREACH_WORD_QUOTED(w, l, rvalue, state)
327                 k++;
328
329         if (!(n = new(char*, k+1)))
330                 return -ENOMEM;
331
332         k = 0;
333         FOREACH_WORD_QUOTED(w, l, rvalue, state)
334                 if (!(n[k++] = strndup(w, l)))
335                         goto fail;
336
337         n[k] = NULL;
338
339         if (!n[0] || n[0][0] != '/') {
340                 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
341                 strv_free(n);
342                 return -EINVAL;
343         }
344
345         if (!(nce = new0(ExecCommand, 1)))
346                 goto fail;
347
348         nce->argv = n;
349         if (!(nce->path = strdup(n[0])))
350                 goto fail;
351
352         if (*e) {
353                 /* It's kinda important that we keep the order here */
354                 LIST_FIND_TAIL(ExecCommand, command, *e, ee);
355                 LIST_INSERT_AFTER(ExecCommand, command, *e, ee, nce);
356         } else
357                 *e = nce;
358
359         return 0;
360
361 fail:
362         for (; k > 0; k--)
363                 free(n[k-1]);
364         free(n);
365
366         free(nce);
367
368         return -ENOMEM;
369 }
370
371 static int config_parse_usec(
372                 const char *filename,
373                 unsigned line,
374                 const char *section,
375                 const char *lvalue,
376                 const char *rvalue,
377                 void *data,
378                 void *userdata) {
379
380         usec_t *usec = data;
381         unsigned long long u;
382         int r;
383
384         assert(filename);
385         assert(lvalue);
386         assert(rvalue);
387         assert(data);
388
389         if ((r = safe_atollu(rvalue, &u)) < 0) {
390                 log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue);
391                 return r;
392         }
393
394         /* We actually assume the user configures seconds. Later on we
395          * might choose to support suffixes for time values, to
396          * configure bigger or smaller units */
397
398         *usec = u * USEC_PER_SEC;
399
400         return 0;
401 }
402
403 static int config_parse_service_type(
404                 const char *filename,
405                 unsigned line,
406                 const char *section,
407                 const char *lvalue,
408                 const char *rvalue,
409                 void *data,
410                 void *userdata) {
411
412         Service *s = data;
413
414         assert(filename);
415         assert(lvalue);
416         assert(rvalue);
417         assert(data);
418
419         if (streq(rvalue, "forking"))
420                 s->type = SERVICE_FORKING;
421         else if (streq(rvalue, "simple"))
422                 s->type = SERVICE_SIMPLE;
423         else {
424                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
425                 return -EBADMSG;
426         }
427
428         return 0;
429 }
430
431 static int config_parse_service_restart(
432                 const char *filename,
433                 unsigned line,
434                 const char *section,
435                 const char *lvalue,
436                 const char *rvalue,
437                 void *data,
438                 void *userdata) {
439
440         Service *s = data;
441
442         assert(filename);
443         assert(lvalue);
444         assert(rvalue);
445         assert(data);
446
447         if (streq(rvalue, "once"))
448                 s->restart = SERVICE_ONCE;
449         else if (streq(rvalue, "on-success"))
450                 s->type = SERVICE_RESTART_ON_SUCCESS;
451         else if (streq(rvalue, "always"))
452                 s->type = SERVICE_RESTART_ALWAYS;
453         else {
454                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
455                 return -EBADMSG;
456         }
457
458         return 0;
459 }
460
461 int name_load_fragment(Name *n) {
462
463         static const char* const section_table[_NAME_TYPE_MAX] = {
464                 [NAME_SERVICE]   = "Service",
465                 [NAME_TIMER]     = "Timer",
466                 [NAME_SOCKET]    = "Socket",
467                 [NAME_MILESTONE] = "Milestone",
468                 [NAME_DEVICE]    = "Device",
469                 [NAME_MOUNT]     = "Mount",
470                 [NAME_AUTOMOUNT] = "Automount",
471                 [NAME_SNAPSHOT]  = "Snapshot"
472         };
473
474 #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
475                 { "Directory",              config_parse_path,            &(context).directory,                              section   }, \
476                 { "User",                   config_parse_string,          &(context).user,                                   section   }, \
477                 { "Group",                  config_parse_string,          &(context).group,                                  section   }, \
478                 { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                   section   }, \
479                 { "Nice",                   config_parse_nice,            &(context).nice,                                   section   }, \
480                 { "OOMAdjust",              config_parse_oom_adjust,      &(context).oom_adjust,                             section   }, \
481                 { "UMask",                  config_parse_umask,           &(context).umask,                                  section   }, \
482                 { "Environment",            config_parse_strv,            &(context).environment,                            section   }
483
484         const ConfigItem items[] = {
485                 { "Names",                  config_parse_names,           &n->meta.names,                                    "Meta"    },
486                 { "Description",            config_parse_string,          &n->meta.description,                              "Meta"    },
487                 { "Requires",               config_parse_deps,            UINT_TO_PTR(NAME_REQUIRES),                        "Meta"    },
488                 { "SoftRequires",           config_parse_deps,            UINT_TO_PTR(NAME_SOFT_REQUIRES),                   "Meta"    },
489                 { "Wants",                  config_parse_deps,            UINT_TO_PTR(NAME_WANTS),                           "Meta"    },
490                 { "Requisite",              config_parse_deps,            UINT_TO_PTR(NAME_REQUISITE),                       "Meta"    },
491                 { "SoftRequisite",          config_parse_deps,            UINT_TO_PTR(NAME_SOFT_REQUISITE),                  "Meta"    },
492                 { "Conflicts",              config_parse_deps,            UINT_TO_PTR(NAME_CONFLICTS),                       "Meta"    },
493                 { "Before",                 config_parse_deps,            UINT_TO_PTR(NAME_BEFORE),                          "Meta"    },
494                 { "After",                  config_parse_deps,            UINT_TO_PTR(NAME_AFTER),                           "Meta"    },
495
496                 { "PIDFile",                config_parse_path,            &n->service.pid_file,                              "Service" },
497                 { "ExecStartPre",           config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_START_PRE],  "Service" },
498                 { "ExecStart",              config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_START],      "Service" },
499                 { "ExecStartPost",          config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_START_POST], "Service" },
500                 { "ExecReload",             config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_RELOAD],     "Service" },
501                 { "ExecStop",               config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_STOP],       "Service" },
502                 { "ExecStopPost",           config_parse_exec,            &n->service.exec_command[SERVICE_EXEC_STOP_POST],  "Service" },
503                 { "RestartSec",             config_parse_usec,            &n->service.restart_usec,                          "Service" },
504                 { "TimeoutSec",             config_parse_usec,            &n->service.timeout_usec,                          "Service" },
505                 { "Type",                   config_parse_service_type,    &n->service,                                       "Service" },
506                 { "Restart",                config_parse_service_restart, &n->service,                                       "Service" },
507                 EXEC_CONTEXT_CONFIG_ITEMS(n->service.exec_context, "Service"),
508
509                 { "ListenStream",           config_parse_listen,          &n->socket,                                        "Socket"  },
510                 { "ListenDatagram",         config_parse_listen,          &n->socket,                                        "Socket"  },
511                 { "ListenSequentialPacket", config_parse_listen,          &n->socket,                                        "Socket"  },
512                 { "ListenFIFO",             config_parse_listen,          &n->socket,                                        "Socket"  },
513                 { "BindIPv6Only",           config_parse_socket_bind,     &n->socket,                                        "Socket"  },
514                 { "Backlog",                config_parse_unsigned,        &n->socket.backlog,                                "Socket"  },
515                 { "ExecStartPre",           config_parse_exec,            &n->service.exec_command[SOCKET_EXEC_START_PRE],   "Socket"  },
516                 { "ExecStartPost",          config_parse_exec,            &n->service.exec_command[SOCKET_EXEC_START_POST],  "Socket"  },
517                 { "ExecStopPre",            config_parse_exec,            &n->service.exec_command[SOCKET_EXEC_STOP_PRE],    "Socket"  },
518                 { "ExecStopPost",           config_parse_exec,            &n->service.exec_command[SOCKET_EXEC_STOP_POST],   "Socket"  },
519                 EXEC_CONTEXT_CONFIG_ITEMS(n->socket.exec_context, "Socket"),
520
521                 EXEC_CONTEXT_CONFIG_ITEMS(n->automount.exec_context, "Automount"),
522
523                 { NULL, NULL, NULL, NULL }
524         };
525
526 #undef EXEC_CONTEXT_CONFIG_ITEMS
527
528         char *t;
529         int r;
530         const char *sections[3];
531         Iterator i;
532
533         assert(n);
534         assert(n->meta.load_state == NAME_STUB);
535
536         sections[0] = "Meta";
537         sections[1] = section_table[n->meta.type];
538         sections[2] = NULL;
539
540         SET_FOREACH(t, n->meta.names, i) {
541
542                 /* Try to find a name we can load this with */
543                 if ((r = config_parse(t, sections, items, n)) == -ENOENT)
544                         continue;
545
546                 /* Yay, we succeeded! Now let's call this our identifier */
547                 if (r == 0)
548                         n->meta.id = t;
549
550                 return r;
551         }
552
553         return -ENOENT;
554 }