]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/lp/lpdaemon.c
usb lib: add maxpkt and ntds to Altc struct
[plan9front.git] / sys / src / cmd / lp / lpdaemon.c
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
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
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
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
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
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
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 *
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
317 alarmhandler(int sig) {
318         signal(sig, alarmhandler);
319         error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
320 }
321
322 void
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 }