printjob.c revision 1.11 1 /* $NetBSD: printjob.c,v 1.11 1996/12/09 09:57:46 mrg Exp $ */
2 /*
3 * Copyright (c) 1983, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1983, 1993\n\
39 The Regents of the University of California. All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 static char sccsid[] = "@(#)printjob.c 8.2 (Berkeley) 4/16/94";
44 #endif /* not lint */
45
46
47 /*
48 * printjob -- print jobs in the queue.
49 *
50 * NOTE: the lock file is used to pass information to lpq and lprm.
51 * it does not need to be removed because file locks are dynamic.
52 */
53
54 #include <sys/param.h>
55 #include <sys/wait.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58
59 #include <pwd.h>
60 #include <unistd.h>
61 #include <signal.h>
62 #include <termios.h>
63 #include <syslog.h>
64 #include <fcntl.h>
65 #include <dirent.h>
66 #include <errno.h>
67 #include <stdio.h>
68 #include <string.h>
69 #include <stdlib.h>
70 #include "lp.h"
71 #include "lp.local.h"
72 #include "pathnames.h"
73 #include "extern.h"
74
75 #define DORETURN 0 /* absorb fork error */
76 #define DOABORT 1 /* abort if dofork fails */
77
78 /*
79 * Error tokens
80 */
81 #define REPRINT -2
82 #define ERROR -1
83 #define OK 0
84 #define FATALERR 1
85 #define NOACCT 2
86 #define FILTERERR 3
87 #define ACCESS 4
88
89 static dev_t fdev; /* device of file pointed to by symlink */
90 static ino_t fino; /* inode of file pointed to by symlink */
91 static FILE *cfp; /* control file */
92 static int child; /* id of any filters */
93 static int lfd; /* lock file descriptor */
94 static int ofd; /* output filter file descriptor */
95 static int ofilter; /* id of output filter, if any */
96 static int pfd; /* prstatic inter file descriptor */
97 static int pid; /* pid of lpd process */
98 static int prchild; /* id of pr process */
99 static int remote; /* true if sending files to remote */
100 static char title[80]; /* ``pr'' title */
101 static int tof; /* true if at top of form */
102
103 static char class[32]; /* classification field */
104 static char fromhost[32]; /* user's host machine */
105 /* indentation size in static characters */
106 static char indent[10] = "-i0";
107 static char jobname[100]; /* job or file name */
108 static char length[10] = "-l"; /* page length in lines */
109 static char logname[32]; /* user's login name */
110 static char pxlength[10] = "-y"; /* page length in pixels */
111 static char pxwidth[10] = "-x"; /* page width in pixels */
112 static char tempfile[] = "errsXXXXXX"; /* file name for filter output */
113 static char width[10] = "-w"; /* page width in static characters */
114
115 static void abortpr __P((int));
116 static void banner __P((char *, char *));
117 static int dofork __P((int));
118 static int dropit __P((int));
119 static void init __P((void));
120 static void openpr __P((void));
121 static int print __P((int, char *));
122 static int printit __P((char *));
123 static void pstatus __P((const char *, ...));
124 static char response __P((void));
125 static void scan_out __P((int, char *, int));
126 static char *scnline __P((int, char *, int));
127 static int sendfile __P((int, char *));
128 static int sendit __P((char *));
129 static void sendmail __P((char *, int));
130 static void set_ttyflags __P((struct termios *));
131 static void setty __P((void));
132
133 void
134 printjob()
135 {
136 struct stat stb;
137 register struct queue *q, **qp;
138 struct queue **queue;
139 register int i, nitems;
140 long pidoff;
141 int count = 0;
142
143 init(); /* set up capabilities */
144 (void)write(1, "", 1); /* ack that daemon is started */
145 (void)close(2); /* set up log file */
146 if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
147 syslog(LOG_ERR, "%s: %m", LF);
148 (void)open(_PATH_DEVNULL, O_WRONLY);
149 }
150 setgid(getegid());
151 pid = getpid(); /* for use with lprm */
152 setpgrp(0, pid);
153 signal(SIGHUP, abortpr);
154 signal(SIGINT, abortpr);
155 signal(SIGQUIT, abortpr);
156 signal(SIGTERM, abortpr);
157
158 (void)mktemp(tempfile);
159
160 /*
161 * uses short form file names
162 */
163 if (chdir(SD) < 0) {
164 syslog(LOG_ERR, "%s: %m", SD);
165 exit(1);
166 }
167 if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
168 exit(0); /* printing disabled */
169 lfd = open(LO, O_WRONLY|O_CREAT, 0644);
170 if (lfd < 0) {
171 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
172 exit(1);
173 }
174 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
175 if (errno == EWOULDBLOCK) /* active deamon present */
176 exit(0);
177 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
178 exit(1);
179 }
180 ftruncate(lfd, 0);
181 /*
182 * write process id for others to know
183 */
184 pidoff = i = snprintf(line, sizeof(line), "%u\n", pid);
185 if (write(lfd, line, i) != i) {
186 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
187 exit(1);
188 }
189 /*
190 * search the spool directory for work and sort by queue order.
191 */
192 if ((nitems = getq(&queue)) < 0) {
193 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
194 exit(1);
195 }
196 if (nitems == 0) /* no work to do */
197 exit(0);
198 if (stb.st_mode & 01) { /* reset queue flag */
199 if (fchmod(lfd, stb.st_mode & 0776) < 0)
200 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
201 }
202 openpr(); /* open printer or remote */
203 again:
204 /*
205 * we found something to do now do it --
206 * write the name of the current control file into the lock file
207 * so the spool queue program can tell what we're working on
208 */
209 for (qp = queue; nitems--; free((char *) q)) {
210 q = *qp++;
211 if (stat(q->q_name, &stb) < 0)
212 continue;
213 restart:
214 (void)lseek(lfd, (off_t)pidoff, 0);
215 i = snprintf(line, sizeof(line), "%s\n", q->q_name);
216 if (write(lfd, line, i) != i)
217 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
218 if (!remote)
219 i = printit(q->q_name);
220 else
221 i = sendit(q->q_name);
222 /*
223 * Check to see if we are supposed to stop printing or
224 * if we are to rebuild the queue.
225 */
226 if (fstat(lfd, &stb) == 0) {
227 /* stop printing before starting next job? */
228 if (stb.st_mode & 0100)
229 goto done;
230 /* rebuild queue (after lpc topq) */
231 if (stb.st_mode & 01) {
232 for (free((char *) q); nitems--; free((char *) q))
233 q = *qp++;
234 if (fchmod(lfd, stb.st_mode & 0776) < 0)
235 syslog(LOG_WARNING, "%s: %s: %m",
236 printer, LO);
237 break;
238 }
239 }
240 if (i == OK) /* file ok and printed */
241 count++;
242 else if (i == REPRINT) { /* try reprinting the job */
243 syslog(LOG_INFO, "restarting %s", printer);
244 if (ofilter > 0) {
245 kill(ofilter, SIGCONT); /* to be sure */
246 (void)close(ofd);
247 while ((i = wait(0)) > 0 && i != ofilter)
248 ;
249 ofilter = 0;
250 }
251 (void)close(pfd); /* close printer */
252 if (ftruncate(lfd, pidoff) < 0)
253 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
254 openpr(); /* try to reopen printer */
255 goto restart;
256 }
257 }
258 free((char *) queue);
259 /*
260 * search the spool directory for more work.
261 */
262 if ((nitems = getq(&queue)) < 0) {
263 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
264 exit(1);
265 }
266 if (nitems == 0) { /* no more work to do */
267 done:
268 if (count > 0) { /* Files actually printed */
269 if (!SF && !tof)
270 (void)write(ofd, FF, strlen(FF));
271 if (TR != NULL) /* output trailer */
272 (void)write(ofd, TR, strlen(TR));
273 }
274 (void)unlink(tempfile);
275 exit(0);
276 }
277 goto again;
278 }
279
280 #define FONTLEN 50
281 char fonts[4][FONTLEN]; /* fonts for troff */
282
283 char ifonts[4][40] = {
284 _PATH_VFONTR,
285 _PATH_VFONTI,
286 _PATH_VFONTB,
287 _PATH_VFONTS,
288 };
289
290 /*
291 * The remaining part is the reading of the control file (cf)
292 * and performing the various actions.
293 */
294 static int
295 printit(file)
296 char *file;
297 {
298 register int i;
299 char *cp;
300 int bombed = OK;
301
302 /*
303 * open control file; ignore if no longer there.
304 */
305 if ((cfp = fopen(file, "r")) == NULL) {
306 syslog(LOG_INFO, "%s: %s: %m", printer, file);
307 return(OK);
308 }
309 /*
310 * Reset troff fonts.
311 */
312 for (i = 0; i < 4; i++)
313 strncpy(fonts[i], ifonts[i], FONTLEN);
314 (void)snprintf(&width[2], sizeof(width) - 2, "%d", PW);
315 indent[2] = '0';
316 indent[3] = '\0';
317
318 /*
319 * read the control file for work to do
320 *
321 * file format -- first character in the line is a command
322 * rest of the line is the argument.
323 * valid commands are:
324 *
325 * S -- "stat info" for symbolic link protection
326 * J -- "job name" on banner page
327 * C -- "class name" on banner page
328 * L -- "literal" user's name to print on banner
329 * T -- "title" for pr
330 * H -- "host name" of machine where lpr was done
331 * P -- "person" user's login name
332 * I -- "indent" amount to indent output
333 * f -- "file name" name of text file to print
334 * l -- "file name" text file with control chars
335 * p -- "file name" text file to print with pr(1)
336 * t -- "file name" troff(1) file to print
337 * n -- "file name" ditroff(1) file to print
338 * d -- "file name" dvi file to print
339 * g -- "file name" plot(1G) file to print
340 * v -- "file name" plain raster file to print
341 * c -- "file name" cifplot file to print
342 * 1 -- "R font file" for troff
343 * 2 -- "I font file" for troff
344 * 3 -- "B font file" for troff
345 * 4 -- "S font file" for troff
346 * N -- "name" of file (used by lpq)
347 * U -- "unlink" name of file to remove
348 * (after we print it. (Pass 2 only)).
349 * M -- "mail" to user when done printing
350 *
351 * getline reads a line and expands tabs to blanks
352 */
353
354 /* pass 1 */
355
356 while (getline(cfp))
357 switch (line[0]) {
358 case 'H':
359 strncpy(fromhost, line+1, sizeof(fromhost) - 1);
360 if (class[0] == '\0')
361 strncpy(class, line+1, sizeof(class) - 1);
362 continue;
363
364 case 'P':
365 strncpy(logname, line+1, sizeof(logname) - 1);
366 if (RS) { /* restricted */
367 if (getpwnam(logname) == NULL) {
368 bombed = NOACCT;
369 sendmail(line+1, bombed);
370 goto pass2;
371 }
372 }
373 continue;
374
375 case 'S':
376 cp = line+1;
377 i = 0;
378 while (*cp >= '0' && *cp <= '9')
379 i = i * 10 + (*cp++ - '0');
380 fdev = i;
381 cp++;
382 i = 0;
383 while (*cp >= '0' && *cp <= '9')
384 i = i * 10 + (*cp++ - '0');
385 fino = i;
386 continue;
387
388 case 'J':
389 if (line[1] != '\0')
390 strncpy(jobname, line+1, sizeof(jobname) - 1);
391 else {
392 jobname[0] = ' ';
393 jobname[1] = '\0';
394 }
395 continue;
396
397 case 'C':
398 if (line[1] != '\0')
399 strncpy(class, line+1, sizeof(class) - 1);
400 else if (class[0] == '\0')
401 gethostname(class, sizeof(class));
402 continue;
403
404 case 'T': /* header title for pr */
405 strncpy(title, line+1, sizeof(title) - 1);
406 continue;
407
408 case 'L': /* identification line */
409 if (!SH && !HL)
410 banner(line+1, jobname);
411 continue;
412
413 case '1': /* troff fonts */
414 case '2':
415 case '3':
416 case '4':
417 if (line[1] != '\0')
418 strncpy(fonts[line[0]-'1'], line+1, FONTLEN - 1);
419 continue;
420
421 case 'W': /* page width */
422 strncpy(width+2, line+1, sizeof(width) - 3);
423 continue;
424
425 case 'I': /* indent amount */
426 strncpy(indent+2, line+1, sizeof(indent) - 3);
427 continue;
428
429 default: /* some file to print */
430 switch (i = print(line[0], line+1)) {
431 case ERROR:
432 if (bombed == OK)
433 bombed = FATALERR;
434 break;
435 case REPRINT:
436 (void)fclose(cfp);
437 return(REPRINT);
438 case FILTERERR:
439 case ACCESS:
440 bombed = i;
441 sendmail(logname, bombed);
442 }
443 title[0] = '\0';
444 continue;
445
446 case 'N':
447 case 'U':
448 case 'M':
449 continue;
450 }
451
452 /* pass 2 */
453
454 pass2:
455 fseek(cfp, 0L, 0);
456 while (getline(cfp))
457 switch (line[0]) {
458 case 'L': /* identification line */
459 if (!SH && HL)
460 banner(line+1, jobname);
461 continue;
462
463 case 'M':
464 if (bombed < NOACCT) /* already sent if >= NOACCT */
465 sendmail(line+1, bombed);
466 continue;
467
468 case 'U':
469 (void)unlink(line+1);
470 }
471 /*
472 * clean-up in case another control file exists
473 */
474 (void)fclose(cfp);
475 (void)unlink(file);
476 return(bombed == OK ? OK : ERROR);
477 }
478
479 /*
480 * Print a file.
481 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
482 * Return -1 if a non-recoverable error occured,
483 * 2 if the filter detected some errors (but printed the job anyway),
484 * 1 if we should try to reprint this job and
485 * 0 if all is well.
486 * Note: all filters take stdin as the file, stdout as the printer,
487 * stderr as the log file, and must not ignore SIGINT.
488 */
489 static int
490 print(format, file)
491 int format;
492 char *file;
493 {
494 register int n;
495 register char *prog;
496 int fi, fo;
497 FILE *fp;
498 char *av[15], buf[BUFSIZ];
499 int pid, p[2], stopped = 0;
500 union wait status;
501 struct stat stb;
502
503 if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
504 return(ERROR);
505 /*
506 * Check to see if data file is a symbolic link. If so, it should
507 * still point to the same file or someone is trying to print
508 * something he shouldn't.
509 */
510 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
511 (stb.st_dev != fdev || stb.st_ino != fino))
512 return(ACCESS);
513 if (!SF && !tof) { /* start on a fresh page */
514 (void)write(ofd, FF, strlen(FF));
515 tof = 1;
516 }
517 if (IF == NULL && (format == 'f' || format == 'l')) {
518 tof = 0;
519 while ((n = read(fi, buf, BUFSIZ)) > 0)
520 if (write(ofd, buf, n) != n) {
521 (void)close(fi);
522 return(REPRINT);
523 }
524 (void)close(fi);
525 return(OK);
526 }
527 switch (format) {
528 case 'p': /* print file using 'pr' */
529 if (IF == NULL) { /* use output filter */
530 prog = _PATH_PR;
531 av[0] = "pr";
532 av[1] = width;
533 av[2] = length;
534 av[3] = "-h";
535 av[4] = *title ? title : " ";
536 av[5] = 0;
537 fo = ofd;
538 goto start;
539 }
540 pipe(p);
541 if ((prchild = dofork(DORETURN)) == 0) { /* child */
542 dup2(fi, 0); /* file is stdin */
543 dup2(p[1], 1); /* pipe is stdout */
544 for (n = 3; n < NOFILE; n++)
545 (void)close(n);
546 execl(_PATH_PR, "pr", width, length,
547 "-h", *title ? title : " ", 0);
548 syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
549 exit(2);
550 }
551 (void)close(p[1]); /* close output side */
552 (void)close(fi);
553 if (prchild < 0) {
554 prchild = 0;
555 (void)close(p[0]);
556 return(ERROR);
557 }
558 fi = p[0]; /* use pipe for input */
559 case 'f': /* print plain text file */
560 prog = IF;
561 av[1] = width;
562 av[2] = length;
563 av[3] = indent;
564 n = 4;
565 break;
566 case 'l': /* like 'f' but pass control characters */
567 prog = IF;
568 av[1] = "-c";
569 av[2] = width;
570 av[3] = length;
571 av[4] = indent;
572 n = 5;
573 break;
574 case 'r': /* print a fortran text file */
575 prog = RF;
576 av[1] = width;
577 av[2] = length;
578 n = 3;
579 break;
580 case 't': /* print troff output */
581 case 'n': /* print ditroff output */
582 case 'd': /* print tex output */
583 (void)unlink(".railmag");
584 if ((fo = creat(".railmag", FILMOD)) < 0) {
585 syslog(LOG_ERR, "%s: cannot create .railmag", printer);
586 (void)unlink(".railmag");
587 } else {
588 for (n = 0; n < 4; n++) {
589 if (fonts[n][0] != '/')
590 (void)write(fo, _PATH_VFONT,
591 sizeof(_PATH_VFONT) - 1);
592 (void)write(fo, fonts[n], strlen(fonts[n]));
593 (void)write(fo, "\n", 1);
594 }
595 (void)close(fo);
596 }
597 prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
598 av[1] = pxwidth;
599 av[2] = pxlength;
600 n = 3;
601 break;
602 case 'c': /* print cifplot output */
603 prog = CF;
604 av[1] = pxwidth;
605 av[2] = pxlength;
606 n = 3;
607 break;
608 case 'g': /* print plot(1G) output */
609 prog = GF;
610 av[1] = pxwidth;
611 av[2] = pxlength;
612 n = 3;
613 break;
614 case 'v': /* print raster output */
615 prog = VF;
616 av[1] = pxwidth;
617 av[2] = pxlength;
618 n = 3;
619 break;
620 default:
621 (void)close(fi);
622 syslog(LOG_ERR, "%s: illegal format character '%c'",
623 printer, format);
624 return(ERROR);
625 }
626 if ((av[0] = rindex(prog, '/')) != NULL)
627 av[0]++;
628 else
629 av[0] = prog;
630 av[n++] = "-n";
631 av[n++] = logname;
632 av[n++] = "-h";
633 av[n++] = fromhost;
634 av[n++] = AF;
635 av[n] = 0;
636 fo = pfd;
637 if (ofilter > 0) { /* stop output filter */
638 write(ofd, "\031\1", 2);
639 while ((pid =
640 wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
641 ;
642 if (status.w_stopval != WSTOPPED) {
643 (void)close(fi);
644 syslog(LOG_WARNING, "%s: output filter died (%d)",
645 printer, status.w_retcode);
646 return(REPRINT);
647 }
648 stopped++;
649 }
650 start:
651 if ((child = dofork(DORETURN)) == 0) { /* child */
652 dup2(fi, 0);
653 dup2(fo, 1);
654 n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
655 if (n >= 0)
656 dup2(n, 2);
657 for (n = 3; n < NOFILE; n++)
658 (void)close(n);
659 execv(prog, av);
660 syslog(LOG_ERR, "cannot execv %s", prog);
661 exit(2);
662 }
663 (void)close(fi);
664 if (child < 0)
665 status.w_retcode = 100;
666 else
667 while ((pid = wait((int *)&status)) > 0 && pid != child)
668 ;
669 child = 0;
670 prchild = 0;
671 if (stopped) { /* restart output filter */
672 if (kill(ofilter, SIGCONT) < 0) {
673 syslog(LOG_ERR, "cannot restart output filter");
674 exit(1);
675 }
676 }
677 tof = 0;
678
679 /* Copy filter output to "lf" logfile */
680 if (fp = fopen(tempfile, "r")) {
681 while (fgets(buf, sizeof(buf), fp))
682 fputs(buf, stderr);
683 fclose(fp);
684 }
685
686 if (!WIFEXITED(status)) {
687 syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
688 printer, format, status.w_termsig);
689 return(ERROR);
690 }
691 switch (status.w_retcode) {
692 case 0:
693 tof = 1;
694 return(OK);
695 case 1:
696 return(REPRINT);
697 default:
698 syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
699 printer, format, status.w_retcode);
700 case 2:
701 return(ERROR);
702 }
703 }
704
705 /*
706 * Send the daemon control file (cf) and any data files.
707 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
708 * 0 if all is well.
709 */
710 static int
711 sendit(file)
712 char *file;
713 {
714 register int i, err = OK;
715 char *cp, last[BUFSIZ];
716
717 /*
718 * open control file
719 */
720 if ((cfp = fopen(file, "r")) == NULL)
721 return(OK);
722 /*
723 * read the control file for work to do
724 *
725 * file format -- first character in the line is a command
726 * rest of the line is the argument.
727 * commands of interest are:
728 *
729 * a-z -- "file name" name of file to print
730 * U -- "unlink" name of file to remove
731 * (after we print it. (Pass 2 only)).
732 */
733
734 /*
735 * pass 1
736 */
737 while (getline(cfp)) {
738 again:
739 if (line[0] == 'S') {
740 cp = line+1;
741 i = 0;
742 while (*cp >= '0' && *cp <= '9')
743 i = i * 10 + (*cp++ - '0');
744 fdev = i;
745 cp++;
746 i = 0;
747 while (*cp >= '0' && *cp <= '9')
748 i = i * 10 + (*cp++ - '0');
749 fino = i;
750 continue;
751 }
752 if (line[0] >= 'a' && line[0] <= 'z') {
753 strncpy(last, line, sizeof(last) - 1);
754 while (i = getline(cfp))
755 if (strcmp(last, line))
756 break;
757 switch (sendfile('\3', last+1)) {
758 case OK:
759 if (i)
760 goto again;
761 break;
762 case REPRINT:
763 (void)fclose(cfp);
764 return(REPRINT);
765 case ACCESS:
766 sendmail(logname, ACCESS);
767 case ERROR:
768 err = ERROR;
769 }
770 break;
771 }
772 }
773 if (err == OK && sendfile('\2', file) > 0) {
774 (void)fclose(cfp);
775 return(REPRINT);
776 }
777 /*
778 * pass 2
779 */
780 fseek(cfp, 0L, 0);
781 while (getline(cfp))
782 if (line[0] == 'U')
783 (void)unlink(line+1);
784 /*
785 * clean-up in case another control file exists
786 */
787 (void)fclose(cfp);
788 (void)unlink(file);
789 return(err);
790 }
791
792 /*
793 * Send a data file to the remote machine and spool it.
794 * Return positive if we should try resending.
795 */
796 static int
797 sendfile(type, file)
798 int type;
799 char *file;
800 {
801 register int f, i, amt;
802 struct stat stb;
803 char buf[BUFSIZ];
804 int sizerr, resp;
805
806 if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
807 return(ERROR);
808 /*
809 * Check to see if data file is a symbolic link. If so, it should
810 * still point to the same file or someone is trying to print something
811 * he shouldn't.
812 */
813 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
814 (stb.st_dev != fdev || stb.st_ino != fino))
815 return(ACCESS);
816 amt = snprintf(buf, sizeof(buf), "%c%qd %s\n", type, stb.st_size, file);
817 for (i = 0; ; i++) {
818 if (write(pfd, buf, amt) != amt ||
819 (resp = response()) < 0 || resp == '\1') {
820 (void)close(f);
821 return(REPRINT);
822 } else if (resp == '\0')
823 break;
824 if (i == 0)
825 pstatus("no space on remote; waiting for queue to drain");
826 if (i == 10)
827 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
828 printer, RM);
829 sleep(5 * 60);
830 }
831 if (i)
832 pstatus("sending to %s", RM);
833 sizerr = 0;
834 for (i = 0; i < stb.st_size; i += BUFSIZ) {
835 amt = BUFSIZ;
836 if (i + amt > stb.st_size)
837 amt = stb.st_size - i;
838 if (sizerr == 0 && read(f, buf, amt) != amt)
839 sizerr = 1;
840 if (write(pfd, buf, amt) != amt) {
841 (void)close(f);
842 return(REPRINT);
843 }
844 }
845
846
847
848
849 (void)close(f);
850 if (sizerr) {
851 syslog(LOG_INFO, "%s: %s: changed size", printer, file);
852 /* tell recvjob to ignore this file */
853 (void)write(pfd, "\1", 1);
854 return(ERROR);
855 }
856 if (write(pfd, "", 1) != 1 || response())
857 return(REPRINT);
858 return(OK);
859 }
860
861 /*
862 * Check to make sure there have been no errors and that both programs
863 * are in sync with eachother.
864 * Return non-zero if the connection was lost.
865 */
866 static char
867 response()
868 {
869 char resp;
870
871 if (read(pfd, &resp, 1) != 1) {
872 syslog(LOG_INFO, "%s: lost connection", printer);
873 return(-1);
874 }
875 return(resp);
876 }
877
878 /*
879 * Banner printing stuff
880 */
881 static void
882 banner(name1, name2)
883 char *name1, *name2;
884 {
885 time_t tvec;
886 extern char *ctime();
887
888 time(&tvec);
889 if (!SF && !tof)
890 (void)write(ofd, FF, strlen(FF));
891 if (SB) { /* short banner only */
892 if (class[0]) {
893 (void)write(ofd, class, strlen(class));
894 (void)write(ofd, ":", 1);
895 }
896 (void)write(ofd, name1, strlen(name1));
897 (void)write(ofd, " Job: ", 7);
898 (void)write(ofd, name2, strlen(name2));
899 (void)write(ofd, " Date: ", 8);
900 (void)write(ofd, ctime(&tvec), 24);
901 (void)write(ofd, "\n", 1);
902 } else { /* normal banner */
903 (void)write(ofd, "\n\n\n", 3);
904 scan_out(ofd, name1, '\0');
905 (void)write(ofd, "\n\n", 2);
906 scan_out(ofd, name2, '\0');
907 if (class[0]) {
908 (void)write(ofd,"\n\n\n",3);
909 scan_out(ofd, class, '\0');
910 }
911 (void)write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
912 (void)write(ofd, name2, strlen(name2));
913 (void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
914 (void)write(ofd, ctime(&tvec), 24);
915 (void)write(ofd, "\n", 1);
916 }
917 if (!SF)
918 (void)write(ofd, FF, strlen(FF));
919 tof = 1;
920 }
921
922 static char *
923 scnline(key, p, c)
924 register int key;
925 register char *p;
926 int c;
927 {
928 register scnwidth;
929
930 for (scnwidth = WIDTH; --scnwidth;) {
931 key <<= 1;
932 *p++ = key & 0200 ? c : BACKGND;
933 }
934 return (p);
935 }
936
937 #define TRC(q) (((q)-' ')&0177)
938
939 static void
940 scan_out(scfd, scsp, dlm)
941 int scfd, dlm;
942 char *scsp;
943 {
944 register char *strp;
945 register nchrs, j;
946 char outbuf[LINELEN+1], *sp, c, cc;
947 int d, scnhgt;
948 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
949
950 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
951 strp = &outbuf[0];
952 sp = scsp;
953 for (nchrs = 0; ; ) {
954 d = dropit(c = TRC(cc = *sp++));
955 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
956 for (j = WIDTH; --j;)
957 *strp++ = BACKGND;
958 else
959 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
960 if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
961 break;
962 *strp++ = BACKGND;
963 *strp++ = BACKGND;
964 }
965 while (*--strp == BACKGND && strp >= outbuf)
966 ;
967 strp++;
968 *strp++ = '\n';
969 (void)write(scfd, outbuf, strp-outbuf);
970 }
971 }
972
973 static int
974 dropit(c)
975 int c;
976 {
977 switch(c) {
978
979 case TRC('_'):
980 case TRC(';'):
981 case TRC(','):
982 case TRC('g'):
983 case TRC('j'):
984 case TRC('p'):
985 case TRC('q'):
986 case TRC('y'):
987 return (DROP);
988
989 default:
990 return (0);
991 }
992 }
993
994 /*
995 * sendmail ---
996 * tell people about job completion
997 */
998 static void
999 sendmail(user, bombed)
1000 char *user;
1001 int bombed;
1002 {
1003 register int i;
1004 int p[2], s;
1005 register char *cp;
1006 char buf[100];
1007 struct stat stb;
1008 FILE *fp;
1009
1010 pipe(p);
1011 if ((s = dofork(DORETURN)) == 0) { /* child */
1012 dup2(p[0], 0);
1013 for (i = 3; i < NOFILE; i++)
1014 (void)close(i);
1015 if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1016 cp++;
1017 else
1018 cp = _PATH_SENDMAIL;
1019 (void)snprintf(buf, sizeof(buf), "%s@%s", user, fromhost);
1020 execl(_PATH_SENDMAIL, cp, buf, 0);
1021 exit(0);
1022 } else if (s > 0) { /* parent */
1023 dup2(p[1], 1);
1024 printf("To: %s@%s\n", user, fromhost);
1025 printf("Subject: printer job\n\n");
1026 printf("Your printer job ");
1027 if (*jobname)
1028 printf("(%s) ", jobname);
1029 switch (bombed) {
1030 case OK:
1031 printf("\ncompleted successfully\n");
1032 break;
1033 default:
1034 case FATALERR:
1035 printf("\ncould not be printed\n");
1036 break;
1037 case NOACCT:
1038 printf("\ncould not be printed without an account on %s\n", host);
1039 break;
1040 case FILTERERR:
1041 if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1042 (fp = fopen(tempfile, "r")) == NULL) {
1043 printf("\nwas printed but had some errors\n");
1044 break;
1045 }
1046 printf("\nwas printed but had the following errors:\n");
1047 while ((i = getc(fp)) != EOF)
1048 putchar(i);
1049 (void)fclose(fp);
1050 break;
1051 case ACCESS:
1052 printf("\nwas not printed because it was not linked to the original file\n");
1053 }
1054 fflush(stdout);
1055 (void)close(1);
1056 }
1057 (void)close(p[0]);
1058 (void)close(p[1]);
1059 wait(&s);
1060 }
1061
1062 /*
1063 * dofork - fork with retries on failure
1064 */
1065 static int
1066 dofork(action)
1067 int action;
1068 {
1069 register int i, pid;
1070 struct passwd *pw;
1071
1072 for (i = 0; i < 20; i++) {
1073 if ((pid = fork()) < 0) {
1074 sleep((unsigned)(i*i));
1075 continue;
1076 }
1077 /*
1078 * Child should run as daemon instead of root
1079 */
1080 if (pid == 0) {
1081 pw = getpwuid(DU);
1082 if (pw == 0) {
1083 syslog(LOG_ERR, "uid %d not in password file",
1084 DU);
1085 break;
1086 }
1087 initgroups(pw->pw_name, pw->pw_gid);
1088 setgid(pw->pw_gid);
1089 setuid(DU);
1090 }
1091 return (pid);
1092 }
1093 syslog(LOG_ERR, "can't fork");
1094
1095 switch (action) {
1096 case DORETURN:
1097 return (-1);
1098 default:
1099 syslog(LOG_ERR, "bad action (%d) to dofork", action);
1100 /*FALL THRU*/
1101 case DOABORT:
1102 exit(1);
1103 }
1104 /*NOTREACHED*/
1105 }
1106
1107 /*
1108 * Kill child processes to abort current job.
1109 */
1110 static void
1111 abortpr(signo)
1112 int signo;
1113 {
1114 (void)unlink(tempfile);
1115 kill(0, SIGINT);
1116 if (ofilter > 0)
1117 kill(ofilter, SIGCONT);
1118 while (wait(NULL) > 0)
1119 ;
1120 exit(0);
1121 }
1122
1123 static void
1124 init()
1125 {
1126 int status;
1127 char *s;
1128
1129 if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1130 syslog(LOG_ERR, "can't open printer description file");
1131 exit(1);
1132 } else if (status == -1) {
1133 syslog(LOG_ERR, "unknown printer: %s", printer);
1134 exit(1);
1135 } else if (status == -3)
1136 fatal("potential reference loop detected in printcap file");
1137
1138 if (cgetstr(bp, "lp", &LP) == -1)
1139 LP = _PATH_DEFDEVLP;
1140 if (cgetstr(bp, "rp", &RP) == -1)
1141 RP = DEFLP;
1142 if (cgetstr(bp, "lo", &LO) == -1)
1143 LO = DEFLOCK;
1144 if (cgetstr(bp, "st", &ST) == -1)
1145 ST = DEFSTAT;
1146 if (cgetstr(bp, "lf", &LF) == -1)
1147 LF = _PATH_CONSOLE;
1148 if (cgetstr(bp, "sd", &SD) == -1)
1149 SD = _PATH_DEFSPOOL;
1150 if (cgetnum(bp, "du", &DU) < 0)
1151 DU = DEFUID;
1152 if (cgetstr(bp,"ff", &FF) == -1)
1153 FF = DEFFF;
1154 if (cgetnum(bp, "pw", &PW) < 0)
1155 PW = DEFWIDTH;
1156 (void)snprintf(&width[2], sizeof(width) - 2, "%d", PW);
1157 if (cgetnum(bp, "pl", &PL) < 0)
1158 PL = DEFLENGTH;
1159 (void)snprintf(&length[2], sizeof(length) - 2, "%d", PL);
1160 if (cgetnum(bp,"px", &PX) < 0)
1161 PX = 0;
1162 (void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%d", PX);
1163 if (cgetnum(bp, "py", &PY) < 0)
1164 PY = 0;
1165 (void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%d", PY);
1166 cgetstr(bp, "rm", &RM);
1167 if (s = checkremote())
1168 syslog(LOG_WARNING, s);
1169
1170 cgetstr(bp, "af", &AF);
1171 cgetstr(bp, "of", &OF);
1172 cgetstr(bp, "if", &IF);
1173 cgetstr(bp, "rf", &RF);
1174 cgetstr(bp, "tf", &TF);
1175 cgetstr(bp, "nf", &NF);
1176 cgetstr(bp, "df", &DF);
1177 cgetstr(bp, "gf", &GF);
1178 cgetstr(bp, "vf", &VF);
1179 cgetstr(bp, "cf", &CF);
1180 cgetstr(bp, "tr", &TR);
1181
1182 RS = (cgetcap(bp, "rs", ':') != NULL);
1183 SF = (cgetcap(bp, "sf", ':') != NULL);
1184 SH = (cgetcap(bp, "sh", ':') != NULL);
1185 SB = (cgetcap(bp, "sb", ':') != NULL);
1186 HL = (cgetcap(bp, "hl", ':') != NULL);
1187 RW = (cgetcap(bp, "rw", ':') != NULL);
1188
1189 cgetnum(bp, "br", &BR);
1190 if (cgetnum(bp, "fc", &FC) < 0)
1191 FC = 0;
1192 if (cgetnum(bp, "fs", &FS) < 0)
1193 FS = 0;
1194 if (cgetnum(bp, "xc", &XC) < 0)
1195 XC = 0;
1196 if (cgetnum(bp, "xs", &XS) < 0)
1197 XS = 0;
1198 cgetstr(bp, "ms", &MS);
1199
1200 tof = (cgetcap(bp, "fo", ':') == NULL);
1201 }
1202
1203 /*
1204 * Acquire line printer or remote connection.
1205 */
1206 static void
1207 openpr()
1208 {
1209 register int i, n;
1210 int resp;
1211
1212 if (!sendtorem && *LP) {
1213 for (i = 1; ; i = i < 32 ? i << 1 : i) {
1214 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1215 if (pfd >= 0)
1216 break;
1217 if (errno == ENOENT) {
1218 syslog(LOG_ERR, "%s: %m", LP);
1219 exit(1);
1220 }
1221 if (i == 1)
1222 pstatus("waiting for %s to become ready (offline ?)", printer);
1223 sleep(i);
1224 }
1225 if (isatty(pfd))
1226 setty();
1227 pstatus("%s is ready and printing", printer);
1228 } else if (RM != NULL) {
1229 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1230 resp = -1;
1231 pfd = getport(RM);
1232 if (pfd >= 0) {
1233 n = snprintf(line, sizeof(line), "\2%s\n", RP);
1234 if (write(pfd, line, n) == n &&
1235 (resp = response()) == '\0')
1236 break;
1237 (void)close(pfd);
1238 }
1239 if (i == 1) {
1240 if (resp < 0)
1241 pstatus("waiting for %s to come up", RM);
1242 else {
1243 pstatus("waiting for queue to be enabled on %s", RM);
1244 i = 256;
1245 }
1246 }
1247 sleep(i);
1248 }
1249 pstatus("sending to %s", RM);
1250 remote = 1;
1251 } else {
1252 syslog(LOG_ERR, "%s: no line printer device or host name",
1253 printer);
1254 exit(1);
1255 }
1256 /*
1257 * Start up an output filter, if needed.
1258 */
1259 if (!remote && OF) {
1260 int p[2];
1261 char *cp;
1262
1263 pipe(p);
1264 if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1265 dup2(p[0], 0); /* pipe is std in */
1266 dup2(pfd, 1); /* printer is std out */
1267 for (i = 3; i < NOFILE; i++)
1268 (void)close(i);
1269 if ((cp = rindex(OF, '/')) == NULL)
1270 cp = OF;
1271 else
1272 cp++;
1273 execl(OF, cp, width, length, 0);
1274 syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1275 exit(1);
1276 }
1277 (void)close(p[0]); /* close input side */
1278 ofd = p[1]; /* use pipe for output */
1279 } else {
1280 ofd = pfd;
1281 ofilter = 0;
1282 }
1283 }
1284
1285 #if !defined(__NetBSD__)
1286 struct bauds {
1287 int baud;
1288 int speed;
1289 } bauds[] = {
1290 50, B50,
1291 75, B75,
1292 110, B110,
1293 134, B134,
1294 150, B150,
1295 200, B200,
1296 300, B300,
1297 600, B600,
1298 1200, B1200,
1299 1800, B1800,
1300 2400, B2400,
1301 4800, B4800,
1302 9600, B9600,
1303 19200, B19200,
1304 38400, B38400,
1305 0, 0
1306 };
1307 #endif
1308
1309 /*
1310 * setup tty lines.
1311 */
1312 static void
1313 setty()
1314 {
1315 struct info i;
1316 char **argv, **ap, *p, *val;
1317
1318 i.fd = pfd;
1319 i.set = i.wset = 0;
1320 if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
1321 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1322 exit(1);
1323 }
1324 if (tcgetattr(i.fd, &i.t) < 0) {
1325 syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1326 exit(1);
1327 }
1328 if (BR > 0) {
1329 #if !defined(__NetBSD__)
1330 register struct bauds *bp;
1331 for (bp = bauds; bp->baud; bp++)
1332 if (BR == bp->baud)
1333 break;
1334 if (!bp->baud) {
1335 syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1336 exit(1);
1337 }
1338 cfsetspeed(&i.t, bp->speed);
1339 #else
1340 cfsetspeed(&i.t, BR);
1341 #endif
1342 i.set = 1;
1343 }
1344 if (MS) {
1345 if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
1346 syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
1347 exit(1);
1348 }
1349 if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
1350 syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
1351 printer);
1352
1353 argv = (char **)calloc(256, sizeof(char *));
1354 if (argv == NULL) {
1355 syslog(LOG_ERR, "%s: calloc: %m", printer);
1356 exit(1);
1357 }
1358 p = strdup(MS);
1359 ap = argv;
1360 while ((val = strsep(&p, " \t,")) != NULL) {
1361 *ap++ = strdup(val);
1362 }
1363
1364 for (; *argv; ++argv) {
1365 if (ksearch(&argv, &i))
1366 continue;
1367 if (msearch(&argv, &i))
1368 continue;
1369 syslog(LOG_INFO, "%s: unknown stty flag: %s",
1370 printer, *argv);
1371 }
1372 } else {
1373 if (FC) {
1374 sttyclearflags(&i.t, FC);
1375 i.set = 1;
1376 }
1377 if (FS) {
1378 sttysetflags(&i.t, FS);
1379 i.set = 1;
1380 }
1381 if (XC) {
1382 sttyclearlflags(&i.t, XC);
1383 i.set = 1;
1384 }
1385 if (XS) {
1386 sttysetlflags(&i.t, XS);
1387 i.set = 1;
1388 }
1389 }
1390
1391 if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
1392 syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1393 exit(1);
1394 }
1395 if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
1396 syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
1397 return;
1398 }
1399
1400 #if __STDC__
1401 #include <stdarg.h>
1402 #else
1403 #include <varargs.h>
1404 #endif
1405
1406 void
1407 #if __STDC__
1408 pstatus(const char *msg, ...)
1409 #else
1410 pstatus(msg, va_alist)
1411 char *msg;
1412 va_dcl
1413 #endif
1414 {
1415 register int fd;
1416 char buf[BUFSIZ];
1417 va_list ap;
1418 #if __STDC__
1419 va_start(ap, msg);
1420 #else
1421 va_start(ap);
1422 #endif
1423
1424 umask(0);
1425 fd = open(ST, O_WRONLY|O_CREAT, 0664);
1426 if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1427 syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1428 exit(1);
1429 }
1430 ftruncate(fd, 0);
1431 (void)vsnprintf(buf, sizeof(buf) - 2, msg, ap);
1432 va_end(ap);
1433 strncat(buf, "\n", 2);
1434 (void)write(fd, buf, strlen(buf));
1435 (void)close(fd);
1436 }
1437