printjob.c revision 1.10 1 /* $NetBSD: printjob.c,v 1.10 1996/06/30 23:55:49 jtc 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 sprintf(line, "%u\n", pid);
185 pidoff = i = strlen(line);
186 if (write(lfd, line, i) != i) {
187 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
188 exit(1);
189 }
190 /*
191 * search the spool directory for work and sort by queue order.
192 */
193 if ((nitems = getq(&queue)) < 0) {
194 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
195 exit(1);
196 }
197 if (nitems == 0) /* no work to do */
198 exit(0);
199 if (stb.st_mode & 01) { /* reset queue flag */
200 if (fchmod(lfd, stb.st_mode & 0776) < 0)
201 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
202 }
203 openpr(); /* open printer or remote */
204 again:
205 /*
206 * we found something to do now do it --
207 * write the name of the current control file into the lock file
208 * so the spool queue program can tell what we're working on
209 */
210 for (qp = queue; nitems--; free((char *) q)) {
211 q = *qp++;
212 if (stat(q->q_name, &stb) < 0)
213 continue;
214 restart:
215 (void) lseek(lfd, (off_t)pidoff, 0);
216 (void) sprintf(line, "%s\n", q->q_name);
217 i = strlen(line);
218 if (write(lfd, line, i) != i)
219 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
220 if (!remote)
221 i = printit(q->q_name);
222 else
223 i = sendit(q->q_name);
224 /*
225 * Check to see if we are supposed to stop printing or
226 * if we are to rebuild the queue.
227 */
228 if (fstat(lfd, &stb) == 0) {
229 /* stop printing before starting next job? */
230 if (stb.st_mode & 0100)
231 goto done;
232 /* rebuild queue (after lpc topq) */
233 if (stb.st_mode & 01) {
234 for (free((char *) q); nitems--; free((char *) q))
235 q = *qp++;
236 if (fchmod(lfd, stb.st_mode & 0776) < 0)
237 syslog(LOG_WARNING, "%s: %s: %m",
238 printer, LO);
239 break;
240 }
241 }
242 if (i == OK) /* file ok and printed */
243 count++;
244 else if (i == REPRINT) { /* try reprinting the job */
245 syslog(LOG_INFO, "restarting %s", printer);
246 if (ofilter > 0) {
247 kill(ofilter, SIGCONT); /* to be sure */
248 (void) close(ofd);
249 while ((i = wait(0)) > 0 && i != ofilter)
250 ;
251 ofilter = 0;
252 }
253 (void) close(pfd); /* close printer */
254 if (ftruncate(lfd, pidoff) < 0)
255 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
256 openpr(); /* try to reopen printer */
257 goto restart;
258 }
259 }
260 free((char *) queue);
261 /*
262 * search the spool directory for more work.
263 */
264 if ((nitems = getq(&queue)) < 0) {
265 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
266 exit(1);
267 }
268 if (nitems == 0) { /* no more work to do */
269 done:
270 if (count > 0) { /* Files actually printed */
271 if (!SF && !tof)
272 (void) write(ofd, FF, strlen(FF));
273 if (TR != NULL) /* output trailer */
274 (void) write(ofd, TR, strlen(TR));
275 }
276 (void) unlink(tempfile);
277 exit(0);
278 }
279 goto again;
280 }
281
282 char fonts[4][50]; /* fonts for troff */
283
284 char ifonts[4][40] = {
285 _PATH_VFONTR,
286 _PATH_VFONTI,
287 _PATH_VFONTB,
288 _PATH_VFONTS,
289 };
290
291 /*
292 * The remaining part is the reading of the control file (cf)
293 * and performing the various actions.
294 */
295 static int
296 printit(file)
297 char *file;
298 {
299 register int i;
300 char *cp;
301 int bombed = OK;
302
303 /*
304 * open control file; ignore if no longer there.
305 */
306 if ((cfp = fopen(file, "r")) == NULL) {
307 syslog(LOG_INFO, "%s: %s: %m", printer, file);
308 return(OK);
309 }
310 /*
311 * Reset troff fonts.
312 */
313 for (i = 0; i < 4; i++)
314 strcpy(fonts[i], ifonts[i]);
315 sprintf(&width[2], "%d", PW);
316 strcpy(indent+2, "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 strcpy(fromhost, line+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 strcpy(jobname, " ");
393 continue;
394
395 case 'C':
396 if (line[1] != '\0')
397 strncpy(class, line+1, sizeof(class)-1);
398 else if (class[0] == '\0')
399 gethostname(class, sizeof(class));
400 continue;
401
402 case 'T': /* header title for pr */
403 strncpy(title, line+1, sizeof(title)-1);
404 continue;
405
406 case 'L': /* identification line */
407 if (!SH && !HL)
408 banner(line+1, jobname);
409 continue;
410
411 case '1': /* troff fonts */
412 case '2':
413 case '3':
414 case '4':
415 if (line[1] != '\0')
416 strcpy(fonts[line[0]-'1'], line+1);
417 continue;
418
419 case 'W': /* page width */
420 strncpy(width+2, line+1, sizeof(width)-3);
421 continue;
422
423 case 'I': /* indent amount */
424 strncpy(indent+2, line+1, sizeof(indent)-3);
425 continue;
426
427 default: /* some file to print */
428 switch (i = print(line[0], line+1)) {
429 case ERROR:
430 if (bombed == OK)
431 bombed = FATALERR;
432 break;
433 case REPRINT:
434 (void) fclose(cfp);
435 return(REPRINT);
436 case FILTERERR:
437 case ACCESS:
438 bombed = i;
439 sendmail(logname, bombed);
440 }
441 title[0] = '\0';
442 continue;
443
444 case 'N':
445 case 'U':
446 case 'M':
447 continue;
448 }
449
450 /* pass 2 */
451
452 pass2:
453 fseek(cfp, 0L, 0);
454 while (getline(cfp))
455 switch (line[0]) {
456 case 'L': /* identification line */
457 if (!SH && HL)
458 banner(line+1, jobname);
459 continue;
460
461 case 'M':
462 if (bombed < NOACCT) /* already sent if >= NOACCT */
463 sendmail(line+1, bombed);
464 continue;
465
466 case 'U':
467 (void) unlink(line+1);
468 }
469 /*
470 * clean-up in case another control file exists
471 */
472 (void) fclose(cfp);
473 (void) unlink(file);
474 return(bombed == OK ? OK : ERROR);
475 }
476
477 /*
478 * Print a file.
479 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
480 * Return -1 if a non-recoverable error occured,
481 * 2 if the filter detected some errors (but printed the job anyway),
482 * 1 if we should try to reprint this job and
483 * 0 if all is well.
484 * Note: all filters take stdin as the file, stdout as the printer,
485 * stderr as the log file, and must not ignore SIGINT.
486 */
487 static int
488 print(format, file)
489 int format;
490 char *file;
491 {
492 register int n;
493 register char *prog;
494 int fi, fo;
495 FILE *fp;
496 char *av[15], buf[BUFSIZ];
497 int pid, p[2], stopped = 0;
498 union wait status;
499 struct stat stb;
500
501 if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
502 return(ERROR);
503 /*
504 * Check to see if data file is a symbolic link. If so, it should
505 * still point to the same file or someone is trying to print
506 * something he shouldn't.
507 */
508 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
509 (stb.st_dev != fdev || stb.st_ino != fino))
510 return(ACCESS);
511 if (!SF && !tof) { /* start on a fresh page */
512 (void) write(ofd, FF, strlen(FF));
513 tof = 1;
514 }
515 if (IF == NULL && (format == 'f' || format == 'l')) {
516 tof = 0;
517 while ((n = read(fi, buf, BUFSIZ)) > 0)
518 if (write(ofd, buf, n) != n) {
519 (void) close(fi);
520 return(REPRINT);
521 }
522 (void) close(fi);
523 return(OK);
524 }
525 switch (format) {
526 case 'p': /* print file using 'pr' */
527 if (IF == NULL) { /* use output filter */
528 prog = _PATH_PR;
529 av[0] = "pr";
530 av[1] = width;
531 av[2] = length;
532 av[3] = "-h";
533 av[4] = *title ? title : " ";
534 av[5] = 0;
535 fo = ofd;
536 goto start;
537 }
538 pipe(p);
539 if ((prchild = dofork(DORETURN)) == 0) { /* child */
540 dup2(fi, 0); /* file is stdin */
541 dup2(p[1], 1); /* pipe is stdout */
542 for (n = 3; n < NOFILE; n++)
543 (void) close(n);
544 execl(_PATH_PR, "pr", width, length,
545 "-h", *title ? title : " ", 0);
546 syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
547 exit(2);
548 }
549 (void) close(p[1]); /* close output side */
550 (void) close(fi);
551 if (prchild < 0) {
552 prchild = 0;
553 (void) close(p[0]);
554 return(ERROR);
555 }
556 fi = p[0]; /* use pipe for input */
557 case 'f': /* print plain text file */
558 prog = IF;
559 av[1] = width;
560 av[2] = length;
561 av[3] = indent;
562 n = 4;
563 break;
564 case 'l': /* like 'f' but pass control characters */
565 prog = IF;
566 av[1] = "-c";
567 av[2] = width;
568 av[3] = length;
569 av[4] = indent;
570 n = 5;
571 break;
572 case 'r': /* print a fortran text file */
573 prog = RF;
574 av[1] = width;
575 av[2] = length;
576 n = 3;
577 break;
578 case 't': /* print troff output */
579 case 'n': /* print ditroff output */
580 case 'd': /* print tex output */
581 (void) unlink(".railmag");
582 if ((fo = creat(".railmag", FILMOD)) < 0) {
583 syslog(LOG_ERR, "%s: cannot create .railmag", printer);
584 (void) unlink(".railmag");
585 } else {
586 for (n = 0; n < 4; n++) {
587 if (fonts[n][0] != '/')
588 (void) write(fo, _PATH_VFONT,
589 sizeof(_PATH_VFONT) - 1);
590 (void) write(fo, fonts[n], strlen(fonts[n]));
591 (void) write(fo, "\n", 1);
592 }
593 (void) close(fo);
594 }
595 prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
596 av[1] = pxwidth;
597 av[2] = pxlength;
598 n = 3;
599 break;
600 case 'c': /* print cifplot output */
601 prog = CF;
602 av[1] = pxwidth;
603 av[2] = pxlength;
604 n = 3;
605 break;
606 case 'g': /* print plot(1G) output */
607 prog = GF;
608 av[1] = pxwidth;
609 av[2] = pxlength;
610 n = 3;
611 break;
612 case 'v': /* print raster output */
613 prog = VF;
614 av[1] = pxwidth;
615 av[2] = pxlength;
616 n = 3;
617 break;
618 default:
619 (void) close(fi);
620 syslog(LOG_ERR, "%s: illegal format character '%c'",
621 printer, format);
622 return(ERROR);
623 }
624 if ((av[0] = rindex(prog, '/')) != NULL)
625 av[0]++;
626 else
627 av[0] = prog;
628 av[n++] = "-n";
629 av[n++] = logname;
630 av[n++] = "-h";
631 av[n++] = fromhost;
632 av[n++] = AF;
633 av[n] = 0;
634 fo = pfd;
635 if (ofilter > 0) { /* stop output filter */
636 write(ofd, "\031\1", 2);
637 while ((pid =
638 wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
639 ;
640 if (status.w_stopval != WSTOPPED) {
641 (void) close(fi);
642 syslog(LOG_WARNING, "%s: output filter died (%d)",
643 printer, status.w_retcode);
644 return(REPRINT);
645 }
646 stopped++;
647 }
648 start:
649 if ((child = dofork(DORETURN)) == 0) { /* child */
650 dup2(fi, 0);
651 dup2(fo, 1);
652 n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
653 if (n >= 0)
654 dup2(n, 2);
655 for (n = 3; n < NOFILE; n++)
656 (void) close(n);
657 execv(prog, av);
658 syslog(LOG_ERR, "cannot execv %s", prog);
659 exit(2);
660 }
661 (void) close(fi);
662 if (child < 0)
663 status.w_retcode = 100;
664 else
665 while ((pid = wait((int *)&status)) > 0 && pid != child)
666 ;
667 child = 0;
668 prchild = 0;
669 if (stopped) { /* restart output filter */
670 if (kill(ofilter, SIGCONT) < 0) {
671 syslog(LOG_ERR, "cannot restart output filter");
672 exit(1);
673 }
674 }
675 tof = 0;
676
677 /* Copy filter output to "lf" logfile */
678 if (fp = fopen(tempfile, "r")) {
679 while (fgets(buf, sizeof(buf), fp))
680 fputs(buf, stderr);
681 fclose(fp);
682 }
683
684 if (!WIFEXITED(status)) {
685 syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
686 printer, format, status.w_termsig);
687 return(ERROR);
688 }
689 switch (status.w_retcode) {
690 case 0:
691 tof = 1;
692 return(OK);
693 case 1:
694 return(REPRINT);
695 default:
696 syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
697 printer, format, status.w_retcode);
698 case 2:
699 return(ERROR);
700 }
701 }
702
703 /*
704 * Send the daemon control file (cf) and any data files.
705 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
706 * 0 if all is well.
707 */
708 static int
709 sendit(file)
710 char *file;
711 {
712 register int i, err = OK;
713 char *cp, last[BUFSIZ];
714
715 /*
716 * open control file
717 */
718 if ((cfp = fopen(file, "r")) == NULL)
719 return(OK);
720 /*
721 * read the control file for work to do
722 *
723 * file format -- first character in the line is a command
724 * rest of the line is the argument.
725 * commands of interest are:
726 *
727 * a-z -- "file name" name of file to print
728 * U -- "unlink" name of file to remove
729 * (after we print it. (Pass 2 only)).
730 */
731
732 /*
733 * pass 1
734 */
735 while (getline(cfp)) {
736 again:
737 if (line[0] == 'S') {
738 cp = line+1;
739 i = 0;
740 while (*cp >= '0' && *cp <= '9')
741 i = i * 10 + (*cp++ - '0');
742 fdev = i;
743 cp++;
744 i = 0;
745 while (*cp >= '0' && *cp <= '9')
746 i = i * 10 + (*cp++ - '0');
747 fino = i;
748 continue;
749 }
750 if (line[0] >= 'a' && line[0] <= 'z') {
751 strcpy(last, line);
752 while (i = getline(cfp))
753 if (strcmp(last, line))
754 break;
755 switch (sendfile('\3', last+1)) {
756 case OK:
757 if (i)
758 goto again;
759 break;
760 case REPRINT:
761 (void) fclose(cfp);
762 return(REPRINT);
763 case ACCESS:
764 sendmail(logname, ACCESS);
765 case ERROR:
766 err = ERROR;
767 }
768 break;
769 }
770 }
771 if (err == OK && sendfile('\2', file) > 0) {
772 (void) fclose(cfp);
773 return(REPRINT);
774 }
775 /*
776 * pass 2
777 */
778 fseek(cfp, 0L, 0);
779 while (getline(cfp))
780 if (line[0] == 'U')
781 (void) unlink(line+1);
782 /*
783 * clean-up in case another control file exists
784 */
785 (void) fclose(cfp);
786 (void) unlink(file);
787 return(err);
788 }
789
790 /*
791 * Send a data file to the remote machine and spool it.
792 * Return positive if we should try resending.
793 */
794 static int
795 sendfile(type, file)
796 int type;
797 char *file;
798 {
799 register int f, i, amt;
800 struct stat stb;
801 char buf[BUFSIZ];
802 int sizerr, resp;
803
804 if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
805 return(ERROR);
806 /*
807 * Check to see if data file is a symbolic link. If so, it should
808 * still point to the same file or someone is trying to print something
809 * he shouldn't.
810 */
811 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
812 (stb.st_dev != fdev || stb.st_ino != fino))
813 return(ACCESS);
814 (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
815 amt = strlen(buf);
816 for (i = 0; ; i++) {
817 if (write(pfd, buf, amt) != amt ||
818 (resp = response()) < 0 || resp == '\1') {
819 (void) close(f);
820 return(REPRINT);
821 } else if (resp == '\0')
822 break;
823 if (i == 0)
824 pstatus("no space on remote; waiting for queue to drain");
825 if (i == 10)
826 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
827 printer, RM);
828 sleep(5 * 60);
829 }
830 if (i)
831 pstatus("sending to %s", RM);
832 sizerr = 0;
833 for (i = 0; i < stb.st_size; i += BUFSIZ) {
834 amt = BUFSIZ;
835 if (i + amt > stb.st_size)
836 amt = stb.st_size - i;
837 if (sizerr == 0 && read(f, buf, amt) != amt)
838 sizerr = 1;
839 if (write(pfd, buf, amt) != amt) {
840 (void) close(f);
841 return(REPRINT);
842 }
843 }
844
845
846
847
848 (void) close(f);
849 if (sizerr) {
850 syslog(LOG_INFO, "%s: %s: changed size", printer, file);
851 /* tell recvjob to ignore this file */
852 (void) write(pfd, "\1", 1);
853 return(ERROR);
854 }
855 if (write(pfd, "", 1) != 1 || response())
856 return(REPRINT);
857 return(OK);
858 }
859
860 /*
861 * Check to make sure there have been no errors and that both programs
862 * are in sync with eachother.
863 * Return non-zero if the connection was lost.
864 */
865 static char
866 response()
867 {
868 char resp;
869
870 if (read(pfd, &resp, 1) != 1) {
871 syslog(LOG_INFO, "%s: lost connection", printer);
872 return(-1);
873 }
874 return(resp);
875 }
876
877 /*
878 * Banner printing stuff
879 */
880 static void
881 banner(name1, name2)
882 char *name1, *name2;
883 {
884 time_t tvec;
885 extern char *ctime();
886
887 time(&tvec);
888 if (!SF && !tof)
889 (void) write(ofd, FF, strlen(FF));
890 if (SB) { /* short banner only */
891 if (class[0]) {
892 (void) write(ofd, class, strlen(class));
893 (void) write(ofd, ":", 1);
894 }
895 (void) write(ofd, name1, strlen(name1));
896 (void) write(ofd, " Job: ", 7);
897 (void) write(ofd, name2, strlen(name2));
898 (void) write(ofd, " Date: ", 8);
899 (void) write(ofd, ctime(&tvec), 24);
900 (void) write(ofd, "\n", 1);
901 } else { /* normal banner */
902 (void) write(ofd, "\n\n\n", 3);
903 scan_out(ofd, name1, '\0');
904 (void) write(ofd, "\n\n", 2);
905 scan_out(ofd, name2, '\0');
906 if (class[0]) {
907 (void) write(ofd,"\n\n\n",3);
908 scan_out(ofd, class, '\0');
909 }
910 (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
911 (void) write(ofd, name2, strlen(name2));
912 (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
913 (void) write(ofd, ctime(&tvec), 24);
914 (void) write(ofd, "\n", 1);
915 }
916 if (!SF)
917 (void) write(ofd, FF, strlen(FF));
918 tof = 1;
919 }
920
921 static char *
922 scnline(key, p, c)
923 register int key;
924 register char *p;
925 int c;
926 {
927 register scnwidth;
928
929 for (scnwidth = WIDTH; --scnwidth;) {
930 key <<= 1;
931 *p++ = key & 0200 ? c : BACKGND;
932 }
933 return (p);
934 }
935
936 #define TRC(q) (((q)-' ')&0177)
937
938 static void
939 scan_out(scfd, scsp, dlm)
940 int scfd, dlm;
941 char *scsp;
942 {
943 register char *strp;
944 register nchrs, j;
945 char outbuf[LINELEN+1], *sp, c, cc;
946 int d, scnhgt;
947 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
948
949 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
950 strp = &outbuf[0];
951 sp = scsp;
952 for (nchrs = 0; ; ) {
953 d = dropit(c = TRC(cc = *sp++));
954 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
955 for (j = WIDTH; --j;)
956 *strp++ = BACKGND;
957 else
958 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
959 if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
960 break;
961 *strp++ = BACKGND;
962 *strp++ = BACKGND;
963 }
964 while (*--strp == BACKGND && strp >= outbuf)
965 ;
966 strp++;
967 *strp++ = '\n';
968 (void) write(scfd, outbuf, strp-outbuf);
969 }
970 }
971
972 static int
973 dropit(c)
974 int c;
975 {
976 switch(c) {
977
978 case TRC('_'):
979 case TRC(';'):
980 case TRC(','):
981 case TRC('g'):
982 case TRC('j'):
983 case TRC('p'):
984 case TRC('q'):
985 case TRC('y'):
986 return (DROP);
987
988 default:
989 return (0);
990 }
991 }
992
993 /*
994 * sendmail ---
995 * tell people about job completion
996 */
997 static void
998 sendmail(user, bombed)
999 char *user;
1000 int bombed;
1001 {
1002 register int i;
1003 int p[2], s;
1004 register char *cp;
1005 char buf[100];
1006 struct stat stb;
1007 FILE *fp;
1008
1009 pipe(p);
1010 if ((s = dofork(DORETURN)) == 0) { /* child */
1011 dup2(p[0], 0);
1012 for (i = 3; i < NOFILE; i++)
1013 (void) close(i);
1014 if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1015 cp++;
1016 else
1017 cp = _PATH_SENDMAIL;
1018 sprintf(buf, "%s@%s", user, fromhost);
1019 execl(_PATH_SENDMAIL, cp, buf, 0);
1020 exit(0);
1021 } else if (s > 0) { /* parent */
1022 dup2(p[1], 1);
1023 printf("To: %s@%s\n", user, fromhost);
1024 printf("Subject: printer job\n\n");
1025 printf("Your printer job ");
1026 if (*jobname)
1027 printf("(%s) ", jobname);
1028 switch (bombed) {
1029 case OK:
1030 printf("\ncompleted successfully\n");
1031 break;
1032 default:
1033 case FATALERR:
1034 printf("\ncould not be printed\n");
1035 break;
1036 case NOACCT:
1037 printf("\ncould not be printed without an account on %s\n", host);
1038 break;
1039 case FILTERERR:
1040 if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1041 (fp = fopen(tempfile, "r")) == NULL) {
1042 printf("\nwas printed but had some errors\n");
1043 break;
1044 }
1045 printf("\nwas printed but had the following errors:\n");
1046 while ((i = getc(fp)) != EOF)
1047 putchar(i);
1048 (void) fclose(fp);
1049 break;
1050 case ACCESS:
1051 printf("\nwas not printed because it was not linked to the original file\n");
1052 }
1053 fflush(stdout);
1054 (void) close(1);
1055 }
1056 (void) close(p[0]);
1057 (void) close(p[1]);
1058 wait(&s);
1059 }
1060
1061 /*
1062 * dofork - fork with retries on failure
1063 */
1064 static int
1065 dofork(action)
1066 int action;
1067 {
1068 register int i, pid;
1069 struct passwd *pw;
1070
1071 for (i = 0; i < 20; i++) {
1072 if ((pid = fork()) < 0) {
1073 sleep((unsigned)(i*i));
1074 continue;
1075 }
1076 /*
1077 * Child should run as daemon instead of root
1078 */
1079 if (pid == 0) {
1080 pw = getpwuid(DU);
1081 if (pw == 0) {
1082 syslog(LOG_ERR, "uid %d not in password file",
1083 DU);
1084 break;
1085 }
1086 initgroups(pw->pw_name, pw->pw_gid);
1087 setgid(pw->pw_gid);
1088 setuid(DU);
1089 }
1090 return (pid);
1091 }
1092 syslog(LOG_ERR, "can't fork");
1093
1094 switch (action) {
1095 case DORETURN:
1096 return (-1);
1097 default:
1098 syslog(LOG_ERR, "bad action (%d) to dofork", action);
1099 /*FALL THRU*/
1100 case DOABORT:
1101 exit(1);
1102 }
1103 /*NOTREACHED*/
1104 }
1105
1106 /*
1107 * Kill child processes to abort current job.
1108 */
1109 static void
1110 abortpr(signo)
1111 int signo;
1112 {
1113 (void) unlink(tempfile);
1114 kill(0, SIGINT);
1115 if (ofilter > 0)
1116 kill(ofilter, SIGCONT);
1117 while (wait(NULL) > 0)
1118 ;
1119 exit(0);
1120 }
1121
1122 static void
1123 init()
1124 {
1125 int status;
1126 char *s;
1127
1128 if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1129 syslog(LOG_ERR, "can't open printer description file");
1130 exit(1);
1131 } else if (status == -1) {
1132 syslog(LOG_ERR, "unknown printer: %s", printer);
1133 exit(1);
1134 } else if (status == -3)
1135 fatal("potential reference loop detected in printcap file");
1136
1137 if (cgetstr(bp, "lp", &LP) == -1)
1138 LP = _PATH_DEFDEVLP;
1139 if (cgetstr(bp, "rp", &RP) == -1)
1140 RP = DEFLP;
1141 if (cgetstr(bp, "lo", &LO) == -1)
1142 LO = DEFLOCK;
1143 if (cgetstr(bp, "st", &ST) == -1)
1144 ST = DEFSTAT;
1145 if (cgetstr(bp, "lf", &LF) == -1)
1146 LF = _PATH_CONSOLE;
1147 if (cgetstr(bp, "sd", &SD) == -1)
1148 SD = _PATH_DEFSPOOL;
1149 if (cgetnum(bp, "du", &DU) < 0)
1150 DU = DEFUID;
1151 if (cgetstr(bp,"ff", &FF) == -1)
1152 FF = DEFFF;
1153 if (cgetnum(bp, "pw", &PW) < 0)
1154 PW = DEFWIDTH;
1155 sprintf(&width[2], "%d", PW);
1156 if (cgetnum(bp, "pl", &PL) < 0)
1157 PL = DEFLENGTH;
1158 sprintf(&length[2], "%d", PL);
1159 if (cgetnum(bp,"px", &PX) < 0)
1160 PX = 0;
1161 sprintf(&pxwidth[2], "%d", PX);
1162 if (cgetnum(bp, "py", &PY) < 0)
1163 PY = 0;
1164 sprintf(&pxlength[2], "%d", PY);
1165 cgetstr(bp, "rm", &RM);
1166 if (s = checkremote())
1167 syslog(LOG_WARNING, s);
1168
1169 cgetstr(bp, "af", &AF);
1170 cgetstr(bp, "of", &OF);
1171 cgetstr(bp, "if", &IF);
1172 cgetstr(bp, "rf", &RF);
1173 cgetstr(bp, "tf", &TF);
1174 cgetstr(bp, "nf", &NF);
1175 cgetstr(bp, "df", &DF);
1176 cgetstr(bp, "gf", &GF);
1177 cgetstr(bp, "vf", &VF);
1178 cgetstr(bp, "cf", &CF);
1179 cgetstr(bp, "tr", &TR);
1180
1181 RS = (cgetcap(bp, "rs", ':') != NULL);
1182 SF = (cgetcap(bp, "sf", ':') != NULL);
1183 SH = (cgetcap(bp, "sh", ':') != NULL);
1184 SB = (cgetcap(bp, "sb", ':') != NULL);
1185 HL = (cgetcap(bp, "hl", ':') != NULL);
1186 RW = (cgetcap(bp, "rw", ':') != NULL);
1187
1188 cgetnum(bp, "br", &BR);
1189 if (cgetnum(bp, "fc", &FC) < 0)
1190 FC = 0;
1191 if (cgetnum(bp, "fs", &FS) < 0)
1192 FS = 0;
1193 if (cgetnum(bp, "xc", &XC) < 0)
1194 XC = 0;
1195 if (cgetnum(bp, "xs", &XS) < 0)
1196 XS = 0;
1197 cgetstr(bp, "ms", &MS);
1198
1199 tof = (cgetcap(bp, "fo", ':') == NULL);
1200 }
1201
1202 /*
1203 * Acquire line printer or remote connection.
1204 */
1205 static void
1206 openpr()
1207 {
1208 register int i, n;
1209 int resp;
1210
1211 if (!sendtorem && *LP) {
1212 for (i = 1; ; i = i < 32 ? i << 1 : i) {
1213 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1214 if (pfd >= 0)
1215 break;
1216 if (errno == ENOENT) {
1217 syslog(LOG_ERR, "%s: %m", LP);
1218 exit(1);
1219 }
1220 if (i == 1)
1221 pstatus("waiting for %s to become ready (offline ?)", printer);
1222 sleep(i);
1223 }
1224 if (isatty(pfd))
1225 setty();
1226 pstatus("%s is ready and printing", printer);
1227 } else if (RM != NULL) {
1228 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1229 resp = -1;
1230 pfd = getport(RM);
1231 if (pfd >= 0) {
1232 (void) sprintf(line, "\2%s\n", RP);
1233 n = strlen(line);
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), msg, ap);
1432 va_end(ap);
1433 strcat(buf, "\n");
1434 (void) write(fd, buf, strlen(buf));
1435 (void) close(fd);
1436 }
1437