conf-file.c 13.4 KB
Newer Older
Per Cederqvist's avatar
Per Cederqvist committed
1
/*
2
3
 * $Id: conf-file.c,v 1.34 2003/08/23 16:38:18 ceder Exp $
 * Copyright (C) 1994-1995, 1998-1999, 2001-2003  Lysator Academic Computer Association.
Per Cederqvist's avatar
Per Cederqvist committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 1, or (at your option) 
 * any later version.
 * 
 * LysKOM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
Per Cederqvist's avatar
Per Cederqvist committed
23
 * Please report bugs at http://bugzilla.lysator.liu.se/. 
Per Cederqvist's avatar
Per Cederqvist committed
24
25
26
27
28
 */
/* 
 * Read configuration files.
 */

David Byers's avatar
David Byers committed
29
30
31
32
33

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

34
#include <errno.h>
Per Cederqvist's avatar
Per Cederqvist committed
35
36
#include <stdio.h>
#include <sys/types.h>
37
#include "timewrap.h"
Per Cederqvist's avatar
Per Cederqvist committed
38
#include <assert.h>
39
#include <ctype.h>
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifdef HAVE_STRING_H
#  include <string.h>
#else
#  ifdef HAVE_STRINGS_H
#    include <strings.h>
#  endif
#endif
#ifndef HAVE_STRCHR
#  define strchr index
#endif
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
53
54

#include "s-string.h"
55
#include "server/smalloc.h"
Per Cederqvist's avatar
Per Cederqvist committed
56
57
58
59
60
#include "log.h"
#include "misc-types.h"
#include "kom-types.h"
#include "conf-file.h"
#include "lyskomd.h"
61
#include "eintr.h"
62
#include "timeval-util.h"
Per Cederqvist's avatar
Per Cederqvist committed
63

David Byers's avatar
David Byers committed
64
#define MAXLINE 1001
Per Cederqvist's avatar
Per Cederqvist committed
65
66
67
68
69
70
71
72
73

static int *assignment_count = NULL;
static int  npar = 0;

static void
init_init(const struct parameter *par)
{
    int ix;

Per Cederqvist's avatar
Per Cederqvist committed
74
    for (npar = 0; par[npar].name != NULL; npar++)
75
76
	if (par[npar].tp->ctor != NULL)
	    par[npar].tp->ctor(&par[npar]);
Per Cederqvist's avatar
Per Cederqvist committed
77

Per Cederqvist's avatar
Per Cederqvist committed
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    assert (assignment_count == NULL);
    assignment_count = smalloc(npar * sizeof (*assignment_count));
    
    for (ix = 0; ix < npar; ix++)
	assignment_count[ix] = 0;
}

/* Check that each parameter is assigned at least as many times
   as the struct parameter says.  Assign the default value if it
   is not assigned at all. */
static void
assign_defaults(const struct parameter *par,
		int *err)
{
    int ix;

    for (ix = 0; ix < npar; ix++)
    {
	if (assignment_count[ix] < par[ix].min_assignments)
	{
David Byers's avatar
David Byers committed
98
99
	    kom_log ("Parameter %s only assigned %d times (%d times needed)\n",
                     par[ix].name, assignment_count[ix], par[ix].min_assignments);
Per Cederqvist's avatar
Per Cederqvist committed
100
101
102
103
	    (*err)++;
	}
	else if (assignment_count[ix] == 0)
	{
104
	    if ((*par[ix].tp->assigner)(par[ix].default_val, &par[ix]) != OK)
Per Cederqvist's avatar
Per Cederqvist committed
105
	    {
David Byers's avatar
David Byers committed
106
		kom_log ("default assigner failed for %s\n", par[ix].name);
Per Cederqvist's avatar
Per Cederqvist committed
107
		(*err)++;
Per Cederqvist's avatar
Per Cederqvist committed
108
	    }
Per Cederqvist's avatar
Per Cederqvist committed
109
110
111
112
	}
    }
}

Per Cederqvist's avatar
Per Cederqvist committed
113
114
115
116
/* Perform the assignments on one line. 
   Returns EOF on end-of-file, 0 otherwise. 
   A log message is printed, and (*err) is incremented,
   if errors are detected. */
