/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"@(#)listen:nlps_serv.c	1.3.11.1"


/* system include files	*/

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <varargs.h>
#include <string.h>
#include <errno.h>
#include <sys/utsname.h>
#include <sys/tiuser.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <values.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <sys/poll.h>
#include <sys/dir.h>
#include <sys/stropts.h>
#include <utmp.h>
#include <sac.h>


/* listener include files */

#include "lsparam.h"		/* listener parameters		*/
#include "lsfiles.h"		/* listener files info		*/
#include "lserror.h"		/* listener error codes		*/
#include "lsnlsmsg.h"		/* NLPS listener protocol	*/
#include "lssmbmsg.h"		/* MS_NET identifier		*/
#include "lsdbf.h"		/* data base file stuff		*/
#include "listen.h"

/* global variables */

FILE *Logfp;		/* file pointer for nlps_server's log file	*/
#ifdef DEBUGMODE
FILE *Debugfp;		/* debugging output				*/
#endif

int Dbf_entries;	/* number of private addresses in dbf file */
dbf_t	*Dbfhead;
dbf_t	*Newdbf;
char	*New_cmd_lines;
char	*Server_cmd_lines;

extern int t_errno;

/* 
 * These global symbols are used for logging.
 * Pid, NLPS_proc, and Lastmsg are significant here; the others aren't used.
 */
int	NLPS_proc = 1;
pid_t	Pid;
char	Lastmsg[BUFSIZ];
char	*Netspec = NULL;
int	Splflag = 0;
int	Logmax = 0;
char	*Mytag = NULL;

char	msgbuf[BUFSIZ];
char	Altbasedir[BUFSIZ];
char	Basedir[BUFSIZ];
extern	char *getenv();

main (path, argv, environ)
char *path;
char **argv;
char **environ;
{
	extern int read_dbf();
	char *provider;

	provider = getenv("PMTAG");
	sprintf(Altbasedir, "%s/%s/", ALTDIR, provider);
	sprintf(Basedir, "%s/%s/", BASEDIR, provider);
	sprintf(msgbuf, "%s/%s", Altbasedir, LOGNAME);
	if (!(Logfp = fopen(msgbuf, "a+")))  {
		(void)exit(1);  
	}

#ifdef DEBUGMODE
	sprintf(msgbuf, "%s/%s", Altbasedir, PDEBUGNAME);
	if (!(Debugfp = fopen(msgbuf, "a")))  {
		logmessage("NLPS: Unable to open DEBUG file");
		(void)exit(1);  
	}
#endif

	/*
	 * re-sync TLI structures after we were exec'ed from listener
	 */

	if (t_sync(0) == -1) {
		DEBUG((9,"t_sync failed, t_errno %d", t_errno));
		logmessage("NLPS: Resynchronization of TLI failed");
		(void)exit(1);
	}

	nlps_server();
	return(0);
}

/*
 *  nlps_server: 
 */

nlps_server()
{
	register size;
	char buf[RCVBUFSZ];
	char **argv;
	register char *bp = buf;
	register dbf_t *dbp;
	dbf_t *getdbfentry();
	extern char **mkdbfargv();

	Pid = getpid();
	DEBUG((9,"in nlps_server (NLPS/SMB message), pid %ld", Pid));

	if ((size = getrequest(bp)) <= 0)  {
		logmessage("NLPS: No/bad service request received");
		return(-1);
	}

	if (size < 0)  {
		DEBUG((7,"nlps_server(): Error returned from getrequest()" ));
		logmessage("NLPS: Error returned from getrequest()");
		return(-1);
	}

	/*
	 * if message is NLPS protocol...
	 */

	if ((!strncmp(bp,NLPSIDSTR,NLPSIDSZ))  && 	/* NLPS request	*/
	    (*(bp + NLPSIDSZ) == NLPSSEPCHAR)) {
		nls_service(bp, size);
		(void)sleep(10);	/* if returned to here, then 
				 * must sleep for a short period of time to
				 * insure that the client received any possible
				 * exit response message from the listener.
					 */

	/*
	 * else if message is for the MS-NET file server...
	 */

	} else if ( (*bp == (char)0xff) && (!strncmp(bp+1,SMBIDSTR,SMBIDSZ)) )  {
		if (dbp = getdbfentry(DBF_SMB_CODE))
		    if (dbp->dbf_flags & DBF_OFF)
			logmessage("NLPS: SMB message, server disabled in data base");
		    else    {
			argv = mkdbfargv(dbp);
			smbservice(bp, size, argv);
		    }
		else
			logmessage("NLPS: SMB message, no data base entry");

	/*
	 * else, message type is unknown...
	 */

	} else  {
		logmessage("NLPS: Unknown service request (ignored)");
		DEBUG((7,"msg size: %d; 1st four chars (hex) %x %x %x %x",
			*bp, *(bp+1), *(bp+2), *(bp+3)));
	}

	/*
	 * the routines that start servers return only if there was an error
	 * and will have logged their own errors.
	 */

	return(-1);
}


