#include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CH_IO:  Channel invocation & communication                          */

/*  Jun 80 Dave Crocker     fix ch_close fildes setting to -1
 *  Nov 80 Dave Crocker     convert to ch_struct pointer from ch_code
 *  Jul 81 Dave Crocker     ch_rdrec a little cleaner on error handling
 */

#define TIMEOUT 3600             /* 60 minute sanity timer on channel IO */

#include "ch.h"
#include <signal.h>
#include "nexec.h"

extern char pgm_bakgrnd;
extern LLog *logptr;
extern char *chndfldir;
extern int  *regfdary;           /* for nexec                         */
extern int  numfds;

extern char *blt();

LOCFUN ch_rrec(), ch_exec();

Chan    *ch_curchan;               /* currently active channel program   */
RP_Buf ch_rp;         /* last channel reply text            */

/* ***************  (ch_) COMMUNICATE WITH CHANNEL  ***************** */

LOCVAR FILE *ch_outfp,            /* output file handle for chan */
	   *ch_infp;              /* input file handle for channel */

LOCVAR int    ch_child;           /* Process id of channel program      */
LOCVAR sigtype (* ch_opipe) ();   /* old pipe action value              */

LOCFUN sigtype
ch_catch ()			  /* catch interrupt                    */
{
}

ch_end (type)                     /* get ready to send mail to channels */
    int type;
{                                 /* NOTOK => close; 0 => addr list end */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_end (%d)", type);
#endif

    return (ch_sbend (type));
}
/**/

ch_sbinit (newchan)               /* "get" a specific channel           */
register Chan  *newchan;          /* pointer to chan structure          */
{                                 /* NOTOK => close; 0 => addr list end */
    int temp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_open(%s)", newchan -> ch_name);
#endif

    if (newchan == ch_curchan)
	return (RP_OK);           /* already have the channel opened    */

    if (!pgm_bakgrnd && (newchan -> ch_access & DLVRBAK))
	return (RP_NOOP);         /* not allowed unless background      */

    if (ch_curchan != OK)         /* already have chan -> get rid of it */
	ch_end (OK);

    newchan -> ch_access |= DLVRDID; /* note that we did this channel      */

    if ((temp = ch_exec (newchan)) == RP_OK)
	ch_curchan = newchan;

    ch_opipe = signal (SIGPIPE, ch_catch);
    signal (SIGALRM, ch_catch);
    return (temp);
}
/**/

ch_sbend (type)               /* free the channel                   */
int type;                     /* clean ending / abort               */
{
    register int status;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "ch_end (%d)", type);
#endif

    if (ch_child == 0)            /* don't have one                     */
	return (OK);

    switch (type)
    {
	case NOTOK:               /* ABORTING;  try to kill child       */
	    ll_log (logptr, LLOGTMP, "Trying to kill child  '%s' pid = (%d)",
			ch_curchan -> ch_name, ch_child);
	    kill (ch_child, SIGKILL);
	    goto closeit;

	case OK:                  /* normal end; just eof the pipes     */
	    ch_wrec ("end");      /* say goodbye */

    closeit:
	    if (ch_outfp == (FILE *) EOF)
	        return(RP_OK);    /* ch_wrec might have already closed  */

	    fclose (ch_outfp);
	    fclose (ch_infp);

	    ch_outfp =            /* flag i/o ports as closed           */
		ch_infp = (FILE *) EOF;
	    signal (SIGPIPE, ch_opipe);
				  /* probably set it back to abort      */
	    break;

	default:
	    err_abrt (RP_NCMD, "Illegal closedest call (%d)", type);
    }

    status = pgmwait (ch_child);  /* pickup child's stats               */
    if (status == NOTOK)          /* abnormal proces termination        */
	ll_err (logptr, LLOGTMP, "(%s) child NOTOK", ch_curchan -> ch_name);

    if (rp_gbval (status) == RP_BNO)
	ll_log (logptr, LLOGTMP, "(%s) child (%s)",
		    rp_valstr (status), ch_curchan -> ch_name);
				  /* log major error returns only       */

    ch_curchan = OK;              /* mark current channel as "none"     */
    ch_child = 0;                 /* don't have one any more            */
    return (RP_OK);
}
/**/

