root/usr/src/cmd/bnu/eio.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include "uucp.h"

#ifdef  E_PROTOCOL

#ifndef MIN
#define     MIN(a,b) (((a)<(b))?(a):(b))
#endif

#if defined(BSD4_2) || defined (ATTSVR4)
#include <netinet/in.h>
#endif /* BSD4_2 || ATTSVR4 */

#define EBUFSIZ 1024
#define EMESGLEN 20

#define TBUFSIZE 1024
#define TPACKSIZE       512

extern long lseek();    /* Find offset into the file. */
static jmp_buf Failbuf;
extern int erdblk();
extern unsigned msgtime;

static char Erdstash[EBUFSIZ];
static int Erdlen;

/*
 * error-free channel protocol
 */
/* ARGSUSED */
static void
ealarm(sig)
int sig;
{
        longjmp(Failbuf, 1);
}
static void (*esig)();

/*
 * turn on protocol timer
 */
int
eturnon()
{
        esig=signal(SIGALRM, ealarm);
        return(0);
}

int
eturnoff()
{
        signal(SIGALRM, esig);
        return(0);
}

/*
 * write message across link
 *      type    -> message type
 *      str     -> message body (ascii string)
 *      fn      -> link file descriptor
 * return
 *      FAIL    -> write failed
 *      SUCCESS -> write succeeded
 */
int
ewrmsg(char type, char *str, int fn)
{
        return(etwrmsg(type, str, fn, 0));
}

/*
 * read message from link
 *      str     -> message buffer
 *      fn      -> file descriptor
 * return
 *      FAIL    -> read timed out
 *      SUCCESS -> ok message in str
 */
int
erdmsg(char *str, int fn)
{
        return(etrdmsg(str, fn, 0));
}

/*
 * read data from file fp1 and write
 * on link
 *      fp1     -> file descriptor
 *      fn      -> link descriptor
 * returns:
 *      FAIL    ->failure in link
 *      SUCCESS -> ok
 */
int
ewrdata(fp1, fn)
FILE *fp1;
int     fn;
{
        int ret;
        int     fd1;
        int len;
        unsigned long bytes;
        char bufr[EBUFSIZ];
        struct stat     statbuf;
        off_t   msglen;
        char    cmsglen[EMESGLEN];
        off_t   startPoint;     /* Offset from begining of the file in
                                 *   case we are restarting from a check
                                 *   point.
                                 */

        if (setjmp(Failbuf)) {
                DEBUG(7, "ewrdata failed\n%s", "");
                return(FAIL);
        }
        bytes = 0L;
        fd1 = fileno(fp1);
        fstat(fd1, &statbuf);
        startPoint = lseek(fd1, 0L, 1);
        if (startPoint < 0)
        {
                DEBUG(7, "ewrdata lseek failed.  Errno=%d\n", errno);
                return(FAIL);
        }
        msglen = statbuf.st_size - startPoint;
        if (msglen < 0)
        {
                DEBUG(7, "ewrdata: startPoint past end of file.\n%s", "");
                return(FAIL);
        }
        sprintf(cmsglen, "%ld", (long) msglen);
        DEBUG(9, "ewrdata writing %d ...", sizeof(cmsglen));
        alarm(msgtime);
        ret = (*Write)(fn, cmsglen, sizeof(cmsglen));
        alarm(0);
        DEBUG(9, "ret %d\n", ret);
        if (ret != sizeof(cmsglen))
                return(FAIL);
        DEBUG(7, "ewrdata planning to send %ld bytes to remote.\n", msglen);
        while ((len = read( fd1, bufr, EBUFSIZ )) > 0) {
                DEBUG(9, "ewrdata writing %d ...", len);
                alarm(msgtime);
                bytes += len;
                putfilesize(bytes);
                ret = (*Write)(fn, bufr, (unsigned) len);
                alarm(0);
                DEBUG(9, "ewrdata ret %d\n", ret);
                if (ret != len)
                        return(FAIL);
                if ((msglen -= len) <= 0)
                        break;
        }
        if (len < 0 || (len == 0 && msglen != 0)) return(FAIL);
        return(SUCCESS);
}

/*
 * read data from link and
 * write into file
 *      fp2     -> file descriptor
 *      fn      -> link descriptor
 * returns:
 *      SUCCESS -> ok
 *      FAIL    -> failure on link
 */