/*
 * getrequest:	read in a full message.  Timeout, in case the client died.
 *		returns: -1 = timeout or other error.
 *			 positive number = message size.
 */

int
getrequest(bp)
register char *bp;
{
	register size;
	register char *tmp = bp;
	int flags;
	extern void timeout();
	short cnt;
	void (*oldhanp)();

	DEBUG((9,"in getrequest"));

	oldhanp = signal(SIGALRM, timeout);
	(void)alarm(ALARMTIME);

	/* read in MINMSGSZ to determine type of msg */
	if ((size = l_rcv(bp, MINMSGSZ, &flags)) != MINMSGSZ) {
		DEBUG((9, "getrequest: l_rcv returned %d", size));
		tli_error(E_RCV_MSG, CONTINUE);
		return(-1);
	}
	tmp += size;

	/*
	 * if message is NLPS protocol...
	 */

	if ((!strncmp(bp,NLPSIDSTR,NLPSIDSZ))  && 	/* NLPS request	*/
	    (*(bp + NLPSIDSZ) == NLPSSEPCHAR)) {

		do {
			if (++size > RCVBUFSZ) {
				logmessage("NLPS: recieve buffer not large enough");
				return(-1);
			}

			if (t_rcv(0, tmp, sizeof(char), &flags) != sizeof(char)) {
				tli_error(E_RCV_MSG, CONTINUE);
				return(-1);
			}

		} while (*tmp++ != '\0');



	/*
	 * else if message is for the MS-NET file server...
	 */

	} else if ( (*bp == (char)0xff) && (!strncmp(bp+1,SMBIDSTR,SMBIDSZ)) )  {

		/* read in 28 more bytes to get count of paramter words */
		if (l_rcv(tmp, 28, &flags) != 28) {
			tli_error(E_RCV_MSG, CONTINUE);
			return(-1);
		}
		tmp += 28;
		size += 28;

		/*
		 * read amount of paramater words plus word for
                 * the number of data bytes to follow (2 bytes/word)
                 */
		cnt = (int)*(tmp - 1) * 2 + 2;

		if ((size += cnt) > RCVBUFSZ) {
			logmessage("NLPS: recieve buffer not large enough");
			return(-1);
		}

		if (l_rcv(tmp, cnt, &flags) != cnt) {
			tli_error(E_RCV_MSG, CONTINUE);
			return(-1);
		}
		tmp += cnt;

		getword(tmp - 2, &cnt);

		if ((size += cnt) > RCVBUFSZ) {
			logmessage("NLPS: recieve buffer not large enough");
			return(-1);
		}

		if (l_rcv(tmp, cnt, &flags) != cnt) {
			tli_error(E_RCV_MSG, CONTINUE);
			return(-1);
		}

		nullfix();

	/*
	 * else, message type is unknown...
	 */

	} else  {
		logmessage("NLPS: Unknown service request (ignored)");
		DEBUG((7,"msg size: %d; 1st four chars (hex) %x %x %x %x",
			*bp, *(bp+1), *(bp+2), *(bp+3)));
		return(-1);
	}

	(void)alarm(0);
	signal(SIGALRM, oldhanp);

	DEBUG((7,"t_rcv returned %d, flags: %x",size,flags));

	return(size);
}


/*
 * The following code is for patching a 6300 side bug.  The original
 * message that comes over may contain 2 null bytes which aren't
 * part of the message, and if left on the stream, will poison the
 * server.  Peek into the stream and snarf up those bytes if they
 * are there.  If anything goes wrong with the I_PEEK, just continue,
 * if the nulls weren't there, it'll work, and if they were, all that
 * will happen is that the server will fail.  Just note what happened
 * in the log file.
 */

