Fixed config parsing bug.
[ashd.git] / lib / cf.c
CommitLineData
06c1a718
FT
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <string.h>
23#include <ctype.h>
24#include <glob.h>
dd281054 25#include <libgen.h>
06c1a718
FT
26#include <errno.h>
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <utils.h>
32#include <cf.h>
33#include <mt.h>
34#include <proc.h>
35#include <log.h>
36
37#define CH_SOCKET 0
38#define CH_FORK 1
39
dd281054
FT
40static int parsefile(struct cfstate *s, FILE *in);
41
42static int doinclude(struct cfstate *s, char *spec)
06c1a718 43{
dd281054
FT
44 int rv, i;
45 FILE *inc;
06c1a718 46 glob_t globm;
dd281054
FT
47 char *fbk, *dir, *fspec;
48
49 rv = 0;
50 fbk = s->file;
51 if(spec[0] == '/') {
52 fspec = spec;
53 } else {
54 dir = sstrdup(fbk);
55 fspec = sprintf3("%s/%s", dirname(dir), spec);
56 free(dir);
57 }
58 if(glob(fspec, 0, NULL, &globm))
59 return(0);
60 for(i = 0; i < globm.gl_pathc; i++) {
61 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
62 s->file = globm.gl_pathv[i];
63 if(parsefile(s, inc)) {
64 fclose(inc);
65 rv = 1;
66 goto out;
67 }
68 fclose(inc);
69 inc = NULL;
70 }
71 }
72
73out:
74 globfree(&globm);
75 s->file = fbk;
76 return(rv);
77}
78
79static int parsefile(struct cfstate *s, FILE *in)
80{
81 int i;
06c1a718
FT
82 char line[1024];
83 int eof, argc;
84 int ind, indst[80], indl;
dd281054 85 char *p, **w;
06c1a718
FT
86
87 s->lno = 0;
88 indst[indl = 0] = 0;
89 eof = 0;
90 while(1) {
91 if(fgets(line, sizeof(line), in) == NULL) {
92 eof = 1;
93 line[0] = 0;
94 }
95 s->lno++;
c3b91092
FT
96 if(line[0]) {
97 for(p = line + strlen(line) - 1; p >= line; p--) {
98 if(isspace(*p))
99 *p = 0;
100 else
101 break;
102 }
06c1a718
FT
103 }
104 for(ind = 0, p = line; *p; p++) {
105 if(*p == ' ') {
106 ind++;
107 } else if(*p == '\t') {
108 ind = ind - (ind % 8) + 8;
109 } else {
110 break;
111 }
112 }
113 if(!eof && (!*p || (*p == '#')))
114 continue;
115
116 reindent:
117 if(ind > indst[indl]) {
118 indst[++indl] = ind;
119 if(!s->expstart) {
120 s->res = tokenize("start");
121 if(yield())
122 return(1);
123 } else {
124 s->expstart = 0;
125 }
126 } else {
127 if(s->expstart) {
128 s->res = tokenize("end");
129 if(yield())
130 return(1);
131 s->expstart = 0;
132 }
133 while(ind < indst[indl]) {
134 indl--;
135 s->res = tokenize("end");
136 if(yield())
137 return(1);
138 }
139 if(ind > indst[indl]) {
140 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
141 goto reindent;
142 }
143 }
144
145 if(eof)
146 return(0);
147
148 argc = calen(w = tokenize(line));
149 if(argc < 1) {
150 /* Shouldn't happen, but... */
151 freeca(w);
152 continue;
153 }
154
155 if(indl == 0) {
156 if(!strcmp(w[0], "include")) {
06c1a718 157 for(i = 1; i < argc; i++) {
dd281054
FT
158 if(doinclude(s, w[i])) {
159 freeca(w);
160 return(1);
06c1a718
FT
161 }
162 }
163 freeca(w);
06c1a718
FT
164 continue;
165 }
166 }
167
168 if(!strcmp(w[0], "start") ||
169 !strcmp(w[0], "end") ||
170 !strcmp(w[0], "eof")) {
171 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
172 } else {
173 s->res = w;
174 if(yield())
175 return(1);
176 }
177 }
178}
179
180static void parsefn(struct muth *mt, va_list args)
181{
182 vavar(struct cfstate *, s);
183 vavar(FILE *, in);
184 vavar(char *, file);
185
186 s->file = sstrdup(file);
187 if(parsefile(s, in))
188 goto out;
189 do {
190 s->res = tokenize("eof");
191 } while(!yield());
192
193out:
194 free(s->file);
195}
196
197char **getcfline(struct cfstate *s)
198{
199 freeca(s->argv);
200 if(s->res == NULL)
201 resume(s->pf, 0);
202 s->argc = calen(s->argv = s->res);
203 s->res = NULL;
204 return(s->argv);
205}
206
207struct cfstate *mkcfparser(FILE *in, char *name)
208{
209 struct cfstate *s;
210
211 omalloc(s);
212 s->pf = mustart(parsefn, s, in, name);
213 return(s);
214}
215
216void freecfparser(struct cfstate *s)
217{
218 resume(s->pf, -1);
219 freeca(s->argv);
220 freeca(s->res);
221 free(s);
222}
223
224static struct child *newchild(char *name, int type)
225{
226 struct child *ch;
227
228 omalloc(ch);
229 ch->name = sstrdup(name);
230 ch->type = type;
231 ch->fd = -1;
232 return(ch);
233}
234
235void freechild(struct child *ch)
236{
237 if(ch->fd != -1)
238 close(ch->fd);
239 if(ch->name != NULL)
240 free(ch->name);
241 if(ch->argv != NULL)
242 freeca(ch->argv);
243 free(ch);
244}
245
246void skipcfblock(struct cfstate *s)
247{
248 char **w;
249
250 while(1) {
251 w = getcfline(s);
252 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
253 return;
254 }
255}
256
257struct child *parsechild(struct cfstate *s)
258{
259 struct child *ch;
260 int i;
261 int sl;
262
263 sl = s->lno;
264 if(!strcmp(s->argv[0], "child")) {
265 s->expstart = 1;
266 if(s->argc < 2) {
267 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
268 skipcfblock(s);
269 return(NULL);
270 }
271 ch = newchild(s->argv[1], CH_SOCKET);
272 } else if(!strcmp(s->argv[0], "fchild")) {
273 s->expstart = 1;
274 if(s->argc < 2) {
275 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
276 skipcfblock(s);
277 return(NULL);
278 }
279 ch = newchild(s->argv[1], CH_FORK);
280 } else {
281 return(NULL);
282 }
283
284 while(1) {
285 getcfline(s);
286 if(!strcmp(s->argv[0], "exec")) {
287 if(s->argc < 2) {
288 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
289 continue;
290 }
291 ch->argv = szmalloc(sizeof(*ch->argv) * s->argc);
292 for(i = 0; i < s->argc - 1; i++)
293 ch->argv[i] = sstrdup(s->argv[i + 1]);
294 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
295 break;
296 } else {
297 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
298 }
299 }
300 if(ch->argv == NULL) {
301 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
302 freechild(ch);
303 return(NULL);
304 }
305 return(ch);
306}
307
308int childhandle(struct child *ch, struct hthead *req, int fd)
309{
310 if(ch->type == CH_SOCKET) {
311 if(ch->fd < 0)
312 ch->fd = stdmkchild(ch->argv);
313 if(sendreq(ch->fd, req, fd)) {
314 if(errno == EPIPE) {
315 /* Assume that the child has crashed and restart it. */
316 close(ch->fd);
317 ch->fd = stdmkchild(ch->argv);
318 if(!sendreq(ch->fd, req, fd))
b390f906 319 return(0);
06c1a718
FT
320 }
321 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
322 close(ch->fd);
323 ch->fd = -1;
324 return(-1);
325 }
326 } else if(ch->type == CH_FORK) {
327 if(stdforkserve(ch->argv, req, fd) < 0)
328 return(-1);
329 }
330 return(0);
331}