fix 32 wrap around bug when handling 64 bit time values
[project/procd.git] / inittab.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <regex.h>
23
24 #include <libubox/utils.h>
25 #include <libubox/list.h>
26
27 #include "procd.h"
28
29 #define TAG_ID          0
30 #define TAG_RUNLVL      1
31 #define TAG_ACTION      2
32 #define TAG_PROCESS     3
33
34 #define MAX_ARGS        8
35
36 struct init_action;
37 const char *console;
38
39 struct init_handler {
40         const char *name;
41         void (*cb) (struct init_action *a);
42         int atomic;
43 };
44
45 struct init_action {
46         struct list_head list;
47
48         char *id;
49         char *argv[MAX_ARGS];
50         char *line;
51
52         struct init_handler *handler;
53         struct uloop_process proc;
54
55         int pending;
56         int respawn;
57         struct uloop_timeout tout;
58 };
59
60 static const char *tab = "/etc/inittab";
61 static char *ask = "/sbin/askfirst";
62
63 static struct init_action *pending;
64
65 static LIST_HEAD(actions);
66
67 static void fork_script(struct init_action *a)
68 {
69         a->proc.pid = fork();
70         if (!a->proc.pid) {
71                 execvp(a->argv[0], a->argv);
72                 ERROR("Failed to execute %s\n", a->argv[0]);
73                 exit(-1);
74         }
75
76         if (a->proc.pid > 0) {
77                 DEBUG(2, "Launched new %s action, pid=%d\n",
78                                         a->handler->name,
79                                         (int) a->proc.pid);
80                 uloop_process_add(&a->proc);
81         }
82 }
83
84 static void child_exit(struct uloop_process *proc, int ret)
85 {
86         struct init_action *a = container_of(proc, struct init_action, proc);
87
88         DEBUG(2, "pid:%d\n", proc->pid);
89         if (a->tout.cb) {
90                 uloop_timeout_set(&a->tout, a->respawn);
91         } else {
92                 a->pending = 0;
93                 pending = NULL;
94                 procd_state_next();
95         }
96 }
97
98 static void respawn(struct uloop_timeout *tout)
99 {
100         struct init_action *a = container_of(tout, struct init_action, tout);
101         fork_script(a);
102 }
103
104 static void runscript(struct init_action *a)
105 {
106         a->proc.cb = child_exit;
107         fork_script(a);
108 }
109
110 static void askfirst(struct init_action *a)
111 {
112         struct stat s;
113         int i;
114
115         chdir("/dev");
116         i = stat(a->id, &s);
117         chdir("/");
118         if (i || (console && !strcmp(console, a->id))) {
119                 DEBUG(2, "Skipping %s\n", a->id);
120                 return;
121         }
122
123         a->tout.cb = respawn;
124         for (i = MAX_ARGS - 2; i >= 2; i--)
125                 a->argv[i] = a->argv[i - 2];
126         a->argv[0] = ask;
127         a->argv[1] = a->id;
128         a->respawn = 500;
129
130         a->proc.cb = child_exit;
131         fork_script(a);
132 }
133
134 static void askconsole(struct init_action *a)
135 {
136         struct stat s;
137         char line[256], *tty;
138         int i, r, fd = open("/proc/cmdline", O_RDONLY);
139         regex_t pat_cmdline;
140         regmatch_t matches[2];
141
142         if (!fd)
143                 return;
144
145         r = read(fd, line, sizeof(line) - 1);
146         line[r] = '\0';
147         close(fd);
148
149         regcomp(&pat_cmdline, "console=([a-zA-Z0-9]*)", REG_EXTENDED);
150         if (regexec(&pat_cmdline, line, 2, matches, 0))
151                 goto err_out;
152         line[matches[1].rm_eo] = '\0';
153         tty = &line[matches[1].rm_so];
154
155         chdir("/dev");
156         i = stat(tty, &s);
157         chdir("/");
158         if (i) {
159                 DEBUG(2, "skipping %s\n", tty);
160                 goto err_out;
161         }
162         console = strdup(tty);
163
164         a->tout.cb = respawn;
165         for (i = MAX_ARGS - 2; i >= 2; i--)
166                 a->argv[i] = a->argv[i - 2];
167         a->argv[0] = ask;
168         a->argv[1] = strdup(tty);
169         a->respawn = 500;
170
171         a->proc.cb = child_exit;
172         fork_script(a);
173 err_out:
174         regfree(&pat_cmdline);
175 }
176
177 static struct init_handler handlers[] = {
178         {
179                 .name = "sysinit",
180                 .cb = runscript,
181         }, {
182                 .name = "shutdown",
183                 .cb = runscript,
184         }, {
185                 .name = "askfirst",
186                 .cb = askfirst,
187                 .atomic = 1,
188         }, {
189                 .name = "askconsole",
190                 .cb = askconsole,
191                 .atomic = 1,
192         }
193 };
194
195 static int add_action(struct init_action *a, const char *name)
196 {
197         int i;
198
199         for (i = 0; i < ARRAY_SIZE(handlers); i++)
200                 if (!strcmp(handlers[i].name, name)) {
201                         a->handler = &handlers[i];
202                         list_add_tail(&a->list, &actions);
203                         return 0;
204                 }
205         ERROR("Unknown init handler %s\n", name);
206         return -1;
207 }
208
209 void procd_inittab_run(const char *handler)
210 {
211         struct init_action *a;
212
213         list_for_each_entry(a, &actions, list)
214                 if (!strcmp(a->handler->name, handler)) {
215                         if (a->handler->atomic) {
216                                 a->handler->cb(a);
217                                 continue;
218                         }
219                         if (pending || a->pending)
220                                 break;
221                         a->pending = 1;
222                         pending = a;
223                         a->handler->cb(a);
224                 }
225 }
226
227 void procd_inittab(void)
228 {
229 #define LINE_LEN        128
230         FILE *fp = fopen(tab, "r");
231         struct init_action *a;
232         regex_t pat_inittab;
233         regmatch_t matches[5];
234         char *line;
235
236         if (!fp) {
237                 ERROR("Failed to open %s\n", tab);
238                 return;
239         }
240
241         regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9/[.-.]. ]*)", REG_EXTENDED);
242         line = malloc(LINE_LEN);
243         a = malloc(sizeof(struct init_action));
244         memset(a, 0, sizeof(struct init_action));
245
246         while (fgets(line, LINE_LEN, fp)) {
247                 char *tags[TAG_PROCESS + 1];
248                 char *tok;
249                 int i;
250
251                 if (*line == '#')
252                         continue;
253
254                 if (regexec(&pat_inittab, line, 5, matches, 0))
255                         continue;
256
257                 DEBUG(2, "Parsing inittab - %s", line);
258
259                 for (i = TAG_ID; i <= TAG_PROCESS; i++) {
260                         line[matches[i].rm_eo] = '\0';
261                         tags[i] = &line[matches[i + 1].rm_so];
262                 };
263
264                 tok = strtok(tags[TAG_PROCESS], " ");
265                 for (i = 0; i < (MAX_ARGS - i - 1) && tok; i++) {
266                         a->argv[i] = tok;
267                         tok = strtok(NULL, " ");
268                 }
269                 a->argv[i] = NULL;
270                 a->id = tags[TAG_ID];
271                 a->line = line;
272
273                 if (add_action(a, tags[TAG_ACTION]))
274                         continue;
275                 line = malloc(LINE_LEN);
276                 a = malloc(sizeof(struct init_action));
277                 memset(a, 0, sizeof(struct init_action));
278         }
279
280         fclose(fp);
281         free(line);
282         free(a);
283         regfree(&pat_inittab);
284 }