nullfix()
{
	struct strpeek peek;
	register struct strpeek *peekp;
	char scratch[BUFSIZ];
	char junk[2];
	int flags;
	int ret;

	peekp = &peek;
	peekp->flags = 0;
	/* need to ask for ctl info to avoid bug in I_PEEK code */
	peekp->ctlbuf.maxlen = 1;
	peekp->ctlbuf.buf = junk;
	peekp->databuf.maxlen = 2;
	peekp->databuf.buf = junk;
	ret = ioctl(0, I_PEEK, &peek);
	if (ret == -1) {
		sprintf(scratch, "NLPS: nullfix(): unable to PEEK, errno is %d", errno);
		DEBUG((9, "nullfix(): I_PEEK failed, errno is %d", errno));
		logmessage(scratch);
	}
	else if (ret == 0) {
		DEBUG((9, "nullfix(): no messages on stream to PEEK"));
	}
	else {
		if (peekp->databuf.len == 2) {
			/* Note: junk contains "peeked" data */
			DEBUG((9, "peeked <%x> <%x>", junk[0], junk[1]));
			if ((junk[0] == 0) && (junk[1] == 0)) {
				/* pitch the nulls */
				DEBUG((9, "pitching 2 nulls from first peek"));
				l_rcv(junk, 2, &flags);
			}
		}

		/*
		 * this represents a somewhat pathological case where
		 * the "2 nulls" are broken across message boundaries.
		 * Pitch the first and hope the next one is there
		 */

		else if (peekp->databuf.len == 1) {
			DEBUG((9, "peeked <%x>", junk[0]));
			if (junk[0] == 0) {
				/* pitch the first */
				DEBUG((9, "split nulls, pitching first"));
				l_rcv(junk, 1, &flags);
				peekp->databuf.maxlen = 1;
				ret = ioctl(0, I_PEEK, &peek);
				if (ret == -1) {
					sprintf(scratch, "NLPS: nullfix(): unable to PEEK second time, errno is %d", errno);
					DEBUG((9, "second peek failed, errno %d", errno));
					logmessage(scratch);
				}
				else if (ret == 0) {
					DEBUG((9, "no messages for 2nd peek"));
				}
				else {
					if (peekp->databuf.len == 1) {
						DEBUG((9, "2nd peek <%x>", junk[0]));
						if (junk[0] == 0) {
							/* pitch the second */
							DEBUG((9, "pitching 2nd single null"));
							l_rcv(junk, 1, &flags);
						}
						else {
							/* uh oh, server will most likely fail */
							DEBUG((9, "2nd null not found"));
							logmessage("NLPS: nullfix(): threw away a valid null byte");
						}
					}
				}
			}
		}
	}
}


/*
 * timeout:	SIGALRM signal handler.  Invoked if t_rcv timed out.
 *		See comments about 'exit' in nlps_server().
 */


void
timeout()
{
	DEBUG((9, "TIMEOUT"));
	error(E_RCV_TMO, EXIT | NOCORE);
}



/*
 * nls_service:	Validate and start a server requested via the NLPS protocol
 *
 *		version 0:1 -- expect "NLPS:000:001:service_code".
 *
 *	returns only if there was an error (either msg format, or couldn't exec)
 */

static char *badversion =
	"NLPS: Unknown version of an NLPS service request: %d:%d";
static char *disabledmsg =
	"NLPS: Request for service code <%s> denied, service is disabled";
static char *nlsunknown =
	"NLPS: Request for service code <%s> denied, unknown service code";


/*
 * Nlsversion can be used as a NLPS flag (< 0 == not nls service)
 * and when >= 0, indicates the version of the NLPS protocol used
 */

static int Nlsversion = -1;	/* protocol version	*/

