printjob.c revision 1.33 1 /* $NetBSD: printjob.c,v 1.33 2002/07/09 01:12:35 hubertf 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.33 2002/07/09 01:12:35 hubertf 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; /* printer 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 __attribute__((__format__(__printf__, 1, 2)));
135 static char response __P((void));
136 static void scan_out __P((int, char *, int));
137 static char *scnline __P((int, char *, int));
138 static int sendfile __P((int, char *));
139 static int sendit __P((char *));
140 static void sendmail __P((char *, int));
141 static void setty __P((void));
142 static void alarmer __P((int));
143
144 void
145 printjob(void)
146 {
147 struct stat stb;
148 struct queue *q, **qp;
149 struct queue **queue;
150 int i, nitems;
151 off_t pidoff;
152 int errcnt, count = 0;
153
154 init(); /* set up capabilities */
155 (void)write(STDOUT_FILENO, "", 1); /* ack that daemon is started */
156 (void)close(STDERR_FILENO); /* set up log file */
157 if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
158 syslog(LOG_ERR, "%s: %m", LF);
159 (void)open(_PATH_DEVNULL, O_WRONLY);
160 }
161 setgid(getegid());
162 pid = getpid(); /* for use with lprm */
163 setpgrp(0, pid);
164 signal(SIGHUP, abortpr);
165 signal(SIGINT, abortpr);
166 signal(SIGQUIT, abortpr);
167 signal(SIGTERM, abortpr);
168
169 (void)mktemp(tempfile); /* OK */
170
171 /*
172 * uses short form file names
173 */
174 if (chdir(SD) < 0) {
175 syslog(LOG_ERR, "%s: %m", SD);
176 exit(1);
177 }
178 if (stat(LO, &stb) == 0 && (stb.st_mode & S_IXUSR))
179 exit(0); /* printing disabled */
180 lfd = open(LO, O_WRONLY|O_CREAT, 0644);
181 if (lfd < 0) {
182 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
183 exit(1);
184 }
185 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
186 if (errno == EWOULDBLOCK) /* active daemon present */
187 exit(0);
188 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
189 exit(1);
190 }
191 ftruncate(lfd, 0);
192 /*
193 * write process id for others to know
194 */
195 pidoff = i = snprintf(line, sizeof(line), "%u\n", pid);
196 if (write(lfd, line, i) != i) {
197 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
198 exit(1);
199 }
200 /*
201 * search the spool directory for work and sort by queue order.
202 */
203 if ((nitems = getq(&queue)) < 0) {
204 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
205 exit(1);
206 }
207 if (nitems == 0) /* no work to do */
208 exit(0);
209 if (stb.st_mode & S_IXOTH) { /* reset queue flag */
210 stb.st_mode &= ~S_IXOTH;
211 if (fchmod(lfd, stb.st_mode & 0777) < 0)
212 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
213 }
214 openpr(); /* open printer or remote */
215 again:
216 /*
217 * we found something to do now do it --
218 * write the name of the current control file into the lock file
219 * so the spool queue program can tell what we're working on
220 */
221 for (qp = queue; nitems--; free((char *) q)) {
222 q = *qp++;
223 if (stat(q->q_name, &stb) < 0)
224 continue;
225 errcnt = 0;
226 restart:
227 (void)lseek(lfd, pidoff, 0);
228 i = snprintf(line, sizeof(line), "%s\n", q->q_name);
229 if (write(lfd, line, i) != i)
230 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
231 if (!remote)
232 i = printit(q->q_name);
233 else
234 i = sendit(q->q_name);
235 /*
236 * Check to see if we are supposed to stop printing or
237 * if we are to rebuild the queue.
238 */
239 if (fstat(lfd, &stb) == 0) {
240 /* stop printing before starting next job? */
241 if (stb.st_mode & S_IXUSR)
242 goto done;
243 /* rebuild queue (after lpc topq) */
244 if (stb.st_mode & S_IXOTH) {
245 for (free((char *) q); nitems--; free((char *) q))
246 q = *qp++;
247 stb.st_mode &= ~S_IXOTH;
248 if (fchmod(lfd, stb.st_mode & 0777) < 0)
249 syslog(LOG_WARNING, "%s: %s: %m",
250 printer, LO);
251 break;
252 }
253 }
254 if (i == OK) /* file ok and printed */
255 count++;
256 else if (i == REPRINT && ++errcnt < 5) {
257 /* try reprinting the job */
258 syslog(LOG_INFO, "restarting %s", printer);
259 if (ofilter > 0) {
260 kill(ofilter, SIGCONT); /* to be sure */
261 (void)close(ofd);
262 while ((i = wait(NULL)) > 0 && i != ofilter)
263 ;
264 ofilter = 0;
265 }
266 (void)close(pfd); /* close printer */
267 if (ftruncate(lfd, pidoff) < 0)
268 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
269 openpr(); /* try to reopen printer */
270 goto restart;
271 } else {
272 syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
273 remote ? "sent to remote host" : "printed", q->q_name);
274 if (i == REPRINT) {
275 /* ensure we don't attempt this job again */
276 (void) unlink(q->q_name);
277 q->q_name[0] = 'd';
278 (void) unlink(q->q_name);
279 if (logname[0])
280 sendmail(logname, FATALERR);
281 }
282 }
283 }
284 free((char *) queue);
285 /*
286 * search the spool directory for more work.
287 */
288 if ((nitems = getq(&queue)) < 0) {
289 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
290 exit(1);
291 }
292 if (nitems == 0) { /* no more work to do */
293 done:
294 if (count > 0) { /* Files actually printed */
295 if (!SF && !tof)
296 (void)write(ofd, FF, strlen(FF));
297 if (TR != NULL) /* output trailer */
298 (void)write(ofd, TR, strlen(TR));
299 }
300 (void)unlink(tempfile);
301 exit(0);
302 }
303 goto again;
304 }
305
306 #define FONTLEN 50
307 char fonts[4][FONTLEN]; /* fonts for troff */
308
309 char ifonts[4][40] = {
310 _PATH_VFONTR,
311 _PATH_VFONTI,
312 _PATH_VFONTB,
313 _PATH_VFONTS,
314 };
315
316 /*
317 * The remaining part is the reading of the control file (cf)
318 * and performing the various actions.
319 */
320 static int
321 printit(char *file)
322 {
323 int i;
324 char *cp;
325 int bombed = OK;
326
327 /*
328 * open control file; ignore if no longer there.
329 */
330 if ((cfp = fopen(file, "r")) == NULL) {
331 syslog(LOG_INFO, "%s: %s: %m", printer, file);
332 return(OK);
333 }
334 /*
335 * Reset troff fonts.
336 */
337 for (i = 0; i < 4; i++)
338 strncpy(fonts[i], ifonts[i], FONTLEN);
339 (void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
340 indent[2] = '0';
341 indent[3] = '\0';
342
343 /*
344 * read the control file for work to do
345 *
346 * file format -- first character in the line is a command
347 * rest of the line is the argument.
348 * valid commands are:
349 *
350 * S -- "stat info" for symbolic link protection
351 * J -- "job name" on banner page
352 * C -- "class name" on banner page
353 * L -- "literal" user's name to print on banner
354 * T -- "title" for pr
355 * H -- "host name" of machine where lpr was done
356 * P -- "person" user's login name
357 * I -- "indent" amount to indent output
358 * R -- laser dpi "resolution"
359 * f -- "file name" name of text file to print
360 * l -- "file name" text file with control chars
361 * p -- "file name" text file to print with pr(1)
362 * t -- "file name" troff(1) file to print
363 * n -- "file name" ditroff(1) file to print
364 * d -- "file name" dvi file to print
365 * g -- "file name" plot(1G) file to print
366 * v -- "file name" plain raster file to print
367 * c -- "file name" cifplot file to print
368 * 1 -- "R font file" for troff
369 * 2 -- "I font file" for troff
370 * 3 -- "B font file" for troff
371 * 4 -- "S font file" for troff
372 * N -- "name" of file (used by lpq)
373 * U -- "unlink" name of file to remove
374 * (after we print it. (Pass 2 only)).
375 * M -- "mail" to user when done printing
376 *
377 * getline reads a line and expands tabs to blanks
378 */
379
380 /* pass 1 */
381
382 while (getline(cfp))
383 switch (line[0]) {
384 case 'H':
385 strncpy(fromhost, line+1, sizeof(fromhost) - 1);
386 if (class[0] == '\0')
387 strncpy(class, line+1, sizeof(class) - 1);
388 class[sizeof(class)-1] = '\0';
389 continue;
390
391 case 'P':
392 strncpy(logname, line+1, sizeof(logname) - 1);
393 logname[sizeof(logname)-1] = '\0';
394 if (RS) { /* restricted */
395 if (getpwnam(logname) == NULL) {
396 bombed = NOACCT;
397 sendmail(line+1, bombed);
398 goto pass2;
399 }
400 }
401 continue;
402
403 case 'S':
404 cp = line+1;
405 i = 0;
406 while (*cp >= '0' && *cp <= '9')
407 i = i * 10 + (*cp++ - '0');
408 fdev = i;
409 cp++;
410 i = 0;
411 while (*cp >= '0' && *cp <= '9')
412 i = i * 10 + (*cp++ - '0');
413 fino = i;
414 continue;
415
416 case 'J':
417 if (line[1] != '\0') {
418 strncpy(jobname, line+1, sizeof(jobname) - 1);
419 jobname[sizeof(jobname)-1] = '\0';
420 } else {
421 jobname[0] = ' ';
422 jobname[1] = '\0';
423 }
424 continue;
425
426 case 'C':
427 if (line[1] != '\0') {
428 strncpy(class, line+1, sizeof(class) - 1);
429 class[sizeof(class)-1] = '\0';
430 } else if (class[0] == '\0') {
431 gethostname(class, sizeof(class));
432 class[sizeof(class) - 1] = '\0';
433 }
434 continue;
435
436 case 'T': /* header title for pr */
437 strncpy(title, line+1, sizeof(title) - 1);
438 title[sizeof(title)-1] = '\0';
439 continue;
440
441 case 'L': /* identification line */
442 if (!SH && !HL)
443 banner(line+1, jobname);
444 continue;
445
446 case '1': /* troff fonts */
447 case '2':
448 case '3':
449 case '4':
450 if (line[1] != '\0') {
451 strncpy(fonts[line[0]-'1'], line+1, FONTLEN - 1);
452 fonts[line[0]-'1'][50-1] = '\0';
453 }
454 continue;
455
456 case 'W': /* page width */
457 strncpy(width+2, line+1, sizeof(width) - 3);
458 width[sizeof(width)-1] = '\0';
459 continue;
460
461 case 'I': /* indent amount */
462 strncpy(indent+2, line+1, sizeof(indent) - 3);
463 indent[sizeof(indent)-1] = '\0';
464 continue;
465
466 default: /* some file to print */
467 switch (i = print(line[0], line+1)) {
468 case ERROR:
469 if (bombed == OK)
470 bombed = FATALERR;
471 break;
472 case REPRINT:
473 (void)fclose(cfp);
474 return(REPRINT);
475 case FILTERERR:
476 case ACCESS:
477 bombed = i;
478 sendmail(logname, bombed);
479 }
480 title[0] = '\0';
481 continue;
482
483 case 'N':
484 case 'U':
485 case 'M':
486 case 'R':
487 continue;
488 }
489
490 /* pass 2 */
491
492 pass2:
493 fseek(cfp, 0L, 0);
494 while (getline(cfp))
495 switch (line[0]) {
496 case 'L': /* identification line */
497 if (!SH && HL)
498 banner(line+1, jobname);
499 continue;
500
501 case 'M':
502 if (bombed < NOACCT) /* already sent if >= NOACCT */
503 sendmail(line+1, bombed);
504 continue;
505
506 case 'U':
507 if (strchr(line+1, '/'))
508 continue;
509 (void)unlink(line+1);
510 }
511 /*
512 * clean-up in case another control file exists
513 */
514 (void)fclose(cfp);
515 (void)unlink(file);
516 return(bombed == OK ? OK : ERROR);
517 }
518
519 /*
520 * Print a file.
521 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
522 * Return -1 if a non-recoverable error occurred,
523 * 2 if the filter detected some errors (but printed the job anyway),
524 * 1 if we should try to reprint this job and
525 * 0 if all is well.
526 * Note: all filters take stdin as the file, stdout as the printer,
527 * stderr as the log file, and must not ignore SIGINT.
528 */
529 static int
530 print(int format, 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 occurred, 1 if a recoverable error and
762 * 0 if all is well.
763 */
764 static int
765 sendit(char *file)
766 {
767 int i, err = OK;
768 char *cp, last[BUFSIZ];
769
770 /*
771 * open control file
772 */
773 if ((cfp = fopen(file, "r")) == NULL)
774 return(OK);
775 /*
776 * read the control file for work to do
777 *
778 * file format -- first character in the line is a command
779 * rest of the line is the argument.
780 * commands of interest are:
781 *
782 * a-z -- "file name" name of file to print
783 * U -- "unlink" name of file to remove
784 * (after we print it. (Pass 2 only)).
785 */
786
787 /*
788 * pass 1
789 */
790 while (getline(cfp)) {
791 again:
792 if (line[0] == 'S') {
793 cp = line+1;
794 i = 0;
795 while (*cp >= '0' && *cp <= '9')
796 i = i * 10 + (*cp++ - '0');
797 fdev = i;
798 cp++;
799 i = 0;
800 while (*cp >= '0' && *cp <= '9')
801 i = i * 10 + (*cp++ - '0');
802 fino = i;
803 continue;
804 }
805 if (line[0] >= 'a' && line[0] <= 'z') {
806 strncpy(last, line, sizeof(last) - 1);
807 last[sizeof(last) - 1] = '\0';
808 while ((i = getline(cfp)) != 0)
809 if (strcmp(last, line))
810 break;
811 switch (sendfile('\3', last+1)) {
812 case OK:
813 if (i)
814 goto again;
815 break;
816 case REPRINT:
817 (void)fclose(cfp);
818 return(REPRINT);
819 case ACCESS:
820 sendmail(logname, ACCESS);
821 case ERROR:
822 err = ERROR;
823 }
824 break;
825 }
826 }
827 if (err == OK && sendfile('\2', file) > 0) {
828 (void)fclose(cfp);
829 return(REPRINT);
830 }
831 /*
832 * pass 2
833 */
834 fseek(cfp, 0L, 0);
835 while (getline(cfp))
836 if (line[0] == 'U' && strchr(line+1, '/') == 0)
837 (void)unlink(line+1);
838 /*
839 * clean-up in case another control file exists
840 */
841 (void)fclose(cfp);
842 (void)unlink(file);
843 return(err);
844 }
845
846 /*
847 * Send a data file to the remote machine and spool it.
848 * Return positive if we should try resending.
849 */
850 static int
851 sendfile(int type, char *file)
852 {
853 int f, i, amt;
854 struct stat stb;
855 char buf[BUFSIZ];
856 int sizerr, resp;
857
858 if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
859 return(ERROR);
860 /*
861 * Check to see if data file is a symbolic link. If so, it should
862 * still point to the same file or someone is trying to print something
863 * he shouldn't.
864 */
865 if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
866 (stb.st_dev != fdev || stb.st_ino != fino))
867 return(ACCESS);
868 amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
869 (long long)stb.st_size, file);
870 for (i = 0; ; i++) {
871 if (write(pfd, buf, amt) != amt ||
872 (resp = response()) < 0 || resp == '\1') {
873 (void)close(f);
874 return(REPRINT);
875 } else if (resp == '\0')
876 break;
877 if (i == 0)
878 pstatus("no space on remote; waiting for queue to drain");
879 if (i == 10)
880 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
881 printer, RM);
882 sleep(5 * 60);
883 }
884 if (i)
885 pstatus("sending to %s", RM);
886 sizerr = 0;
887 for (i = 0; i < stb.st_size; i += BUFSIZ) {
888 struct sigaction osa, nsa;
889
890 amt = BUFSIZ;
891 if (i + amt > stb.st_size)
892 amt = stb.st_size - i;
893 if (sizerr == 0 && read(f, buf, amt) != amt)
894 sizerr = 1;
895 nsa.sa_handler = alarmer;
896 sigemptyset(&nsa.sa_mask);
897 sigaddset(&nsa.sa_mask, SIGALRM);
898 nsa.sa_flags = 0;
899 (void)sigaction(SIGALRM, &nsa, &osa);
900 alarm(wait_time);
901 if (write(pfd, buf, amt) != amt) {
902 alarm(0);
903 (void)sigaction(SIGALRM, &osa, NULL);
904 (void)close(f);
905 return(REPRINT);
906 }
907 alarm(0);
908 (void)sigaction(SIGALRM, &osa, NULL);
909 }
910
911 (void)close(f);
912 if (sizerr) {
913 syslog(LOG_INFO, "%s: %s: changed size", printer, file);
914 /* tell recvjob to ignore this file */
915 (void)write(pfd, "\1", 1);
916 return(ERROR);
917 }
918 if (write(pfd, "", 1) != 1 || response())
919 return(REPRINT);
920 return(OK);
921 }
922
923 /*
924 * Check to make sure there have been no errors and that both programs
925 * are in sync with eachother.
926 * Return non-zero if the connection was lost.
927 */
928 static char
929 response(void)
930 {
931 struct sigaction osa, nsa;
932 char resp;
933
934 nsa.sa_handler = alarmer;
935 sigemptyset(&nsa.sa_mask);
936 sigaddset(&nsa.sa_mask, SIGALRM);
937 nsa.sa_flags = 0;
938 (void)sigaction(SIGALRM, &nsa, &osa);
939 alarm(wait_time);
940 if (read(pfd, &resp, 1) != 1) {
941 syslog(LOG_INFO, "%s: lost connection", printer);
942 resp = -1;
943 }
944 alarm(0);
945 (void)sigaction(SIGALRM, &osa, NULL);
946 return (resp);
947 }
948
949 /*
950 * Banner printing stuff
951 */
952 static void
953 banner(char *name1, char *name2)
954 {
955 time_t tvec;
956
957 time(&tvec);
958 if (!SF && !tof)
959 (void)write(ofd, FF, strlen(FF));
960 if (SB) { /* short banner only */
961 if (class[0]) {
962 (void)write(ofd, class, strlen(class));
963 (void)write(ofd, ":", 1);
964 }
965 (void)write(ofd, name1, strlen(name1));
966 (void)write(ofd, " Job: ", 7);
967 (void)write(ofd, name2, strlen(name2));
968 (void)write(ofd, " Date: ", 8);
969 (void)write(ofd, ctime(&tvec), 24);
970 (void)write(ofd, "\n", 1);
971 } else { /* normal banner */
972 (void)write(ofd, "\n\n\n", 3);
973 scan_out(ofd, name1, '\0');
974 (void)write(ofd, "\n\n", 2);
975 scan_out(ofd, name2, '\0');
976 if (class[0]) {
977 (void)write(ofd,"\n\n\n",3);
978 scan_out(ofd, class, '\0');
979 }
980 (void)write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
981 (void)write(ofd, name2, strlen(name2));
982 (void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
983 (void)write(ofd, ctime(&tvec), 24);
984 (void)write(ofd, "\n", 1);
985 }
986 if (!SF)
987 (void)write(ofd, FF, strlen(FF));
988 tof = 1;
989 }
990
991 static char *
992 scnline(key, p, c)
993 int key;
994 char *p;
995 int c;
996 {
997 int scnwidth;
998
999 for (scnwidth = WIDTH; --scnwidth;) {
1000 key <<= 1;
1001 *p++ = key & 0200 ? c : BACKGND;
1002 }
1003 return (p);
1004 }
1005
1006 #define TRC(q) (((q)-' ')&0177)
1007
1008 static void
1009 scan_out(int scfd, char *scsp, int dlm)
1010 {
1011 char *strp;
1012 int nchrs, j;
1013 char outbuf[LINELEN+1], *sp, c, cc;
1014 int d, scnhgt;
1015 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
1016
1017 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1018 strp = &outbuf[0];
1019 sp = scsp;
1020 for (nchrs = 0; ; ) {
1021 d = dropit(c = TRC(cc = *sp++));
1022 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1023 for (j = WIDTH; --j;)
1024 *strp++ = BACKGND;
1025 else
1026 strp = scnline(scnkey[(int)c][scnhgt-1-d],
1027 strp, cc);
1028 if (*sp == dlm || *sp == '\0' ||
1029 nchrs++ >= PW/(WIDTH+1)-1)
1030 break;
1031 *strp++ = BACKGND;
1032 *strp++ = BACKGND;
1033 }
1034 while (*--strp == BACKGND && strp >= outbuf)
1035 ;
1036 strp++;
1037 *strp++ = '\n';
1038 (void)write(scfd, outbuf, strp-outbuf);
1039 }
1040 }
1041
1042 static int
1043 dropit(int c)
1044 {
1045 switch(c) {
1046
1047 case TRC('_'):
1048 case TRC(';'):
1049 case TRC(','):
1050 case TRC('g'):
1051 case TRC('j'):
1052 case TRC('p'):
1053 case TRC('q'):
1054 case TRC('y'):
1055 return (DROP);
1056
1057 default:
1058 return (0);
1059 }
1060 }
1061
1062 /*
1063 * sendmail ---
1064 * tell people about job completion
1065 */
1066 static void
1067 sendmail(char *user, int bombed)
1068 {
1069 int i, p[2], s, nofile;
1070 char *cp = NULL; /* XXX gcc */
1071 struct stat stb;
1072 FILE *fp;
1073
1074 if (user[0] == '-' || user[0] == '/' || !isprint(user[0]))
1075 return;
1076 pipe(p);
1077 if ((s = dofork(DORETURN)) == 0) { /* child */
1078 dup2(p[0], 0);
1079 closelog();
1080 nofile = sysconf(_SC_OPEN_MAX);
1081 for (i = 3; i < nofile; i++)
1082 (void)close(i);
1083 if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1084 cp++;
1085 else
1086 cp = _PATH_SENDMAIL;
1087 execl(_PATH_SENDMAIL, cp, "-t", 0);
1088 _exit(0);
1089 } else if (s > 0) { /* parent */
1090 dup2(p[1], 1);
1091 printf("To: %s@%s\n", user, fromhost);
1092 printf("Subject: %s printer job \"%s\"\n", printer,
1093 *jobname ? jobname : "<unknown>");
1094 printf("Reply-To: root@%s\n\n", host);
1095 printf("Your printer job ");
1096 if (*jobname)
1097 printf("(%s) ", jobname);
1098 switch (bombed) {
1099 case OK:
1100 printf("\ncompleted successfully\n");
1101 cp = "OK";
1102 break;
1103 default:
1104 case FATALERR:
1105 printf("\ncould not be printed\n");
1106 cp = "FATALERR";
1107 break;
1108 case NOACCT:
1109 printf("\ncould not be printed without an account on %s\n", host);
1110 cp = "NOACCT";
1111 break;
1112 case FILTERERR:
1113 cp = "FILTERERR";
1114 if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1115 (fp = fopen(tempfile, "r")) == NULL) {
1116 printf("\nhad some errors and may not have printed\n");
1117 break;
1118 }
1119 printf("\nhad the following errors and may not have printed:\n");
1120 while ((i = getc(fp)) != EOF)
1121 putchar(i);
1122 (void)fclose(fp);
1123 break;
1124 case ACCESS:
1125 printf("\nwas not printed because it was not linked to the original file\n");
1126 cp = "ACCESS";
1127 }
1128 fflush(stdout);
1129 (void)close(1);
1130 } else {
1131 syslog(LOG_ERR, "fork for sendmail failed: %m");
1132 }
1133 (void)close(p[0]);
1134 (void)close(p[1]);
1135 if (s > 0) {
1136 wait(NULL);
1137 syslog(LOG_INFO, "mail sent to user %s about job %s on "
1138 "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
1139 printer, cp);
1140 }
1141 }
1142
1143 /*
1144 * dofork - fork with retries on failure
1145 */
1146 static int
1147 dofork(int action)
1148 {
1149 int i, pid;
1150 struct passwd *pw;
1151
1152 for (i = 0; i < 20; i++) {
1153 if ((pid = fork()) < 0) {
1154 sleep((unsigned)(i*i));
1155 continue;
1156 }
1157 /*
1158 * Child should run as daemon instead of root
1159 */
1160 if (pid == 0) {
1161 pw = getpwuid(DU);
1162 if (pw == 0) {
1163 syslog(LOG_ERR, "uid %ld not in password file",
1164 DU);
1165 break;
1166 }
1167 initgroups(pw->pw_name, pw->pw_gid);
1168 setgid(pw->pw_gid);
1169 setuid(DU);
1170 }
1171 return (pid);
1172 }
1173 syslog(LOG_ERR, "can't fork");
1174
1175 switch (action) {
1176 case DORETURN:
1177 return (-1);
1178 default:
1179 syslog(LOG_ERR, "bad action (%d) to dofork", action);
1180 /*FALL THRU*/
1181 case DOABORT:
1182 exit(1);
1183 }
1184 /*NOTREACHED*/
1185 }
1186
1187 /*
1188 * Kill child processes to abort current job.
1189 */
1190 static void
1191 abortpr(int signo)
1192 {
1193 (void)unlink(tempfile);
1194 kill(0, SIGINT);
1195 if (ofilter > 0)
1196 kill(ofilter, SIGCONT);
1197 while (wait(NULL) > 0)
1198 ;
1199 exit(0);
1200 }
1201
1202 static void
1203 init(void)
1204 {
1205 int status;
1206 char *s;
1207
1208 if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1209 syslog(LOG_ERR, "can't open printer description file");
1210 exit(1);
1211 } else if (status == -1) {
1212 syslog(LOG_ERR, "unknown printer: %s", printer);
1213 exit(1);
1214 } else if (status == -3)
1215 fatal("potential reference loop detected in printcap file");
1216
1217 if (cgetstr(bp, DEFLP, &LP) == -1)
1218 LP = _PATH_DEFDEVLP;
1219 if (cgetstr(bp, "rp", &RP) == -1)
1220 RP = DEFLP;
1221 if (cgetstr(bp, "lo", &LO) == -1)
1222 LO = DEFLOCK;
1223 if (cgetstr(bp, "st", &ST) == -1)
1224 ST = DEFSTAT;
1225 if (cgetstr(bp, "lf", &LF) == -1)
1226 LF = _PATH_CONSOLE;
1227 if (cgetstr(bp, "sd", &SD) == -1)
1228 SD = _PATH_DEFSPOOL;
1229 if (cgetnum(bp, "du", &DU) < 0)
1230 DU = DEFUID;
1231 if (cgetstr(bp,"ff", &FF) == -1)
1232 FF = DEFFF;
1233 if (cgetnum(bp, "pw", &PW) < 0)
1234 PW = DEFWIDTH;
1235 (void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
1236 if (cgetnum(bp, "pl", &PL) < 0)
1237 PL = DEFLENGTH;
1238 (void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
1239 if (cgetnum(bp,"px", &PX) < 0)
1240 PX = 0;
1241 (void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
1242 if (cgetnum(bp, "py", &PY) < 0)
1243 PY = 0;
1244 (void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
1245 cgetstr(bp, "rm", &RM);
1246 if ((s = checkremote()) != NULL)
1247 syslog(LOG_WARNING, "%s", s);
1248
1249 cgetstr(bp, "af", &AF);
1250 cgetstr(bp, "of", &OF);
1251 cgetstr(bp, "if", &IF);
1252 cgetstr(bp, "rf", &RF);
1253 cgetstr(bp, "tf", &TF);
1254 cgetstr(bp, "nf", &NF);
1255 cgetstr(bp, "df", &DF);
1256 cgetstr(bp, "gf", &GF);
1257 cgetstr(bp, "vf", &VF);
1258 cgetstr(bp, "cf", &CF);
1259 cgetstr(bp, "tr", &TR);
1260
1261 RS = (cgetcap(bp, "rs", ':') != NULL);
1262 SF = (cgetcap(bp, "sf", ':') != NULL);
1263 SH = (cgetcap(bp, "sh", ':') != NULL);
1264 SB = (cgetcap(bp, "sb", ':') != NULL);
1265 HL = (cgetcap(bp, "hl", ':') != NULL);
1266 RW = (cgetcap(bp, "rw", ':') != NULL);
1267
1268 cgetnum(bp, "br", &BR);
1269 if (cgetnum(bp, "fc", &FC) < 0)
1270 FC = 0;
1271 if (cgetnum(bp, "fs", &FS) < 0)
1272 FS = 0;
1273 if (cgetnum(bp, "xc", &XC) < 0)
1274 XC = 0;
1275 if (cgetnum(bp, "xs", &XS) < 0)
1276 XS = 0;
1277 cgetstr(bp, "ms", &MS);
1278
1279 tof = (cgetcap(bp, "fo", ':') == NULL);
1280 }
1281
1282 /*
1283 * Acquire line printer or remote connection.
1284 */
1285 static void
1286 openpr(void)
1287 {
1288 int i, nofile;
1289 char *cp;
1290 extern int rflag;
1291
1292 if (!remote && *LP) {
1293 if ((cp = strchr(LP, '@')))
1294 opennet(cp);
1295 else
1296 opentty();
1297 } else if (remote) {
1298 openrem();
1299 } else {
1300 syslog(LOG_ERR, "%s: no line printer device or host name",
1301 printer);
1302 exit(1);
1303 }
1304
1305 /*
1306 * Start up an output filter, if needed.
1307 */
1308 if ((!remote || rflag) && OF) {
1309 int p[2];
1310
1311 pipe(p);
1312 if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1313 dup2(p[0], 0); /* pipe is std in */
1314 dup2(pfd, 1); /* printer is std out */
1315 closelog();
1316 nofile = sysconf(_SC_OPEN_MAX);
1317 for (i = 3; i < nofile; i++)
1318 (void)close(i);
1319 if ((cp = strrchr(OF, '/')) == NULL)
1320 cp = OF;
1321 else
1322 cp++;
1323 execl(OF, cp, width, length, 0);
1324 syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1325 exit(1);
1326 }
1327 (void)close(p[0]); /* close input side */
1328 ofd = p[1]; /* use pipe for output */
1329 } else {
1330 ofd = pfd;
1331 ofilter = 0;
1332 }
1333 }
1334
1335 /*
1336 * Printer connected directly to the network
1337 * or to a terminal server on the net
1338 */
1339 static void
1340 opennet(char *cp)
1341 {
1342 int i;
1343 int resp, port;
1344 char save_ch;
1345
1346 save_ch = *cp;
1347 *cp = '\0';
1348 port = atoi(LP);
1349 if (port <= 0) {
1350 syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
1351 exit(1);
1352 }
1353 *cp++ = save_ch;
1354
1355 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1356 resp = -1;
1357 pfd = getport(cp, port);
1358 if (pfd < 0 && errno == ECONNREFUSED)
1359 resp = 1;
1360 else if (pfd >= 0) {
1361 /*
1362 * need to delay a bit for rs232 lines
1363 * to stabilize in case printer is
1364 * connected via a terminal server
1365 */
1366 delay(500);
1367 break;
1368 }
1369 if (i == 1) {
1370 if (resp < 0)
1371 pstatus("waiting for %s to come up", LP);
1372 else
1373 pstatus("waiting for access to printer on %s", LP);
1374 }
1375 sleep(i);
1376 }
1377 pstatus("sending to %s port %d", cp, port);
1378 }
1379
1380 /*
1381 * Printer is connected to an RS232 port on this host
1382 */
1383 static void
1384 opentty(void)
1385 {
1386 int i;
1387
1388 for (i = 1; ; i = i < 32 ? i << 1 : i) {
1389 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1390 if (pfd >= 0) {
1391 delay(500);
1392 break;
1393 }
1394 if (errno == ENOENT) {
1395 syslog(LOG_ERR, "%s: %m", LP);
1396 exit(1);
1397 }
1398 if (i == 1)
1399 pstatus("waiting for %s to become ready (offline ?)",
1400 printer);
1401 sleep(i);
1402 }
1403 if (isatty(pfd))
1404 setty();
1405 pstatus("%s is ready and printing", printer);
1406 }
1407
1408 /*
1409 * Printer is on a remote host
1410 */
1411 static void
1412 openrem(void)
1413 {
1414 int i, n;
1415 int resp;
1416
1417 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1418 resp = -1;
1419 pfd = getport(RM, 0);
1420 if (pfd >= 0) {
1421 n = snprintf(line, sizeof(line), "\2%s\n", RP);
1422 if (write(pfd, line, n) == n &&
1423 (resp = response()) == '\0')
1424 break;
1425 (void) close(pfd);
1426 }
1427 if (i == 1) {
1428 if (resp < 0)
1429 pstatus("waiting for %s to come up", RM);
1430 else {
1431 pstatus("waiting for queue to be enabled on %s",
1432 RM);
1433 i = 256;
1434 }
1435 }
1436 sleep(i);
1437 }
1438 pstatus("sending to %s", RM);
1439 }
1440
1441 static void
1442 alarmer(int s)
1443 {
1444 /* nothing */
1445 }
1446
1447 #if !defined(__NetBSD__)
1448 struct bauds {
1449 int baud;
1450 int speed;
1451 } bauds[] = {
1452 50, B50,
1453 75, B75,
1454 110, B110,
1455 134, B134,
1456 150, B150,
1457 200, B200,
1458 300, B300,
1459 600, B600,
1460 1200, B1200,
1461 1800, B1800,
1462 2400, B2400,
1463 4800, B4800,
1464 9600, B9600,
1465 19200, B19200,
1466 38400, B38400,
1467 57600, B57600,
1468 115200, B115200,
1469 0, 0
1470 };
1471 #endif
1472
1473 /*
1474 * setup tty lines.
1475 */
1476 static void
1477 setty(void)
1478 {
1479 struct info i;
1480 char **argv, **ap, *p, *val;
1481
1482 i.fd = pfd;
1483 i.set = i.wset = 0;
1484 if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
1485 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1486 exit(1);
1487 }
1488 if (tcgetattr(i.fd, &i.t) < 0) {
1489 syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1490 exit(1);
1491 }
1492 if (BR > 0) {
1493 #if !defined(__NetBSD__)
1494 struct bauds *bp;
1495 for (bp = bauds; bp->baud; bp++)
1496 if (BR == bp->baud)
1497 break;
1498 if (!bp->baud) {
1499 syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1500 exit(1);
1501 }
1502 cfsetspeed(&i.t, bp->speed);
1503 #else
1504 cfsetspeed(&i.t, BR);
1505 #endif
1506 i.set = 1;
1507 }
1508 if (MS) {
1509 if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
1510 syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
1511 exit(1);
1512 }
1513 if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
1514 syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
1515 printer);
1516
1517 argv = (char **)calloc(256, sizeof(char *));
1518 if (argv == NULL) {
1519 syslog(LOG_ERR, "%s: calloc: %m", printer);
1520 exit(1);
1521 }
1522 p = strdup(MS);
1523 ap = argv;
1524 while ((val = strsep(&p, " \t,")) != NULL) {
1525 *ap++ = strdup(val);
1526 }
1527
1528 for (; *argv; ++argv) {
1529 if (ksearch(&argv, &i))
1530 continue;
1531 if (msearch(&argv, &i))
1532 continue;
1533 syslog(LOG_INFO, "%s: unknown stty flag: %s",
1534 printer, *argv);
1535 }
1536 } else {
1537 if (FC) {
1538 sttyclearflags(&i.t, FC);
1539 i.set = 1;
1540 }
1541 if (FS) {
1542 sttysetflags(&i.t, FS);
1543 i.set = 1;
1544 }
1545 if (XC) {
1546 sttyclearlflags(&i.t, XC);
1547 i.set = 1;
1548 }
1549 if (XS) {
1550 sttysetlflags(&i.t, XS);
1551 i.set = 1;
1552 }
1553 }
1554
1555 if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
1556 syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1557 exit(1);
1558 }
1559 if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
1560 syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
1561 return;
1562 }
1563
1564 #ifdef __STDC__
1565 #include <stdarg.h>
1566 #else
1567 #include <varargs.h>
1568 #endif
1569
1570 static void
1571 #ifdef __STDC__
1572 pstatus(const char *msg, ...)
1573 #else
1574 pstatus(msg, va_alist)
1575 char *msg;
1576 va_dcl
1577 #endif
1578 {
1579 int fd;
1580 char *buf;
1581 va_list ap;
1582
1583 umask(0);
1584 fd = open(ST, O_WRONLY|O_CREAT, 0664);
1585 if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1586 syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1587 exit(1);
1588 }
1589 ftruncate(fd, 0);
1590 #ifdef __STDC__
1591 va_start(ap, msg);
1592 #else
1593 va_start(ap);
1594 #endif
1595 (void)vasprintf(&buf, msg, ap);
1596 va_end(ap);
1597 /* XXX writev */
1598 (void)write(fd, buf, strlen(buf));
1599 (void)write(fd, "\n", 2);
1600 (void)close(fd);
1601 free(buf);
1602 }
1603