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