int
nls_service(bp, size)
int  size;
char *bp;
{
	int low, high;
	char svc_buf[64];
	register char *svc_code_p = svc_buf;
	char scratch[256];
	register dbf_t *dbp;
	dbf_t *getdbfentry();
	extern char **mkdbfargv();
	int passfd;
	int i;

	if (nls_chkmsg(bp, size, &low, &high, svc_code_p))  {
		if ((low == 0) || (low == 2))
			Nlsversion = low;
		else  {
			sprintf(scratch, badversion, low, high);
			logmessage(scratch);
			error(E_BAD_VERSION, CONTINUE);
			return(-1);
		}

		DEBUG((9,"nls_service: protocol version %d", Nlsversion));

		/*
		 * common code for protocol version 0 or 2
		 * version 0 allows no answerback message
		 * version 2 allows exactly 1 answerback message
		 */

		if (dbp = getdbfentry(svc_code_p)) {
			if (dbp->dbf_flags & DBF_OFF)  {
				sprintf(scratch, disabledmsg, svc_code_p);
				logmessage(scratch);
				nls_reply(NLSDISABLED, scratch);
			}  
			else {
				if (dbp->dbf_sflags & CFLAG) {
					exec_cmd(dbp, (char **)0);
					/* return is an error	*/
				}
				else {
					sprintf(msgbuf,"NLPS (%s) passfd: %s", 
						dbp->dbf_svc_code, 
						dbp->dbf_cmd_line);
					nls_reply(NLSSTART, msgbuf);
					logmessage(msgbuf);
					/* open pipe to pass fd through */
					if ((passfd = open(dbp->dbf_cmd_line, 
							O_WRONLY)) < 0) {
						sprintf(scratch,"NLPS open failed: %s", dbp->dbf_cmd_line);
						logmessage(scratch);
					}
					DEBUG((9, "pushmod string: %s", dbp->dbf_modules));
					if (pushmod(0, dbp->dbf_modules)) {
						logmessage("NLPS: Can't push server's modules: exit");
						(void)exit(2); /* server, don't log */
					}

					DEBUG((9, "Running doconfig on %s", dbp->dbf_svc_code));
					if ((i = doconfig(0, dbp->dbf_svc_code, NOASSIGN)) != 0) {
						DEBUG((9, "doconfig exited with code %d", i));
						sprintf(scratch, "doconfig failed on line %d of script %s", 
								i, dbp->dbf_svc_code);
						logmessage(scratch);
						(void)exit(2);
					}
					if (ioctl(passfd, I_SENDFD, 0) < 0) {
						sprintf(scratch,"NLPS passfd failed: %s", dbp->dbf_cmd_line);
						logmessage(scratch);
					}
				}
			}
		}
		else  {
			sprintf(scratch, nlsunknown, svc_code_p);
			logmessage(scratch);
			nls_reply(NLSUNKNOWN, scratch);
		}
		exit(2);

	}  else
		error(E_BAD_FORMAT, CONTINUE);

	/* if we're still here, server didn't get exec'ed	*/

	return(-1);
}



/*
 * nls_chkmsg:	validate message and return fields to caller.
 *		returns: TRUE == good format
 *			 FALSE== bad format
 */

nls_chkmsg(bp, size, lowp, highp, svc_code_p)
char *bp, *svc_code_p;
int size, *lowp, *highp;
{

	/* first, make sure bp is null terminated */

	if ((*(bp + size - 1)) != (char)0)
		return(0);

	/* scanf returns number of "matched and assigned items"	*/

	return(sscanf(bp, "%*4c:%3d:%3d:%s", lowp, highp, svc_code_p) == 3);

}


/*
 * nls_reply:	send the "service request response message"
 *		when appropriate.  (Valid if running version 2 or greater).
 *		Must use write(2) since unknown modules may be pushed.
 *
 *		Message format:
 *		protocol_verion_# : message_code_# : message_text
 */

static char *srrpprot = "%d:%d:%s";

nls_reply(code, text)
register code;
register char *text;
{
	char scratch[256];

	/* Nlsversion = -1 for login service */

	if (Nlsversion >= 2)  {
		DEBUG((7, "nls_reply: sending response message"));
		sprintf(scratch, srrpprot, Nlsversion, code, text);
		t_snd(0, scratch, strlen(scratch)+1, 0);
	}
}


/*
 * common code to  start a server process (for any service)
 * if optional argv is given, info comes from o_argv, else pointer
 * to dbf struct is used.  In either case, first argument in argv is
 * full pathname of server. Before exec-ing the server, the caller's
 * logical address, opt and udata are added to the environment. 
 */

static char homeenv[BUFSIZ];
#define NETFD	0


