printjob.c revision 117554
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 4215648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 43117554Sgad#endif 441553Srgrimes 45117554Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 46117554Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 117554 2003-07-14 15:54:41Z gad $"); 471553Srgrimes 481553Srgrimes/* 491553Srgrimes * printjob -- print jobs in the queue. 501553Srgrimes * 511553Srgrimes * NOTE: the lock file is used to pass information to lpq and lprm. 521553Srgrimes * it does not need to be removed because file locks are dynamic. 531553Srgrimes */ 541553Srgrimes 551553Srgrimes#include <sys/param.h> 561553Srgrimes#include <sys/wait.h> 571553Srgrimes#include <sys/stat.h> 581553Srgrimes#include <sys/types.h> 591553Srgrimes 601553Srgrimes#include <pwd.h> 611553Srgrimes#include <unistd.h> 621553Srgrimes#include <signal.h> 631553Srgrimes#include <syslog.h> 641553Srgrimes#include <fcntl.h> 651553Srgrimes#include <dirent.h> 661553Srgrimes#include <errno.h> 671553Srgrimes#include <stdio.h> 681553Srgrimes#include <string.h> 691553Srgrimes#include <stdlib.h> 7015032Ssef#include <sys/ioctl.h> 7115032Ssef#include <termios.h> 7215703Sjoerg#include <time.h> 731553Srgrimes#include "lp.h" 741553Srgrimes#include "lp.local.h" 751553Srgrimes#include "pathnames.h" 761553Srgrimes#include "extern.h" 771553Srgrimes 7880230Sgad#define DORETURN 0 /* dofork should return "can't fork" error */ 7980230Sgad#define DOABORT 1 /* dofork should just die if fork() fails */ 801553Srgrimes 811553Srgrimes/* 821553Srgrimes * Error tokens 831553Srgrimes */ 841553Srgrimes#define REPRINT -2 851553Srgrimes#define ERROR -1 861553Srgrimes#define OK 0 871553Srgrimes#define FATALERR 1 881553Srgrimes#define NOACCT 2 891553Srgrimes#define FILTERERR 3 901553Srgrimes#define ACCESS 4 911553Srgrimes 921553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 931553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 941553Srgrimesstatic FILE *cfp; /* control file */ 9597793Sgadstatic pid_t of_pid; /* process id of output filter, if any */ 961553Srgrimesstatic int child; /* id of any filters */ 9768253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 981553Srgrimesstatic int lfd; /* lock file descriptor */ 991553Srgrimesstatic int ofd; /* output filter file descriptor */ 10024831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1011553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1021553Srgrimesstatic int prchild; /* id of pr process */ 1031553Srgrimesstatic char title[80]; /* ``pr'' title */ 10453956Sachestatic char locale[80]; /* ``pr'' locale */ 1051553Srgrimes 10680230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 10780230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 10880230Sgadstatic int daemon_defgid; 10980230Sgad 1101553Srgrimesstatic char class[32]; /* classification field */ 11178300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1121553Srgrimes /* indentation size in static characters */ 1138857Srgrimesstatic char indent[10] = "-i0"; 1141553Srgrimesstatic char jobname[100]; /* job or file name */ 1151553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1161553Srgrimesstatic char logname[32]; /* user's login name */ 1171553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1181553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 11968664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12068664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1211553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12224831Sbrian#define TFILENAME "fltXXXXXX" 12324831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1241553Srgrimes 12578146Sgadstatic void abortpr(int _signo); 12678146Sgadstatic void alarmhandler(int _signo); 12778146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 12878146Sgadstatic int dofork(const struct printer *_pp, int _action); 12978146Sgadstatic int dropit(int _c); 13094032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13194032Sgad int _infd, int _outfd); 13278146Sgadstatic void init(struct printer *_pp); 13378146Sgadstatic void openpr(const struct printer *_pp); 13478146Sgadstatic void opennet(const struct printer *_pp); 13578146Sgadstatic void opentty(const struct printer *_pp); 13678146Sgadstatic void openrem(const struct printer *pp); 13778146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 13878146Sgadstatic int printit(struct printer *_pp, char *_file); 13979739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14079739Sgad __printflike(2, 3); 14178146Sgadstatic char response(const struct printer *_pp); 14278146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14378146Sgad int _dlm); 14478146Sgadstatic char *scnline(int _key, char *_p, int _c); 14578146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 14695293Sgad char _format, int _copyreq); 14778146Sgadstatic int sendit(struct printer *_pp, char *_file); 14894038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 14978146Sgadstatic void setty(const struct printer *_pp); 1501553Srgrimes 1511553Srgrimesvoid 15278146Sgadprintjob(struct printer *pp) 1531553Srgrimes{ 1541553Srgrimes struct stat stb; 15568401Sgad register struct jobqueue *q, **qp; 15668401Sgad struct jobqueue **queue; 1571553Srgrimes register int i, nitems; 15868733Sgad off_t pidoff; 15997793Sgad pid_t printpid; 16068733Sgad int errcnt, jobcount, tempfd; 1611553Srgrimes 16268733Sgad jobcount = 0; 16331492Swollman init(pp); /* set up capabilities */ 16431492Swollman (void) write(1, "", 1); /* ack that daemon is started */ 1651553Srgrimes (void) close(2); /* set up log file */ 16631492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 16797792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 16897792Sgad pp->log_file); 1691553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1701553Srgrimes } 1711553Srgrimes setgid(getegid()); 17297793Sgad printpid = getpid(); /* for use with lprm */ 17397793Sgad setpgrp(0, printpid); 17479735Sgad 17579735Sgad /* 17679735Sgad * At initial lpd startup, printjob may be called with various 17779735Sgad * signal handlers in effect. After that initial startup, any 17879735Sgad * calls to printjob will have a *different* set of signal-handlers 17979735Sgad * in effect. Make sure all handlers are the ones we want. 18079735Sgad */ 18179735Sgad signal(SIGCHLD, SIG_DFL); 1821553Srgrimes signal(SIGHUP, abortpr); 1831553Srgrimes signal(SIGINT, abortpr); 1841553Srgrimes signal(SIGQUIT, abortpr); 1851553Srgrimes signal(SIGTERM, abortpr); 1861553Srgrimes 1871553Srgrimes /* 1881553Srgrimes * uses short form file names 1891553Srgrimes */ 19031492Swollman if (chdir(pp->spool_dir) < 0) { 19197792Sgad syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, 19297792Sgad pp->spool_dir); 1931553Srgrimes exit(1); 1941553Srgrimes } 19531492Swollman if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS)) 1961553Srgrimes exit(0); /* printing disabled */ 19731492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 19831492Swollman LOCK_FILE_MODE); 1991553Srgrimes if (lfd < 0) { 20031492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 20131492Swollman exit(0); 20297792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 20397792Sgad pp->lock_file); 2041553Srgrimes exit(1); 2051553Srgrimes } 20631492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 20731492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 20897792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 20997792Sgad pp->lock_file); 2101553Srgrimes exit(1); 2111553Srgrimes } 2121553Srgrimes ftruncate(lfd, 0); 2131553Srgrimes /* 2141553Srgrimes * write process id for others to know 2151553Srgrimes */ 21697793Sgad sprintf(line, "%u\n", printpid); 2171553Srgrimes pidoff = i = strlen(line); 2181553Srgrimes if (write(lfd, line, i) != i) { 21997792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 22097792Sgad pp->lock_file); 2211553Srgrimes exit(1); 2221553Srgrimes } 2231553Srgrimes /* 2241553Srgrimes * search the spool directory for work and sort by queue order. 2251553Srgrimes */ 22631492Swollman if ((nitems = getq(pp, &queue)) < 0) { 22731492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 22897792Sgad pp->spool_dir); 2291553Srgrimes exit(1); 2301553Srgrimes } 2311553Srgrimes if (nitems == 0) /* no work to do */ 2321553Srgrimes exit(0); 23331492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 23431492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 23597792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 23697792Sgad pp->lock_file); 2371553Srgrimes } 23868664Sgad 23968664Sgad /* create a file which will be used to hold stderr from filters */ 24068664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 24168664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 24297792Sgad tempstderr); 24368732Sgad exit(1); 24468664Sgad } 24568664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 24668664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 24797792Sgad tempstderr); 24868732Sgad exit(1); 24968664Sgad } 25068664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 25168664Sgad close(tempfd); 25268664Sgad 25331492Swollman openpr(pp); /* open printer or remote */ 2541553Srgrimesagain: 2551553Srgrimes /* 2561553Srgrimes * we found something to do now do it -- 2571553Srgrimes * write the name of the current control file into the lock file 2581553Srgrimes * so the spool queue program can tell what we're working on 2591553Srgrimes */ 2601553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2611553Srgrimes q = *qp++; 26268401Sgad if (stat(q->job_cfname, &stb) < 0) 2631553Srgrimes continue; 26415648Sjoerg errcnt = 0; 2651553Srgrimes restart: 26615648Sjoerg (void) lseek(lfd, pidoff, 0); 26768401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2681553Srgrimes i = strlen(line); 2691553Srgrimes if (write(lfd, line, i) != i) 27097792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 27197792Sgad pp->lock_file); 27231492Swollman if (!pp->remote) 27368401Sgad i = printit(pp, q->job_cfname); 2741553Srgrimes else 27568401Sgad i = sendit(pp, q->job_cfname); 2761553Srgrimes /* 2771553Srgrimes * Check to see if we are supposed to stop printing or 2781553Srgrimes * if we are to rebuild the queue. 2791553Srgrimes */ 2801553Srgrimes if (fstat(lfd, &stb) == 0) { 2811553Srgrimes /* stop printing before starting next job? */ 28231492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2831553Srgrimes goto done; 2841553Srgrimes /* rebuild queue (after lpc topq) */ 28531492Swollman if (stb.st_mode & LFM_RESET_QUE) { 28631492Swollman for (free(q); nitems--; free(q)) 2871553Srgrimes q = *qp++; 28831492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 28931492Swollman < 0) 29097792Sgad syslog(LOG_WARNING, 29197792Sgad "%s: fchmod(%s): %m", 29297792Sgad pp->printer, pp->lock_file); 2931553Srgrimes break; 2941553Srgrimes } 2951553Srgrimes } 29668733Sgad if (i == OK) /* all files of this job printed */ 29768733Sgad jobcount++; 29815648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 29915648Sjoerg /* try reprinting the job */ 30031492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 30197793Sgad if (of_pid > 0) { 30297793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3031553Srgrimes (void) close(ofd); 30497793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3051553Srgrimes ; 30679735Sgad if (i < 0) 30779735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 30897793Sgad pp->printer, of_pid); 30997793Sgad of_pid = 0; 3101553Srgrimes } 3111553Srgrimes (void) close(pfd); /* close printer */ 3121553Srgrimes if (ftruncate(lfd, pidoff) < 0) 31397792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 31497792Sgad pp->printer, pp->lock_file); 31531492Swollman openpr(pp); /* try to reopen printer */ 3161553Srgrimes goto restart; 31715648Sjoerg } else { 31831492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 31997792Sgad pp->printer, 32097792Sgad pp->remote ? "sent to remote host" : "printed", 32197792Sgad q->job_cfname); 32215648Sjoerg if (i == REPRINT) { 32327748Simp /* ensure we don't attempt this job again */ 32468401Sgad (void) unlink(q->job_cfname); 32568401Sgad q->job_cfname[0] = 'd'; 32668401Sgad (void) unlink(q->job_cfname); 32715648Sjoerg if (logname[0]) 32831492Swollman sendmail(pp, logname, FATALERR); 32915648Sjoerg } 3301553Srgrimes } 3311553Srgrimes } 33231492Swollman free(queue); 3331553Srgrimes /* 3341553Srgrimes * search the spool directory for more work. 3351553Srgrimes */ 33631492Swollman if ((nitems = getq(pp, &queue)) < 0) { 33731492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 33897792Sgad pp->spool_dir); 3391553Srgrimes exit(1); 3401553Srgrimes } 3411553Srgrimes if (nitems == 0) { /* no more work to do */ 3421553Srgrimes done: 34368733Sgad if (jobcount > 0) { /* jobs actually printed */ 34431492Swollman if (!pp->no_formfeed && !pp->tof) 34531492Swollman (void) write(ofd, pp->form_feed, 34631492Swollman strlen(pp->form_feed)); 34731492Swollman if (pp->trailer != NULL) /* output trailer */ 34831492Swollman (void) write(ofd, pp->trailer, 34931492Swollman strlen(pp->trailer)); 3501553Srgrimes } 35119202Simp (void) close(ofd); 35219202Simp (void) wait(NULL); 35368664Sgad (void) unlink(tempstderr); 3541553Srgrimes exit(0); 3551553Srgrimes } 3561553Srgrimes goto again; 3571553Srgrimes} 3581553Srgrimes 3591553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3601553Srgrimes 3611553Srgrimeschar ifonts[4][40] = { 3621553Srgrimes _PATH_VFONTR, 3631553Srgrimes _PATH_VFONTI, 3641553Srgrimes _PATH_VFONTB, 3651553Srgrimes _PATH_VFONTS, 3661553Srgrimes}; 3671553Srgrimes 3681553Srgrimes/* 3691553Srgrimes * The remaining part is the reading of the control file (cf) 3701553Srgrimes * and performing the various actions. 3711553Srgrimes */ 3721553Srgrimesstatic int 37378146Sgadprintit(struct printer *pp, char *file) 3741553Srgrimes{ 3751553Srgrimes register int i; 37668734Sgad char *cp; 37768734Sgad int bombed, didignorehdr; 3781553Srgrimes 37968734Sgad bombed = OK; 38068734Sgad didignorehdr = 0; 3811553Srgrimes /* 3821553Srgrimes * open control file; ignore if no longer there. 3831553Srgrimes */ 3841553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 38597792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 38697791Sgad return (OK); 3871553Srgrimes } 3881553Srgrimes /* 3891553Srgrimes * Reset troff fonts. 3901553Srgrimes */ 3911553Srgrimes for (i = 0; i < 4; i++) 3921553Srgrimes strcpy(fonts[i], ifonts[i]); 39331492Swollman sprintf(&width[2], "%ld", pp->page_width); 3941553Srgrimes strcpy(indent+2, "0"); 3951553Srgrimes 39668253Sgad /* initialize job-specific count of datafiles processed */ 39768253Sgad job_dfcnt = 0; 39868253Sgad 3991553Srgrimes /* 4001553Srgrimes * read the control file for work to do 4011553Srgrimes * 4021553Srgrimes * file format -- first character in the line is a command 4031553Srgrimes * rest of the line is the argument. 4041553Srgrimes * valid commands are: 4051553Srgrimes * 4061553Srgrimes * S -- "stat info" for symbolic link protection 4071553Srgrimes * J -- "job name" on banner page 4081553Srgrimes * C -- "class name" on banner page 4091553Srgrimes * L -- "literal" user's name to print on banner 4101553Srgrimes * T -- "title" for pr 4111553Srgrimes * H -- "host name" of machine where lpr was done 4121553Srgrimes * P -- "person" user's login name 4131553Srgrimes * I -- "indent" amount to indent output 41415648Sjoerg * R -- laser dpi "resolution" 4151553Srgrimes * f -- "file name" name of text file to print 4161553Srgrimes * l -- "file name" text file with control chars 41783684Sgad * o -- "file name" postscript file, according to 41883684Sgad * the RFC. Here it is treated like an 'f'. 4191553Srgrimes * p -- "file name" text file to print with pr(1) 4201553Srgrimes * t -- "file name" troff(1) file to print 4211553Srgrimes * n -- "file name" ditroff(1) file to print 4221553Srgrimes * d -- "file name" dvi file to print 4231553Srgrimes * g -- "file name" plot(1G) file to print 4241553Srgrimes * v -- "file name" plain raster file to print 4251553Srgrimes * c -- "file name" cifplot file to print 4261553Srgrimes * 1 -- "R font file" for troff 4271553Srgrimes * 2 -- "I font file" for troff 4281553Srgrimes * 3 -- "B font file" for troff 4291553Srgrimes * 4 -- "S font file" for troff 4301553Srgrimes * N -- "name" of file (used by lpq) 4311553Srgrimes * U -- "unlink" name of file to remove 4321553Srgrimes * (after we print it. (Pass 2 only)). 4331553Srgrimes * M -- "mail" to user when done printing 43453956Sache * Z -- "locale" for pr 4351553Srgrimes * 4361553Srgrimes * getline reads a line and expands tabs to blanks 4371553Srgrimes */ 4381553Srgrimes 4391553Srgrimes /* pass 1 */ 4401553Srgrimes 4411553Srgrimes while (getline(cfp)) 4421553Srgrimes switch (line[0]) { 4431553Srgrimes case 'H': 44478300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 44527748Simp if (class[0] == '\0') { 44680133Sgad strlcpy(class, line+1, sizeof(class)); 44727748Simp } 4481553Srgrimes continue; 4491553Srgrimes 4501553Srgrimes case 'P': 45180133Sgad strlcpy(logname, line + 1, sizeof(logname)); 45231492Swollman if (pp->restricted) { /* restricted */ 4531553Srgrimes if (getpwnam(logname) == NULL) { 4541553Srgrimes bombed = NOACCT; 45531492Swollman sendmail(pp, line+1, bombed); 4561553Srgrimes goto pass2; 4571553Srgrimes } 4581553Srgrimes } 4591553Srgrimes continue; 4601553Srgrimes 4611553Srgrimes case 'S': 4621553Srgrimes cp = line+1; 4631553Srgrimes i = 0; 4641553Srgrimes while (*cp >= '0' && *cp <= '9') 4651553Srgrimes i = i * 10 + (*cp++ - '0'); 4661553Srgrimes fdev = i; 4671553Srgrimes cp++; 4681553Srgrimes i = 0; 4691553Srgrimes while (*cp >= '0' && *cp <= '9') 4701553Srgrimes i = i * 10 + (*cp++ - '0'); 4711553Srgrimes fino = i; 4721553Srgrimes continue; 4731553Srgrimes 4741553Srgrimes case 'J': 47527748Simp if (line[1] != '\0') { 47680133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 47727748Simp } else 4781553Srgrimes strcpy(jobname, " "); 4791553Srgrimes continue; 4801553Srgrimes 4811553Srgrimes case 'C': 4821553Srgrimes if (line[1] != '\0') 48380133Sgad strlcpy(class, line + 1, sizeof(class)); 48480133Sgad else if (class[0] == '\0') { 48580133Sgad /* XXX - why call gethostname instead of 48680133Sgad * just strlcpy'ing local_host? */ 4871553Srgrimes gethostname(class, sizeof(class)); 48880133Sgad class[sizeof(class) - 1] = '\0'; 48980133Sgad } 4901553Srgrimes continue; 4911553Srgrimes 4921553Srgrimes case 'T': /* header title for pr */ 49380133Sgad strlcpy(title, line + 1, sizeof(title)); 4941553Srgrimes continue; 4951553Srgrimes 4961553Srgrimes case 'L': /* identification line */ 49731492Swollman if (!pp->no_header && !pp->header_last) 49831492Swollman banner(pp, line+1, jobname); 4991553Srgrimes continue; 5001553Srgrimes 5011553Srgrimes case '1': /* troff fonts */ 5021553Srgrimes case '2': 5031553Srgrimes case '3': 5041553Srgrimes case '4': 50527748Simp if (line[1] != '\0') { 50680133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 50780133Sgad (size_t)50); 50827748Simp } 5091553Srgrimes continue; 5101553Srgrimes 5111553Srgrimes case 'W': /* page width */ 51280133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5131553Srgrimes continue; 5141553Srgrimes 5151553Srgrimes case 'I': /* indent amount */ 51680133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5171553Srgrimes continue; 5181553Srgrimes 51953956Sache case 'Z': /* locale for pr */ 52080133Sgad strlcpy(locale, line + 1, sizeof(locale)); 52153956Sache continue; 52253956Sache 5231553Srgrimes default: /* some file to print */ 52468467Sgad /* only lowercase cmd-codes include a file-to-print */ 52568467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 52668467Sgad /* ignore any other lines */ 52768467Sgad if (lflag <= 1) 52868467Sgad continue; 52968467Sgad if (!didignorehdr) { 53068467Sgad syslog(LOG_INFO, "%s: in %s :", 53197792Sgad pp->printer, file); 53268467Sgad didignorehdr = 1; 53368467Sgad } 53468467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 53597792Sgad pp->printer, line[0], &line[1]); 53668467Sgad continue; 53768467Sgad } 53868467Sgad i = print(pp, line[0], line+1); 53968467Sgad switch (i) { 5401553Srgrimes case ERROR: 5411553Srgrimes if (bombed == OK) 5421553Srgrimes bombed = FATALERR; 5431553Srgrimes break; 5441553Srgrimes case REPRINT: 5451553Srgrimes (void) fclose(cfp); 54697791Sgad return (REPRINT); 5471553Srgrimes case FILTERERR: 5481553Srgrimes case ACCESS: 5491553Srgrimes bombed = i; 55031492Swollman sendmail(pp, logname, bombed); 5511553Srgrimes } 5521553Srgrimes title[0] = '\0'; 5531553Srgrimes continue; 5541553Srgrimes 5551553Srgrimes case 'N': 5561553Srgrimes case 'U': 5571553Srgrimes case 'M': 55815648Sjoerg case 'R': 5591553Srgrimes continue; 5601553Srgrimes } 5611553Srgrimes 5621553Srgrimes /* pass 2 */ 5631553Srgrimes 5641553Srgrimespass2: 5651553Srgrimes fseek(cfp, 0L, 0); 5661553Srgrimes while (getline(cfp)) 5671553Srgrimes switch (line[0]) { 5681553Srgrimes case 'L': /* identification line */ 56931492Swollman if (!pp->no_header && pp->header_last) 57031492Swollman banner(pp, line+1, jobname); 5711553Srgrimes continue; 5721553Srgrimes 5731553Srgrimes case 'M': 5741553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 57531492Swollman sendmail(pp, line+1, bombed); 5761553Srgrimes continue; 5771553Srgrimes 5781553Srgrimes case 'U': 57927748Simp if (strchr(line+1, '/')) 58027748Simp continue; 5811553Srgrimes (void) unlink(line+1); 5821553Srgrimes } 5831553Srgrimes /* 5841553Srgrimes * clean-up in case another control file exists 5851553Srgrimes */ 5861553Srgrimes (void) fclose(cfp); 5871553Srgrimes (void) unlink(file); 58897791Sgad return (bombed == OK ? OK : ERROR); 5891553Srgrimes} 5901553Srgrimes 5911553Srgrimes/* 5921553Srgrimes * Print a file. 5931553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 5941553Srgrimes * Return -1 if a non-recoverable error occured, 5951553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 5961553Srgrimes * 1 if we should try to reprint this job and 5971553Srgrimes * 0 if all is well. 5981553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 5991553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6001553Srgrimes */ 6011553Srgrimesstatic int 60278146Sgadprint(struct printer *pp, int format, char *file) 6031553Srgrimes{ 60453956Sache register int n, i; 6051553Srgrimes register char *prog; 60631492Swollman int fi, fo; 6071553Srgrimes FILE *fp; 6081553Srgrimes char *av[15], buf[BUFSIZ]; 60997793Sgad pid_t wpid; 61097793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6111553Srgrimes struct stat stb; 6121553Srgrimes 61368467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 61468467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 61597792Sgad pp->printer, file, format); 61697791Sgad return (ERROR); 61768467Sgad } 6181553Srgrimes /* 6191553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6201553Srgrimes * still point to the same file or someone is trying to print 6211553Srgrimes * something he shouldn't. 6221553Srgrimes */ 6231553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6241553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 62597791Sgad return (ACCESS); 62668253Sgad 62768253Sgad job_dfcnt++; /* increment datafile counter for this job */ 62868734Sgad stopped = 0; /* output filter is not stopped */ 62968253Sgad 63068253Sgad /* everything seems OK, start it up */ 63131492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 63231492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 63331492Swollman pp->tof = 1; 6341553Srgrimes } 63531492Swollman if (pp->filters[LPF_INPUT] == NULL 63683684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 63731492Swollman pp->tof = 0; 6381553Srgrimes while ((n = read(fi, buf, BUFSIZ)) > 0) 6391553Srgrimes if (write(ofd, buf, n) != n) { 6401553Srgrimes (void) close(fi); 64197791Sgad return (REPRINT); 6421553Srgrimes } 6431553Srgrimes (void) close(fi); 64497791Sgad return (OK); 6451553Srgrimes } 6461553Srgrimes switch (format) { 6471553Srgrimes case 'p': /* print file using 'pr' */ 64831492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6491553Srgrimes prog = _PATH_PR; 65053956Sache i = 0; 65153956Sache av[i++] = "pr"; 65253956Sache av[i++] = width; 65353956Sache av[i++] = length; 65453956Sache av[i++] = "-h"; 65553956Sache av[i++] = *title ? title : " "; 65653956Sache av[i++] = "-L"; 65753956Sache av[i++] = *locale ? locale : "C"; 65853956Sache av[i++] = "-F"; 65953956Sache av[i] = 0; 6601553Srgrimes fo = ofd; 6611553Srgrimes goto start; 6621553Srgrimes } 6631553Srgrimes pipe(p); 66431492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 6651553Srgrimes dup2(fi, 0); /* file is stdin */ 6661553Srgrimes dup2(p[1], 1); /* pipe is stdout */ 6678094Sjkh closelog(); 66831492Swollman closeallfds(3); 6691553Srgrimes execl(_PATH_PR, "pr", width, length, 67053956Sache "-h", *title ? title : " ", 67153956Sache "-L", *locale ? locale : "C", 67279452Sbrian "-F", (char *)0); 6731553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6741553Srgrimes exit(2); 6751553Srgrimes } 6761553Srgrimes (void) close(p[1]); /* close output side */ 6771553Srgrimes (void) close(fi); 6781553Srgrimes if (prchild < 0) { 6791553Srgrimes prchild = 0; 6801553Srgrimes (void) close(p[0]); 68197791Sgad return (ERROR); 6821553Srgrimes } 6831553Srgrimes fi = p[0]; /* use pipe for input */ 6841553Srgrimes case 'f': /* print plain text file */ 68531492Swollman prog = pp->filters[LPF_INPUT]; 6861553Srgrimes av[1] = width; 6871553Srgrimes av[2] = length; 6881553Srgrimes av[3] = indent; 6891553Srgrimes n = 4; 6901553Srgrimes break; 69186935Sgad case 'o': /* print postscript file */ 69286935Sgad /* 69386935Sgad * Treat this as a "plain file with control characters", and 69486935Sgad * assume the standard LPF_INPUT filter will recognize that 69586935Sgad * the data is postscript and know what to do with it. These 69686935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 69786935Sgad * (later versions of MacOS 10 will explicitly use 'l') 69886935Sgad * A postscript file can contain binary data, which is why 'l' 69986935Sgad * is somewhat more appropriate than 'f'. 70086935Sgad */ 70186935Sgad /* FALLTHROUGH */ 7021553Srgrimes case 'l': /* like 'f' but pass control characters */ 70331492Swollman prog = pp->filters[LPF_INPUT]; 7041553Srgrimes av[1] = "-c"; 7051553Srgrimes av[2] = width; 7061553Srgrimes av[3] = length; 7071553Srgrimes av[4] = indent; 7081553Srgrimes n = 5; 7091553Srgrimes break; 7101553Srgrimes case 'r': /* print a fortran text file */ 71131492Swollman prog = pp->filters[LPF_FORTRAN]; 7121553Srgrimes av[1] = width; 7131553Srgrimes av[2] = length; 7141553Srgrimes n = 3; 7151553Srgrimes break; 7161553Srgrimes case 't': /* print troff output */ 7171553Srgrimes case 'n': /* print ditroff output */ 7181553Srgrimes case 'd': /* print tex output */ 7191553Srgrimes (void) unlink(".railmag"); 7201553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 72131492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 72297792Sgad pp->printer); 7231553Srgrimes (void) unlink(".railmag"); 7241553Srgrimes } else { 7251553Srgrimes for (n = 0; n < 4; n++) { 7261553Srgrimes if (fonts[n][0] != '/') 7271553Srgrimes (void) write(fo, _PATH_VFONT, 7281553Srgrimes sizeof(_PATH_VFONT) - 1); 7291553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7301553Srgrimes (void) write(fo, "\n", 1); 7311553Srgrimes } 7321553Srgrimes (void) close(fo); 7331553Srgrimes } 73431492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 73531492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 73631492Swollman : pp->filters[LPF_DVI]); 7371553Srgrimes av[1] = pxwidth; 7381553Srgrimes av[2] = pxlength; 7391553Srgrimes n = 3; 7401553Srgrimes break; 7411553Srgrimes case 'c': /* print cifplot output */ 74231492Swollman prog = pp->filters[LPF_CIFPLOT]; 7431553Srgrimes av[1] = pxwidth; 7441553Srgrimes av[2] = pxlength; 7451553Srgrimes n = 3; 7461553Srgrimes break; 7471553Srgrimes case 'g': /* print plot(1G) output */ 74831492Swollman prog = pp->filters[LPF_GRAPH]; 7491553Srgrimes av[1] = pxwidth; 7501553Srgrimes av[2] = pxlength; 7511553Srgrimes n = 3; 7521553Srgrimes break; 7531553Srgrimes case 'v': /* print raster output */ 75431492Swollman prog = pp->filters[LPF_RASTER]; 7551553Srgrimes av[1] = pxwidth; 7561553Srgrimes av[2] = pxlength; 7571553Srgrimes n = 3; 7581553Srgrimes break; 7591553Srgrimes default: 7601553Srgrimes (void) close(fi); 7611553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 76297792Sgad pp->printer, format); 76397791Sgad return (ERROR); 7641553Srgrimes } 76515648Sjoerg if (prog == NULL) { 76615648Sjoerg (void) close(fi); 76715648Sjoerg syslog(LOG_ERR, 76815648Sjoerg "%s: no filter found in printcap for format character '%c'", 76931492Swollman pp->printer, format); 77097791Sgad return (ERROR); 77115648Sjoerg } 77227635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7731553Srgrimes av[0]++; 7741553Srgrimes else 7751553Srgrimes av[0] = prog; 7761553Srgrimes av[n++] = "-n"; 7771553Srgrimes av[n++] = logname; 7781553Srgrimes av[n++] = "-h"; 77978300Sgad av[n++] = origin_host; 78031492Swollman av[n++] = pp->acct_file; 7811553Srgrimes av[n] = 0; 7821553Srgrimes fo = pfd; 78397793Sgad if (of_pid > 0) { /* stop output filter */ 7841553Srgrimes write(ofd, "\031\1", 2); 78597793Sgad while ((wpid = 78697793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 7871553Srgrimes ; 78897793Sgad if (wpid < 0) 78997792Sgad syslog(LOG_WARNING, 79097792Sgad "%s: after stopping 'of', wait3() returned: %m", 79179735Sgad pp->printer); 79297781Sgad else if (!WIFSTOPPED(wstatus)) { 7931553Srgrimes (void) close(fi); 79497781Sgad syslog(LOG_WARNING, "%s: output filter died " 79597781Sgad "(pid=%d retcode=%d termsig=%d)", 79697793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 79797781Sgad WTERMSIG(wstatus)); 79897791Sgad return (REPRINT); 7991553Srgrimes } 8001553Srgrimes stopped++; 8011553Srgrimes } 8021553Srgrimesstart: 80331492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 8041553Srgrimes dup2(fi, 0); 8051553Srgrimes dup2(fo, 1); 80668664Sgad /* setup stderr for the filter (child process) 80768664Sgad * so it goes to our temporary errors file */ 80868664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8091553Srgrimes if (n >= 0) 8101553Srgrimes dup2(n, 2); 8118094Sjkh closelog(); 81231492Swollman closeallfds(3); 8131553Srgrimes execv(prog, av); 81495067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 81595067Sgad prog); 8161553Srgrimes exit(2); 8171553Srgrimes } 8181553Srgrimes (void) close(fi); 81997789Sgad wstatus_set = 0; 8201553Srgrimes if (child < 0) 82197781Sgad retcode = 100; 82279735Sgad else { 82397793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8241553Srgrimes ; 82597793Sgad if (wpid < 0) { 82697781Sgad retcode = 100; 82797792Sgad syslog(LOG_WARNING, 82897792Sgad "%s: after execv(%s), wait() returned: %m", 82979735Sgad pp->printer, prog); 83097789Sgad } else { 83197789Sgad wstatus_set = 1; 83297781Sgad retcode = WEXITSTATUS(wstatus); 83397789Sgad } 83479735Sgad } 8351553Srgrimes child = 0; 8361553Srgrimes prchild = 0; 8371553Srgrimes if (stopped) { /* restart output filter */ 83897793Sgad if (kill(of_pid, SIGCONT) < 0) { 8391553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8401553Srgrimes exit(1); 8411553Srgrimes } 8421553Srgrimes } 84331492Swollman pp->tof = 0; 8441553Srgrimes 84568664Sgad /* Copy the filter's output to "lf" logfile */ 84668664Sgad if ((fp = fopen(tempstderr, "r"))) { 8471553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8481553Srgrimes fputs(buf, stderr); 8491553Srgrimes fclose(fp); 8501553Srgrimes } 8511553Srgrimes 85297789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 85315648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 85497781Sgad pp->printer, format, WTERMSIG(wstatus)); 85597791Sgad return (ERROR); 8561553Srgrimes } 85797781Sgad switch (retcode) { 8581553Srgrimes case 0: 85931492Swollman pp->tof = 1; 86097791Sgad return (OK); 8611553Srgrimes case 1: 86297791Sgad return (REPRINT); 86315648Sjoerg case 2: 86497791Sgad return (ERROR); 8651553Srgrimes default: 86615648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 86797792Sgad pp->printer, format, retcode); 86897791Sgad return (FILTERERR); 8691553Srgrimes } 8701553Srgrimes} 8711553Srgrimes 8721553Srgrimes/* 8731553Srgrimes * Send the daemon control file (cf) and any data files. 8741553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 8751553Srgrimes * 0 if all is well. 8761553Srgrimes */ 8771553Srgrimesstatic int 87878146Sgadsendit(struct printer *pp, char *file) 8791553Srgrimes{ 88095293Sgad int dfcopies, err, i; 8811553Srgrimes char *cp, last[BUFSIZ]; 8821553Srgrimes 8831553Srgrimes /* 8841553Srgrimes * open control file 8851553Srgrimes */ 8861553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 88797791Sgad return (OK); 88868253Sgad 88968253Sgad /* initialize job-specific count of datafiles processed */ 89068253Sgad job_dfcnt = 0; 89168253Sgad 8921553Srgrimes /* 8931553Srgrimes * read the control file for work to do 8941553Srgrimes * 8951553Srgrimes * file format -- first character in the line is a command 8961553Srgrimes * rest of the line is the argument. 8971553Srgrimes * commands of interest are: 8981553Srgrimes * 8991553Srgrimes * a-z -- "file name" name of file to print 9001553Srgrimes * U -- "unlink" name of file to remove 9011553Srgrimes * (after we print it. (Pass 2 only)). 9021553Srgrimes */ 9031553Srgrimes 9041553Srgrimes /* 9051553Srgrimes * pass 1 9061553Srgrimes */ 90795293Sgad err = OK; 9081553Srgrimes while (getline(cfp)) { 9091553Srgrimes again: 9101553Srgrimes if (line[0] == 'S') { 9111553Srgrimes cp = line+1; 9121553Srgrimes i = 0; 9131553Srgrimes while (*cp >= '0' && *cp <= '9') 9141553Srgrimes i = i * 10 + (*cp++ - '0'); 9151553Srgrimes fdev = i; 9161553Srgrimes cp++; 9171553Srgrimes i = 0; 9181553Srgrimes while (*cp >= '0' && *cp <= '9') 9191553Srgrimes i = i * 10 + (*cp++ - '0'); 9201553Srgrimes fino = i; 92124831Sbrian } else if (line[0] == 'H') { 92278300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 92368343Sgad if (class[0] == '\0') { 92480133Sgad strlcpy(class, line + 1, sizeof(class)); 92568343Sgad } 92624831Sbrian } else if (line[0] == 'P') { 92780133Sgad strlcpy(logname, line + 1, sizeof(logname)); 92831492Swollman if (pp->restricted) { /* restricted */ 92924831Sbrian if (getpwnam(logname) == NULL) { 93031492Swollman sendmail(pp, line+1, NOACCT); 93124831Sbrian err = ERROR; 93224831Sbrian break; 93324831Sbrian } 93424831Sbrian } 93524831Sbrian } else if (line[0] == 'I') { 93680133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 93724831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 93895293Sgad dfcopies = 1; 9391553Srgrimes strcpy(last, line); 94095293Sgad while ((i = getline(cfp)) != 0) { 94195293Sgad if (strcmp(last, line) != 0) 9421553Srgrimes break; 94395293Sgad dfcopies++; 94495293Sgad } 94595293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9461553Srgrimes case OK: 9471553Srgrimes if (i) 9481553Srgrimes goto again; 9491553Srgrimes break; 9501553Srgrimes case REPRINT: 9511553Srgrimes (void) fclose(cfp); 95297791Sgad return (REPRINT); 9531553Srgrimes case ACCESS: 95431492Swollman sendmail(pp, logname, ACCESS); 9551553Srgrimes case ERROR: 9561553Srgrimes err = ERROR; 9571553Srgrimes } 9581553Srgrimes break; 9591553Srgrimes } 9601553Srgrimes } 96195293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9621553Srgrimes (void) fclose(cfp); 96397791Sgad return (REPRINT); 9641553Srgrimes } 9651553Srgrimes /* 9661553Srgrimes * pass 2 9671553Srgrimes */ 9681553Srgrimes fseek(cfp, 0L, 0); 9691553Srgrimes while (getline(cfp)) 97027748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9711553Srgrimes (void) unlink(line+1); 9721553Srgrimes /* 9731553Srgrimes * clean-up in case another control file exists 9741553Srgrimes */ 9751553Srgrimes (void) fclose(cfp); 9761553Srgrimes (void) unlink(file); 97797791Sgad return (err); 9781553Srgrimes} 9791553Srgrimes 9801553Srgrimes/* 9811553Srgrimes * Send a data file to the remote machine and spool it. 9821553Srgrimes * Return positive if we should try resending. 9831553Srgrimes */ 9841553Srgrimesstatic int 98595293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 9861553Srgrimes{ 98794036Sgad int i, amt; 9881553Srgrimes struct stat stb; 98994032Sgad char *av[15], *filtcmd; 99094032Sgad char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 99195293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 9921553Srgrimes 99374124Sgad statrc = lstat(file, &stb); 99474124Sgad if (statrc < 0) { 99574124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 99674124Sgad pp->printer, file); 99794036Sgad return (ERROR); 99874124Sgad } 99994036Sgad sfd = open(file, O_RDONLY); 100094036Sgad if (sfd < 0) { 100174124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 100274124Sgad pp->printer, file); 100394036Sgad return (ERROR); 100474124Sgad } 10051553Srgrimes /* 10061553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10071553Srgrimes * still point to the same file or someone is trying to print something 10081553Srgrimes * he shouldn't. 10091553Srgrimes */ 101094036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 101194036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 101294036Sgad close(sfd); 101394036Sgad return (ACCESS); 101494036Sgad } 101524831Sbrian 101694032Sgad /* Everything seems OK for reading the file, now to send it */ 101794032Sgad filtcmd = NULL; 101824831Sbrian sizerr = 0; 101994032Sgad tfd = -1; 102024831Sbrian if (type == '\3') { 102194032Sgad /* 102294032Sgad * Type == 3 means this is a datafile, not a control file. 102394032Sgad * Increment the counter of data-files in this job, and 102494032Sgad * then check for input or output filters (which are only 102594032Sgad * applied to datafiles, not control files). 102694032Sgad */ 102794032Sgad job_dfcnt++; 102894032Sgad 102994032Sgad /* 103094032Sgad * Note that here we are filtering datafiles, one at a time, 103194032Sgad * as they are sent to the remote machine. Here, the *only* 103294032Sgad * difference between an input filter (`if=') and an output 103394032Sgad * filter (`of=') is the argument list that the filter is 103494032Sgad * started up with. Here, the output filter is executed 103594032Sgad * for each individual file as it is sent. This is not the 103694032Sgad * same as local print queues, where the output filter is 103794032Sgad * started up once, and then all jobs are passed thru that 103894032Sgad * single invocation of the output filter. 103994032Sgad * 104094032Sgad * Also note that a queue for a remote-machine can have an 104194032Sgad * input filter or an output filter, but not both. 104294032Sgad */ 104331492Swollman if (pp->filters[LPF_INPUT]) { 104494032Sgad filtcmd = pp->filters[LPF_INPUT]; 104594032Sgad av[0] = filtcmd; 104694032Sgad narg = 0; 104794032Sgad strcpy(opt_c, "-c"); 104894032Sgad strcpy(opt_h, "-h"); 104994032Sgad strcpy(opt_n, "-n"); 105024831Sbrian if (format == 'l') 105194032Sgad av[++narg] = opt_c; 105294032Sgad av[++narg] = width; 105394032Sgad av[++narg] = length; 105494032Sgad av[++narg] = indent; 105594032Sgad av[++narg] = opt_n; 105694032Sgad av[++narg] = logname; 105794032Sgad av[++narg] = opt_h; 105894032Sgad av[++narg] = origin_host; 105994032Sgad av[++narg] = pp->acct_file; 106094032Sgad av[++narg] = NULL; 106194032Sgad } else if (pp->filters[LPF_OUTPUT]) { 106294032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 106394032Sgad av[0] = filtcmd; 106494032Sgad narg = 0; 106594032Sgad av[++narg] = width; 106694032Sgad av[++narg] = length; 106794032Sgad av[++narg] = NULL; 106824831Sbrian } 106924831Sbrian } 107094032Sgad if (filtcmd) { 107194032Sgad /* 107294032Sgad * If there is an input or output filter, we have to run 107394032Sgad * the datafile thru that filter and store the result as 107494032Sgad * a temporary spool file, because the protocol requires 107594032Sgad * that we send the remote host the file-size before we 107694032Sgad * start to send any of the data. 107794032Sgad */ 107894032Sgad strcpy(tfile, TFILENAME); 107994032Sgad tfd = mkstemp(tfile); 108094032Sgad if (tfd == -1) { 108194032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 108294032Sgad TFILENAME); 108394036Sgad sfres = ERROR; 108494036Sgad goto return_sfres; 108594032Sgad } 108694036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 108724831Sbrian 108894032Sgad /* process the return-code from the filter */ 108994032Sgad switch (filtstat) { 109094032Sgad case 0: 109194032Sgad break; 109294032Sgad case 1: 109394036Sgad sfres = REPRINT; 109494036Sgad goto return_sfres; 109594032Sgad case 2: 109694036Sgad sfres = ERROR; 109794036Sgad goto return_sfres; 109894032Sgad default: 109994032Sgad syslog(LOG_WARNING, 110094032Sgad "%s: filter '%c' exited (retcode=%d)", 110194032Sgad pp->printer, format, filtstat); 110294036Sgad sfres = FILTERERR; 110394036Sgad goto return_sfres; 110494032Sgad } 110594032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 110694032Sgad if (statrc < 0) { 110794032Sgad syslog(LOG_ERR, 110894032Sgad "%s: error processing 'if', fstat(%s): %m", 110994032Sgad pp->printer, tfile); 111094036Sgad sfres = ERROR; 111194036Sgad goto return_sfres; 111294032Sgad } 111394036Sgad close(sfd); 111494036Sgad sfd = tfd; 111594036Sgad lseek(sfd, 0, SEEK_SET); 111694032Sgad } 111794032Sgad 111895293Sgad copycnt = 0; 111995293Sgadsendagain: 112095293Sgad copycnt++; 112195293Sgad 112295293Sgad if (copycnt < 2) 112395293Sgad (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 112495293Sgad else 112595293Sgad (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 112695293Sgad file, copycnt); 11271553Srgrimes amt = strlen(buf); 11281553Srgrimes for (i = 0; ; i++) { 11291553Srgrimes if (write(pfd, buf, amt) != amt || 113031492Swollman (resp = response(pp)) < 0 || resp == '\1') { 113194036Sgad sfres = REPRINT; 113294036Sgad goto return_sfres; 11331553Srgrimes } else if (resp == '\0') 11341553Srgrimes break; 11351553Srgrimes if (i == 0) 113631492Swollman pstatus(pp, 113731492Swollman "no space on remote; waiting for queue to drain"); 11381553Srgrimes if (i == 10) 11391553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 114097792Sgad pp->printer, pp->remote_host); 11411553Srgrimes sleep(5 * 60); 11421553Srgrimes } 11431553Srgrimes if (i) 114431492Swollman pstatus(pp, "sending to %s", pp->remote_host); 114595293Sgad /* 114695293Sgad * XXX - we should change trstat_init()/trstat_write() to include 114795293Sgad * the copycnt in the statistics record it may write. 114895293Sgad */ 114968253Sgad if (type == '\3') 115068253Sgad trstat_init(pp, file, job_dfcnt); 11511553Srgrimes for (i = 0; i < stb.st_size; i += BUFSIZ) { 11521553Srgrimes amt = BUFSIZ; 11531553Srgrimes if (i + amt > stb.st_size) 11541553Srgrimes amt = stb.st_size - i; 115594036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11561553Srgrimes sizerr = 1; 11571553Srgrimes if (write(pfd, buf, amt) != amt) { 115894036Sgad sfres = REPRINT; 115994036Sgad goto return_sfres; 11601553Srgrimes } 11611553Srgrimes } 11621553Srgrimes 11631553Srgrimes if (sizerr) { 116431492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11651553Srgrimes /* tell recvjob to ignore this file */ 11661553Srgrimes (void) write(pfd, "\1", 1); 116794036Sgad sfres = ERROR; 116894036Sgad goto return_sfres; 11691553Srgrimes } 117031492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 117194036Sgad sfres = REPRINT; 117294036Sgad goto return_sfres; 117324831Sbrian } 117495293Sgad if (type == '\3') { 117568253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 117695293Sgad pp->remote_host, origin_host); 117795293Sgad /* 117895293Sgad * Usually we only need to send one copy of a datafile, 117995293Sgad * because the control-file will simply print the same 118095293Sgad * file multiple times. However, some printers ignore 118195293Sgad * the control file, and simply print each data file as 118295293Sgad * it arrives. For such "remote hosts", we need to 118395293Sgad * transfer the same data file multiple times. Such a 118495293Sgad * a host is indicated by adding 'rc' to the printcap 118595293Sgad * entry. 118695293Sgad * XXX - Right now this ONLY works for remote hosts which 118795293Sgad * do ignore the name of the data file, because 118895293Sgad * this sends the file multiple times with slight 118995293Sgad * changes to the filename. To do this right would 119095293Sgad * require that we also rewrite the control file 119195293Sgad * to match those filenames. 119295293Sgad */ 119395293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 119495293Sgad lseek(sfd, 0, SEEK_SET); 119595293Sgad goto sendagain; 119695293Sgad } 119795293Sgad } 119894036Sgad sfres = OK; 119994036Sgad 120094036Sgadreturn_sfres: 120194036Sgad (void)close(sfd); 120294036Sgad if (tfd != -1) { 120394036Sgad /* 120494036Sgad * If tfd is set, then it is the same value as sfd, and 120594036Sgad * therefore it is already closed at this point. All 120694036Sgad * we need to do is remove the temporary file. 120794036Sgad */ 120894036Sgad tfd = -1; 120994036Sgad unlink(tfile); 121094036Sgad } 121194036Sgad return (sfres); 12121553Srgrimes} 12131553Srgrimes 12141553Srgrimes/* 121594032Sgad * This routine is called to execute one of the filters as was 121694036Sgad * specified in a printcap entry. While the child-process will read 121794036Sgad * all of 'infd', it is up to the caller to close that file descriptor 121894036Sgad * in the parent process. 121994032Sgad */ 122094032Sgadstatic int 122194032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 122294032Sgad{ 122397793Sgad pid_t fpid, wpid; 122497793Sgad int errfd, retcode, wstatus; 122594032Sgad FILE *errfp; 122694032Sgad char buf[BUFSIZ], *slash; 122794032Sgad 122894032Sgad fpid = dofork(pp, DORETURN); 122994032Sgad if (fpid != 0) { 123094032Sgad /* 123194032Sgad * This is the parent process, which just waits for the child 123294032Sgad * to complete and then returns the result. Note that it is 123394032Sgad * the child process which reads the input stream. 123494032Sgad */ 123594032Sgad if (fpid < 0) 123697781Sgad retcode = 100; 123794032Sgad else { 123897781Sgad while ((wpid = wait(&wstatus)) > 0 && 123994032Sgad wpid != fpid) 124094032Sgad ; 124194032Sgad if (wpid < 0) { 124297781Sgad retcode = 100; 124394032Sgad syslog(LOG_WARNING, 124494032Sgad "%s: after execv(%s), wait() returned: %m", 124594032Sgad pp->printer, f_cmd); 124697781Sgad } else 124797781Sgad retcode = WEXITSTATUS(wstatus); 124894032Sgad } 124994032Sgad 125094032Sgad /* 125194032Sgad * Copy everything the filter wrote to stderr from our 125294032Sgad * temporary errors file to the "lf=" logfile. 125394032Sgad */ 125494032Sgad errfp = fopen(tempstderr, "r"); 125594032Sgad if (errfp) { 125694032Sgad while (fgets(buf, sizeof(buf), errfp)) 125794032Sgad fputs(buf, stderr); 125894032Sgad fclose(errfp); 125994032Sgad } 126094032Sgad 126197781Sgad return (retcode); 126294032Sgad } 126394032Sgad 126494032Sgad /* 126594032Sgad * This is the child process, which is the one that executes the 126694032Sgad * given filter. 126794032Sgad */ 126894032Sgad /* 126994032Sgad * If the first parameter has any slashes in it, then change it 127094032Sgad * to point to the first character after the last slash. 127194032Sgad */ 127294032Sgad slash = strrchr(f_av[0], '/'); 127394032Sgad if (slash != NULL) 127494032Sgad f_av[0] = slash + 1; 127594032Sgad /* 127694032Sgad * XXX - in the future, this should setup an explicit list of 127794032Sgad * environment variables and use execve()! 127894032Sgad */ 127994032Sgad 128094032Sgad /* 128194032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 128294032Sgad * is running. Stderr is setup so it points to a temporary errors 128394032Sgad * file, and the parent process will copy that temporary file to 128494032Sgad * the real logfile after the filter completes. 128594032Sgad */ 128694032Sgad dup2(infd, 0); 128794032Sgad dup2(outfd, 1); 128894032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 128994032Sgad if (errfd >= 0) 129094032Sgad dup2(errfd, 2); 129194032Sgad closelog(); 129294032Sgad closeallfds(3); 129394032Sgad execv(f_cmd, f_av); 129495067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 129594032Sgad exit(2); 129694032Sgad /* NOTREACHED */ 129794032Sgad} 129894032Sgad 129994032Sgad/* 13001553Srgrimes * Check to make sure there have been no errors and that both programs 13011553Srgrimes * are in sync with eachother. 13021553Srgrimes * Return non-zero if the connection was lost. 13031553Srgrimes */ 13041553Srgrimesstatic char 130578146Sgadresponse(const struct printer *pp) 13061553Srgrimes{ 13071553Srgrimes char resp; 13081553Srgrimes 13091553Srgrimes if (read(pfd, &resp, 1) != 1) { 131031492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 131197791Sgad return (-1); 13121553Srgrimes } 131397791Sgad return (resp); 13141553Srgrimes} 13151553Srgrimes 13161553Srgrimes/* 13171553Srgrimes * Banner printing stuff 13181553Srgrimes */ 13191553Srgrimesstatic void 132078146Sgadbanner(struct printer *pp, char *name1, char *name2) 13211553Srgrimes{ 13221553Srgrimes time_t tvec; 13231553Srgrimes 13241553Srgrimes time(&tvec); 132531492Swollman if (!pp->no_formfeed && !pp->tof) 132631492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 132731492Swollman if (pp->short_banner) { /* short banner only */ 13281553Srgrimes if (class[0]) { 13291553Srgrimes (void) write(ofd, class, strlen(class)); 13301553Srgrimes (void) write(ofd, ":", 1); 13311553Srgrimes } 13321553Srgrimes (void) write(ofd, name1, strlen(name1)); 13331553Srgrimes (void) write(ofd, " Job: ", 7); 13341553Srgrimes (void) write(ofd, name2, strlen(name2)); 13351553Srgrimes (void) write(ofd, " Date: ", 8); 13361553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13371553Srgrimes (void) write(ofd, "\n", 1); 13381553Srgrimes } else { /* normal banner */ 13391553Srgrimes (void) write(ofd, "\n\n\n", 3); 134031492Swollman scan_out(pp, ofd, name1, '\0'); 13411553Srgrimes (void) write(ofd, "\n\n", 2); 134231492Swollman scan_out(pp, ofd, name2, '\0'); 13431553Srgrimes if (class[0]) { 13441553Srgrimes (void) write(ofd,"\n\n\n",3); 134531492Swollman scan_out(pp, ofd, class, '\0'); 13461553Srgrimes } 13471553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 13481553Srgrimes (void) write(ofd, name2, strlen(name2)); 13491553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 13501553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13511553Srgrimes (void) write(ofd, "\n", 1); 13521553Srgrimes } 135331492Swollman if (!pp->no_formfeed) 135431492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 135531492Swollman pp->tof = 1; 13561553Srgrimes} 13571553Srgrimes 13581553Srgrimesstatic char * 135978146Sgadscnline(int key, char *p, int c) 13601553Srgrimes{ 136139084Swollman register int scnwidth; 13621553Srgrimes 13631553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 13641553Srgrimes key <<= 1; 13651553Srgrimes *p++ = key & 0200 ? c : BACKGND; 13661553Srgrimes } 13671553Srgrimes return (p); 13681553Srgrimes} 13691553Srgrimes 13701553Srgrimes#define TRC(q) (((q)-' ')&0177) 13711553Srgrimes 13721553Srgrimesstatic void 137378146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 13741553Srgrimes{ 13751553Srgrimes register char *strp; 137639084Swollman register int nchrs, j; 13771553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 13781553Srgrimes int d, scnhgt; 13791553Srgrimes 13801553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 13811553Srgrimes strp = &outbuf[0]; 13821553Srgrimes sp = scsp; 13831553Srgrimes for (nchrs = 0; ; ) { 13841553Srgrimes d = dropit(c = TRC(cc = *sp++)); 13851553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 13861553Srgrimes for (j = WIDTH; --j;) 13871553Srgrimes *strp++ = BACKGND; 13881553Srgrimes else 138931492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 139031492Swollman if (*sp == dlm || *sp == '\0' || 139131492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 13921553Srgrimes break; 13931553Srgrimes *strp++ = BACKGND; 13941553Srgrimes *strp++ = BACKGND; 13951553Srgrimes } 13961553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 13971553Srgrimes ; 13981553Srgrimes strp++; 13998857Srgrimes *strp++ = '\n'; 14001553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 14011553Srgrimes } 14021553Srgrimes} 14031553Srgrimes 14041553Srgrimesstatic int 140578146Sgaddropit(int c) 14061553Srgrimes{ 14071553Srgrimes switch(c) { 14081553Srgrimes 14091553Srgrimes case TRC('_'): 14101553Srgrimes case TRC(';'): 14111553Srgrimes case TRC(','): 14121553Srgrimes case TRC('g'): 14131553Srgrimes case TRC('j'): 14141553Srgrimes case TRC('p'): 14151553Srgrimes case TRC('q'): 14161553Srgrimes case TRC('y'): 14171553Srgrimes return (DROP); 14181553Srgrimes 14191553Srgrimes default: 14201553Srgrimes return (0); 14211553Srgrimes } 14221553Srgrimes} 14231553Srgrimes 14241553Srgrimes/* 14251553Srgrimes * sendmail --- 14261553Srgrimes * tell people about job completion 14271553Srgrimes */ 14281553Srgrimesstatic void 142994038Sgadsendmail(struct printer *pp, char *userid, int bombed) 14301553Srgrimes{ 14311553Srgrimes register int i; 14321553Srgrimes int p[2], s; 143378146Sgad register const char *cp; 14341553Srgrimes struct stat stb; 14351553Srgrimes FILE *fp; 14361553Srgrimes 14371553Srgrimes pipe(p); 143831492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 14391553Srgrimes dup2(p[0], 0); 14408094Sjkh closelog(); 144131492Swollman closeallfds(3); 144227635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 14431553Srgrimes cp++; 144431492Swollman else 14451553Srgrimes cp = _PATH_SENDMAIL; 144679452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 144731492Swollman _exit(0); 14481553Srgrimes } else if (s > 0) { /* parent */ 14491553Srgrimes dup2(p[1], 1); 145094038Sgad printf("To: %s@%s\n", userid, origin_host); 145131492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 145215648Sjoerg *jobname ? jobname : "<unknown>"); 145378300Sgad printf("Reply-To: root@%s\n\n", local_host); 14541553Srgrimes printf("Your printer job "); 14551553Srgrimes if (*jobname) 14561553Srgrimes printf("(%s) ", jobname); 145794040Sgad 14581553Srgrimes switch (bombed) { 14591553Srgrimes case OK: 146094040Sgad cp = "OK"; 14611553Srgrimes printf("\ncompleted successfully\n"); 14621553Srgrimes break; 14631553Srgrimes default: 14641553Srgrimes case FATALERR: 146594040Sgad cp = "FATALERR"; 14661553Srgrimes printf("\ncould not be printed\n"); 14671553Srgrimes break; 14681553Srgrimes case NOACCT: 146994040Sgad cp = "NOACCT"; 147078300Sgad printf("\ncould not be printed without an account on %s\n", 147178300Sgad local_host); 14721553Srgrimes break; 14731553Srgrimes case FILTERERR: 147494040Sgad cp = "FILTERERR"; 147568664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 147668664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 147715648Sjoerg printf("\nhad some errors and may not have printed\n"); 14781553Srgrimes break; 14791553Srgrimes } 148015648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 14811553Srgrimes while ((i = getc(fp)) != EOF) 14821553Srgrimes putchar(i); 14831553Srgrimes (void) fclose(fp); 14841553Srgrimes break; 14851553Srgrimes case ACCESS: 148694040Sgad cp = "ACCESS"; 14871553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 14881553Srgrimes } 14891553Srgrimes fflush(stdout); 14901553Srgrimes (void) close(1); 149131492Swollman } else { 149294038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 149331492Swollman return; 14941553Srgrimes } 14951553Srgrimes (void) close(p[0]); 14961553Srgrimes (void) close(p[1]); 149715648Sjoerg wait(NULL); 149815648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 149994038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 15001553Srgrimes} 15011553Srgrimes 15021553Srgrimes/* 15031553Srgrimes * dofork - fork with retries on failure 15041553Srgrimes */ 15051553Srgrimesstatic int 150678146Sgaddofork(const struct printer *pp, int action) 15071553Srgrimes{ 150897793Sgad pid_t forkpid; 150997793Sgad int i, fail; 151060871Smpp struct passwd *pwd; 15111553Srgrimes 151280230Sgad forkpid = -1; 151380230Sgad if (daemon_uname == NULL) { 151480230Sgad pwd = getpwuid(pp->daemon_user); 151580230Sgad if (pwd == NULL) { 151680230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 151780230Sgad pp->printer, pp->daemon_user); 151880230Sgad goto error_ret; 151980230Sgad } 152080230Sgad daemon_uname = strdup(pwd->pw_name); 152180230Sgad daemon_defgid = pwd->pw_gid; 152280230Sgad } 152380230Sgad 15241553Srgrimes for (i = 0; i < 20; i++) { 152580230Sgad forkpid = fork(); 152680230Sgad if (forkpid < 0) { 15271553Srgrimes sleep((unsigned)(i*i)); 15281553Srgrimes continue; 15291553Srgrimes } 15301553Srgrimes /* 15311553Srgrimes * Child should run as daemon instead of root 15321553Srgrimes */ 153378146Sgad if (forkpid == 0) { 153480230Sgad errno = 0; 153580230Sgad fail = initgroups(daemon_uname, daemon_defgid); 153680230Sgad if (fail) { 153780230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 153880230Sgad pp->printer, daemon_uname, daemon_defgid); 153960871Smpp break; 154060871Smpp } 154180230Sgad fail = setgid(daemon_defgid); 154280230Sgad if (fail) { 154380230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 154480230Sgad pp->printer, daemon_defgid); 154580230Sgad break; 154680230Sgad } 154780230Sgad fail = setuid(pp->daemon_user); 154880230Sgad if (fail) { 154980230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 155080230Sgad pp->printer, pp->daemon_user); 155180230Sgad break; 155280230Sgad } 155360871Smpp } 155497791Sgad return (forkpid); 15551553Srgrimes } 15561553Srgrimes 155780230Sgad /* 155880230Sgad * An error occurred. If the error is in the child process, then 155980230Sgad * this routine MUST always exit(). DORETURN only effects how 156080230Sgad * errors should be handled in the parent process. 156180230Sgad */ 156280230Sgaderror_ret: 156380230Sgad if (forkpid == 0) { 156480230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 156580230Sgad pp->printer); 156680230Sgad exit(1); 156780230Sgad } 156880230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 156980230Sgad 157080230Sgad sleep(1); /* throttle errors, as a safety measure */ 15711553Srgrimes switch (action) { 15721553Srgrimes case DORETURN: 157397791Sgad return (-1); 15741553Srgrimes default: 15751553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 157680230Sgad /* FALLTHROUGH */ 15771553Srgrimes case DOABORT: 15781553Srgrimes exit(1); 15791553Srgrimes } 15801553Srgrimes /*NOTREACHED*/ 15811553Srgrimes} 15821553Srgrimes 15831553Srgrimes/* 15841553Srgrimes * Kill child processes to abort current job. 15851553Srgrimes */ 15861553Srgrimesstatic void 158778146Sgadabortpr(int signo __unused) 15881553Srgrimes{ 158968664Sgad 159068664Sgad (void) unlink(tempstderr); 15911553Srgrimes kill(0, SIGINT); 159297793Sgad if (of_pid > 0) 159397793Sgad kill(of_pid, SIGCONT); 15941553Srgrimes while (wait(NULL) > 0) 15951553Srgrimes ; 159697793Sgad if (of_pid > 0 && tfd != -1) 159724831Sbrian unlink(tfile); 15981553Srgrimes exit(0); 15991553Srgrimes} 16001553Srgrimes 16011553Srgrimesstatic void 160278146Sgadinit(struct printer *pp) 16031553Srgrimes{ 16041553Srgrimes char *s; 16051553Srgrimes 160631492Swollman sprintf(&width[2], "%ld", pp->page_width); 160731492Swollman sprintf(&length[2], "%ld", pp->page_length); 160831492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 160931492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 161031492Swollman if ((s = checkremote(pp)) != 0) { 161131492Swollman syslog(LOG_WARNING, "%s", s); 161231492Swollman free(s); 161331492Swollman } 161431492Swollman} 161531492Swollman 161631492Swollmanvoid 161778146Sgadstartprinting(const char *printer) 161831492Swollman{ 161931492Swollman struct printer myprinter, *pp = &myprinter; 162031492Swollman int status; 162131492Swollman 162231492Swollman init_printer(pp); 162331492Swollman status = getprintcap(printer, pp); 162431492Swollman switch(status) { 162531492Swollman case PCAPERR_OSERR: 162631492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 16271553Srgrimes exit(1); 162831492Swollman case PCAPERR_NOTFOUND: 16291553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 16301553Srgrimes exit(1); 163131492Swollman case PCAPERR_TCLOOP: 163231492Swollman fatal(pp, "potential reference loop detected in printcap file"); 163331492Swollman default: 163431492Swollman break; 163531492Swollman } 163631492Swollman printjob(pp); 16371553Srgrimes} 16381553Srgrimes 16391553Srgrimes/* 16401553Srgrimes * Acquire line printer or remote connection. 16411553Srgrimes */ 16421553Srgrimesstatic void 164378146Sgadopenpr(const struct printer *pp) 16441553Srgrimes{ 164531492Swollman int p[2]; 164615648Sjoerg char *cp; 16471553Srgrimes 164831492Swollman if (pp->remote) { 164931492Swollman openrem(pp); 165094032Sgad /* 165194032Sgad * Lpd does support the setting of 'of=' filters for 165294032Sgad * jobs going to remote machines, but that does not 165394032Sgad * have the same meaning as 'of=' does when handling 165494032Sgad * local print queues. For remote machines, all 'of=' 165594032Sgad * filter processing is handled in sendfile(), and that 165694032Sgad * does not use these global "output filter" variables. 165794032Sgad */ 165894032Sgad ofd = -1; 165997793Sgad of_pid = 0; 166094032Sgad return; 166131492Swollman } else if (*pp->lp) { 166231492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 166331492Swollman opennet(pp); 166415648Sjoerg else 166531492Swollman opentty(pp); 16661553Srgrimes } else { 16671553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 166897792Sgad pp->printer); 16691553Srgrimes exit(1); 16701553Srgrimes } 167115648Sjoerg 16721553Srgrimes /* 16731553Srgrimes * Start up an output filter, if needed. 16741553Srgrimes */ 167597793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 16761553Srgrimes pipe(p); 167731492Swollman if (pp->remote) { 167831492Swollman strcpy(tfile, TFILENAME); 167924831Sbrian tfd = mkstemp(tfile); 168024831Sbrian } 168197793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 16821553Srgrimes dup2(p[0], 0); /* pipe is std in */ 168324831Sbrian /* tfile/printer is stdout */ 168431492Swollman dup2(pp->remote ? tfd : pfd, 1); 16858094Sjkh closelog(); 168631492Swollman closeallfds(3); 168731492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 168831492Swollman cp = pp->filters[LPF_OUTPUT]; 16891553Srgrimes else 16901553Srgrimes cp++; 169179452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 169279452Sbrian (char *)0); 169397792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 169497792Sgad pp->filters[LPF_OUTPUT]); 16951553Srgrimes exit(1); 16961553Srgrimes } 16971553Srgrimes (void) close(p[0]); /* close input side */ 16981553Srgrimes ofd = p[1]; /* use pipe for output */ 16991553Srgrimes } else { 17001553Srgrimes ofd = pfd; 170197793Sgad of_pid = 0; 17021553Srgrimes } 17031553Srgrimes} 17041553Srgrimes 170515648Sjoerg/* 170615648Sjoerg * Printer connected directly to the network 170715648Sjoerg * or to a terminal server on the net 170815648Sjoerg */ 170915648Sjoergstatic void 171078146Sgadopennet(const struct printer *pp) 171115648Sjoerg{ 171215648Sjoerg register int i; 171331492Swollman int resp; 171431492Swollman u_long port; 171531492Swollman char *ep; 171630407Sjoerg void (*savealrm)(int); 171715648Sjoerg 171831492Swollman port = strtoul(pp->lp, &ep, 0); 171938470Sbrian if (*ep != '@' || port > 65535) { 172031492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 172197792Sgad pp->lp); 172215648Sjoerg exit(1); 172315648Sjoerg } 172431492Swollman ep++; 172515648Sjoerg 172615648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 172715648Sjoerg resp = -1; 172830407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 172931492Swollman alarm(pp->conn_timeout); 173031492Swollman pfd = getport(pp, ep, port); 173131020Sjoerg alarm(0); 173230407Sjoerg (void)signal(SIGALRM, savealrm); 173315648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 173415648Sjoerg resp = 1; 173515648Sjoerg else if (pfd >= 0) { 173615648Sjoerg /* 173715648Sjoerg * need to delay a bit for rs232 lines 173815648Sjoerg * to stabilize in case printer is 173915648Sjoerg * connected via a terminal server 174015648Sjoerg */ 174115648Sjoerg delay(500); 174215648Sjoerg break; 174315648Sjoerg } 174415648Sjoerg if (i == 1) { 174531492Swollman if (resp < 0) 174631492Swollman pstatus(pp, "waiting for %s to come up", 174731492Swollman pp->lp); 174831492Swollman else 174931492Swollman pstatus(pp, 175031492Swollman "waiting for access to printer on %s", 175131492Swollman pp->lp); 175215648Sjoerg } 175315648Sjoerg sleep(i); 175415648Sjoerg } 175579739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 175615648Sjoerg} 175715648Sjoerg 175815648Sjoerg/* 175915648Sjoerg * Printer is connected to an RS232 port on this host 176015648Sjoerg */ 176115648Sjoergstatic void 176278146Sgadopentty(const struct printer *pp) 176315648Sjoerg{ 176415648Sjoerg register int i; 176515648Sjoerg 176615648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 176731492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 176815648Sjoerg if (pfd >= 0) { 176915648Sjoerg delay(500); 177015648Sjoerg break; 177115648Sjoerg } 177215648Sjoerg if (errno == ENOENT) { 177331492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 177415648Sjoerg exit(1); 177515648Sjoerg } 177615648Sjoerg if (i == 1) 177731492Swollman pstatus(pp, 177831492Swollman "waiting for %s to become ready (offline?)", 177931492Swollman pp->printer); 178015648Sjoerg sleep(i); 178115648Sjoerg } 178215648Sjoerg if (isatty(pfd)) 178331492Swollman setty(pp); 178431492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 178515648Sjoerg} 178615648Sjoerg 178715648Sjoerg/* 178815648Sjoerg * Printer is on a remote host 178915648Sjoerg */ 179015648Sjoergstatic void 179178146Sgadopenrem(const struct printer *pp) 179215648Sjoerg{ 179331492Swollman register int i; 179427748Simp int resp; 179530407Sjoerg void (*savealrm)(int); 179615648Sjoerg 179715648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 179815648Sjoerg resp = -1; 179930407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 180031492Swollman alarm(pp->conn_timeout); 180131492Swollman pfd = getport(pp, pp->remote_host, 0); 180231020Sjoerg alarm(0); 180330407Sjoerg (void)signal(SIGALRM, savealrm); 180415648Sjoerg if (pfd >= 0) { 180531492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 180631492Swollman (char *)0) 180731492Swollman == 2 + strlen(pp->remote_queue)) 180831492Swollman && (resp = response(pp)) == 0) 180915648Sjoerg break; 181015648Sjoerg (void) close(pfd); 181115648Sjoerg } 181215648Sjoerg if (i == 1) { 181315648Sjoerg if (resp < 0) 181431492Swollman pstatus(pp, "waiting for %s to come up", 181531492Swollman pp->remote_host); 181615648Sjoerg else { 181731492Swollman pstatus(pp, 181831492Swollman "waiting for queue to be enabled on %s", 181931492Swollman pp->remote_host); 182015648Sjoerg i = 256; 182115648Sjoerg } 182215648Sjoerg } 182315648Sjoerg sleep(i); 182415648Sjoerg } 182531492Swollman pstatus(pp, "sending to %s", pp->remote_host); 182615648Sjoerg} 182715648Sjoerg 18281553Srgrimes/* 18291553Srgrimes * setup tty lines. 18301553Srgrimes */ 18311553Srgrimesstatic void 183278146Sgadsetty(const struct printer *pp) 18331553Srgrimes{ 183415032Ssef struct termios ttybuf; 18351553Srgrimes 18361553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 183731492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 18381553Srgrimes exit(1); 18391553Srgrimes } 184015032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 184131492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 18421553Srgrimes exit(1); 18431553Srgrimes } 184431492Swollman if (pp->baud_rate > 0) 184531492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 184631492Swollman if (pp->mode_set) { 184731492Swollman char *s = strdup(pp->mode_set), *tmp; 184815032Ssef 184931492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 185039084Swollman (void) msearch(tmp, &ttybuf); 18511553Srgrimes } 18521553Srgrimes } 185331492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 185415032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 185531492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 18561553Srgrimes } 18571553Srgrimes } 18581553Srgrimes} 18591553Srgrimes 18601553Srgrimes#include <stdarg.h> 18611553Srgrimes 186215648Sjoergstatic void 186331492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 18641553Srgrimes{ 186531492Swollman int fd; 186631492Swollman char *buf; 18671553Srgrimes va_list ap; 18681553Srgrimes va_start(ap, msg); 18691553Srgrimes 18701553Srgrimes umask(0); 187131492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 187231492Swollman if (fd < 0) { 187397792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 187497792Sgad pp->status_file); 18751553Srgrimes exit(1); 18761553Srgrimes } 18771553Srgrimes ftruncate(fd, 0); 187831492Swollman vasprintf(&buf, msg, ap); 18791553Srgrimes va_end(ap); 188031492Swollman writel(fd, buf, "\n", (char *)0); 188131492Swollman close(fd); 188231492Swollman free(buf); 18831553Srgrimes} 188430407Sjoerg 188530407Sjoergvoid 188678146Sgadalarmhandler(int signo __unused) 188730407Sjoerg{ 188878146Sgad /* the signal is ignored */ 188978146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 189030407Sjoerg} 1891