chiark / gitweb /
s/name/unit
[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] || n[0][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 #define FOLLOW_MAX 8
454
455 static char *build_path(const char *path, const char *filename) {
456         char *e, *r;
457         size_t k;
458
459         assert(path);
460         assert(filename);
461
462         /* This removes the last component of path and appends
463          * filename, unless the latter is absolute anyway or the
464          * former isn't */
465
466         if (filename[0] == '/')
467                 return strdup(filename);
468
469         if (!(e = strrchr(path, '/')))
470                 return strdup(filename);
471
472         k = strlen(filename);
473         if (!(r = new(char, e-path+1+k+1)))
474                 return NULL;
475
476         memcpy(r, path, e-path+1);
477         memcpy(r+(e-path)+1, filename, k+1);
478
479         return r;
480 }
481
482 static int open_follow(const char **filename, FILE **_f, Set *names) {
483         unsigned c;
484         int fd, r;
485         FILE *f;
486         char *n = NULL;
487         const char *fn;
488
489         assert(filename);
490         assert(*filename);
491         assert(_f);
492         assert(names);
493
494         fn = *filename;
495
496         for (c = 0; c < FOLLOW_MAX; c++) {
497                 char *target, *k, *name;
498
499                 /* Add the file name we are currently looking at to
500                  * the names of this unit */
501                 name = file_name_from_path(fn);
502                 if (!set_get(names, name)) {
503
504                         if (!(name = strdup(name))) {
505                                 r = -ENOMEM;
506                                 goto finish;
507                         }
508
509                         if ((r = set_put(names, name)) < 0) {
510                                 free(name);
511                                 goto finish;
512                         }
513
514                         free(name);
515                 }
516
517                 /* Try to open the file name, but don' if its a symlink */
518                 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
519                 if (fd >= 0 || errno != ELOOP)
520                         break;
521
522                 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
523                 if ((r = readlink_malloc(fn, &target)) < 0)
524                         goto finish;
525
526                 k = build_path(fn, target);
527                 free(target);
528
529                 if (!k) {
530                         r = -ENOMEM;
531                         goto finish;
532                 }
533
534                 free(n);
535                 fn = n = k;
536         }
537
538         if (c >= FOLLOW_MAX) {
539                 r = -ELOOP;
540                 goto finish;
541         }
542
543         if (fd < 0) {
544                 r = -errno;
545                 goto finish;
546         }
547
548         if (!(f = fdopen(fd, "r"))) {
549                 r = -errno;
550                 assert(close_nointr(fd) == 0);
551                 goto finish;
552         }
553
554         *_f = f;
555         *filename = fn;
556         r = 0;
557
558 finish:
559         free(n);
560         return r;
561 }
562
563 int unit_load_fragment(Unit *u) {
564
565         static const char* const section_table[_UNIT_TYPE_MAX] = {
566                 [UNIT_SERVICE]   = "Service",
567                 [UNIT_TIMER]     = "Timer",
568                 [UNIT_SOCKET]    = "Socket",
569                 [UNIT_TARGET]    = "Target",
570                 [UNIT_DEVICE]    = "Device",
571                 [UNIT_MOUNT]     = "Mount",
572                 [UNIT_AUTOMOUNT] = "Automount",
573                 [UNIT_SNAPSHOT]  = "Snapshot"
574         };
575
576 #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
577                 { "Directory",              config_parse_path,            &(context).directory,                              section   }, \
578                 { "User",                   config_parse_string,          &(context).user,                                   section   }, \
579                 { "Group",                  config_parse_string,          &(context).group,                                  section   }, \
580                 { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                   section   }, \
581                 { "Nice",                   config_parse_nice,            &(context).nice,                                   section   }, \
582                 { "OOMAdjust",              config_parse_oom_adjust,      &(context).oom_adjust,                             section   }, \
583                 { "UMask",                  config_parse_umask,           &(context).umask,                                  section   }, \
584                 { "Environment",            config_parse_strv,            &(context).environment,                            section   }
585
586         const ConfigItem items[] = {
587                 { "Names",                  config_parse_names,           u,                                                 "Meta"    },
588                 { "Description",            config_parse_string,          &u->meta.description,                              "Meta"    },
589                 { "Requires",               config_parse_deps,            UINT_TO_PTR(UNIT_REQUIRES),                        "Meta"    },
590                 { "SoftRequires",           config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUIRES),                   "Meta"    },
591                 { "Wants",                  config_parse_deps,            UINT_TO_PTR(UNIT_WANTS),                           "Meta"    },
592                 { "Requisite",              config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE),                       "Meta"    },
593                 { "SoftRequisite",          config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUISITE),                  "Meta"    },
594                 { "Conflicts",              config_parse_deps,            UINT_TO_PTR(UNIT_CONFLICTS),                       "Meta"    },
595                 { "Before",                 config_parse_deps,            UINT_TO_PTR(UNIT_BEFORE),                          "Meta"    },
596                 { "After",                  config_parse_deps,            UINT_TO_PTR(UNIT_AFTER),                           "Meta"    },
597
598                 { "PIDFile",                config_parse_path,            &u->service.pid_file,                              "Service" },
599                 { "ExecStartPre",           config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START_PRE],  "Service" },
600                 { "ExecStart",              config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START],      "Service" },
601                 { "ExecStartPost",          config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START_POST], "Service" },
602                 { "ExecReload",             config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_RELOAD],     "Service" },
603                 { "ExecStop",               config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_STOP],       "Service" },
604                 { "ExecStopPost",           config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_STOP_POST],  "Service" },
605                 { "RestartSec",             config_parse_usec,            &u->service.restart_usec,                          "Service" },
606                 { "TimeoutSec",             config_parse_usec,            &u->service.timeout_usec,                          "Service" },
607                 { "Type",                   config_parse_service_type,    &u->service,                                       "Service" },
608                 { "Restart",                config_parse_service_restart, &u->service,                                       "Service" },
609                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
610
611                 { "ListenStream",           config_parse_listen,          &u->socket,                                        "Socket"  },
612                 { "ListenDatagram",         config_parse_listen,          &u->socket,                                        "Socket"  },
613                 { "ListenSequentialPacket", config_parse_listen,          &u->socket,                                        "Socket"  },
614                 { "ListenFIFO",             config_parse_listen,          &u->socket,                                        "Socket"  },
615                 { "BindIPv6Only",           config_parse_socket_bind,     &u->socket,                                        "Socket"  },
616                 { "Backlog",                config_parse_unsigned,        &u->socket.backlog,                                "Socket"  },
617                 { "ExecStartPre",           config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_START_PRE],   "Socket"  },
618                 { "ExecStartPost",          config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_START_POST],  "Socket"  },
619                 { "ExecStopPre",            config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_STOP_PRE],    "Socket"  },
620                 { "ExecStopPost",           config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_STOP_POST],   "Socket"  },
621                 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
622
623                 EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
624
625                 { NULL, NULL, NULL, NULL }
626         };
627
628 #undef EXEC_CONTEXT_CONFIG_ITEMS
629
630         char *t, *k;
631         int r;
632         const char *sections[3];
633         Iterator i;
634         Set *symlink_names;
635
636         assert(u);
637         assert(u->meta.load_state == UNIT_STUB);
638
639         sections[0] = "Meta";
640         sections[1] = section_table[u->meta.type];
641         sections[2] = NULL;
642
643         if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
644                 return -ENOMEM;
645
646         /* Try to find a name we can load this with */
647         SET_FOREACH(t, u->meta.names, i) {
648                 FILE *f;
649                 char *fn;
650
651                 /* Clear the symlink name set first */
652                 while ((k = set_steal_first(symlink_names)))
653                         free(k);
654
655                 /* Instead of opening the path right away, we manually
656                  * follow all symlinks and add their name to our unit
657                  * name set while doing so */
658                 fn = t;
659                 if ((r = open_follow((const char**) &fn, &f, symlink_names)) < 0) {
660                         if (r == -ENOENT)
661                                 continue;
662
663                         goto finish;
664                 }
665
666                 /* Now, parse the file contents */
667                 r = config_parse(fn, f, sections, items, u);
668                 if (fn != t)
669                         free(fn);
670                 if (r < 0)
671                         goto finish;
672
673                 /* Let's try to add in all symlink names we found */
674                 while ((k = set_steal_first(symlink_names)))
675                         if ((r = unit_add_name(u, k)) < 0)
676                                 goto finish;
677
678                 /* Yay, we succeeded! Now let's call this our identifier */
679                 u->meta.id = t;
680                 goto finish;
681         }
682
683
684         r = -ENOENT;
685
686 finish:
687         while ((k = set_steal_first(symlink_names)))
688                 free(k);
689
690         set_free(symlink_names);
691
692         return r;
693 }