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