root/usr/src/cmd/listen/lsdbf.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   */


/*
 * data base routines for the network listener process
 */

/* system include files */

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/tiuser.h>
#include <sys/stropts.h>

/* listener include files */

#include "lsparam.h"            /* listener parameters          */
#include "listen.h"             /* listener includes            */
#include "lsfiles.h"            /* listener files info          */
#include "lserror.h"            /* listener error codes         */
#include "lsdbf.h"              /* data base file stuff         */
/* #include "nsaddr.h"          nls includes                    */

#define SUPPRESS        1       /* suppress messages during scan*/
#define NOSUPPRESS      0       /* don't suppress messages      */

/* static data          */

static char *dbfopenmsg = "Trouble opening data base file";
static char *dbfrderror = "Error reading data base file: line %d";
static char *dbfbadlmsg = "Data base file: Error on line %d";
static char *dbfdupcmsg = "Data base file: Duplicate service code: <%s>";
static char *dbfunknown = "Unknown error reading data base file: line %d";
static char *dbfsvccmsg = "Data base file: Illegal service code: <%s>";
static char *dbfcorrupt = "Data base file has been corrupted";

static int   Dbflineno;         /* current line number in dbf           */
static unsigned Dbfentries;     /* number of non-comment lines          */
extern char  *Server_cmd_lines; /* contains svc_code, cmd_line, mod_list */
extern char  *New_cmd_lines; /* svc_code, cmd_line, mod_list (on reread)*/

/* public variables */


/*
 * read_dbf:
 *
 * read the data base file into internal structures
 *
 * all data base routines under read_dbf log there own errors and return -1
 * in case of an error.
 *
 * if 're_read' is non-zero, this stuff is being called to read a new
 * data base file after the listener's initialization.
 */

int
read_dbf(re_read)
int     re_read;        /* zero means first time        */
{
        register unsigned size;
        int exit_flag = EXIT | NOCORE;
        register dbf_t *dbf_p;
        register char  *cmd_p;
        unsigned scan_dbf();
        extern dbf_t *Dbfhead;          /* Dbfentries (when allocated)  */
        extern dbf_t *Newdbf;           /* Dbfentries (on re-read)      */
        extern char *calloc();

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

        if (check_version())
                error(E_BADVER, EXIT | NOCORE);

        if (re_read)    {                       /* not first time */
                exit_flag = CONTINUE;
        }

        /*
         * note: data base routines log their own error messages
         */

        Dbfentries = 0;
        DEBUG((9,"read_dbf: open file here:  %s", DBFNAME));
        if ( (size = scan_dbf(DBFNAME)) == (unsigned)(-1) )
                error( E_SCAN_DBF, exit_flag | NO_MSG );

        DEBUG((5,"read_dbf: scan complete: non-commented lines: %u, size: %u",
                Dbfentries, size));

        if (!size)  {
                logmessage("No database?  0 entries?");
                return(0);
        }

        /*
         * allocate enough space for Dbfentries of 'size' bytes (total)
         * The +1 is to insure a NULL last entry!
         */

        if (!(dbf_p = (dbf_t *)calloc(Dbfentries+1,sizeof(dbf_t)))
           || !(cmd_p = calloc(size, 1)))  {
                DEBUG((1,"cannot calloc %u + %u bytes", size,
                        (Dbfentries+1)*(unsigned)sizeof(dbf_t)));
                error( E_DBF_ALLOC, exit_flag); /* if init, exit */

                /* if still here, this is a re-read     */

                if (dbf_p)
                        free(dbf_p);
                if (cmd_p)
                        free(cmd_p);
                return(-1);
        }

        if (get_dbf(dbf_p, cmd_p))  {
                DEBUG((9, "get_dbf FAILED"));
                error(E_DBF_IO, exit_flag | NO_MSG);

                /* if still here, this is a re_read */
                free(dbf_p);
                free(cmd_p);
                return(-1);
        }

        if (re_read)  {
                Newdbf = dbf_p;
                New_cmd_lines = cmd_p;
#ifdef  DEBUGMODE
                DEBUG((7,"read_dbf: NEW data base dump..."));
                if (Newdbf)
                        for (dbf_p = Newdbf; dbf_p->dbf_svc_code; ++dbf_p)
                                DEBUG((7, "svc code <%s>; id: %s; private address: %s; modules: %s; cmd line: %s; sflags: %x, prognum: %d version: %d",
                                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));
#endif  /* DEBUGMODE */
        }
        else {
                Dbfhead = dbf_p;
                Server_cmd_lines = cmd_p;
#ifdef  DEBUGMODE
                DEBUG((7,"read_dbf: data base dump..."));
                if (Dbfhead)
                        for (dbf_p = Dbfhead; dbf_p->dbf_svc_code; ++dbf_p)
                                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",
                                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));
