Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#ifdef plan9

#include <u.h>
#include <libc.h>

enum {
        stderr = 2,
        RDNETIMEOUT = 30*60*1000,
        WRNETIMEOUT = RDNETIMEOUT,
};
#else

/* not for plan 9 */
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>

#define create  creat
#define seek    lseek
#define fprint  fprintf
#define sprint  sprintf
#define exits   exit

#define ORDWR   O_RDWR
#define OTRUNC  O_TRUNC
#define ORCLOSE 0

#define RDNETIMEOUT     60
#define WRNETIMEOUT     60

#endif

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

#define ACK(a)  write(a, "", 1)
#define NAK(a)  write(a, "\001", 1)

#define LPDAEMONLOG     "/tmp/lpdaemonl"

#define LNBFSZ  4096
char lnbuf[LNBFSZ];
int dbgstate = 0;
char *dbgstrings[] = {
        "",
        "rcvack1",
        "send",
        "rcvack2",
        "response",
        "done"
};

#ifdef plan9

void
error(int level, char *s1, ...)
{
        va_list ap;
        long thetime;
        char *chartime;
        char *args[8];
        int argno = 0;

        if (level == 0) {
                time(&thetime);
                chartime = ctime(thetime);
                fprint(stderr, "%.15s ", &(chartime[4]));
        }
        va_start(ap, s1);
        while(args[argno++] = va_arg(ap, char*))
                ;
        va_end(ap);
        fprint(stderr, s1, *args);
}

int
alarmhandler(void *foo, char *note) {
        USED(foo);
        if(strcmp(note, "alarm")==0) {
                fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
                return(1);
        } else return(0);
}

#else

void
error(int level, char *s1, ...)
{
        time_t thetime;
        char *chartime;

        if (level == 0) {
                time(&thetime);
                chartime = ctime(&thetime);
                fprintf(stderr, "%.15s ", &(chartime[4]));
        }
        fprintf(stderr, s1, &s1 + 1);
}

void
alarmhandler() {
        fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
}

#endif

/* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
 * long for the buffer.  The result is left in lnbuf and the number of characters
 * read in is returned.
 */
int
readline(int inpfd)
{
        register char *ap;
        register int i;

        ap = lnbuf;
        i = 0;
        do {
                if (read(inpfd, ap, 1) != 1) {
                        error(0, "read error in readline, fd=%d\n", inpfd);
                        break;
                }
        } while ((++i < LNBFSZ - 2) && *ap++ != '\n');
        if (i == LNBFSZ - 2) {
                *ap = '\n';
                i++;
        }
        *ap = '\0';
        return(i);
}

#define RDSIZE 512
char jobbuf[RDSIZE];

int
pass(int inpfd, int outfd, int bsize)
{
        int rv, bcnt;

        for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
                alarm(WRNETIMEOUT);     /* to break hanging */
                if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
                        error(0, "read error during pass, %d remaining\n", bcnt);
                        break;
                } else if((write(outfd, jobbuf, rv)) != rv) {
                        error(0, "write error during pass, %d remaining\n", bcnt);
                        break;
                }
        }
        alarm(0);
        return(bcnt);
}
        
/* get whatever stdin has and put it into the temporary file.
 * return the file size.
 */
int
prereadfile(int inpfd)
{
        int rv, bsize;

        bsize = 0;
        do {
                if((rv=read(0, jobbuf, RDSIZE))<0) {
                        error(0, "read error while making temp file\n");
                        exits("read error while making temp file");
                } else if((write(inpfd, jobbuf, rv)) != rv) {
                        error(0, "write error while making temp file\n");
                        exits("write error while making temp file");
                }
                bsize += rv;
        } while (rv!=0);
        return(bsize);
}

int
tempfile(void)
{
        static tindx = 0;
        char tmpf[20];
        int tmpfd;

        sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
        if((tmpfd=create(tmpf,
#ifdef plan9
                ORDWR|OTRUNC,
#endif
            0666)) < 0) {
                error(0, "cannot create temp file %s\n", tmpf);
                exits("cannot create temp file");
        }
        close(tmpfd);
        if((tmpfd=open(tmpf, ORDWR
#ifdef plan9
                |ORCLOSE|OTRUNC
#endif
            )) < 0) {
                error(0, "cannot open temp file %s\n", tmpf);
                exits("cannot open temp file");
        }
        return(tmpfd);
}

int
recvACK(int netfd)
{
        int rv;

        *jobbuf = '\0';
        alarm(RDNETIMEOUT);
        if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
                error(0, "failed to receive ACK, ");
                if (*jobbuf == '\0')
                        error(1, "read failed\n");
                else
                        error(1, "received <%#x> instead\n", (uchar)*jobbuf);
                rv = 0;
        } else rv = 1;
        alarm(0);
        return(rv);
}

void
main(int argc, char *argv[])
{
        int i, rv, netfd, bsize, datafd;
#ifndef plan9
        void (*oldhandler)();
#endif

        /* make connection */
        if (argc != 2) {
                fprint(stderr, "usage: %s network!destination!service\n",
                        argv[0]);
                exits("usage");
        }

        /* read options line from stdin into lnbuf */
        i = readline(0);

        /* read stdin into tempfile to get size */
        datafd = tempfile();
        bsize = prereadfile(datafd);

        /* network connection is opened after data is in to avoid timeout */
        if ((netfd = dial(argv[1], 0, 0, 0)) < 0) {
                fprint(stderr, "dialing ");
                perror(argv[1]);
                exits("can't dial");
        }

        /* write out the options we read above */
        if (write(netfd, lnbuf, i) != i) {
                error(0, "write error while sending options\n");
                exits("write error sending options");
        }

        /* send the size of the file to be sent */
        sprint(lnbuf, "%d\n", bsize);
        i = strlen(lnbuf);
        if ((rv=write(netfd, lnbuf, i)) != i) {
                perror("write error while sending size");
                error(0, "write returned %d\n", rv);
                exits("write error sending size");
        }

        if (seek(datafd, 0L, 0) < 0) {
                error(0, "error seeking temp file\n");
                exits("seek error");
        }
        /* mirror performance in readfile() in lpdaemon */

#ifdef plan9
        atnotify(alarmhandler, 1);
#else
        oldhandler = signal(SIGALRM, alarmhandler);
#endif

        dbgstate = 1;
        if(!recvACK(netfd)) {
                error(0, "failed to receive ACK before sending data\n");
                exits("recv ack1 failed");
        }
        dbgstate = 2;
        if ((i=pass(datafd, netfd, bsize)) != 0) {
                NAK(netfd);
                error(0, "failed to send %d bytes\n", i);
                exits("send data failed");
        }
        ACK(netfd);
        dbgstate = 3;
        if(!recvACK(netfd)) {
                error(0, "failed to receive ACK after sending data\n");
                exits("recv ack2 failed");
        }

        /* get response, as from lp -q */
        dbgstate = 4;
        while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
                if((write(1, jobbuf, rv)) != rv) {
                        error(0, "write error while sending to stdout\n");
                        exits("write error while sending to stdout");
                }
        }
        dbgstate = 5;

#ifdef plan9
        atnotify(alarmhandler, 0);
        /* close down network connections and go away */
        exits("");
#else
        signal(SIGALRM, oldhandler);
        exit(0);
#endif
}