int
erddata(int fn, FILE *fp2)
{
        int ret;
        int     fd2;
        char bufr[EBUFSIZ];
        int     len;
        long    msglen, bytes;
        char    cmsglen[EMESGLEN], *cptr, *erdptr = Erdstash;

        DEBUG(9, "erddata wants %d\n", sizeof(cmsglen));
        if (Erdlen > 0) {
                DEBUG(9, "%d bytes stashed\n", Erdlen);
                if (Erdlen >= sizeof(cmsglen)) {
                        memcpy(cmsglen, erdptr, sizeof(cmsglen));
                        Erdlen -= sizeof(cmsglen);
                        erdptr += sizeof(cmsglen);
                        ret = len = 0;
                } else {
                        memcpy(cmsglen, Erdstash, Erdlen);
                        cptr = cmsglen + Erdlen;
                        len = sizeof(cmsglen) - Erdlen;
                        ret = erdblk(cptr, len, fn);
                        Erdlen = 0;
                }
        } else {
                len = sizeof(cmsglen);
                ret = erdblk(cmsglen, sizeof(cmsglen), fn);
        }
        if (ret != len)
                return(FAIL);
        ret = SUCCESS;
        sscanf(cmsglen, "%ld", &msglen);
        if ( ((msglen-1)/512 +1) > Ulimit )
                ret = EFBIG;
        DEBUG(7, "erddata file is %ld bytes\n", msglen);
        fd2 = fileno( fp2 );

        if (Erdlen > 0) {
                DEBUG(9, "%d bytes stashed\n", Erdlen);
                if (write(fileno(fp2), erdptr, Erdlen) != Erdlen)
                        return(FAIL);
                msglen -= Erdlen;
                Erdlen = 0;
                DEBUG(7, "erddata remainder is %ld bytes\n", msglen);
        }

        for (;;) {
                len = erdblk(bufr, (int) MIN(msglen, EBUFSIZ), fn);
                DEBUG(9, "erdblk ret %d\n", len);
                if (len < 0) {
                        DEBUG(7, "erdblk failed\n%s", "");
                        return(FAIL);
                }

                /*
                 * handle the case for remote socket close.
                 */
                if (len == 0) {
                        ret = errno;
                        DEBUG(7, "erddata: remote socket closed, errno %d\n",
                                    ret);
                        break;
                }
                bytes += len;
                putfilesize(bytes);
                if ((msglen -= len) < 0) {
                        DEBUG(7, "erdblk read too much\n%s", "");
                        return(FAIL);
                }
                /* this write is to file -- use write(2), not (*Write) */
                if ( ret == SUCCESS && write( fd2, bufr, len ) != len ) {
                        ret = errno;
                        DEBUG(7, "erddata: write to file failed, errno %d\n", ret);
                }
                if (msglen == 0)
                        break;
        }
        return(ret);
}

/*
 * read block from link
 * reads are timed
 *      blk     -> address of buffer
 *      len     -> size to read
 *      fn      -> link descriptor
 * returns:
 *      FAIL    -> link error timeout on link
 *      i       -> # of bytes read (must not be 0)
 */
int
erdblk(char *blk, int len, int fn)
{
        int i, ret;

        if(setjmp(Failbuf)) {
                DEBUG(7, "timeout (%d sec)\n", msgtime);
                return(FAIL);
        }

        alarm(msgtime);
        for (i = 0; i < len; i += ret) {
                DEBUG(9, "erdblk ask %d ", len - i);
                if ((ret = (*Read)(fn, blk, (unsigned) len - i)) < 0) {
                        alarm(0);
                        DEBUG(7, "erdblk read failed\n%s", "");
                        return(FAIL);
                }
                DEBUG(9, "erdblk got %d\n", ret);
                if (ret == 0)
                        break;
                blk += ret;
        }
        alarm(0);
        return(i);
}

struct tbuf {
        long t_nbytes;
        char t_data[TBUFSIZE];
};

/*
 * read message from link
 *      str     -> message buffer
 *      fn      -> file descriptor
 * return
 *      FAIL    -> read timed out
 *      SUCCESS -> ok message in str
 */
int
trdmsg(char *str, int fn)
{
        return(etrdmsg(str, fn, TPACKSIZE));
}

/*
 * write message across link
 *      type    -> message type
 *      str     -> message body (ascii string)
 *      fn      -> link file descriptor
 * return
 *      FAIL    -> write failed
 *      SUCCESS -> write succeeded
 */
int
twrmsg(char type, char *str, int fn)
{
        return(etwrmsg(type, str, fn, TPACKSIZE));
}

/*
 * read data from file fp1 and write on link
 *      fp1     -> file descriptor
 *      fn      -> link descriptor
 * returns:
 *      FAIL    ->failure in link
 *      SUCCESS -> ok
 */
int
twrdata(fp1, fn)
FILE *fp1;
int     fn;
{
        int ret;
        int len;
        unsigned long bytes;
        struct tbuf bufr;
        struct stat statbuf;

        if (setjmp(Failbuf)) {
                DEBUG(7, "twrdata failed\n", 0);
                return(FAIL);
        }
        fstat(fileno(fp1), &statbuf);
        bytes = 0L;
        while ((len = read(fileno(fp1), bufr.t_data, TBUFSIZE)) > 0) {
                bufr.t_nbytes = htonl((long)len);
                DEBUG(7, "twrdata writing %d ...", len);
                bytes += len;
                putfilesize(bytes);
                len += sizeof(long);
                alarm(msgtime);
                ret = (*Write)(fn, (char *)&bufr, (unsigned) len);
                alarm(0);
                DEBUG(7, "ret %d\n", ret);
                if (ret != len)
                        return(FAIL);
                if (len != TBUFSIZE+sizeof(long))
                        break;
        }
        bufr.t_nbytes = 0;
        alarm(msgtime);
        ret = write(fn, (char *)&bufr, sizeof(long));
        alarm(0);
        if (ret != sizeof(long))
                return FAIL;
        return(SUCCESS);
}