Per Cederqvist's avatar
Per Cederqvist committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static int
configure_line(FILE *fp,
	       const struct parameter *par,
	       int *err)
{
    char line[MAXLINE];
    char *start;
    char *val;
    char *end;
    int   found;
    int   ix;

    if (fgets(line, MAXLINE, fp) == NULL)
	return EOF;

132
133
134
135
    /* Separate the line into its two parts.  Set up ``start'' to point 
       to the beginning of the variable name, ``val'' to point to the
       beginning of the value, and remove the trailing newline (using
       ``end''). */
Per Cederqvist's avatar
Per Cederqvist committed
136
137
138
139
140
141
142
    for (start = line; *start == ' ' || *start == '\t'; start++)
	;
    switch (*start)
    {
    case '#':			/* Comment line */
	return 0;
    case '\0':			/* Too long */
David Byers's avatar
David Byers committed
143
	kom_log ("line too long (max %d chars allowed): %s\n", MAXLINE-1, line);
Per Cederqvist's avatar
Per Cederqvist committed
144
145
146
147
148
149
150
151
152
	(*err)++;
	return 0;
    case '\n':			/* Empty line */
	return 0;
    }
    
    val = strchr(line, ':');
    if (val == NULL)
    {
153
154
155
156
157
	/* Remove any trailing newline to avoid a blank line in the log. */
	end = strchr(line, '\n');
	if (end != NULL)
	    *end = '\0';

David Byers's avatar
David Byers committed
158
	kom_log("missing colon: %s\n", line);
Per Cederqvist's avatar
Per Cederqvist committed
159
160
161
162
163
164
165
166
167
	(*err)++;
	return 0;
    }
    *val = '\0';		/* NUL-terminate key */
    for (val++; *val == ' ' || *val == '\t'; val++)
	;
    switch (*val)
    {
    case '\0':
David Byers's avatar
David Byers committed
168
	kom_log ("line too long (max %d chars allowed): %s\n", MAXLINE-1, line);
Per Cederqvist's avatar
Per Cederqvist committed
169
170
171
172
173
174
175
	(*err)++;
	return 0;
    }

    end = strchr(val, '\n');
    if (end == NULL)
    {
David Byers's avatar
David Byers committed
176
	kom_log ("line too long (max %d chars allowed): %s\n", MAXLINE-1, line);
Per Cederqvist's avatar
Per Cederqvist committed
177
178
179
180
181
182
183
184
185
	(*err)++;
	return 0;
    }	
    *end = '\0';

    /* Set the variable which match this line. */
    found = 0;
    for (ix = 0; ix < npar; ix++)
    {
186
	if (!strcmp(start, par[ix].name))
Per Cederqvist's avatar
Per Cederqvist committed
187
188
	{
	    found++;
David Byers's avatar
David Byers committed
189
	    if (assignment_count[ix] >= par[ix].max_assignments
Per Cederqvist's avatar
Per Cederqvist committed
190
		&& par[ix].max_assignments != -1)
Per Cederqvist's avatar
Per Cederqvist committed
191
192
	    {
		(*err)++;
David Byers's avatar
David Byers committed
193
		kom_log ("variable already assigned %d times: %s\n", 
Per Cederqvist's avatar
Per Cederqvist committed
194
195
196
197
198
		     assignment_count[ix], line);
	    }
	    else
	    {
		assignment_count[ix]++;
199
		if ((*par[ix].tp->assigner)(val, &par[ix]) != OK)
Per Cederqvist's avatar
Per Cederqvist committed
200
		{
David Byers's avatar
David Byers committed
201
		    kom_log ("assigner for %s failed\n", par[ix].name);
Per Cederqvist's avatar
Per Cederqvist committed
202
		    (*err)++;
Per Cederqvist's avatar
Per Cederqvist committed
203
		}
Per Cederqvist's avatar
Per Cederqvist committed
204
205
206
207
	    }
	}
    }

Per Cederqvist's avatar
Per Cederqvist committed
208
    if (found != 1)
Per Cederqvist's avatar
Per Cederqvist committed
209
    {
David Byers's avatar
David Byers committed
210
	kom_log ("line matches %d times: %s\n", found, line);
Per Cederqvist's avatar
Per Cederqvist committed
211
212
213
214
215
216
	(*err)++;
	return 0;
    }
    return 0;
}

