Added the library header files to SOURCES.
[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
0fc6fd13
FT
224char *findstdconf(char *name)
225{
226 char *path, *p, *p2, *t;
227
228 if((path = getenv("PATH")) == NULL)
229 return(NULL);
230 path = sstrdup(path);
231 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
232 if((p2 = strrchr(p, '/')) == NULL)
233 continue;
234 *p2 = 0;
235 if(!access(t = sprintf3("%s/etc/%s", p, name), R_OK)) {
236 free(path);
237 return(sstrdup(t));
238 }
239 }
240 free(path);
241 return(NULL);
242}
243
06c1a718
FT
244static struct child *newchild(char *name, int type)
245{
246 struct child *ch;
247
248 omalloc(ch);
249 ch->name = sstrdup(name);
250 ch->type = type;
251 ch->fd = -1;
252 return(ch);
253}
254
255void freechild(struct child *ch)
256{
257 if(ch->fd != -1)
258 close(ch->fd);
259 if(ch->name != NULL)
260 free(ch->name);
261 if(ch->argv != NULL)
262 freeca(ch->argv);
263 free(ch);
264}
265
266void skipcfblock(struct cfstate *s)
267{
268 char **w;
269
270 while(1) {
271 w = getcfline(s);
272 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
273 return;
274 }
275}
276
277struct child *parsechild(struct cfstate *s)
278{
279 struct child *ch;
280 int i;
281 int sl;
282
283 sl = s->lno;
284 if(!strcmp(s->argv[0], "child")) {
285 s->expstart = 1;
286 if(s->argc < 2) {
287 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
288 skipcfblock(s);
289 return(NULL);
290 }
291 ch = newchild(s->argv[1], CH_SOCKET);
292 } else if(!strcmp(s->argv[0], "fchild")) {
293 s->expstart = 1;
294 if(s->argc < 2) {
295 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
296 skipcfblock(s);
297 return(NULL);
298 }
299 ch = newchild(s->argv[1], CH_FORK);
300 } else {
301 return(NULL);
302 }
303
304 while(1) {
305 getcfline(s);
306 if(!strcmp(s->argv[0], "exec")) {
307 if(s->argc < 2) {
308 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
309 continue;
310 }
311 ch->argv = szmalloc(sizeof(*ch->argv) * s->argc);
312 for(i = 0; i < s->argc - 1; i++)
313 ch->argv[i] = sstrdup(s->argv[i + 1]);
314 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
315 break;
316 } else {
317 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
318 }
319 }
320 if(ch->argv == NULL) {
321 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
322 freechild(ch);
323 return(NULL);
324 }
325 return(ch);
326}
327
328int childhandle(struct child *ch, struct hthead *req, int fd)
329{
330 if(ch->type == CH_SOCKET) {
331 if(ch->fd < 0)
332 ch->fd = stdmkchild(ch->argv);
333 if(sendreq(ch->fd, req, fd)) {
334 if(errno == EPIPE) {
335 /* Assume that the child has crashed and restart it. */
336 close(ch->fd);
337 ch->fd = stdmkchild(ch->argv);
338 if(!sendreq(ch->fd, req, fd))
b390f906 339 return(0);
06c1a718
FT
340 }
341 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
342 close(ch->fd);
343 ch->fd = -1;
344 return(-1);
345 }
346 } else if(ch->type == CH_FORK) {
347 if(stdforkserve(ch->argv, req, fd) < 0)
348 return(-1);
349 }
350 return(0);
351}