Fix bugs in {G,}CBREG.
[doldaconnect.git] / daemon / conf.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <langinfo.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <malloc.h>
23#include <string.h>
24#include <sys/types.h>
25#include <errno.h>
26#include <wctype.h>
27#include <stddef.h>
28#include <wchar.h>
29#include <iconv.h>
30#include <arpa/inet.h>
05fe12f8 31#include <gdbm.h>
d3372da9 32
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36#include "conf.h"
37#include "log.h"
38#include "utils.h"
39
40static struct configmod *modules = NULL;
41
42#if 0
43static void dumpconfig(void)
44{
45 struct configmod *mod;
46 struct configvar *var;
47
48 for(mod = modules; mod != NULL; mod = mod->next)
49 {
50 printf("%s:\n", mod->name);
51 for(var = mod->vars; var->type != CONF_VAR_END; var++)
52 {
53 switch(var->type)
54 {
55 case CONF_VAR_BOOL:
56 printf("\t%s: %s\n", var->name, var->val.num?"t":"f");
57 break;
58 case CONF_VAR_INT:
59 printf("\t%s: %i\n", var->name, var->val.num);
60 break;
61 case CONF_VAR_STRING:
62 printf("\t%s: \"%ls\" (%i)\n", var->name, var->val.str, wcslen(var->val.str));
63 break;
64 case CONF_VAR_IPV4:
65 printf("\t%s: %s\n", var->name, inet_ntoa(var->val.ipv4));
66 break;
67 }
68 }
69 }
70}
71#endif
72
73struct configvar *confgetvar(char *modname, char *varname)
74{
75 struct configmod *m;
76 struct configvar *v;
77
78 for(m = modules; m != NULL; m = m->next)
79 {
80 if(!strcmp(m->name, modname))
81 {
82 for(v = m->vars; v->type != CONF_VAR_END; v++)
83 {
84 if(!strcmp(v->name, varname))
85 return(v);
86 }
87 break;
88 }
89 }
90 return(NULL);
91}
92
93void confregmod(struct configmod *mod)
94{
95 struct configvar *var;
96
97 for(var = mod->vars; var->type != CONF_VAR_END; var++)
98 {
99 switch(var->type)
100 {
101 case CONF_VAR_BOOL:
102 case CONF_VAR_INT:
103 var->val.num = var->defaults.num;
104 break;
105 case CONF_VAR_STRING:
106 if(var->defaults.str != NULL)
107 {
108 var->val.str = swcsdup(var->defaults.str);
109 } else {
110 var->val.str = NULL;
111 }
112 break;
113 case CONF_VAR_IPV4:
114 var->val.ipv4.s_addr = var->defaults.ipv4.s_addr;
115 break;
116 }
117 CBCHAININIT(var, conf_update);
118 }
119 mod->next = modules;
120 modules = mod;
121}
122
123int runconfcmd(int argc, wchar_t **argv)
124{
125 struct configmod *module;
126 struct configvar *var;
127 struct configcmd *cmd;
128 int ret, handled;
129 wchar_t *p;
130 char *cmdn, *buf, *buf2, *valbuf;
131 long num;
132 struct in_addr newipv4;
133 int cb;
134
135 if(argc < 1)
136 return(0);
137 if((cmdn = icwcstombs(argv[0], "us-ascii")) == NULL)
138 {
139 flog(LOG_WARNING, "could not convert %ls to us-ascii", argv[0]);
140 return(1);
141 }
142 ret = 1;
143 handled = 0;
144 if(!strcmp(cmdn, "set"))
145 {
146 handled = 1;
147 ret = 0;
148 if((p = wcschr(argv[1], L'.')) == NULL)
149 {
150 flog(LOG_WARNING, "illegal configuration variable format: %ls", argv[1]);
151 errno = EINVAL;
152 free(cmdn);
153 return(1);
154 }
155 *(p++) = L'\0';
156 if((buf = icwcstombs(argv[1], "us-ascii")) == NULL)
157 {
158 flog(LOG_WARNING, "could not convert %ls to us-ascii", argv[1]);
159 free(cmdn);
160 return(1);
161 }
162 if((buf2 = icwcstombs(p, "us-ascii")) == NULL)
163 {
164 free(buf);
165 flog(LOG_WARNING, "could not convert %ls to us-ascii", p);
166 free(cmdn);
167 return(1);
168 }
169 for(module = modules; module != NULL; module = module->next)
170 {
171 if(!strcmp(module->name, buf) && (module->vars != NULL))
172 {
173 for(var = module->vars; var->type != CONF_VAR_END; var++)
174 {
175 if(!strcmp(var->name, buf2))
176 {
177 cb = 0;
178 switch(var->type)
179 {
180 case CONF_VAR_BOOL:
181 wcstolower(argv[2]);
182 if(!wcscmp(argv[2], L"off") ||
183 !wcscmp(argv[2], L"false") ||
184 !wcscmp(argv[2], L"no") ||
185 !wcscmp(argv[2], L"0"))
186 {
187 if(var->val.num)
188 cb = 1;
189 var->val.num = 0;
190 } else if(!wcscmp(argv[2], L"on") ||
191 !wcscmp(argv[2], L"true") ||
192 !wcscmp(argv[2], L"yes") ||
193 !wcscmp(argv[2], L"1")) {
194 if(!var->val.num)
195 cb = 1;
196 var->val.num = 1;
197 } else {
198 flog(LOG_WARNING, "unrecognized boolean: %ls", argv[2]);
199 }
200 break;
201 case CONF_VAR_INT:
202 num = wcstol(argv[2], &p, 0);
203 if(p == argv[2])
204 {
205 flog(LOG_WARNING, "%ls: not a number, ignoring", argv[2]);
206 ret = 1;
207 } else {
208 if(*p != L'\0')
209 flog(LOG_WARNING, "%ls: could not entirely parse as a number, ignoring trailing garbage", argv[2]);
210 if(num != var->val.num)
211 cb = 1;
212 var->val.num = num;
213 }
214 break;
215 case CONF_VAR_STRING:
216 if(wcscmp(var->val.str, argv[2]))
217 cb = 1;
218 free(var->val.str);
219 var->val.str = swcsdup(argv[2]);
220 break;
221 case CONF_VAR_IPV4:
222 if((valbuf = icwcstombs(argv[2], "us-ascii")) == NULL)
223 {
224 flog(LOG_WARNING, "could not convert IPv4 address to as-ascii in var %s, ignoring", buf2);
225 } else {
226 if(!inet_aton(valbuf, &newipv4))
227 {
228 flog(LOG_WARNING, "could not parse IPv4 address (%s), ignoring", valbuf);
229 memcpy(&var->val.ipv4, &var->defaults.ipv4, sizeof(var->val.ipv4));
230 } else {
231 if(memcmp(&newipv4, &var->val.ipv4, sizeof(newipv4)))
232 cb = 1;
233 memcpy(&var->val.ipv4, &newipv4, sizeof(newipv4));
234 }
235 free(valbuf);
236 }
237 break;
238 }
239 if(cb)
240 CBCHAINDOCB(var, conf_update, var);
241 break;
242 }
243 }
244 if(var == NULL)
245 flog(LOG_WARNING, "variable %s not found, ignoring set command", buf2);
246 break;
247 }
248 }
249 if(module == NULL)
250 flog(LOG_WARNING, "module %s not found, ignoring set command", buf);
251 free(buf2);
252 free(buf);
253 }
254 for(module = modules; !handled && (module != NULL); module = module->next)
255 {
256 if(module->cmds != NULL)
257 {
258 for(cmd = module->cmds; cmd->name != NULL; cmd++)
259 {
260 if(!strcmp(cmd->name, cmdn))
261 {
262 handled = 1;
263 ret = cmd->handler(argc, argv);
264 break;
265 }
266 }
267 }
268 }
269 if(!handled)
270 flog(LOG_WARNING, "command not found: %s", cmdn);
271 free(cmdn);
272 return(ret);
273}
274
d3372da9 275void readconfig(FILE *stream)
276{
277 int state;
278 wint_t c;
279 wchar_t *words[16];
280 wchar_t *buf, *p, *p2;
281 int w;
282 int line;
283
284 buf = smalloc(sizeof(wchar_t) * 1024);
285 state = 0;
286 c = getwc(stream);
287 w = 0;
288 line = 1;
289 p = buf;
290 while(c != WEOF)
291 {
292 if(c == '#')
293 {
294 do
295 c = getwc(stream);
296 while((c != WEOF) && (c != L'\n'));
297 continue;
298 }
299 switch(state)
300 {
301 case 0:
302 if(iswspace(c))
303 {
304 if(c == L'\n')
305 {
306 line++;
307 if(runconfcmd(w, words))
308 flog(LOG_WARNING, "ignoring this command on line %i", line);
309 w = 0;
310 }
311 c = getwc(stream);
312 } else {
313 state = 1;
314 p2 = p;
315 }
316 break;
317 case 1:
318 if(c == L'\"')
319 {
320 state = 2;
321 c = getwc(stream);
322 } else if(iswspace(c)) {
323 if(w >= 16)
324 {
325 flog(LOG_WARNING, "too many words on config line %i, ignoring rest", line);
326 } else {
327 *(p++) = L'\0';
328 words[w++] = p2;
329 }
330 state = 0;
331 } else {
332 if(c == L'\\')
333 c = getwc(stream);
334 if(p - buf < 1023)
335 *(p++) = c;
336 else
337 flog(LOG_WARNING, "too many characters on config line %i, ignoring rest", line);
338 c = getwc(stream);
339 }
340 break;
341 case 2:
342 if(c == L'\"')
343 {
344 c = getwc(stream);
345 state = 1;
346 } else {
347 if(c == L'\\')
348 c = getwc(stream);
349 if(p - buf < 1023)
350 *(p++) = c;
351 else
352 flog(LOG_WARNING, "too many characters on config line %i, ignoring rest", line);
353 c = getwc(stream);
354 }
355 break;
356 }
357 }
358 free(buf);
359 if(ferror(stream))
360 flog(LOG_WARNING, "error on configuration stream: %s", strerror(errno));
361 if(state != 0)
362 flog(LOG_WARNING, "unexpected end of file");
363}
05fe12f8 364
365/* {store,fetch}var re-opens the database every time, just in case two
366 * doldacond processes would be running simultaneously. */
367void storevar(char *key, void *val, size_t len)
368{
369 char *dbname;
370 GDBM_FILE db;
371 datum k, v;
372
373 dbname = findfile("dc-vardb", "dc-vardb", NULL, 1);
374 if((db = gdbm_open(dbname, 0, GDBM_WRCREAT, 0666, NULL)) == NULL)
375 {
376 flog(LOG_CRIT, "could not open var database for writing, cannot continue: %s", gdbm_strerror(gdbm_errno));
377 abort();
378 }
379 free(dbname);
380 k.dptr = key;
381 k.dsize = strlen(key);
382 v.dptr = val;
383 v.dsize = len;
384 gdbm_store(db, k, v, GDBM_REPLACE);
385 gdbm_close(db);
386}
387
388void *fetchvar(char *key, size_t *lenb)
389{
390 char *dbname;
391 GDBM_FILE db;
392 datum k, v;
393
394 if((dbname = findfile("dc-vardb", "dc-vardb", NULL, 0)) == NULL)
395 return(NULL);
396 if((db = gdbm_open(dbname, 0, GDBM_READER, 0666, NULL)) == NULL)
397 return(NULL);
398 free(dbname);
399 k.dptr = key;
400 k.dsize = strlen(key);
401 v = gdbm_fetch(db, k);
402 gdbm_close(db);
403 if(v.dptr == NULL)
404 return(NULL);
405 if(lenb != NULL)
406 *lenb = v.dsize;
407 return(v.dptr);
408}