ch_rrply (valstr, len)     /* read reply from channel            */
    RP_Buf *valstr;
    int *len;
{
    int     result;

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_rrply()");
#endif

    ch_rp.rp_val = RP_NOOP;
    (void) strcpy (ch_rp.rp_line, "(No reason given)");

    result = ch_rrec ((char *) valstr, len);

#ifdef RUNALON
    switch (valstr -> rp_val)
    {
	case 'y':
	    result = RP_OK;
	    break;
	case 'n':
	    result = RP_NO;
	    break;
	case 'a':
	    result = RP_AOK;
	    break;
	case 'd':
	    result = RP_DOK;
	    break;
	case 'm':
	    result = RP_MOK;
	    break;
	case 't':
	    result = RP_TIME;
	    break;
	case 'c':
	    result = RP_NET;
    }
#endif

    *len -= 1;
    if ((*len > 0) && (*len <= RP_LINEBUF_MAX)) {
	blt (valstr -> rp_line, ch_rp.rp_line, *len);
	ch_rp.rp_line[*len] = '\0';
    }
    else
	*len = 0;


#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "reply: (%s) '%s'",
	    rp_valstr (valstr -> rp_val), valstr -> rp_line);
#endif

    ch_rp.rp_val = valstr -> rp_val;

    if (rp_isbad (result))
	return (result);

    return (RP_OK);
}
/**/

ch_wrec (str)                     /* send info to channel               */
char    str[];                    /* string to send                     */
{
#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_wrec ()");
#endif
				  /* default error message              */
    if (ch_curchan == OK)         /* channel isn't really there         */
	return (RP_DHST);

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "sending:  '%s'", str);
#endif
#ifdef RUNALON
    printx ("Channel output:  '%s'\n", str);
#else
    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) output err to channel",
		ch_curchan -> ch_name);
	ch_sbend (NOTOK);         /* close it                           */
	return (RP_NET);          /* class it as a "network" error      */
    }
    s_alarm (TIMEOUT);
    if (str)                  /* send it to channel                 */
	fputs (str, ch_outfp);

    putc ('\0', ch_outfp);
    fflush (ch_outfp);	/* This causes real IO */
    s_alarm (0);

    if (ferror (ch_outfp))
    {
	ll_err (logptr, LLOGTMP, "(%s) output err to channel",
		ch_curchan -> ch_name);
	ch_sbend (NOTOK);         /* close it                           */
	return (RP_NET);          /* class it as a "network" error      */
    }
    return (RP_OK);
#endif
}
/**/

LOCFUN
	ch_rrec (rplyline, len)    /* read reply from channel            */
    char *rplyline;
    int *len;
{
    int     result;
    char *valstr;

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_rrec()");
#endif
#ifdef RUNALON
    printx ("Chrcv:  ");
#endif

    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) read err from channel",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }
    s_alarm (TIMEOUT);
    result = RP_OK;
    rplyline[0] = getc (ch_infp); /* one-char rp.h reply value          */
    s_alarm (0);

    *len = 1;
    if (feof (ch_infp))
    {
	ll_log (logptr, LLOGTMP, "(%s) premature channel eof",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }
    if (ferror (ch_infp))
    {
	ll_err (logptr, LLOGTMP, "(%s) read err from channel",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }

#ifdef RUNALON
    switch (rplyline[0])
    {
	case 'y':
	    replyline[0] = RP_OK;
	    break;
	case 'n':
	    replyline[0] = RP_NO;
	    break;
	case 'a':
	    replyline[0] = RP_AOK;
	    break;
	case 'd':
	    replyline[0] = RP_DOK;
	    break;
	case 'm':
	    replyline[0] = RP_MOK;
	    break;
	case 't':
	    replyline[0] = RP_TIME;
	    break;
	case 'c':
	    replyline[0] = RP_NET;
    }
#endif
    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) Channel died", ch_curchan -> ch_name);
	*len += 1;
	return (rplyline[0] = result = RP_NET);
    }
    s_alarm (TIMEOUT);
    if ((*len = gcread (ch_infp, &rplyline[1], LINESIZE - 2, "\n\000\377"))
			== 0)
    {
	ll_err (logptr, LLOGTMP, "(%s) Channel died", ch_curchan -> ch_name);
	rplyline[0] =
	    result = RP_NET;
    }
    s_alarm (0);

    valstr = rp_valstr (rplyline[0]);
    if (*valstr == '*')
    {
	ll_log (logptr, LLOGFAT, "(%s) (%s) '%s'",
		ch_curchan -> ch_name, valstr, &rplyline[1]);
	return (RP_RPLY);
    }

    *len += 1;
