lib: Add $HOME/.ashd/etc to the directories to search for slash-less files.
[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>
3d74fc07 26#include <sys/socket.h>
06c1a718
FT
27#include <errno.h>
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <utils.h>
33#include <cf.h>
34#include <mt.h>
35#include <proc.h>
36#include <log.h>
37
38#define CH_SOCKET 0
39#define CH_FORK 1
40
1924fe8c
FT
41struct stdchild {
42 int type;
43 char **argv;
44 int fd;
45};
46
dd281054 47static int parsefile(struct cfstate *s, FILE *in);
1924fe8c
FT
48static void stdmerge(struct child *old, struct child *new);
49static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata);
50static void stddestroy(struct child *ch);
dd281054
FT
51
52static int doinclude(struct cfstate *s, char *spec)
06c1a718 53{
dd281054
FT
54 int rv, i;
55 FILE *inc;
06c1a718 56 glob_t globm;
dd281054
FT
57 char *fbk, *dir, *fspec;
58
59 rv = 0;
60 fbk = s->file;
61 if(spec[0] == '/') {
62 fspec = spec;
63 } else {
64 dir = sstrdup(fbk);
65 fspec = sprintf3("%s/%s", dirname(dir), spec);
66 free(dir);
67 }
68 if(glob(fspec, 0, NULL, &globm))
69 return(0);
70 for(i = 0; i < globm.gl_pathc; i++) {
71 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
72 s->file = globm.gl_pathv[i];
73 if(parsefile(s, inc)) {
74 fclose(inc);
75 rv = 1;
76 goto out;
77 }
78 fclose(inc);
79 inc = NULL;
80 }
81 }
82
83out:
84 globfree(&globm);
85 s->file = fbk;
86 return(rv);
87}
88
89static int parsefile(struct cfstate *s, FILE *in)
90{
91 int i;
06c1a718
FT
92 char line[1024];
93 int eof, argc;
94 int ind, indst[80], indl;
dd281054 95 char *p, **w;
06c1a718
FT
96
97 s->lno = 0;
98 indst[indl = 0] = 0;
99 eof = 0;
100 while(1) {
101 if(fgets(line, sizeof(line), in) == NULL) {
102 eof = 1;
103 line[0] = 0;
104 }
105 s->lno++;
c3b91092
FT
106 if(line[0]) {
107 for(p = line + strlen(line) - 1; p >= line; p--) {
108 if(isspace(*p))
109 *p = 0;
110 else
111 break;
112 }
06c1a718
FT
113 }
114 for(ind = 0, p = line; *p; p++) {
115 if(*p == ' ') {
116 ind++;
117 } else if(*p == '\t') {
118 ind = ind - (ind % 8) + 8;
119 } else {
120 break;
121 }
122 }
123 if(!eof && (!*p || (*p == '#')))
124 continue;
125
126 reindent:
127 if(ind > indst[indl]) {
128 indst[++indl] = ind;
129 if(!s->expstart) {
130 s->res = tokenize("start");
131 if(yield())
132 return(1);
133 } else {
134 s->expstart = 0;
135 }
136 } else {
137 if(s->expstart) {
138 s->res = tokenize("end");
139 if(yield())
140 return(1);
141 s->expstart = 0;
142 }
143 while(ind < indst[indl]) {
144 indl--;
145 s->res = tokenize("end");
146 if(yield())
147 return(1);
148 }
149 if(ind > indst[indl]) {
150 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
151 goto reindent;
152 }
153 }
154
155 if(eof)
156 return(0);
157
158 argc = calen(w = tokenize(line));
159 if(argc < 1) {
160 /* Shouldn't happen, but... */
161 freeca(w);
162 continue;
163 }
164
165 if(indl == 0) {
166 if(!strcmp(w[0], "include")) {
06c1a718 167 for(i = 1; i < argc; i++) {
dd281054
FT
168 if(doinclude(s, w[i])) {
169 freeca(w);
170 return(1);
06c1a718
FT
171 }
172 }
173 freeca(w);
06c1a718
FT
174 continue;
175 }
176 }
177
178 if(!strcmp(w[0], "start") ||
179 !strcmp(w[0], "end") ||
180 !strcmp(w[0], "eof")) {
181 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
182 } else {
183 s->res = w;
184 if(yield())
185 return(1);
186 }
187 }
188}
189
190static void parsefn(struct muth *mt, va_list args)
191{
192 vavar(struct cfstate *, s);
193 vavar(FILE *, in);
194 vavar(char *, file);
195
196 s->file = sstrdup(file);
197 if(parsefile(s, in))
198 goto out;
199 do {
200 s->res = tokenize("eof");
201 } while(!yield());
202
203out:
204 free(s->file);
205}
206
207char **getcfline(struct cfstate *s)
208{
209 freeca(s->argv);
210 if(s->res == NULL)
211 resume(s->pf, 0);
212 s->argc = calen(s->argv = s->res);
213 s->res = NULL;
214 return(s->argv);
215}
216
217struct cfstate *mkcfparser(FILE *in, char *name)
218{
219 struct cfstate *s;
220
221 omalloc(s);
222 s->pf = mustart(parsefn, s, in, name);
223 return(s);
224}
225
226void freecfparser(struct cfstate *s)
227{
228 resume(s->pf, -1);
229 freeca(s->argv);
230 freeca(s->res);
231 free(s);
232}
233
0fc6fd13
FT
234char *findstdconf(char *name)
235{
f9a65eb2 236 char *home, *path, *p, *p2, *t;
0fc6fd13 237
f9a65eb2
FT
238 if((home = getenv("HOME")) != NULL) {
239 if(!access(t = sprintf2("%s/.ashd/etc/%s", home, name), R_OK))
13975be5 240 return(t);
13975be5 241 free(t);
0fc6fd13 242 }
f9a65eb2
FT
243 if((path = getenv("PATH")) != NULL) {
244 path = sstrdup(path);
245 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
246 if((p2 = strrchr(p, '/')) == NULL)
247 continue;
248 *p2 = 0;
249 if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) {
250 free(path);
251 return(t);
252 }
253 free(t);
254 }
255 free(path);
256 }
0fc6fd13
FT
257 return(NULL);
258}
259
1924fe8c 260struct child *newchild(char *name, struct chandler *iface, void *pdata)
06c1a718
FT
261{
262 struct child *ch;
263
264 omalloc(ch);
265 ch->name = sstrdup(name);
1924fe8c
FT
266 ch->iface = iface;
267 ch->pdata = pdata;
06c1a718
FT
268 return(ch);
269}
270
271void freechild(struct child *ch)
272{
1924fe8c
FT
273 if(ch->iface->destroy != NULL)
274 ch->iface->destroy(ch);
06c1a718
FT
275 if(ch->name != NULL)
276 free(ch->name);
06c1a718
FT
277 free(ch);
278}
279
1924fe8c
FT
280void mergechildren(struct child *dst, struct child *src)
281{
282 struct child *ch1, *ch2;
283
284 for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) {
285 for(ch2 = src; ch2 != NULL; ch2 = ch2->next) {
286 if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) {
287 ch1->iface->merge(ch1, ch2);
288 break;
289 }
290 }
291 }
292}
293
06c1a718
FT
294void skipcfblock(struct cfstate *s)
295{
296 char **w;
297
298 while(1) {
299 w = getcfline(s);
300 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
301 return;
302 }
303}
304
1924fe8c
FT
305static struct chandler stdhandler = {
306 .handle = stdhandle,
307 .merge = stdmerge,
308 .destroy = stddestroy,
309};
310
311static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
312{
313 struct stdchild *i = ch->pdata;
45b19d96 314 int serr;
1924fe8c
FT
315
316 if(i->type == CH_SOCKET) {
317 if(i->fd < 0)
318 i->fd = stdmkchild(i->argv, chinit, idata);
3d74fc07 319 if(sendreq2(i->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) {
45b19d96
FT
320 serr = errno;
321 if((serr == EPIPE) || (serr == ECONNRESET)) {
1924fe8c
FT
322 /* Assume that the child has crashed and restart it. */
323 close(i->fd);
324 i->fd = stdmkchild(i->argv, chinit, idata);
3d74fc07 325 if(!sendreq2(i->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT))
1924fe8c
FT
326 return(0);
327 }
45b19d96
FT
328 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(serr));
329 if(serr != EAGAIN) {
fc35a3ef
FT
330 close(i->fd);
331 i->fd = -1;
332 }
1924fe8c
FT
333 return(-1);
334 }
335 } else if(i->type == CH_FORK) {
336 if(stdforkserve(i->argv, req, fd, chinit, idata) < 0)
337 return(-1);
338 }
339 return(0);
340}
341
342static void stdmerge(struct child *dst, struct child *src)
343{
344 struct stdchild *od, *nd;
345
346 if(src->iface == &stdhandler) {
347 nd = dst->pdata;
348 od = src->pdata;
349 nd->fd = od->fd;
350 od->fd = -1;
351 }
352}
353
354static void stddestroy(struct child *ch)
355{
356 struct stdchild *d = ch->pdata;
357
358 if(d->fd >= 0)
359 close(d->fd);
360 if(d->argv)
361 freeca(d->argv);
362 free(d);
363}
364
06c1a718
FT
365struct child *parsechild(struct cfstate *s)
366{
367 struct child *ch;
1924fe8c 368 struct stdchild *d;
06c1a718
FT
369 int i;
370 int sl;
371
372 sl = s->lno;
373 if(!strcmp(s->argv[0], "child")) {
374 s->expstart = 1;
375 if(s->argc < 2) {
376 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
377 skipcfblock(s);
378 return(NULL);
379 }
1924fe8c
FT
380 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
381 d->type = CH_SOCKET;
06c1a718
FT
382 } else if(!strcmp(s->argv[0], "fchild")) {
383 s->expstart = 1;
384 if(s->argc < 2) {
385 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
386 skipcfblock(s);
387 return(NULL);
388 }
1924fe8c
FT
389 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
390 d->type = CH_FORK;
06c1a718
FT
391 } else {
392 return(NULL);
393 }
1924fe8c 394 d->fd = -1;
06c1a718
FT
395
396 while(1) {
397 getcfline(s);
398 if(!strcmp(s->argv[0], "exec")) {
399 if(s->argc < 2) {
400 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
401 continue;
402 }
1924fe8c 403 d->argv = szmalloc(sizeof(*d->argv) * s->argc);
06c1a718 404 for(i = 0; i < s->argc - 1; i++)
1924fe8c 405 d->argv[i] = sstrdup(s->argv[i + 1]);
06c1a718
FT
406 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
407 break;
408 } else {
409 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
410 }
411 }
1924fe8c 412 if(d->argv == NULL) {
06c1a718
FT
413 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
414 freechild(ch);
415 return(NULL);
416 }
417 return(ch);
418}
419
6a7a868e 420int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
06c1a718 421{
1924fe8c 422 return(ch->iface->handle(ch, req, fd, chinit, idata));
06c1a718 423}