|  | #include <u.h> | 
|  | #define NOPLAN9DEFINES | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  | #include <stdlib.h> | 
|  | #include <sys/wait.h> | 
|  | #include <fcntl.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <signal.h> | 
|  | #include <errno.h> | 
|  | #include <time.h> | 
|  | #include <string.h> | 
|  | #include <stdarg.h> | 
|  | #include <libc.h> | 
|  |  | 
|  | #define LP	unsharp("#9/bin/lp") | 
|  | #define TMPDIR "/var/tmp" | 
|  | #define LPDAEMONLOG	unsharp("#9/lp/log/lpdaemonl") | 
|  |  | 
|  | #define ARGSIZ 4096 | 
|  | #define NAMELEN 30 | 
|  |  | 
|  | unsigned char argvstr[ARGSIZ];		/* arguments after parsing */ | 
|  | unsigned char *argvals[ARGSIZ/2+1];	/* pointers to arguments after parsing */ | 
|  | int ascnt = 0, argcnt = 0;	/* number of arguments parsed */ | 
|  | /* for 'stuff' gleened from lpr cntrl file */ | 
|  | struct jobinfo { | 
|  | char user[NAMELEN+1]; | 
|  | char host[NAMELEN+1]; | 
|  | } *getjobinfo(); | 
|  |  | 
|  | #define MIN(a,b)	((a<b)?a:b) | 
|  |  | 
|  | #define	CPYFIELD(src, dst)	{ while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; } | 
|  |  | 
|  | #define	ACK()	write(1, "", 1) | 
|  | #define NAK()	write(1, "\001", 1) | 
|  |  | 
|  | #define LNBFSZ	4096 | 
|  | unsigned char lnbuf[LNBFSZ]; | 
|  |  | 
|  | #define	RDSIZE 512 | 
|  | unsigned char jobbuf[RDSIZE]; | 
|  |  | 
|  | int datafd[400], cntrlfd = -1; | 
|  |  | 
|  | int dbgstate = 0; | 
|  | char *dbgstrings[] = { | 
|  | "", | 
|  | "sendack1", | 
|  | "send", | 
|  | "rcvack", | 
|  | "sendack2", | 
|  | "done" | 
|  | }; | 
|  |  | 
|  | void | 
|  | error(char *s1, ...) | 
|  | { | 
|  | FILE *fp; | 
|  | long thetime; | 
|  | char *chartime; | 
|  | va_list ap; | 
|  | char *args[8]; | 
|  | int argno = 0; | 
|  |  | 
|  | if((fp=fopen(LPDAEMONLOG, "a"))==NULL) { | 
|  | fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG); | 
|  | return; | 
|  | } | 
|  | time(&thetime); | 
|  | chartime = ctime(&thetime); | 
|  | fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid()); | 
|  | va_start(ap, s1); | 
|  | while((args[argno++] = va_arg(ap, char*)) && argno<8); | 
|  | va_end(ap); | 
|  | fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); | 
|  | fflush(fp); | 
|  | fclose(fp); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void | 
|  | forklp(int inputfd) | 
|  | { | 
|  | int i, cpid; | 
|  | unsigned char *bp, *cp; | 
|  | unsigned char logent[LNBFSZ]; | 
|  |  | 
|  | /* log this call to lp */ | 
|  | cp = logent; | 
|  | for (i=1; i<argcnt; i++) { | 
|  | bp = argvals[i]; | 
|  | if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) { | 
|  | CPYFIELD(bp, cp); | 
|  | *cp++ = ' '; | 
|  | } | 
|  | } | 
|  | *--cp = '\n'; | 
|  | *++cp = '\0'; | 
|  | error((char *)logent); | 
|  | switch((cpid=fork())){ | 
|  | case -1: | 
|  | error("fork error\n"); | 
|  | exit(2); | 
|  | case 0: | 
|  | if (inputfd != 0) | 
|  | dup2(inputfd, 0); | 
|  | dup2(1, 2); | 
|  | lseek(0, 0L, 0); | 
|  | execvp(LP, (char **)argvals); | 
|  | error("exec failed\n"); | 
|  | exit(3); | 
|  | default: | 
|  | while((i=wait((int *)0)) != cpid){ | 
|  | if(i == -1 && errno == ECHILD) | 
|  | break; | 
|  | printf("%d %d\n", i, errno); | 
|  | fflush(stdout); | 
|  | } | 
|  | error("wait got %d\n", cpid); | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | tempfile(void) | 
|  | { | 
|  | static int tindx = 0; | 
|  | char tmpf[sizeof(TMPDIR)+64]; | 
|  | int crtfd, tmpfd; | 
|  |  | 
|  | sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++); | 
|  | if((crtfd=creat(tmpf, 0666)) < 0) { | 
|  | error("cannot create temp file %s\n", tmpf); | 
|  | NAK(); | 
|  | exit(3); | 
|  | } | 
|  | if((tmpfd=open(tmpf, 2)) < 0) { | 
|  | error("cannot open temp file %s\n", tmpf); | 
|  | NAK(); | 
|  | exit(3); | 
|  | } | 
|  | close(crtfd); | 
|  | /*	unlink(tmpf);	/* comment out for debugging */ | 
|  | return(tmpfd); | 
|  | } | 
|  |  | 
|  | int | 
|  | readfile(int outfd, int bsize) | 
|  | { | 
|  | int rv; | 
|  |  | 
|  | dbgstate = 1; | 
|  | alarm(60); | 
|  | ACK(); | 
|  | dbgstate = 2; | 
|  | for(; bsize > 0; bsize -= rv) { | 
|  | alarm(60); | 
|  | if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) { | 
|  | error("error reading input, %d unread\n", bsize); | 
|  | exit(4); | 
|  | } else if (rv == 0) { | 
|  | error("connection closed prematurely\n"); | 
|  | exit(4); | 
|  | } else if((write(outfd, jobbuf, rv)) != rv) { | 
|  | error("error writing temp file, %d unread\n", bsize); | 
|  | exit(5); | 
|  | } | 
|  | } | 
|  | dbgstate = 3; | 
|  | alarm(60); | 
|  | if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) { | 
|  | alarm(60); | 
|  | ACK(); | 
|  | dbgstate = 4; | 
|  | alarm(0); | 
|  | return(outfd); | 
|  | } | 
|  | alarm(0); | 
|  | error("received bad status <%d> from sender\n", *jobbuf); | 
|  | error("rv=%d\n", rv); | 
|  | NAK(); | 
|  | return(-1); | 
|  | } | 
|  |  | 
|  | /* reads a line from the input into lnbuf | 
|  | * if there is no error, it returns | 
|  | *   the number of characters in the buffer | 
|  | * if there is an error and there where characters | 
|  | *   read, it returns the negative value of the | 
|  | *   number of characters read | 
|  | * if there is an error and no characters were read, | 
|  | *   it returns the negative value of 1 greater than | 
|  | *   the size of the line buffer | 
|  | */ | 
|  | int | 
|  | readline(int inpfd) | 
|  | { | 
|  | unsigned char *ap; | 
|  | int i, rv; | 
|  |  | 
|  | ap = lnbuf; | 
|  | lnbuf[0] = '\0'; | 
|  | i = 0; | 
|  | alarm(60); | 
|  | do { | 
|  | rv = read(inpfd, ap, 1); | 
|  | } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2)); | 
|  | alarm(0); | 
|  | if (i != 0 && *ap != '\n') { | 
|  | *++ap = '\n'; | 
|  | i++; | 
|  | } | 
|  | *++ap = '\0'; | 
|  | if (rv < 0) { | 
|  | error("read error; lost connection\n"); | 
|  | if (i==0) i = -(LNBFSZ+1); | 
|  | else i = -i; | 
|  | } | 
|  | return(i); | 
|  | } | 
|  |  | 
|  | int | 
|  | getfiles(void) | 
|  | { | 
|  | unsigned char *ap; | 
|  | int filecnt, bsize, rv; | 
|  |  | 
|  | filecnt = 0; | 
|  | /* get a line, hopefully containing a ctrl char, size, and name */ | 
|  | for(;;) { | 
|  | ap = lnbuf; | 
|  | if ((rv=readline(0)) < 0) NAK(); | 
|  | if (rv <= 0) { | 
|  | return(filecnt); | 
|  | } | 
|  | switch(*ap++) { | 
|  | case '\1':		/* cleanup - data sent was bad (whatever that means) */ | 
|  | break; | 
|  | case '\2':		/* read control file */ | 
|  | bsize = atoi((const char *)ap); | 
|  | cntrlfd = tempfile(); | 
|  | if (readfile(cntrlfd, bsize) < 0) { | 
|  | close(cntrlfd); | 
|  | NAK(); | 
|  | return(0); | 
|  | } | 
|  | break; | 
|  | case '\3':		/* read data file */ | 
|  | bsize = atoi((const char *)ap); | 
|  | datafd[filecnt] = tempfile(); | 
|  | if (readfile(datafd[filecnt], bsize) < 0) { | 
|  | close(datafd[filecnt]); | 
|  | NAK(); | 
|  | return(0); | 
|  | } | 
|  | filecnt++; | 
|  | break; | 
|  | default: | 
|  | error("protocol error <%d>\n", *(ap-1)); | 
|  | NAK(); | 
|  | } | 
|  | } | 
|  | return(filecnt); | 
|  | } | 
|  |  | 
|  | struct jobinfo * | 
|  | getjobinfo(int fd) | 
|  | { | 
|  | unsigned char *ap; | 
|  | int rv; | 
|  | static struct jobinfo info; | 
|  |  | 
|  | if (fd < 0) error("getjobinfo: bad file descriptor\n"); | 
|  | if (lseek(fd, 0L, 0) < 0) { | 
|  | error("error seeking in temp file\n"); | 
|  | exit(7); | 
|  | } | 
|  | /* the following strings should be < NAMELEN or else they will not | 
|  | * be null terminated. | 
|  | */ | 
|  | strncpy(info.user, "daemon", NAMELEN); | 
|  | strncpy(info.host, "nowhere", NAMELEN); | 
|  | /* there may be a space after the name and host.  It will be filtered out | 
|  | * by CPYFIELD. | 
|  | */ | 
|  | while ((rv=readline(fd)) > 0) { | 
|  | ap = lnbuf; | 
|  | ap[rv-1] = '\0';	/* remove newline from string */ | 
|  | switch (*ap) { | 
|  | case 'H': | 
|  | if (ap[1] == '\0') | 
|  | strncpy(info.host, "unknown", NAMELEN); | 
|  | else | 
|  | strncpy(info.host, (const char *)&ap[1], NAMELEN); | 
|  | info.host[strlen(info.host)] = '\0'; | 
|  | break; | 
|  | case 'P': | 
|  | if (ap[1] == '\0') | 
|  | strncpy(info.user, "unknown", NAMELEN); | 
|  | else | 
|  | strncpy(info.user, (const char *)&ap[1], NAMELEN); | 
|  | info.user[strlen(info.user)] = '\0'; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return(&info); | 
|  | } | 
|  |  | 
|  | void | 
|  | alarmhandler(int sig) { | 
|  | signal(sig, alarmhandler); | 
|  | error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); | 
|  | } | 
|  |  | 
|  | void | 
|  | nop(int sig) | 
|  | { | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | main() | 
|  | { | 
|  | unsigned char *ap, *bp, *cp, *savbufpnt; | 
|  | int i, blen, rv, saveflg, savargcnt; | 
|  | struct jobinfo *jinfop; | 
|  |  | 
|  | signal(SIGHUP, SIG_IGN);		/* SIGHUP not in lcc */ | 
|  | signal(SIGALRM, alarmhandler);	/* SIGALRM not in lcc */ | 
|  | signal(SIGCHLD, nop);	/* so that wait will get us something */ | 
|  | cp = argvstr; | 
|  | /* setup argv[0] for exec */ | 
|  | argvals[argcnt++] = cp; | 
|  | for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++); | 
|  | *cp++ = '\0'; | 
|  | /* get the first line sent and parse it as arguments for lp */ | 
|  | if ((rv=readline(0)) < 0) | 
|  | exit(1); | 
|  | bp = lnbuf; | 
|  | /* setup the remaining arguments */ | 
|  | /* check for BSD style request */ | 
|  | /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */ | 
|  | switch (*bp) { | 
|  | case '\001': | 
|  | case '\003': | 
|  | case '\004': | 
|  | bp++;	/* drop the ctrl character from the input */ | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0';		/* -q */ | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'd'; 			/* -d */ | 
|  | CPYFIELD(bp, cp);				/* printer */ | 
|  | *cp++ = '\0'; | 
|  | break; | 
|  | case '\002': | 
|  | bp++;	/* drop the ctrl character from the input */ | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'd'; 			/* -d */ | 
|  | CPYFIELD(bp, cp);				/* printer */ | 
|  | *cp++ = '\0'; | 
|  | ACK(); | 
|  | savargcnt = argcnt; | 
|  | savbufpnt = cp; | 
|  | while ((rv=getfiles())) { | 
|  | jinfop = getjobinfo(cntrlfd); | 
|  | close(cntrlfd); | 
|  | argcnt = savargcnt; | 
|  | cp = savbufpnt; | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'M'; 			/* -M */ | 
|  | bp = (unsigned char *)jinfop->host; | 
|  | CPYFIELD(bp, cp);				/* host name */ | 
|  | *cp++ = '\0'; | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'u'; 			/* -u */ | 
|  | bp = (unsigned char *)jinfop->user; | 
|  | CPYFIELD(bp, cp);				/* user name */ | 
|  | *cp++ = '\0'; | 
|  | for(i=0;i<rv;i++) | 
|  | forklp(datafd[i]); | 
|  | } | 
|  | exit(0); | 
|  | case '\005': | 
|  | bp++;	/* drop the ctrl character from the input */ | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0';		/* -k */ | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; *cp++ = 'd'; 			/* -d */ | 
|  | CPYFIELD(bp, cp);				/* printer */ | 
|  | *cp++ = '\0'; | 
|  | argvals[argcnt++] = cp; | 
|  | *cp++ = '-'; ap = cp; *cp++ = 'u'; 		/* -u */ | 
|  | CPYFIELD(bp, cp);				/* username */ | 
|  |  | 
|  | /* deal with bug in lprng where the username is not supplied | 
|  | */ | 
|  | if (ap == (cp-1)) { | 
|  | ap = (unsigned char *)"none"; | 
|  | CPYFIELD(ap, cp); | 
|  | } | 
|  |  | 
|  | *cp++ = '\0'; | 
|  | datafd[0] = tempfile(); | 
|  | blen = strlen((const char *)bp); | 
|  | if (write(datafd[0], bp, blen) != blen) { | 
|  | error("write error\n"); | 
|  | exit(6); | 
|  | } | 
|  | if (write(datafd[0], "\n", 1) != 1) { | 
|  | error("write error\n"); | 
|  | exit(6); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | /* otherwise get my lp arguments */ | 
|  | do { | 
|  | /* move to next non-white space */ | 
|  | while (*bp==' '||*bp=='\t') | 
|  | ++bp; | 
|  | if (*bp=='\n') continue; | 
|  | /* only accept arguments beginning with - | 
|  | * this is done to prevent the printing of | 
|  | * local files from the destination host | 
|  | */ | 
|  | if (*bp=='-') { | 
|  | argvals[argcnt++] = cp; | 
|  | saveflg = 1; | 
|  | } else | 
|  | saveflg = 0; | 
|  | /* move to next white space copying text to argument buffer */ | 
|  | while (*bp!=' ' && *bp!='\t' && *bp!='\n' | 
|  | && *bp!='\0') { | 
|  | *cp = *bp++; | 
|  | cp += saveflg; | 
|  | } | 
|  | *cp = '\0'; | 
|  | cp += saveflg; | 
|  | } while (*bp!='\n' && *bp!='\0'); | 
|  | if (readline(0) < 0) exit(7); | 
|  | datafd[0] = tempfile(); | 
|  | if(readfile(datafd[0], atoi((const char *)lnbuf)) < 0) { | 
|  | error("readfile failed\n"); | 
|  | exit(8); | 
|  | } | 
|  | } | 
|  | forklp(datafd[0]); | 
|  | exit(0); | 
|  | } |