#endif  /* DEBUGMODE */
        }

        return(0);
}


/*
 * get_dbf: read the file and fill the structures
 *          checking for duplicate entries as we go
 */

int
get_dbf(dbf_p, cmd_p)
register dbf_t *dbf_p;
register char *cmd_p;
{
        dbf_t *dbfhead = dbf_p;
        register int n, i;
        char buf[DBFLINESZ];
        register char *p = buf;
        char scratch[128];
        FILE *dbfilep;
        char *cmd_line_p;
        int flags;
        char *svc_code_p;
        char *id_p;
        char *res1_p;
        char *res2_p;
        char *res3_p;
        char *private_p;
        int sflags;
        int prognum;
        int vernum;
        char *module_p;
        register dbf_t *tdbf_p;
        extern int atoi();
        extern int Dbf_entries;
        extern int NLPS_proc;
        extern int errno;

        Dbflineno = 0;
        Dbf_entries = 0;  /* number of private addresses in dbf file */

        DEBUG((9,"in get_dbf: "));
        if (!(dbfilep = fopen(DBFNAME,"r")))  {
                logmessage(dbfopenmsg);
                error(E_DBF_IO, EXIT | NOCORE | NO_MSG);
        }

        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))  {

                if (n == -1)  {                 /* read error   */
                        fclose(dbfilep);
                        return(-1);
                }

                /* make sure service code is legal                      */

                i = strlen(svc_code_p);
                if ( (i == 0) || (i >= SVC_CODE_SZ) )
                        goto reject;

                /* check for duplicate service code                     */
                tdbf_p = dbfhead;
                while (tdbf_p->dbf_svc_code)  { /* duplicate svc code?  */
                        if (!strcmp(svc_code_p, tdbf_p->dbf_svc_code))  {
                                sprintf(scratch, dbfdupcmsg, svc_code_p);
                                logmessage(scratch);
                                return(-1);
                        }
                        ++tdbf_p;
                }

                /* NLPS_proc is set by the nlps_server, which also uses these
                 * routines.  The actual listener child shouldn't ever need
                 * to read a database, so it will never be here
                 */
                if (!NLPS_proc && (strlen(private_p) == 0) && !(sflags & DFLAG))
                        continue;       /* ignore entries with no address */

                /*
                 * child doesn't care about private addresses
                 */

                if (!NLPS_proc) {
                        i = strlen(private_p);
                        if (i >= PRV_ADR_SZ) {
                                goto p_reject;
                        }
                        Dbf_entries++;
                }

                /*
                 * legal, non-duplicate entry: copy it into internal data base
                 */

                dbf_p->dbf_fd = -1;     /* set to actual fd in add_prvaddr */
                dbf_p->dbf_flags = flags;
                dbf_p->dbf_sflags = sflags;
                dbf_p->dbf_prognum = prognum;
                dbf_p->dbf_version = vernum;
                strcpy(cmd_p, svc_code_p);
                dbf_p->dbf_svc_code = cmd_p;
                cmd_p += strlen(svc_code_p) + 1;        /* +1 for null */
                strcpy(cmd_p, cmd_line_p);
                dbf_p->dbf_cmd_line = cmd_p;
                cmd_p += strlen(cmd_line_p) + 1;
                strcpy(cmd_p, id_p);
                dbf_p->dbf_id = cmd_p;
                cmd_p += strlen(id_p) + 1;
                strcpy(cmd_p, res1_p);
                dbf_p->dbf_res1 = cmd_p;
                cmd_p += strlen(res1_p) + 1;
                strcpy(cmd_p, res2_p);
                dbf_p->dbf_res2 = cmd_p;
                cmd_p += strlen(res2_p) + 1;
                strcpy(cmd_p, res3_p);
                dbf_p->dbf_res3 = cmd_p;
                cmd_p += strlen(res3_p) + 1;
                if (strlen(private_p) != 0) {
                        strcpy(cmd_p, private_p);
                        dbf_p->dbf_prv_adr = cmd_p;
                        cmd_p += strlen(private_p) + 1;
                }
                else
                        dbf_p->dbf_prv_adr = NULL;
                strcpy(cmd_p, module_p);
                dbf_p->dbf_modules = cmd_p;
                cmd_p += strlen(module_p) + 1;  /* cmd line + null char */
                ++dbf_p;
        }

        fclose(dbfilep);
        return(0);

