448 lines
9.7 KiB
C
448 lines
9.7 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <signal.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
/* for Plan 9 */
|
||
|
#ifdef PLAN9
|
||
|
#define LP "/bin/lp"
|
||
|
#define TMPDIR "/sys/lib/lp/tmp"
|
||
|
#define LPDAEMONLOG "/sys/lib/lp/log/lpdaemonl"
|
||
|
#endif
|
||
|
/* for Tenth Edition systems */
|
||
|
#ifdef V10
|
||
|
#define LP "/usr/bin/lp"
|
||
|
#define TMPDIR "/tmp"
|
||
|
#define LPDAEMONLOG "/tmp/lpdaemonl"
|
||
|
#endif
|
||
|
/* for System V or BSD systems */
|
||
|
#if defined(SYSV) || defined(BSD)
|
||
|
#define LP "/v/bin/lp"
|
||
|
#define TMPDIR "/tmp"
|
||
|
#define LPDAEMONLOG "/tmp/lpdaemonl"
|
||
|
#endif
|
||
|
|
||
|
#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]);
|
||
|
fclose(fp);
|
||
|
}
|
||
|
|
||
|
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((const 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, (const char **)argvals);
|
||
|
error("exec failed\n");
|
||
|
exit(3);
|
||
|
default:
|
||
|
while(wait((int *)0) != cpid)
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
tempfile(void)
|
||
|
{
|
||
|
static 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]);
|
||
|
}
|
||
|
|
||
|
main()
|
||
|
{
|
||
|
unsigned char *ap, *bp, *cp, *savbufpnt;
|
||
|
int i, blen, rv, saveflg, savargcnt;
|
||
|
struct jobinfo *jinfop;
|
||
|
|
||
|
signal(SIGHUP, SIG_IGN);
|
||
|
signal(SIGALRM, alarmhandler);
|
||
|
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);
|
||
|
}
|