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