/*
 * read data from link and write into file
 *      fp2     -> file descriptor
 *      fn      -> link descriptor
 * returns:
 *      SUCCESS -> ok
 *      FAIL    -> failure on link
 */
int
trddata(int fn, FILE *fp2)
{
        int len, nread;
        long Nbytes;
        unsigned long bytes = 0L;
        char bufr[TBUFSIZE];

        for (;;) {
                len = erdblk((char *)&Nbytes, sizeof(Nbytes), fn);
                DEBUG(7, "trddata ret %d\n", len);
                if (len != sizeof(Nbytes))
                        return(FAIL);
                Nbytes = ntohl(Nbytes);
                DEBUG(7,"trddata expecting %ld bytes\n", Nbytes);
                nread = Nbytes;
                if (nread == 0)
                        break;
                len = erdblk(bufr, nread, fn);
                if (len != Nbytes)
                        return(FAIL);
                bytes += len;
                putfilesize(bytes);
                if (write(fileno(fp2), bufr, len) != len)
                        return(FAIL);
        }
        return(SUCCESS);
}

/*
 * read message from link
 *      str     -> message buffer
 *      fn      -> file descriptor
 *      i       -> if non-zero, amount to read; o.w., read up to '\0'
 * return
 *      FAIL    -> read timed out
 *      SUCCESS -> ok message in str
 *
 * 'e' is fatally flawed -- in a byte stream world, rdmsg can pick up
 * the cmsglen on a R request.  if this happens, we stash the excess
 * where rddata can pick it up.
 */

int
etrdmsg(char *str, int fn, int i)
{
        int len;
        int nullterm = 0;
        char *null, *argstr;


        if (i == 0) {
                DEBUG(9, "etrdmsg looking for null terminator\n", 0);
                nullterm++;
                i = EBUFSIZ;
                argstr = str;
        }

        if(setjmp(Failbuf)) {
                DEBUG(7, "timeout (%d sec)\n", msgtime);
                return(FAIL);
        }

        alarm(msgtime);
        for (;;) {
                DEBUG(9, "etrdmsg want %d ...", i);
                len = (*Read)(fn, str, i);
                DEBUG(9, "got %d\n", len);
                if (len == 0)
                        continue;       /* timeout will get this */
                if (len < 0) {
                        alarm(0);
                        return(FAIL);
                }
                str += len;
                i -= len;
                if (nullterm) {
                        /* no way can a msg be as long as EBUFSIZ-1 ... */
                        *str = 0;
                        null = strchr(argstr, '\0');
                        if (null != str) {
                                null++; /* start of stash */
                                memcpy(Erdstash + Erdlen, null, str - null);
                                Erdlen += str - null;
                                break;
                        } else
                                argstr = str;
                } else {
                        if (i == 0)
                                break;
                }
        }
        alarm(0);
        return(SUCCESS);
}

/*
 * write message across link
 *      type    -> message type
 *      str     -> message body (ascii string)
 *      fn      -> link file descriptor
 *      len     -> if non-zero, amount to write;
                   o.w., write up to '\0' (inclusive)
 * return
 *      FAIL    -> write failed
 *      SUCCESS -> write succeeded
 */
int
etwrmsg(type, str, fn, len)
char type;
char *str;
int fn, len;
{
        char bufr[EBUFSIZ], *endstr;
        int ret;

        bufr[0] = type;

        /* point endstr to last character to be sent */
        if ((endstr = strchr(str, '\n')) != 0)
                *endstr = 0;
        else
                endstr = str + strlen(str);

        memcpy(bufr+1, str, (endstr - str) + 1);        /* include '\0' */
        if (len == 0)
                len = (endstr - str) + 2;       /* include bufr[0] and '\0' */
        else
                bufr[len-1] = 0;                /* 't' needs this terminator */


        if (setjmp(Failbuf)) {
                DEBUG(7, "etwrmsg write failed\n", 0);
                return(FAIL);
        }
        DEBUG(9, "etwrmsg want %d ... ", len);
        alarm(msgtime);
        ret = (*Write)(fn, bufr, (unsigned) len);
        alarm(0);
        DEBUG(9, "sent %d\n", ret);
        if (ret != len)
                return(FAIL);
        return(SUCCESS);
}
#endif  /* E_PROTOCOL */