217
Success
Per Cederqvist's avatar
Per Cederqvist committed
218
219
220
221
222
223
224
225
read_config(const char *config_file, 
	    const struct parameter *par)
{
    FILE *fp;
    int errs = 0;

    init_init(par);

226
    fp = i_fopen(config_file, "r");
Per Cederqvist's avatar
Per Cederqvist committed
227
    if (fp == NULL)
228
229
230
231
    {
	free_config(par);
	sfree(assignment_count);
	assignment_count = 0;
232
	restart_kom("cannot open config file %s\n", config_file);
233
    }
Per Cederqvist's avatar
Per Cederqvist committed
234
235
236

    while (configure_line(fp, par, &errs) != EOF)
	;
237
    i_fclose(fp);
Per Cederqvist's avatar
Per Cederqvist committed
238
239
240
241
    assign_defaults(par, &errs);

    sfree(assignment_count);
    assignment_count = 0;
242
243
244
245
246

    if (errs)
	return FAILURE;
    else
	return OK;
Per Cederqvist's avatar
Per Cederqvist committed
247
248
}

249
250
251
252
253
254
255
256
257
258
259
void
free_config(const struct parameter *par)
{
    int i;

    for (i = 0; par[i].name != NULL; ++i)
        if (par[i].tp->dtor != NULL)
            (*par[i].tp->dtor)(&par[i]);
}


