1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31
32/*
33 * data base routines for the network listener process
34 */
35
36/* system include files	*/
37
38#include <fcntl.h>
39#include <stdio.h>
40#include <string.h>
41#include <ctype.h>
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/tiuser.h>
45#include <sys/stropts.h>
46
47/* listener include files */
48
49#include "lsparam.h"		/* listener parameters		*/
50#include "listen.h"		/* listener includes 		*/
51#include "lsfiles.h"		/* listener files info		*/
52#include "lserror.h"		/* listener error codes		*/
53#include "lsdbf.h"		/* data base file stuff		*/
54/* #include "nsaddr.h"		nls includes			*/
55
56#define SUPPRESS	1	/* suppress messages during scan*/
57#define NOSUPPRESS	0	/* don't suppress messages 	*/
58
59/* static data		*/
60
61static char *dbfopenmsg = "Trouble opening data base file";
62static char *dbfrderror = "Error reading data base file: line %d";
63static char *dbfbadlmsg = "Data base file: Error on line %d";
64static char *dbfdupcmsg = "Data base file: Duplicate service code: <%s>";
65static char *dbfunknown = "Unknown error reading data base file: line %d";
66static char *dbfsvccmsg = "Data base file: Illegal service code: <%s>";
67static char *dbfcorrupt = "Data base file has been corrupted";
68
69static int   Dbflineno;		/* current line number in dbf		*/
70static unsigned Dbfentries;	/* number of non-comment lines		*/
71extern char  *Server_cmd_lines; /* contains svc_code, cmd_line, mod_list */
72extern char  *New_cmd_lines; /* svc_code, cmd_line, mod_list (on reread)*/
73
74/* public variables */
75
76
77/*
78 * read_dbf:
79 *
80 * read the data base file into internal structures
81 *
82 * all data base routines under read_dbf log there own errors and return -1
83 * in case of an error.
84 *
85 * if 're_read' is non-zero, this stuff is being called to read a new
86 * data base file after the listener's initialization.
87 */
88
89int
90read_dbf(re_read)
91int	re_read;	/* zero means first time	*/
92{
93	register unsigned size;
94	int exit_flag = EXIT | NOCORE;
95	register dbf_t *dbf_p;
96	register char  *cmd_p;
97	unsigned scan_dbf();
98	extern dbf_t *Dbfhead;		/* Dbfentries (when allocated)	*/
99	extern dbf_t *Newdbf;		/* Dbfentries (on re-read)	*/
100	extern char *calloc();
101
102	DEBUG((9,"in read_dbf"));
103
104	if (check_version())
105		error(E_BADVER, EXIT | NOCORE);
106
107	if (re_read)	{			/* not first time */
108		exit_flag = CONTINUE;
109	}
110
111	/*
112	 * note: data base routines log their own error messages
113	 */
114
115	Dbfentries = 0;
116	DEBUG((9,"read_dbf: open file here:  %s", DBFNAME));
117	if ( (size = scan_dbf(DBFNAME)) == (unsigned)(-1) )
118		error( E_SCAN_DBF, exit_flag | NO_MSG );
119
120	DEBUG((5,"read_dbf: scan complete: non-commented lines: %u, size: %u",
121		Dbfentries, size));
122
123	if (!size)  {
124		logmessage("No database?  0 entries?");
125		return(0);
126	}
127
128	/*
129	 * allocate enough space for Dbfentries of 'size' bytes (total)
130	 * The +1 is to insure a NULL last entry!
131	 */
132
133	if (!(dbf_p = (dbf_t *)calloc(Dbfentries+1,sizeof(dbf_t)))
134	   || !(cmd_p = calloc(size, 1)))  {
135		DEBUG((1,"cannot calloc %u + %u bytes", size,
136			(Dbfentries+1)*(unsigned)sizeof(dbf_t)));
137		error( E_DBF_ALLOC, exit_flag);	/* if init, exit */
138
139		/* if still here, this is a re-read	*/
140
141		if (dbf_p)
142			free(dbf_p);
143		if (cmd_p)
144			free(cmd_p);
145		return(-1);
146	}
147
148	if (get_dbf(dbf_p, cmd_p))  {
149		DEBUG((9, "get_dbf FAILED"));
150		error(E_DBF_IO, exit_flag | NO_MSG);
151
152		/* if still here, this is a re_read */
153		free(dbf_p);
154		free(cmd_p);
155		return(-1);
156	}
157
158	if (re_read)  {
159		Newdbf = dbf_p;
160		New_cmd_lines = cmd_p;
161#ifdef	DEBUGMODE
162		DEBUG((7,"read_dbf: NEW data base dump..."));
163		if (Newdbf)
164			for (dbf_p = Newdbf; dbf_p->dbf_svc_code; ++dbf_p)
165				DEBUG((7, "svc code <%s>; id: %s; private address: %s; modules: %s; cmd line: %s; sflags: %x, prognum: %d version: %d",
166				dbf_p->dbf_svc_code, dbf_p->dbf_id, dbf_p->dbf_prv_adr, dbf_p->dbf_modules, dbf_p->dbf_cmd_line, dbf_p->dbf_sflags, dbf_p->dbf_prognum, dbf_p->dbf_version));
167#endif	/* DEBUGMODE */
168	}
169	else {
170		Dbfhead = dbf_p;
171		Server_cmd_lines = cmd_p;
172#ifdef	DEBUGMODE
173		DEBUG((7,"read_dbf: data base dump..."));
174		if (Dbfhead)
175			for (dbf_p = Dbfhead; dbf_p->dbf_svc_code; ++dbf_p)
176				DEBUG((7, "svc code <%s>; id: %s; r1: %s; r2: %s; r3: %s; private address: %s; modules: %s; cmd line: %s; sflags: %x, prognum: %d version: %d",
177				dbf_p->dbf_svc_code, dbf_p->dbf_id, dbf_p->dbf_res1, dbf_p->dbf_res2, dbf_p->dbf_res3, dbf_p->dbf_prv_adr, dbf_p->dbf_modules, dbf_p->dbf_cmd_line, dbf_p->dbf_sflags, dbf_p->dbf_prognum, dbf_p->dbf_version));
178#endif	/* DEBUGMODE */
179	}
180
181	return(0);
182}
183
184
185/*
186 * get_dbf: read the file and fill the structures
187 *	    checking for duplicate entries as we go
188 */
189
190int
191get_dbf(dbf_p, cmd_p)
192register dbf_t *dbf_p;
193register char *cmd_p;
194{
195	dbf_t *dbfhead = dbf_p;
196	register int n, i;
197	char buf[DBFLINESZ];
198	register char *p = buf;
199	char scratch[128];
200	FILE *dbfilep;
201	char *cmd_line_p;
202	int flags;
203	char *svc_code_p;
204	char *id_p;
205	char *res1_p;
206	char *res2_p;
207	char *res3_p;
208	char *private_p;
209	int sflags;
210	int prognum;
211	int vernum;
212	char *module_p;
213	register dbf_t *tdbf_p;
214	extern int atoi();
215	extern int Dbf_entries;
216	extern int NLPS_proc;
217	extern int errno;
218
219	Dbflineno = 0;
220	Dbf_entries = 0;  /* number of private addresses in dbf file */
221
222	DEBUG((9,"in get_dbf: "));
223	if (!(dbfilep = fopen(DBFNAME,"r")))  {
224		logmessage(dbfopenmsg);
225		error(E_DBF_IO, EXIT | NOCORE | NO_MSG);
226	}
227
228	while (n = rd_dbf_line(dbfilep,p,&svc_code_p,&flags,&id_p,&res1_p,&res2_p,&res3_p,&private_p,&prognum,&vernum,&module_p,&sflags,&cmd_line_p,NOSUPPRESS))  {
229
230		if (n == -1)  {			/* read error	*/
231			fclose(dbfilep);
232			return(-1);
233		}
234
235		/* make sure service code is legal 			*/
236
237		i = strlen(svc_code_p);
238		if ( (i == 0) || (i >= SVC_CODE_SZ) )
239			goto reject;
240
241		/* check for duplicate service code			*/
242		tdbf_p = dbfhead;
243		while (tdbf_p->dbf_svc_code)  {	/* duplicate svc code?	*/
244			if (!strcmp(svc_code_p, tdbf_p->dbf_svc_code))  {
245				sprintf(scratch, dbfdupcmsg, svc_code_p);
246				logmessage(scratch);
247				return(-1);
248			}
249			++tdbf_p;
250		}
251
252		/* NLPS_proc is set by the nlps_server, which also uses these
253		 * routines.  The actual listener child shouldn't ever need
254		 * to read a database, so it will never be here
255		 */
256		if (!NLPS_proc && (strlen(private_p) == 0) && !(sflags & DFLAG))
257			continue;	/* ignore entries with no address */
258
259		/*
260		 * child doesn't care about private addresses
261		 */
262
263		if (!NLPS_proc) {
264			i = strlen(private_p);
265			if (i >= PRV_ADR_SZ) {
266				goto p_reject;
267			}
268			Dbf_entries++;
269		}
270
271		/*
272		 * legal, non-duplicate entry: copy it into internal data base
273		 */
274
275		dbf_p->dbf_fd = -1;	/* set to actual fd in add_prvaddr */
276		dbf_p->dbf_flags = flags;
277		dbf_p->dbf_sflags = sflags;
278		dbf_p->dbf_prognum = prognum;
279		dbf_p->dbf_version = vernum;
280		strcpy(cmd_p, svc_code_p);
281		dbf_p->dbf_svc_code = cmd_p;
282		cmd_p += strlen(svc_code_p) + 1;	/* +1 for null */
283		strcpy(cmd_p, cmd_line_p);
284		dbf_p->dbf_cmd_line = cmd_p;
285		cmd_p += strlen(cmd_line_p) + 1;
286		strcpy(cmd_p, id_p);
287		dbf_p->dbf_id = cmd_p;
288		cmd_p += strlen(id_p) + 1;
289		strcpy(cmd_p, res1_p);
290		dbf_p->dbf_res1 = cmd_p;
291		cmd_p += strlen(res1_p) + 1;
292		strcpy(cmd_p, res2_p);
293		dbf_p->dbf_res2 = cmd_p;
294		cmd_p += strlen(res2_p) + 1;
295		strcpy(cmd_p, res3_p);
296		dbf_p->dbf_res3 = cmd_p;
297		cmd_p += strlen(res3_p) + 1;
298		if (strlen(private_p) != 0) {
299			strcpy(cmd_p, private_p);
300			dbf_p->dbf_prv_adr = cmd_p;
301			cmd_p += strlen(private_p) + 1;
302		}
303		else
304			dbf_p->dbf_prv_adr = NULL;
305		strcpy(cmd_p, module_p);
306		dbf_p->dbf_modules = cmd_p;
307		cmd_p += strlen(module_p) + 1;	/* cmd line + null char */
308		++dbf_p;
309	}
310
311	fclose(dbfilep);
312	return(0);
313
314reject:
315	DEBUG((9, "svc_code <%s> failed validation test", svc_code_p));
316	sprintf(scratch, dbfsvccmsg, svc_code_p);
317	logmessage(scratch);
318	return(-1);
319p_reject:
320	DEBUG((9,"private address <%s> failed validation test", private_p));
321	sprintf(scratch, "Invalid private address ignored: \\x%x", private_p);
322	logmessage(scratch);
323	return(-1);
324}
325
326
327/*
328 * scan_dbf:	Take a quick pass through the data base file to figure out
329 *		approx. how many items in the file we'll need to
330 *		allocate storage for.  Essentially, returns the number
331 *		of non-null, non-comment lines in the data base file.
332 *
333 *		return: -1 == error.
334 *			other == number of non-comment characters.
335 *			Dbfentries set.
336 */
337
338unsigned
339scan_dbf(path)
340register char *path;
341{
342	register unsigned int size = 0;
343	register int n;
344	register FILE *dbfilep;
345	char buf[DBFLINESZ];
346	register char *p = buf;
347	char *svc_code_p;
348	int flags;
349	char *cmd_line_p;
350	char *module_p;
351	char *id_p;
352	char *res1_p;
353	char *res2_p;
354	char *res3_p;
355	int sflags;
356	int prognum;
357	int vernum;
358	char *private_p;
359	extern int errno;
360
361	DEBUG((9, "In scan_dbf.  Scanning data base file %s.", path));
362
363	if (!(dbfilep = fopen(path,"r")))  {
364		DEBUG((9,"errorno = %d", errno));
365		logmessage(dbfopenmsg);
366		return(-1);
367	}
368
369	do {
370		n = rd_dbf_line(dbfilep,p,&svc_code_p,&flags,&id_p,&res1_p,&res2_p,&res3_p,&private_p,&prognum,&vernum,&module_p,&sflags,&cmd_line_p,SUPPRESS);
371		if (n == -1)  {
372			fclose(dbfilep);
373			return(-1);
374		}
375		size += (unsigned)n;
376		if (n)
377			++Dbfentries;
378	} while (n);
379
380	fclose(dbfilep);
381	return(size);
382}
383
384
385/*
386 * rd_dbf_line:	Returns the next non-comment line into the
387 *		given buffer (up to DBFLINESZ bytes).
388 *		Skips 'ignore' lines.
389 *
390 *		Returns:	0 = done, -1 = error,
391 * 				other = cmd line size incl. terminating null.
392 *				*svc_code_p = service code;
393 *				*id_p = user id string;
394 *				*res1_p = reserved string;
395 *				*res2_p = reserved string;
396 *				*res3_p = reserved string;
397 *				*private_p = contains private address;
398 *				*flags_p = decoded flags;
399 *				prognum = RPC program #;
400 *				vernum = RPC version $;
401 *				cnd_line_p points to null terminated cmd line;
402 *
403 * When logging errors, the extern Dbflineno is used.
404 */
405
406int
407rd_dbf_line(fp, bp, svc_code_p, flags_p, id_p, res1_p, res2_p, res3_p, private_p, prognum, vernum, module_p, sflags, cmd_line_p, mflag)
408register FILE *fp;
409register char *bp;
410char **svc_code_p;
411int *flags_p;
412char **id_p;
413char **res1_p;
414char **res2_p;
415char **res3_p;
416char **private_p;
417int *prognum;
418int *vernum;
419char **module_p;
420int *sflags;
421char **cmd_line_p;
422int mflag;
423{
424	register int length;
425	register char *p;
426
427	do {
428		++Dbflineno;
429		length = 0;
430
431		if (!fgets(bp, DBFLINESZ, fp))  {
432			if (feof(fp))  {
433				return(0);	/* EOF	*/
434			}
435			if (ferror(fp))  {
436				sprintf(bp,dbfrderror,Dbflineno);
437				logmessage(bp);
438				return(-1);
439			}
440			sprintf(bp,dbfunknown,Dbflineno);
441			logmessage(bp);
442			return(-1);		/* Unknown error (?)	*/
443		}
444
445		if (*(bp+strlen(bp)-1) != '\n')  {  /* terminating newline? */
446			sprintf(bp, dbfbadlmsg, Dbflineno);
447			logmessage(bp);
448			return(-1);
449		}
450
451		*(bp + strlen(bp) -1) = (char)0; /* delete newline	*/
452
453		if (strlen(bp) && (p = strchr(bp, DBFCOMMENT)))
454			*p = (char)0;		/* delete comments	*/
455		if (!strlen(bp))
456			continue;
457
458		p = bp + strlen(bp) - 1;	/* bp->start; p->end	*/
459		while ((p != bp) && (isspace(*p)))  {
460			*p = (char)0;		/* delete terminating spaces */
461			--p;
462		}
463
464		while (*bp)			/* del beginning white space*/
465			if (isspace(*bp))
466				++bp;
467			else
468				break;
469
470		if (strlen(bp)) {		/* anything left?	*/
471
472		   if (!(length=scan_line(bp,svc_code_p,flags_p,id_p,res1_p,res2_p,res3_p,private_p,prognum,vernum,module_p,sflags,cmd_line_p,mflag))) {
473
474			DEBUG((1, "rd_dbf_line line %d, error while scanning entry",
475			  Dbflineno));
476			sprintf(bp, dbfbadlmsg, Dbflineno);
477			logmessage(bp);
478			return(-1);
479		    }
480		}
481
482	}  while (!length);		/* until we have something */
483
484	DEBUG((5,"rd_dbf_line: line: %d,cmd line len: %d",Dbflineno, length+1));
485
486	return(length+1); /* +1 for the trailing NULL */
487
488}
489
490
491/*
492 * scan a non-white space line
493 *		0 = error;
494 *		other = length of cmd line;
495 *
496 *	non-null lines have the following format:
497 *
498 *	service_code: flags: id: res1: res2: res3: private address: rpcdata: sflags: modules: cmd_line # comments
499 *
500 * mflag is set to suppress messages (scan_line is called both for parsing
501 * and counting, messages should only be output once)
502 */
503
504int
505scan_line(bp, svc_code_p, flags_p, id_p, res1_p, res2_p, res3_p, private_p, prognum, vernum, module_p, sflags, cmd_line_p, mflag)
506register char *bp;
507char **svc_code_p;
508register int *flags_p;
509char **id_p;
510char **res1_p;
511char **res2_p;
512char **res3_p;
513char **private_p;
514int *prognum;
515int *vernum;
516char **module_p;
517int *sflags;
518register char **cmd_line_p;
519int mflag;
520{
521	register char *p;
522	register char *nexttok;
523	register char *ptr;
524	int sawsep = 0;
525	char scratch[BUFSIZ];
526
527	*flags_p = 0;
528
529	if (!(p = strchr(bp, DBFTOKSEP ))) {	/* look for service code string */
530		DEBUG((9,"scan_line failed svc_code strchr"));
531		return(0);
532	}
533	*p = '\0';
534	*svc_code_p = bp;
535	nexttok = ++p;
536
537	if (!(p = strchr(nexttok, DBFTOKSEP )))  {
538		DEBUG((9,"scan_line failed flags strchr"));
539		return(0);
540	}
541	*p = '\0';
542
543	while (*nexttok)  {
544		switch (*nexttok)  {
545		case 'x':		/* service is turned off	*/
546		case 'X':
547			*flags_p |= DBF_OFF;
548			break;
549		case 'u':		/* create utmp entry		*/
550			*flags_p |= DBF_UTMP;
551			break;
552		default:
553			DEBUG((1,"scan_line: unknown flag char: 0x%x",*nexttok));
554			*flags_p = DBF_UNKNOWN;
555			break;
556		}
557		++nexttok;
558	}
559	nexttok = ++p;
560
561	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
562		DEBUG((9,"scan_line failed id strchr"));
563		return(0);
564	}
565	*p = '\0';
566	*id_p = nexttok;
567	nexttok = ++p;
568
569	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
570		DEBUG((9,"scan_line failed res1 strchr"));
571		return(0);
572	}
573	*p = '\0';
574	*res1_p = nexttok;
575	nexttok = ++p;
576
577	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
578		DEBUG((9,"scan_line failed res2 strchr"));
579		return(0);
580	}
581	*p = '\0';
582	*res2_p = nexttok;
583	nexttok = ++p;
584
585	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
586		DEBUG((9,"scan_line failed res3 strchr"));
587		return(0);
588	}
589	*p = '\0';
590	*res3_p = nexttok;
591	nexttok = ++p;
592
593	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
594		DEBUG((9,"scan_line failed private strchr"));
595		return(0);
596	}
597	*p = '\0';
598	*private_p = nexttok;
599	nexttok = ++p;
600
601	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
602		DEBUG((9,"scan_line failed rpc strchr"));
603		return(0);
604	}
605	*p = '\0';
606
607	*prognum = -1;
608	*vernum = -1;
609	if (*nexttok) {
610		/* there is rpc info */
611		for (ptr = nexttok; *ptr; ++ptr) {
612			if ((*ptr == ',') && !sawsep) {
613				/*
614				 * skip separator - note that if
615				 * separator has been seen, it's not
616				 * a digit so it will fail below
617				 */
618				sawsep++;
619				continue;
620			}
621			if (!isdigit(*ptr)) {
622				sprintf(scratch, "service code <%s> specifies non-integer rpc info", *svc_code_p);
623				logmessage(scratch);
624				return(0);
625			}
626		}
627		ptr = strchr(nexttok, ',');
628		if (ptr) {
629			if ((*prognum = atoi(nexttok)) < 0) {
630				if (!mflag) {
631					/* messages aren't suppressed */
632					sprintf(scratch, "service code <%s> specifies negative program number", *svc_code_p);
633					logmessage(scratch);
634				}
635				return(0);
636			}
637			if ((*vernum = atoi(ptr + 1)) < 0) {
638				if (!mflag) {
639					sprintf(scratch, "service code <%s> specifies negative version number", *svc_code_p);
640					logmessage(scratch);
641				}
642				return(0);
643			}
644		}
645		else {
646			if (!mflag) {
647				sprintf(scratch, "service code <%s> - invalid rpcinfo", *svc_code_p);
648				logmessage(scratch);
649			}
650			return(0);
651		}
652	}
653	nexttok = ++p;
654
655	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
656		DEBUG((9,"scan_line failed sflags strchr"));
657		return(0);
658	}
659	*p = '\0';
660
661	*sflags = 0;
662	while (*nexttok)  {
663		switch (*nexttok)  {
664		case 'c':	/* dbf_cmd_line is a command */
665			*sflags |= CFLAG;
666			break;
667		case 'd':	/* dynamic address */
668			if ((int) strlen(*private_p) > 0) {
669				if (!mflag) {
670					sprintf(scratch, "service code <%s> specifies static and dynamic address", *svc_code_p);
671					logmessage(scratch);
672					logmessage("  address info ignored");
673				}
674				/* don't set DFLAG and wipe out private addr */
675				**private_p = '\0';
676			}
677			else {
678				*sflags |= DFLAG;
679			}
680			break;
681		case 'p':	/* dbf_cmd_line is a pipe */
682			*sflags |= PFLAG;
683			break;
684		default:
685			if (!mflag) {
686				sprintf(scratch, "service code <%s> unknown flag <%c> ignored", *svc_code_p, *nexttok);
687				logmessage(scratch);
688			}
689			break;
690		}
691		++nexttok;
692	}
693	nexttok = ++p;
694
695	if (!(p = strchr(nexttok, DBFTOKSEP ))) {
696		DEBUG((9,"scan_line failed module strchr"));
697		return(0);
698	}
699	*p = '\0';
700	*module_p = nexttok;
701	nexttok = ++p;
702
703	*cmd_line_p = nexttok;
704
705	DEBUG((9,"scan_line: modules: %s; line: %s; len: %d", *module_p, *cmd_line_p, strlen(*svc_code_p)+strlen(*id_p)+strlen(*res1_p)+strlen(*res2_p)+strlen(*res3_p)+strlen(*private_p)+strlen(*module_p)+strlen(*cmd_line_p)+9));
706	/*
707	 * return the length of the buffer.  Add 9 for the NULLs after each
708	 * string
709	 */
710	return(strlen(*svc_code_p)+strlen(*id_p)+strlen(*res1_p)+strlen(*res2_p)+strlen(*res3_p)+strlen(*private_p)+strlen(*module_p)+strlen(*cmd_line_p)+9);
711}
712
713
714
715#define VERSIONSTR	"# VERSION="
716
717int
718check_version(void)
719{
720	FILE *fp;
721	char *line, *p, *tmp;
722	int version;
723
724	if ((fp = fopen(DBFNAME, "r")) == NULL) {
725		logmessage(dbfopenmsg);
726		error(E_DBF_IO, EXIT | NOCORE | NO_MSG);
727	}
728	if ((line = (char *) malloc(DBFLINESZ)) == NULL)
729		error(E_DBF_ALLOC, EXIT | NOCORE);
730	p = line;
731	while (fgets(p, DBFLINESZ, fp)) {
732		if (!strncmp(p, VERSIONSTR, strlen(VERSIONSTR))) {
733			/* pitch the newline */
734			tmp = strchr(p, '\n');
735			if (tmp)
736				*tmp = '\0';
737			else {
738				logmessage(dbfcorrupt);
739				error(E_DBF_CORRUPT, EXIT | NOCORE);
740			}
741			p += strlen(VERSIONSTR);
742			if (*p)
743				version = atoi(p);
744			else {
745				logmessage(dbfcorrupt);
746				error(E_DBF_CORRUPT, EXIT | NOCORE);
747			}
748			free(line);
749			fclose(fp);
750			if (version != VERSION)
751				return(1);	/* wrong version */
752			else
753				return(0);	/* version ok */
754		}
755		p = line;
756	}
757	logmessage(dbfcorrupt);
758	error(E_DBF_CORRUPT, EXIT | NOCORE);
759	return(1);
760}
761
762
763/*
764 * mkdbfargv:	Given a pointer to a dbf_t, construct argv
765 *		for an exec system call.
766 *		Warning: returns a pointer to static data which are
767 *			 overritten by each call.
768 *
769 *		There is a practical limit of 50 arguments (including argv[0])
770 *
771 *		Warning: calling mkdbfargv destroys the data (by writing null
772 *		characters via strtok) pointed to by dbp->dbf_cmd_line.
773 */
774
775static char *dbfargv[50];
776static char *delim = " \t'\"";		/* delimiters */
777
778char **
779mkdbfargv(dbp)
780register dbf_t	*dbp;
781{
782	register char **argvp = dbfargv;
783	register char *p = dbp->dbf_cmd_line;
784	char delch;
785	register char *savep;
786	register char *tp;
787	char scratch[BUFSIZ];
788	char *strpbrk();
789#ifdef	DEBUGMODE
790	register int i = 0;
791#endif
792
793	*argvp = 0;
794	savep = p;
795	while (p && *p) {
796		if (p = strpbrk(p, delim)) {
797			switch (*p) {
798			case ' ':
799			case '\t':
800				/* "normal" cases */
801				*p++ = '\0';
802				*argvp++ = savep;
803				DEBUG((9, "argv[%d] = %s", i++, savep));
804				/* zap trailing white space */
805				while (isspace(*p))
806					p++;
807				savep = p;
808				break;
809			case '"':
810			case '\'':
811				/* found a string */
812				delch = *p; /* remember the delimiter */
813				savep = ++p;
814
815/*
816 * We work the string in place, embedded instances of the string delimiter,
817 * i.e. \" must have the '\' removed.  Since we'd have to do a compare to
818 * decide if a copy were needed, it's less work to just do the copy, even
819 * though it is most likely unnecessary.
820 */
821
822				tp = p;
823				for (;;) {
824					if (*p == '\0') {
825						sprintf(scratch, "invalid command line, non-terminated string for service code %s", dbp->dbf_svc_code);
826						logmessage(scratch);
827						exit(2); /* server, don't log */
828					}
829					if (*p == delch) {
830						if (*(tp - 1) == '\\') { /* \delim */
831							*(tp - 1) = *p;
832							p++;
833						}
834						else { /* end of string */
835							*tp = 0;
836							*argvp++ = savep;
837							DEBUG((9, "argv[%d] = %s", i++, savep));
838							p++;
839							/* zap trailing white space */
840							while (isspace(*p))
841								p++;
842							savep = p;
843							break;
844						}
845					}
846					else {
847						*tp++ = *p++;
848					}
849				}
850				break;
851			default:
852				logmessage("Internal error in parse routine");
853				exit(2); /* server, don't log */
854			}
855		}
856		else {
857			*argvp++ = savep;
858			DEBUG((9, "argv[%d] = %s", i++, savep));
859		}
860	}
861	*argvp = 0;
862	return(dbfargv);
863}
864
865
866
867/*
868 *
869 * getentry: Given a fd, this routine will return a
870 *		    dbf entry.  If the entry doesn't exist it will
871 *		    return NULL.
872 */
873
874dbf_t *
875getentry(fd)
876int	fd;
877{
878	extern dbf_t *Dbfhead;		/* Dbfentries (when allocated)	*/
879	register dbf_t 	*dbp = Dbfhead;
880
881	if (!Dbfhead) {		/* no private address in database file */
882		DEBUG((9, "getdbfentry - nothing in Dbfhead = %s ",Dbfhead));
883		return((dbf_t *)0);
884	}
885	else
886		for (dbp = Dbfhead;  dbp->dbf_prv_adr;   ++dbp)
887			if (fd == dbp->dbf_fd) {
888				return(dbp);
889			}
890
891	return((dbf_t *)0);	/* requested private address not in list */
892
893}
894
895
896/*
897 * pushmod:	push modules if defined in the data base entry.
898 *
899 *		WARNING: This routine writes into the in-memory copy
900 *		of the database file.  Therefore, after it is called,
901 *		the incore copy of the database file will no longer be valid.
902 */
903
904int
905pushmod(fd, mp)
906int fd;
907register char *mp;
908{
909	register char *cp;
910	register char *bufp = mp;
911	char name[32];
912	extern int errno;
913
914	DEBUG((9,"in pushmod:"));
915	if (!mp || *mp == '\0') {
916		DEBUG((9,"NULL list: exiting pushmod"));
917		return(0);
918	}
919	/* pop timod if it is on the stack */
920	if (ioctl(fd, I_LOOK, name) >= 0) {
921		if (strcmp(name, "timod") == 0) {
922			if (ioctl(fd, I_POP, 0) < 0)
923				DEBUG((9,"pushmod: I_POP failed"));
924		}
925	}
926	while ((cp = strtok(bufp, ",")) != NULL) {
927		bufp = NULL;
928		DEBUG((9,"pushmod: about to push %s", cp));
929		if (ioctl(fd, I_PUSH, cp) < 0) {
930			DEBUG((9,"pushmod: ioctl failed, errno = %d",errno));
931			return(1);
932		}
933	}
934	DEBUG((9,"exiting pushmod:"));
935	return(0);
936}
937