Cache dirplex .htrc files better to avoid redundant rechecking...
[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++;
96 for(p = line + strlen(line) - 1; p >= line; p--) {
97 if(isspace(*p))
98 *p = 0;
99 else
100 break;
101 }
102 for(ind = 0, p = line; *p; p++) {
103 if(*p == ' ') {
104 ind++;
105 } else if(*p == '\t') {
106 ind = ind - (ind % 8) + 8;
107 } else {
108 break;
109 }
110 }
111 if(!eof && (!*p || (*p == '#')))
112 continue;
113
114 reindent:
115 if(ind > indst[indl]) {
116 indst[++indl] = ind;
117 if(!s->expstart) {
118 s->res = tokenize("start");
119 if(yield())
120 return(1);
121 } else {
122 s->expstart = 0;
123 }
124 } else {
125 if(s->expstart) {
126 s->res = tokenize("end");
127 if(yield())
128 return(1);
129 s->expstart = 0;
130 }
131 while(ind < indst[indl]) {
132 indl--;
133 s->res = tokenize("end");
134 if(yield())
135 return(1);
136 }
137 if(ind > indst[indl]) {
138 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
139 goto reindent;
140 }
141 }
142
143 if(eof)
144 return(0);
145
146 argc = calen(w = tokenize(line));
147 if(argc < 1) {
148 /* Shouldn't happen, but... */
149 freeca(w);
150 continue;
151 }
152
153 if(indl == 0) {
154 if(!strcmp(w[0], "include")) {
06c1a718 155 for(i = 1; i < argc; i++) {
dd281054
FT
156 if(doinclude(s, w[i])) {
157 freeca(w);
158 return(1);
06c1a718
FT
159 }
160 }
161 freeca(w);
06c1a718
FT
162 continue;
163 }
164 }
165
166 if(!strcmp(w[0], "start") ||
167 !strcmp(w[0], "end") ||
168 !strcmp(w[0], "eof")) {
169 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
170 } else {
171 s->res = w;
172 if(yield())
173 return(1);
174 }
175 }
176}
177
178static void parsefn(struct muth *mt, va_list args)
179{
180 vavar(struct cfstate *, s);
181 vavar(FILE *, in);
182 vavar(char *, file);
183
184 s->file = sstrdup(file);
185 if(parsefile(s, in))
186 goto out;
187 do {
188 s->res = tokenize("eof");
189 } while(!yield());
190
191out:
192 free(s->file);
193}
194
195char **getcfline(struct cfstate *s)
196{
197 freeca(s->argv);
198 if(s->res == NULL)
199 resume(s->pf, 0);
200 s->argc = calen(s->argv = s->res);
201 s->res = NULL;
202 return(s->argv);
203}
204
205struct cfstate *mkcfparser(FILE *in, char *name)
206{
207 struct cfstate *s;
208
209 omalloc(s);
210 s->pf = mustart(parsefn, s, in, name);
211 return(s);
212}
213
214void freecfparser(struct cfstate *s)
215{
216 resume(s->pf, -1);
217 freeca(s->argv);
218 freeca(s->res);
219 free(s);
220}
221
222static struct child *newchild(char *name, int type)
223{
224 struct child *ch;
225
226 omalloc(ch);
227 ch->name = sstrdup(name);
228 ch->type = type;
229 ch->fd = -1;
230 return(ch);
231}
232
233void freechild(struct child *ch)
234{
235 if(ch->fd != -1)
236 close(ch->fd);
237 if(ch->name != NULL)
238 free(ch->name);
239 if(ch->argv != NULL)
240 freeca(ch->argv);
241 free(ch);
242}
243
244void skipcfblock(struct cfstate *s)
245{
246 char **w;
247
248 while(1) {
249 w = getcfline(s);
250 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
251 return;
252 }
253}
254
255struct child *parsechild(struct cfstate *s)
256{
257 struct child *ch;
258 int i;
259 int sl;
260
261 sl = s->lno;
262 if(!strcmp(s->argv[0], "child")) {
263 s->expstart = 1;
264 if(s->argc < 2) {
265 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
266 skipcfblock(s);
267 return(NULL);
268 }
269 ch = newchild(s->argv[1], CH_SOCKET);
270 } else if(!strcmp(s->argv[0], "fchild")) {
271 s->expstart = 1;
272 if(s->argc < 2) {
273 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
274 skipcfblock(s);
275 return(NULL);
276 }
277 ch = newchild(s->argv[1], CH_FORK);
278 } else {
279 return(NULL);
280 }
281
282 while(1) {
283 getcfline(s);
284 if(!strcmp(s->argv[0], "exec")) {
285 if(s->argc < 2) {
286 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
287 continue;
288 }
289 ch->argv = szmalloc(sizeof(*ch->argv) * s->argc);
290 for(i = 0; i < s->argc - 1; i++)
291 ch->argv[i] = sstrdup(s->argv[i + 1]);
292 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
293 break;
294 } else {
295 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
296 }
297 }
298 if(ch->argv == NULL) {
299 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
300 freechild(ch);
301 return(NULL);
302 }
303 return(ch);
304}
305
306int childhandle(struct child *ch, struct hthead *req, int fd)
307{
308 if(ch->type == CH_SOCKET) {
309 if(ch->fd < 0)
310 ch->fd = stdmkchild(ch->argv);
311 if(sendreq(ch->fd, req, fd)) {
312 if(errno == EPIPE) {
313 /* Assume that the child has crashed and restart it. */
314 close(ch->fd);
315 ch->fd = stdmkchild(ch->argv);
316 if(!sendreq(ch->fd, req, fd))
b390f906 317 return(0);
06c1a718
FT
318 }
319 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
320 close(ch->fd);
321 ch->fd = -1;
322 return(-1);
323 }
324 } else if(ch->type == CH_FORK) {
325 if(stdforkserve(ch->argv, req, fd) < 0)
326 return(-1);
327 }
328 return(0);
329}