| #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); | 
 | } |