Added a generic config parser/child handler and used it for dirplex and patplex.
[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>
25#include <errno.h>
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30#include <utils.h>
31#include <cf.h>
32#include <mt.h>
33#include <proc.h>
34#include <log.h>
35
36#define CH_SOCKET 0
37#define CH_FORK 1
38
39static int parsefile(struct cfstate *s, FILE *in)
40{
41 int i, o, ret;
42 glob_t globm;
43 char line[1024];
44 int eof, argc;
45 int ind, indst[80], indl;
46 char *p, **w, *fbk;
47 FILE *inc;
48
49 s->lno = 0;
50 indst[indl = 0] = 0;
51 eof = 0;
52 while(1) {
53 if(fgets(line, sizeof(line), in) == NULL) {
54 eof = 1;
55 line[0] = 0;
56 }
57 s->lno++;
58 for(p = line + strlen(line) - 1; p >= line; p--) {
59 if(isspace(*p))
60 *p = 0;
61 else
62 break;
63 }
64 for(ind = 0, p = line; *p; p++) {
65 if(*p == ' ') {
66 ind++;
67 } else if(*p == '\t') {
68 ind = ind - (ind % 8) + 8;
69 } else {
70 break;
71 }
72 }
73 if(!eof && (!*p || (*p == '#')))
74 continue;
75
76 reindent:
77 if(ind > indst[indl]) {
78 indst[++indl] = ind;
79 if(!s->expstart) {
80 s->res = tokenize("start");
81 if(yield())
82 return(1);
83 } else {
84 s->expstart = 0;
85 }
86 } else {
87 if(s->expstart) {
88 s->res = tokenize("end");
89 if(yield())
90 return(1);
91 s->expstart = 0;
92 }
93 while(ind < indst[indl]) {
94 indl--;
95 s->res = tokenize("end");
96 if(yield())
97 return(1);
98 }
99 if(ind > indst[indl]) {
100 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
101 goto reindent;
102 }
103 }
104
105 if(eof)
106 return(0);
107
108 argc = calen(w = tokenize(line));
109 if(argc < 1) {
110 /* Shouldn't happen, but... */
111 freeca(w);
112 continue;
113 }
114
115 if(indl == 0) {
116 if(!strcmp(w[0], "include")) {
117 fbk = s->file;
118 for(i = 1; i < argc; i++) {
119 if((ret = glob(w[i], 0, NULL, &globm)) == 0) {
120 for(o = 0; o < globm.gl_pathc; o++) {
121 if((inc = fopen(globm.gl_pathv[o], "r")) != NULL) {
122 s->file = globm.gl_pathv[o];
123 if(parsefile(s, inc)) {
124 fclose(inc);
125 globfree(&globm);
126 freeca(w);
127 return(1);
128 }
129 fclose(inc);
130 }
131 }
132 globfree(&globm);
133 }
134 }
135 freeca(w);
136 s->file = fbk;
137 continue;
138 }
139 }
140
141 if(!strcmp(w[0], "start") ||
142 !strcmp(w[0], "end") ||
143 !strcmp(w[0], "eof")) {
144 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
145 } else {
146 s->res = w;
147 if(yield())
148 return(1);
149 }
150 }
151}
152
153static void parsefn(struct muth *mt, va_list args)
154{
155 vavar(struct cfstate *, s);
156 vavar(FILE *, in);
157 vavar(char *, file);
158
159 s->file = sstrdup(file);
160 if(parsefile(s, in))
161 goto out;
162 do {
163 s->res = tokenize("eof");
164 } while(!yield());
165
166out:
167 free(s->file);
168}
169
170char **getcfline(struct cfstate *s)
171{
172 freeca(s->argv);
173 if(s->res == NULL)
174 resume(s->pf, 0);
175 s->argc = calen(s->argv = s->res);
176 s->res = NULL;
177 return(s->argv);
178}
179
180struct cfstate *mkcfparser(FILE *in, char *name)
181{
182 struct cfstate *s;
183
184 omalloc(s);
185 s->pf = mustart(parsefn, s, in, name);
186 return(s);
187}
188
189void freecfparser(struct cfstate *s)
190{
191 resume(s->pf, -1);
192 freeca(s->argv);
193 freeca(s->res);
194 free(s);
195}
196
197static struct child *newchild(char *name, int type)
198{
199 struct child *ch;
200
201 omalloc(ch);
202 ch->name = sstrdup(name);
203 ch->type = type;
204 ch->fd = -1;
205 return(ch);
206}
207
208void freechild(struct child *ch)
209{
210 if(ch->fd != -1)
211 close(ch->fd);
212 if(ch->name != NULL)
213 free(ch->name);
214 if(ch->argv != NULL)
215 freeca(ch->argv);
216 free(ch);
217}
218
219void skipcfblock(struct cfstate *s)
220{
221 char **w;
222
223 while(1) {
224 w = getcfline(s);
225 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
226 return;
227 }
228}
229
230struct child *parsechild(struct cfstate *s)
231{
232 struct child *ch;
233 int i;
234 int sl;
235
236 sl = s->lno;
237 if(!strcmp(s->argv[0], "child")) {
238 s->expstart = 1;
239 if(s->argc < 2) {
240 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
241 skipcfblock(s);
242 return(NULL);
243 }
244 ch = newchild(s->argv[1], CH_SOCKET);
245 } else if(!strcmp(s->argv[0], "fchild")) {
246 s->expstart = 1;
247 if(s->argc < 2) {
248 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
249 skipcfblock(s);
250 return(NULL);
251 }
252 ch = newchild(s->argv[1], CH_FORK);
253 } else {
254 return(NULL);
255 }
256
257 while(1) {
258 getcfline(s);
259 if(!strcmp(s->argv[0], "exec")) {
260 if(s->argc < 2) {
261 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
262 continue;
263 }
264 ch->argv = szmalloc(sizeof(*ch->argv) * s->argc);
265 for(i = 0; i < s->argc - 1; i++)
266 ch->argv[i] = sstrdup(s->argv[i + 1]);
267 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
268 break;
269 } else {
270 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
271 }
272 }
273 if(ch->argv == NULL) {
274 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
275 freechild(ch);
276 return(NULL);
277 }
278 return(ch);
279}
280
281int childhandle(struct child *ch, struct hthead *req, int fd)
282{
283 if(ch->type == CH_SOCKET) {
284 if(ch->fd < 0)
285 ch->fd = stdmkchild(ch->argv);
286 if(sendreq(ch->fd, req, fd)) {
287 if(errno == EPIPE) {
288 /* Assume that the child has crashed and restart it. */
289 close(ch->fd);
290 ch->fd = stdmkchild(ch->argv);
291 if(!sendreq(ch->fd, req, fd))
292 return(-1);
293 }
294 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
295 close(ch->fd);
296 ch->fd = -1;
297 return(-1);
298 }
299 } else if(ch->type == CH_FORK) {
300 if(stdforkserve(ch->argv, req, fd) < 0)
301 return(-1);
302 }
303 return(0);
304}