exec_cmd(dbp, o_argv)
register dbf_t *dbp;
register char **o_argv;
{
	char *path;
	char **argvp;
	extern char **environ;
	dbf_t *getdbfentry();
	extern char **mkdbfargv();
	register struct passwd *pwdp;
	struct group *grpp;
	register dbf_t *wdbp = dbp;
	int	i;

	/*
	 * o_argv is set during SMB service setup only, in
	 * which case dbp is NULL.
	 */

	if (o_argv) {
		argvp = o_argv;
		if ((wdbp = getdbfentry(DBF_SMB_CODE)) == NULL) {
			/* this shouldn't happen because at this point we've
			   already found it once */
			logmessage("NLPS: SMB message, missing data base entry");
			(void)exit(2); /* server, don't log */
		}
	}
	else
		argvp = mkdbfargv(dbp);
	path = *argvp;

	sprintf(msgbuf,"NLPS (%s) exec: %s", 
			(dbp)?dbp->dbf_svc_code:DBF_SMB_CODE, path);
	nls_reply(NLSSTART, msgbuf);
	logmessage(msgbuf);

	if (wdbp->dbf_flags & DBF_UTMP) {
		pid_t	tmp;
		struct	stat	sbuf;
		char	*prefix;
		char	device[20];
		struct	utmp utline;

		/* 
		 * create a utmp entry.  extra fork makes parent init,
		 * which will clean up the entry.
		 */

		DEBUG((9, "Creating a utmp entry for this service "));
		if ((tmp = fork()) < 0) {
			logmessage("NLPS: Can't fork to create utmp entry");
			(void)exit(2);
		}
		if (tmp)
			(void)exit(0);	/* kill parent */

		/* 
		 * child continues processing, creating utmp and exec'ing
		 * the service
		 */

		setpgrp();
		if (fstat(0, &sbuf) < 0) {
			logmessage("NLPS: Stat failed on fd 0: no line field available for utmp entry");
			*device = '\0';
		}
		else {
			/* 
			 * MPREFIX is added to the environment by the parent
			 * listener process.
			 */
			prefix = getenv("MPREFIX");
			if (minor(sbuf.st_rdev) < 100)
				sprintf(device, "%.9s%02.02d", prefix, minor(sbuf.st_rdev));
			else
				sprintf(device, "%.8s%03.03d", prefix, minor(sbuf.st_rdev));
			DEBUG((9, "Device: %s", device));
		}
		strncpy(utline.ut_user, wdbp->dbf_id, 8);
		sprintf(utline.ut_id, "ps%c%c", SC_WILDC, SC_WILDC);
		strncpy(utline.ut_line, device, 12);
		utline.ut_pid = (o_pid_t)getpid();
                utline.ut_type = USER_PROCESS;
		utline.ut_exit.e_termination = 0;
		utline.ut_exit.e_exit = 0;
		utline.ut_time = (time_t) time((long *)0);
		makeut(&utline);
	}

	/* after pushmod, tli calls are questionable?		*/

	DEBUG((9, "pushmod string: %s", wdbp->dbf_modules));
	if (dbp && pushmod(NETFD, dbp->dbf_modules)) {
		logmessage("NLPS: Can't push server's modules: exit");
		exit(2); /* server, don't log */
	}

	DEBUG((9, "Running doconfig on %s", wdbp->dbf_svc_code));
	if ((i = doconfig(NETFD, wdbp->dbf_svc_code, 0)) != 0) {
		DEBUG((9, "doconfig exited with code %d", i));
		sprintf(msgbuf, "doconfig failed on line %d of script %s", 
				i, wdbp->dbf_svc_code);
		logmessage(msgbuf);
	}

	if (wdbp == NULL) {
		logmessage("NLPS: No database entry");
		exit(2); /* server, don't log */
	}
	
	if ((pwdp = getpwnam(wdbp->dbf_id)) == NULL)  {
		sprintf(msgbuf, "NLPS: Missing or bad passwd entry for <%s>",wdbp->dbf_id);
		logmessage(msgbuf);
		exit(2); /* server, don't log */
	}		


	if (setgid(pwdp->pw_gid)) {
		if ((grpp = getgrgid(pwdp->pw_gid)) == NULL) {
			sprintf(msgbuf, "NLPS: No group entry for %ld", pwdp->pw_gid);
			logmessage(msgbuf);
			exit(2); /* server, don't log */
		}
		sprintf(msgbuf, "NLPS: Cannot set group id to %s", grpp->gr_name);
		logmessage(msgbuf);
		(void)exit(2); /* server, don't log */
	}

	if (setuid(pwdp->pw_uid)) {
		sprintf(msgbuf, "NLPS: Cannot set user id to %s", wdbp->dbf_id);
		logmessage(msgbuf);
		(void)exit(2); /* server, don't log */
	}

	if (chdir(pwdp->pw_dir)) {
		sprintf(msgbuf, "NLPS: Cannot chdir to %s", pwdp->pw_dir);
		logmessage(msgbuf);
		(void)exit(2); /* server, don't log */
	}

	DEBUG((9, "New uid %ld New gid %ld", getuid(), getgid()));

	sprintf(homeenv, "HOME=%s", pwdp->pw_dir);
	DEBUG((9,"HOME=%s", pwdp->pw_dir));
	putenv(homeenv);
	endpwent();

	fclose(Logfp);
#ifdef DEBUGMODE
	fclose(Debugfp);
#endif
	execve(path, argvp, environ);

	/* exec returns only on failure!		*/

	logmessage("NLPS server: could not exec service");
	sys_error(E_SYS_ERROR, CONTINUE);
	return(-1);
}