#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "record: (%s) '%s'", valstr, &rplyline[1]);
#endif
    return (result);
}
/**/

LOCFUN
	ch_exec (thechan)         /* invoke process which "is" channel  */
Chan   *thechan;      /* channel descriptor structure       */
{
    register  tmp;
    int     nampipe[2];           /* to xmit process for addr name        */
    int     respipe[2];           /* from xmit process, for responses     */
    int     oldpid;               /* parent pid */
    char    temppath[128];
    char    arg1[10];             /* fd for input (names/addrs)         */
    char    arg2[10];             /* fd for output (responses)          */
    char    arg3[20];             /* options                            */

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_exec()");
#endif
#ifdef RUNALON
    printx ("Forking %s\n", thechan -> ch_name);
    ch_infp = fdopen (0, "r");
    ch_outfp = fdopen (1, "w");

    return (RP_OK);               /* debug mode                         */
#endif;

    if (pipe (nampipe) < OK)
    {
	ll_err(logptr, LLOGTMP, "problem with 1st ch_exec pipe");
	return (RP_NO);
    }
    if (pipe (respipe) < OK)
    {
	ll_err(logptr, LLOGTMP, "problem with 2d ch_exec pipe");
	(void) close (nampipe[0]);
	(void) close (nampipe[1]);
	return (RP_NO);
    }
    sprintf (arg1, "%d", nampipe[0]);
    sprintf (arg2, "%d", respipe[1]);
    arg3[0] = '\0';
    if( domsg == TRUE )
	strcat( arg3, "w" );

    regfdary[0] = 0;              /* must keep open, in case of POBox   */
    regfdary[1] = 1;              /*   or other interaction with caller */

    for(tmp=2; tmp < numfds; regfdary[tmp++] = CLOSEFD);

    regfdary[nampipe[0]] = nampipe[0];
    regfdary[respipe[1]] = respipe[1];

    getfpath (thechan -> ch_ppath, chndfldir, temppath);

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "[%s]'%s' ('%s','%s', '%s')",
		temppath, thechan -> ch_name, arg1, arg2, arg3);

#endif
    ll_close (logptr);            /* since we are about to fork()       */
    fflush (stdout);
    fflush (stderr);
    oldpid = getpid ();

    ch_child = nexecl (FRKEXEC, FRKERRR, regfdary, temppath,
			thechan -> ch_name, arg1, arg2, arg3, (char *)0);

    (void) close (nampipe[0]);     /* Close other end of pipes */
    (void) close (respipe[1]);
    if (ch_child == NOTOK)        /* didn't get the child               */
    {
	ll_err (logptr, LLOGFAT, "Unable to run '%s'", thechan -> ch_ppath);
	ch_child = 0;
	(void) close (nampipe[1]);       /* Close other end of pipes     */
	(void) close (respipe[0]);
	if (getpid () == oldpid)
	    return (RP_NO);       /* still the parent => fork() error   */
	else
	    exit (RP_MECH);      /* must be child                       */
    }
				  /* clean up regfd arrary for future   */
    regfdary[nampipe[0]] = -1;
    regfdary[respipe[1]] = -1;

    ch_infp = fdopen (respipe[0], "r");
    ch_outfp = fdopen (nampipe[1], "w");

    printx ("[ Accessing %s (%s)]\n", 
		thechan -> ch_name, thechan -> ch_show);
    ll_log (logptr, LLOGPTR, "[ %s ] (r%d,w%d)",
		thechan -> ch_name, respipe[0], nampipe[1]);

    return (RP_OK);
}

