printjob.c revision 118881
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 3. All advertising materials mentioning features or use of this software 151553Srgrimes * must display the following acknowledgement: 161553Srgrimes * This product includes software developed by the University of 171553Srgrimes * California, Berkeley and its contributors. 181553Srgrimes * 4. Neither the name of the University nor the names of its contributors 191553Srgrimes * may be used to endorse or promote products derived from this software 201553Srgrimes * without specific prior written permission. 211553Srgrimes * 221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321553Srgrimes * SUCH DAMAGE. 331553Srgrimes */ 341553Srgrimes 351553Srgrimes#ifndef lint 3631492Swollmanstatic const char copyright[] = 371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 381553Srgrimes The Regents of the University of California. All rights reserved.\n"; 391553Srgrimes#endif /* not lint */ 401553Srgrimes 41117554Sgad#if 0 42117587Sgad#ifndef lint 4315648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 44117587Sgad#endif /* not lint */ 45117554Sgad#endif 461553Srgrimes 47117554Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 48117554Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 118881 2003-08-13 20:31:33Z gad $"); 491553Srgrimes 501553Srgrimes/* 511553Srgrimes * printjob -- print jobs in the queue. 521553Srgrimes * 531553Srgrimes * NOTE: the lock file is used to pass information to lpq and lprm. 541553Srgrimes * it does not need to be removed because file locks are dynamic. 551553Srgrimes */ 561553Srgrimes 571553Srgrimes#include <sys/param.h> 581553Srgrimes#include <sys/wait.h> 591553Srgrimes#include <sys/stat.h> 601553Srgrimes#include <sys/types.h> 611553Srgrimes 621553Srgrimes#include <pwd.h> 631553Srgrimes#include <unistd.h> 641553Srgrimes#include <signal.h> 651553Srgrimes#include <syslog.h> 661553Srgrimes#include <fcntl.h> 671553Srgrimes#include <dirent.h> 681553Srgrimes#include <errno.h> 691553Srgrimes#include <stdio.h> 701553Srgrimes#include <string.h> 711553Srgrimes#include <stdlib.h> 7215032Ssef#include <sys/ioctl.h> 7315032Ssef#include <termios.h> 7415703Sjoerg#include <time.h> 751553Srgrimes#include "lp.h" 761553Srgrimes#include "lp.local.h" 771553Srgrimes#include "pathnames.h" 781553Srgrimes#include "extern.h" 791553Srgrimes 8080230Sgad#define DORETURN 0 /* dofork should return "can't fork" error */ 8180230Sgad#define DOABORT 1 /* dofork should just die if fork() fails */ 821553Srgrimes 831553Srgrimes/* 841553Srgrimes * Error tokens 851553Srgrimes */ 861553Srgrimes#define REPRINT -2 871553Srgrimes#define ERROR -1 881553Srgrimes#define OK 0 891553Srgrimes#define FATALERR 1 901553Srgrimes#define NOACCT 2 911553Srgrimes#define FILTERERR 3 921553Srgrimes#define ACCESS 4 931553Srgrimes 941553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 951553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 961553Srgrimesstatic FILE *cfp; /* control file */ 9797793Sgadstatic pid_t of_pid; /* process id of output filter, if any */ 981553Srgrimesstatic int child; /* id of any filters */ 9968253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 1001553Srgrimesstatic int lfd; /* lock file descriptor */ 1011553Srgrimesstatic int ofd; /* output filter file descriptor */ 10224831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1031553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1041553Srgrimesstatic int prchild; /* id of pr process */ 1051553Srgrimesstatic char title[80]; /* ``pr'' title */ 10653956Sachestatic char locale[80]; /* ``pr'' locale */ 1071553Srgrimes 10880230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 10980230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 11080230Sgadstatic int daemon_defgid; 11180230Sgad 1121553Srgrimesstatic char class[32]; /* classification field */ 11378300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1141553Srgrimes /* indentation size in static characters */ 1158857Srgrimesstatic char indent[10] = "-i0"; 1161553Srgrimesstatic char jobname[100]; /* job or file name */ 1171553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1181553Srgrimesstatic char logname[32]; /* user's login name */ 1191553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1201553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 12168664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12268664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1231553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12424831Sbrian#define TFILENAME "fltXXXXXX" 12524831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1261553Srgrimes 12778146Sgadstatic void abortpr(int _signo); 12878146Sgadstatic void alarmhandler(int _signo); 12978146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 13078146Sgadstatic int dofork(const struct printer *_pp, int _action); 13178146Sgadstatic int dropit(int _c); 13294032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13394032Sgad int _infd, int _outfd); 13478146Sgadstatic void init(struct printer *_pp); 13578146Sgadstatic void openpr(const struct printer *_pp); 13678146Sgadstatic void opennet(const struct printer *_pp); 13778146Sgadstatic void opentty(const struct printer *_pp); 13878146Sgadstatic void openrem(const struct printer *pp); 13978146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 14078146Sgadstatic int printit(struct printer *_pp, char *_file); 14179739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14279739Sgad __printflike(2, 3); 14378146Sgadstatic char response(const struct printer *_pp); 14478146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14578146Sgad int _dlm); 14678146Sgadstatic char *scnline(int _key, char *_p, int _c); 14778146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 14895293Sgad char _format, int _copyreq); 14978146Sgadstatic int sendit(struct printer *_pp, char *_file); 15094038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 15178146Sgadstatic void setty(const struct printer *_pp); 1521553Srgrimes 1531553Srgrimesvoid 15478146Sgadprintjob(struct printer *pp) 1551553Srgrimes{ 1561553Srgrimes struct stat stb; 15768401Sgad register struct jobqueue *q, **qp; 15868401Sgad struct jobqueue **queue; 1591553Srgrimes register int i, nitems; 16068733Sgad off_t pidoff; 16197793Sgad pid_t printpid; 16268733Sgad int errcnt, jobcount, tempfd; 1631553Srgrimes 16468733Sgad jobcount = 0; 16531492Swollman init(pp); /* set up capabilities */ 166118881Sgad (void) write(STDOUT_FILENO, "", 1); /* ack that daemon is started */ 167118881Sgad (void) close(STDERR_FILENO); /* set up log file */ 16831492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 16997792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 17097792Sgad pp->log_file); 1711553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1721553Srgrimes } 1731553Srgrimes setgid(getegid()); 17497793Sgad printpid = getpid(); /* for use with lprm */ 17597793Sgad setpgrp(0, printpid); 17679735Sgad 17779735Sgad /* 17879735Sgad * At initial lpd startup, printjob may be called with various 17979735Sgad * signal handlers in effect. After that initial startup, any 18079735Sgad * calls to printjob will have a *different* set of signal-handlers 18179735Sgad * in effect. Make sure all handlers are the ones we want. 18279735Sgad */ 18379735Sgad signal(SIGCHLD, SIG_DFL); 1841553Srgrimes signal(SIGHUP, abortpr); 1851553Srgrimes signal(SIGINT, abortpr); 1861553Srgrimes signal(SIGQUIT, abortpr); 1871553Srgrimes signal(SIGTERM, abortpr); 1881553Srgrimes 1891553Srgrimes /* 1901553Srgrimes * uses short form file names 1911553Srgrimes */ 19231492Swollman if (chdir(pp->spool_dir) < 0) { 19397792Sgad syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, 19497792Sgad pp->spool_dir); 1951553Srgrimes exit(1); 1961553Srgrimes } 19731492Swollman if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS)) 1981553Srgrimes exit(0); /* printing disabled */ 19931492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 20031492Swollman LOCK_FILE_MODE); 2011553Srgrimes if (lfd < 0) { 20231492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 20331492Swollman exit(0); 20497792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 20597792Sgad pp->lock_file); 2061553Srgrimes exit(1); 2071553Srgrimes } 20831492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 20931492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 21097792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 21197792Sgad pp->lock_file); 2121553Srgrimes exit(1); 2131553Srgrimes } 2141553Srgrimes ftruncate(lfd, 0); 2151553Srgrimes /* 2161553Srgrimes * write process id for others to know 2171553Srgrimes */ 21897793Sgad sprintf(line, "%u\n", printpid); 2191553Srgrimes pidoff = i = strlen(line); 2201553Srgrimes if (write(lfd, line, i) != i) { 22197792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 22297792Sgad pp->lock_file); 2231553Srgrimes exit(1); 2241553Srgrimes } 2251553Srgrimes /* 2261553Srgrimes * search the spool directory for work and sort by queue order. 2271553Srgrimes */ 22831492Swollman if ((nitems = getq(pp, &queue)) < 0) { 22931492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 23097792Sgad pp->spool_dir); 2311553Srgrimes exit(1); 2321553Srgrimes } 2331553Srgrimes if (nitems == 0) /* no work to do */ 2341553Srgrimes exit(0); 23531492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 23631492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 23797792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 23897792Sgad pp->lock_file); 2391553Srgrimes } 24068664Sgad 24168664Sgad /* create a file which will be used to hold stderr from filters */ 24268664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 24368664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 24497792Sgad tempstderr); 24568732Sgad exit(1); 24668664Sgad } 24768664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 24868664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 24997792Sgad tempstderr); 25068732Sgad exit(1); 25168664Sgad } 25268664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 25368664Sgad close(tempfd); 25468664Sgad 25531492Swollman openpr(pp); /* open printer or remote */ 2561553Srgrimesagain: 2571553Srgrimes /* 2581553Srgrimes * we found something to do now do it -- 2591553Srgrimes * write the name of the current control file into the lock file 2601553Srgrimes * so the spool queue program can tell what we're working on 2611553Srgrimes */ 2621553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2631553Srgrimes q = *qp++; 26468401Sgad if (stat(q->job_cfname, &stb) < 0) 2651553Srgrimes continue; 26615648Sjoerg errcnt = 0; 2671553Srgrimes restart: 26815648Sjoerg (void) lseek(lfd, pidoff, 0); 26968401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2701553Srgrimes i = strlen(line); 2711553Srgrimes if (write(lfd, line, i) != i) 27297792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 27397792Sgad pp->lock_file); 27431492Swollman if (!pp->remote) 27568401Sgad i = printit(pp, q->job_cfname); 2761553Srgrimes else 27768401Sgad i = sendit(pp, q->job_cfname); 2781553Srgrimes /* 2791553Srgrimes * Check to see if we are supposed to stop printing or 2801553Srgrimes * if we are to rebuild the queue. 2811553Srgrimes */ 2821553Srgrimes if (fstat(lfd, &stb) == 0) { 2831553Srgrimes /* stop printing before starting next job? */ 28431492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2851553Srgrimes goto done; 2861553Srgrimes /* rebuild queue (after lpc topq) */ 28731492Swollman if (stb.st_mode & LFM_RESET_QUE) { 28831492Swollman for (free(q); nitems--; free(q)) 2891553Srgrimes q = *qp++; 29031492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 29131492Swollman < 0) 29297792Sgad syslog(LOG_WARNING, 29397792Sgad "%s: fchmod(%s): %m", 29497792Sgad pp->printer, pp->lock_file); 2951553Srgrimes break; 2961553Srgrimes } 2971553Srgrimes } 29868733Sgad if (i == OK) /* all files of this job printed */ 29968733Sgad jobcount++; 30015648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 30115648Sjoerg /* try reprinting the job */ 30231492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 30397793Sgad if (of_pid > 0) { 30497793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3051553Srgrimes (void) close(ofd); 30697793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3071553Srgrimes ; 30879735Sgad if (i < 0) 30979735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 31097793Sgad pp->printer, of_pid); 31197793Sgad of_pid = 0; 3121553Srgrimes } 3131553Srgrimes (void) close(pfd); /* close printer */ 3141553Srgrimes if (ftruncate(lfd, pidoff) < 0) 31597792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 31697792Sgad pp->printer, pp->lock_file); 31731492Swollman openpr(pp); /* try to reopen printer */ 3181553Srgrimes goto restart; 31915648Sjoerg } else { 32031492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 32197792Sgad pp->printer, 32297792Sgad pp->remote ? "sent to remote host" : "printed", 32397792Sgad q->job_cfname); 32415648Sjoerg if (i == REPRINT) { 32527748Simp /* ensure we don't attempt this job again */ 32668401Sgad (void) unlink(q->job_cfname); 32768401Sgad q->job_cfname[0] = 'd'; 32868401Sgad (void) unlink(q->job_cfname); 32915648Sjoerg if (logname[0]) 33031492Swollman sendmail(pp, logname, FATALERR); 33115648Sjoerg } 3321553Srgrimes } 3331553Srgrimes } 33431492Swollman free(queue); 3351553Srgrimes /* 3361553Srgrimes * search the spool directory for more work. 3371553Srgrimes */ 33831492Swollman if ((nitems = getq(pp, &queue)) < 0) { 33931492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 34097792Sgad pp->spool_dir); 3411553Srgrimes exit(1); 3421553Srgrimes } 3431553Srgrimes if (nitems == 0) { /* no more work to do */ 3441553Srgrimes done: 34568733Sgad if (jobcount > 0) { /* jobs actually printed */ 34631492Swollman if (!pp->no_formfeed && !pp->tof) 34731492Swollman (void) write(ofd, pp->form_feed, 34831492Swollman strlen(pp->form_feed)); 34931492Swollman if (pp->trailer != NULL) /* output trailer */ 35031492Swollman (void) write(ofd, pp->trailer, 35131492Swollman strlen(pp->trailer)); 3521553Srgrimes } 35319202Simp (void) close(ofd); 35419202Simp (void) wait(NULL); 35568664Sgad (void) unlink(tempstderr); 3561553Srgrimes exit(0); 3571553Srgrimes } 3581553Srgrimes goto again; 3591553Srgrimes} 3601553Srgrimes 3611553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3621553Srgrimes 3631553Srgrimeschar ifonts[4][40] = { 3641553Srgrimes _PATH_VFONTR, 3651553Srgrimes _PATH_VFONTI, 3661553Srgrimes _PATH_VFONTB, 3671553Srgrimes _PATH_VFONTS, 3681553Srgrimes}; 3691553Srgrimes 3701553Srgrimes/* 3711553Srgrimes * The remaining part is the reading of the control file (cf) 3721553Srgrimes * and performing the various actions. 3731553Srgrimes */ 3741553Srgrimesstatic int 37578146Sgadprintit(struct printer *pp, char *file) 3761553Srgrimes{ 3771553Srgrimes register int i; 37868734Sgad char *cp; 37968734Sgad int bombed, didignorehdr; 3801553Srgrimes 38168734Sgad bombed = OK; 38268734Sgad didignorehdr = 0; 3831553Srgrimes /* 3841553Srgrimes * open control file; ignore if no longer there. 3851553Srgrimes */ 3861553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 38797792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 38897791Sgad return (OK); 3891553Srgrimes } 3901553Srgrimes /* 3911553Srgrimes * Reset troff fonts. 3921553Srgrimes */ 3931553Srgrimes for (i = 0; i < 4; i++) 3941553Srgrimes strcpy(fonts[i], ifonts[i]); 39531492Swollman sprintf(&width[2], "%ld", pp->page_width); 3961553Srgrimes strcpy(indent+2, "0"); 3971553Srgrimes 39868253Sgad /* initialize job-specific count of datafiles processed */ 39968253Sgad job_dfcnt = 0; 40068253Sgad 4011553Srgrimes /* 4021553Srgrimes * read the control file for work to do 4031553Srgrimes * 4041553Srgrimes * file format -- first character in the line is a command 4051553Srgrimes * rest of the line is the argument. 4061553Srgrimes * valid commands are: 4071553Srgrimes * 4081553Srgrimes * S -- "stat info" for symbolic link protection 4091553Srgrimes * J -- "job name" on banner page 4101553Srgrimes * C -- "class name" on banner page 4111553Srgrimes * L -- "literal" user's name to print on banner 4121553Srgrimes * T -- "title" for pr 4131553Srgrimes * H -- "host name" of machine where lpr was done 4141553Srgrimes * P -- "person" user's login name 4151553Srgrimes * I -- "indent" amount to indent output 41615648Sjoerg * R -- laser dpi "resolution" 4171553Srgrimes * f -- "file name" name of text file to print 4181553Srgrimes * l -- "file name" text file with control chars 41983684Sgad * o -- "file name" postscript file, according to 42083684Sgad * the RFC. Here it is treated like an 'f'. 4211553Srgrimes * p -- "file name" text file to print with pr(1) 4221553Srgrimes * t -- "file name" troff(1) file to print 4231553Srgrimes * n -- "file name" ditroff(1) file to print 4241553Srgrimes * d -- "file name" dvi file to print 4251553Srgrimes * g -- "file name" plot(1G) file to print 4261553Srgrimes * v -- "file name" plain raster file to print 4271553Srgrimes * c -- "file name" cifplot file to print 4281553Srgrimes * 1 -- "R font file" for troff 4291553Srgrimes * 2 -- "I font file" for troff 4301553Srgrimes * 3 -- "B font file" for troff 4311553Srgrimes * 4 -- "S font file" for troff 4321553Srgrimes * N -- "name" of file (used by lpq) 4331553Srgrimes * U -- "unlink" name of file to remove 4341553Srgrimes * (after we print it. (Pass 2 only)). 4351553Srgrimes * M -- "mail" to user when done printing 43653956Sache * Z -- "locale" for pr 4371553Srgrimes * 4381553Srgrimes * getline reads a line and expands tabs to blanks 4391553Srgrimes */ 4401553Srgrimes 4411553Srgrimes /* pass 1 */ 4421553Srgrimes 4431553Srgrimes while (getline(cfp)) 4441553Srgrimes switch (line[0]) { 4451553Srgrimes case 'H': 44678300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 44727748Simp if (class[0] == '\0') { 44880133Sgad strlcpy(class, line+1, sizeof(class)); 44927748Simp } 4501553Srgrimes continue; 4511553Srgrimes 4521553Srgrimes case 'P': 45380133Sgad strlcpy(logname, line + 1, sizeof(logname)); 45431492Swollman if (pp->restricted) { /* restricted */ 4551553Srgrimes if (getpwnam(logname) == NULL) { 4561553Srgrimes bombed = NOACCT; 45731492Swollman sendmail(pp, line+1, bombed); 4581553Srgrimes goto pass2; 4591553Srgrimes } 4601553Srgrimes } 4611553Srgrimes continue; 4621553Srgrimes 4631553Srgrimes case 'S': 4641553Srgrimes cp = line+1; 4651553Srgrimes i = 0; 4661553Srgrimes while (*cp >= '0' && *cp <= '9') 4671553Srgrimes i = i * 10 + (*cp++ - '0'); 4681553Srgrimes fdev = i; 4691553Srgrimes cp++; 4701553Srgrimes i = 0; 4711553Srgrimes while (*cp >= '0' && *cp <= '9') 4721553Srgrimes i = i * 10 + (*cp++ - '0'); 4731553Srgrimes fino = i; 4741553Srgrimes continue; 4751553Srgrimes 4761553Srgrimes case 'J': 47727748Simp if (line[1] != '\0') { 47880133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 47927748Simp } else 4801553Srgrimes strcpy(jobname, " "); 4811553Srgrimes continue; 4821553Srgrimes 4831553Srgrimes case 'C': 4841553Srgrimes if (line[1] != '\0') 48580133Sgad strlcpy(class, line + 1, sizeof(class)); 48680133Sgad else if (class[0] == '\0') { 48780133Sgad /* XXX - why call gethostname instead of 48880133Sgad * just strlcpy'ing local_host? */ 4891553Srgrimes gethostname(class, sizeof(class)); 49080133Sgad class[sizeof(class) - 1] = '\0'; 49180133Sgad } 4921553Srgrimes continue; 4931553Srgrimes 4941553Srgrimes case 'T': /* header title for pr */ 49580133Sgad strlcpy(title, line + 1, sizeof(title)); 4961553Srgrimes continue; 4971553Srgrimes 4981553Srgrimes case 'L': /* identification line */ 49931492Swollman if (!pp->no_header && !pp->header_last) 50031492Swollman banner(pp, line+1, jobname); 5011553Srgrimes continue; 5021553Srgrimes 5031553Srgrimes case '1': /* troff fonts */ 5041553Srgrimes case '2': 5051553Srgrimes case '3': 5061553Srgrimes case '4': 50727748Simp if (line[1] != '\0') { 50880133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 50980133Sgad (size_t)50); 51027748Simp } 5111553Srgrimes continue; 5121553Srgrimes 5131553Srgrimes case 'W': /* page width */ 51480133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5151553Srgrimes continue; 5161553Srgrimes 5171553Srgrimes case 'I': /* indent amount */ 51880133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5191553Srgrimes continue; 5201553Srgrimes 52153956Sache case 'Z': /* locale for pr */ 52280133Sgad strlcpy(locale, line + 1, sizeof(locale)); 52353956Sache continue; 52453956Sache 5251553Srgrimes default: /* some file to print */ 52668467Sgad /* only lowercase cmd-codes include a file-to-print */ 52768467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 52868467Sgad /* ignore any other lines */ 52968467Sgad if (lflag <= 1) 53068467Sgad continue; 53168467Sgad if (!didignorehdr) { 53268467Sgad syslog(LOG_INFO, "%s: in %s :", 53397792Sgad pp->printer, file); 53468467Sgad didignorehdr = 1; 53568467Sgad } 53668467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 53797792Sgad pp->printer, line[0], &line[1]); 53868467Sgad continue; 53968467Sgad } 54068467Sgad i = print(pp, line[0], line+1); 54168467Sgad switch (i) { 5421553Srgrimes case ERROR: 5431553Srgrimes if (bombed == OK) 5441553Srgrimes bombed = FATALERR; 5451553Srgrimes break; 5461553Srgrimes case REPRINT: 5471553Srgrimes (void) fclose(cfp); 54897791Sgad return (REPRINT); 5491553Srgrimes case FILTERERR: 5501553Srgrimes case ACCESS: 5511553Srgrimes bombed = i; 55231492Swollman sendmail(pp, logname, bombed); 5531553Srgrimes } 5541553Srgrimes title[0] = '\0'; 5551553Srgrimes continue; 5561553Srgrimes 5571553Srgrimes case 'N': 5581553Srgrimes case 'U': 5591553Srgrimes case 'M': 56015648Sjoerg case 'R': 5611553Srgrimes continue; 5621553Srgrimes } 5631553Srgrimes 5641553Srgrimes /* pass 2 */ 5651553Srgrimes 5661553Srgrimespass2: 5671553Srgrimes fseek(cfp, 0L, 0); 5681553Srgrimes while (getline(cfp)) 5691553Srgrimes switch (line[0]) { 5701553Srgrimes case 'L': /* identification line */ 57131492Swollman if (!pp->no_header && pp->header_last) 57231492Swollman banner(pp, line+1, jobname); 5731553Srgrimes continue; 5741553Srgrimes 5751553Srgrimes case 'M': 5761553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 57731492Swollman sendmail(pp, line+1, bombed); 5781553Srgrimes continue; 5791553Srgrimes 5801553Srgrimes case 'U': 58127748Simp if (strchr(line+1, '/')) 58227748Simp continue; 5831553Srgrimes (void) unlink(line+1); 5841553Srgrimes } 5851553Srgrimes /* 5861553Srgrimes * clean-up in case another control file exists 5871553Srgrimes */ 5881553Srgrimes (void) fclose(cfp); 5891553Srgrimes (void) unlink(file); 59097791Sgad return (bombed == OK ? OK : ERROR); 5911553Srgrimes} 5921553Srgrimes 5931553Srgrimes/* 5941553Srgrimes * Print a file. 5951553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 5961553Srgrimes * Return -1 if a non-recoverable error occured, 5971553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 5981553Srgrimes * 1 if we should try to reprint this job and 5991553Srgrimes * 0 if all is well. 6001553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 6011553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6021553Srgrimes */ 6031553Srgrimesstatic int 60478146Sgadprint(struct printer *pp, int format, char *file) 6051553Srgrimes{ 60653956Sache register int n, i; 6071553Srgrimes register char *prog; 60831492Swollman int fi, fo; 6091553Srgrimes FILE *fp; 6101553Srgrimes char *av[15], buf[BUFSIZ]; 61197793Sgad pid_t wpid; 61297793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6131553Srgrimes struct stat stb; 6141553Srgrimes 61568467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 61668467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 61797792Sgad pp->printer, file, format); 61897791Sgad return (ERROR); 61968467Sgad } 6201553Srgrimes /* 6211553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6221553Srgrimes * still point to the same file or someone is trying to print 6231553Srgrimes * something he shouldn't. 6241553Srgrimes */ 6251553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6261553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 62797791Sgad return (ACCESS); 62868253Sgad 62968253Sgad job_dfcnt++; /* increment datafile counter for this job */ 63068734Sgad stopped = 0; /* output filter is not stopped */ 63168253Sgad 63268253Sgad /* everything seems OK, start it up */ 63331492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 63431492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 63531492Swollman pp->tof = 1; 6361553Srgrimes } 63731492Swollman if (pp->filters[LPF_INPUT] == NULL 63883684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 63931492Swollman pp->tof = 0; 6401553Srgrimes while ((n = read(fi, buf, BUFSIZ)) > 0) 6411553Srgrimes if (write(ofd, buf, n) != n) { 6421553Srgrimes (void) close(fi); 64397791Sgad return (REPRINT); 6441553Srgrimes } 6451553Srgrimes (void) close(fi); 64697791Sgad return (OK); 6471553Srgrimes } 6481553Srgrimes switch (format) { 6491553Srgrimes case 'p': /* print file using 'pr' */ 65031492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6511553Srgrimes prog = _PATH_PR; 65253956Sache i = 0; 65353956Sache av[i++] = "pr"; 65453956Sache av[i++] = width; 65553956Sache av[i++] = length; 65653956Sache av[i++] = "-h"; 65753956Sache av[i++] = *title ? title : " "; 65853956Sache av[i++] = "-L"; 65953956Sache av[i++] = *locale ? locale : "C"; 66053956Sache av[i++] = "-F"; 66153956Sache av[i] = 0; 6621553Srgrimes fo = ofd; 6631553Srgrimes goto start; 6641553Srgrimes } 6651553Srgrimes pipe(p); 66631492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 667118881Sgad dup2(fi, STDIN_FILENO); /* file is stdin */ 668118881Sgad dup2(p[1], STDOUT_FILENO); /* pipe is stdout */ 6698094Sjkh closelog(); 67031492Swollman closeallfds(3); 6711553Srgrimes execl(_PATH_PR, "pr", width, length, 67253956Sache "-h", *title ? title : " ", 67353956Sache "-L", *locale ? locale : "C", 67479452Sbrian "-F", (char *)0); 6751553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6761553Srgrimes exit(2); 6771553Srgrimes } 6781553Srgrimes (void) close(p[1]); /* close output side */ 6791553Srgrimes (void) close(fi); 6801553Srgrimes if (prchild < 0) { 6811553Srgrimes prchild = 0; 6821553Srgrimes (void) close(p[0]); 68397791Sgad return (ERROR); 6841553Srgrimes } 6851553Srgrimes fi = p[0]; /* use pipe for input */ 6861553Srgrimes case 'f': /* print plain text file */ 68731492Swollman prog = pp->filters[LPF_INPUT]; 6881553Srgrimes av[1] = width; 6891553Srgrimes av[2] = length; 6901553Srgrimes av[3] = indent; 6911553Srgrimes n = 4; 6921553Srgrimes break; 69386935Sgad case 'o': /* print postscript file */ 69486935Sgad /* 69586935Sgad * Treat this as a "plain file with control characters", and 69686935Sgad * assume the standard LPF_INPUT filter will recognize that 69786935Sgad * the data is postscript and know what to do with it. These 69886935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 69986935Sgad * (later versions of MacOS 10 will explicitly use 'l') 70086935Sgad * A postscript file can contain binary data, which is why 'l' 70186935Sgad * is somewhat more appropriate than 'f'. 70286935Sgad */ 70386935Sgad /* FALLTHROUGH */ 7041553Srgrimes case 'l': /* like 'f' but pass control characters */ 70531492Swollman prog = pp->filters[LPF_INPUT]; 7061553Srgrimes av[1] = "-c"; 7071553Srgrimes av[2] = width; 7081553Srgrimes av[3] = length; 7091553Srgrimes av[4] = indent; 7101553Srgrimes n = 5; 7111553Srgrimes break; 7121553Srgrimes case 'r': /* print a fortran text file */ 71331492Swollman prog = pp->filters[LPF_FORTRAN]; 7141553Srgrimes av[1] = width; 7151553Srgrimes av[2] = length; 7161553Srgrimes n = 3; 7171553Srgrimes break; 7181553Srgrimes case 't': /* print troff output */ 7191553Srgrimes case 'n': /* print ditroff output */ 7201553Srgrimes case 'd': /* print tex output */ 7211553Srgrimes (void) unlink(".railmag"); 7221553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 72331492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 72497792Sgad pp->printer); 7251553Srgrimes (void) unlink(".railmag"); 7261553Srgrimes } else { 7271553Srgrimes for (n = 0; n < 4; n++) { 7281553Srgrimes if (fonts[n][0] != '/') 7291553Srgrimes (void) write(fo, _PATH_VFONT, 7301553Srgrimes sizeof(_PATH_VFONT) - 1); 7311553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7321553Srgrimes (void) write(fo, "\n", 1); 7331553Srgrimes } 7341553Srgrimes (void) close(fo); 7351553Srgrimes } 73631492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 73731492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 73831492Swollman : pp->filters[LPF_DVI]); 7391553Srgrimes av[1] = pxwidth; 7401553Srgrimes av[2] = pxlength; 7411553Srgrimes n = 3; 7421553Srgrimes break; 7431553Srgrimes case 'c': /* print cifplot output */ 74431492Swollman prog = pp->filters[LPF_CIFPLOT]; 7451553Srgrimes av[1] = pxwidth; 7461553Srgrimes av[2] = pxlength; 7471553Srgrimes n = 3; 7481553Srgrimes break; 7491553Srgrimes case 'g': /* print plot(1G) output */ 75031492Swollman prog = pp->filters[LPF_GRAPH]; 7511553Srgrimes av[1] = pxwidth; 7521553Srgrimes av[2] = pxlength; 7531553Srgrimes n = 3; 7541553Srgrimes break; 7551553Srgrimes case 'v': /* print raster output */ 75631492Swollman prog = pp->filters[LPF_RASTER]; 7571553Srgrimes av[1] = pxwidth; 7581553Srgrimes av[2] = pxlength; 7591553Srgrimes n = 3; 7601553Srgrimes break; 7611553Srgrimes default: 7621553Srgrimes (void) close(fi); 7631553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 76497792Sgad pp->printer, format); 76597791Sgad return (ERROR); 7661553Srgrimes } 76715648Sjoerg if (prog == NULL) { 76815648Sjoerg (void) close(fi); 76915648Sjoerg syslog(LOG_ERR, 77015648Sjoerg "%s: no filter found in printcap for format character '%c'", 77131492Swollman pp->printer, format); 77297791Sgad return (ERROR); 77315648Sjoerg } 77427635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7751553Srgrimes av[0]++; 7761553Srgrimes else 7771553Srgrimes av[0] = prog; 7781553Srgrimes av[n++] = "-n"; 7791553Srgrimes av[n++] = logname; 7801553Srgrimes av[n++] = "-h"; 78178300Sgad av[n++] = origin_host; 78231492Swollman av[n++] = pp->acct_file; 7831553Srgrimes av[n] = 0; 7841553Srgrimes fo = pfd; 78597793Sgad if (of_pid > 0) { /* stop output filter */ 7861553Srgrimes write(ofd, "\031\1", 2); 78797793Sgad while ((wpid = 78897793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 7891553Srgrimes ; 79097793Sgad if (wpid < 0) 79197792Sgad syslog(LOG_WARNING, 79297792Sgad "%s: after stopping 'of', wait3() returned: %m", 79379735Sgad pp->printer); 79497781Sgad else if (!WIFSTOPPED(wstatus)) { 7951553Srgrimes (void) close(fi); 79697781Sgad syslog(LOG_WARNING, "%s: output filter died " 79797781Sgad "(pid=%d retcode=%d termsig=%d)", 79897793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 79997781Sgad WTERMSIG(wstatus)); 80097791Sgad return (REPRINT); 8011553Srgrimes } 8021553Srgrimes stopped++; 8031553Srgrimes } 8041553Srgrimesstart: 80531492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 806118881Sgad dup2(fi, STDIN_FILENO); 807118881Sgad dup2(fo, STDOUT_FILENO); 80868664Sgad /* setup stderr for the filter (child process) 80968664Sgad * so it goes to our temporary errors file */ 81068664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8111553Srgrimes if (n >= 0) 812118881Sgad dup2(n, STDERR_FILENO); 8138094Sjkh closelog(); 81431492Swollman closeallfds(3); 8151553Srgrimes execv(prog, av); 81695067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 81795067Sgad prog); 8181553Srgrimes exit(2); 8191553Srgrimes } 8201553Srgrimes (void) close(fi); 82197789Sgad wstatus_set = 0; 8221553Srgrimes if (child < 0) 82397781Sgad retcode = 100; 82479735Sgad else { 82597793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8261553Srgrimes ; 82797793Sgad if (wpid < 0) { 82897781Sgad retcode = 100; 82997792Sgad syslog(LOG_WARNING, 83097792Sgad "%s: after execv(%s), wait() returned: %m", 83179735Sgad pp->printer, prog); 83297789Sgad } else { 83397789Sgad wstatus_set = 1; 83497781Sgad retcode = WEXITSTATUS(wstatus); 83597789Sgad } 83679735Sgad } 8371553Srgrimes child = 0; 8381553Srgrimes prchild = 0; 8391553Srgrimes if (stopped) { /* restart output filter */ 84097793Sgad if (kill(of_pid, SIGCONT) < 0) { 8411553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8421553Srgrimes exit(1); 8431553Srgrimes } 8441553Srgrimes } 84531492Swollman pp->tof = 0; 8461553Srgrimes 84768664Sgad /* Copy the filter's output to "lf" logfile */ 84868664Sgad if ((fp = fopen(tempstderr, "r"))) { 8491553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8501553Srgrimes fputs(buf, stderr); 8511553Srgrimes fclose(fp); 8521553Srgrimes } 8531553Srgrimes 85497789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 85515648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 85697781Sgad pp->printer, format, WTERMSIG(wstatus)); 85797791Sgad return (ERROR); 8581553Srgrimes } 85997781Sgad switch (retcode) { 8601553Srgrimes case 0: 86131492Swollman pp->tof = 1; 86297791Sgad return (OK); 8631553Srgrimes case 1: 86497791Sgad return (REPRINT); 86515648Sjoerg case 2: 86697791Sgad return (ERROR); 8671553Srgrimes default: 86815648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 86997792Sgad pp->printer, format, retcode); 87097791Sgad return (FILTERERR); 8711553Srgrimes } 8721553Srgrimes} 8731553Srgrimes 8741553Srgrimes/* 8751553Srgrimes * Send the daemon control file (cf) and any data files. 8761553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 8771553Srgrimes * 0 if all is well. 8781553Srgrimes */ 8791553Srgrimesstatic int 88078146Sgadsendit(struct printer *pp, char *file) 8811553Srgrimes{ 88295293Sgad int dfcopies, err, i; 8831553Srgrimes char *cp, last[BUFSIZ]; 8841553Srgrimes 8851553Srgrimes /* 8861553Srgrimes * open control file 8871553Srgrimes */ 8881553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 88997791Sgad return (OK); 89068253Sgad 89168253Sgad /* initialize job-specific count of datafiles processed */ 89268253Sgad job_dfcnt = 0; 89368253Sgad 8941553Srgrimes /* 8951553Srgrimes * read the control file for work to do 8961553Srgrimes * 8971553Srgrimes * file format -- first character in the line is a command 8981553Srgrimes * rest of the line is the argument. 8991553Srgrimes * commands of interest are: 9001553Srgrimes * 9011553Srgrimes * a-z -- "file name" name of file to print 9021553Srgrimes * U -- "unlink" name of file to remove 9031553Srgrimes * (after we print it. (Pass 2 only)). 9041553Srgrimes */ 9051553Srgrimes 9061553Srgrimes /* 9071553Srgrimes * pass 1 9081553Srgrimes */ 90995293Sgad err = OK; 9101553Srgrimes while (getline(cfp)) { 9111553Srgrimes again: 9121553Srgrimes if (line[0] == 'S') { 9131553Srgrimes cp = line+1; 9141553Srgrimes i = 0; 9151553Srgrimes while (*cp >= '0' && *cp <= '9') 9161553Srgrimes i = i * 10 + (*cp++ - '0'); 9171553Srgrimes fdev = i; 9181553Srgrimes cp++; 9191553Srgrimes i = 0; 9201553Srgrimes while (*cp >= '0' && *cp <= '9') 9211553Srgrimes i = i * 10 + (*cp++ - '0'); 9221553Srgrimes fino = i; 92324831Sbrian } else if (line[0] == 'H') { 92478300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 92568343Sgad if (class[0] == '\0') { 92680133Sgad strlcpy(class, line + 1, sizeof(class)); 92768343Sgad } 92824831Sbrian } else if (line[0] == 'P') { 92980133Sgad strlcpy(logname, line + 1, sizeof(logname)); 93031492Swollman if (pp->restricted) { /* restricted */ 93124831Sbrian if (getpwnam(logname) == NULL) { 93231492Swollman sendmail(pp, line+1, NOACCT); 93324831Sbrian err = ERROR; 93424831Sbrian break; 93524831Sbrian } 93624831Sbrian } 93724831Sbrian } else if (line[0] == 'I') { 93880133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 93924831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 94095293Sgad dfcopies = 1; 9411553Srgrimes strcpy(last, line); 94295293Sgad while ((i = getline(cfp)) != 0) { 94395293Sgad if (strcmp(last, line) != 0) 9441553Srgrimes break; 94595293Sgad dfcopies++; 94695293Sgad } 94795293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9481553Srgrimes case OK: 9491553Srgrimes if (i) 9501553Srgrimes goto again; 9511553Srgrimes break; 9521553Srgrimes case REPRINT: 9531553Srgrimes (void) fclose(cfp); 95497791Sgad return (REPRINT); 9551553Srgrimes case ACCESS: 95631492Swollman sendmail(pp, logname, ACCESS); 9571553Srgrimes case ERROR: 9581553Srgrimes err = ERROR; 9591553Srgrimes } 9601553Srgrimes break; 9611553Srgrimes } 9621553Srgrimes } 96395293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9641553Srgrimes (void) fclose(cfp); 96597791Sgad return (REPRINT); 9661553Srgrimes } 9671553Srgrimes /* 9681553Srgrimes * pass 2 9691553Srgrimes */ 9701553Srgrimes fseek(cfp, 0L, 0); 9711553Srgrimes while (getline(cfp)) 97227748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9731553Srgrimes (void) unlink(line+1); 9741553Srgrimes /* 9751553Srgrimes * clean-up in case another control file exists 9761553Srgrimes */ 9771553Srgrimes (void) fclose(cfp); 9781553Srgrimes (void) unlink(file); 97997791Sgad return (err); 9801553Srgrimes} 9811553Srgrimes 9821553Srgrimes/* 9831553Srgrimes * Send a data file to the remote machine and spool it. 9841553Srgrimes * Return positive if we should try resending. 9851553Srgrimes */ 9861553Srgrimesstatic int 98795293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 9881553Srgrimes{ 98994036Sgad int i, amt; 9901553Srgrimes struct stat stb; 99194032Sgad char *av[15], *filtcmd; 99294032Sgad char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 99395293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 9941553Srgrimes 99574124Sgad statrc = lstat(file, &stb); 99674124Sgad if (statrc < 0) { 99774124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 99874124Sgad pp->printer, file); 99994036Sgad return (ERROR); 100074124Sgad } 100194036Sgad sfd = open(file, O_RDONLY); 100294036Sgad if (sfd < 0) { 100374124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 100474124Sgad pp->printer, file); 100594036Sgad return (ERROR); 100674124Sgad } 10071553Srgrimes /* 10081553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10091553Srgrimes * still point to the same file or someone is trying to print something 10101553Srgrimes * he shouldn't. 10111553Srgrimes */ 101294036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 101394036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 101494036Sgad close(sfd); 101594036Sgad return (ACCESS); 101694036Sgad } 101724831Sbrian 101894032Sgad /* Everything seems OK for reading the file, now to send it */ 101994032Sgad filtcmd = NULL; 102024831Sbrian sizerr = 0; 102194032Sgad tfd = -1; 102224831Sbrian if (type == '\3') { 102394032Sgad /* 102494032Sgad * Type == 3 means this is a datafile, not a control file. 102594032Sgad * Increment the counter of data-files in this job, and 102694032Sgad * then check for input or output filters (which are only 102794032Sgad * applied to datafiles, not control files). 102894032Sgad */ 102994032Sgad job_dfcnt++; 103094032Sgad 103194032Sgad /* 103294032Sgad * Note that here we are filtering datafiles, one at a time, 103394032Sgad * as they are sent to the remote machine. Here, the *only* 103494032Sgad * difference between an input filter (`if=') and an output 103594032Sgad * filter (`of=') is the argument list that the filter is 103694032Sgad * started up with. Here, the output filter is executed 103794032Sgad * for each individual file as it is sent. This is not the 103894032Sgad * same as local print queues, where the output filter is 103994032Sgad * started up once, and then all jobs are passed thru that 104094032Sgad * single invocation of the output filter. 104194032Sgad * 104294032Sgad * Also note that a queue for a remote-machine can have an 104394032Sgad * input filter or an output filter, but not both. 104494032Sgad */ 104531492Swollman if (pp->filters[LPF_INPUT]) { 104694032Sgad filtcmd = pp->filters[LPF_INPUT]; 104794032Sgad av[0] = filtcmd; 104894032Sgad narg = 0; 104994032Sgad strcpy(opt_c, "-c"); 105094032Sgad strcpy(opt_h, "-h"); 105194032Sgad strcpy(opt_n, "-n"); 105224831Sbrian if (format == 'l') 105394032Sgad av[++narg] = opt_c; 105494032Sgad av[++narg] = width; 105594032Sgad av[++narg] = length; 105694032Sgad av[++narg] = indent; 105794032Sgad av[++narg] = opt_n; 105894032Sgad av[++narg] = logname; 105994032Sgad av[++narg] = opt_h; 106094032Sgad av[++narg] = origin_host; 106194032Sgad av[++narg] = pp->acct_file; 106294032Sgad av[++narg] = NULL; 106394032Sgad } else if (pp->filters[LPF_OUTPUT]) { 106494032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 106594032Sgad av[0] = filtcmd; 106694032Sgad narg = 0; 106794032Sgad av[++narg] = width; 106894032Sgad av[++narg] = length; 106994032Sgad av[++narg] = NULL; 107024831Sbrian } 107124831Sbrian } 107294032Sgad if (filtcmd) { 107394032Sgad /* 107494032Sgad * If there is an input or output filter, we have to run 107594032Sgad * the datafile thru that filter and store the result as 107694032Sgad * a temporary spool file, because the protocol requires 107794032Sgad * that we send the remote host the file-size before we 107894032Sgad * start to send any of the data. 107994032Sgad */ 108094032Sgad strcpy(tfile, TFILENAME); 108194032Sgad tfd = mkstemp(tfile); 108294032Sgad if (tfd == -1) { 108394032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 108494032Sgad TFILENAME); 108594036Sgad sfres = ERROR; 108694036Sgad goto return_sfres; 108794032Sgad } 108894036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 108924831Sbrian 109094032Sgad /* process the return-code from the filter */ 109194032Sgad switch (filtstat) { 109294032Sgad case 0: 109394032Sgad break; 109494032Sgad case 1: 109594036Sgad sfres = REPRINT; 109694036Sgad goto return_sfres; 109794032Sgad case 2: 109894036Sgad sfres = ERROR; 109994036Sgad goto return_sfres; 110094032Sgad default: 110194032Sgad syslog(LOG_WARNING, 110294032Sgad "%s: filter '%c' exited (retcode=%d)", 110394032Sgad pp->printer, format, filtstat); 110494036Sgad sfres = FILTERERR; 110594036Sgad goto return_sfres; 110694032Sgad } 110794032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 110894032Sgad if (statrc < 0) { 110994032Sgad syslog(LOG_ERR, 111094032Sgad "%s: error processing 'if', fstat(%s): %m", 111194032Sgad pp->printer, tfile); 111294036Sgad sfres = ERROR; 111394036Sgad goto return_sfres; 111494032Sgad } 111594036Sgad close(sfd); 111694036Sgad sfd = tfd; 111794036Sgad lseek(sfd, 0, SEEK_SET); 111894032Sgad } 111994032Sgad 112095293Sgad copycnt = 0; 112195293Sgadsendagain: 112295293Sgad copycnt++; 112395293Sgad 112495293Sgad if (copycnt < 2) 112595293Sgad (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 112695293Sgad else 112795293Sgad (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 112895293Sgad file, copycnt); 11291553Srgrimes amt = strlen(buf); 11301553Srgrimes for (i = 0; ; i++) { 11311553Srgrimes if (write(pfd, buf, amt) != amt || 113231492Swollman (resp = response(pp)) < 0 || resp == '\1') { 113394036Sgad sfres = REPRINT; 113494036Sgad goto return_sfres; 11351553Srgrimes } else if (resp == '\0') 11361553Srgrimes break; 11371553Srgrimes if (i == 0) 113831492Swollman pstatus(pp, 113931492Swollman "no space on remote; waiting for queue to drain"); 11401553Srgrimes if (i == 10) 11411553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 114297792Sgad pp->printer, pp->remote_host); 11431553Srgrimes sleep(5 * 60); 11441553Srgrimes } 11451553Srgrimes if (i) 114631492Swollman pstatus(pp, "sending to %s", pp->remote_host); 114795293Sgad /* 114895293Sgad * XXX - we should change trstat_init()/trstat_write() to include 114995293Sgad * the copycnt in the statistics record it may write. 115095293Sgad */ 115168253Sgad if (type == '\3') 115268253Sgad trstat_init(pp, file, job_dfcnt); 11531553Srgrimes for (i = 0; i < stb.st_size; i += BUFSIZ) { 11541553Srgrimes amt = BUFSIZ; 11551553Srgrimes if (i + amt > stb.st_size) 11561553Srgrimes amt = stb.st_size - i; 115794036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11581553Srgrimes sizerr = 1; 11591553Srgrimes if (write(pfd, buf, amt) != amt) { 116094036Sgad sfres = REPRINT; 116194036Sgad goto return_sfres; 11621553Srgrimes } 11631553Srgrimes } 11641553Srgrimes 11651553Srgrimes if (sizerr) { 116631492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11671553Srgrimes /* tell recvjob to ignore this file */ 11681553Srgrimes (void) write(pfd, "\1", 1); 116994036Sgad sfres = ERROR; 117094036Sgad goto return_sfres; 11711553Srgrimes } 117231492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 117394036Sgad sfres = REPRINT; 117494036Sgad goto return_sfres; 117524831Sbrian } 117695293Sgad if (type == '\3') { 117768253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 117895293Sgad pp->remote_host, origin_host); 117995293Sgad /* 118095293Sgad * Usually we only need to send one copy of a datafile, 118195293Sgad * because the control-file will simply print the same 118295293Sgad * file multiple times. However, some printers ignore 118395293Sgad * the control file, and simply print each data file as 118495293Sgad * it arrives. For such "remote hosts", we need to 118595293Sgad * transfer the same data file multiple times. Such a 118695293Sgad * a host is indicated by adding 'rc' to the printcap 118795293Sgad * entry. 118895293Sgad * XXX - Right now this ONLY works for remote hosts which 118995293Sgad * do ignore the name of the data file, because 119095293Sgad * this sends the file multiple times with slight 119195293Sgad * changes to the filename. To do this right would 119295293Sgad * require that we also rewrite the control file 119395293Sgad * to match those filenames. 119495293Sgad */ 119595293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 119695293Sgad lseek(sfd, 0, SEEK_SET); 119795293Sgad goto sendagain; 119895293Sgad } 119995293Sgad } 120094036Sgad sfres = OK; 120194036Sgad 120294036Sgadreturn_sfres: 120394036Sgad (void)close(sfd); 120494036Sgad if (tfd != -1) { 120594036Sgad /* 120694036Sgad * If tfd is set, then it is the same value as sfd, and 120794036Sgad * therefore it is already closed at this point. All 120894036Sgad * we need to do is remove the temporary file. 120994036Sgad */ 121094036Sgad tfd = -1; 121194036Sgad unlink(tfile); 121294036Sgad } 121394036Sgad return (sfres); 12141553Srgrimes} 12151553Srgrimes 12161553Srgrimes/* 121794032Sgad * This routine is called to execute one of the filters as was 121894036Sgad * specified in a printcap entry. While the child-process will read 121994036Sgad * all of 'infd', it is up to the caller to close that file descriptor 122094036Sgad * in the parent process. 122194032Sgad */ 122294032Sgadstatic int 122394032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 122494032Sgad{ 122597793Sgad pid_t fpid, wpid; 122697793Sgad int errfd, retcode, wstatus; 122794032Sgad FILE *errfp; 122894032Sgad char buf[BUFSIZ], *slash; 122994032Sgad 123094032Sgad fpid = dofork(pp, DORETURN); 123194032Sgad if (fpid != 0) { 123294032Sgad /* 123394032Sgad * This is the parent process, which just waits for the child 123494032Sgad * to complete and then returns the result. Note that it is 123594032Sgad * the child process which reads the input stream. 123694032Sgad */ 123794032Sgad if (fpid < 0) 123897781Sgad retcode = 100; 123994032Sgad else { 124097781Sgad while ((wpid = wait(&wstatus)) > 0 && 124194032Sgad wpid != fpid) 124294032Sgad ; 124394032Sgad if (wpid < 0) { 124497781Sgad retcode = 100; 124594032Sgad syslog(LOG_WARNING, 124694032Sgad "%s: after execv(%s), wait() returned: %m", 124794032Sgad pp->printer, f_cmd); 124897781Sgad } else 124997781Sgad retcode = WEXITSTATUS(wstatus); 125094032Sgad } 125194032Sgad 125294032Sgad /* 125394032Sgad * Copy everything the filter wrote to stderr from our 125494032Sgad * temporary errors file to the "lf=" logfile. 125594032Sgad */ 125694032Sgad errfp = fopen(tempstderr, "r"); 125794032Sgad if (errfp) { 125894032Sgad while (fgets(buf, sizeof(buf), errfp)) 125994032Sgad fputs(buf, stderr); 126094032Sgad fclose(errfp); 126194032Sgad } 126294032Sgad 126397781Sgad return (retcode); 126494032Sgad } 126594032Sgad 126694032Sgad /* 126794032Sgad * This is the child process, which is the one that executes the 126894032Sgad * given filter. 126994032Sgad */ 127094032Sgad /* 127194032Sgad * If the first parameter has any slashes in it, then change it 127294032Sgad * to point to the first character after the last slash. 127394032Sgad */ 127494032Sgad slash = strrchr(f_av[0], '/'); 127594032Sgad if (slash != NULL) 127694032Sgad f_av[0] = slash + 1; 127794032Sgad /* 127894032Sgad * XXX - in the future, this should setup an explicit list of 127994032Sgad * environment variables and use execve()! 128094032Sgad */ 128194032Sgad 128294032Sgad /* 128394032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 128494032Sgad * is running. Stderr is setup so it points to a temporary errors 128594032Sgad * file, and the parent process will copy that temporary file to 128694032Sgad * the real logfile after the filter completes. 128794032Sgad */ 1288118881Sgad dup2(infd, STDIN_FILENO); 1289118881Sgad dup2(outfd, STDOUT_FILENO); 129094032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 129194032Sgad if (errfd >= 0) 1292118881Sgad dup2(errfd, STDERR_FILENO); 129394032Sgad closelog(); 129494032Sgad closeallfds(3); 129594032Sgad execv(f_cmd, f_av); 129695067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 129794032Sgad exit(2); 129894032Sgad /* NOTREACHED */ 129994032Sgad} 130094032Sgad 130194032Sgad/* 13021553Srgrimes * Check to make sure there have been no errors and that both programs 13031553Srgrimes * are in sync with eachother. 13041553Srgrimes * Return non-zero if the connection was lost. 13051553Srgrimes */ 13061553Srgrimesstatic char 130778146Sgadresponse(const struct printer *pp) 13081553Srgrimes{ 13091553Srgrimes char resp; 13101553Srgrimes 13111553Srgrimes if (read(pfd, &resp, 1) != 1) { 131231492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 131397791Sgad return (-1); 13141553Srgrimes } 131597791Sgad return (resp); 13161553Srgrimes} 13171553Srgrimes 13181553Srgrimes/* 13191553Srgrimes * Banner printing stuff 13201553Srgrimes */ 13211553Srgrimesstatic void 132278146Sgadbanner(struct printer *pp, char *name1, char *name2) 13231553Srgrimes{ 13241553Srgrimes time_t tvec; 13251553Srgrimes 13261553Srgrimes time(&tvec); 132731492Swollman if (!pp->no_formfeed && !pp->tof) 132831492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 132931492Swollman if (pp->short_banner) { /* short banner only */ 13301553Srgrimes if (class[0]) { 13311553Srgrimes (void) write(ofd, class, strlen(class)); 13321553Srgrimes (void) write(ofd, ":", 1); 13331553Srgrimes } 13341553Srgrimes (void) write(ofd, name1, strlen(name1)); 13351553Srgrimes (void) write(ofd, " Job: ", 7); 13361553Srgrimes (void) write(ofd, name2, strlen(name2)); 13371553Srgrimes (void) write(ofd, " Date: ", 8); 13381553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13391553Srgrimes (void) write(ofd, "\n", 1); 13401553Srgrimes } else { /* normal banner */ 13411553Srgrimes (void) write(ofd, "\n\n\n", 3); 134231492Swollman scan_out(pp, ofd, name1, '\0'); 13431553Srgrimes (void) write(ofd, "\n\n", 2); 134431492Swollman scan_out(pp, ofd, name2, '\0'); 13451553Srgrimes if (class[0]) { 13461553Srgrimes (void) write(ofd,"\n\n\n",3); 134731492Swollman scan_out(pp, ofd, class, '\0'); 13481553Srgrimes } 13491553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 13501553Srgrimes (void) write(ofd, name2, strlen(name2)); 13511553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 13521553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13531553Srgrimes (void) write(ofd, "\n", 1); 13541553Srgrimes } 135531492Swollman if (!pp->no_formfeed) 135631492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 135731492Swollman pp->tof = 1; 13581553Srgrimes} 13591553Srgrimes 13601553Srgrimesstatic char * 136178146Sgadscnline(int key, char *p, int c) 13621553Srgrimes{ 136339084Swollman register int scnwidth; 13641553Srgrimes 13651553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 13661553Srgrimes key <<= 1; 13671553Srgrimes *p++ = key & 0200 ? c : BACKGND; 13681553Srgrimes } 13691553Srgrimes return (p); 13701553Srgrimes} 13711553Srgrimes 13721553Srgrimes#define TRC(q) (((q)-' ')&0177) 13731553Srgrimes 13741553Srgrimesstatic void 137578146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 13761553Srgrimes{ 13771553Srgrimes register char *strp; 137839084Swollman register int nchrs, j; 13791553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 13801553Srgrimes int d, scnhgt; 13811553Srgrimes 13821553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 13831553Srgrimes strp = &outbuf[0]; 13841553Srgrimes sp = scsp; 13851553Srgrimes for (nchrs = 0; ; ) { 13861553Srgrimes d = dropit(c = TRC(cc = *sp++)); 13871553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 13881553Srgrimes for (j = WIDTH; --j;) 13891553Srgrimes *strp++ = BACKGND; 13901553Srgrimes else 139131492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 139231492Swollman if (*sp == dlm || *sp == '\0' || 139331492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 13941553Srgrimes break; 13951553Srgrimes *strp++ = BACKGND; 13961553Srgrimes *strp++ = BACKGND; 13971553Srgrimes } 13981553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 13991553Srgrimes ; 14001553Srgrimes strp++; 14018857Srgrimes *strp++ = '\n'; 14021553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 14031553Srgrimes } 14041553Srgrimes} 14051553Srgrimes 14061553Srgrimesstatic int 140778146Sgaddropit(int c) 14081553Srgrimes{ 14091553Srgrimes switch(c) { 14101553Srgrimes 14111553Srgrimes case TRC('_'): 14121553Srgrimes case TRC(';'): 14131553Srgrimes case TRC(','): 14141553Srgrimes case TRC('g'): 14151553Srgrimes case TRC('j'): 14161553Srgrimes case TRC('p'): 14171553Srgrimes case TRC('q'): 14181553Srgrimes case TRC('y'): 14191553Srgrimes return (DROP); 14201553Srgrimes 14211553Srgrimes default: 14221553Srgrimes return (0); 14231553Srgrimes } 14241553Srgrimes} 14251553Srgrimes 14261553Srgrimes/* 14271553Srgrimes * sendmail --- 14281553Srgrimes * tell people about job completion 14291553Srgrimes */ 14301553Srgrimesstatic void 143194038Sgadsendmail(struct printer *pp, char *userid, int bombed) 14321553Srgrimes{ 14331553Srgrimes register int i; 14341553Srgrimes int p[2], s; 143578146Sgad register const char *cp; 14361553Srgrimes struct stat stb; 14371553Srgrimes FILE *fp; 14381553Srgrimes 14391553Srgrimes pipe(p); 144031492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1441118881Sgad dup2(p[0], STDIN_FILENO); 14428094Sjkh closelog(); 144331492Swollman closeallfds(3); 144427635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 14451553Srgrimes cp++; 144631492Swollman else 14471553Srgrimes cp = _PATH_SENDMAIL; 144879452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 144931492Swollman _exit(0); 14501553Srgrimes } else if (s > 0) { /* parent */ 1451118881Sgad dup2(p[1], STDOUT_FILENO); 145294038Sgad printf("To: %s@%s\n", userid, origin_host); 145331492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 145415648Sjoerg *jobname ? jobname : "<unknown>"); 145578300Sgad printf("Reply-To: root@%s\n\n", local_host); 14561553Srgrimes printf("Your printer job "); 14571553Srgrimes if (*jobname) 14581553Srgrimes printf("(%s) ", jobname); 145994040Sgad 14601553Srgrimes switch (bombed) { 14611553Srgrimes case OK: 146294040Sgad cp = "OK"; 14631553Srgrimes printf("\ncompleted successfully\n"); 14641553Srgrimes break; 14651553Srgrimes default: 14661553Srgrimes case FATALERR: 146794040Sgad cp = "FATALERR"; 14681553Srgrimes printf("\ncould not be printed\n"); 14691553Srgrimes break; 14701553Srgrimes case NOACCT: 147194040Sgad cp = "NOACCT"; 147278300Sgad printf("\ncould not be printed without an account on %s\n", 147378300Sgad local_host); 14741553Srgrimes break; 14751553Srgrimes case FILTERERR: 147694040Sgad cp = "FILTERERR"; 147768664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 147868664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 147915648Sjoerg printf("\nhad some errors and may not have printed\n"); 14801553Srgrimes break; 14811553Srgrimes } 148215648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 14831553Srgrimes while ((i = getc(fp)) != EOF) 14841553Srgrimes putchar(i); 14851553Srgrimes (void) fclose(fp); 14861553Srgrimes break; 14871553Srgrimes case ACCESS: 148894040Sgad cp = "ACCESS"; 14891553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 14901553Srgrimes } 14911553Srgrimes fflush(stdout); 1492118881Sgad (void) close(STDOUT_FILENO); 149331492Swollman } else { 149494038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 149531492Swollman return; 14961553Srgrimes } 14971553Srgrimes (void) close(p[0]); 14981553Srgrimes (void) close(p[1]); 149915648Sjoerg wait(NULL); 150015648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 150194038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 15021553Srgrimes} 15031553Srgrimes 15041553Srgrimes/* 15051553Srgrimes * dofork - fork with retries on failure 15061553Srgrimes */ 15071553Srgrimesstatic int 150878146Sgaddofork(const struct printer *pp, int action) 15091553Srgrimes{ 151097793Sgad pid_t forkpid; 151197793Sgad int i, fail; 151260871Smpp struct passwd *pwd; 15131553Srgrimes 151480230Sgad forkpid = -1; 151580230Sgad if (daemon_uname == NULL) { 151680230Sgad pwd = getpwuid(pp->daemon_user); 151780230Sgad if (pwd == NULL) { 151880230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 151980230Sgad pp->printer, pp->daemon_user); 152080230Sgad goto error_ret; 152180230Sgad } 152280230Sgad daemon_uname = strdup(pwd->pw_name); 152380230Sgad daemon_defgid = pwd->pw_gid; 152480230Sgad } 152580230Sgad 15261553Srgrimes for (i = 0; i < 20; i++) { 152780230Sgad forkpid = fork(); 152880230Sgad if (forkpid < 0) { 15291553Srgrimes sleep((unsigned)(i*i)); 15301553Srgrimes continue; 15311553Srgrimes } 15321553Srgrimes /* 15331553Srgrimes * Child should run as daemon instead of root 15341553Srgrimes */ 153578146Sgad if (forkpid == 0) { 153680230Sgad errno = 0; 153780230Sgad fail = initgroups(daemon_uname, daemon_defgid); 153880230Sgad if (fail) { 153980230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 154080230Sgad pp->printer, daemon_uname, daemon_defgid); 154160871Smpp break; 154260871Smpp } 154380230Sgad fail = setgid(daemon_defgid); 154480230Sgad if (fail) { 154580230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 154680230Sgad pp->printer, daemon_defgid); 154780230Sgad break; 154880230Sgad } 154980230Sgad fail = setuid(pp->daemon_user); 155080230Sgad if (fail) { 155180230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 155280230Sgad pp->printer, pp->daemon_user); 155380230Sgad break; 155480230Sgad } 155560871Smpp } 155697791Sgad return (forkpid); 15571553Srgrimes } 15581553Srgrimes 155980230Sgad /* 156080230Sgad * An error occurred. If the error is in the child process, then 156180230Sgad * this routine MUST always exit(). DORETURN only effects how 156280230Sgad * errors should be handled in the parent process. 156380230Sgad */ 156480230Sgaderror_ret: 156580230Sgad if (forkpid == 0) { 156680230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 156780230Sgad pp->printer); 156880230Sgad exit(1); 156980230Sgad } 157080230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 157180230Sgad 157280230Sgad sleep(1); /* throttle errors, as a safety measure */ 15731553Srgrimes switch (action) { 15741553Srgrimes case DORETURN: 157597791Sgad return (-1); 15761553Srgrimes default: 15771553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 157880230Sgad /* FALLTHROUGH */ 15791553Srgrimes case DOABORT: 15801553Srgrimes exit(1); 15811553Srgrimes } 15821553Srgrimes /*NOTREACHED*/ 15831553Srgrimes} 15841553Srgrimes 15851553Srgrimes/* 15861553Srgrimes * Kill child processes to abort current job. 15871553Srgrimes */ 15881553Srgrimesstatic void 158978146Sgadabortpr(int signo __unused) 15901553Srgrimes{ 159168664Sgad 159268664Sgad (void) unlink(tempstderr); 15931553Srgrimes kill(0, SIGINT); 159497793Sgad if (of_pid > 0) 159597793Sgad kill(of_pid, SIGCONT); 15961553Srgrimes while (wait(NULL) > 0) 15971553Srgrimes ; 159897793Sgad if (of_pid > 0 && tfd != -1) 159924831Sbrian unlink(tfile); 16001553Srgrimes exit(0); 16011553Srgrimes} 16021553Srgrimes 16031553Srgrimesstatic void 160478146Sgadinit(struct printer *pp) 16051553Srgrimes{ 16061553Srgrimes char *s; 16071553Srgrimes 160831492Swollman sprintf(&width[2], "%ld", pp->page_width); 160931492Swollman sprintf(&length[2], "%ld", pp->page_length); 161031492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 161131492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 161231492Swollman if ((s = checkremote(pp)) != 0) { 161331492Swollman syslog(LOG_WARNING, "%s", s); 161431492Swollman free(s); 161531492Swollman } 161631492Swollman} 161731492Swollman 161831492Swollmanvoid 161978146Sgadstartprinting(const char *printer) 162031492Swollman{ 162131492Swollman struct printer myprinter, *pp = &myprinter; 162231492Swollman int status; 162331492Swollman 162431492Swollman init_printer(pp); 162531492Swollman status = getprintcap(printer, pp); 162631492Swollman switch(status) { 162731492Swollman case PCAPERR_OSERR: 162831492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 16291553Srgrimes exit(1); 163031492Swollman case PCAPERR_NOTFOUND: 16311553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 16321553Srgrimes exit(1); 163331492Swollman case PCAPERR_TCLOOP: 163431492Swollman fatal(pp, "potential reference loop detected in printcap file"); 163531492Swollman default: 163631492Swollman break; 163731492Swollman } 163831492Swollman printjob(pp); 16391553Srgrimes} 16401553Srgrimes 16411553Srgrimes/* 16421553Srgrimes * Acquire line printer or remote connection. 16431553Srgrimes */ 16441553Srgrimesstatic void 164578146Sgadopenpr(const struct printer *pp) 16461553Srgrimes{ 164731492Swollman int p[2]; 164815648Sjoerg char *cp; 16491553Srgrimes 165031492Swollman if (pp->remote) { 165131492Swollman openrem(pp); 165294032Sgad /* 165394032Sgad * Lpd does support the setting of 'of=' filters for 165494032Sgad * jobs going to remote machines, but that does not 165594032Sgad * have the same meaning as 'of=' does when handling 165694032Sgad * local print queues. For remote machines, all 'of=' 165794032Sgad * filter processing is handled in sendfile(), and that 165894032Sgad * does not use these global "output filter" variables. 165994032Sgad */ 166094032Sgad ofd = -1; 166197793Sgad of_pid = 0; 166294032Sgad return; 166331492Swollman } else if (*pp->lp) { 166431492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 166531492Swollman opennet(pp); 166615648Sjoerg else 166731492Swollman opentty(pp); 16681553Srgrimes } else { 16691553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 167097792Sgad pp->printer); 16711553Srgrimes exit(1); 16721553Srgrimes } 167315648Sjoerg 16741553Srgrimes /* 16751553Srgrimes * Start up an output filter, if needed. 16761553Srgrimes */ 167797793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 16781553Srgrimes pipe(p); 167931492Swollman if (pp->remote) { 168031492Swollman strcpy(tfile, TFILENAME); 168124831Sbrian tfd = mkstemp(tfile); 168224831Sbrian } 168397793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 1684118881Sgad dup2(p[0], STDIN_FILENO); /* pipe is std in */ 168524831Sbrian /* tfile/printer is stdout */ 1686118881Sgad dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); 16878094Sjkh closelog(); 168831492Swollman closeallfds(3); 168931492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 169031492Swollman cp = pp->filters[LPF_OUTPUT]; 16911553Srgrimes else 16921553Srgrimes cp++; 169379452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 169479452Sbrian (char *)0); 169597792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 169697792Sgad pp->filters[LPF_OUTPUT]); 16971553Srgrimes exit(1); 16981553Srgrimes } 16991553Srgrimes (void) close(p[0]); /* close input side */ 17001553Srgrimes ofd = p[1]; /* use pipe for output */ 17011553Srgrimes } else { 17021553Srgrimes ofd = pfd; 170397793Sgad of_pid = 0; 17041553Srgrimes } 17051553Srgrimes} 17061553Srgrimes 170715648Sjoerg/* 170815648Sjoerg * Printer connected directly to the network 170915648Sjoerg * or to a terminal server on the net 171015648Sjoerg */ 171115648Sjoergstatic void 171278146Sgadopennet(const struct printer *pp) 171315648Sjoerg{ 171415648Sjoerg register int i; 171531492Swollman int resp; 171631492Swollman u_long port; 171731492Swollman char *ep; 171830407Sjoerg void (*savealrm)(int); 171915648Sjoerg 172031492Swollman port = strtoul(pp->lp, &ep, 0); 172138470Sbrian if (*ep != '@' || port > 65535) { 172231492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 172397792Sgad pp->lp); 172415648Sjoerg exit(1); 172515648Sjoerg } 172631492Swollman ep++; 172715648Sjoerg 172815648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 172915648Sjoerg resp = -1; 173030407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 173131492Swollman alarm(pp->conn_timeout); 173231492Swollman pfd = getport(pp, ep, port); 173331020Sjoerg alarm(0); 173430407Sjoerg (void)signal(SIGALRM, savealrm); 173515648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 173615648Sjoerg resp = 1; 173715648Sjoerg else if (pfd >= 0) { 173815648Sjoerg /* 173915648Sjoerg * need to delay a bit for rs232 lines 174015648Sjoerg * to stabilize in case printer is 174115648Sjoerg * connected via a terminal server 174215648Sjoerg */ 174315648Sjoerg delay(500); 174415648Sjoerg break; 174515648Sjoerg } 174615648Sjoerg if (i == 1) { 174731492Swollman if (resp < 0) 174831492Swollman pstatus(pp, "waiting for %s to come up", 174931492Swollman pp->lp); 175031492Swollman else 175131492Swollman pstatus(pp, 175231492Swollman "waiting for access to printer on %s", 175331492Swollman pp->lp); 175415648Sjoerg } 175515648Sjoerg sleep(i); 175615648Sjoerg } 175779739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 175815648Sjoerg} 175915648Sjoerg 176015648Sjoerg/* 176115648Sjoerg * Printer is connected to an RS232 port on this host 176215648Sjoerg */ 176315648Sjoergstatic void 176478146Sgadopentty(const struct printer *pp) 176515648Sjoerg{ 176615648Sjoerg register int i; 176715648Sjoerg 176815648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 176931492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 177015648Sjoerg if (pfd >= 0) { 177115648Sjoerg delay(500); 177215648Sjoerg break; 177315648Sjoerg } 177415648Sjoerg if (errno == ENOENT) { 177531492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 177615648Sjoerg exit(1); 177715648Sjoerg } 177815648Sjoerg if (i == 1) 177931492Swollman pstatus(pp, 178031492Swollman "waiting for %s to become ready (offline?)", 178131492Swollman pp->printer); 178215648Sjoerg sleep(i); 178315648Sjoerg } 178415648Sjoerg if (isatty(pfd)) 178531492Swollman setty(pp); 178631492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 178715648Sjoerg} 178815648Sjoerg 178915648Sjoerg/* 179015648Sjoerg * Printer is on a remote host 179115648Sjoerg */ 179215648Sjoergstatic void 179378146Sgadopenrem(const struct printer *pp) 179415648Sjoerg{ 179531492Swollman register int i; 179627748Simp int resp; 179730407Sjoerg void (*savealrm)(int); 179815648Sjoerg 179915648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 180015648Sjoerg resp = -1; 180130407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 180231492Swollman alarm(pp->conn_timeout); 180331492Swollman pfd = getport(pp, pp->remote_host, 0); 180431020Sjoerg alarm(0); 180530407Sjoerg (void)signal(SIGALRM, savealrm); 180615648Sjoerg if (pfd >= 0) { 180731492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 180831492Swollman (char *)0) 180931492Swollman == 2 + strlen(pp->remote_queue)) 181031492Swollman && (resp = response(pp)) == 0) 181115648Sjoerg break; 181215648Sjoerg (void) close(pfd); 181315648Sjoerg } 181415648Sjoerg if (i == 1) { 181515648Sjoerg if (resp < 0) 181631492Swollman pstatus(pp, "waiting for %s to come up", 181731492Swollman pp->remote_host); 181815648Sjoerg else { 181931492Swollman pstatus(pp, 182031492Swollman "waiting for queue to be enabled on %s", 182131492Swollman pp->remote_host); 182215648Sjoerg i = 256; 182315648Sjoerg } 182415648Sjoerg } 182515648Sjoerg sleep(i); 182615648Sjoerg } 182731492Swollman pstatus(pp, "sending to %s", pp->remote_host); 182815648Sjoerg} 182915648Sjoerg 18301553Srgrimes/* 18311553Srgrimes * setup tty lines. 18321553Srgrimes */ 18331553Srgrimesstatic void 183478146Sgadsetty(const struct printer *pp) 18351553Srgrimes{ 183615032Ssef struct termios ttybuf; 18371553Srgrimes 18381553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 183931492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 18401553Srgrimes exit(1); 18411553Srgrimes } 184215032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 184331492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 18441553Srgrimes exit(1); 18451553Srgrimes } 184631492Swollman if (pp->baud_rate > 0) 184731492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 184831492Swollman if (pp->mode_set) { 184931492Swollman char *s = strdup(pp->mode_set), *tmp; 185015032Ssef 185131492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 185239084Swollman (void) msearch(tmp, &ttybuf); 18531553Srgrimes } 18541553Srgrimes } 185531492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 185615032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 185731492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 18581553Srgrimes } 18591553Srgrimes } 18601553Srgrimes} 18611553Srgrimes 18621553Srgrimes#include <stdarg.h> 18631553Srgrimes 186415648Sjoergstatic void 186531492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 18661553Srgrimes{ 186731492Swollman int fd; 186831492Swollman char *buf; 18691553Srgrimes va_list ap; 18701553Srgrimes va_start(ap, msg); 18711553Srgrimes 18721553Srgrimes umask(0); 187331492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 187431492Swollman if (fd < 0) { 187597792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 187697792Sgad pp->status_file); 18771553Srgrimes exit(1); 18781553Srgrimes } 18791553Srgrimes ftruncate(fd, 0); 188031492Swollman vasprintf(&buf, msg, ap); 18811553Srgrimes va_end(ap); 188231492Swollman writel(fd, buf, "\n", (char *)0); 188331492Swollman close(fd); 188431492Swollman free(buf); 18851553Srgrimes} 188630407Sjoerg 188730407Sjoergvoid 188878146Sgadalarmhandler(int signo __unused) 188930407Sjoerg{ 189078146Sgad /* the signal is ignored */ 189178146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 189230407Sjoerg} 1893