reject:
        DEBUG((9, "svc_code <%s> failed validation test", svc_code_p));
        sprintf(scratch, dbfsvccmsg, svc_code_p);
        logmessage(scratch);
        return(-1);
p_reject:
        DEBUG((9,"private address <%s> failed validation test", private_p));
        sprintf(scratch, "Invalid private address ignored: \\x%x", private_p);
        logmessage(scratch);
        return(-1);
}


/*
 * scan_dbf:    Take a quick pass through the data base file to figure out
 *              approx. how many items in the file we'll need to
 *              allocate storage for.  Essentially, returns the number
 *              of non-null, non-comment lines in the data base file.
 *
 *              return: -1 == error.
 *                      other == number of non-comment characters.
 *                      Dbfentries set.
 */

unsigned
scan_dbf(path)
register char *path;
{
        register unsigned int size = 0;
        register int n;
        register FILE *dbfilep;
        char buf[DBFLINESZ];
        register char *p = buf;
        char *svc_code_p;
        int flags;
        char *cmd_line_p;
        char *module_p;
        char *id_p;
        char *res1_p;
        char *res2_p;
        char *res3_p;
        int sflags;
        int prognum;
        int vernum;
        char *private_p;
        extern int errno;

        DEBUG((9, "In scan_dbf.  Scanning data base file %s.", path));

        if (!(dbfilep = fopen(path,"r")))  {
                DEBUG((9,"errorno = %d", errno));
                logmessage(dbfopenmsg);
                return(-1);
        }

        do {
                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);
                if (n == -1)  {
                        fclose(dbfilep);
                        return(-1);
                }
                size += (unsigned)n;
                if (n)
                        ++Dbfentries;
        } while (n);

        fclose(dbfilep);
        return(size);
}


/*
 * rd_dbf_line: Returns the next non-comment line into the
 *              given buffer (up to DBFLINESZ bytes).
 *              Skips 'ignore' lines.
 *
 *              Returns:        0 = done, -1 = error,
 *                              other = cmd line size incl. terminating null.
 *                              *svc_code_p = service code;
 *                              *id_p = user id string;
 *                              *res1_p = reserved string;
 *                              *res2_p = reserved string;
 *                              *res3_p = reserved string;
 *                              *private_p = contains private address;
 *                              *flags_p = decoded flags;
 *                              prognum = RPC program #;
 *                              vernum = RPC version $;
 *                              cnd_line_p points to null terminated cmd line;
 *
 * When logging errors, the extern Dbflineno is used.
 */

int
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)
register FILE *fp;
register char *bp;
char **svc_code_p;
int *flags_p;
char **id_p;
char **res1_p;
char **res2_p;
char **res3_p;
char **private_p;
int *prognum;
int *vernum;
char **module_p;
int *sflags;
char **cmd_line_p;
int mflag;
{
        register int length;
        register char *p;

        do {
                ++Dbflineno;
                length = 0;

                if (!fgets(bp, DBFLINESZ, fp))  {
                        if (feof(fp))  {
                                return(0);      /* EOF  */
                        }
                        if (ferror(fp))  {
                                sprintf(bp,dbfrderror,Dbflineno);
                                logmessage(bp);
                                return(-1);
                        }
                        sprintf(bp,dbfunknown,Dbflineno);
                        logmessage(bp);
                        return(-1);             /* Unknown error (?)    */
                }

                if (*(bp+strlen(bp)-1) != '\n')  {  /* terminating newline? */
                        sprintf(bp, dbfbadlmsg, Dbflineno);
                        logmessage(bp);
                        return(-1);
                }

                *(bp + strlen(bp) -1) = (char)0; /* delete newline      */

                if (strlen(bp) && (p = strchr(bp, DBFCOMMENT)))
                        *p = (char)0;           /* delete comments      */
                if (!strlen(bp))
                        continue;

                p = bp + strlen(bp) - 1;        /* bp->start; p->end    */
                while ((p != bp) && (isspace(*p)))  {
                        *p = (char)0;           /* delete terminating spaces */
                        --p;
                }

                while (*bp)                     /* del beginning white space*/
                        if (isspace(*bp))
                                ++bp;
                        else
                                break;

                if (strlen(bp)) {               /* anything left?       */

                   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))) {

                        DEBUG((1, "rd_dbf_line line %d, error while scanning entry",
                          Dbflineno));
                        sprintf(bp, dbfbadlmsg, Dbflineno);
                        logmessage(bp);
                        return(-1);
                    }
                }

        }  while (!length);             /* until we have something */

        DEBUG((5,"rd_dbf_line: line: %d,cmd line len: %d",Dbflineno, length+1));

        return(length+1); /* +1 for the trailing NULL */

}


