1/*
2 * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 *---------------------------------------------------------------------------
26 *
27 *	i4b daemon - config file processing
28 *	-----------------------------------
29 *
30 *	$Id: rc_config.c,v 1.25 2009/04/16 05:56:32 lukem Exp $
31 *
32 * $FreeBSD$
33 *
34 *      last edit-date: [Sat Jan  6 12:57:36 2001]
35 *
36 *---------------------------------------------------------------------------*/
37
38#include <sys/types.h>
39#include <sys/socket.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42
43#include <sys/callout.h>
44#include <sys/ioctl.h>
45#include <ifaddrs.h>
46
47#include "isdnd.h"
48#include "rc_parse.h"
49
50#include "monitor.h"
51
52extern int lineno;
53extern char *yytext;
54
55extern FILE *yyin;
56extern int yyparse(void);
57
58static void set_config_defaults(void);
59static void check_config(void);
60static void print_config(void);
61static void parse_valid(char *dt);
62static int lookup_l4_driver(const char *name);
63void init_currrent_cfg_state(void);
64static void set_isppp_auth(struct cfg_entry*);
65static void set_autoupdown(struct cfg_entry*);
66void flush_config(void);
67
68static int nregexpr = 0;
69static int nregprog = 0;
70static struct cfg_entry * current_cfe = NULL;
71struct isdn_ctrl_state * cur_ctrl = NULL;
72
73/*---------------------------------------------------------------------------*
74 *	called from main to read and process config file
75 *---------------------------------------------------------------------------*/
76void
77configure(const char *filename, int reread)
78{
79	extern void reset_scanner(FILE *inputfile);
80
81	set_config_defaults();
82
83	yyin = fopen(filename, "r");
84
85	if (reread)
86	{
87		reset_scanner(yyin);
88		current_cfe = NULL;
89	}
90
91	if (yyin == NULL)
92	{
93		logit(LL_ERR, "cannot fopen file [%s]", filename);
94		exit(1);
95	}
96
97	yyparse();
98
99	monitor_fixup_rights();
100
101	check_config();		/* validation and consistency check */
102
103	fclose(yyin);
104
105	if (do_print)
106	{
107		if (config_error_flag)
108		{
109			logit(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
110			exit(1);
111		}
112		print_config();
113		do_exit(0);
114	}
115}
116
117/*---------------------------------------------------------------------------*
118 *	yacc error routine
119 *---------------------------------------------------------------------------*/
120void
121yyerror(const char *msg)
122{
123	logit(LL_ERR, "configuration error: %s at line %d, token \"%s\"", msg, lineno+1, yytext);
124	config_error_flag++;
125}
126
127/*
128 * Prepare a new default entry
129 */
130void
131init_currrent_cfg_state()
132{
133	if (current_cfe != NULL) {
134		add_cfg_entry(current_cfe);
135	}
136	current_cfe = malloc(sizeof(struct cfg_entry));
137	memset(current_cfe, 0, sizeof(struct cfg_entry));
138
139	current_cfe->isdncontroller = INVALID;
140	current_cfe->isdnchannel = CHAN_ANY;
141	current_cfe->usrdevice = INVALID;
142	current_cfe->usrdeviceunit = INVALID;
143	current_cfe->remote_numbers_handling = RNH_LAST;
144	current_cfe->dialin_reaction = REACT_IGNORE;
145	current_cfe->b1protocol = BPROT_NONE;
146	current_cfe->unitlength = UNITLENGTH_DEFAULT;
147	current_cfe->earlyhangup = EARLYHANGUP_DEFAULT;
148	current_cfe->ratetype = INVALID_RATE;
149	current_cfe->unitlengthsrc = ULSRC_NONE;
150	current_cfe->answerprog = ANSWERPROG_DEF;
151	current_cfe->callbackwait = CALLBACKWAIT_MIN;
152	current_cfe->calledbackwait = CALLEDBACKWAIT_MIN;
153	current_cfe->dialretries = DIALRETRIES_DEF;
154	current_cfe->recoverytime = RECOVERYTIME_MIN;
155	current_cfe->dialouttype = DIALOUT_NORMAL;
156	current_cfe->inout = DIR_INOUT;
157	current_cfe->ppp_expect_auth = AUTH_UNDEF;
158	current_cfe->ppp_send_auth = AUTH_UNDEF;
159	current_cfe->ppp_auth_flags = AUTH_RECHALLENGE | AUTH_REQUIRED;
160	current_cfe->cdid = CDID_UNUSED;
161	current_cfe->state = ST_IDLE;
162	current_cfe->aoc_valid = AOC_INVALID;
163	current_cfe->autoupdown = AUTOUPDOWN_YES;
164}
165
166/*---------------------------------------------------------------------------*
167 *	fill all config entries with default values
168 *---------------------------------------------------------------------------*/
169static void
170set_config_defaults(void)
171{
172	int i;
173
174	/* system section cleanup */
175
176	nregprog = nregexpr = 0;
177
178	rt_prio = RTPRIO_NOTUSED;
179
180	mailer[0] = '\0';
181	mailto[0] = '\0';
182
183	/* clean regular expression table */
184
185	for (i=0; i < MAX_RE; i++)
186	{
187		if (rarr[i].re_expr)
188			free(rarr[i].re_expr);
189		rarr[i].re_expr = NULL;
190
191		if (rarr[i].re_prog)
192			free(rarr[i].re_prog);
193		rarr[i].re_prog = NULL;
194
195		rarr[i].re_flg = 0;
196	}
197
198	strlcpy(rotatesuffix, "", sizeof(rotatesuffix));
199}
200
201static void
202set_autoupdown(struct cfg_entry *cep)
203{
204        struct ifaddrs *res = NULL, *p;
205	struct ifreq ifr;
206	int r, s, cnt, in6;
207
208	s = socket(AF_INET, SOCK_DGRAM, 0);
209	memset(&ifr, 0, sizeof ifr);
210	snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "%s%d", cep->usrdevicename, cep->usrdeviceunit);
211	r = ioctl(s, SIOCGIFFLAGS, &ifr);
212
213	/*
214	 * See if this interface has got any valid addresses - if not,
215	 * leave it alone.
216	 */
217	if (r >= 0 && !(ifr.ifr_flags & IFF_UP)) {
218		cnt = in6 = 0;
219		if (getifaddrs(&res) == 0) {
220			for (p = res; p; p = p->ifa_next) {
221				if (p->ifa_addr == NULL)
222					continue;
223				if (p->ifa_addr->sa_family == AF_LINK)
224					continue;
225				if (strcmp(p->ifa_name, ifr.ifr_name) != 0)
226					continue;
227				if (p->ifa_addr->sa_family == AF_INET6)
228					in6 = 1;
229				cnt++;
230			}
231			freeifaddrs(res);
232		}
233
234		if (in6)
235			cnt--;	/* XXX - heuristic to adjust for INET6 local scope */
236
237		/* Ok, we have some addres - so UP the interface */
238		if (cnt > 0) {
239			ifr.ifr_flags |= IFF_UP;
240			r = ioctl(s, SIOCSIFFLAGS, &ifr);
241			if (r >= 0)
242				cep->autoupdown |= AUTOUPDOWN_DONE;
243		}
244	}
245
246	close(s);
247}
248
249static void
250set_isppp_auth(struct cfg_entry *cep)
251{
252	struct spppauthcfg spcfg;
253	int s;
254	int doioctl = 0;
255
256	if (cep->ppp_expect_auth == AUTH_UNDEF
257	   && cep->ppp_send_auth == AUTH_UNDEF)
258		return;
259
260	if (cep->ppp_expect_auth == AUTH_NONE
261	   || cep->ppp_send_auth == AUTH_NONE)
262		doioctl = 1;
263
264	if ((cep->ppp_expect_auth == AUTH_CHAP
265	     || cep->ppp_expect_auth == AUTH_PAP)
266	    && cep->ppp_expect_name != NULL
267	    && cep->ppp_expect_password != NULL)
268		doioctl = 1;
269
270	if ((cep->ppp_send_auth == AUTH_CHAP || cep->ppp_send_auth == AUTH_PAP)
271			&& cep->ppp_send_name != NULL
272			&& cep->ppp_send_password != NULL)
273		doioctl = 1;
274
275	if (!doioctl)
276		return;
277
278	memset(&spcfg, 0, sizeof spcfg);
279	snprintf(spcfg.ifname, sizeof(spcfg.ifname), "%s%d",
280		cep->usrdevicename, cep->usrdeviceunit);
281
282	/* use a random AF to create the socket */
283	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
284		logit(LL_ERR, "ERROR opening control socket at line %d!", lineno);
285		config_error_flag++;
286		return;
287	}
288
289	if (ioctl(s, SPPPGETAUTHCFG, &spcfg) == -1) {
290		logit(LL_ERR, "ERROR fetching active PPP authentication info for %s at line %d!", spcfg.ifname, lineno);
291		close(s);
292		config_error_flag++;
293		return;
294	}
295	if (cep->ppp_expect_auth != AUTH_UNDEF)
296	{
297		if (cep->ppp_expect_auth == AUTH_NONE)
298		{
299			spcfg.hisauth = SPPP_AUTHPROTO_NONE;
300		}
301		else if ((cep->ppp_expect_auth == AUTH_CHAP
302			  || cep->ppp_expect_auth == AUTH_PAP)
303			 && cep->ppp_expect_name != NULL
304			 && cep->ppp_expect_password != NULL)
305		{
306			spcfg.hisauth = cep->ppp_expect_auth == AUTH_PAP ? SPPP_AUTHPROTO_PAP : SPPP_AUTHPROTO_CHAP;
307			spcfg.hisname = cep->ppp_expect_name;
308			spcfg.hisname_length = strlen(cep->ppp_expect_name)+1;
309			spcfg.hissecret = cep->ppp_expect_password;
310			spcfg.hissecret_length = strlen(cep->ppp_expect_password)+1;
311		}
312	}
313	if (cep->ppp_send_auth != AUTH_UNDEF)
314	{
315		if (cep->ppp_send_auth == AUTH_NONE)
316		{
317			spcfg.myauth = SPPP_AUTHPROTO_NONE;
318		}
319		else if ((cep->ppp_send_auth == AUTH_CHAP
320			  || cep->ppp_send_auth == AUTH_PAP)
321			 && cep->ppp_send_name != NULL
322			 && cep->ppp_send_password != NULL)
323		{
324			spcfg.myauth = cep->ppp_send_auth == AUTH_PAP ? SPPP_AUTHPROTO_PAP : SPPP_AUTHPROTO_CHAP;
325			spcfg.myname = cep->ppp_send_name;
326			spcfg.myname_length = strlen(cep->ppp_send_name)+1;
327			spcfg.mysecret = cep->ppp_send_password;
328			spcfg.mysecret_length = strlen(cep->ppp_send_password)+1;
329
330			if (cep->ppp_auth_flags & AUTH_REQUIRED)
331				spcfg.hisauthflags &= ~SPPP_AUTHFLAG_NOCALLOUT;
332			else
333				spcfg.hisauthflags |= SPPP_AUTHFLAG_NOCALLOUT;
334
335			if (cep->ppp_auth_flags & AUTH_RECHALLENGE)
336				spcfg.hisauthflags &= ~SPPP_AUTHFLAG_NORECHALLENGE;
337			else
338				spcfg.hisauthflags |= SPPP_AUTHFLAG_NORECHALLENGE;
339		}
340	}
341
342	if (ioctl(s, SPPPSETAUTHCFG, &spcfg) == -1) {
343		logit(LL_ERR, "ERROR setting new PPP authentication parameters for %s at line %d!", spcfg.ifname, lineno);
344		config_error_flag++;
345	}
346	close(s);
347}
348
349/*---------------------------------------------------------------------------*
350 *	extract values from config and fill table
351 *---------------------------------------------------------------------------*/
352void
353cfg_setval(int keyword)
354{
355	int i;
356
357	switch (keyword)
358	{
359	case ACCTALL:
360		acct_all = yylval.booln;
361		DBGL(DL_RCCF, (logit(LL_DBG, "system: acctall = %d", yylval.booln)));
362		break;
363
364	case ACCTFILE:
365		strlcpy(acctfile, yylval.str, sizeof(acctfile));
366		DBGL(DL_RCCF, (logit(LL_DBG, "system: acctfile = %s", yylval.str)));
367		break;
368
369	case ALERT:
370		if (yylval.num < MINALERT)
371		{
372			yylval.num = MINALERT;
373			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: alert < %d, min = %d", current_cfe->name, MINALERT, yylval.num)));
374		}
375		else if (yylval.num > MAXALERT)
376		{
377			yylval.num = MAXALERT;
378			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: alert > %d, min = %d", current_cfe->name, MAXALERT, yylval.num)));
379		}
380
381		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: alert = %d", current_cfe->name, yylval.num)));
382		current_cfe->alert = yylval.num;
383		break;
384
385	case ALIASING:
386		DBGL(DL_RCCF, (logit(LL_DBG, "system: aliasing = %d", yylval.booln)));
387		aliasing = yylval.booln;
388		break;
389
390	case ALIASFNAME:
391		strlcpy(aliasfile, yylval.str, sizeof(aliasfile));
392		DBGL(DL_RCCF, (logit(LL_DBG, "system: aliasfile = %s", yylval.str)));
393		break;
394
395	case ANSWERPROG:
396		if ((current_cfe->answerprog = strdup(yylval.str)) == NULL)
397		{
398			logit(LL_ERR, "entry %s: answerstring, malloc failed!", current_cfe->name);
399			do_exit(1);
400		}
401		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: answerprog = %s", current_cfe->name, yylval.str)));
402		break;
403
404	case B1PROTOCOL:
405		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: b1protocol = %s", current_cfe->name, yylval.str)));
406		if (!(strcmp(yylval.str, "raw")))
407			current_cfe->b1protocol = BPROT_NONE;
408		else if (!(strcmp(yylval.str, "hdlc")))
409			current_cfe->b1protocol = BPROT_RHDLC;
410		else
411		{
412			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"b1protocol\" at line %d!", lineno);
413			config_error_flag++;
414		}
415		break;
416
417	case BEEPCONNECT:
418		do_bell = yylval.booln;
419		DBGL(DL_RCCF, (logit(LL_DBG, "system: beepconnect = %d", yylval.booln)));
420		break;
421
422	case BUDGETCALLBACKPERIOD:
423		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-callbackperiod = %d", current_cfe->name, yylval.num)));
424		current_cfe->budget_callbackperiod = yylval.num;
425		break;
426
427	case BUDGETCALLBACKNCALLS:
428		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-callbackncalls = %d", current_cfe->name, yylval.num)));
429		current_cfe->budget_callbackncalls = yylval.num;
430		break;
431
432	case BUDGETCALLOUTPERIOD:
433		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-calloutperiod = %d", current_cfe->name, yylval.num)));
434		current_cfe->budget_calloutperiod = yylval.num;
435		break;
436
437	case BUDGETCALLOUTNCALLS:
438		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-calloutncalls = %d", current_cfe->name, yylval.num)));
439		current_cfe->budget_calloutncalls = yylval.num;
440		break;
441
442	case AUTOUPDOWN:
443		current_cfe->autoupdown = yylval.booln;
444		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: autoupdown = %d", current_cfe->name, yylval.booln)));
445		break;
446
447	case BUDGETCALLBACKSFILEROTATE:
448		current_cfe->budget_callbacksfile_rotate = yylval.booln;
449		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-callbacksfile-rotate = %d", current_cfe->name, yylval.booln)));
450		break;
451
452	case BUDGETCALLBACKSFILE:
453		{
454			FILE *fp;
455			int s, l;
456			int n;
457			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-callbacksfile = %s", yylval.str)));
458			fp = fopen(yylval.str, "r");
459			if (fp != NULL)
460			{
461				if ((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
462				{
463					DBGL(DL_RCCF, (logit(LL_DBG, "entry %d: initializing budget-callbacksfile %s", current_cfe->name, yylval.str)));
464					fclose(fp);
465					fp = fopen(yylval.str, "w");
466					if (fp != NULL) {
467						fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
468						fclose(fp);
469					}
470				} else
471					fclose(fp);
472			}
473			else
474			{
475				DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: creating budget-callbacksfile %s", current_cfe->name, yylval.str)));
476				fp = fopen(yylval.str, "w");
477				if (fp != NULL)
478					fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
479				fclose(fp);
480			}
481
482			fp = fopen(yylval.str, "r");
483			if (fp != NULL)
484			{
485				if ((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
486				{
487					if ((current_cfe->budget_callbacks_file = strdup(yylval.str)) == NULL)
488					{
489						logit(LL_ERR, "entry %s: budget-callbacksfile, malloc failed!", current_cfe->name);
490						do_exit(1);
491					}
492					DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: using callbacksfile %s", current_cfe->name, yylval.str)));
493				}
494				fclose(fp);
495			}
496		}
497		break;
498
499	case BUDGETCALLOUTSFILEROTATE:
500		current_cfe->budget_calloutsfile_rotate = yylval.booln;
501		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-calloutsfile-rotate = %d", current_cfe->name, yylval.booln)));
502		break;
503
504	case BUDGETCALLOUTSFILE:
505		{
506			FILE *fp;
507			int s, l;
508			int n;
509			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: budget-calloutsfile = %s", current_cfe->name, yylval.str)));
510			fp = fopen(yylval.str, "r");
511			if (fp != NULL)
512			{
513				if ((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
514				{
515					DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: initializing budget-calloutsfile %s", current_cfe->name, yylval.str)));
516					fclose(fp);
517					fp = fopen(yylval.str, "w");
518					if (fp != NULL)
519						fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
520					fclose(fp);
521				}
522			}
523			else
524			{
525				DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: creating budget-calloutsfile %s", current_cfe->name, yylval.str)));
526				fp = fopen(yylval.str, "w");
527				if (fp != NULL)
528					fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
529				fclose(fp);
530			}
531
532			fp = fopen(yylval.str, "r");
533			if (fp != NULL)
534			{
535				if ((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
536				{
537					if ((current_cfe->budget_callouts_file = strdup(yylval.str)) == NULL)
538					{
539						logit(LL_ERR, "entry %s: budget-calloutsfile, malloc failed!", current_cfe->name);
540						do_exit(1);
541					}
542					DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: using calloutsfile %s", current_cfe->name, yylval.str)));
543				}
544				fclose(fp);
545			}
546		}
547		break;
548
549	case CALLBACKWAIT:
550		if (yylval.num < CALLBACKWAIT_MIN)
551		{
552			yylval.num = CALLBACKWAIT_MIN;
553			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: callbackwait < %d, min = %d", current_cfe->name, CALLBACKWAIT_MIN, yylval.num)));
554		}
555
556		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: callbackwait = %d", current_cfe->name, yylval.num)));
557		current_cfe->callbackwait = yylval.num;
558		break;
559
560	case CALLEDBACKWAIT:
561		if (yylval.num < CALLEDBACKWAIT_MIN)
562		{
563			yylval.num = CALLEDBACKWAIT_MIN;
564			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: calledbackwait < %d, min = %d", current_cfe->name, CALLEDBACKWAIT_MIN, yylval.num)));
565		}
566
567		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: calledbackwait = %d", current_cfe->name, yylval.num)));
568		current_cfe->calledbackwait = yylval.num;
569		break;
570
571	case CONNECTPROG:
572		if ((current_cfe->connectprog = strdup(yylval.str)) == NULL)
573		{
574			logit(LL_ERR, "entry %s: connectprog, malloc failed!", current_cfe->name);
575			do_exit(1);
576		}
577		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: connectprog = %s", current_cfe->name, yylval.str)));
578		break;
579
580	case DIALOUTTYPE:
581		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: dialouttype = %s", current_cfe->name, yylval.str)));
582		if (!(strcmp(yylval.str, "normal")))
583			current_cfe->dialouttype = DIALOUT_NORMAL;
584		else if (!(strcmp(yylval.str, "calledback")))
585			current_cfe->dialouttype = DIALOUT_CALLEDBACK;
586		else
587		{
588			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialout-type\" at line %d!", lineno);
589			config_error_flag++;
590		}
591		break;
592
593	case DIALRETRIES:
594		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: dialretries = %d", current_cfe->name, yylval.num)));
595		current_cfe->dialretries = yylval.num;
596		break;
597
598	case DIALRANDINCR:
599		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: dialrandincr = %d", current_cfe->name, yylval.booln)));
600		current_cfe->dialrandincr = yylval.booln;
601		break;
602
603	case DIRECTION:
604		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: direction = %s", current_cfe->name, yylval.str)));
605
606		if (!(strcmp(yylval.str, "inout")))
607			current_cfe->inout = DIR_INOUT;
608		else if (!(strcmp(yylval.str, "in")))
609			current_cfe->inout = DIR_INONLY;
610		else if (!(strcmp(yylval.str, "out")))
611			current_cfe->inout = DIR_OUTONLY;
612		else
613		{
614			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"direction\" at line %d!", lineno);
615			config_error_flag++;
616		}
617		break;
618
619	case DISCONNECTPROG:
620		if ((current_cfe->disconnectprog = strdup(yylval.str)) == NULL)
621		{
622			logit(LL_ERR, "entry %s: disconnectprog, malloc failed!", current_cfe->name);
623			do_exit(1);
624		}
625		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: disconnectprog = %s", current_cfe->name, yylval.str)));
626		break;
627
628	case DOWNTRIES:
629		if (yylval.num > DOWN_TRIES_MAX)
630			yylval.num = DOWN_TRIES_MAX;
631		else if (yylval.num < DOWN_TRIES_MIN)
632			yylval.num = DOWN_TRIES_MIN;
633
634		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: downtries = %d", current_cfe->name, yylval.num)));
635		current_cfe->downtries = yylval.num;
636		break;
637
638	case DOWNTIME:
639		if (yylval.num > DOWN_TIME_MAX)
640			yylval.num = DOWN_TIME_MAX;
641		else if (yylval.num < DOWN_TIME_MIN)
642			yylval.num = DOWN_TIME_MIN;
643
644		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: downtime = %d", current_cfe->name, yylval.num)));
645		current_cfe->downtime = yylval.num;
646		break;
647
648	case EARLYHANGUP:
649		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: earlyhangup = %d", current_cfe->name, yylval.num)));
650		current_cfe->earlyhangup = yylval.num;
651		break;
652
653	case EXTCALLATTR:
654		DBGL(DL_RCCF, (logit(LL_DBG, "system: extcallattr = %d", yylval.booln)));
655		extcallattr = yylval.booln;
656		break;
657
658	case FIRMWARE:
659		DBGL(DL_RCCF, (logit(LL_DBG, "controller %d: firmware = %s", cur_ctrl->isdnif, yylval.str)));
660		cur_ctrl->firmware = strdup(yylval.str);
661		break;
662
663	case HOLIDAYFILE:
664		strlcpy(holidayfile, yylval.str, sizeof(holidayfile));
665		DBGL(DL_RCCF, (logit(LL_DBG, "system: holidayfile = %s", yylval.str)));
666		break;
667
668	case IDLE_ALG_OUT:
669		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: idle-algorithm-outgoing = %s", current_cfe->name, yylval.str)));
670
671		if (!(strcmp(yylval.str, "fix-unit-size")))
672		{
673			current_cfe->shorthold_algorithm = SHA_FIXU;
674		}
675		else if (!(strcmp(yylval.str, "var-unit-size")))
676		{
677			current_cfe->shorthold_algorithm = SHA_VARU;
678		}
679		else
680		{
681			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"idle-algorithm-outgoing\" at line %d!", lineno);
682			config_error_flag++;
683		}
684		break;
685
686	case IDLETIME_IN:
687		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: idle_time_in = %d", current_cfe->name, yylval.num)));
688		current_cfe->idle_time_in = yylval.num;
689		break;
690
691	case IDLETIME_OUT:
692		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: idle_time_out = %d", current_cfe->name, yylval.num)));
693		current_cfe->idle_time_out = yylval.num;
694		break;
695
696	case ISDNCONTROLLER:
697		current_cfe->isdncontroller = yylval.num;
698		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: isdncontroller = %d", current_cfe->name, yylval.num)));
699		break;
700
701	case ISDNCHANNEL:
702		if (yylval.num == 0 || yylval.num == -1) {
703			current_cfe->isdnchannel = CHAN_ANY;
704			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: isdnchannel = any", current_cfe->name)));
705		} else if (yylval.num > MAX_BCHAN) {
706			logit(LL_DBG, "entry %s: isdnchannel value out of range", current_cfe->name);
707			config_error_flag++;
708		} else {
709			current_cfe->isdnchannel = yylval.num - 1;
710			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: isdnchannel = %d", current_cfe->name, yylval.num)));
711		}
712		break;
713
714	case ISDNTIME:
715		DBGL(DL_RCCF, (logit(LL_DBG, "system: isdntime = %d", yylval.booln)));
716		isdntime = yylval.booln;
717		break;
718
719	case ISDNTXDELIN:
720		current_cfe->isdntxdelin = yylval.num;
721		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: isdntxdel-incoming = %d", current_cfe->name, yylval.num)));
722		break;
723
724	case ISDNTXDELOUT:
725		current_cfe->isdntxdelout = yylval.num;
726		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: isdntxdel-outgoing = %d", current_cfe->name, yylval.num)));
727		break;
728
729	case LOCAL_PHONE_DIALOUT:
730		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: local_phone_dialout = %s", current_cfe->name, yylval.str)));
731		strlcpy(current_cfe->local_phone_dialout, yylval.str,
732		    sizeof(current_cfe->local_phone_dialout));
733		break;
734
735	case LOCAL_PHONE_INCOMING:
736		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: local_phone_incoming = %s", current_cfe->name, yylval.str)));
737		strlcpy(current_cfe->local_phone_incoming, yylval.str,
738		    sizeof(current_cfe->local_phone_incoming));
739		break;
740
741	case MAILER:
742		strlcpy(mailer, yylval.str, sizeof(mailer));
743		DBGL(DL_RCCF, (logit(LL_DBG, "system: mailer = %s", yylval.str)));
744		break;
745
746	case MAILTO:
747		strlcpy(mailto, yylval.str, sizeof(mailto));
748		DBGL(DL_RCCF, (logit(LL_DBG, "system: mailto = %s", yylval.str)));
749		break;
750
751	case MONITORPORT:
752		monitorport = yylval.num;
753		DBGL(DL_RCCF, (logit(LL_DBG, "system: monitorport = %d", yylval.num)));
754		break;
755
756	case MONITORSW:
757		if (yylval.booln && inhibit_monitor)
758		{
759			do_monitor = 0;
760			DBGL(DL_RCCF, (logit(LL_DBG, "system: monitor-enable overriden by command line flag")));
761		}
762		else
763		{
764			do_monitor = yylval.booln;
765			DBGL(DL_RCCF, (logit(LL_DBG, "system: monitor-enable = %d", yylval.booln)));
766		}
767		break;
768
769	case NAME:
770		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: name = %s", current_cfe->name, yylval.str)));
771		strlcpy(current_cfe->name, yylval.str,
772		    sizeof(current_cfe->name));
773		break;
774
775	case PPP_AUTH_RECHALLENGE:
776		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-auth-rechallenge = %d", current_cfe->name, yylval.booln)));
777		if (yylval.booln)
778			current_cfe->ppp_auth_flags |= AUTH_RECHALLENGE;
779		else
780			current_cfe->ppp_auth_flags &= ~AUTH_RECHALLENGE;
781		break;
782
783	case PPP_AUTH_PARANOID:
784		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-auth-paranoid = %d", current_cfe->name, yylval.booln)));
785		if (yylval.booln)
786			current_cfe->ppp_auth_flags |= AUTH_REQUIRED;
787		else
788			current_cfe->ppp_auth_flags &= ~AUTH_REQUIRED;
789		break;
790
791	case PPP_EXPECT_AUTH:
792		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-expect-auth = %s", current_cfe->name, yylval.str)));
793		if (!(strcmp(yylval.str, "none")))
794			current_cfe->ppp_expect_auth = AUTH_NONE;
795		else if (!(strcmp(yylval.str, "pap")))
796			current_cfe->ppp_expect_auth = AUTH_PAP;
797		else if (!(strcmp(yylval.str, "chap")))
798			current_cfe->ppp_expect_auth = AUTH_CHAP;
799		else
800		{
801			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-expect-auth\" at line %d!", lineno);
802			config_error_flag++;
803			break;
804		}
805		break;
806
807	case PPP_EXPECT_NAME:
808		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-expect-name = %s", current_cfe->name, yylval.str)));
809		if (current_cfe->ppp_expect_name)
810		    free(current_cfe->ppp_expect_name);
811		current_cfe->ppp_expect_name = strdup(yylval.str);
812		break;
813
814	case PPP_EXPECT_PASSWORD:
815		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-expect-password = %s", current_cfe->name, yylval.str)));
816		if (current_cfe->ppp_expect_password)
817		    free(current_cfe->ppp_expect_password);
818		current_cfe->ppp_expect_password = strdup(yylval.str);
819		break;
820
821	case PPP_SEND_AUTH:
822		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-send-auth = %s", current_cfe->name, yylval.str)));
823		if (!(strcmp(yylval.str, "none")))
824			current_cfe->ppp_send_auth = AUTH_NONE;
825		else if (!(strcmp(yylval.str, "pap")))
826			current_cfe->ppp_send_auth = AUTH_PAP;
827		else if (!(strcmp(yylval.str, "chap")))
828			current_cfe->ppp_send_auth = AUTH_CHAP;
829		else
830		{
831			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-send-auth\" at line %d!", lineno);
832			config_error_flag++;
833			break;
834		}
835		break;
836
837	case PPP_SEND_NAME:
838		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-send-name = %s", current_cfe->name, yylval.str)));
839		if (current_cfe->ppp_send_name)
840		    free(current_cfe->ppp_send_name);
841		current_cfe->ppp_send_name = strdup(yylval.str);
842		break;
843
844	case PPP_SEND_PASSWORD:
845		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ppp-send-password = %s", current_cfe->name, yylval.str)));
846		if (current_cfe->ppp_send_password)
847		    free(current_cfe->ppp_send_password);
848		current_cfe->ppp_send_password = strdup(yylval.str);
849		break;
850
851	case PROTOCOL:
852		DBGL(DL_RCCF, (logit(LL_DBG, "controller %d: protocol = %s", cur_ctrl->isdnif, yylval.str)));
853		if (!(strcmp(yylval.str, "dss1")))
854			cur_ctrl->protocol = PROTOCOL_DSS1;
855		else if (!(strcmp(yylval.str, "d64s")))
856			cur_ctrl->protocol = PROTOCOL_D64S;
857		else
858		{
859			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"protocol\" at line %d!", lineno);
860			config_error_flag++;
861		}
862		break;
863
864	case REACTION:
865		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: dialin_reaction = %s", current_cfe->name, yylval.str)));
866		if (!(strcmp(yylval.str, "accept")))
867			current_cfe->dialin_reaction = REACT_ACCEPT;
868		else if (!(strcmp(yylval.str, "reject")))
869			current_cfe->dialin_reaction = REACT_REJECT;
870		else if (!(strcmp(yylval.str, "ignore")))
871			current_cfe->dialin_reaction = REACT_IGNORE;
872		else if (!(strcmp(yylval.str, "answer")))
873			current_cfe->dialin_reaction = REACT_ANSWER;
874		else if (!(strcmp(yylval.str, "callback")))
875			current_cfe->dialin_reaction = REACT_CALLBACK;
876		else
877		{
878			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialin_reaction\" at line %d!", lineno);
879			config_error_flag++;
880		}
881		break;
882
883	case REMOTE_PHONE_DIALOUT:
884		if (current_cfe->remote_numbers_count >= MAXRNUMBERS)
885		{
886			logit(LL_ERR, "ERROR parsing config file: too many remote numbers at line %d!", lineno);
887			config_error_flag++;
888			break;
889		}
890
891		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: remote_phone_dialout #%d = %s",
892			current_cfe->name, current_cfe->remote_numbers_count, yylval.str)));
893
894		strlcpy(current_cfe->remote_numbers[current_cfe->remote_numbers_count].number,
895		    yylval.str,
896		    sizeof(current_cfe->remote_numbers[current_cfe->remote_numbers_count].number));
897		current_cfe->remote_numbers[current_cfe->remote_numbers_count].flag = 0;
898
899		current_cfe->remote_numbers_count++;
900
901		break;
902
903	case REMOTE_NUMBERS_HANDLING:
904		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: remdial_handling = %s", current_cfe->name, yylval.str)));
905		if (!(strcmp(yylval.str, "next")))
906			current_cfe->remote_numbers_handling = RNH_NEXT;
907		else if (!(strcmp(yylval.str, "last")))
908			current_cfe->remote_numbers_handling = RNH_LAST;
909		else if (!(strcmp(yylval.str, "first")))
910			current_cfe->remote_numbers_handling = RNH_FIRST;
911		else
912		{
913			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"remdial_handling\" at line %d!", lineno);
914			config_error_flag++;
915		}
916		break;
917
918	case REMOTE_PHONE_INCOMING:
919		{
920			int n;
921			n = current_cfe->incoming_numbers_count;
922			if (n >= MAX_INCOMING)
923			{
924				logit(LL_ERR, "ERROR parsing config file: too many \"remote_phone_incoming\" entries at line %d!", lineno);
925				config_error_flag++;
926				break;
927			}
928			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: remote_phone_incoming #%d = %s", current_cfe->name, n, yylval.str)));
929			strlcpy(current_cfe->remote_phone_incoming[n].number,
930			    yylval.str,
931			    sizeof(current_cfe->remote_phone_incoming[n].number));
932			current_cfe->incoming_numbers_count++;
933		}
934		break;
935
936	case RATESFILE:
937		strlcpy(ratesfile, yylval.str, sizeof(ratesfile));
938		DBGL(DL_RCCF, (logit(LL_DBG, "system: ratesfile = %s", yylval.str)));
939		break;
940
941	case RATETYPE:
942		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: ratetype = %d", current_cfe->name, yylval.num)));
943		current_cfe->ratetype = yylval.num;
944		break;
945
946	case RECOVERYTIME:
947		if (yylval.num < RECOVERYTIME_MIN)
948		{
949			yylval.num = RECOVERYTIME_MIN;
950			DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: recoverytime < %d, min = %d", current_cfe->name, RECOVERYTIME_MIN, yylval.num)));
951		}
952
953		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: recoverytime = %d", current_cfe->name, yylval.num)));
954		current_cfe->recoverytime = yylval.num;
955		break;
956
957	case REGEXPR:
958		if (nregexpr >= MAX_RE)
959		{
960			logit(LL_ERR, "system: regexpr #%d >= MAX_RE", nregexpr);
961			config_error_flag++;
962			break;
963		}
964
965		if ((i = regcomp(&(rarr[nregexpr].re), yylval.str, REG_EXTENDED|REG_NOSUB)) != 0)
966		{
967			char buf[256];
968			regerror(i, &(rarr[nregexpr].re), buf, sizeof(buf));
969			logit(LL_ERR, "system: regcomp error for %s: [%s]", yylval.str, buf);
970			config_error_flag++;
971			break;
972		}
973		else
974		{
975			if ((rarr[nregexpr].re_expr = strdup(yylval.str)) == NULL)
976			{
977				logit(LL_ERR, "system: regexpr malloc error error for %s", yylval.str);
978				config_error_flag++;
979				break;
980			}
981
982			DBGL(DL_RCCF, (logit(LL_DBG, "system: regexpr %s stored into slot %d", yylval.str, nregexpr)));
983
984			if (rarr[nregexpr].re_prog != NULL)
985				rarr[nregexpr].re_flg = 1;
986
987			nregexpr++;
988
989		}
990		break;
991
992	case REGPROG:
993		if (nregprog >= MAX_RE)
994		{
995			logit(LL_ERR, "system: regprog #%d >= MAX_RE", nregprog);
996			config_error_flag++;
997			break;
998		}
999		if ((rarr[nregprog].re_prog = strdup(yylval.str)) == NULL)
1000		{
1001			logit(LL_ERR, "system: regprog malloc error error for %s", yylval.str);
1002			config_error_flag++;
1003			break;
1004		}
1005
1006		DBGL(DL_RCCF, (logit(LL_DBG, "system: regprog %s stored into slot %d", yylval.str, nregprog)));
1007
1008		if (rarr[nregprog].re_expr != NULL)
1009			rarr[nregprog].re_flg = 1;
1010
1011		nregprog++;
1012		break;
1013
1014	case ROTATESUFFIX:
1015		strlcpy(rotatesuffix, yylval.str, sizeof(rotatesuffix));
1016		DBGL(DL_RCCF, (logit(LL_DBG, "system: rotatesuffix = %s", yylval.str)));
1017		break;
1018
1019	case RTPRIO:
1020#ifdef USE_RTPRIO
1021		rt_prio = yylval.num;
1022		if (rt_prio < RTP_PRIO_MIN || rt_prio > RTP_PRIO_MAX)
1023		{
1024			config_error_flag++;
1025			logit(LL_ERR, "system: error, rtprio (%d) out of range!", yylval.num);
1026		}
1027		else
1028		{
1029			DBGL(DL_RCCF, (logit(LL_DBG, "system: rtprio = %d", yylval.num)));
1030		}
1031#else
1032		rt_prio = RTPRIO_NOTUSED;
1033#endif
1034		break;
1035
1036	case TINAINITPROG:
1037		strlcpy(tinainitprog, yylval.str, sizeof(tinainitprog));
1038		DBGL(DL_RCCF, (logit(LL_DBG, "system: tinainitprog = %s", yylval.str)));
1039		break;
1040
1041	case UNITLENGTH:
1042		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: unitlength = %d", current_cfe->name, yylval.num)));
1043		current_cfe->unitlength = yylval.num;
1044		break;
1045
1046	case UNITLENGTHSRC:
1047		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: unitlengthsrc = %s", current_cfe->name, yylval.str)));
1048		if (!(strcmp(yylval.str, "none")))
1049			current_cfe->unitlengthsrc = ULSRC_NONE;
1050		else if (!(strcmp(yylval.str, "cmdl")))
1051			current_cfe->unitlengthsrc = ULSRC_CMDL;
1052		else if (!(strcmp(yylval.str, "conf")))
1053			current_cfe->unitlengthsrc = ULSRC_CONF;
1054		else if (!(strcmp(yylval.str, "rate")))
1055			current_cfe->unitlengthsrc = ULSRC_RATE;
1056		else if (!(strcmp(yylval.str, "aocd")))
1057			current_cfe->unitlengthsrc = ULSRC_DYN;
1058		else
1059		{
1060			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"unitlengthsrc\" at line %d!", lineno);
1061			config_error_flag++;
1062		}
1063		break;
1064
1065	case USRDEVICENAME:
1066		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: usrdevicename = %s", current_cfe->name, yylval.str)));
1067		strncpy(current_cfe->usrdevicename, yylval.str, sizeof(current_cfe->usrdevicename));
1068		current_cfe->usrdevice = lookup_l4_driver(yylval.str);
1069		if (current_cfe->usrdevice < 0)
1070		{
1071			logit(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"usrdevicename\" at line %d!", lineno);
1072			config_error_flag++;
1073		}
1074		break;
1075
1076	case USRDEVICEUNIT:
1077		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: usrdeviceunit = %d", current_cfe->name, yylval.num)));
1078		current_cfe->usrdeviceunit = yylval.num;
1079		break;
1080
1081	case USEACCTFILE:
1082		useacctfile = yylval.booln;
1083		DBGL(DL_RCCF, (logit(LL_DBG, "system: useacctfile = %d", yylval.booln)));
1084		break;
1085
1086	case USEDOWN:
1087		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: usedown = %d", current_cfe->name, yylval.booln)));
1088		current_cfe->usedown = yylval.booln;
1089		break;
1090
1091	case VALID:
1092		DBGL(DL_RCCF, (logit(LL_DBG, "entry %s: valid = %s", current_cfe->name, yylval.str)));
1093		parse_valid(yylval.str);
1094		break;
1095
1096	default:
1097		logit(LL_ERR, "ERROR parsing config file: unknown keyword at line %d!", lineno);
1098		config_error_flag++;
1099		break;
1100	}
1101}
1102
1103/*---------------------------------------------------------------------------*
1104 *	parse a date/time range
1105 *---------------------------------------------------------------------------*/
1106static void
1107parse_valid(char *dt)
1108{
1109	/* a valid string consists of some days of week separated by
1110	 * commas, where 0=sunday, 1=monday .. 6=saturday and a special
1111	 * value of 7 which is a holiday from the holiday file.
1112	 * after the days comes an optional (!) time range in the form
1113	 * aa:bb-cc:dd, this format is fixed to be parsable by sscanf.
1114	 * Valid specifications looks like this:
1115	 * 1,2,3,4,5,09:00-18:00	Monday-Friday 9-18h
1116	 * 1,2,3,4,5,18:00-09:00	Monday-Friday 18-9h
1117	 * 6				Saturday (whole day)
1118	 * 0,7				Sunday and Holidays
1119	 */
1120
1121	int day = 0;
1122	int fromhr = 0;
1123	int frommin = 0;
1124	int tohr = 0;
1125	int tomin = 0;
1126	int ret;
1127
1128	for (;;)
1129	{
1130		if ( ( ((*dt >= '0') && (*dt <= '9')) && (*(dt+1) == ':') ) ||
1131		    ( ((*dt >= '0') && (*dt <= '2')) && ((*(dt+1) >= '0') && (*(dt+1) <= '9')) && (*(dt+2) == ':') ) )
1132		{
1133			/* dt points to time spec */
1134			ret = sscanf(dt, "%d:%d-%d:%d", &fromhr, &frommin, &tohr, &tomin);
1135			if (ret !=4)
1136			{
1137				logit(LL_ERR, "ERROR parsing config file: timespec [%s] error at line %d!", *dt, lineno);
1138				config_error_flag++;
1139				return;
1140			}
1141
1142			if (fromhr < 0 || fromhr > 24 || tohr < 0 || tohr > 24 ||
1143			   frommin < 0 || frommin > 59 || tomin < 0 || tomin > 59)
1144			{
1145				logit(LL_ERR, "ERROR parsing config file: invalid time [%s] at line %d!", *dt, lineno);
1146				config_error_flag++;
1147				return;
1148			}
1149			break;
1150		}
1151		else if ((*dt >= '0') && (*dt <= '7'))
1152		{
1153			/* dt points to day spec */
1154			day |= 1 << (*dt - '0');
1155			dt++;
1156			continue;
1157		}
1158		else if (*dt == ',')
1159		{
1160			/* dt points to delimiter */
1161			dt++;
1162			continue;
1163		}
1164		else if (*dt == '\0')
1165		{
1166			/* dt points to end of string */
1167			break;
1168		}
1169		else
1170		{
1171			/* dt points to illegal character */
1172			logit(LL_ERR, "ERROR parsing config file: illegal character [%c=0x%x] in date/time spec at line %d!", *dt, *dt, lineno);
1173			config_error_flag++;
1174			return;
1175		}
1176	}
1177	current_cfe->day = day;
1178	current_cfe->fromhr = fromhr;
1179	current_cfe->frommin = frommin;
1180	current_cfe->tohr = tohr;
1181	current_cfe->tomin = tomin;
1182}
1183
1184void
1185flush_config()
1186{
1187	if (current_cfe != NULL) {
1188		add_cfg_entry(current_cfe);
1189	}
1190}
1191
1192/*---------------------------------------------------------------------------*
1193 *	configuration validation and consistency check
1194 *---------------------------------------------------------------------------*/
1195static void
1196check_config(void)
1197{
1198	struct cfg_entry *cep = NULL;
1199	int i;
1200	int error = 0;
1201
1202	/* regular expression table */
1203
1204	for (i=0; i < MAX_RE; i++)
1205	{
1206		if ((rarr[i].re_expr != NULL) && (rarr[i].re_prog == NULL))
1207		{
1208			logit(LL_ERR, "check_config: regular expression %d without program!", i);
1209			error++;
1210		}
1211		if ((rarr[i].re_prog != NULL) && (rarr[i].re_expr == NULL))
1212		{
1213			logit(LL_ERR, "check_config: regular expression program %d without expression!", i);
1214			error++;
1215		}
1216	}
1217
1218	/* entry sections */
1219
1220	for (cep = get_first_cfg_entry(); cep; cep = NEXT_CFE(cep)) {
1221
1222		/* does this entry have a bchannel driver configured? */
1223		if (cep->usrdevice < 0) {
1224			logit(LL_ERR, "check_config: usrdevicename not set in entry \"%s\"!",
1225			    cep->name);
1226			error++;
1227		}
1228		if (cep->usrdeviceunit < 0) {
1229			logit(LL_ERR, "check_config: usrdeviceunit not set in entry \"%s\"!",
1230			    cep->name);
1231			error++;
1232		}
1233
1234		/* numbers used for dialout */
1235
1236		if ((cep->inout != DIR_INONLY) && (cep->dialin_reaction != REACT_ANSWER))
1237		{
1238			if (cep->remote_numbers_count == 0)
1239			{
1240				logit(LL_ERR, "check_config: remote-phone-dialout not set in entry \"%s\"!",
1241				    cep->name);
1242				error++;
1243			}
1244		}
1245
1246		/* numbers used for incoming calls */
1247
1248		if (cep->inout != DIR_OUTONLY)
1249		{
1250			if (strlen(cep->local_phone_incoming) == 0)
1251			{
1252				logit(LL_ERR, "check_config: local-phone-incoming not set in entry \"%s\"!",
1253				    cep->name);
1254				error++;
1255			}
1256			if (cep->incoming_numbers_count == 0)
1257			{
1258				logit(LL_ERR, "check_config: remote-phone-incoming not set in entry \"%s\"!",
1259				    cep->name);
1260				error++;
1261			}
1262		}
1263
1264		if ((cep->dialin_reaction == REACT_ANSWER) && (cep->b1protocol != BPROT_NONE))
1265		{
1266			logit(LL_ERR, "check_config: b1protocol not raw for telephony in entry \"%s\"!",
1267			    cep->name);
1268			error++;
1269		}
1270
1271		if ((cep->ppp_send_auth == AUTH_PAP) || (cep->ppp_send_auth == AUTH_CHAP))
1272		{
1273			if (cep->ppp_send_name == NULL)
1274			{
1275				logit(LL_ERR, "check_config: no remote authentification name in entry \"%s\"!",
1276				    cep->name);
1277				error++;
1278			}
1279			if (cep->ppp_send_password == NULL)
1280			{
1281				logit(LL_ERR, "check_config: no remote authentification password in entry \"%s\"!",
1282				    cep->name);
1283				error++;
1284			}
1285		}
1286		if ((cep->ppp_expect_auth == AUTH_PAP) || (cep->ppp_expect_auth == AUTH_CHAP))
1287		{
1288			if (cep->ppp_expect_name == NULL)
1289			{
1290				logit(LL_ERR, "check_config: no local authentification name in entry \"%s\"!",
1291				    cep->name);
1292				error++;
1293			}
1294			if (cep->ppp_expect_password == NULL)
1295			{
1296				logit(LL_ERR, "check_config: no local authentification secret in entry \"%s\"!",
1297				    cep->name);
1298				error++;
1299			}
1300		}
1301
1302		if (cep->ppp_expect_auth != AUTH_UNDEF
1303		   || cep->ppp_send_auth != AUTH_UNDEF)
1304			set_isppp_auth(cep);
1305
1306		/*
1307		 * Only if AUTOUPDOWN_YES is the only bit set, otherwise
1308		 * we already have handled this interface.
1309		 */
1310		if (cep->autoupdown == AUTOUPDOWN_YES)
1311			set_autoupdown(cep);
1312	}
1313	if (error) {
1314		logit(LL_ERR, "check_config: %d error(s) in configuration file, exiting!",
1315		    error);
1316		do_exit(1);
1317	}
1318}
1319
1320/*---------------------------------------------------------------------------*
1321 *	print the configuration
1322 *---------------------------------------------------------------------------*/
1323static void
1324print_config(void)
1325{
1326#define PFILE stdout
1327
1328#ifdef I4B_EXTERNAL_MONITOR
1329	extern struct monitor_rights * monitor_next_rights(const struct monitor_rights *r);
1330	struct monitor_rights *m_rights;
1331#endif
1332	struct cfg_entry *cep = NULL;
1333	int i, j;
1334	time_t now;
1335	char mytime[64];
1336
1337	time(&now);
1338	strlcpy(mytime, ctime(&now), sizeof(mytime));
1339	mytime[strlen(mytime)-1] = '\0';
1340
1341	fprintf(PFILE, "#---------------------------------------------------------------------------\n");
1342	fprintf(PFILE, "# system section (generated %s)\n", mytime);
1343	fprintf(PFILE, "#---------------------------------------------------------------------------\n");
1344	fprintf(PFILE, "system\n");
1345	fprintf(PFILE, "useacctfile     = %s\n", useacctfile ? "on\t\t\t\t# update accounting information file" : "off\t\t\t\t# don't update accounting information file");
1346	fprintf(PFILE, "acctall         = %s\n", acct_all ? "on\t\t\t\t# put all events into accounting file" : "off\t\t\t\t# put only charged events into accounting file");
1347	fprintf(PFILE, "acctfile        = %s\t\t# accounting information file\n", acctfile);
1348	fprintf(PFILE, "ratesfile       = %s\t\t# charging rates database file\n", ratesfile);
1349
1350#ifdef USE_RTPRIO
1351	if (rt_prio == RTPRIO_NOTUSED)
1352		fprintf(PFILE, "# rtprio is unused\n");
1353	else
1354		fprintf(PFILE, "rtprio          = %d\t\t\t\t# isdnd runs at realtime priority\n", rt_prio);
1355#endif
1356
1357	/* regular expression table */
1358
1359	for (i=0; i < MAX_RE; i++)
1360	{
1361		if (rarr[i].re_expr != NULL)
1362		{
1363			fprintf(PFILE, "regexpr         = \"%s\"\t\t# scan logfile for this expression\n", rarr[i].re_expr);
1364		}
1365		if (rarr[i].re_prog != NULL)
1366		{
1367			fprintf(PFILE, "regprog         = %s\t\t# program to run when expression is matched\n", rarr[i].re_prog);
1368		}
1369	}
1370
1371#ifdef I4B_EXTERNAL_MONITOR
1372
1373	fprintf(PFILE, "monitor-allowed = %s\n", do_monitor ? "on\t\t\t\t# remote isdnd monitoring allowed" : "off\t\t\t\t# remote isdnd monitoring disabled");
1374	fprintf(PFILE, "monitor-port    = %d\t\t\t\t# TCP/IP port number used for remote monitoring\n", monitorport);
1375
1376	m_rights = monitor_next_rights(NULL);
1377	if (m_rights != NULL)
1378	{
1379		const char *s = "error\n";
1380		char b[512];
1381
1382		for ( ; m_rights != NULL; m_rights = monitor_next_rights(m_rights))
1383		{
1384			if (m_rights->local)
1385			{
1386				fprintf(PFILE, "monitor         = \"%s\"\t\t# local socket name for monitoring\n", m_rights->name);
1387			}
1388			else
1389			{
1390				struct in_addr ia;
1391				ia.s_addr = ntohl(m_rights->net);
1392
1393				switch (m_rights->mask)
1394				{
1395				case 0xffffffff:
1396					s = "32";
1397					break;
1398				case 0xfffffffe:
1399					s = "31";
1400					break;
1401				case 0xfffffffc:
1402					s = "30";
1403					break;
1404				case 0xfffffff8:
1405					s = "29";
1406					break;
1407				case 0xfffffff0:
1408					s = "28";
1409					break;
1410				case 0xffffffe0:
1411					s = "27";
1412					break;
1413				case 0xffffffc0:
1414					s = "26";
1415					break;
1416				case 0xffffff80:
1417					s = "25";
1418					break;
1419				case 0xffffff00:
1420					s = "24";
1421					break;
1422				case 0xfffffe00:
1423					s = "23";
1424					break;
1425				case 0xfffffc00:
1426					s = "22";
1427					break;
1428				case 0xfffff800:
1429					s = "21";
1430					break;
1431				case 0xfffff000:
1432					s = "20";
1433					break;
1434				case 0xffffe000:
1435					s = "19";
1436					break;
1437				case 0xffffc000:
1438					s = "18";
1439					break;
1440				case 0xffff8000:
1441					s = "17";
1442					break;
1443				case 0xffff0000:
1444					s = "16";
1445					break;
1446				case 0xfffe0000:
1447					s = "15";
1448					break;
1449				case 0xfffc0000:
1450					s = "14";
1451					break;
1452				case 0xfff80000:
1453					s = "13";
1454					break;
1455				case 0xfff00000:
1456					s = "12";
1457					break;
1458				case 0xffe00000:
1459					s = "11";
1460					break;
1461				case 0xffc00000:
1462					s = "10";
1463					break;
1464				case 0xff800000:
1465					s = "9";
1466					break;
1467				case 0xff000000:
1468					s = "8";
1469					break;
1470				case 0xfe000000:
1471					s = "7";
1472					break;
1473				case 0xfc000000:
1474					s = "6";
1475					break;
1476				case 0xf8000000:
1477					s = "5";
1478					break;
1479				case 0xf0000000:
1480					s = "4";
1481					break;
1482				case 0xe0000000:
1483					s = "3";
1484					break;
1485				case 0xc0000000:
1486					s = "2";
1487					break;
1488				case 0x80000000:
1489					s = "1";
1490					break;
1491				case 0x00000000:
1492					s = "0";
1493					break;
1494				}
1495				fprintf(PFILE, "monitor         = \"%s/%s\"\t\t# host (net/mask) allowed to connect for monitoring\n", inet_ntoa(ia), s);
1496			}
1497			b[0] = '\0';
1498
1499			if ((m_rights->rights) & I4B_CA_COMMAND_FULL)
1500				strlcat(b, "fullcmd,", sizeof(b));
1501			if ((m_rights->rights) & I4B_CA_COMMAND_RESTRICTED)
1502				strlcat(b, "restrictedcmd,", sizeof(b));
1503			if ((m_rights->rights) & I4B_CA_EVNT_CHANSTATE)
1504				strlcat(b, "channelstate,", sizeof(b));
1505			if ((m_rights->rights) & I4B_CA_EVNT_CALLIN)
1506				strlcat(b, "callin,", sizeof(b));
1507			if ((m_rights->rights) & I4B_CA_EVNT_CALLOUT)
1508				strlcat(b, "callout,", sizeof(b));
1509			if ((m_rights->rights) & I4B_CA_EVNT_I4B)
1510				strlcat(b, "logevents,", sizeof(b));
1511
1512			if (strlen(b) > 0 && b[strlen(b)-1] == ',')
1513				b[strlen(b)-1] = '\0';
1514
1515			fprintf(PFILE, "monitor-access  = %s\t\t# monitor access rights\n", b);
1516		}
1517	}
1518
1519#endif
1520	/* entry sections */
1521
1522	for (cep = get_first_cfg_entry(); cep; cep = NEXT_CFE(cep)) {
1523		fprintf(PFILE, "\n");
1524		fprintf(PFILE, "#---------------------------------------------------------------------------\n");
1525		fprintf(PFILE, "# entry section %d\n", i);
1526		fprintf(PFILE, "#---------------------------------------------------------------------------\n");
1527		fprintf(PFILE, "entry\n");
1528
1529		fprintf(PFILE, "name                  = %s\t\t# name for this entry section\n", cep->name);
1530
1531		fprintf(PFILE, "isdncontroller        = %d\t\t# ISDN card number used for this entry\n", cep->isdncontroller);
1532		fprintf(PFILE, "isdnchannel           = ");
1533		switch (cep->isdnchannel)
1534		{
1535		case CHAN_ANY:
1536			fprintf(PFILE, "-1\t\t# any ISDN B-channel may be used\n");
1537			break;
1538		default:
1539			fprintf(PFILE, "1\t\t# only ISDN B-channel %d may be used\n", cep->isdnchannel);
1540			break;
1541		}
1542
1543		fprintf(PFILE, "usrdevicename         = %s\t\t# name of userland ISDN B-channel device\n", cep->usrdevicename);
1544		fprintf(PFILE, "usrdeviceunit         = %d\t\t# unit number of userland ISDN B-channel device\n", cep->usrdeviceunit);
1545
1546		fprintf(PFILE, "b1protocol            = %s\n", cep->b1protocol ? "hdlc\t\t# B-channel layer 1 protocol is HDLC" : "raw\t\t# No B-channel layer 1 protocol used");
1547
1548		fprintf(PFILE, "direction             = ");
1549		switch (cep->inout)
1550		{
1551		case DIR_INONLY:
1552			fprintf(PFILE, "in\t\t# only incoming connections allowed\n");
1553			break;
1554		case DIR_OUTONLY:
1555			fprintf(PFILE, "out\t\t# only outgoing connections allowed\n");
1556			break;
1557		case DIR_INOUT:
1558			fprintf(PFILE, "inout\t\t# incoming and outgoing connections allowed\n");
1559			break;
1560		}
1561
1562		if (cep->remote_numbers_count > 1)
1563		{
1564			for (j = 0; j < cep->remote_numbers_count; j++)
1565				fprintf(PFILE, "remote-phone-dialout  = %s\t\t# telephone number %d for dialing out to remote\n", cep->remote_numbers[j].number, j+1);
1566
1567				fprintf(PFILE, "remdial-handling      = ");
1568
1569				switch (cep->remote_numbers_handling)
1570				{
1571				case RNH_NEXT:
1572					fprintf(PFILE, "next\t\t# use next number after last successful for new dial\n");
1573					break;
1574				case RNH_LAST:
1575					fprintf(PFILE, "last\t\t# use last successful number for new dial\n");
1576					break;
1577				case RNH_FIRST:
1578					fprintf(PFILE, "first\t\t# always start with first number for new dial\n");
1579					break;
1580				}
1581			}
1582
1583			if (cep->local_phone_dialout[0])
1584				fprintf(PFILE, "local-phone-dialout   = %s\t\t# show this number to remote when dialling out\n",
1585				    cep->local_phone_dialout);
1586			fprintf(PFILE, "dialout-type          = %s\n", cep->dialouttype ? "calledback\t\t# i am called back by remote" : "normal\t\t# i am not called back by remote");
1587		}
1588
1589		if (!(cep->inout == DIR_OUTONLY))
1590		{
1591			int n;
1592
1593			fprintf(PFILE, "local-phone-incoming  = %s\t\t# incoming calls must match this (mine) telephone number\n", cep->local_phone_incoming);
1594			for (n = 0; n < cep->incoming_numbers_count; n++)
1595				fprintf(PFILE, "remote-phone-incoming = %s\t\t# this is a valid remote number to call me\n",
1596					cep->remote_phone_incoming[n].number);
1597
1598			fprintf(PFILE, "dialin-reaction       = ");
1599			switch (cep->dialin_reaction)
1600			{
1601			case REACT_ACCEPT:
1602				fprintf(PFILE, "accept\t\t# i accept a call from remote and connect\n");
1603				break;
1604			case REACT_REJECT:
1605				fprintf(PFILE, "reject\t\t# i reject the call from remote\n");
1606				break;
1607			case REACT_IGNORE:
1608				fprintf(PFILE, "ignore\t\t# i ignore the call from remote\n");
1609				break;
1610			case REACT_ANSWER:
1611				fprintf(PFILE, "answer\t\t# i will start telephone answering when remote calls in\n");
1612				break;
1613			case REACT_CALLBACK:
1614				fprintf(PFILE, "callback\t\t# when remote calls in, i will hangup and call back\n");
1615				break;
1616			}
1617		}
1618
1619		{
1620			const char *s;
1621			switch (cep->ppp_expect_auth)
1622			{
1623			case AUTH_NONE:
1624				s = "none";
1625				break;
1626			case AUTH_PAP:
1627				s = "pap";
1628				break;
1629			case AUTH_CHAP:
1630				s = "chap";
1631				break;
1632			default:
1633				s = NULL;
1634				break;
1635			}
1636			if (s != NULL)
1637			{
1638				fprintf(PFILE, "ppp-expect-auth       = %s\t\t# the auth protocol we expect to receive on dial-in (none,pap,chap)\n", s);
1639				if (cep->ppp_expect_auth != AUTH_NONE)
1640				{
1641					fprintf(PFILE, "ppp-expect-name       = %s\t\t# the user name allowed in\n", cep->ppp_expect_name);
1642					fprintf(PFILE, "ppp-expect-password   = %s\t\t# the key expected from the other side\n", cep->ppp_expect_password);
1643					fprintf(PFILE, "ppp-auth-paranoid     = %s\t\t# do we require remote to authenticate even if we dial out\n", cep->ppp_auth_flags & AUTH_REQUIRED ? "yes" : "no");
1644				}
1645			}
1646			switch (cep->ppp_send_auth)
1647			{
1648			case AUTH_NONE:
1649				s = "none";
1650				break;
1651			case AUTH_PAP:
1652				s = "pap";
1653				break;
1654			case AUTH_CHAP:
1655				s = "chap";
1656				break;
1657			default:
1658				s = NULL;
1659				break;
1660			}
1661			if (s != NULL)
1662			{
1663				fprintf(PFILE, "ppp-send-auth         = %s\t\t# the auth protocol we use when dialing out (none,pap,chap)\n", s);
1664				if (cep->ppp_send_auth != AUTH_NONE)
1665				{
1666					fprintf(PFILE, "ppp-send-name         = %s\t\t# our PPP account used for dial-out\n", cep->ppp_send_name);
1667					fprintf(PFILE, "ppp-send-password     = %s\t\t# the key sent to the other side\n", cep->ppp_send_password);
1668				}
1669			}
1670			if (cep->ppp_send_auth == AUTH_CHAP ||
1671			   cep->ppp_expect_auth == AUTH_CHAP) {
1672				fprintf(PFILE, "ppp-auth-rechallenge   = %s\t\t# rechallenge CHAP connections once in a while\n", cep->ppp_auth_flags & AUTH_RECHALLENGE ? "yes" : "no");
1673			}
1674		}
1675
1676		if (cep->autoupdown == AUTOUPDOWN_NO)
1677			fprintf(PFILE, "autoupdown = no\n");
1678
1679		{
1680			const char *s;
1681			fprintf(PFILE, "idletime-outgoing     = %d\t\t# outgoing call idle timeout\n", cep->idle_time_out);
1682
1683			switch ( cep->shorthold_algorithm )
1684			{
1685			case SHA_FIXU:
1686				s = "fix-unit-size";
1687				break;
1688			case SHA_VARU:
1689				s = "var-unit-size";
1690				break;
1691			default:
1692				s = "error!!!";
1693				break;
1694			}
1695
1696			fprintf(PFILE, "idle-algorithm-outgoing     = %s\t\t# outgoing call idle algorithm\n", s);
1697		}
1698
1699		if (!(cep->inout == DIR_OUTONLY))
1700			fprintf(PFILE, "idletime-incoming     = %d\t\t# incoming call idle timeout\n", cep->idle_time_in);
1701
1702		{
1703	 		fprintf(PFILE, "unitlengthsrc         = ");
1704			switch (cep->unitlengthsrc)
1705			{
1706			case ULSRC_NONE:
1707				fprintf(PFILE, "none\t\t# no unit length specified, using default\n");
1708				break;
1709			case ULSRC_CMDL:
1710				fprintf(PFILE, "cmdl\t\t# using unit length specified on commandline\n");
1711				break;
1712			case ULSRC_CONF:
1713				fprintf(PFILE, "conf\t\t# using unitlength specified by unitlength-keyword\n");
1714				fprintf(PFILE, "unitlength            = %d\t\t# fixed unitlength\n", cep->unitlength);
1715				break;
1716			case ULSRC_RATE:
1717				fprintf(PFILE, "rate\t\t# using unitlength specified in rate database\n");
1718				fprintf(PFILE, "ratetype              = %d\t\t# type of rate from rate database\n", cep->ratetype);
1719				break;
1720			case ULSRC_DYN:
1721				fprintf(PFILE, "aocd\t\t# using dynamically calculated unitlength based on AOCD subscription\n");
1722				fprintf(PFILE, "ratetype              = %d\t\t# type of rate from rate database\n", cep->ratetype);
1723				break;
1724			}
1725
1726			fprintf(PFILE, "earlyhangup           = %d\t\t# early hangup safety time\n", cep->earlyhangup);
1727
1728		}
1729
1730		{
1731			fprintf(PFILE, "answerprog            = %s\t\t# program used to answer incoming telephone calls\n", cep->answerprog);
1732			fprintf(PFILE, "alert                 = %d\t\t# number of seconds to wait before accepting a call\n", cep->alert);
1733		}
1734
1735		{
1736			if (cep->dialin_reaction == REACT_CALLBACK)
1737				fprintf(PFILE, "callbackwait          = %d\t\t# i am waiting this time before calling back remote\n", cep->callbackwait);
1738
1739			if (cep->dialouttype == DIALOUT_CALLEDBACK)
1740				fprintf(PFILE, "calledbackwait        = %d\t\t# i am waiting this time for a call back from remote\n", cep->calledbackwait);
1741
1742			if (!(cep->inout == DIR_INONLY))
1743			{
1744				fprintf(PFILE, "dialretries           = %d\t\t# number of dialing retries\n", cep->dialretries);
1745				fprintf(PFILE, "recoverytime          = %d\t\t# time to wait between dialling retries\n", cep->recoverytime);
1746				fprintf(PFILE, "dialrandincr          = %s\t\t# use random dialing time addon\n", cep->dialrandincr ? "on" : "off");
1747
1748				fprintf(PFILE, "usedown               = %s\n", cep->usedown ? "on\t\t# ISDN device switched off on excessive dial failures" : "off\t\t# no device switchoff on excessive dial failures");
1749				if (cep->usedown)
1750				{
1751					fprintf(PFILE, "downtries             = %d\t\t# number of dialretries failures before switching off\n", cep->downtries);
1752					fprintf(PFILE, "downtime              = %d\t\t# time device is switched off\n", cep->downtime);
1753				}
1754			}
1755	}
1756	fprintf(PFILE, "\n");
1757}
1758
1759static int
1760lookup_l4_driver(const char *name)
1761{
1762	msg_l4driver_lookup_t query;
1763	int e;
1764
1765	memset(&query, 0, sizeof query);
1766	strncpy(query.name, name, sizeof query.name);
1767	e = ioctl(isdnfd, I4B_L4DRIVER_LOOKUP, &query);
1768	if (e != 0) return -1;
1769	return query.driver_id;
1770}
1771
1772