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