/*
 * scan a non-white space line
 *              0 = error;
 *              other = length of cmd line;
 *
 *      non-null lines have the following format:
 *
 *      service_code: flags: id: res1: res2: res3: private address: rpcdata: sflags: modules: cmd_line # comments
 *
 * mflag is set to suppress messages (scan_line is called both for parsing
 * and counting, messages should only be output once)
 */

int
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)
register char *bp;
char **svc_code_p;
register int *flags_p;
char **id_p;
char **res1_p;
char **res2_p;
char **res3_p;
char **private_p;
int *prognum;
int *vernum;
char **module_p;
int *sflags;
register char **cmd_line_p;
int mflag;
{
        register char *p;
        register char *nexttok;
        register char *ptr;
        int sawsep = 0;
        char scratch[BUFSIZ];

        *flags_p = 0;

        if (!(p = strchr(bp, DBFTOKSEP ))) {    /* look for service code string */
                DEBUG((9,"scan_line failed svc_code strchr"));
                return(0);
        }
        *p = '\0';
        *svc_code_p = bp;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP )))  {
                DEBUG((9,"scan_line failed flags strchr"));
                return(0);
        }
        *p = '\0';

        while (*nexttok)  {
                switch (*nexttok)  {
                case 'x':               /* service is turned off        */
                case 'X':
                        *flags_p |= DBF_OFF;
                        break;
                case 'u':               /* create utmp entry            */
                        *flags_p |= DBF_UTMP;
                        break;
                default:
                        DEBUG((1,"scan_line: unknown flag char: 0x%x",*nexttok));
                        *flags_p = DBF_UNKNOWN;
                        break;
                }
                ++nexttok;
        }
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed id strchr"));
                return(0);
        }
        *p = '\0';
        *id_p = nexttok;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed res1 strchr"));
                return(0);
        }
        *p = '\0';
        *res1_p = nexttok;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed res2 strchr"));
                return(0);
        }
        *p = '\0';
        *res2_p = nexttok;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed res3 strchr"));
                return(0);
        }
        *p = '\0';
        *res3_p = nexttok;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed private strchr"));
                return(0);
        }
        *p = '\0';
        *private_p = nexttok;
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed rpc strchr"));
                return(0);
        }
        *p = '\0';

        *prognum = -1;
        *vernum = -1;
        if (*nexttok) {
                /* there is rpc info */
                for (ptr = nexttok; *ptr; ++ptr) {
                        if ((*ptr == ',') && !sawsep) {
                                /*
                                 * skip separator - note that if
                                 * separator has been seen, it's not
                                 * a digit so it will fail below
                                 */
                                sawsep++;
                                continue;
                        }
                        if (!isdigit(*ptr)) {
                                sprintf(scratch, "service code <%s> specifies non-integer rpc info", *svc_code_p);
                                logmessage(scratch);
                                return(0);
                        }
                }
                ptr = strchr(nexttok, ',');
                if (ptr) {
                        if ((*prognum = atoi(nexttok)) < 0) {
                                if (!mflag) {
                                        /* messages aren't suppressed */
                                        sprintf(scratch, "service code <%s> specifies negative program number", *svc_code_p);
                                        logmessage(scratch);
                                }
                                return(0);
                        }
                        if ((*vernum = atoi(ptr + 1)) < 0) {
                                if (!mflag) {
                                        sprintf(scratch, "service code <%s> specifies negative version number", *svc_code_p);
                                        logmessage(scratch);
                                }
                                return(0);
                        }
                }
                else {
                        if (!mflag) {
                                sprintf(scratch, "service code <%s> - invalid rpcinfo", *svc_code_p);
                                logmessage(scratch);
                        }
                        return(0);
                }
        }
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed sflags strchr"));
                return(0);
        }
        *p = '\0';

        *sflags = 0;
        while (*nexttok)  {
                switch (*nexttok)  {
                case 'c':       /* dbf_cmd_line is a command */
                        *sflags |= CFLAG;
                        break;
                case 'd':       /* dynamic address */
                        if ((int) strlen(*private_p) > 0) {
                                if (!mflag) {
                                        sprintf(scratch, "service code <%s> specifies static and dynamic address", *svc_code_p);
                                        logmessage(scratch);
                                        logmessage("  address info ignored");
                                }
                                /* don't set DFLAG and wipe out private addr */
                                **private_p = '\0';
                        }
                        else {
                                *sflags |= DFLAG;
                        }
                        break;
                case 'p':       /* dbf_cmd_line is a pipe */
                        *sflags |= PFLAG;
                        break;
                default:
                        if (!mflag) {
                                sprintf(scratch, "service code <%s> unknown flag <%c> ignored", *svc_code_p, *nexttok);
                                logmessage(scratch);
                        }
                        break;
                }
                ++nexttok;
        }
        nexttok = ++p;

        if (!(p = strchr(nexttok, DBFTOKSEP ))) {
                DEBUG((9,"scan_line failed module strchr"));
                return(0);
        }
        *p = '\0';
        *module_p = nexttok;
        nexttok = ++p;

        *cmd_line_p = nexttok;

        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));
        /*
         * return the length of the buffer.  Add 9 for the NULLs after each
         * string
         */
        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);
}



