gconfig cbs now return int. almost finished receiving all dynamic server config,...
[radsecproxy.git] / gconfig.c
1 /*
2  * Copyright (C) 2007, 2008 Stig Venaas <venaas@uninett.no>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  */
8
9 #include <string.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <glob.h>
14 #include <sys/types.h>
15 #include <libgen.h>
16 #include <errno.h>
17 #include "debug.h"
18 #include "util.h"
19 #include "gconfig.h"
20
21 /* returns NULL on error, where to continue parsing if token and ok. E.g. "" will return token with empty string */
22 char *strtokenquote(char *s, char **token, char *del, char *quote, char *comment) {
23     char *t = s, *q, *r;
24
25     if (!t || !token || !del)
26         return NULL;
27     while (*t && strchr(del, *t))
28         t++;
29     if (!*t || (comment && strchr(comment, *t))) {
30         *token = NULL;
31         return t + 1; /* needs to be non-NULL, but value doesn't matter */
32     }
33     if (quote && (q = strchr(quote, *t))) {
34         t++;
35         r = t;
36         while (*t && *t != *q)
37             t++;
38         if (!*t || (t[1] && !strchr(del, t[1])))
39             return NULL;
40         *t = '\0';
41         *token = r;
42         return t + 1;
43     }
44     *token = t;
45     t++;
46     while (*t && !strchr(del, *t))
47         t++;
48     *t = '\0';
49     return t + 1;
50 }
51
52 int pushgconfdata(struct gconffile **cf, const char *data) {
53     int i;
54     struct gconffile *newcf;
55
56     if (!*cf) {
57         newcf = malloc(sizeof(struct gconffile) * 2);
58         if (!newcf)
59             return 0;
60         memset(newcf, 0, sizeof(struct gconffile) * 2);
61     } else {
62         for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
63         newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
64         if (!newcf)
65             return 0;
66         memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
67         memset(newcf, 0, sizeof(struct gconffile));
68     }
69     newcf[0].data = data;
70     *cf = newcf;
71     return 1;
72 }
73
74 FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description) {
75     int i;
76     struct gconffile *newcf;
77     char *desc;
78
79     if (!file) {
80         debug(DBG_INFO, "could not read config from %s", description);
81         return NULL;
82     }
83     debug(DBG_DBG, "reading config from %s", description);
84
85     desc = stringcopy(description, 0);
86     if (!desc)
87         goto errmalloc;
88     
89     if (!*cf) {
90         newcf = malloc(sizeof(struct gconffile) * 2);
91         if (!newcf)
92             goto errmalloc;
93         memset(newcf, 0, sizeof(struct gconffile) * 2);
94     } else {
95         for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
96         newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
97         if (!newcf)
98             goto errmalloc;
99         memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
100         memset(newcf, 0, sizeof(struct gconffile));
101     }
102     newcf[0].file = file;
103     newcf[0].path = desc;
104     *cf = newcf;
105     return file;
106     
107  errmalloc:
108     free(desc);
109     fclose(file);
110     debug(DBG_ERR, "malloc failed");
111     return NULL;
112 }
113
114 FILE *pushgconfpath(struct gconffile **cf, const char *path) {
115     FILE *f;
116
117     f = fopen(path, "r");
118     return pushgconffile(cf, f, path);
119 }
120
121 FILE *pushgconfpaths(struct gconffile **cf, const char *cfgpath) {
122     int i;
123     FILE *f = NULL;
124     glob_t globbuf;
125     char *path, *curfile = NULL, *dir;
126     
127     /* if cfgpath is relative, make it relative to current config */
128     if (*cfgpath == '/')
129         path = (char *)cfgpath;
130     else {
131         /* dirname may modify its argument */
132         curfile = stringcopy((*cf)->path, 0);
133         if (!curfile) {
134             debug(DBG_ERR, "malloc failed");
135             goto exit;
136         }
137         dir = dirname(curfile);
138         path = malloc(strlen(dir) + strlen(cfgpath) + 2);
139         if (!path) {
140             debug(DBG_ERR, "malloc failed");
141             goto exit;
142         }
143         strcpy(path, dir);
144         path[strlen(dir)] = '/';
145         strcpy(path + strlen(dir) + 1, cfgpath);
146     }
147     memset(&globbuf, 0, sizeof(glob_t));
148     if (glob(path, 0, NULL, &globbuf)) {
149         debug(DBG_INFO, "could not glob %s", path);
150         goto exit;
151     }
152
153     for (i = globbuf.gl_pathc - 1; i >= 0; i--) {
154         f = pushgconfpath(cf, globbuf.gl_pathv[i]);
155         if (!f)
156             break;
157     }    
158     globfree(&globbuf);
159
160  exit:    
161     if (curfile) {
162         free(curfile);
163         free(path);
164     }
165     return f;
166 }
167
168 int popgconf(struct gconffile **cf) {
169     int i;
170
171     if (!*cf)
172         return 0;
173     for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
174     if (i && (*cf)[0].file) {
175         fclose((*cf)[0].file);
176         if ((*cf)[0].path) {
177             debug(DBG_DBG, "closing config file %s", (*cf)[0].path);
178             free((*cf)[0].path);
179         }
180     }
181     if (i < 2) {
182         free(*cf);
183         *cf = NULL;
184         return 0;
185     }
186     memmove(*cf, *cf + 1, sizeof(struct gconffile) * i);
187     return 1;
188 }
189
190 void freegconf(struct gconffile **cf) {
191     int i;
192
193     if (!*cf)
194         return;
195     
196     for (i = 0; (*cf)[i].data || (*cf)[i].path; i++) {
197         if ((*cf)[i].file) {
198             fclose((*cf)[i].file);
199             if ((*cf)[i].path) {
200                 debug(DBG_DBG, "closing config file %s", (*cf)[i].path);
201                 free((*cf)[i].path);
202             }
203         }
204     }
205     free(*cf);
206     *cf = NULL;
207 }
208
209 struct gconffile *openconfigfile(const char *file) {
210     struct gconffile *cf = NULL;
211
212     if (!pushgconfpath(&cf, file)) {
213         debug(DBG_ERR, "could not read config file %s\n%s", file, strerror(errno));
214         return NULL;
215     }
216     debug(DBG_DBG, "reading config file %s", file);
217     return cf;
218 }
219
220 /* Parses config with following syntax:
221  * One of these:
222  * option-name value
223  * option-name = value
224  * Or:
225  * option-name value {
226  *     option-name [=] value
227  *     ...
228  * }
229  */
230
231 int getlinefromcf(struct gconffile *cf, char *line, const size_t size) {
232     size_t i, pos;
233     
234     if (!cf)
235         return 0;
236     
237     if (cf->file)
238         return fgets(line, size, cf->file) ? 1 : 0;
239     else if (cf->data) {
240         pos = cf->datapos;
241         if (!cf->data[pos])
242             return 0;
243         for (i = pos; cf->data[i] && cf->data[i] != '\n'; i++);
244         if (cf->data[i] == '\n')
245             i++;
246         if (i - pos > size - 1)
247             i = size - 1 + pos;
248         memcpy(line, cf->data + pos, i - pos);
249         line[i - pos] = '\0';
250         cf->datapos = i;
251         return 1;
252     }
253     return 0;
254 }
255
256 int getconfigline(struct gconffile **cf, char *block, char **opt, char **val, int *conftype) {
257     char line[1024];
258     char *tokens[3], *s;
259     int tcount;
260     
261     *opt = NULL;
262     *val = NULL;
263     *conftype = 0;
264     
265     if (!cf || !*cf || (!(*cf)->file && !(*cf)->data))
266         return 1;
267
268     for (;;) {
269         if (!getlinefromcf(*cf, line, 1024)) {
270             if (popgconf(cf))
271                 continue;
272             return 1;
273         }
274         s = line;
275         for (tcount = 0; tcount < 3; tcount++) {
276             s = strtokenquote(s, &tokens[tcount], " \t\r\n", "\"'", tcount ? NULL : "#");
277             if (!s) {
278                 debug(DBG_ERR, "Syntax error in line starting with: %s", line);
279                 return 0;
280             }
281             if (!tokens[tcount])
282                 break;
283         }
284         if (!tcount || **tokens == '#')
285             continue;
286
287         if (**tokens == '}') {
288             if (block)
289                 return 1;
290             debug(DBG_ERR, "configuration error, found } with no matching {");
291             return 0;
292         }
293         break;
294     }
295     
296     switch (tcount) {
297     case 2:
298         *opt = stringcopy(tokens[0], 0);
299         if (!*opt)
300             goto errmalloc;
301         *val = stringcopy(tokens[1], 0);
302         if (!*val)
303             goto errmalloc;
304         *conftype = CONF_STR;
305         break;
306     case 3:
307         if (tokens[1][0] == '=' && tokens[1][1] == '\0') {
308             *opt = stringcopy(tokens[0], 0);
309             if (!*opt)
310                 goto errmalloc;
311             *val = stringcopy(tokens[2], 0);
312             if (!*val)
313                 goto errmalloc;
314             *conftype = CONF_STR;
315             break;
316         }
317         if (tokens[2][0] == '{' && tokens[2][1] == '\0') {
318             *opt = stringcopy(tokens[0], 0);
319             if (!*opt)
320                 goto errmalloc;
321             *val = stringcopy(tokens[1], 0);
322             if (!*val)
323                 goto errmalloc;
324             *conftype = CONF_CBK;
325             break;
326         }
327         /* fall through */
328     default:
329         if (block)
330             debug(DBG_ERR, "configuration error in block %s, line starting with %s", block, tokens[0]);
331         else
332             debug(DBG_ERR, "configuration error, syntax error in line starting with %s", tokens[0]);
333         return 0;
334     }
335
336     if (**val)
337         return 1;
338     
339     debug(DBG_ERR, "configuration error, option %s needs a non-empty value", *opt);
340     goto errexit;
341
342  errmalloc:
343     debug(DBG_ERR, "malloc failed");
344  errexit:    
345     free(*opt);
346     *opt = NULL;
347     free(*val);
348     *val = NULL;
349     return 0;
350 }
351
352 /* returns 1 if ok, 0 on error */
353 /* caller must free returned values also on error */
354 int getgenericconfig(struct gconffile **cf, char *block, ...) {
355     va_list ap;
356     char *opt = NULL, *val, *word, *optval, **str = NULL, ***mstr = NULL, **newmstr;
357     uint8_t *bln;
358     int type = 0, conftype = 0, n;
359     int (*cbk)(struct gconffile **, void *, char *, char *, char *) = NULL;
360     void *cbkarg = NULL;
361
362     for (;;) {
363         free(opt);
364         if (!getconfigline(cf, block, &opt, &val, &conftype))
365             return 0;
366         if (!opt)
367             return 1;
368
369         if (conftype == CONF_STR && !strcasecmp(opt, "include")) {
370             if (!pushgconfpaths(cf, val)) {
371                 debug(DBG_ERR, "failed to include config file %s", val);
372                 goto errexit;
373             }
374             free(val);
375             continue;
376         }
377             
378         va_start(ap, block);
379         while ((word = va_arg(ap, char *))) {
380             type = va_arg(ap, int);
381             switch (type) {
382             case CONF_STR:
383                 str = va_arg(ap, char **);
384                 if (!str)
385                     goto errparam;
386                 break;
387             case CONF_MSTR:
388                 mstr = va_arg(ap, char ***);
389                 if (!mstr)
390                     goto errparam;
391                 break;
392             case CONF_BLN:
393                 bln = va_arg(ap, uint8_t *);
394                 if (!bln)
395                     goto errparam;
396                 break;
397             case CONF_CBK:
398                 cbk = va_arg(ap, int (*)(struct gconffile **, void *, char *, char *, char *));
399                 if (!cbk)
400                     goto errparam;
401                 cbkarg = va_arg(ap, void *);
402                 break;
403             default:
404                 goto errparam;
405             }
406             if (!strcasecmp(opt, word))
407                 break;
408         }
409         va_end(ap);
410         
411         if (!word) {
412             if (block)
413                 debug(DBG_ERR, "configuration error in block %s, unknown option %s", block, opt);
414             debug(DBG_ERR, "configuration error, unknown option %s", opt);
415             goto errexit;
416         }
417
418         if (((type == CONF_STR || type == CONF_MSTR || type == CONF_BLN) && conftype != CONF_STR) ||
419             (type == CONF_CBK && conftype != CONF_CBK)) {
420             if (block)
421                 debug(DBG_ERR, "configuration error in block %s, wrong syntax for option %s", block, opt);
422             debug(DBG_ERR, "configuration error, wrong syntax for option %s", opt);
423             goto errexit;
424         }
425
426         switch (type) {
427         case CONF_STR:
428             if (*str) {
429                 debug(DBG_ERR, "configuration error, option %s already set to %s", opt, *str);
430                 goto errexit;
431             }
432             *str = val;
433             break;
434         case CONF_MSTR:
435             if (*mstr)
436                 for (n = 0; (*mstr)[n]; n++);
437             else
438                 n = 0;
439             newmstr = realloc(*mstr, sizeof(char *) * (n + 2));
440             if (!newmstr) {
441                 debug(DBG_ERR, "malloc failed");
442                 goto errexit;
443             }
444             newmstr[n] = val;
445             newmstr[n + 1] = NULL;
446             *mstr = newmstr;
447             break;
448         case CONF_BLN:
449             if (!strcasecmp(val, "on"))
450                 *bln = 1;
451             else if (!strcasecmp(val, "off"))
452                 *bln = 0;
453             else {
454                 if (block)
455                     debug(DBG_ERR, "configuration error in block %s, value for option %s must be on or off, not %s", block, opt, val);
456                 else
457                     debug(DBG_ERR, "configuration error, value for option %s must be on or off, not %s", opt, val);
458                 goto errexit;
459             }
460             break;
461         case CONF_CBK:
462             optval = malloc(strlen(opt) + strlen(val) + 2);
463             if (!optval) {
464                 debug(DBG_ERR, "malloc failed");
465                 goto errexit;
466             }
467             sprintf(optval, "%s %s", opt, val);
468             if (!cbk(cf, cbkarg, optval, opt, val)) {
469                 free(optval);
470                 goto errexit;
471             }
472             free(val);
473             free(optval);
474             continue;
475         default:
476             goto errparam;
477         }
478         if (block)
479             debug(DBG_DBG, "getgenericconfig: block %s: %s = %s", block, opt, val);
480         else 
481             debug(DBG_DBG, "getgenericconfig: %s = %s", opt, val);
482         if (type == CONF_BLN)
483             free(val);
484     }
485
486  errparam:
487     debug(DBG_ERR, "getgenericconfig: internal parameter error");
488  errexit:
489     free(opt);
490     free(val);
491     return 0;
492 }