Added an initial implementation of HTTPS.
[ashd.git] / lib / cf.c
... / ...
CommitLineData
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 <libgen.h>
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
40static int parsefile(struct cfstate *s, FILE *in);
41
42static int doinclude(struct cfstate *s, char *spec)
43{
44 int rv, i;
45 FILE *inc;
46 glob_t globm;
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;
82 char line[1024];
83 int eof, argc;
84 int ind, indst[80], indl;
85 char *p, **w;
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 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 }
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")) {
157 for(i = 1; i < argc; i++) {
158 if(doinclude(s, w[i])) {
159 freeca(w);
160 return(1);
161 }
162 }
163 freeca(w);
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
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 = sprintf2("%s/etc/%s", p, name), R_OK)) {
236 free(path);
237 return(t);
238 }
239 free(t);
240 }
241 free(path);
242 return(NULL);
243}
244
245static struct child *newchild(char *name, int type)
246{
247 struct child *ch;
248
249 omalloc(ch);
250 ch->name = sstrdup(name);
251 ch->type = type;
252 ch->fd = -1;
253 return(ch);
254}
255
256void freechild(struct child *ch)
257{
258 if(ch->fd != -1)
259 close(ch->fd);
260 if(ch->name != NULL)
261 free(ch->name);
262 if(ch->argv != NULL)
263 freeca(ch->argv);
264 free(ch);
265}
266
267void skipcfblock(struct cfstate *s)
268{
269 char **w;
270
271 while(1) {
272 w = getcfline(s);
273 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
274 return;
275 }
276}
277
278struct child *parsechild(struct cfstate *s)
279{
280 struct child *ch;
281 int i;
282 int sl;
283
284 sl = s->lno;
285 if(!strcmp(s->argv[0], "child")) {
286 s->expstart = 1;
287 if(s->argc < 2) {
288 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
289 skipcfblock(s);
290 return(NULL);
291 }
292 ch = newchild(s->argv[1], CH_SOCKET);
293 } else if(!strcmp(s->argv[0], "fchild")) {
294 s->expstart = 1;
295 if(s->argc < 2) {
296 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
297 skipcfblock(s);
298 return(NULL);
299 }
300 ch = newchild(s->argv[1], CH_FORK);
301 } else {
302 return(NULL);
303 }
304
305 while(1) {
306 getcfline(s);
307 if(!strcmp(s->argv[0], "exec")) {
308 if(s->argc < 2) {
309 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
310 continue;
311 }
312 ch->argv = szmalloc(sizeof(*ch->argv) * s->argc);
313 for(i = 0; i < s->argc - 1; i++)
314 ch->argv[i] = sstrdup(s->argv[i + 1]);
315 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
316 break;
317 } else {
318 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
319 }
320 }
321 if(ch->argv == NULL) {
322 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
323 freechild(ch);
324 return(NULL);
325 }
326 return(ch);
327}
328
329int childhandle(struct child *ch, struct hthead *req, int fd)
330{
331 if(ch->type == CH_SOCKET) {
332 if(ch->fd < 0)
333 ch->fd = stdmkchild(ch->argv);
334 if(sendreq(ch->fd, req, fd)) {
335 if(errno == EPIPE) {
336 /* Assume that the child has crashed and restart it. */
337 close(ch->fd);
338 ch->fd = stdmkchild(ch->argv);
339 if(!sendreq(ch->fd, req, fd))
340 return(0);
341 }
342 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
343 close(ch->fd);
344 ch->fd = -1;
345 return(-1);
346 }
347 } else if(ch->type == CH_FORK) {
348 if(stdforkserve(ch->argv, req, fd) < 0)
349 return(-1);
350 }
351 return(0);
352}