root/usr/src/cmd/listen/nlps_serv.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/* system include files */

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.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/stropts.h>
#include <utmpx.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();

static void nls_reply(int code, char *text);
static void nullfix(void);

int
main(int argc, char **argv)
{
        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:
 */

int
nlps_server()
{
        int size;
        char buf[RCVBUFSZ];
        char **argv;
        char *bp = buf;
        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)
char *bp;
{
        int size;
        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.
 */

static void
nullfix(void)
{
        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));

                                        sprintf(msgbuf,"%s/%s",Basedir,dbp->dbf_svc_code);

                                        if ((i = doconfig(0, msgbuf, NOASSIGN)) != 0) {
                                                DEBUG((9, "doconfig exited with code %d", i));
                                                sprintf(scratch, "doconfig failed on line %d of script %s",
                                                                i, msgbuf);
                                                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
 */

int
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";

static void
nls_reply(int code, 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


int
exec_cmd(dbf_t *dbp, char **o_argv)
{
        char *path;
        char **argvp;
        extern char **environ;
        dbf_t *getdbfentry();
        extern char **mkdbfargv();
        struct passwd *pwdp;
        struct group *grpp;
        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  utmpx utline;

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

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

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

                setpgrp();
                if (fstat(0, &sbuf) < 0) {
                        logmessage("NLPS: Stat failed on fd 0: no line "
                            "field available for utmpx 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,
                    sizeof (utline.ut_user) - 1);
                sprintf(utline.ut_id, "ps%c%c", SC_WILDC, SC_WILDC);
                strncpy(utline.ut_line, device, sizeof (utline.ut_line) - 1);
                utline.ut_pid = getpid();
                utline.ut_type = USER_PROCESS;
                utline.ut_exit.e_termination = 0;
                utline.ut_exit.e_exit = 0;
                utline.ut_xtime = (time_t) time((time_t *)0);
                makeutx(&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     */
}