Blob


1 #include <u.h>
2 #define NOPLAN9DEFINES
3 #include <sys/types.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <sys/wait.h>
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <signal.h>
11 #include <errno.h>
12 #include <time.h>
13 #include <string.h>
14 #include <stdarg.h>
15 #include <libc.h>
17 #define LP unsharp("#9/bin/lp")
18 #define TMPDIR "/var/tmp"
19 #define LPDAEMONLOG unsharp("#9/lp/log/lpdaemonl")
21 #define ARGSIZ 4096
22 #define NAMELEN 30
24 unsigned char argvstr[ARGSIZ]; /* arguments after parsing */
25 unsigned char *argvals[ARGSIZ/2+1]; /* pointers to arguments after parsing */
26 int ascnt = 0, argcnt = 0; /* number of arguments parsed */
27 /* for 'stuff' gleened from lpr cntrl file */
28 struct jobinfo {
29 char user[NAMELEN+1];
30 char host[NAMELEN+1];
31 } *getjobinfo();
33 #define MIN(a,b) ((a<b)?a:b)
35 #define CPYFIELD(src, dst) { while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; }
37 #define ACK() write(1, "", 1)
38 #define NAK() write(1, "\001", 1)
40 #define LNBFSZ 4096
41 unsigned char lnbuf[LNBFSZ];
43 #define RDSIZE 512
44 unsigned char jobbuf[RDSIZE];
46 int datafd[400], cntrlfd = -1;
48 int dbgstate = 0;
49 char *dbgstrings[] = {
50 "",
51 "sendack1",
52 "send",
53 "rcvack",
54 "sendack2",
55 "done"
56 };
58 void
59 error(char *s1, ...)
60 {
61 FILE *fp;
62 long thetime;
63 char *chartime;
64 va_list ap;
65 char *args[8];
66 int argno = 0;
68 if((fp=fopen(LPDAEMONLOG, "a"))==NULL) {
69 fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG);
70 return;
71 }
72 time(&thetime);
73 chartime = ctime(&thetime);
74 fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid());
75 va_start(ap, s1);
76 while((args[argno++] = va_arg(ap, char*)) && argno<8);
77 va_end(ap);
78 fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
79 fflush(fp);
80 fclose(fp);
81 return;
82 }
84 void
85 forklp(int inputfd)
86 {
87 int i, cpid;
88 unsigned char *bp, *cp;
89 unsigned char logent[LNBFSZ];
91 /* log this call to lp */
92 cp = logent;
93 for (i=1; i<argcnt; i++) {
94 bp = argvals[i];
95 if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) {
96 CPYFIELD(bp, cp);
97 *cp++ = ' ';
98 }
99 }
100 *--cp = '\n';
101 *++cp = '\0';
102 error((char *)logent);
103 switch((cpid=fork())){
104 case -1:
105 error("fork error\n");
106 exit(2);
107 case 0:
108 if (inputfd != 0)
109 dup2(inputfd, 0);
110 dup2(1, 2);
111 lseek(0, 0L, 0);
112 execvp(LP, (char **)argvals);
113 error("exec failed\n");
114 exit(3);
115 default:
116 while((i=wait((int *)0)) != cpid){
117 if(i == -1 && errno == ECHILD)
118 break;
119 printf("%d %d\n", i, errno);
120 fflush(stdout);
122 error("wait got %d\n", cpid);
126 int
127 tempfile(void)
129 static int tindx = 0;
130 char tmpf[sizeof(TMPDIR)+64];
131 int crtfd, tmpfd;
133 sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++);
134 if((crtfd=creat(tmpf, 0666)) < 0) {
135 error("cannot create temp file %s\n", tmpf);
136 NAK();
137 exit(3);
139 if((tmpfd=open(tmpf, 2)) < 0) {
140 error("cannot open temp file %s\n", tmpf);
141 NAK();
142 exit(3);
144 close(crtfd);
145 /* unlink(tmpf); /* comment out for debugging */
146 return(tmpfd);
149 int
150 readfile(int outfd, int bsize)
152 int rv;
154 dbgstate = 1;
155 alarm(60);
156 ACK();
157 dbgstate = 2;
158 for(; bsize > 0; bsize -= rv) {
159 alarm(60);
160 if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) {
161 error("error reading input, %d unread\n", bsize);
162 exit(4);
163 } else if (rv == 0) {
164 error("connection closed prematurely\n");
165 exit(4);
166 } else if((write(outfd, jobbuf, rv)) != rv) {
167 error("error writing temp file, %d unread\n", bsize);
168 exit(5);
171 dbgstate = 3;
172 alarm(60);
173 if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) {
174 alarm(60);
175 ACK();
176 dbgstate = 4;
177 alarm(0);
178 return(outfd);
180 alarm(0);
181 error("received bad status <%d> from sender\n", *jobbuf);
182 error("rv=%d\n", rv);
183 NAK();
184 return(-1);
187 /* reads a line from the input into lnbuf
188 * if there is no error, it returns
189 * the number of characters in the buffer
190 * if there is an error and there where characters
191 * read, it returns the negative value of the
192 * number of characters read
193 * if there is an error and no characters were read,
194 * it returns the negative value of 1 greater than
195 * the size of the line buffer
196 */
197 int
198 readline(int inpfd)
200 unsigned char *ap;
201 int i, rv;
203 ap = lnbuf;
204 lnbuf[0] = '\0';
205 i = 0;
206 alarm(60);
207 do {
208 rv = read(inpfd, ap, 1);
209 } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2));
210 alarm(0);
211 if (i != 0 && *ap != '\n') {
212 *++ap = '\n';
213 i++;
215 *++ap = '\0';
216 if (rv < 0) {
217 error("read error; lost connection\n");
218 if (i==0) i = -(LNBFSZ+1);
219 else i = -i;
221 return(i);
224 int
225 getfiles(void)
227 unsigned char *ap;
228 int filecnt, bsize, rv;
230 filecnt = 0;
231 /* get a line, hopefully containing a ctrl char, size, and name */
232 for(;;) {
233 ap = lnbuf;
234 if ((rv=readline(0)) < 0) NAK();
235 if (rv <= 0) {
236 return(filecnt);
238 switch(*ap++) {
239 case '\1': /* cleanup - data sent was bad (whatever that means) */
240 break;
241 case '\2': /* read control file */
242 bsize = atoi((const char *)ap);
243 cntrlfd = tempfile();
244 if (readfile(cntrlfd, bsize) < 0) {
245 close(cntrlfd);
246 NAK();
247 return(0);
249 break;
250 case '\3': /* read data file */
251 bsize = atoi((const char *)ap);
252 datafd[filecnt] = tempfile();
253 if (readfile(datafd[filecnt], bsize) < 0) {
254 close(datafd[filecnt]);
255 NAK();
256 return(0);
258 filecnt++;
259 break;
260 default:
261 error("protocol error <%d>\n", *(ap-1));
262 NAK();
265 return(filecnt);
268 struct jobinfo *
269 getjobinfo(int fd)
271 unsigned char *ap;
272 int rv;
273 static struct jobinfo info;
275 if (fd < 0) error("getjobinfo: bad file descriptor\n");
276 if (lseek(fd, 0L, 0) < 0) {
277 error("error seeking in temp file\n");
278 exit(7);
280 /* the following strings should be < NAMELEN or else they will not
281 * be null terminated.
282 */
283 strncpy(info.user, "daemon", NAMELEN);
284 strncpy(info.host, "nowhere", NAMELEN);
285 /* there may be a space after the name and host. It will be filtered out
286 * by CPYFIELD.
287 */
288 while ((rv=readline(fd)) > 0) {
289 ap = lnbuf;
290 ap[rv-1] = '\0'; /* remove newline from string */
291 switch (*ap) {
292 case 'H':
293 if (ap[1] == '\0')
294 strncpy(info.host, "unknown", NAMELEN);
295 else
296 strncpy(info.host, (const char *)&ap[1], NAMELEN);
297 info.host[strlen(info.host)] = '\0';
298 break;
299 case 'P':
300 if (ap[1] == '\0')
301 strncpy(info.user, "unknown", NAMELEN);
302 else
303 strncpy(info.user, (const char *)&ap[1], NAMELEN);
304 info.user[strlen(info.user)] = '\0';
305 break;
308 return(&info);
311 void
312 alarmhandler(int sig) {
313 signal(sig, alarmhandler);
314 error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
317 void
318 nop(int sig)
323 int
324 main()
326 unsigned char *ap, *bp, *cp, *savbufpnt;
327 int i, blen, rv, saveflg, savargcnt;
328 struct jobinfo *jinfop;
330 signal(SIGHUP, SIG_IGN); /* SIGHUP not in lcc */
331 signal(SIGALRM, alarmhandler); /* SIGALRM not in lcc */
332 signal(SIGCHLD, nop); /* so that wait will get us something */
333 cp = argvstr;
334 /* setup argv[0] for exec */
335 argvals[argcnt++] = cp;
336 for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++);
337 *cp++ = '\0';
338 /* get the first line sent and parse it as arguments for lp */
339 if ((rv=readline(0)) < 0)
340 exit(1);
341 bp = lnbuf;
342 /* setup the remaining arguments */
343 /* check for BSD style request */
344 /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */
345 switch (*bp) {
346 case '\001':
347 case '\003':
348 case '\004':
349 bp++; /* drop the ctrl character from the input */
350 argvals[argcnt++] = cp;
351 *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0'; /* -q */
352 argvals[argcnt++] = cp;
353 *cp++ = '-'; *cp++ = 'd'; /* -d */
354 CPYFIELD(bp, cp); /* printer */
355 *cp++ = '\0';
356 break;
357 case '\002':
358 bp++; /* drop the ctrl character from the input */
359 argvals[argcnt++] = cp;
360 *cp++ = '-'; *cp++ = 'd'; /* -d */
361 CPYFIELD(bp, cp); /* printer */
362 *cp++ = '\0';
363 ACK();
364 savargcnt = argcnt;
365 savbufpnt = cp;
366 while ((rv=getfiles())) {
367 jinfop = getjobinfo(cntrlfd);
368 close(cntrlfd);
369 argcnt = savargcnt;
370 cp = savbufpnt;
371 argvals[argcnt++] = cp;
372 *cp++ = '-'; *cp++ = 'M'; /* -M */
373 bp = (unsigned char *)jinfop->host;
374 CPYFIELD(bp, cp); /* host name */
375 *cp++ = '\0';
376 argvals[argcnt++] = cp;
377 *cp++ = '-'; *cp++ = 'u'; /* -u */
378 bp = (unsigned char *)jinfop->user;
379 CPYFIELD(bp, cp); /* user name */
380 *cp++ = '\0';
381 for(i=0;i<rv;i++)
382 forklp(datafd[i]);
384 exit(0);
385 case '\005':
386 bp++; /* drop the ctrl character from the input */
387 argvals[argcnt++] = cp;
388 *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0'; /* -k */
389 argvals[argcnt++] = cp;
390 *cp++ = '-'; *cp++ = 'd'; /* -d */
391 CPYFIELD(bp, cp); /* printer */
392 *cp++ = '\0';
393 argvals[argcnt++] = cp;
394 *cp++ = '-'; ap = cp; *cp++ = 'u'; /* -u */
395 CPYFIELD(bp, cp); /* username */
397 /* deal with bug in lprng where the username is not supplied
398 */
399 if (ap == (cp-1)) {
400 ap = (unsigned char *)"none";
401 CPYFIELD(ap, cp);
404 *cp++ = '\0';
405 datafd[0] = tempfile();
406 blen = strlen((const char *)bp);
407 if (write(datafd[0], bp, blen) != blen) {
408 error("write error\n");
409 exit(6);
411 if (write(datafd[0], "\n", 1) != 1) {
412 error("write error\n");
413 exit(6);
415 break;
416 default:
417 /* otherwise get my lp arguments */
418 do {
419 /* move to next non-white space */
420 while (*bp==' '||*bp=='\t')
421 ++bp;
422 if (*bp=='\n') continue;
423 /* only accept arguments beginning with -
424 * this is done to prevent the printing of
425 * local files from the destination host
426 */
427 if (*bp=='-') {
428 argvals[argcnt++] = cp;
429 saveflg = 1;
430 } else
431 saveflg = 0;
432 /* move to next white space copying text to argument buffer */
433 while (*bp!=' ' && *bp!='\t' && *bp!='\n'
434 && *bp!='\0') {
435 *cp = *bp++;
436 cp += saveflg;
438 *cp = '\0';
439 cp += saveflg;
440 } while (*bp!='\n' && *bp!='\0');
441 if (readline(0) < 0) exit(7);
442 datafd[0] = tempfile();
443 if(readfile(datafd[0], atoi((const char *)lnbuf)) < 0) {
444 error("readfile failed\n");
445 exit(8);
448 forklp(datafd[0]);
449 exit(0);