/*
 * isdigits:	string version of isdigit.  (See ctype(3))
 */

/* This routine is public here and used in lsdbf.c as an external */
int
isdigits(p)
register char *p;
{
	register int flag = 1;

	if (!strlen(p))
		return(0);

	while (*p)
		if (!isdigit(*p++))
			flag = 0;
	return(flag);
}


int
l_rcv(bufp, bytes, flagp)
char *bufp;
int bytes;
int *flagp;
{
	register int n;
	register int count = bytes;
	register char *bp = bufp;

	DEBUG((9, "in l_rcv"));

	do {
		*flagp = 0;
		n = t_rcv(0, bp, count, flagp);
		DEBUG((9, "l_rcv, after t_rcv call, n =  %d",n));   

		if (n < 0) {
			DEBUG((9, "l_rcv, t_errno is %d", t_errno));   
#ifdef DEBUGMODE
			if (t_errno == TLOOK) {
				DEBUG((9, "l_rcv, t_look returns %d", t_look(0)));
			}
#endif
			return(n);
		}
		count -= n;
		bp += n;
	} while (count > 0);

	return(bp - bufp);
}


/*
 * getdbfentry:	Given a service code, return a pointer to the dbf_t
 *		entry.  Return NULL if the entry doesn't exist.
 *		Reads the data base, one line at a time, into
 *		Dbf_line_buf.
 */

static	char	Dbf_line_buf[DBFLINESZ];
static	dbf_t	Dbf_entry;

dbf_t *
getdbfentry(svc_code_p)
register char *svc_code_p;
{
	FILE	*dbfp;
	char	dbfname[BUFSIZ];

	sprintf(dbfname, "%s/%s", Basedir, DBFNAME);
	if ((dbfp = fopen(dbfname, "r")) == NULL) {
		DEBUG((9, "open of database file %s failed", DBFNAME));
		logmessage("NLPS: Unable to open database file");
		return((dbf_t *)NULL);
	}

	DEBUG((9, "database file opened, looking for %s", svc_code_p));
	while (rd_dbf_line(dbfp, Dbf_line_buf, &Dbf_entry.dbf_svc_code, 
		&Dbf_entry.dbf_flags, &Dbf_entry.dbf_id, &Dbf_entry.dbf_res1,
		&Dbf_entry.dbf_res2, &Dbf_entry.dbf_res3,&Dbf_entry.dbf_prv_adr,
		&Dbf_entry.dbf_prognum, &Dbf_entry.dbf_version, 
		&Dbf_entry.dbf_modules, &Dbf_entry.dbf_sflags, 
		&Dbf_entry.dbf_cmd_line) > 0) {

		/* see if this line is the one we want (svc_code match) */
		if (!strcmp(Dbf_entry.dbf_svc_code, svc_code_p)) {
			fclose(dbfp);
			return(&Dbf_entry);
		}
	}

	DEBUG((9, "No svc code match"));
	fclose(dbfp);
	return((dbf_t *)0);	/* svc code not in database	*/
}
