Style(9) cleanup.
[dragonfly.git] / usr.sbin / lpr / lpd / printjob.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
1de703da
MD
33 *
34 * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved.
35 * @(#)printjob.c 8.7 (Berkeley) 5/10/95
36 * $FreeBSD: src/usr.sbin/lpr/lpd/printjob.c,v 1.22.2.32 2002/06/19 23:58:16 gad Exp $
be6b9a2e 37 * $DragonFly: src/usr.sbin/lpr/lpd/printjob.c,v 1.3 2004/03/22 22:32:50 cpressey Exp $
984263bc
MD
38 */
39
984263bc
MD
40/*
41 * printjob -- print jobs in the queue.
42 *
43 * NOTE: the lock file is used to pass information to lpq and lprm.
44 * it does not need to be removed because file locks are dynamic.
45 */
46
47#include <sys/param.h>
48#include <sys/wait.h>
49#include <sys/stat.h>
50#include <sys/types.h>
51
52#include <pwd.h>
53#include <unistd.h>
54#include <signal.h>
55#include <syslog.h>
56#include <fcntl.h>
57#include <dirent.h>
58#include <errno.h>
59#include <stdio.h>
60#include <string.h>
61#include <stdlib.h>
62#include <sys/ioctl.h>
63#include <termios.h>
64#include <time.h>
65#include "lp.h"
66#include "lp.local.h"
67#include "pathnames.h"
68#include "extern.h"
69
70#define DORETURN 0 /* dofork should return "can't fork" error */
71#define DOABORT 1 /* dofork should just die if fork() fails */
72
73/*
74 * Error tokens
75 */
76#define REPRINT -2
77#define ERROR -1
78#define OK 0
79#define FATALERR 1
80#define NOACCT 2
81#define FILTERERR 3
82#define ACCESS 4
83
84static dev_t fdev; /* device of file pointed to by symlink */
85static ino_t fino; /* inode of file pointed to by symlink */
86static FILE *cfp; /* control file */
87static pid_t of_pid; /* process id of output filter, if any */
88static int child; /* id of any filters */
89static int job_dfcnt; /* count of datafiles in current user job */
90static int lfd; /* lock file descriptor */
91static int ofd; /* output filter file descriptor */
92static int tfd = -1; /* output filter temp file output */
93static int pfd; /* prstatic inter file descriptor */
94static int prchild; /* id of pr process */
95static char title[80]; /* ``pr'' title */
96static char locale[80]; /* ``pr'' locale */
97
98/* these two are set from pp->daemon_user, but only if they are needed */
99static char *daemon_uname; /* set from pwd->pw_name */
100static int daemon_defgid;
101
102static char class[32]; /* classification field */
103static char origin_host[MAXHOSTNAMELEN]; /* user's host machine */
104 /* indentation size in static characters */
105static char indent[10] = "-i0";
106static char jobname[100]; /* job or file name */
107static char length[10] = "-l"; /* page length in lines */
108static char logname[32]; /* user's login name */
109static char pxlength[10] = "-y"; /* page length in pixels */
110static char pxwidth[10] = "-x"; /* page width in pixels */
111/* tempstderr is the filename used to catch stderr from exec-ing filters */
112static char tempstderr[] = "errs.XXXXXXX";
113static char width[10] = "-w"; /* page width in static characters */
114#define TFILENAME "fltXXXXXX"
115static char tfile[] = TFILENAME; /* file name for filter output */
116
117static void abortpr(int _signo);
118static void alarmhandler(int _signo);
119static void banner(struct printer *_pp, char *_name1, char *_name2);
120static int dofork(const struct printer *_pp, int _action);
121static int dropit(int _c);
122static int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av,
123 int _infd, int _outfd);
124static void init(struct printer *_pp);
125static void openpr(const struct printer *_pp);
126static void opennet(const struct printer *_pp);
127static void opentty(const struct printer *_pp);
128static void openrem(const struct printer *pp);
129static int print(struct printer *_pp, int _format, char *_file);
130static int printit(struct printer *_pp, char *_file);
131static void pstatus(const struct printer *_pp, const char *_msg, ...)
132 __printflike(2, 3);
133static char response(const struct printer *_pp);
134static void scan_out(struct printer *_pp, int _scfd, char *_scsp,
135 int _dlm);
136static char *scnline(int _key, char *_p, int _c);
137static int sendfile(struct printer *_pp, int _type, char *_file,
138 char _format, int _copyreq);
139static int sendit(struct printer *_pp, char *_file);
140static void sendmail(struct printer *_pp, char *_userid, int _bombed);
141static void setty(const struct printer *_pp);
142
143void
144printjob(struct printer *pp)
145{
146 struct stat stb;
be6b9a2e 147 struct jobqueue *q, **qp;
984263bc 148 struct jobqueue **queue;
be6b9a2e 149 int i, nitems;
984263bc
MD
150 off_t pidoff;
151 pid_t printpid;
152 int errcnt, jobcount, tempfd;
153
154 jobcount = 0;
155 init(pp); /* set up capabilities */
156 (void) write(1, "", 1); /* ack that daemon is started */
157 (void) close(2); /* set up log file */
158 if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
159 syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
160 pp->log_file);
161 (void) open(_PATH_DEVNULL, O_WRONLY);
162 }
163 setgid(getegid());
164 printpid = getpid(); /* for use with lprm */
165 setpgrp(0, printpid);
166
167 /*
168 * At initial lpd startup, printjob may be called with various
169 * signal handlers in effect. After that initial startup, any
170 * calls to printjob will have a *different* set of signal-handlers
171 * in effect. Make sure all handlers are the ones we want.
172 */
173 signal(SIGCHLD, SIG_DFL);
174 signal(SIGHUP, abortpr);
175 signal(SIGINT, abortpr);
176 signal(SIGQUIT, abortpr);
177 signal(SIGTERM, abortpr);
178
179 /*
180 * uses short form file names
181 */
182 if (chdir(pp->spool_dir) < 0) {
183 syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer,
184 pp->spool_dir);
185 exit(1);
186 }
187 if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS))
188 exit(0); /* printing disabled */
189 lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
190 LOCK_FILE_MODE);
191 if (lfd < 0) {
192 if (errno == EWOULDBLOCK) /* active daemon present */
193 exit(0);
194 syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
195 pp->lock_file);
196 exit(1);
197 }
198 /* turn off non-blocking mode (was turned on for lock effects only) */
199 if (fcntl(lfd, F_SETFL, 0) < 0) {
200 syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer,
201 pp->lock_file);
202 exit(1);
203 }
204 ftruncate(lfd, 0);
205 /*
206 * write process id for others to know
207 */
208 sprintf(line, "%u\n", printpid);
209 pidoff = i = strlen(line);
210 if (write(lfd, line, i) != i) {
211 syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
212 pp->lock_file);
213 exit(1);
214 }
215 /*
216 * search the spool directory for work and sort by queue order.
217 */
218 if ((nitems = getq(pp, &queue)) < 0) {
219 syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
220 pp->spool_dir);
221 exit(1);
222 }
223 if (nitems == 0) /* no work to do */
224 exit(0);
225 if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */
226 if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0)
227 syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
228 pp->lock_file);
229 }
230
231 /* create a file which will be used to hold stderr from filters */
232 if ((tempfd = mkstemp(tempstderr)) == -1) {
233 syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
234 tempstderr);
235 exit(1);
236 }
237 if ((i = fchmod(tempfd, 0664)) == -1) {
238 syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
239 tempstderr);
240 exit(1);
241 }
242 /* lpd doesn't need it to be open, it just needs it to exist */
243 close(tempfd);
244
245 openpr(pp); /* open printer or remote */
246again:
247 /*
248 * we found something to do now do it --
249 * write the name of the current control file into the lock file
250 * so the spool queue program can tell what we're working on
251 */
252 for (qp = queue; nitems--; free((char *) q)) {
253 q = *qp++;
254 if (stat(q->job_cfname, &stb) < 0)
255 continue;
256 errcnt = 0;
257 restart:
258 (void) lseek(lfd, pidoff, 0);
259 (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname);
260 i = strlen(line);
261 if (write(lfd, line, i) != i)
262 syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
263 pp->lock_file);
264 if (!pp->remote)
265 i = printit(pp, q->job_cfname);
266 else
267 i = sendit(pp, q->job_cfname);
268 /*
269 * Check to see if we are supposed to stop printing or
270 * if we are to rebuild the queue.
271 */
272 if (fstat(lfd, &stb) == 0) {
273 /* stop printing before starting next job? */
274 if (stb.st_mode & LFM_PRINT_DIS)
275 goto done;
276 /* rebuild queue (after lpc topq) */
277 if (stb.st_mode & LFM_RESET_QUE) {
278 for (free(q); nitems--; free(q))
279 q = *qp++;
280 if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE)
281 < 0)
282 syslog(LOG_WARNING,
283 "%s: fchmod(%s): %m",
284 pp->printer, pp->lock_file);
285 break;
286 }
287 }
288 if (i == OK) /* all files of this job printed */
289 jobcount++;
290 else if (i == REPRINT && ++errcnt < 5) {
291 /* try reprinting the job */
292 syslog(LOG_INFO, "restarting %s", pp->printer);
293 if (of_pid > 0) {
294 kill(of_pid, SIGCONT); /* to be sure */
295 (void) close(ofd);
296 while ((i = wait(NULL)) > 0 && i != of_pid)
297 ;
298 if (i < 0)
299 syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m",
300 pp->printer, of_pid);
301 of_pid = 0;
302 }
303 (void) close(pfd); /* close printer */
304 if (ftruncate(lfd, pidoff) < 0)
305 syslog(LOG_WARNING, "%s: ftruncate(%s): %m",
306 pp->printer, pp->lock_file);
307 openpr(pp); /* try to reopen printer */
308 goto restart;
309 } else {
310 syslog(LOG_WARNING, "%s: job could not be %s (%s)",
311 pp->printer,
312 pp->remote ? "sent to remote host" : "printed",
313 q->job_cfname);
314 if (i == REPRINT) {
315 /* ensure we don't attempt this job again */
316 (void) unlink(q->job_cfname);
317 q->job_cfname[0] = 'd';
318 (void) unlink(q->job_cfname);
319 if (logname[0])
320 sendmail(pp, logname, FATALERR);
321 }
322 }
323 }
324 free(queue);
325 /*
326 * search the spool directory for more work.
327 */
328 if ((nitems = getq(pp, &queue)) < 0) {
329 syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
330 pp->spool_dir);
331 exit(1);
332 }
333 if (nitems == 0) { /* no more work to do */
334 done:
335 if (jobcount > 0) { /* jobs actually printed */
336 if (!pp->no_formfeed && !pp->tof)
337 (void) write(ofd, pp->form_feed,
338 strlen(pp->form_feed));
339 if (pp->trailer != NULL) /* output trailer */
340 (void) write(ofd, pp->trailer,
341 strlen(pp->trailer));
342 }
343 (void) close(ofd);
344 (void) wait(NULL);
345 (void) unlink(tempstderr);
346 exit(0);
347 }
348 goto again;
349}
350
351char fonts[4][50]; /* fonts for troff */
352
353char ifonts[4][40] = {
354 _PATH_VFONTR,
355 _PATH_VFONTI,
356 _PATH_VFONTB,
357 _PATH_VFONTS,
358};
359
360/*
361 * The remaining part is the reading of the control file (cf)
362 * and performing the various actions.
363 */
364static int
365printit(struct printer *pp, char *file)
366{
be6b9a2e 367 int i;
984263bc
MD
368 char *cp;
369 int bombed, didignorehdr;
370
371 bombed = OK;
372 didignorehdr = 0;
373 /*
374 * open control file; ignore if no longer there.
375 */
376 if ((cfp = fopen(file, "r")) == NULL) {
377 syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file);
378 return (OK);
379 }
380 /*
381 * Reset troff fonts.
382 */
383 for (i = 0; i < 4; i++)
384 strcpy(fonts[i], ifonts[i]);
385 sprintf(&width[2], "%ld", pp->page_width);
386 strcpy(indent+2, "0");
387
388 /* initialize job-specific count of datafiles processed */
389 job_dfcnt = 0;
390
391 /*
392 * read the control file for work to do
393 *
394 * file format -- first character in the line is a command
395 * rest of the line is the argument.
396 * valid commands are:
397 *
398 * S -- "stat info" for symbolic link protection
399 * J -- "job name" on banner page
400 * C -- "class name" on banner page
401 * L -- "literal" user's name to print on banner
402 * T -- "title" for pr
403 * H -- "host name" of machine where lpr was done
404 * P -- "person" user's login name
405 * I -- "indent" amount to indent output
406 * R -- laser dpi "resolution"
407 * f -- "file name" name of text file to print
408 * l -- "file name" text file with control chars
409 * o -- "file name" postscript file, according to
410 * the RFC. Here it is treated like an 'f'.
411 * p -- "file name" text file to print with pr(1)
412 * t -- "file name" troff(1) file to print
413 * n -- "file name" ditroff(1) file to print
414 * d -- "file name" dvi file to print
415 * g -- "file name" plot(1G) file to print
416 * v -- "file name" plain raster file to print
417 * c -- "file name" cifplot file to print
418 * 1 -- "R font file" for troff
419 * 2 -- "I font file" for troff
420 * 3 -- "B font file" for troff
421 * 4 -- "S font file" for troff
422 * N -- "name" of file (used by lpq)
423 * U -- "unlink" name of file to remove
424 * (after we print it. (Pass 2 only)).
425 * M -- "mail" to user when done printing
426 * Z -- "locale" for pr
427 *
428 * getline reads a line and expands tabs to blanks
429 */
430
431 /* pass 1 */
432
433 while (getline(cfp))
434 switch (line[0]) {
435 case 'H':
436 strlcpy(origin_host, line + 1, sizeof(origin_host));
437 if (class[0] == '\0') {
438 strlcpy(class, line+1, sizeof(class));
439 }
440 continue;
441
442 case 'P':
443 strlcpy(logname, line + 1, sizeof(logname));
444 if (pp->restricted) { /* restricted */
445 if (getpwnam(logname) == NULL) {
446 bombed = NOACCT;
447 sendmail(pp, line+1, bombed);
448 goto pass2;
449 }
450 }
451 continue;
452
453 case 'S':
454 cp = line+1;
455 i = 0;
456 while (*cp >= '0' && *cp <= '9')
457 i = i * 10 + (*cp++ - '0');
458 fdev = i;
459 cp++;
460 i = 0;
461 while (*cp >= '0' && *cp <= '9')
462 i = i * 10 + (*cp++ - '0');
463 fino = i;
464 continue;
465
466 case 'J':
467 if (line[1] != '\0') {
468 strlcpy(jobname, line + 1, sizeof(jobname));
469 } else
470 strcpy(jobname, " ");
471 continue;
472
473 case 'C':
474 if (line[1] != '\0')
475 strlcpy(class, line + 1, sizeof(class));
476 else if (class[0] == '\0') {
477 /* XXX - why call gethostname instead of
478 * just strlcpy'ing local_host? */
479 gethostname(class, sizeof(class));
480 class[sizeof(class) - 1] = '\0';
481 }
482 continue;
483
484 case 'T': /* header title for pr */
485 strlcpy(title, line + 1, sizeof(title));
486 continue;
487
488 case 'L': /* identification line */
489 if (!pp->no_header && !pp->header_last)
490 banner(pp, line+1, jobname);
491 continue;
492
493 case '1': /* troff fonts */
494 case '2':
495 case '3':
496 case '4':
497 if (line[1] != '\0') {
498 strlcpy(fonts[line[0]-'1'], line + 1,
499 (size_t)50);
500 }
501 continue;
502
503 case 'W': /* page width */
504 strlcpy(width+2, line + 1, sizeof(width) - 2);
505 continue;
506
507 case 'I': /* indent amount */
508 strlcpy(indent+2, line + 1, sizeof(indent) - 2);
509 continue;
510
511 case 'Z': /* locale for pr */
512 strlcpy(locale, line + 1, sizeof(locale));
513 continue;
514
515 default: /* some file to print */
516 /* only lowercase cmd-codes include a file-to-print */
517 if ((line[0] < 'a') || (line[0] > 'z')) {
518 /* ignore any other lines */
519 if (lflag <= 1)
520 continue;
521 if (!didignorehdr) {
522 syslog(LOG_INFO, "%s: in %s :",
523 pp->printer, file);
524 didignorehdr = 1;
525 }
526 syslog(LOG_INFO, "%s: ignoring line: '%c' %s",
527 pp->printer, line[0], &line[1]);
528 continue;
529 }
530 i = print(pp, line[0], line+1);
531 switch (i) {
532 case ERROR:
533 if (bombed == OK)
534 bombed = FATALERR;
535 break;
536 case REPRINT:
537 (void) fclose(cfp);
538 return (REPRINT);
539 case FILTERERR:
540 case ACCESS:
541 bombed = i;
542 sendmail(pp, logname, bombed);
543 }
544 title[0] = '\0';
545 continue;
546
547 case 'N':
548 case 'U':
549 case 'M':
550 case 'R':
551 continue;
552 }
553
554 /* pass 2 */
555
556pass2:
557 fseek(cfp, 0L, 0);
558 while (getline(cfp))
559 switch (line[0]) {
560 case 'L': /* identification line */
561 if (!pp->no_header && pp->header_last)
562 banner(pp, line+1, jobname);
563 continue;
564
565 case 'M':
566 if (bombed < NOACCT) /* already sent if >= NOACCT */
567 sendmail(pp, line+1, bombed);
568 continue;
569
570 case 'U':
571 if (strchr(line+1, '/'))
572 continue;
573 (void) unlink(line+1);
574 }
575 /*
576 * clean-up in case another control file exists
577 */
578 (void) fclose(cfp);
579 (void) unlink(file);
580 return (bombed == OK ? OK : ERROR);
581}
582
583/*
584 * Print a file.
585 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
586 * Return -1 if a non-recoverable error occured,
587 * 2 if the filter detected some errors (but printed the job anyway),
588 * 1 if we should try to reprint this job and
589 * 0 if all is well.
590 * Note: all filters take stdin as the file, stdout as the printer,
591 * stderr as the log file, and must not ignore SIGINT.
592 */
593static int
594print(struct printer *pp, int format, char *file)
595{
be6b9a2e
CP
596 int n, i;
597 char *prog;
984263bc
MD
598 int fi, fo;
599 FILE *fp;
600 char *av[15], buf[BUFSIZ];
601 pid_t wpid;
602 int p[2], retcode, stopped, wstatus, wstatus_set;
603 struct stat stb;
604
605 if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) {
606 syslog(LOG_INFO, "%s: unable to open %s ('%c' line)",
607 pp->printer, file, format);
608 return (ERROR);
609 }
610 /*
611 * Check to see if data file is a symbolic link. If so, it should
612 * still point to the same file or someone is trying to print
613 * something he shouldn't.
614 */
615 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
616 (stb.st_dev != fdev || stb.st_ino != fino))
617 return (ACCESS);
618
619 job_dfcnt++; /* increment datafile counter for this job */
620 stopped = 0; /* output filter is not stopped */
621
622 /* everything seems OK, start it up */
623 if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */
624 (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
625 pp->tof = 1;
626 }
627 if (pp->filters[LPF_INPUT] == NULL
628 && (format == 'f' || format == 'l' || format == 'o')) {
629 pp->tof = 0;
630 while ((n = read(fi, buf, BUFSIZ)) > 0)
631 if (write(ofd, buf, n) != n) {
632 (void) close(fi);
633 return (REPRINT);
634 }
635 (void) close(fi);
636 return (OK);
637 }
638 switch (format) {
639 case 'p': /* print file using 'pr' */
640 if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */
641 prog = _PATH_PR;
642 i = 0;
643 av[i++] = "pr";
644 av[i++] = width;
645 av[i++] = length;
646 av[i++] = "-h";
647 av[i++] = *title ? title : " ";
648 av[i++] = "-L";
649 av[i++] = *locale ? locale : "C";
650 av[i++] = "-F";
651 av[i] = 0;
652 fo = ofd;
653 goto start;
654 }
655 pipe(p);
656 if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */
657 dup2(fi, 0); /* file is stdin */
658 dup2(p[1], 1); /* pipe is stdout */
659 closelog();
660 closeallfds(3);
661 execl(_PATH_PR, "pr", width, length,
662 "-h", *title ? title : " ",
663 "-L", *locale ? locale : "C",
664 "-F", (char *)0);
665 syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
666 exit(2);
667 }
668 (void) close(p[1]); /* close output side */
669 (void) close(fi);
670 if (prchild < 0) {
671 prchild = 0;
672 (void) close(p[0]);
673 return (ERROR);
674 }
675 fi = p[0]; /* use pipe for input */
676 case 'f': /* print plain text file */
677 prog = pp->filters[LPF_INPUT];
678 av[1] = width;
679 av[2] = length;
680 av[3] = indent;
681 n = 4;
682 break;
683 case 'o': /* print postscript file */
684 /*
685 * Treat this as a "plain file with control characters", and
686 * assume the standard LPF_INPUT filter will recognize that
687 * the data is postscript and know what to do with it. These
688 * 'o'-file requests could come from MacOS 10.1 systems.
689 * (later versions of MacOS 10 will explicitly use 'l')
690 * A postscript file can contain binary data, which is why 'l'
691 * is somewhat more appropriate than 'f'.
692 */
693 /* FALLTHROUGH */
694 case 'l': /* like 'f' but pass control characters */
695 prog = pp->filters[LPF_INPUT];
696 av[1] = "-c";
697 av[2] = width;
698 av[3] = length;
699 av[4] = indent;
700 n = 5;
701 break;
702 case 'r': /* print a fortran text file */
703 prog = pp->filters[LPF_FORTRAN];
704 av[1] = width;
705 av[2] = length;
706 n = 3;
707 break;
708 case 't': /* print troff output */
709 case 'n': /* print ditroff output */
710 case 'd': /* print tex output */
711 (void) unlink(".railmag");
712 if ((fo = creat(".railmag", FILMOD)) < 0) {
713 syslog(LOG_ERR, "%s: cannot create .railmag",
714 pp->printer);
715 (void) unlink(".railmag");
716 } else {
717 for (n = 0; n < 4; n++) {
718 if (fonts[n][0] != '/')
719 (void) write(fo, _PATH_VFONT,
720 sizeof(_PATH_VFONT) - 1);
721 (void) write(fo, fonts[n], strlen(fonts[n]));
722 (void) write(fo, "\n", 1);
723 }
724 (void) close(fo);
725 }
726 prog = (format == 't') ? pp->filters[LPF_TROFF]
727 : ((format == 'n') ? pp->filters[LPF_DITROFF]
728 : pp->filters[LPF_DVI]);
729 av[1] = pxwidth;
730 av[2] = pxlength;
731 n = 3;
732 break;
733 case 'c': /* print cifplot output */
734 prog = pp->filters[LPF_CIFPLOT];
735 av[1] = pxwidth;
736 av[2] = pxlength;
737 n = 3;
738 break;
739 case 'g': /* print plot(1G) output */
740 prog = pp->filters[LPF_GRAPH];
741 av[1] = pxwidth;
742 av[2] = pxlength;
743 n = 3;
744 break;
745 case 'v': /* print raster output */
746 prog = pp->filters[LPF_RASTER];
747 av[1] = pxwidth;
748 av[2] = pxlength;
749 n = 3;
750 break;
751 default:
752 (void) close(fi);
753 syslog(LOG_ERR, "%s: illegal format character '%c'",
754 pp->printer, format);
755 return (ERROR);
756 }
757 if (prog == NULL) {
758 (void) close(fi);
759 syslog(LOG_ERR,
760 "%s: no filter found in printcap for format character '%c'",
761 pp->printer, format);
762 return (ERROR);
763 }
764 if ((av[0] = strrchr(prog, '/')) != NULL)
765 av[0]++;
766 else
767 av[0] = prog;
768 av[n++] = "-n";
769 av[n++] = logname;
770 av[n++] = "-h";
771 av[n++] = origin_host;
772 av[n++] = pp->acct_file;
773 av[n] = 0;
774 fo = pfd;
775 if (of_pid > 0) { /* stop output filter */
776 write(ofd, "\031\1", 2);
777 while ((wpid =
778 wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid)
779 ;
780 if (wpid < 0)
781 syslog(LOG_WARNING,
782 "%s: after stopping 'of', wait3() returned: %m",
783 pp->printer);
784 else if (!WIFSTOPPED(wstatus)) {
785 (void) close(fi);
786 syslog(LOG_WARNING, "%s: output filter died "
787 "(pid=%d retcode=%d termsig=%d)",
788 pp->printer, of_pid, WEXITSTATUS(wstatus),
789 WTERMSIG(wstatus));
790 return (REPRINT);
791 }
792 stopped++;
793 }
794start:
795 if ((child = dofork(pp, DORETURN)) == 0) { /* child */
796 dup2(fi, 0);
797 dup2(fo, 1);
798 /* setup stderr for the filter (child process)
799 * so it goes to our temporary errors file */
800 n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
801 if (n >= 0)
802 dup2(n, 2);
803 closelog();
804 closeallfds(3);
805 execv(prog, av);
806 syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer,
807 prog);
808 exit(2);
809 }
810 (void) close(fi);
811 wstatus_set = 0;
812 if (child < 0)
813 retcode = 100;
814 else {
815 while ((wpid = wait(&wstatus)) > 0 && wpid != child)
816 ;
817 if (wpid < 0) {
818 retcode = 100;
819 syslog(LOG_WARNING,
820 "%s: after execv(%s), wait() returned: %m",
821 pp->printer, prog);
822 } else {
823 wstatus_set = 1;
824 retcode = WEXITSTATUS(wstatus);
825 }
826 }
827 child = 0;
828 prchild = 0;
829 if (stopped) { /* restart output filter */
830 if (kill(of_pid, SIGCONT) < 0) {
831 syslog(LOG_ERR, "cannot restart output filter");
832 exit(1);
833 }
834 }
835 pp->tof = 0;
836
837 /* Copy the filter's output to "lf" logfile */
838 if ((fp = fopen(tempstderr, "r"))) {
839 while (fgets(buf, sizeof(buf), fp))
840 fputs(buf, stderr);
841 fclose(fp);
842 }
843
844 if (wstatus_set && !WIFEXITED(wstatus)) {
845 syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
846 pp->printer, format, WTERMSIG(wstatus));
847 return (ERROR);
848 }
849 switch (retcode) {
850 case 0:
851 pp->tof = 1;
852 return (OK);
853 case 1:
854 return (REPRINT);
855 case 2:
856 return (ERROR);
857 default:
858 syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
859 pp->printer, format, retcode);
860 return (FILTERERR);
861 }
862}
863
864/*
865 * Send the daemon control file (cf) and any data files.
866 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
867 * 0 if all is well.
868 */
869static int
870sendit(struct printer *pp, char *file)
871{
872 int dfcopies, err, i;
873 char *cp, last[BUFSIZ];
874
875 /*
876 * open control file
877 */
878 if ((cfp = fopen(file, "r")) == NULL)
879 return (OK);
880
881 /* initialize job-specific count of datafiles processed */
882 job_dfcnt = 0;
883
884 /*
885 * read the control file for work to do
886 *
887 * file format -- first character in the line is a command
888 * rest of the line is the argument.
889 * commands of interest are:
890 *
891 * a-z -- "file name" name of file to print
892 * U -- "unlink" name of file to remove
893 * (after we print it. (Pass 2 only)).
894 */
895
896 /*
897 * pass 1
898 */
899 err = OK;
900 while (getline(cfp)) {
901 again:
902 if (line[0] == 'S') {
903 cp = line+1;
904 i = 0;
905 while (*cp >= '0' && *cp <= '9')
906 i = i * 10 + (*cp++ - '0');
907 fdev = i;
908 cp++;
909 i = 0;
910 while (*cp >= '0' && *cp <= '9')
911 i = i * 10 + (*cp++ - '0');
912 fino = i;
913 } else if (line[0] == 'H') {
914 strlcpy(origin_host, line + 1, sizeof(origin_host));
915 if (class[0] == '\0') {
916 strlcpy(class, line + 1, sizeof(class));
917 }
918 } else if (line[0] == 'P') {
919 strlcpy(logname, line + 1, sizeof(logname));
920 if (pp->restricted) { /* restricted */
921 if (getpwnam(logname) == NULL) {
922 sendmail(pp, line+1, NOACCT);
923 err = ERROR;
924 break;
925 }
926 }
927 } else if (line[0] == 'I') {
928 strlcpy(indent+2, line + 1, sizeof(indent) - 2);
929 } else if (line[0] >= 'a' && line[0] <= 'z') {
930 dfcopies = 1;
931 strcpy(last, line);
932 while ((i = getline(cfp)) != 0) {
933 if (strcmp(last, line) != 0)
934 break;
935 dfcopies++;
936 }
937 switch (sendfile(pp, '\3', last+1, *last, dfcopies)) {
938 case OK:
939 if (i)
940 goto again;
941 break;
942 case REPRINT:
943 (void) fclose(cfp);
944 return (REPRINT);
945 case ACCESS:
946 sendmail(pp, logname, ACCESS);
947 case ERROR:
948 err = ERROR;
949 }
950 break;
951 }
952 }
953 if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) {
954 (void) fclose(cfp);
955 return (REPRINT);
956 }
957 /*
958 * pass 2
959 */
960 fseek(cfp, 0L, 0);
961 while (getline(cfp))
962 if (line[0] == 'U' && !strchr(line+1, '/'))
963 (void) unlink(line+1);
964 /*
965 * clean-up in case another control file exists
966 */
967 (void) fclose(cfp);
968 (void) unlink(file);
969 return (err);
970}
971
972/*
973 * Send a data file to the remote machine and spool it.
974 * Return positive if we should try resending.
975 */
976static int
977sendfile(struct printer *pp, int type, char *file, char format, int copyreq)
978{
979 int i, amt;
980 struct stat stb;
981 char *av[15], *filtcmd;
982 char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4];
983 int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc;
984
985 statrc = lstat(file, &stb);
986 if (statrc < 0) {
987 syslog(LOG_ERR, "%s: error from lstat(%s): %m",
988 pp->printer, file);
989 return (ERROR);
990 }
991 sfd = open(file, O_RDONLY);
992 if (sfd < 0) {
993 syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m",
994 pp->printer, file);
995 return (ERROR);
996 }
997 /*
998 * Check to see if data file is a symbolic link. If so, it should
999 * still point to the same file or someone is trying to print something
1000 * he shouldn't.
1001 */
1002 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 &&
1003 (stb.st_dev != fdev || stb.st_ino != fino)) {
1004 close(sfd);
1005 return (ACCESS);
1006 }
1007
1008 /* Everything seems OK for reading the file, now to send it */
1009 filtcmd = NULL;
1010 sizerr = 0;
1011 tfd = -1;
1012 if (type == '\3') {
1013 /*
1014 * Type == 3 means this is a datafile, not a control file.
1015 * Increment the counter of data-files in this job, and
1016 * then check for input or output filters (which are only
1017 * applied to datafiles, not control files).
1018 */
1019 job_dfcnt++;
1020
1021 /*
1022 * Note that here we are filtering datafiles, one at a time,
1023 * as they are sent to the remote machine. Here, the *only*
1024 * difference between an input filter (`if=') and an output
1025 * filter (`of=') is the argument list that the filter is
1026 * started up with. Here, the output filter is executed
1027 * for each individual file as it is sent. This is not the
1028 * same as local print queues, where the output filter is
1029 * started up once, and then all jobs are passed thru that
1030 * single invocation of the output filter.
1031 *
1032 * Also note that a queue for a remote-machine can have an
1033 * input filter or an output filter, but not both.
1034 */
1035 if (pp->filters[LPF_INPUT]) {
1036 filtcmd = pp->filters[LPF_INPUT];
1037 av[0] = filtcmd;
1038 narg = 0;
1039 strcpy(opt_c, "-c");
1040 strcpy(opt_h, "-h");
1041 strcpy(opt_n, "-n");
1042 if (format == 'l')
1043 av[++narg] = opt_c;
1044 av[++narg] = width;
1045 av[++narg] = length;
1046 av[++narg] = indent;
1047 av[++narg] = opt_n;
1048 av[++narg] = logname;
1049 av[++narg] = opt_h;
1050 av[++narg] = origin_host;
1051 av[++narg] = pp->acct_file;
1052 av[++narg] = NULL;
1053 } else if (pp->filters[LPF_OUTPUT]) {
1054 filtcmd = pp->filters[LPF_OUTPUT];
1055 av[0] = filtcmd;
1056 narg = 0;
1057 av[++narg] = width;
1058 av[++narg] = length;
1059 av[++narg] = NULL;
1060 }
1061 }
1062 if (filtcmd) {
1063 /*
1064 * If there is an input or output filter, we have to run
1065 * the datafile thru that filter and store the result as
1066 * a temporary spool file, because the protocol requires
1067 * that we send the remote host the file-size before we
1068 * start to send any of the data.
1069 */
1070 strcpy(tfile, TFILENAME);
1071 tfd = mkstemp(tfile);
1072 if (tfd == -1) {
1073 syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
1074 TFILENAME);
1075 sfres = ERROR;
1076 goto return_sfres;
1077 }
1078 filtstat = execfilter(pp, filtcmd, av, sfd, tfd);
1079
1080 /* process the return-code from the filter */
1081 switch (filtstat) {
1082 case 0:
1083 break;
1084 case 1:
1085 sfres = REPRINT;
1086 goto return_sfres;
1087 case 2:
1088 sfres = ERROR;
1089 goto return_sfres;
1090 default:
1091 syslog(LOG_WARNING,
1092 "%s: filter '%c' exited (retcode=%d)",
1093 pp->printer, format, filtstat);
1094 sfres = FILTERERR;
1095 goto return_sfres;
1096 }
1097 statrc = fstat(tfd, &stb); /* to find size of tfile */
1098 if (statrc < 0) {
1099 syslog(LOG_ERR,
1100 "%s: error processing 'if', fstat(%s): %m",
1101 pp->printer, tfile);
1102 sfres = ERROR;
1103 goto return_sfres;
1104 }
1105 close(sfd);
1106 sfd = tfd;
1107 lseek(sfd, 0, SEEK_SET);
1108 }
1109
1110 copycnt = 0;
1111sendagain:
1112 copycnt++;
1113
1114 if (copycnt < 2)
1115 (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
1116 else
1117 (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size,
1118 file, copycnt);
1119 amt = strlen(buf);
1120 for (i = 0; ; i++) {
1121 if (write(pfd, buf, amt) != amt ||
1122 (resp = response(pp)) < 0 || resp == '\1') {
1123 sfres = REPRINT;
1124 goto return_sfres;
1125 } else if (resp == '\0')
1126 break;
1127 if (i == 0)
1128 pstatus(pp,
1129 "no space on remote; waiting for queue to drain");
1130 if (i == 10)
1131 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
1132 pp->printer, pp->remote_host);
1133 sleep(5 * 60);
1134 }
1135 if (i)
1136 pstatus(pp, "sending to %s", pp->remote_host);
1137 /*
1138 * XXX - we should change trstat_init()/trstat_write() to include
1139 * the copycnt in the statistics record it may write.
1140 */
1141 if (type == '\3')
1142 trstat_init(pp, file, job_dfcnt);
1143 for (i = 0; i < stb.st_size; i += BUFSIZ) {
1144 amt = BUFSIZ;
1145 if (i + amt > stb.st_size)
1146 amt = stb.st_size - i;
1147 if (sizerr == 0 && read(sfd, buf, amt) != amt)
1148 sizerr = 1;
1149 if (write(pfd, buf, amt) != amt) {
1150 sfres = REPRINT;
1151 goto return_sfres;
1152 }
1153 }
1154
1155 if (sizerr) {
1156 syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
1157 /* tell recvjob to ignore this file */
1158 (void) write(pfd, "\1", 1);
1159 sfres = ERROR;
1160 goto return_sfres;
1161 }
1162 if (write(pfd, "", 1) != 1 || response(pp)) {
1163 sfres = REPRINT;
1164 goto return_sfres;
1165 }
1166 if (type == '\3') {
1167 trstat_write(pp, TR_SENDING, stb.st_size, logname,
1168 pp->remote_host, origin_host);
1169 /*
1170 * Usually we only need to send one copy of a datafile,
1171 * because the control-file will simply print the same
1172 * file multiple times. However, some printers ignore
1173 * the control file, and simply print each data file as
1174 * it arrives. For such "remote hosts", we need to
1175 * transfer the same data file multiple times. Such a
1176 * a host is indicated by adding 'rc' to the printcap
1177 * entry.
1178 * XXX - Right now this ONLY works for remote hosts which
1179 * do ignore the name of the data file, because
1180 * this sends the file multiple times with slight
1181 * changes to the filename. To do this right would
1182 * require that we also rewrite the control file
1183 * to match those filenames.
1184 */
1185 if (pp->resend_copies && (copycnt < copyreq)) {
1186 lseek(sfd, 0, SEEK_SET);
1187 goto sendagain;
1188 }
1189 }
1190 sfres = OK;
1191
1192return_sfres:
1193 (void)close(sfd);
1194 if (tfd != -1) {
1195 /*
1196 * If tfd is set, then it is the same value as sfd, and
1197 * therefore it is already closed at this point. All
1198 * we need to do is remove the temporary file.
1199 */
1200 tfd = -1;
1201 unlink(tfile);
1202 }
1203 return (sfres);
1204}
1205
1206/*
1207 * This routine is called to execute one of the filters as was
1208 * specified in a printcap entry. While the child-process will read
1209 * all of 'infd', it is up to the caller to close that file descriptor
1210 * in the parent process.
1211 */
1212static int
1213execfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd)
1214{
1215 pid_t fpid, wpid;
1216 int errfd, retcode, wstatus;
1217 FILE *errfp;
1218 char buf[BUFSIZ], *slash;
1219
1220 fpid = dofork(pp, DORETURN);
1221 if (fpid != 0) {
1222 /*
1223 * This is the parent process, which just waits for the child
1224 * to complete and then returns the result. Note that it is
1225 * the child process which reads the input stream.
1226 */
1227 if (fpid < 0)
1228 retcode = 100;
1229 else {
1230 while ((wpid = wait(&wstatus)) > 0 &&
1231 wpid != fpid)
1232 ;
1233 if (wpid < 0) {
1234 retcode = 100;
1235 syslog(LOG_WARNING,
1236 "%s: after execv(%s), wait() returned: %m",
1237 pp->printer, f_cmd);
1238 } else
1239 retcode = WEXITSTATUS(wstatus);
1240 }
1241
1242 /*
1243 * Copy everything the filter wrote to stderr from our
1244 * temporary errors file to the "lf=" logfile.
1245 */
1246 errfp = fopen(tempstderr, "r");
1247 if (errfp) {
1248 while (fgets(buf, sizeof(buf), errfp))
1249 fputs(buf, stderr);
1250 fclose(errfp);
1251 }
1252
1253 return (retcode);
1254 }
1255
1256 /*
1257 * This is the child process, which is the one that executes the
1258 * given filter.
1259 */
1260 /*
1261 * If the first parameter has any slashes in it, then change it
1262 * to point to the first character after the last slash.
1263 */
1264 slash = strrchr(f_av[0], '/');
1265 if (slash != NULL)
1266 f_av[0] = slash + 1;
1267 /*
1268 * XXX - in the future, this should setup an explicit list of
1269 * environment variables and use execve()!
1270 */
1271
1272 /*
1273 * Setup stdin, stdout, and stderr as we want them when the filter
1274 * is running. Stderr is setup so it points to a temporary errors
1275 * file, and the parent process will copy that temporary file to
1276 * the real logfile after the filter completes.
1277 */
1278 dup2(infd, 0);
1279 dup2(outfd, 1);
1280 errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
1281 if (errfd >= 0)
1282 dup2(errfd, 2);
1283 closelog();
1284 closeallfds(3);
1285 execv(f_cmd, f_av);
1286 syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd);
1287 exit(2);
1288 /* NOTREACHED */
1289}
1290
1291/*
1292 * Check to make sure there have been no errors and that both programs
1293 * are in sync with eachother.
1294 * Return non-zero if the connection was lost.
1295 */
1296static char
1297response(const struct printer *pp)
1298{
1299 char resp;
1300
1301 if (read(pfd, &resp, 1) != 1) {
1302 syslog(LOG_INFO, "%s: lost connection", pp->printer);
1303 return (-1);
1304 }
1305 return (resp);
1306}
1307
1308/*
1309 * Banner printing stuff
1310 */
1311static void
1312banner(struct printer *pp, char *name1, char *name2)
1313{
1314 time_t tvec;
1315
1316 time(&tvec);
1317 if (!pp->no_formfeed && !pp->tof)
1318 (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1319 if (pp->short_banner) { /* short banner only */
1320 if (class[0]) {
1321 (void) write(ofd, class, strlen(class));
1322 (void) write(ofd, ":", 1);
1323 }
1324 (void) write(ofd, name1, strlen(name1));
1325 (void) write(ofd, " Job: ", 7);
1326 (void) write(ofd, name2, strlen(name2));
1327 (void) write(ofd, " Date: ", 8);
1328 (void) write(ofd, ctime(&tvec), 24);
1329 (void) write(ofd, "\n", 1);
1330 } else { /* normal banner */
1331 (void) write(ofd, "\n\n\n", 3);
1332 scan_out(pp, ofd, name1, '\0');
1333 (void) write(ofd, "\n\n", 2);
1334 scan_out(pp, ofd, name2, '\0');
1335 if (class[0]) {
1336 (void) write(ofd,"\n\n\n",3);
1337 scan_out(pp, ofd, class, '\0');
1338 }
1339 (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
1340 (void) write(ofd, name2, strlen(name2));
1341 (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
1342 (void) write(ofd, ctime(&tvec), 24);
1343 (void) write(ofd, "\n", 1);
1344 }
1345 if (!pp->no_formfeed)
1346 (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1347 pp->tof = 1;
1348}
1349
1350static char *
1351scnline(int key, char *p, int c)
1352{
be6b9a2e 1353 int scnwidth;
984263bc
MD
1354
1355 for (scnwidth = WIDTH; --scnwidth;) {
1356 key <<= 1;
1357 *p++ = key & 0200 ? c : BACKGND;
1358 }
1359 return (p);
1360}
1361
1362#define TRC(q) (((q)-' ')&0177)
1363
1364static void
1365scan_out(struct printer *pp, int scfd, char *scsp, int dlm)
1366{
be6b9a2e
CP
1367 char *strp;
1368 int nchrs, j;
984263bc
MD
1369 char outbuf[LINELEN+1], *sp, c, cc;
1370 int d, scnhgt;
1371
1372 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1373 strp = &outbuf[0];
1374 sp = scsp;
1375 for (nchrs = 0; ; ) {
1376 d = dropit(c = TRC(cc = *sp++));
1377 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1378 for (j = WIDTH; --j;)
1379 *strp++ = BACKGND;
1380 else
1381 strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc);
1382 if (*sp == dlm || *sp == '\0' ||
1383 nchrs++ >= pp->page_width/(WIDTH+1)-1)
1384 break;
1385 *strp++ = BACKGND;
1386 *strp++ = BACKGND;
1387 }
1388 while (*--strp == BACKGND && strp >= outbuf)
1389 ;
1390 strp++;
1391 *strp++ = '\n';
1392 (void) write(scfd, outbuf, strp-outbuf);
1393 }
1394}
1395
1396static int
1397dropit(int c)
1398{
1399 switch(c) {
1400
1401 case TRC('_'):
1402 case TRC(';'):
1403 case TRC(','):
1404 case TRC('g'):
1405 case TRC('j'):
1406 case TRC('p'):
1407 case TRC('q'):
1408 case TRC('y'):
1409 return (DROP);
1410
1411 default:
1412 return (0);
1413 }
1414}
1415
1416/*
1417 * sendmail ---
1418 * tell people about job completion
1419 */
1420static void
1421sendmail(struct printer *pp, char *userid, int bombed)
1422{
be6b9a2e 1423 int i;
984263bc 1424 int p[2], s;
be6b9a2e 1425 const char *cp;
984263bc
MD
1426 struct stat stb;
1427 FILE *fp;
1428
1429 pipe(p);
1430 if ((s = dofork(pp, DORETURN)) == 0) { /* child */
1431 dup2(p[0], 0);
1432 closelog();
1433 closeallfds(3);
1434 if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1435 cp++;
1436 else
1437 cp = _PATH_SENDMAIL;
1438 execl(_PATH_SENDMAIL, cp, "-t", (char *)0);
1439 _exit(0);
1440 } else if (s > 0) { /* parent */
1441 dup2(p[1], 1);
1442 printf("To: %s@%s\n", userid, origin_host);
1443 printf("Subject: %s printer job \"%s\"\n", pp->printer,
1444 *jobname ? jobname : "<unknown>");
1445 printf("Reply-To: root@%s\n\n", local_host);
1446 printf("Your printer job ");
1447 if (*jobname)
1448 printf("(%s) ", jobname);
1449
1450 switch (bombed) {
1451 case OK:
1452 cp = "OK";
1453 printf("\ncompleted successfully\n");
1454 break;
1455 default:
1456 case FATALERR:
1457 cp = "FATALERR";
1458 printf("\ncould not be printed\n");
1459 break;
1460 case NOACCT:
1461 cp = "NOACCT";
1462 printf("\ncould not be printed without an account on %s\n",
1463 local_host);
1464 break;
1465 case FILTERERR:
1466 cp = "FILTERERR";
1467 if (stat(tempstderr, &stb) < 0 || stb.st_size == 0
1468 || (fp = fopen(tempstderr, "r")) == NULL) {
1469 printf("\nhad some errors and may not have printed\n");
1470 break;
1471 }
1472 printf("\nhad the following errors and may not have printed:\n");
1473 while ((i = getc(fp)) != EOF)
1474 putchar(i);
1475 (void) fclose(fp);
1476 break;
1477 case ACCESS:
1478 cp = "ACCESS";
1479 printf("\nwas not printed because it was not linked to the original file\n");
1480 }
1481 fflush(stdout);
1482 (void) close(1);
1483 } else {
1484 syslog(LOG_WARNING, "unable to send mail to %s: %m", userid);
1485 return;
1486 }
1487 (void) close(p[0]);
1488 (void) close(p[1]);
1489 wait(NULL);
1490 syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1491 userid, *jobname ? jobname : "<unknown>", pp->printer, cp);
1492}
1493
1494/*
1495 * dofork - fork with retries on failure
1496 */
1497static int
1498dofork(const struct printer *pp, int action)
1499{
1500 pid_t forkpid;
1501 int i, fail;
1502 struct passwd *pwd;
1503
1504 forkpid = -1;
1505 if (daemon_uname == NULL) {
1506 pwd = getpwuid(pp->daemon_user);
1507 if (pwd == NULL) {
1508 syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file",
1509 pp->printer, pp->daemon_user);
1510 goto error_ret;
1511 }
1512 daemon_uname = strdup(pwd->pw_name);
1513 daemon_defgid = pwd->pw_gid;
1514 }
1515
1516 for (i = 0; i < 20; i++) {
1517 forkpid = fork();
1518 if (forkpid < 0) {
1519 sleep((unsigned)(i*i));
1520 continue;
1521 }
1522 /*
1523 * Child should run as daemon instead of root
1524 */
1525 if (forkpid == 0) {
1526 errno = 0;
1527 fail = initgroups(daemon_uname, daemon_defgid);
1528 if (fail) {
1529 syslog(LOG_ERR, "%s: initgroups(%s,%u): %m",
1530 pp->printer, daemon_uname, daemon_defgid);
1531 break;
1532 }
1533 fail = setgid(daemon_defgid);
1534 if (fail) {
1535 syslog(LOG_ERR, "%s: setgid(%u): %m",
1536 pp->printer, daemon_defgid);
1537 break;
1538 }
1539 fail = setuid(pp->daemon_user);
1540 if (fail) {
1541 syslog(LOG_ERR, "%s: setuid(%ld): %m",
1542 pp->printer, pp->daemon_user);
1543 break;
1544 }
1545 }
1546 return (forkpid);
1547 }
1548
1549 /*
1550 * An error occurred. If the error is in the child process, then
1551 * this routine MUST always exit(). DORETURN only effects how
1552 * errors should be handled in the parent process.
1553 */
1554error_ret:
1555 if (forkpid == 0) {
1556 syslog(LOG_ERR, "%s: dofork(): aborting child process...",
1557 pp->printer);
1558 exit(1);
1559 }
1560 syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer);
1561
1562 sleep(1); /* throttle errors, as a safety measure */
1563 switch (action) {
1564 case DORETURN:
1565 return (-1);
1566 default:
1567 syslog(LOG_ERR, "bad action (%d) to dofork", action);
1568 /* FALLTHROUGH */
1569 case DOABORT:
1570 exit(1);
1571 }
1572 /*NOTREACHED*/
1573}
1574
1575/*
1576 * Kill child processes to abort current job.
1577 */
1578static void
1579abortpr(int signo __unused)
1580{
1581
1582 (void) unlink(tempstderr);
1583 kill(0, SIGINT);
1584 if (of_pid > 0)
1585 kill(of_pid, SIGCONT);
1586 while (wait(NULL) > 0)
1587 ;
1588 if (of_pid > 0 && tfd != -1)
1589 unlink(tfile);
1590 exit(0);
1591}
1592
1593static void
1594init(struct printer *pp)
1595{
1596 char *s;
1597
1598 sprintf(&width[2], "%ld", pp->page_width);
1599 sprintf(&length[2], "%ld", pp->page_length);
1600 sprintf(&pxwidth[2], "%ld", pp->page_pwidth);
1601 sprintf(&pxlength[2], "%ld", pp->page_plength);
1602 if ((s = checkremote(pp)) != 0) {
1603 syslog(LOG_WARNING, "%s", s);
1604 free(s);
1605 }
1606}
1607
1608void
1609startprinting(const char *printer)
1610{
1611 struct printer myprinter, *pp = &myprinter;
1612 int status;
1613
1614 init_printer(pp);
1615 status = getprintcap(printer, pp);
1616 switch(status) {
1617 case PCAPERR_OSERR:
1618 syslog(LOG_ERR, "can't open printer description file: %m");
1619 exit(1);
1620 case PCAPERR_NOTFOUND:
1621 syslog(LOG_ERR, "unknown printer: %s", printer);
1622 exit(1);
1623 case PCAPERR_TCLOOP:
1624 fatal(pp, "potential reference loop detected in printcap file");
1625 default:
1626 break;
1627 }
1628 printjob(pp);
1629}
1630
1631/*
1632 * Acquire line printer or remote connection.
1633 */
1634static void
1635openpr(const struct printer *pp)
1636{
1637 int p[2];
1638 char *cp;
1639
1640 if (pp->remote) {
1641 openrem(pp);
1642 /*
1643 * Lpd does support the setting of 'of=' filters for
1644 * jobs going to remote machines, but that does not
1645 * have the same meaning as 'of=' does when handling
1646 * local print queues. For remote machines, all 'of='
1647 * filter processing is handled in sendfile(), and that
1648 * does not use these global "output filter" variables.
1649 */
1650 ofd = -1;
1651 of_pid = 0;
1652 return;
1653 } else if (*pp->lp) {
1654 if ((cp = strchr(pp->lp, '@')) != NULL)
1655 opennet(pp);
1656 else
1657 opentty(pp);
1658 } else {
1659 syslog(LOG_ERR, "%s: no line printer device or host name",
1660 pp->printer);
1661 exit(1);
1662 }
1663
1664 /*
1665 * Start up an output filter, if needed.
1666 */
1667 if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) {
1668 pipe(p);
1669 if (pp->remote) {
1670 strcpy(tfile, TFILENAME);
1671 tfd = mkstemp(tfile);
1672 }
1673 if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */
1674 dup2(p[0], 0); /* pipe is std in */
1675 /* tfile/printer is stdout */
1676 dup2(pp->remote ? tfd : pfd, 1);
1677 closelog();
1678 closeallfds(3);
1679 if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL)
1680 cp = pp->filters[LPF_OUTPUT];
1681 else
1682 cp++;
1683 execl(pp->filters[LPF_OUTPUT], cp, width, length,
1684 (char *)0);
1685 syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer,
1686 pp->filters[LPF_OUTPUT]);
1687 exit(1);
1688 }
1689 (void) close(p[0]); /* close input side */
1690 ofd = p[1]; /* use pipe for output */
1691 } else {
1692 ofd = pfd;
1693 of_pid = 0;
1694 }
1695}
1696
1697/*
1698 * Printer connected directly to the network
1699 * or to a terminal server on the net
1700 */
1701static void
1702opennet(const struct printer *pp)
1703{
be6b9a2e 1704 int i;
984263bc
MD
1705 int resp;
1706 u_long port;
1707 char *ep;
1708 void (*savealrm)(int);
1709
1710 port = strtoul(pp->lp, &ep, 0);
1711 if (*ep != '@' || port > 65535) {
1712 syslog(LOG_ERR, "%s: bad port number: %s", pp->printer,
1713 pp->lp);
1714 exit(1);
1715 }
1716 ep++;
1717
1718 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1719 resp = -1;
1720 savealrm = signal(SIGALRM, alarmhandler);
1721 alarm(pp->conn_timeout);
1722 pfd = getport(pp, ep, port);
1723 alarm(0);
1724 (void)signal(SIGALRM, savealrm);
1725 if (pfd < 0 && errno == ECONNREFUSED)
1726 resp = 1;
1727 else if (pfd >= 0) {
1728 /*
1729 * need to delay a bit for rs232 lines
1730 * to stabilize in case printer is
1731 * connected via a terminal server
1732 */
1733 delay(500);
1734 break;
1735 }
1736 if (i == 1) {
1737 if (resp < 0)
1738 pstatus(pp, "waiting for %s to come up",
1739 pp->lp);
1740 else
1741 pstatus(pp,
1742 "waiting for access to printer on %s",
1743 pp->lp);
1744 }
1745 sleep(i);
1746 }
1747 pstatus(pp, "sending to %s port %lu", ep, port);
1748}
1749
1750/*
1751 * Printer is connected to an RS232 port on this host
1752 */
1753static void
1754opentty(const struct printer *pp)
1755{
be6b9a2e 1756 int i;
984263bc
MD
1757
1758 for (i = 1; ; i = i < 32 ? i << 1 : i) {
1759 pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY);
1760 if (pfd >= 0) {
1761 delay(500);
1762 break;
1763 }
1764 if (errno == ENOENT) {
1765 syslog(LOG_ERR, "%s: %m", pp->lp);
1766 exit(1);
1767 }
1768 if (i == 1)
1769 pstatus(pp,
1770 "waiting for %s to become ready (offline?)",
1771 pp->printer);
1772 sleep(i);
1773 }
1774 if (isatty(pfd))
1775 setty(pp);
1776 pstatus(pp, "%s is ready and printing", pp->printer);
1777}
1778
1779/*
1780 * Printer is on a remote host
1781 */
1782static void
1783openrem(const struct printer *pp)
1784{
be6b9a2e 1785 int i;
984263bc
MD
1786 int resp;
1787 void (*savealrm)(int);
1788
1789 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1790 resp = -1;
1791 savealrm = signal(SIGALRM, alarmhandler);
1792 alarm(pp->conn_timeout);
1793 pfd = getport(pp, pp->remote_host, 0);
1794 alarm(0);
1795 (void)signal(SIGALRM, savealrm);
1796 if (pfd >= 0) {
1797 if ((writel(pfd, "\2", pp->remote_queue, "\n",
1798 (char *)0)
1799 == 2 + strlen(pp->remote_queue))
1800 && (resp = response(pp)) == 0)
1801 break;
1802 (void) close(pfd);
1803 }
1804 if (i == 1) {
1805 if (resp < 0)
1806 pstatus(pp, "waiting for %s to come up",
1807 pp->remote_host);
1808 else {
1809 pstatus(pp,
1810 "waiting for queue to be enabled on %s",
1811 pp->remote_host);
1812 i = 256;
1813 }
1814 }
1815 sleep(i);
1816 }
1817 pstatus(pp, "sending to %s", pp->remote_host);
1818}
1819
1820/*
1821 * setup tty lines.
1822 */
1823static void
1824setty(const struct printer *pp)
1825{
1826 struct termios ttybuf;
1827
1828 if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1829 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer);
1830 exit(1);
1831 }
1832 if (tcgetattr(pfd, &ttybuf) < 0) {
1833 syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer);
1834 exit(1);
1835 }
1836 if (pp->baud_rate > 0)
1837 cfsetspeed(&ttybuf, pp->baud_rate);
1838 if (pp->mode_set) {
1839 char *s = strdup(pp->mode_set), *tmp;
1840
1841 while ((tmp = strsep(&s, ",")) != NULL) {
1842 (void) msearch(tmp, &ttybuf);
1843 }
1844 }
1845 if (pp->mode_set != 0 || pp->baud_rate > 0) {
1846 if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
1847 syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer);
1848 }
1849 }
1850}
1851
1852#include <stdarg.h>
1853
1854static void
1855pstatus(const struct printer *pp, const char *msg, ...)
1856{
1857 int fd;
1858 char *buf;
1859 va_list ap;
1860 va_start(ap, msg);
1861
1862 umask(0);
1863 fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
1864 if (fd < 0) {
1865 syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
1866 pp->status_file);
1867 exit(1);
1868 }
1869 ftruncate(fd, 0);
1870 vasprintf(&buf, msg, ap);
1871 va_end(ap);
1872 writel(fd, buf, "\n", (char *)0);
1873 close(fd);
1874 free(buf);
1875}
1876
1877void
1878alarmhandler(int signo __unused)
1879{
1880 /* the signal is ignored */
1881 /* (the '__unused' is just to avoid a compile-time warning) */
1882}