#define VERSIONSTR      "# VERSION="

int
check_version(void)
{
        FILE *fp;
        char *line, *p, *tmp;
        int version;

        if ((fp = fopen(DBFNAME, "r")) == NULL) {
                logmessage(dbfopenmsg);
                error(E_DBF_IO, EXIT | NOCORE | NO_MSG);
        }
        if ((line = (char *) malloc(DBFLINESZ)) == NULL)
                error(E_DBF_ALLOC, EXIT | NOCORE);
        p = line;
        while (fgets(p, DBFLINESZ, fp)) {
                if (!strncmp(p, VERSIONSTR, strlen(VERSIONSTR))) {
                        /* pitch the newline */
                        tmp = strchr(p, '\n');
                        if (tmp)
                                *tmp = '\0';
                        else {
                                logmessage(dbfcorrupt);
                                error(E_DBF_CORRUPT, EXIT | NOCORE);
                        }
                        p += strlen(VERSIONSTR);
                        if (*p)
                                version = atoi(p);
                        else {
                                logmessage(dbfcorrupt);
                                error(E_DBF_CORRUPT, EXIT | NOCORE);
                        }
                        free(line);
                        fclose(fp);
                        if (version != VERSION)
                                return(1);      /* wrong version */
                        else
                                return(0);      /* version ok */
                }
                p = line;
        }
        logmessage(dbfcorrupt);
        error(E_DBF_CORRUPT, EXIT | NOCORE);
        return(1);
}


/*
 * mkdbfargv:   Given a pointer to a dbf_t, construct argv
 *              for an exec system call.
 *              Warning: returns a pointer to static data which are
 *                       overritten by each call.
 *
 *              There is a practical limit of 50 arguments (including argv[0])
 *
 *              Warning: calling mkdbfargv destroys the data (by writing null
 *              characters via strtok) pointed to by dbp->dbf_cmd_line.
 */

static char *dbfargv[50];
static char *delim = " \t'\"";          /* delimiters */

