printjob.c revision 1.12 1 /* $NetBSD: printjob.c,v 1.12 1997/03/22 03:20:38 lukem 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 unlink(tempfile);
655 n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
656 if (n >= 0)
657 dup2(n, 2);
658 for (n = 3; n < NOFILE; n++)
659 (void)close(n);
660 execv(prog, av);
661 syslog(LOG_ERR, "cannot execv %s", prog);
662 exit(2);
663 }
664 (void)close(fi);
665 if (child < 0)
666 status.w_retcode = 100;
667 else
668 while ((pid = wait((int *)&status)) > 0 && pid != child)
669 ;
670 child = 0;
671 prchild = 0;
672 if (stopped) { /* restart output filter */
673 if (kill(ofilter, SIGCONT) < 0) {
674 syslog(LOG_ERR, "cannot restart output filter");
675 exit(1);
676 }
677 }
678 tof = 0;
679
680 /* Copy filter output to "lf" logfile */
681 if (fp = fopen(tempfile, "r")) {
682 while (fgets(buf, sizeof(buf), fp))
683 fputs(buf, stderr);
684 fclose(fp);
685 }
686
687 if (!WIFEXITED(status)) {
688 syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
689 printer, format, status.w_termsig);
690 return(ERROR);
691 }
692 switch (status.w_retcode) {
693 case 0:
694 tof = 1;
695 return(OK);
696 case 1:
697 return(REPRINT);
698 default:
699 syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
700 printer, format, status.w_retcode);
701 case 2:
702 return(ERROR);
703 }
704 }
705
706 /*
707 * Send the daemon control file (cf) and any data files.
708 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
709 * 0 if all is well.
710 */
711 static int
712 sendit(file)
713 char *file;
714 {
715 register int i, err = OK;
716 char *cp, last[BUFSIZ];
717
718 /*
719 * open control file
720 */
721 if ((cfp = fopen(file, "r")) == NULL)
722 return(OK);
723 /*
724 * read the control file for work to do
725 *
726 * file format -- first character in the line is a command
727 * rest of the line is the argument.
728 * commands of interest are:
729 *
730 * a-z -- "file name" name of file to print
731 * U -- "unlink" name of file to remove
732 * (after we print it. (Pass 2 only)).
733 */
734
735 /*
736 * pass 1
737 */
738 while (getline(cfp)) {
739 again:
740 if (line[0] == 'S') {
741 cp = line+1;
742 i = 0;
743 while (*cp >= '0' && *cp <= '9')
744 i = i * 10 + (*cp++ - '0');
745 fdev = i;
746 cp++;
747 i = 0;
748 while (*cp >= '0' && *cp <= '9')
749 i = i * 10 + (*cp++ - '0');
750 fino = i;
751 continue;
752 }
753 if (line[0] >= 'a' && line[0] <= 'z') {
754 strncpy(last, line, sizeof(last) - 1);
755 while (i = getline(cfp))
756 if (strcmp(last, line))
757 break;
758 switch (sendfile('\3', last+1)) {
759 case OK:
760 if (i)
761 goto again;
762 break;
763 case REPRINT:
764 (void)fclose(cfp);
765 return(REPRINT);
766 case ACCESS:
767 sendmail(logname, ACCESS);
768 case ERROR:
769 err = ERROR;
770 }
771 break;
772 }
773 }
774 if (err == OK && sendfile('\2', file) > 0) {
775 (void)fclose(cfp);
776 return(REPRINT);
777 }
778 /*
779 * pass 2
780 */
781 fseek(cfp, 0L, 0);
782 while (getline(cfp))
783 if (line[0] == 'U')
784 (void)unlink(line+1);
785 /*
786 * clean-up in case another control file exists
787 */
788 (void)fclose(cfp);
789 (void)unlink(file);
790 return(err);
791 }
792
793 /*
794 * Send a data file to the remote machine and spool it.
795 * Return positive if we should try resending.
796 */
797 static int
798 sendfile(type, file)
799 int type;
800 char *file;
801 {
802 register int f, i, amt;
803 struct stat stb;
804 char buf[BUFSIZ];
805 int sizerr, resp;
806
807 if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
808 return(ERROR);
809 /*
810 * Check to see if data file is a symbolic link. If so, it should
811 * still point to the same file or someone is trying to print something
812 * he shouldn't.
813 */
814 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
815 (stb.st_dev != fdev || stb.st_ino != fino))
816 return(ACCESS);
817 amt = snprintf(buf, sizeof(buf), "%c%qd %s\n", type, stb.st_size, file);
818 for (i = 0; ; i++) {
819 if (write(pfd, buf, amt) != amt ||
820 (resp = response()) < 0 || resp == '\1') {
821 (void)close(f);
822 return(REPRINT);
823 } else if (resp == '\0')
824 break;
825 if (i == 0)
826 pstatus("no space on remote; waiting for queue to drain");
827 if (i == 10)
828 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
829 printer, RM);
830 sleep(5 * 60);
831 }
832 if (i)
833 pstatus("sending to %s", RM);
834 sizerr = 0;
835 for (i = 0; i < stb.st_size; i += BUFSIZ) {
836 amt = BUFSIZ;
837 if (i + amt > stb.st_size)
838 amt = stb.st_size - i;
839 if (sizerr == 0 && read(f, buf, amt) != amt)
840 sizerr = 1;
841 if (write(pfd, buf, amt) != amt) {
842 (void)close(f);
843 return(REPRINT);
844 }
845 }
846
847
848
849
850 (void)close(f);
851 if (sizerr) {
852 syslog(LOG_INFO, "%s: %s: changed size", printer, file);
853 /* tell recvjob to ignore this file */
854 (void)write(pfd, "\1", 1);
855 return(ERROR);
856 }
857 if (write(pfd, "", 1) != 1 || response())
858 return(REPRINT);
859 return(OK);
860 }
861
862 /*
863 * Check to make sure there have been no errors and that both programs
864 * are in sync with eachother.
865 * Return non-zero if the connection was lost.
866 */
867 static char
868 response()
869 {
870 char resp;
871
872 if (read(pfd, &resp, 1) != 1) {
873 syslog(LOG_INFO, "%s: lost connection", printer);
874 return(-1);
875 }
876 return(resp);
877 }
878
879 /*
880 * Banner printing stuff
881 */
882 static void
883 banner(name1, name2)
884 char *name1, *name2;
885 {
886 time_t tvec;
887 extern char *ctime();
888
889 time(&tvec);
890 if (!SF && !tof)
891 (void)write(ofd, FF, strlen(FF));
892 if (SB) { /* short banner only */
893 if (class[0]) {
894 (void)write(ofd, class, strlen(class));
895 (void)write(ofd, ":", 1);
896 }
897 (void)write(ofd, name1, strlen(name1));
898 (void)write(ofd, " Job: ", 7);
899 (void)write(ofd, name2, strlen(name2));
900 (void)write(ofd, " Date: ", 8);
901 (void)write(ofd, ctime(&tvec), 24);
902 (void)write(ofd, "\n", 1);
903 } else { /* normal banner */
904 (void)write(ofd, "\n\n\n", 3);
905 scan_out(ofd, name1, '\0');
906 (void)write(ofd, "\n\n", 2);
907 scan_out(ofd, name2, '\0');
908 if (class[0]) {
909 (void)write(ofd,"\n\n\n",3);
910 scan_out(ofd, class, '\0');
911 }
912 (void)write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
913 (void)write(ofd, name2, strlen(name2));
914 (void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
915 (void)write(ofd, ctime(&tvec), 24);
916 (void)write(ofd, "\n", 1);
917 }
918 if (!SF)
919 (void)write(ofd, FF, strlen(FF));
920 tof = 1;
921 }
922
923 static char *
924 scnline(key, p, c)
925 register int key;
926 register char *p;
927 int c;
928 {
929 register scnwidth;
930
931 for (scnwidth = WIDTH; --scnwidth;) {
932 key <<= 1;
933 *p++ = key & 0200 ? c : BACKGND;
934 }
935 return (p);
936 }
937
938 #define TRC(q) (((q)-' ')&0177)
939
940 static void
941 scan_out(scfd, scsp, dlm)
942 int scfd, dlm;
943 char *scsp;
944 {
945 register char *strp;
946 register nchrs, j;
947 char outbuf[LINELEN+1], *sp, c, cc;
948 int d, scnhgt;
949 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
950
951 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
952 strp = &outbuf[0];
953 sp = scsp;
954 for (nchrs = 0; ; ) {
955 d = dropit(c = TRC(cc = *sp++));
956 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
957 for (j = WIDTH; --j;)
958 *strp++ = BACKGND;
959 else
960 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
961 if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
962 break;
963 *strp++ = BACKGND;
964 *strp++ = BACKGND;
965 }
966 while (*--strp == BACKGND && strp >= outbuf)
967 ;
968 strp++;
969 *strp++ = '\n';
970 (void)write(scfd, outbuf, strp-outbuf);
971 }
972 }
973
974 static int
975 dropit(c)
976 int c;
977 {
978 switch(c) {
979
980 case TRC('_'):
981 case TRC(';'):
982 case TRC(','):
983 case TRC('g'):
984 case TRC('j'):
985 case TRC('p'):
986 case TRC('q'):
987 case TRC('y'):
988 return (DROP);
989
990 default:
991 return (0);
992 }
993 }
994
995 /*
996 * sendmail ---
997 * tell people about job completion
998 */
999 static void
1000 sendmail(user, bombed)
1001 char *user;
1002 int bombed;
1003 {
1004 register int i;
1005 int p[2], s;
1006 register char *cp;
1007 char buf[100];
1008 struct stat stb;
1009 FILE *fp;
1010
1011 pipe(p);
1012 if ((s = dofork(DORETURN)) == 0) { /* child */
1013 dup2(p[0], 0);
1014 for (i = 3; i < NOFILE; i++)
1015 (void)close(i);
1016 if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1017 cp++;
1018 else
1019 cp = _PATH_SENDMAIL;
1020 (void)snprintf(buf, sizeof(buf), "%s@%s", user, fromhost);
1021 execl(_PATH_SENDMAIL, cp, buf, 0);
1022 exit(0);
1023 } else if (s > 0) { /* parent */
1024 dup2(p[1], 1);
1025 printf("To: %s@%s\n", user, fromhost);
1026 printf("Subject: printer job\n\n");
1027 printf("Your printer job ");
1028 if (*jobname)
1029 printf("(%s) ", jobname);
1030 switch (bombed) {
1031 case OK:
1032 printf("\ncompleted successfully\n");
1033 break;
1034 default:
1035 case FATALERR:
1036 printf("\ncould not be printed\n");
1037 break;
1038 case NOACCT:
1039 printf("\ncould not be printed without an account on %s\n", host);
1040 break;
1041 case FILTERERR:
1042 if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1043 (fp = fopen(tempfile, "r")) == NULL) {
1044 printf("\nwas printed but had some errors\n");
1045 break;
1046 }
1047 printf("\nwas printed but had the following errors:\n");
1048 while ((i = getc(fp)) != EOF)
1049 putchar(i);
1050 (void)fclose(fp);
1051 break;
1052 case ACCESS:
1053 printf("\nwas not printed because it was not linked to the original file\n");
1054 }
1055 fflush(stdout);
1056 (void)close(1);
1057 }
1058 (void)close(p[0]);
1059 (void)close(p[1]);
1060 wait(&s);
1061 }
1062
1063 /*
1064 * dofork - fork with retries on failure
1065 */
1066 static int
1067 dofork(action)
1068 int action;
1069 {
1070 register int i, pid;
1071 struct passwd *pw;
1072
1073 for (i = 0; i < 20; i++) {
1074 if ((pid = fork()) < 0) {
1075 sleep((unsigned)(i*i));
1076 continue;
1077 }
1078 /*
1079 * Child should run as daemon instead of root
1080 */
1081 if (pid == 0) {
1082 pw = getpwuid(DU);
1083 if (pw == 0) {
1084 syslog(LOG_ERR, "uid %d not in password file",
1085 DU);
1086 break;
1087 }
1088 initgroups(pw->pw_name, pw->pw_gid);
1089 setgid(pw->pw_gid);
1090 setuid(DU);
1091 }
1092 return (pid);
1093 }
1094 syslog(LOG_ERR, "can't fork");
1095
1096 switch (action) {
1097 case DORETURN:
1098 return (-1);
1099 default:
1100 syslog(LOG_ERR, "bad action (%d) to dofork", action);
1101 /*FALL THRU*/
1102 case DOABORT:
1103 exit(1);
1104 }
1105 /*NOTREACHED*/
1106 }
1107
1108 /*
1109 * Kill child processes to abort current job.
1110 */
1111 static void
1112 abortpr(signo)
1113 int signo;
1114 {
1115 (void)unlink(tempfile);
1116 kill(0, SIGINT);
1117 if (ofilter > 0)
1118 kill(ofilter, SIGCONT);
1119 while (wait(NULL) > 0)
1120 ;
1121 exit(0);
1122 }
1123
1124 static void
1125 init()
1126 {
1127 int status;
1128 char *s;
1129
1130 if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1131 syslog(LOG_ERR, "can't open printer description file");
1132 exit(1);
1133 } else if (status == -1) {
1134 syslog(LOG_ERR, "unknown printer: %s", printer);
1135 exit(1);
1136 } else if (status == -3)
1137 fatal("potential reference loop detected in printcap file");
1138
1139 if (cgetstr(bp, "lp", &LP) == -1)
1140 LP = _PATH_DEFDEVLP;
1141 if (cgetstr(bp, "rp", &RP) == -1)
1142 RP = DEFLP;
1143 if (cgetstr(bp, "lo", &LO) == -1)
1144 LO = DEFLOCK;
1145 if (cgetstr(bp, "st", &ST) == -1)
1146 ST = DEFSTAT;
1147 if (cgetstr(bp, "lf", &LF) == -1)
1148 LF = _PATH_CONSOLE;
1149 if (cgetstr(bp, "sd", &SD) == -1)
1150 SD = _PATH_DEFSPOOL;
1151 if (cgetnum(bp, "du", &DU) < 0)
1152 DU = DEFUID;
1153 if (cgetstr(bp,"ff", &FF) == -1)
1154 FF = DEFFF;
1155 if (cgetnum(bp, "pw", &PW) < 0)
1156 PW = DEFWIDTH;
1157 (void)snprintf(&width[2], sizeof(width) - 2, "%d", PW);
1158 if (cgetnum(bp, "pl", &PL) < 0)
1159 PL = DEFLENGTH;
1160 (void)snprintf(&length[2], sizeof(length) - 2, "%d", PL);
1161 if (cgetnum(bp,"px", &PX) < 0)
1162 PX = 0;
1163 (void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%d", PX);
1164 if (cgetnum(bp, "py", &PY) < 0)
1165 PY = 0;
1166 (void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%d", PY);
1167 cgetstr(bp, "rm", &RM);
1168 if (s = checkremote())
1169 syslog(LOG_WARNING, s);
1170
1171 cgetstr(bp, "af", &AF);
1172 cgetstr(bp, "of", &OF);
1173 cgetstr(bp, "if", &IF);
1174 cgetstr(bp, "rf", &RF);
1175 cgetstr(bp, "tf", &TF);
1176 cgetstr(bp, "nf", &NF);
1177 cgetstr(bp, "df", &DF);
1178 cgetstr(bp, "gf", &GF);
1179 cgetstr(bp, "vf", &VF);
1180 cgetstr(bp, "cf", &CF);
1181 cgetstr(bp, "tr", &TR);
1182
1183 RS = (cgetcap(bp, "rs", ':') != NULL);
1184 SF = (cgetcap(bp, "sf", ':') != NULL);
1185 SH = (cgetcap(bp, "sh", ':') != NULL);
1186 SB = (cgetcap(bp, "sb", ':') != NULL);
1187 HL = (cgetcap(bp, "hl", ':') != NULL);
1188 RW = (cgetcap(bp, "rw", ':') != NULL);
1189
1190 cgetnum(bp, "br", &BR);
1191 if (cgetnum(bp, "fc", &FC) < 0)
1192 FC = 0;
1193 if (cgetnum(bp, "fs", &FS) < 0)
1194 FS = 0;
1195 if (cgetnum(bp, "xc", &XC) < 0)
1196 XC = 0;
1197 if (cgetnum(bp, "xs", &XS) < 0)
1198 XS = 0;
1199 cgetstr(bp, "ms", &MS);
1200
1201 tof = (cgetcap(bp, "fo", ':') == NULL);
1202 }
1203
1204 /*
1205 * Acquire line printer or remote connection.
1206 */
1207 static void
1208 openpr()
1209 {
1210 register int i, n;
1211 int resp;
1212
1213 if (!sendtorem && *LP) {
1214 for (i = 1; ; i = i < 32 ? i << 1 : i) {
1215 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1216 if (pfd >= 0)
1217 break;
1218 if (errno == ENOENT) {
1219 syslog(LOG_ERR, "%s: %m", LP);
1220 exit(1);
1221 }
1222 if (i == 1)
1223 pstatus("waiting for %s to become ready (offline ?)", printer);
1224 sleep(i);
1225 }
1226 if (isatty(pfd))
1227 setty();
1228 pstatus("%s is ready and printing", printer);
1229 } else if (RM != NULL) {
1230 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1231 resp = -1;
1232 pfd = getport(RM);
1233 if (pfd >= 0) {
1234 n = snprintf(line, sizeof(line), "\2%s\n", RP);
1235 if (write(pfd, line, n) == n &&
1236 (resp = response()) == '\0')
1237 break;
1238 (void)close(pfd);
1239 }
1240 if (i == 1) {
1241 if (resp < 0)
1242 pstatus("waiting for %s to come up", RM);
1243 else {
1244 pstatus("waiting for queue to be enabled on %s", RM);
1245 i = 256;
1246 }
1247 }
1248 sleep(i);
1249 }
1250 pstatus("sending to %s", RM);
1251 remote = 1;
1252 } else {
1253 syslog(LOG_ERR, "%s: no line printer device or host name",
1254 printer);
1255 exit(1);
1256 }
1257 /*
1258 * Start up an output filter, if needed.
1259 */
1260 if (!remote && OF) {
1261 int p[2];
1262 char *cp;
1263
1264 pipe(p);
1265 if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1266 dup2(p[0], 0); /* pipe is std in */
1267 dup2(pfd, 1); /* printer is std out */
1268 for (i = 3; i < NOFILE; i++)
1269 (void)close(i);
1270 if ((cp = rindex(OF, '/')) == NULL)
1271 cp = OF;
1272 else
1273 cp++;
1274 execl(OF, cp, width, length, 0);
1275 syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1276 exit(1);
1277 }
1278 (void)close(p[0]); /* close input side */
1279 ofd = p[1]; /* use pipe for output */
1280 } else {
1281 ofd = pfd;
1282 ofilter = 0;
1283 }
1284 }
1285
1286 #if !defined(__NetBSD__)
1287 struct bauds {
1288 int baud;
1289 int speed;
1290 } bauds[] = {
1291 50, B50,
1292 75, B75,
1293 110, B110,
1294 134, B134,
1295 150, B150,
1296 200, B200,
1297 300, B300,
1298 600, B600,
1299 1200, B1200,
1300 1800, B1800,
1301 2400, B2400,
1302 4800, B4800,
1303 9600, B9600,
1304 19200, B19200,
1305 38400, B38400,
1306 0, 0
1307 };
1308 #endif
1309
1310 /*
1311 * setup tty lines.
1312 */
1313 static void
1314 setty()
1315 {
1316 struct info i;
1317 char **argv, **ap, *p, *val;
1318
1319 i.fd = pfd;
1320 i.set = i.wset = 0;
1321 if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
1322 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1323 exit(1);
1324 }
1325 if (tcgetattr(i.fd, &i.t) < 0) {
1326 syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1327 exit(1);
1328 }
1329 if (BR > 0) {
1330 #if !defined(__NetBSD__)
1331 register struct bauds *bp;
1332 for (bp = bauds; bp->baud; bp++)
1333 if (BR == bp->baud)
1334 break;
1335 if (!bp->baud) {
1336 syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1337 exit(1);
1338 }
1339 cfsetspeed(&i.t, bp->speed);
1340 #else
1341 cfsetspeed(&i.t, BR);
1342 #endif
1343 i.set = 1;
1344 }
1345 if (MS) {
1346 if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
1347 syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
1348 exit(1);
1349 }
1350 if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
1351 syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
1352 printer);
1353
1354 argv = (char **)calloc(256, sizeof(char *));
1355 if (argv == NULL) {
1356 syslog(LOG_ERR, "%s: calloc: %m", printer);
1357 exit(1);
1358 }
1359 p = strdup(MS);
1360 ap = argv;
1361 while ((val = strsep(&p, " \t,")) != NULL) {
1362 *ap++ = strdup(val);
1363 }
1364
1365 for (; *argv; ++argv) {
1366 if (ksearch(&argv, &i))
1367 continue;
1368 if (msearch(&argv, &i))
1369 continue;
1370 syslog(LOG_INFO, "%s: unknown stty flag: %s",
1371 printer, *argv);
1372 }
1373 } else {
1374 if (FC) {
1375 sttyclearflags(&i.t, FC);
1376 i.set = 1;
1377 }
1378 if (FS) {
1379 sttysetflags(&i.t, FS);
1380 i.set = 1;
1381 }
1382 if (XC) {
1383 sttyclearlflags(&i.t, XC);
1384 i.set = 1;
1385 }
1386 if (XS) {
1387 sttysetlflags(&i.t, XS);
1388 i.set = 1;
1389 }
1390 }
1391
1392 if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
1393 syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1394 exit(1);
1395 }
1396 if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
1397 syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
1398 return;
1399 }
1400
1401 #if __STDC__
1402 #include <stdarg.h>
1403 #else
1404 #include <varargs.h>
1405 #endif
1406
1407 void
1408 #if __STDC__
1409 pstatus(const char *msg, ...)
1410 #else
1411 pstatus(msg, va_alist)
1412 char *msg;
1413 va_dcl
1414 #endif
1415 {
1416 register int fd;
1417 char buf[BUFSIZ];
1418 va_list ap;
1419 #if __STDC__
1420 va_start(ap, msg);
1421 #else
1422 va_start(ap);
1423 #endif
1424
1425 umask(0);
1426 fd = open(ST, O_WRONLY|O_CREAT, 0664);
1427 if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1428 syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1429 exit(1);
1430 }
1431 ftruncate(fd, 0);
1432 (void)vsnprintf(buf, sizeof(buf) - 2, msg, ap);
1433 va_end(ap);
1434 strncat(buf, "\n", 2);
1435 (void)write(fd, buf, strlen(buf));
1436 (void)close(fd);
1437 }
1438