Per Cederqvist's avatar
Per Cederqvist committed
260
static Success
Per Cederqvist's avatar
Per Cederqvist committed
261
262
263
assign_text_no(const char *val, 
	       const struct parameter *par)
{
264
265
    int c;

266
    /* FIXME (bug 143): use strtol */
Per Cederqvist's avatar
Per Cederqvist committed
267
    if (val != NULL)
268
    {
269
270
	c = (unsigned char)*val;
	if (!isascii(c) || !isdigit(c))
271
	    return FAILURE;
Per Cederqvist's avatar
Per Cederqvist committed
272
	*(Text_no*)par->value = atol(val);
273
    }
Per Cederqvist's avatar
Per Cederqvist committed
274
275
276
    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
277
static Success
Per Cederqvist's avatar
Per Cederqvist committed
278
279
280
assign_conf_no(const char *val, 
	       const struct parameter *par)
{
281
282
    int c;

283
    /* FIXME (bug 143): use strtol */
Per Cederqvist's avatar
Per Cederqvist committed
284
    if (val != NULL)
285
    {
286
287
	c = (unsigned char)*val;
	if (!isascii(c) || !isdigit(c))
288
	    return FAILURE;
Per Cederqvist's avatar
Per Cederqvist committed
289
	*(Conf_no*)par->value = atol(val);
290
    }
Per Cederqvist's avatar
Per Cederqvist committed
291
292
293
    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
294
static Success
Per Cederqvist's avatar
Per Cederqvist committed
295
296
297
assign_int(const char *val, 
	   const struct parameter *par)
{
298
299
    int c;

300
    /* FIXME (bug 143): use strtol */
Per Cederqvist's avatar
Per Cederqvist committed
301
    if (val != NULL)
302
    {
303
304
	c = (unsigned char)*val;
	if ((!isascii(c) || !isdigit(c)) && c != '-')
305
	    return FAILURE;
Per Cederqvist's avatar
Per Cederqvist committed
306
	*(int*)par->value = atol(val);
307
    }
Per Cederqvist's avatar
Per Cederqvist committed
308
309
310
    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
311
static Success
David Byers's avatar
David Byers committed
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
assign_ulong(const char *val,
             const struct parameter *par)
{
    int c;

    if (val != NULL)
    {
        c = (unsigned char)*val;
	if ((!isascii(c) || !isdigit(c)) && c != '-')
	    return FAILURE;
	*(unsigned long*)par->value = (unsigned long)atol(val);
    }
    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
327
static Success
Per Cederqvist's avatar
Per Cederqvist committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
assign_uint(const char *val,
	    const struct parameter *par)
{
    int c;

    if (val != NULL)
    {
        c = (unsigned char)*val;
	if ((!isascii(c) || !isdigit(c)) && c != '-')
	    return FAILURE;
	*(unsigned int*)par->value = (unsigned int)atol(val);
    }
    return OK;
}

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
static void
ctor_string(const struct parameter *par)
{
    *(char**)par->value = NULL;
}

static void
dtor_string(const struct parameter *par)
{
    if (*(char**)par->value != NULL)
    {
	sfree(*(char**)par->value);
	*(char**)par->value = NULL;
    }
}

Per Cederqvist's avatar
Per Cederqvist committed
359
static Success
Per Cederqvist's avatar
Per Cederqvist committed
360
361
362
assign_string(const char *val, 
	      const struct parameter *par)
{
363
364
365
    if (*(char**)par->value != NULL)
	sfree(*(char**)par->value);

Per Cederqvist's avatar
Per Cederqvist committed
366
367
368
369
370
371
372
    if (val == NULL)
	*(char**)par->value = NULL;
    else
    {
	*(char**)par->value = smalloc(strlen(val) + 1);
	strcpy(*(char**)par->value, val);
    }
Per Cederqvist's avatar
Per Cederqvist committed
373
374
375
    return OK;
}

Per Cederqvist's avatar
Per Cederqvist committed
376
static Success
Per Cederqvist's avatar
Per Cederqvist committed
377
378
379
assign_bool(const char *val, 
	    const struct parameter *par)
{
Per Cederqvist's avatar
Per Cederqvist committed
380
    if (val == NULL 
381
382
383
384
	||!strcmp(val, "false") 
	|| !strcmp(val, "off")
	|| !strcmp(val, "no")
	|| !strcmp(val, "0"))
Per Cederqvist's avatar
Per Cederqvist committed
385
386
387
    {
	*(Bool*)par->value = FALSE;
    }
388
389
390
391
    else if (!strcmp(val, "true") 
	|| !strcmp(val, "on")
	|| !strcmp(val, "yes")
	|| !strcmp(val, "1"))
Per Cederqvist's avatar
Per Cederqvist committed
392
    {
Per Cederqvist's avatar
Per Cederqvist committed
393
	*(Bool*)par->value = TRUE;
Per Cederqvist's avatar
Per Cederqvist committed
394
    }
Per Cederqvist's avatar
Per Cederqvist committed
395
    else
Per Cederqvist's avatar
Per Cederqvist committed
396
    {
Per Cederqvist's avatar
Per Cederqvist committed
397
	return FAILURE;
Per Cederqvist's avatar
Per Cederqvist committed
398
    }
Per Cederqvist's avatar
Per Cederqvist committed
399
400
401

    return OK;
}
David Byers's avatar
David Byers committed
402
403


Per Cederqvist's avatar
Per Cederqvist committed
404
static Success
405
406
407
408
409
410
411
412
assign_double(const char *val, 
	      const struct parameter *par)
{
    char *tail = NULL;

    if (val != NULL)
    {
	errno = 0;
413
	*(double*)par->value = strtod(val, &tail);
414
415
416
417
418
419
	if (errno || tail == val || *tail != '\0')
	    return FAILURE;
    }
    return OK;
}

420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
struct suffix_conversion {
    const char *suffix;
    double factor;
};

static const struct suffix_conversion suffix_table[] = {
    {"seconds",  1.0},
    {"second",   1.0},
    {"sec",      1.0},
    {"s",        1.0},

    {"minutes", 60.0},
    {"minute",  60.0},
    {"min",     60.0},

    {"hours", 3600.0},
    {"hour",  3600.0},
    {"h",     3600.0},

    {"days", 86400.0},
    {"day",  86400.0},
    {"d",    86400.0},

    {"milliseconds", 0.001},
    {"millisecond",  0.001},
    {"milli",        0.001},
    {"ms",           0.001},
    
    {"microseconds", 0.000001},
    {"microsecond",  0.000001},
    {"micro",        0.000001},
    {"us",           0.000001},
    {"u",            0.000001},
    
    {"kiloseconds",     1000.0},
    {"megaseconds",  1000000.0},

    {"microfortnights", 1.2096},
    {"microfortnight",  1.2096},
    
    {NULL, 0.0},
};

Per Cederqvist's avatar
Per Cederqvist committed
463
static Success
464
465
466
467
468
assign_timeval(const char *val, 
	       const struct parameter *par)
{
    char *tail = NULL;
    double number;
469
    double err;
470
471
    const char *suffix;
    const struct suffix_conversion *s;
472
    struct timeval tv;
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487

    assert(par->default_suffix != NULL);

    if (val != NULL)
    {
	errno = 0;
	number = strtod(val, &tail);
	if (errno || tail == val)
	    return FAILURE;

	if (*tail != '\0')
	    suffix = tail;
	else
	    suffix = par->default_suffix;

488
	while (isspace((int)(unsigned char)*suffix))
489
490
	    ++suffix;

491
492
493
494
495
	for (s = suffix_table; s->suffix != NULL; ++s)
	    if (!strcmp(s->suffix, suffix))
		break;

	if (s->suffix == NULL)
496
497
	{
	    kom_log("Bad suffix for parameter %s\n", par->name);
498
	    return FAILURE;
499
	}
500
501
502

	number *= s->factor;

503
504
505
506
507
508
509
510
511
512
513
	if (number < 0)
	{
	    kom_log("Negative values not supported for parameter %s\n",
		    par->name);
	    return FAILURE;
	}

	tv = timeval_ctor(number, ((number - (int)number) * 1000000) + 0.5);
	err = (tv.tv_sec + 1.0e-6 * tv.tv_usec) - number;
	if (err < 0)
	    err = -err;
514
	if ((err > 1.0e-6 && err > 1.0e-6 * number)
515
516
517
518
519
520
521
522
	    || tv.tv_sec < 0
	    || tv.tv_usec < 0
	    || tv.tv_usec >= 1000000)
	{
	    kom_log("Overflow for parameter %s (%s)\n", par->name, val);
	    return FAILURE;
	}
	*(struct timeval*)par->value = tv;
523
524
525
526
    }
    return OK;
}

527

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
static void
ctor_ipport_list(const struct parameter *par)
{
    struct ipport_list *val = par->value;
    val->size = 0;
    val->entries = NULL;
}

static void
dtor_ipport_list(const struct parameter *par)
{
    int i;

    struct ipport_list *val = par->value;
    for (i = 0; i < val->size; i++)
    {
	sfree(val->entries[i].ipaddr);
	sfree(val->entries[i].port);
    }
    sfree(val->entries);
    val->entries = NULL;
    val->size = 0;
}


static Success
assign_ipport_list(const char *val, 
		   const struct parameter *par)
{
    struct ipport_list *list = par->value;
    struct ipport_entry *entry;
    const char *host_start = NULL;
    const char *host_end = NULL;
    const char *port_start = val;

    if (val[0] == '[')
    {
	/* IPv6 address. */
#ifndef USE_INET6
	kom_log("IPv6 address found in \"Listen:\" directive, but"
		"IPv6 support not enabled in this build.\n");
	return FAILURE;
#else
	host_start = val + 1;
	host_end = strchr(val, ']');
	if (host_end == NULL)
	{
	    kom_log("Unterminated IPv6 address: %s\n", val);
	    return FAILURE;
	}
	if (*(host_end+1) != ':')
	{
	    kom_log("IPv6 address not followed by colon and port: %s\n", val);
	    return FAILURE;
	}
	port_start = host_end + 2;
#endif
    }
    else if ((host_end = strchr(val, ':')) != NULL)
    {
	/* IPv4 address or DNS name. */
	host_start = val;
	port_start = host_end + 1;
    }

    list->entries = srealloc(list->entries,
			     (list->size + 1) * sizeof(*list->entries));
    entry = &list->entries[list->size];
    list->size++;
    if (host_start == NULL)
	entry->ipaddr = NULL;
    else
    {
	entry->ipaddr = smalloc(host_end - host_start + 1);
	memcpy(entry->ipaddr, host_start, host_end - host_start);
	entry->ipaddr[host_end - host_start] = '\0';
    }

    if (*port_start == '\0')
    {
	entry->port = smalloc(strlen(par->default_suffix) + 1);
	strcpy(entry->port, par->default_suffix);
    }
    else
    {
	entry->port = smalloc(strlen(port_start) + 1);
	strcpy(entry->port, port_start);
    }

    return OK;
}


Per Cederqvist's avatar
Per Cederqvist committed
621
const struct datatype cf_text_no = {
622
    assign_text_no, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
623
624
625
};

const struct datatype cf_conf_no = {
626
    assign_conf_no, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
627
628
629
};

const struct datatype cf_int = {
630
    assign_int, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
631
632
633
};

const struct datatype cf_ulong = {
634
    assign_ulong, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
635
636
637
};

const struct datatype cf_uint = {
638
    assign_uint, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
639
640
641
};

const struct datatype cf_string = {
642
    assign_string, ctor_string, dtor_string,
Per Cederqvist's avatar
Per Cederqvist committed
643
644
645
};

const struct datatype cf_bool = {
646
    assign_bool, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
647
648
649
};

const struct datatype cf_double = {
650
    assign_double, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
651
652
653
};

const struct datatype cf_timeval = {
654
    assign_timeval, NULL, NULL,
Per Cederqvist's avatar
Per Cederqvist committed
655
};
656
657
658
659

const struct datatype cf_ipport_list = {
    assign_ipport_list, ctor_ipport_list, dtor_ipport_list,
};