char **
mkdbfargv(dbp)
register dbf_t  *dbp;
{
        register char **argvp = dbfargv;
        register char *p = dbp->dbf_cmd_line;
        char delch;
        register char *savep;
        register char *tp;
        char scratch[BUFSIZ];
        char *strpbrk();
#ifdef  DEBUGMODE
        register int i = 0;
#endif

        *argvp = 0;
        savep = p;
        while (p && *p) {
                if (p = strpbrk(p, delim)) {
                        switch (*p) {
                        case ' ':
                        case '\t':
                                /* "normal" cases */
                                *p++ = '\0';
                                *argvp++ = savep;
                                DEBUG((9, "argv[%d] = %s", i++, savep));
                                /* zap trailing white space */
                                while (isspace(*p))
                                        p++;
                                savep = p;
                                break;
                        case '"':
                        case '\'':
                                /* found a string */
                                delch = *p; /* remember the delimiter */
                                savep = ++p;

/*
 * We work the string in place, embedded instances of the string delimiter,
 * i.e. \" must have the '\' removed.  Since we'd have to do a compare to
 * decide if a copy were needed, it's less work to just do the copy, even
 * though it is most likely unnecessary.
 */

                                tp = p;
                                for (;;) {
                                        if (*p == '\0') {
                                                sprintf(scratch, "invalid command line, non-terminated string for service code %s", dbp->dbf_svc_code);
                                                logmessage(scratch);
                                                exit(2); /* server, don't log */
                                        }
                                        if (*p == delch) {
                                                if (*(tp - 1) == '\\') { /* \delim */
                                                        *(tp - 1) = *p;
                                                        p++;
                                                }
                                                else { /* end of string */
                                                        *tp = 0;
                                                        *argvp++ = savep;
                                                        DEBUG((9, "argv[%d] = %s", i++, savep));
                                                        p++;
                                                        /* zap trailing white space */
                                                        while (isspace(*p))
                                                                p++;
                                                        savep = p;
                                                        break;
                                                }
                                        }
                                        else {
                                                *tp++ = *p++;
                                        }
                                }
                                break;
                        default:
                                logmessage("Internal error in parse routine");
                                exit(2); /* server, don't log */
                        }
                }
                else {
                        *argvp++ = savep;
                        DEBUG((9, "argv[%d] = %s", i++, savep));
                }
        }
        *argvp = 0;
        return(dbfargv);
}



/*
 *
 * getentry: Given a fd, this routine will return a
 *                  dbf entry.  If the entry doesn't exist it will
 *                  return NULL.
 */

dbf_t *
getentry(fd)
int     fd;
{
        extern dbf_t *Dbfhead;          /* Dbfentries (when allocated)  */
        register dbf_t  *dbp = Dbfhead;

        if (!Dbfhead) {         /* no private address in database file */
                DEBUG((9, "getdbfentry - nothing in Dbfhead = %s ",Dbfhead));
                return((dbf_t *)0);
        }
        else
                for (dbp = Dbfhead;  dbp->dbf_prv_adr;   ++dbp)
                        if (fd == dbp->dbf_fd) {
                                return(dbp);
                        }

        return((dbf_t *)0);     /* requested private address not in list */

}


/*
 * pushmod:     push modules if defined in the data base entry.
 *
 *              WARNING: This routine writes into the in-memory copy
 *              of the database file.  Therefore, after it is called,
 *              the incore copy of the database file will no longer be valid.
 */

int
pushmod(fd, mp)
int fd;
register char *mp;
{
        register char *cp;
        register char *bufp = mp;
        char name[32];
        extern int errno;

        DEBUG((9,"in pushmod:"));
        if (!mp || *mp == '\0') {
                DEBUG((9,"NULL list: exiting pushmod"));
                return(0);
        }
        /* pop timod if it is on the stack */
        if (ioctl(fd, I_LOOK, name) >= 0) {
                if (strcmp(name, "timod") == 0) {
                        if (ioctl(fd, I_POP, 0) < 0)
                                DEBUG((9,"pushmod: I_POP failed"));
                }
        }
        while ((cp = strtok(bufp, ",")) != NULL) {
                bufp = NULL;
                DEBUG((9,"pushmod: about to push %s", cp));
                if (ioctl(fd, I_PUSH, cp) < 0) {
                        DEBUG((9,"pushmod: ioctl failed, errno = %d",errno));
                        return(1);
                }
        }
        DEBUG((9,"exiting pushmod:"));
        return(0);
}