ref: cd747191012d0420ac631340d962da1f80cef0a5
dir: /sys/src/cmd/lp/lpdaemon.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[NAMELEN] = '\0'; break; case 'P': if (ap[1] == '\0') strncpy(info.user, "unknown", NAMELEN); else strncpy(info.user, (const char *)&ap[1], NAMELEN); info.user[NAMELEN] = '\0'; break; } } return(&info); } void alarmhandler(int sig) { signal(sig, alarmhandler); error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); } void 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); }