printjob.c revision 97789
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 411553Srgrimes#ifndef lint 4231492Swollman/* 4315648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 4431492Swollman*/ 4531492Swollmanstatic const char rcsid[] = 4650479Speter "$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 97789 2002-06-04 01:16:13Z gad $"; 471553Srgrimes#endif /* not lint */ 481553Srgrimes 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 */ 971553Srgrimesstatic int child; /* id of any filters */ 9868253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 991553Srgrimesstatic int lfd; /* lock file descriptor */ 1001553Srgrimesstatic int ofd; /* output filter file descriptor */ 1011553Srgrimesstatic int ofilter; /* id of output filter, if any */ 10224831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1031553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1041553Srgrimesstatic int pid; /* pid of lpd process */ 1051553Srgrimesstatic int prchild; /* id of pr process */ 1061553Srgrimesstatic char title[80]; /* ``pr'' title */ 10753956Sachestatic char locale[80]; /* ``pr'' locale */ 1081553Srgrimes 10980230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 11080230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 11180230Sgadstatic int daemon_defgid; 11280230Sgad 1131553Srgrimesstatic char class[32]; /* classification field */ 11478300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1151553Srgrimes /* indentation size in static characters */ 1168857Srgrimesstatic char indent[10] = "-i0"; 1171553Srgrimesstatic char jobname[100]; /* job or file name */ 1181553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1191553Srgrimesstatic char logname[32]; /* user's login name */ 1201553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1211553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 12268664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12368664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1241553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12524831Sbrian#define TFILENAME "fltXXXXXX" 12624831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1271553Srgrimes 12878146Sgadstatic void abortpr(int _signo); 12978146Sgadstatic void alarmhandler(int _signo); 13078146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 13178146Sgadstatic int dofork(const struct printer *_pp, int _action); 13278146Sgadstatic int dropit(int _c); 13394032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13494032Sgad int _infd, int _outfd); 13578146Sgadstatic void init(struct printer *_pp); 13678146Sgadstatic void openpr(const struct printer *_pp); 13778146Sgadstatic void opennet(const struct printer *_pp); 13878146Sgadstatic void opentty(const struct printer *_pp); 13978146Sgadstatic void openrem(const struct printer *pp); 14078146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 14178146Sgadstatic int printit(struct printer *_pp, char *_file); 14279739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14379739Sgad __printflike(2, 3); 14478146Sgadstatic char response(const struct printer *_pp); 14578146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14678146Sgad int _dlm); 14778146Sgadstatic char *scnline(int _key, char *_p, int _c); 14878146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 14995293Sgad char _format, int _copyreq); 15078146Sgadstatic int sendit(struct printer *_pp, char *_file); 15194038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 15278146Sgadstatic void setty(const struct printer *_pp); 1531553Srgrimes 1541553Srgrimesvoid 15578146Sgadprintjob(struct printer *pp) 1561553Srgrimes{ 1571553Srgrimes struct stat stb; 15868401Sgad register struct jobqueue *q, **qp; 15968401Sgad struct jobqueue **queue; 1601553Srgrimes register int i, nitems; 16168733Sgad off_t pidoff; 16268733Sgad int errcnt, jobcount, tempfd; 1631553Srgrimes 16468733Sgad jobcount = 0; 16531492Swollman init(pp); /* set up capabilities */ 16631492Swollman (void) write(1, "", 1); /* ack that daemon is started */ 1671553Srgrimes (void) close(2); /* set up log file */ 16831492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 16931492Swollman syslog(LOG_ERR, "%s: %m", pp->log_file); 1701553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1711553Srgrimes } 1721553Srgrimes setgid(getegid()); 1731553Srgrimes pid = getpid(); /* for use with lprm */ 1741553Srgrimes setpgrp(0, pid); 17579735Sgad 17679735Sgad /* 17779735Sgad * At initial lpd startup, printjob may be called with various 17879735Sgad * signal handlers in effect. After that initial startup, any 17979735Sgad * calls to printjob will have a *different* set of signal-handlers 18079735Sgad * in effect. Make sure all handlers are the ones we want. 18179735Sgad */ 18279735Sgad signal(SIGCHLD, SIG_DFL); 1831553Srgrimes signal(SIGHUP, abortpr); 1841553Srgrimes signal(SIGINT, abortpr); 1851553Srgrimes signal(SIGQUIT, abortpr); 1861553Srgrimes signal(SIGTERM, abortpr); 1871553Srgrimes 1881553Srgrimes /* 1891553Srgrimes * uses short form file names 1901553Srgrimes */ 19131492Swollman if (chdir(pp->spool_dir) < 0) { 19231492Swollman syslog(LOG_ERR, "%s: %m", 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); 20231492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 2031553Srgrimes exit(1); 2041553Srgrimes } 20531492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 20631492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 20731492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 2081553Srgrimes exit(1); 2091553Srgrimes } 2101553Srgrimes ftruncate(lfd, 0); 2111553Srgrimes /* 2121553Srgrimes * write process id for others to know 2131553Srgrimes */ 2141553Srgrimes sprintf(line, "%u\n", pid); 2151553Srgrimes pidoff = i = strlen(line); 2161553Srgrimes if (write(lfd, line, i) != i) { 21731492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 2181553Srgrimes exit(1); 2191553Srgrimes } 2201553Srgrimes /* 2211553Srgrimes * search the spool directory for work and sort by queue order. 2221553Srgrimes */ 22331492Swollman if ((nitems = getq(pp, &queue)) < 0) { 22431492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 22531492Swollman pp->spool_dir); 2261553Srgrimes exit(1); 2271553Srgrimes } 2281553Srgrimes if (nitems == 0) /* no work to do */ 2291553Srgrimes exit(0); 23031492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 23131492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 23231492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, 23331492Swollman pp->lock_file); 2341553Srgrimes } 23568664Sgad 23668664Sgad /* create a file which will be used to hold stderr from filters */ 23768664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 23868664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 23968664Sgad tempstderr); 24068732Sgad exit(1); 24168664Sgad } 24268664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 24368664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 24468664Sgad tempstderr); 24568732Sgad exit(1); 24668664Sgad } 24768664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 24868664Sgad close(tempfd); 24968664Sgad 25031492Swollman openpr(pp); /* open printer or remote */ 2511553Srgrimesagain: 2521553Srgrimes /* 2531553Srgrimes * we found something to do now do it -- 2541553Srgrimes * write the name of the current control file into the lock file 2551553Srgrimes * so the spool queue program can tell what we're working on 2561553Srgrimes */ 2571553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2581553Srgrimes q = *qp++; 25968401Sgad if (stat(q->job_cfname, &stb) < 0) 2601553Srgrimes continue; 26115648Sjoerg errcnt = 0; 2621553Srgrimes restart: 26315648Sjoerg (void) lseek(lfd, pidoff, 0); 26468401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2651553Srgrimes i = strlen(line); 2661553Srgrimes if (write(lfd, line, i) != i) 26731492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, 26831492Swollman pp->lock_file); 26931492Swollman if (!pp->remote) 27068401Sgad i = printit(pp, q->job_cfname); 2711553Srgrimes else 27268401Sgad i = sendit(pp, q->job_cfname); 2731553Srgrimes /* 2741553Srgrimes * Check to see if we are supposed to stop printing or 2751553Srgrimes * if we are to rebuild the queue. 2761553Srgrimes */ 2771553Srgrimes if (fstat(lfd, &stb) == 0) { 2781553Srgrimes /* stop printing before starting next job? */ 27931492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2801553Srgrimes goto done; 2811553Srgrimes /* rebuild queue (after lpc topq) */ 28231492Swollman if (stb.st_mode & LFM_RESET_QUE) { 28331492Swollman for (free(q); nitems--; free(q)) 2841553Srgrimes q = *qp++; 28531492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 28631492Swollman < 0) 2871553Srgrimes syslog(LOG_WARNING, "%s: %s: %m", 28831492Swollman pp->printer, pp->lock_file); 2891553Srgrimes break; 2901553Srgrimes } 2911553Srgrimes } 29268733Sgad if (i == OK) /* all files of this job printed */ 29368733Sgad jobcount++; 29415648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 29515648Sjoerg /* try reprinting the job */ 29631492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 2971553Srgrimes if (ofilter > 0) { 2981553Srgrimes kill(ofilter, SIGCONT); /* to be sure */ 2991553Srgrimes (void) close(ofd); 30015648Sjoerg while ((i = wait(NULL)) > 0 && i != ofilter) 3011553Srgrimes ; 30279735Sgad if (i < 0) 30379735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 30479735Sgad pp->printer, ofilter); 3051553Srgrimes ofilter = 0; 3061553Srgrimes } 3071553Srgrimes (void) close(pfd); /* close printer */ 3081553Srgrimes if (ftruncate(lfd, pidoff) < 0) 30931492Swollman syslog(LOG_WARNING, "%s: %s: %m", 31031492Swollman pp->printer, pp->lock_file); 31131492Swollman openpr(pp); /* try to reopen printer */ 3121553Srgrimes goto restart; 31315648Sjoerg } else { 31431492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 31531492Swollman pp->printer, 31631492Swollman pp->remote ? "sent to remote host" : "printed", 31768401Sgad q->job_cfname); 31815648Sjoerg if (i == REPRINT) { 31927748Simp /* ensure we don't attempt this job again */ 32068401Sgad (void) unlink(q->job_cfname); 32168401Sgad q->job_cfname[0] = 'd'; 32268401Sgad (void) unlink(q->job_cfname); 32315648Sjoerg if (logname[0]) 32431492Swollman sendmail(pp, logname, FATALERR); 32515648Sjoerg } 3261553Srgrimes } 3271553Srgrimes } 32831492Swollman free(queue); 3291553Srgrimes /* 3301553Srgrimes * search the spool directory for more work. 3311553Srgrimes */ 33231492Swollman if ((nitems = getq(pp, &queue)) < 0) { 33331492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 33431492Swollman pp->spool_dir); 3351553Srgrimes exit(1); 3361553Srgrimes } 3371553Srgrimes if (nitems == 0) { /* no more work to do */ 3381553Srgrimes done: 33968733Sgad if (jobcount > 0) { /* jobs actually printed */ 34031492Swollman if (!pp->no_formfeed && !pp->tof) 34131492Swollman (void) write(ofd, pp->form_feed, 34231492Swollman strlen(pp->form_feed)); 34331492Swollman if (pp->trailer != NULL) /* output trailer */ 34431492Swollman (void) write(ofd, pp->trailer, 34531492Swollman strlen(pp->trailer)); 3461553Srgrimes } 34719202Simp (void) close(ofd); 34819202Simp (void) wait(NULL); 34968664Sgad (void) unlink(tempstderr); 3501553Srgrimes exit(0); 3511553Srgrimes } 3521553Srgrimes goto again; 3531553Srgrimes} 3541553Srgrimes 3551553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3561553Srgrimes 3571553Srgrimeschar ifonts[4][40] = { 3581553Srgrimes _PATH_VFONTR, 3591553Srgrimes _PATH_VFONTI, 3601553Srgrimes _PATH_VFONTB, 3611553Srgrimes _PATH_VFONTS, 3621553Srgrimes}; 3631553Srgrimes 3641553Srgrimes/* 3651553Srgrimes * The remaining part is the reading of the control file (cf) 3661553Srgrimes * and performing the various actions. 3671553Srgrimes */ 3681553Srgrimesstatic int 36978146Sgadprintit(struct printer *pp, char *file) 3701553Srgrimes{ 3711553Srgrimes register int i; 37268734Sgad char *cp; 37368734Sgad int bombed, didignorehdr; 3741553Srgrimes 37568734Sgad bombed = OK; 37668734Sgad didignorehdr = 0; 3771553Srgrimes /* 3781553Srgrimes * open control file; ignore if no longer there. 3791553Srgrimes */ 3801553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 38131492Swollman syslog(LOG_INFO, "%s: %s: %m", pp->printer, file); 3821553Srgrimes return(OK); 3831553Srgrimes } 3841553Srgrimes /* 3851553Srgrimes * Reset troff fonts. 3861553Srgrimes */ 3871553Srgrimes for (i = 0; i < 4; i++) 3881553Srgrimes strcpy(fonts[i], ifonts[i]); 38931492Swollman sprintf(&width[2], "%ld", pp->page_width); 3901553Srgrimes strcpy(indent+2, "0"); 3911553Srgrimes 39268253Sgad /* initialize job-specific count of datafiles processed */ 39368253Sgad job_dfcnt = 0; 39468253Sgad 3951553Srgrimes /* 3961553Srgrimes * read the control file for work to do 3971553Srgrimes * 3981553Srgrimes * file format -- first character in the line is a command 3991553Srgrimes * rest of the line is the argument. 4001553Srgrimes * valid commands are: 4011553Srgrimes * 4021553Srgrimes * S -- "stat info" for symbolic link protection 4031553Srgrimes * J -- "job name" on banner page 4041553Srgrimes * C -- "class name" on banner page 4051553Srgrimes * L -- "literal" user's name to print on banner 4061553Srgrimes * T -- "title" for pr 4071553Srgrimes * H -- "host name" of machine where lpr was done 4081553Srgrimes * P -- "person" user's login name 4091553Srgrimes * I -- "indent" amount to indent output 41015648Sjoerg * R -- laser dpi "resolution" 4111553Srgrimes * f -- "file name" name of text file to print 4121553Srgrimes * l -- "file name" text file with control chars 41383684Sgad * o -- "file name" postscript file, according to 41483684Sgad * the RFC. Here it is treated like an 'f'. 4151553Srgrimes * p -- "file name" text file to print with pr(1) 4161553Srgrimes * t -- "file name" troff(1) file to print 4171553Srgrimes * n -- "file name" ditroff(1) file to print 4181553Srgrimes * d -- "file name" dvi file to print 4191553Srgrimes * g -- "file name" plot(1G) file to print 4201553Srgrimes * v -- "file name" plain raster file to print 4211553Srgrimes * c -- "file name" cifplot file to print 4221553Srgrimes * 1 -- "R font file" for troff 4231553Srgrimes * 2 -- "I font file" for troff 4241553Srgrimes * 3 -- "B font file" for troff 4251553Srgrimes * 4 -- "S font file" for troff 4261553Srgrimes * N -- "name" of file (used by lpq) 4271553Srgrimes * U -- "unlink" name of file to remove 4281553Srgrimes * (after we print it. (Pass 2 only)). 4291553Srgrimes * M -- "mail" to user when done printing 43053956Sache * Z -- "locale" for pr 4311553Srgrimes * 4321553Srgrimes * getline reads a line and expands tabs to blanks 4331553Srgrimes */ 4341553Srgrimes 4351553Srgrimes /* pass 1 */ 4361553Srgrimes 4371553Srgrimes while (getline(cfp)) 4381553Srgrimes switch (line[0]) { 4391553Srgrimes case 'H': 44078300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 44127748Simp if (class[0] == '\0') { 44280133Sgad strlcpy(class, line+1, sizeof(class)); 44327748Simp } 4441553Srgrimes continue; 4451553Srgrimes 4461553Srgrimes case 'P': 44780133Sgad strlcpy(logname, line + 1, sizeof(logname)); 44831492Swollman if (pp->restricted) { /* restricted */ 4491553Srgrimes if (getpwnam(logname) == NULL) { 4501553Srgrimes bombed = NOACCT; 45131492Swollman sendmail(pp, line+1, bombed); 4521553Srgrimes goto pass2; 4531553Srgrimes } 4541553Srgrimes } 4551553Srgrimes continue; 4561553Srgrimes 4571553Srgrimes case 'S': 4581553Srgrimes cp = line+1; 4591553Srgrimes i = 0; 4601553Srgrimes while (*cp >= '0' && *cp <= '9') 4611553Srgrimes i = i * 10 + (*cp++ - '0'); 4621553Srgrimes fdev = i; 4631553Srgrimes cp++; 4641553Srgrimes i = 0; 4651553Srgrimes while (*cp >= '0' && *cp <= '9') 4661553Srgrimes i = i * 10 + (*cp++ - '0'); 4671553Srgrimes fino = i; 4681553Srgrimes continue; 4691553Srgrimes 4701553Srgrimes case 'J': 47127748Simp if (line[1] != '\0') { 47280133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 47327748Simp } else 4741553Srgrimes strcpy(jobname, " "); 4751553Srgrimes continue; 4761553Srgrimes 4771553Srgrimes case 'C': 4781553Srgrimes if (line[1] != '\0') 47980133Sgad strlcpy(class, line + 1, sizeof(class)); 48080133Sgad else if (class[0] == '\0') { 48180133Sgad /* XXX - why call gethostname instead of 48280133Sgad * just strlcpy'ing local_host? */ 4831553Srgrimes gethostname(class, sizeof(class)); 48480133Sgad class[sizeof(class) - 1] = '\0'; 48580133Sgad } 4861553Srgrimes continue; 4871553Srgrimes 4881553Srgrimes case 'T': /* header title for pr */ 48980133Sgad strlcpy(title, line + 1, sizeof(title)); 4901553Srgrimes continue; 4911553Srgrimes 4921553Srgrimes case 'L': /* identification line */ 49331492Swollman if (!pp->no_header && !pp->header_last) 49431492Swollman banner(pp, line+1, jobname); 4951553Srgrimes continue; 4961553Srgrimes 4971553Srgrimes case '1': /* troff fonts */ 4981553Srgrimes case '2': 4991553Srgrimes case '3': 5001553Srgrimes case '4': 50127748Simp if (line[1] != '\0') { 50280133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 50380133Sgad (size_t)50); 50427748Simp } 5051553Srgrimes continue; 5061553Srgrimes 5071553Srgrimes case 'W': /* page width */ 50880133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5091553Srgrimes continue; 5101553Srgrimes 5111553Srgrimes case 'I': /* indent amount */ 51280133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5131553Srgrimes continue; 5141553Srgrimes 51553956Sache case 'Z': /* locale for pr */ 51680133Sgad strlcpy(locale, line + 1, sizeof(locale)); 51753956Sache continue; 51853956Sache 5191553Srgrimes default: /* some file to print */ 52068467Sgad /* only lowercase cmd-codes include a file-to-print */ 52168467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 52268467Sgad /* ignore any other lines */ 52368467Sgad if (lflag <= 1) 52468467Sgad continue; 52568467Sgad if (!didignorehdr) { 52668467Sgad syslog(LOG_INFO, "%s: in %s :", 52768467Sgad pp->printer, file); 52868467Sgad didignorehdr = 1; 52968467Sgad } 53068467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 53168467Sgad pp->printer, line[0], &line[1]); 53268467Sgad continue; 53368467Sgad } 53468467Sgad i = print(pp, line[0], line+1); 53568467Sgad switch (i) { 5361553Srgrimes case ERROR: 5371553Srgrimes if (bombed == OK) 5381553Srgrimes bombed = FATALERR; 5391553Srgrimes break; 5401553Srgrimes case REPRINT: 5411553Srgrimes (void) fclose(cfp); 5421553Srgrimes return(REPRINT); 5431553Srgrimes case FILTERERR: 5441553Srgrimes case ACCESS: 5451553Srgrimes bombed = i; 54631492Swollman sendmail(pp, logname, bombed); 5471553Srgrimes } 5481553Srgrimes title[0] = '\0'; 5491553Srgrimes continue; 5501553Srgrimes 5511553Srgrimes case 'N': 5521553Srgrimes case 'U': 5531553Srgrimes case 'M': 55415648Sjoerg case 'R': 5551553Srgrimes continue; 5561553Srgrimes } 5571553Srgrimes 5581553Srgrimes /* pass 2 */ 5591553Srgrimes 5601553Srgrimespass2: 5611553Srgrimes fseek(cfp, 0L, 0); 5621553Srgrimes while (getline(cfp)) 5631553Srgrimes switch (line[0]) { 5641553Srgrimes case 'L': /* identification line */ 56531492Swollman if (!pp->no_header && pp->header_last) 56631492Swollman banner(pp, line+1, jobname); 5671553Srgrimes continue; 5681553Srgrimes 5691553Srgrimes case 'M': 5701553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 57131492Swollman sendmail(pp, line+1, bombed); 5721553Srgrimes continue; 5731553Srgrimes 5741553Srgrimes case 'U': 57527748Simp if (strchr(line+1, '/')) 57627748Simp continue; 5771553Srgrimes (void) unlink(line+1); 5781553Srgrimes } 5791553Srgrimes /* 5801553Srgrimes * clean-up in case another control file exists 5811553Srgrimes */ 5821553Srgrimes (void) fclose(cfp); 5831553Srgrimes (void) unlink(file); 5841553Srgrimes return(bombed == OK ? OK : ERROR); 5851553Srgrimes} 5861553Srgrimes 5871553Srgrimes/* 5881553Srgrimes * Print a file. 5891553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 5901553Srgrimes * Return -1 if a non-recoverable error occured, 5911553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 5921553Srgrimes * 1 if we should try to reprint this job and 5931553Srgrimes * 0 if all is well. 5941553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 5951553Srgrimes * stderr as the log file, and must not ignore SIGINT. 5961553Srgrimes */ 5971553Srgrimesstatic int 59878146Sgadprint(struct printer *pp, int format, char *file) 5991553Srgrimes{ 60053956Sache register int n, i; 6011553Srgrimes register char *prog; 60231492Swollman int fi, fo; 6031553Srgrimes FILE *fp; 6041553Srgrimes char *av[15], buf[BUFSIZ]; 60597789Sgad int pid, p[2], retcode, stopped, wstatus, wstatus_set; 6061553Srgrimes struct stat stb; 6071553Srgrimes 60868467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 60968467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 61068467Sgad pp->printer, file, format); 6111553Srgrimes return(ERROR); 61268467Sgad } 6131553Srgrimes /* 6141553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6151553Srgrimes * still point to the same file or someone is trying to print 6161553Srgrimes * something he shouldn't. 6171553Srgrimes */ 6181553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6191553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 6201553Srgrimes return(ACCESS); 62168253Sgad 62268253Sgad job_dfcnt++; /* increment datafile counter for this job */ 62368734Sgad stopped = 0; /* output filter is not stopped */ 62468253Sgad 62568253Sgad /* everything seems OK, start it up */ 62631492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 62731492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 62831492Swollman pp->tof = 1; 6291553Srgrimes } 63031492Swollman if (pp->filters[LPF_INPUT] == NULL 63183684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 63231492Swollman pp->tof = 0; 6331553Srgrimes while ((n = read(fi, buf, BUFSIZ)) > 0) 6341553Srgrimes if (write(ofd, buf, n) != n) { 6351553Srgrimes (void) close(fi); 6361553Srgrimes return(REPRINT); 6371553Srgrimes } 6381553Srgrimes (void) close(fi); 6391553Srgrimes return(OK); 6401553Srgrimes } 6411553Srgrimes switch (format) { 6421553Srgrimes case 'p': /* print file using 'pr' */ 64331492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6441553Srgrimes prog = _PATH_PR; 64553956Sache i = 0; 64653956Sache av[i++] = "pr"; 64753956Sache av[i++] = width; 64853956Sache av[i++] = length; 64953956Sache av[i++] = "-h"; 65053956Sache av[i++] = *title ? title : " "; 65153956Sache av[i++] = "-L"; 65253956Sache av[i++] = *locale ? locale : "C"; 65353956Sache av[i++] = "-F"; 65453956Sache av[i] = 0; 6551553Srgrimes fo = ofd; 6561553Srgrimes goto start; 6571553Srgrimes } 6581553Srgrimes pipe(p); 65931492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 6601553Srgrimes dup2(fi, 0); /* file is stdin */ 6611553Srgrimes dup2(p[1], 1); /* pipe is stdout */ 6628094Sjkh closelog(); 66331492Swollman closeallfds(3); 6641553Srgrimes execl(_PATH_PR, "pr", width, length, 66553956Sache "-h", *title ? title : " ", 66653956Sache "-L", *locale ? locale : "C", 66779452Sbrian "-F", (char *)0); 6681553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6691553Srgrimes exit(2); 6701553Srgrimes } 6711553Srgrimes (void) close(p[1]); /* close output side */ 6721553Srgrimes (void) close(fi); 6731553Srgrimes if (prchild < 0) { 6741553Srgrimes prchild = 0; 6751553Srgrimes (void) close(p[0]); 6761553Srgrimes return(ERROR); 6771553Srgrimes } 6781553Srgrimes fi = p[0]; /* use pipe for input */ 6791553Srgrimes case 'f': /* print plain text file */ 68031492Swollman prog = pp->filters[LPF_INPUT]; 6811553Srgrimes av[1] = width; 6821553Srgrimes av[2] = length; 6831553Srgrimes av[3] = indent; 6841553Srgrimes n = 4; 6851553Srgrimes break; 68686935Sgad case 'o': /* print postscript file */ 68786935Sgad /* 68886935Sgad * Treat this as a "plain file with control characters", and 68986935Sgad * assume the standard LPF_INPUT filter will recognize that 69086935Sgad * the data is postscript and know what to do with it. These 69186935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 69286935Sgad * (later versions of MacOS 10 will explicitly use 'l') 69386935Sgad * A postscript file can contain binary data, which is why 'l' 69486935Sgad * is somewhat more appropriate than 'f'. 69586935Sgad */ 69686935Sgad /* FALLTHROUGH */ 6971553Srgrimes case 'l': /* like 'f' but pass control characters */ 69831492Swollman prog = pp->filters[LPF_INPUT]; 6991553Srgrimes av[1] = "-c"; 7001553Srgrimes av[2] = width; 7011553Srgrimes av[3] = length; 7021553Srgrimes av[4] = indent; 7031553Srgrimes n = 5; 7041553Srgrimes break; 7051553Srgrimes case 'r': /* print a fortran text file */ 70631492Swollman prog = pp->filters[LPF_FORTRAN]; 7071553Srgrimes av[1] = width; 7081553Srgrimes av[2] = length; 7091553Srgrimes n = 3; 7101553Srgrimes break; 7111553Srgrimes case 't': /* print troff output */ 7121553Srgrimes case 'n': /* print ditroff output */ 7131553Srgrimes case 'd': /* print tex output */ 7141553Srgrimes (void) unlink(".railmag"); 7151553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 71631492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 71731492Swollman pp->printer); 7181553Srgrimes (void) unlink(".railmag"); 7191553Srgrimes } else { 7201553Srgrimes for (n = 0; n < 4; n++) { 7211553Srgrimes if (fonts[n][0] != '/') 7221553Srgrimes (void) write(fo, _PATH_VFONT, 7231553Srgrimes sizeof(_PATH_VFONT) - 1); 7241553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7251553Srgrimes (void) write(fo, "\n", 1); 7261553Srgrimes } 7271553Srgrimes (void) close(fo); 7281553Srgrimes } 72931492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 73031492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 73131492Swollman : pp->filters[LPF_DVI]); 7321553Srgrimes av[1] = pxwidth; 7331553Srgrimes av[2] = pxlength; 7341553Srgrimes n = 3; 7351553Srgrimes break; 7361553Srgrimes case 'c': /* print cifplot output */ 73731492Swollman prog = pp->filters[LPF_CIFPLOT]; 7381553Srgrimes av[1] = pxwidth; 7391553Srgrimes av[2] = pxlength; 7401553Srgrimes n = 3; 7411553Srgrimes break; 7421553Srgrimes case 'g': /* print plot(1G) output */ 74331492Swollman prog = pp->filters[LPF_GRAPH]; 7441553Srgrimes av[1] = pxwidth; 7451553Srgrimes av[2] = pxlength; 7461553Srgrimes n = 3; 7471553Srgrimes break; 7481553Srgrimes case 'v': /* print raster output */ 74931492Swollman prog = pp->filters[LPF_RASTER]; 7501553Srgrimes av[1] = pxwidth; 7511553Srgrimes av[2] = pxlength; 7521553Srgrimes n = 3; 7531553Srgrimes break; 7541553Srgrimes default: 7551553Srgrimes (void) close(fi); 7561553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 75731492Swollman pp->printer, format); 7581553Srgrimes return(ERROR); 7591553Srgrimes } 76015648Sjoerg if (prog == NULL) { 76115648Sjoerg (void) close(fi); 76215648Sjoerg syslog(LOG_ERR, 76315648Sjoerg "%s: no filter found in printcap for format character '%c'", 76431492Swollman pp->printer, format); 76515648Sjoerg return(ERROR); 76615648Sjoerg } 76727635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7681553Srgrimes av[0]++; 7691553Srgrimes else 7701553Srgrimes av[0] = prog; 7711553Srgrimes av[n++] = "-n"; 7721553Srgrimes av[n++] = logname; 7731553Srgrimes av[n++] = "-h"; 77478300Sgad av[n++] = origin_host; 77531492Swollman av[n++] = pp->acct_file; 7761553Srgrimes av[n] = 0; 7771553Srgrimes fo = pfd; 7781553Srgrimes if (ofilter > 0) { /* stop output filter */ 7791553Srgrimes write(ofd, "\031\1", 2); 7801553Srgrimes while ((pid = 78197781Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && pid != ofilter) 7821553Srgrimes ; 78379735Sgad if (pid < 0) 78479735Sgad syslog(LOG_WARNING, "%s: after stopping 'of', wait3() returned: %m", 78579735Sgad pp->printer); 78697781Sgad else if (!WIFSTOPPED(wstatus)) { 7871553Srgrimes (void) close(fi); 78897781Sgad syslog(LOG_WARNING, "%s: output filter died " 78997781Sgad "(pid=%d retcode=%d termsig=%d)", 79097781Sgad pp->printer, ofilter, WEXITSTATUS(wstatus), 79197781Sgad WTERMSIG(wstatus)); 7921553Srgrimes return(REPRINT); 7931553Srgrimes } 7941553Srgrimes stopped++; 7951553Srgrimes } 7961553Srgrimesstart: 79731492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 7981553Srgrimes dup2(fi, 0); 7991553Srgrimes dup2(fo, 1); 80068664Sgad /* setup stderr for the filter (child process) 80168664Sgad * so it goes to our temporary errors file */ 80268664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8031553Srgrimes if (n >= 0) 8041553Srgrimes dup2(n, 2); 8058094Sjkh closelog(); 80631492Swollman closeallfds(3); 8071553Srgrimes execv(prog, av); 80895067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 80995067Sgad prog); 8101553Srgrimes exit(2); 8111553Srgrimes } 8121553Srgrimes (void) close(fi); 81397789Sgad wstatus_set = 0; 8141553Srgrimes if (child < 0) 81597781Sgad retcode = 100; 81679735Sgad else { 81797781Sgad while ((pid = wait(&wstatus)) > 0 && pid != child) 8181553Srgrimes ; 81979735Sgad if (pid < 0) { 82097781Sgad retcode = 100; 82179735Sgad syslog(LOG_WARNING, "%s: after execv(%s), wait() returned: %m", 82279735Sgad pp->printer, prog); 82397789Sgad } else { 82497789Sgad wstatus_set = 1; 82597781Sgad retcode = WEXITSTATUS(wstatus); 82697789Sgad } 82779735Sgad } 8281553Srgrimes child = 0; 8291553Srgrimes prchild = 0; 8301553Srgrimes if (stopped) { /* restart output filter */ 8311553Srgrimes if (kill(ofilter, SIGCONT) < 0) { 8321553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8331553Srgrimes exit(1); 8341553Srgrimes } 8351553Srgrimes } 83631492Swollman pp->tof = 0; 8371553Srgrimes 83868664Sgad /* Copy the filter's output to "lf" logfile */ 83968664Sgad if ((fp = fopen(tempstderr, "r"))) { 8401553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8411553Srgrimes fputs(buf, stderr); 8421553Srgrimes fclose(fp); 8431553Srgrimes } 8441553Srgrimes 84597789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 84615648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 84797781Sgad pp->printer, format, WTERMSIG(wstatus)); 8481553Srgrimes return(ERROR); 8491553Srgrimes } 85097781Sgad switch (retcode) { 8511553Srgrimes case 0: 85231492Swollman pp->tof = 1; 8531553Srgrimes return(OK); 8541553Srgrimes case 1: 8551553Srgrimes return(REPRINT); 85615648Sjoerg case 2: 85715648Sjoerg return(ERROR); 8581553Srgrimes default: 85915648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 86097781Sgad pp->printer, format, retcode); 86115648Sjoerg return(FILTERERR); 8621553Srgrimes } 8631553Srgrimes} 8641553Srgrimes 8651553Srgrimes/* 8661553Srgrimes * Send the daemon control file (cf) and any data files. 8671553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 8681553Srgrimes * 0 if all is well. 8691553Srgrimes */ 8701553Srgrimesstatic int 87178146Sgadsendit(struct printer *pp, char *file) 8721553Srgrimes{ 87395293Sgad int dfcopies, err, i; 8741553Srgrimes char *cp, last[BUFSIZ]; 8751553Srgrimes 8761553Srgrimes /* 8771553Srgrimes * open control file 8781553Srgrimes */ 8791553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 8801553Srgrimes return(OK); 88168253Sgad 88268253Sgad /* initialize job-specific count of datafiles processed */ 88368253Sgad job_dfcnt = 0; 88468253Sgad 8851553Srgrimes /* 8861553Srgrimes * read the control file for work to do 8871553Srgrimes * 8881553Srgrimes * file format -- first character in the line is a command 8891553Srgrimes * rest of the line is the argument. 8901553Srgrimes * commands of interest are: 8911553Srgrimes * 8921553Srgrimes * a-z -- "file name" name of file to print 8931553Srgrimes * U -- "unlink" name of file to remove 8941553Srgrimes * (after we print it. (Pass 2 only)). 8951553Srgrimes */ 8961553Srgrimes 8971553Srgrimes /* 8981553Srgrimes * pass 1 8991553Srgrimes */ 90095293Sgad err = OK; 9011553Srgrimes while (getline(cfp)) { 9021553Srgrimes again: 9031553Srgrimes if (line[0] == 'S') { 9041553Srgrimes cp = line+1; 9051553Srgrimes i = 0; 9061553Srgrimes while (*cp >= '0' && *cp <= '9') 9071553Srgrimes i = i * 10 + (*cp++ - '0'); 9081553Srgrimes fdev = i; 9091553Srgrimes cp++; 9101553Srgrimes i = 0; 9111553Srgrimes while (*cp >= '0' && *cp <= '9') 9121553Srgrimes i = i * 10 + (*cp++ - '0'); 9131553Srgrimes fino = i; 91424831Sbrian } else if (line[0] == 'H') { 91578300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 91668343Sgad if (class[0] == '\0') { 91780133Sgad strlcpy(class, line + 1, sizeof(class)); 91868343Sgad } 91924831Sbrian } else if (line[0] == 'P') { 92080133Sgad strlcpy(logname, line + 1, sizeof(logname)); 92131492Swollman if (pp->restricted) { /* restricted */ 92224831Sbrian if (getpwnam(logname) == NULL) { 92331492Swollman sendmail(pp, line+1, NOACCT); 92424831Sbrian err = ERROR; 92524831Sbrian break; 92624831Sbrian } 92724831Sbrian } 92824831Sbrian } else if (line[0] == 'I') { 92980133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 93024831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 93195293Sgad dfcopies = 1; 9321553Srgrimes strcpy(last, line); 93395293Sgad while ((i = getline(cfp)) != 0) { 93495293Sgad if (strcmp(last, line) != 0) 9351553Srgrimes break; 93695293Sgad dfcopies++; 93795293Sgad } 93895293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9391553Srgrimes case OK: 9401553Srgrimes if (i) 9411553Srgrimes goto again; 9421553Srgrimes break; 9431553Srgrimes case REPRINT: 9441553Srgrimes (void) fclose(cfp); 9451553Srgrimes return(REPRINT); 9461553Srgrimes case ACCESS: 94731492Swollman sendmail(pp, logname, ACCESS); 9481553Srgrimes case ERROR: 9491553Srgrimes err = ERROR; 9501553Srgrimes } 9511553Srgrimes break; 9521553Srgrimes } 9531553Srgrimes } 95495293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9551553Srgrimes (void) fclose(cfp); 9561553Srgrimes return(REPRINT); 9571553Srgrimes } 9581553Srgrimes /* 9591553Srgrimes * pass 2 9601553Srgrimes */ 9611553Srgrimes fseek(cfp, 0L, 0); 9621553Srgrimes while (getline(cfp)) 96327748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9641553Srgrimes (void) unlink(line+1); 9651553Srgrimes /* 9661553Srgrimes * clean-up in case another control file exists 9671553Srgrimes */ 9681553Srgrimes (void) fclose(cfp); 9691553Srgrimes (void) unlink(file); 9701553Srgrimes return(err); 9711553Srgrimes} 9721553Srgrimes 9731553Srgrimes/* 9741553Srgrimes * Send a data file to the remote machine and spool it. 9751553Srgrimes * Return positive if we should try resending. 9761553Srgrimes */ 9771553Srgrimesstatic int 97895293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 9791553Srgrimes{ 98094036Sgad int i, amt; 9811553Srgrimes struct stat stb; 98294032Sgad char *av[15], *filtcmd; 98394032Sgad char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 98495293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 9851553Srgrimes 98674124Sgad statrc = lstat(file, &stb); 98774124Sgad if (statrc < 0) { 98874124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 98974124Sgad pp->printer, file); 99094036Sgad return (ERROR); 99174124Sgad } 99294036Sgad sfd = open(file, O_RDONLY); 99394036Sgad if (sfd < 0) { 99474124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 99574124Sgad pp->printer, file); 99694036Sgad return (ERROR); 99774124Sgad } 9981553Srgrimes /* 9991553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10001553Srgrimes * still point to the same file or someone is trying to print something 10011553Srgrimes * he shouldn't. 10021553Srgrimes */ 100394036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 100494036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 100594036Sgad close(sfd); 100694036Sgad return (ACCESS); 100794036Sgad } 100824831Sbrian 100994032Sgad /* Everything seems OK for reading the file, now to send it */ 101094032Sgad filtcmd = NULL; 101124831Sbrian sizerr = 0; 101294032Sgad tfd = -1; 101324831Sbrian if (type == '\3') { 101494032Sgad /* 101594032Sgad * Type == 3 means this is a datafile, not a control file. 101694032Sgad * Increment the counter of data-files in this job, and 101794032Sgad * then check for input or output filters (which are only 101894032Sgad * applied to datafiles, not control files). 101994032Sgad */ 102094032Sgad job_dfcnt++; 102194032Sgad 102294032Sgad /* 102394032Sgad * Note that here we are filtering datafiles, one at a time, 102494032Sgad * as they are sent to the remote machine. Here, the *only* 102594032Sgad * difference between an input filter (`if=') and an output 102694032Sgad * filter (`of=') is the argument list that the filter is 102794032Sgad * started up with. Here, the output filter is executed 102894032Sgad * for each individual file as it is sent. This is not the 102994032Sgad * same as local print queues, where the output filter is 103094032Sgad * started up once, and then all jobs are passed thru that 103194032Sgad * single invocation of the output filter. 103294032Sgad * 103394032Sgad * Also note that a queue for a remote-machine can have an 103494032Sgad * input filter or an output filter, but not both. 103594032Sgad */ 103631492Swollman if (pp->filters[LPF_INPUT]) { 103794032Sgad filtcmd = pp->filters[LPF_INPUT]; 103894032Sgad av[0] = filtcmd; 103994032Sgad narg = 0; 104094032Sgad strcpy(opt_c, "-c"); 104194032Sgad strcpy(opt_h, "-h"); 104294032Sgad strcpy(opt_n, "-n"); 104324831Sbrian if (format == 'l') 104494032Sgad av[++narg] = opt_c; 104594032Sgad av[++narg] = width; 104694032Sgad av[++narg] = length; 104794032Sgad av[++narg] = indent; 104894032Sgad av[++narg] = opt_n; 104994032Sgad av[++narg] = logname; 105094032Sgad av[++narg] = opt_h; 105194032Sgad av[++narg] = origin_host; 105294032Sgad av[++narg] = pp->acct_file; 105394032Sgad av[++narg] = NULL; 105494032Sgad } else if (pp->filters[LPF_OUTPUT]) { 105594032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 105694032Sgad av[0] = filtcmd; 105794032Sgad narg = 0; 105894032Sgad av[++narg] = width; 105994032Sgad av[++narg] = length; 106094032Sgad av[++narg] = NULL; 106124831Sbrian } 106224831Sbrian } 106394032Sgad if (filtcmd) { 106494032Sgad /* 106594032Sgad * If there is an input or output filter, we have to run 106694032Sgad * the datafile thru that filter and store the result as 106794032Sgad * a temporary spool file, because the protocol requires 106894032Sgad * that we send the remote host the file-size before we 106994032Sgad * start to send any of the data. 107094032Sgad */ 107194032Sgad strcpy(tfile, TFILENAME); 107294032Sgad tfd = mkstemp(tfile); 107394032Sgad if (tfd == -1) { 107494032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 107594032Sgad TFILENAME); 107694036Sgad sfres = ERROR; 107794036Sgad goto return_sfres; 107894032Sgad } 107994036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 108024831Sbrian 108194032Sgad /* process the return-code from the filter */ 108294032Sgad switch (filtstat) { 108394032Sgad case 0: 108494032Sgad break; 108594032Sgad case 1: 108694036Sgad sfres = REPRINT; 108794036Sgad goto return_sfres; 108894032Sgad case 2: 108994036Sgad sfres = ERROR; 109094036Sgad goto return_sfres; 109194032Sgad default: 109294032Sgad syslog(LOG_WARNING, 109394032Sgad "%s: filter '%c' exited (retcode=%d)", 109494032Sgad pp->printer, format, filtstat); 109594036Sgad sfres = FILTERERR; 109694036Sgad goto return_sfres; 109794032Sgad } 109894032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 109994032Sgad if (statrc < 0) { 110094032Sgad syslog(LOG_ERR, 110194032Sgad "%s: error processing 'if', fstat(%s): %m", 110294032Sgad pp->printer, tfile); 110394036Sgad sfres = ERROR; 110494036Sgad goto return_sfres; 110594032Sgad } 110694036Sgad close(sfd); 110794036Sgad sfd = tfd; 110894036Sgad lseek(sfd, 0, SEEK_SET); 110994032Sgad } 111094032Sgad 111195293Sgad copycnt = 0; 111295293Sgadsendagain: 111395293Sgad copycnt++; 111495293Sgad 111595293Sgad if (copycnt < 2) 111695293Sgad (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 111795293Sgad else 111895293Sgad (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 111995293Sgad file, copycnt); 11201553Srgrimes amt = strlen(buf); 11211553Srgrimes for (i = 0; ; i++) { 11221553Srgrimes if (write(pfd, buf, amt) != amt || 112331492Swollman (resp = response(pp)) < 0 || resp == '\1') { 112494036Sgad sfres = REPRINT; 112594036Sgad goto return_sfres; 11261553Srgrimes } else if (resp == '\0') 11271553Srgrimes break; 11281553Srgrimes if (i == 0) 112931492Swollman pstatus(pp, 113031492Swollman "no space on remote; waiting for queue to drain"); 11311553Srgrimes if (i == 10) 11321553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 113331492Swollman pp->printer, pp->remote_host); 11341553Srgrimes sleep(5 * 60); 11351553Srgrimes } 11361553Srgrimes if (i) 113731492Swollman pstatus(pp, "sending to %s", pp->remote_host); 113895293Sgad /* 113995293Sgad * XXX - we should change trstat_init()/trstat_write() to include 114095293Sgad * the copycnt in the statistics record it may write. 114195293Sgad */ 114268253Sgad if (type == '\3') 114368253Sgad trstat_init(pp, file, job_dfcnt); 11441553Srgrimes for (i = 0; i < stb.st_size; i += BUFSIZ) { 11451553Srgrimes amt = BUFSIZ; 11461553Srgrimes if (i + amt > stb.st_size) 11471553Srgrimes amt = stb.st_size - i; 114894036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11491553Srgrimes sizerr = 1; 11501553Srgrimes if (write(pfd, buf, amt) != amt) { 115194036Sgad sfres = REPRINT; 115294036Sgad goto return_sfres; 11531553Srgrimes } 11541553Srgrimes } 11551553Srgrimes 11561553Srgrimes if (sizerr) { 115731492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11581553Srgrimes /* tell recvjob to ignore this file */ 11591553Srgrimes (void) write(pfd, "\1", 1); 116094036Sgad sfres = ERROR; 116194036Sgad goto return_sfres; 11621553Srgrimes } 116331492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 116494036Sgad sfres = REPRINT; 116594036Sgad goto return_sfres; 116624831Sbrian } 116795293Sgad if (type == '\3') { 116868253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 116995293Sgad pp->remote_host, origin_host); 117095293Sgad /* 117195293Sgad * Usually we only need to send one copy of a datafile, 117295293Sgad * because the control-file will simply print the same 117395293Sgad * file multiple times. However, some printers ignore 117495293Sgad * the control file, and simply print each data file as 117595293Sgad * it arrives. For such "remote hosts", we need to 117695293Sgad * transfer the same data file multiple times. Such a 117795293Sgad * a host is indicated by adding 'rc' to the printcap 117895293Sgad * entry. 117995293Sgad * XXX - Right now this ONLY works for remote hosts which 118095293Sgad * do ignore the name of the data file, because 118195293Sgad * this sends the file multiple times with slight 118295293Sgad * changes to the filename. To do this right would 118395293Sgad * require that we also rewrite the control file 118495293Sgad * to match those filenames. 118595293Sgad */ 118695293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 118795293Sgad lseek(sfd, 0, SEEK_SET); 118895293Sgad goto sendagain; 118995293Sgad } 119095293Sgad } 119194036Sgad sfres = OK; 119294036Sgad 119394036Sgadreturn_sfres: 119494036Sgad (void)close(sfd); 119594036Sgad if (tfd != -1) { 119694036Sgad /* 119794036Sgad * If tfd is set, then it is the same value as sfd, and 119894036Sgad * therefore it is already closed at this point. All 119994036Sgad * we need to do is remove the temporary file. 120094036Sgad */ 120194036Sgad tfd = -1; 120294036Sgad unlink(tfile); 120394036Sgad } 120494036Sgad return (sfres); 12051553Srgrimes} 12061553Srgrimes 12071553Srgrimes/* 120894032Sgad * This routine is called to execute one of the filters as was 120994036Sgad * specified in a printcap entry. While the child-process will read 121094036Sgad * all of 'infd', it is up to the caller to close that file descriptor 121194036Sgad * in the parent process. 121294032Sgad */ 121394032Sgadstatic int 121494032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 121594032Sgad{ 121697781Sgad int errfd, fpid, retcode, wpid, wstatus; 121794032Sgad FILE *errfp; 121894032Sgad char buf[BUFSIZ], *slash; 121994032Sgad 122094032Sgad fpid = dofork(pp, DORETURN); 122194032Sgad if (fpid != 0) { 122294032Sgad /* 122394032Sgad * This is the parent process, which just waits for the child 122494032Sgad * to complete and then returns the result. Note that it is 122594032Sgad * the child process which reads the input stream. 122694032Sgad */ 122794032Sgad if (fpid < 0) 122897781Sgad retcode = 100; 122994032Sgad else { 123097781Sgad while ((wpid = wait(&wstatus)) > 0 && 123194032Sgad wpid != fpid) 123294032Sgad ; 123394032Sgad if (wpid < 0) { 123497781Sgad retcode = 100; 123594032Sgad syslog(LOG_WARNING, 123694032Sgad "%s: after execv(%s), wait() returned: %m", 123794032Sgad pp->printer, f_cmd); 123897781Sgad } else 123997781Sgad retcode = WEXITSTATUS(wstatus); 124094032Sgad } 124194032Sgad 124294032Sgad /* 124394032Sgad * Copy everything the filter wrote to stderr from our 124494032Sgad * temporary errors file to the "lf=" logfile. 124594032Sgad */ 124694032Sgad errfp = fopen(tempstderr, "r"); 124794032Sgad if (errfp) { 124894032Sgad while (fgets(buf, sizeof(buf), errfp)) 124994032Sgad fputs(buf, stderr); 125094032Sgad fclose(errfp); 125194032Sgad } 125294032Sgad 125397781Sgad return (retcode); 125494032Sgad } 125594032Sgad 125694032Sgad /* 125794032Sgad * This is the child process, which is the one that executes the 125894032Sgad * given filter. 125994032Sgad */ 126094032Sgad /* 126194032Sgad * If the first parameter has any slashes in it, then change it 126294032Sgad * to point to the first character after the last slash. 126394032Sgad */ 126494032Sgad slash = strrchr(f_av[0], '/'); 126594032Sgad if (slash != NULL) 126694032Sgad f_av[0] = slash + 1; 126794032Sgad /* 126894032Sgad * XXX - in the future, this should setup an explicit list of 126994032Sgad * environment variables and use execve()! 127094032Sgad */ 127194032Sgad 127294032Sgad /* 127394032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 127494032Sgad * is running. Stderr is setup so it points to a temporary errors 127594032Sgad * file, and the parent process will copy that temporary file to 127694032Sgad * the real logfile after the filter completes. 127794032Sgad */ 127894032Sgad dup2(infd, 0); 127994032Sgad dup2(outfd, 1); 128094032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 128194032Sgad if (errfd >= 0) 128294032Sgad dup2(errfd, 2); 128394032Sgad closelog(); 128494032Sgad closeallfds(3); 128594032Sgad execv(f_cmd, f_av); 128695067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 128794032Sgad exit(2); 128894032Sgad /* NOTREACHED */ 128994032Sgad} 129094032Sgad 129194032Sgad/* 12921553Srgrimes * Check to make sure there have been no errors and that both programs 12931553Srgrimes * are in sync with eachother. 12941553Srgrimes * Return non-zero if the connection was lost. 12951553Srgrimes */ 12961553Srgrimesstatic char 129778146Sgadresponse(const struct printer *pp) 12981553Srgrimes{ 12991553Srgrimes char resp; 13001553Srgrimes 13011553Srgrimes if (read(pfd, &resp, 1) != 1) { 130231492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 13031553Srgrimes return(-1); 13041553Srgrimes } 13051553Srgrimes return(resp); 13061553Srgrimes} 13071553Srgrimes 13081553Srgrimes/* 13091553Srgrimes * Banner printing stuff 13101553Srgrimes */ 13111553Srgrimesstatic void 131278146Sgadbanner(struct printer *pp, char *name1, char *name2) 13131553Srgrimes{ 13141553Srgrimes time_t tvec; 13151553Srgrimes 13161553Srgrimes time(&tvec); 131731492Swollman if (!pp->no_formfeed && !pp->tof) 131831492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 131931492Swollman if (pp->short_banner) { /* short banner only */ 13201553Srgrimes if (class[0]) { 13211553Srgrimes (void) write(ofd, class, strlen(class)); 13221553Srgrimes (void) write(ofd, ":", 1); 13231553Srgrimes } 13241553Srgrimes (void) write(ofd, name1, strlen(name1)); 13251553Srgrimes (void) write(ofd, " Job: ", 7); 13261553Srgrimes (void) write(ofd, name2, strlen(name2)); 13271553Srgrimes (void) write(ofd, " Date: ", 8); 13281553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13291553Srgrimes (void) write(ofd, "\n", 1); 13301553Srgrimes } else { /* normal banner */ 13311553Srgrimes (void) write(ofd, "\n\n\n", 3); 133231492Swollman scan_out(pp, ofd, name1, '\0'); 13331553Srgrimes (void) write(ofd, "\n\n", 2); 133431492Swollman scan_out(pp, ofd, name2, '\0'); 13351553Srgrimes if (class[0]) { 13361553Srgrimes (void) write(ofd,"\n\n\n",3); 133731492Swollman scan_out(pp, ofd, class, '\0'); 13381553Srgrimes } 13391553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 13401553Srgrimes (void) write(ofd, name2, strlen(name2)); 13411553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 13421553Srgrimes (void) write(ofd, ctime(&tvec), 24); 13431553Srgrimes (void) write(ofd, "\n", 1); 13441553Srgrimes } 134531492Swollman if (!pp->no_formfeed) 134631492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 134731492Swollman pp->tof = 1; 13481553Srgrimes} 13491553Srgrimes 13501553Srgrimesstatic char * 135178146Sgadscnline(int key, char *p, int c) 13521553Srgrimes{ 135339084Swollman register int scnwidth; 13541553Srgrimes 13551553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 13561553Srgrimes key <<= 1; 13571553Srgrimes *p++ = key & 0200 ? c : BACKGND; 13581553Srgrimes } 13591553Srgrimes return (p); 13601553Srgrimes} 13611553Srgrimes 13621553Srgrimes#define TRC(q) (((q)-' ')&0177) 13631553Srgrimes 13641553Srgrimesstatic void 136578146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 13661553Srgrimes{ 13671553Srgrimes register char *strp; 136839084Swollman register int nchrs, j; 13691553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 13701553Srgrimes int d, scnhgt; 13711553Srgrimes 13721553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 13731553Srgrimes strp = &outbuf[0]; 13741553Srgrimes sp = scsp; 13751553Srgrimes for (nchrs = 0; ; ) { 13761553Srgrimes d = dropit(c = TRC(cc = *sp++)); 13771553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 13781553Srgrimes for (j = WIDTH; --j;) 13791553Srgrimes *strp++ = BACKGND; 13801553Srgrimes else 138131492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 138231492Swollman if (*sp == dlm || *sp == '\0' || 138331492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 13841553Srgrimes break; 13851553Srgrimes *strp++ = BACKGND; 13861553Srgrimes *strp++ = BACKGND; 13871553Srgrimes } 13881553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 13891553Srgrimes ; 13901553Srgrimes strp++; 13918857Srgrimes *strp++ = '\n'; 13921553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 13931553Srgrimes } 13941553Srgrimes} 13951553Srgrimes 13961553Srgrimesstatic int 139778146Sgaddropit(int c) 13981553Srgrimes{ 13991553Srgrimes switch(c) { 14001553Srgrimes 14011553Srgrimes case TRC('_'): 14021553Srgrimes case TRC(';'): 14031553Srgrimes case TRC(','): 14041553Srgrimes case TRC('g'): 14051553Srgrimes case TRC('j'): 14061553Srgrimes case TRC('p'): 14071553Srgrimes case TRC('q'): 14081553Srgrimes case TRC('y'): 14091553Srgrimes return (DROP); 14101553Srgrimes 14111553Srgrimes default: 14121553Srgrimes return (0); 14131553Srgrimes } 14141553Srgrimes} 14151553Srgrimes 14161553Srgrimes/* 14171553Srgrimes * sendmail --- 14181553Srgrimes * tell people about job completion 14191553Srgrimes */ 14201553Srgrimesstatic void 142194038Sgadsendmail(struct printer *pp, char *userid, int bombed) 14221553Srgrimes{ 14231553Srgrimes register int i; 14241553Srgrimes int p[2], s; 142578146Sgad register const char *cp; 14261553Srgrimes struct stat stb; 14271553Srgrimes FILE *fp; 14281553Srgrimes 14291553Srgrimes pipe(p); 143031492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 14311553Srgrimes dup2(p[0], 0); 14328094Sjkh closelog(); 143331492Swollman closeallfds(3); 143427635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 14351553Srgrimes cp++; 143631492Swollman else 14371553Srgrimes cp = _PATH_SENDMAIL; 143879452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 143931492Swollman _exit(0); 14401553Srgrimes } else if (s > 0) { /* parent */ 14411553Srgrimes dup2(p[1], 1); 144294038Sgad printf("To: %s@%s\n", userid, origin_host); 144331492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 144415648Sjoerg *jobname ? jobname : "<unknown>"); 144578300Sgad printf("Reply-To: root@%s\n\n", local_host); 14461553Srgrimes printf("Your printer job "); 14471553Srgrimes if (*jobname) 14481553Srgrimes printf("(%s) ", jobname); 144994040Sgad 14501553Srgrimes switch (bombed) { 14511553Srgrimes case OK: 145294040Sgad cp = "OK"; 14531553Srgrimes printf("\ncompleted successfully\n"); 14541553Srgrimes break; 14551553Srgrimes default: 14561553Srgrimes case FATALERR: 145794040Sgad cp = "FATALERR"; 14581553Srgrimes printf("\ncould not be printed\n"); 14591553Srgrimes break; 14601553Srgrimes case NOACCT: 146194040Sgad cp = "NOACCT"; 146278300Sgad printf("\ncould not be printed without an account on %s\n", 146378300Sgad local_host); 14641553Srgrimes break; 14651553Srgrimes case FILTERERR: 146694040Sgad cp = "FILTERERR"; 146768664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 146868664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 146915648Sjoerg printf("\nhad some errors and may not have printed\n"); 14701553Srgrimes break; 14711553Srgrimes } 147215648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 14731553Srgrimes while ((i = getc(fp)) != EOF) 14741553Srgrimes putchar(i); 14751553Srgrimes (void) fclose(fp); 14761553Srgrimes break; 14771553Srgrimes case ACCESS: 147894040Sgad cp = "ACCESS"; 14791553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 14801553Srgrimes } 14811553Srgrimes fflush(stdout); 14821553Srgrimes (void) close(1); 148331492Swollman } else { 148494038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 148531492Swollman return; 14861553Srgrimes } 14871553Srgrimes (void) close(p[0]); 14881553Srgrimes (void) close(p[1]); 148915648Sjoerg wait(NULL); 149015648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 149194038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 14921553Srgrimes} 14931553Srgrimes 14941553Srgrimes/* 14951553Srgrimes * dofork - fork with retries on failure 14961553Srgrimes */ 14971553Srgrimesstatic int 149878146Sgaddofork(const struct printer *pp, int action) 14991553Srgrimes{ 150080230Sgad int i, fail, forkpid; 150160871Smpp struct passwd *pwd; 15021553Srgrimes 150380230Sgad forkpid = -1; 150480230Sgad if (daemon_uname == NULL) { 150580230Sgad pwd = getpwuid(pp->daemon_user); 150680230Sgad if (pwd == NULL) { 150780230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 150880230Sgad pp->printer, pp->daemon_user); 150980230Sgad goto error_ret; 151080230Sgad } 151180230Sgad daemon_uname = strdup(pwd->pw_name); 151280230Sgad daemon_defgid = pwd->pw_gid; 151380230Sgad } 151480230Sgad 15151553Srgrimes for (i = 0; i < 20; i++) { 151680230Sgad forkpid = fork(); 151780230Sgad if (forkpid < 0) { 15181553Srgrimes sleep((unsigned)(i*i)); 15191553Srgrimes continue; 15201553Srgrimes } 15211553Srgrimes /* 15221553Srgrimes * Child should run as daemon instead of root 15231553Srgrimes */ 152478146Sgad if (forkpid == 0) { 152580230Sgad errno = 0; 152680230Sgad fail = initgroups(daemon_uname, daemon_defgid); 152780230Sgad if (fail) { 152880230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 152980230Sgad pp->printer, daemon_uname, daemon_defgid); 153060871Smpp break; 153160871Smpp } 153280230Sgad fail = setgid(daemon_defgid); 153380230Sgad if (fail) { 153480230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 153580230Sgad pp->printer, daemon_defgid); 153680230Sgad break; 153780230Sgad } 153880230Sgad fail = setuid(pp->daemon_user); 153980230Sgad if (fail) { 154080230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 154180230Sgad pp->printer, pp->daemon_user); 154280230Sgad break; 154380230Sgad } 154460871Smpp } 154580230Sgad return forkpid; 15461553Srgrimes } 15471553Srgrimes 154880230Sgad /* 154980230Sgad * An error occurred. If the error is in the child process, then 155080230Sgad * this routine MUST always exit(). DORETURN only effects how 155180230Sgad * errors should be handled in the parent process. 155280230Sgad */ 155380230Sgaderror_ret: 155480230Sgad if (forkpid == 0) { 155580230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 155680230Sgad pp->printer); 155780230Sgad exit(1); 155880230Sgad } 155980230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 156080230Sgad 156180230Sgad sleep(1); /* throttle errors, as a safety measure */ 15621553Srgrimes switch (action) { 15631553Srgrimes case DORETURN: 156480230Sgad return -1; 15651553Srgrimes default: 15661553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 156780230Sgad /* FALLTHROUGH */ 15681553Srgrimes case DOABORT: 15691553Srgrimes exit(1); 15701553Srgrimes } 15711553Srgrimes /*NOTREACHED*/ 15721553Srgrimes} 15731553Srgrimes 15741553Srgrimes/* 15751553Srgrimes * Kill child processes to abort current job. 15761553Srgrimes */ 15771553Srgrimesstatic void 157878146Sgadabortpr(int signo __unused) 15791553Srgrimes{ 158068664Sgad 158168664Sgad (void) unlink(tempstderr); 15821553Srgrimes kill(0, SIGINT); 15831553Srgrimes if (ofilter > 0) 15841553Srgrimes kill(ofilter, SIGCONT); 15851553Srgrimes while (wait(NULL) > 0) 15861553Srgrimes ; 158724831Sbrian if (ofilter > 0 && tfd != -1) 158824831Sbrian unlink(tfile); 15891553Srgrimes exit(0); 15901553Srgrimes} 15911553Srgrimes 15921553Srgrimesstatic void 159378146Sgadinit(struct printer *pp) 15941553Srgrimes{ 15951553Srgrimes char *s; 15961553Srgrimes 159731492Swollman sprintf(&width[2], "%ld", pp->page_width); 159831492Swollman sprintf(&length[2], "%ld", pp->page_length); 159931492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 160031492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 160131492Swollman if ((s = checkremote(pp)) != 0) { 160231492Swollman syslog(LOG_WARNING, "%s", s); 160331492Swollman free(s); 160431492Swollman } 160531492Swollman} 160631492Swollman 160731492Swollmanvoid 160878146Sgadstartprinting(const char *printer) 160931492Swollman{ 161031492Swollman struct printer myprinter, *pp = &myprinter; 161131492Swollman int status; 161231492Swollman 161331492Swollman init_printer(pp); 161431492Swollman status = getprintcap(printer, pp); 161531492Swollman switch(status) { 161631492Swollman case PCAPERR_OSERR: 161731492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 16181553Srgrimes exit(1); 161931492Swollman case PCAPERR_NOTFOUND: 16201553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 16211553Srgrimes exit(1); 162231492Swollman case PCAPERR_TCLOOP: 162331492Swollman fatal(pp, "potential reference loop detected in printcap file"); 162431492Swollman default: 162531492Swollman break; 162631492Swollman } 162731492Swollman printjob(pp); 16281553Srgrimes} 16291553Srgrimes 16301553Srgrimes/* 16311553Srgrimes * Acquire line printer or remote connection. 16321553Srgrimes */ 16331553Srgrimesstatic void 163478146Sgadopenpr(const struct printer *pp) 16351553Srgrimes{ 163631492Swollman int p[2]; 163715648Sjoerg char *cp; 16381553Srgrimes 163931492Swollman if (pp->remote) { 164031492Swollman openrem(pp); 164194032Sgad /* 164294032Sgad * Lpd does support the setting of 'of=' filters for 164394032Sgad * jobs going to remote machines, but that does not 164494032Sgad * have the same meaning as 'of=' does when handling 164594032Sgad * local print queues. For remote machines, all 'of=' 164694032Sgad * filter processing is handled in sendfile(), and that 164794032Sgad * does not use these global "output filter" variables. 164894032Sgad */ 164994032Sgad ofd = -1; 165094032Sgad ofilter = 0; 165194032Sgad return; 165231492Swollman } else if (*pp->lp) { 165331492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 165431492Swollman opennet(pp); 165515648Sjoerg else 165631492Swollman opentty(pp); 16571553Srgrimes } else { 16581553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 165931492Swollman pp->printer); 16601553Srgrimes exit(1); 16611553Srgrimes } 166215648Sjoerg 16631553Srgrimes /* 16641553Srgrimes * Start up an output filter, if needed. 16651553Srgrimes */ 166631492Swollman if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !ofilter) { 16671553Srgrimes pipe(p); 166831492Swollman if (pp->remote) { 166931492Swollman strcpy(tfile, TFILENAME); 167024831Sbrian tfd = mkstemp(tfile); 167124831Sbrian } 167231492Swollman if ((ofilter = dofork(pp, DOABORT)) == 0) { /* child */ 16731553Srgrimes dup2(p[0], 0); /* pipe is std in */ 167424831Sbrian /* tfile/printer is stdout */ 167531492Swollman dup2(pp->remote ? tfd : pfd, 1); 16768094Sjkh closelog(); 167731492Swollman closeallfds(3); 167831492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 167931492Swollman cp = pp->filters[LPF_OUTPUT]; 16801553Srgrimes else 16811553Srgrimes cp++; 168279452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 168379452Sbrian (char *)0); 168431492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, 168531492Swollman pp->filters[LPF_OUTPUT]); 16861553Srgrimes exit(1); 16871553Srgrimes } 16881553Srgrimes (void) close(p[0]); /* close input side */ 16891553Srgrimes ofd = p[1]; /* use pipe for output */ 16901553Srgrimes } else { 16911553Srgrimes ofd = pfd; 16921553Srgrimes ofilter = 0; 16931553Srgrimes } 16941553Srgrimes} 16951553Srgrimes 169615648Sjoerg/* 169715648Sjoerg * Printer connected directly to the network 169815648Sjoerg * or to a terminal server on the net 169915648Sjoerg */ 170015648Sjoergstatic void 170178146Sgadopennet(const struct printer *pp) 170215648Sjoerg{ 170315648Sjoerg register int i; 170431492Swollman int resp; 170531492Swollman u_long port; 170631492Swollman char *ep; 170730407Sjoerg void (*savealrm)(int); 170815648Sjoerg 170931492Swollman port = strtoul(pp->lp, &ep, 0); 171038470Sbrian if (*ep != '@' || port > 65535) { 171131492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 171231492Swollman pp->lp); 171315648Sjoerg exit(1); 171415648Sjoerg } 171531492Swollman ep++; 171615648Sjoerg 171715648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 171815648Sjoerg resp = -1; 171930407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 172031492Swollman alarm(pp->conn_timeout); 172131492Swollman pfd = getport(pp, ep, port); 172231020Sjoerg alarm(0); 172330407Sjoerg (void)signal(SIGALRM, savealrm); 172415648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 172515648Sjoerg resp = 1; 172615648Sjoerg else if (pfd >= 0) { 172715648Sjoerg /* 172815648Sjoerg * need to delay a bit for rs232 lines 172915648Sjoerg * to stabilize in case printer is 173015648Sjoerg * connected via a terminal server 173115648Sjoerg */ 173215648Sjoerg delay(500); 173315648Sjoerg break; 173415648Sjoerg } 173515648Sjoerg if (i == 1) { 173631492Swollman if (resp < 0) 173731492Swollman pstatus(pp, "waiting for %s to come up", 173831492Swollman pp->lp); 173931492Swollman else 174031492Swollman pstatus(pp, 174131492Swollman "waiting for access to printer on %s", 174231492Swollman pp->lp); 174315648Sjoerg } 174415648Sjoerg sleep(i); 174515648Sjoerg } 174679739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 174715648Sjoerg} 174815648Sjoerg 174915648Sjoerg/* 175015648Sjoerg * Printer is connected to an RS232 port on this host 175115648Sjoerg */ 175215648Sjoergstatic void 175378146Sgadopentty(const struct printer *pp) 175415648Sjoerg{ 175515648Sjoerg register int i; 175615648Sjoerg 175715648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 175831492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 175915648Sjoerg if (pfd >= 0) { 176015648Sjoerg delay(500); 176115648Sjoerg break; 176215648Sjoerg } 176315648Sjoerg if (errno == ENOENT) { 176431492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 176515648Sjoerg exit(1); 176615648Sjoerg } 176715648Sjoerg if (i == 1) 176831492Swollman pstatus(pp, 176931492Swollman "waiting for %s to become ready (offline?)", 177031492Swollman pp->printer); 177115648Sjoerg sleep(i); 177215648Sjoerg } 177315648Sjoerg if (isatty(pfd)) 177431492Swollman setty(pp); 177531492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 177615648Sjoerg} 177715648Sjoerg 177815648Sjoerg/* 177915648Sjoerg * Printer is on a remote host 178015648Sjoerg */ 178115648Sjoergstatic void 178278146Sgadopenrem(const struct printer *pp) 178315648Sjoerg{ 178431492Swollman register int i; 178527748Simp int resp; 178630407Sjoerg void (*savealrm)(int); 178715648Sjoerg 178815648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 178915648Sjoerg resp = -1; 179030407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 179131492Swollman alarm(pp->conn_timeout); 179231492Swollman pfd = getport(pp, pp->remote_host, 0); 179331020Sjoerg alarm(0); 179430407Sjoerg (void)signal(SIGALRM, savealrm); 179515648Sjoerg if (pfd >= 0) { 179631492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 179731492Swollman (char *)0) 179831492Swollman == 2 + strlen(pp->remote_queue)) 179931492Swollman && (resp = response(pp)) == 0) 180015648Sjoerg break; 180115648Sjoerg (void) close(pfd); 180215648Sjoerg } 180315648Sjoerg if (i == 1) { 180415648Sjoerg if (resp < 0) 180531492Swollman pstatus(pp, "waiting for %s to come up", 180631492Swollman pp->remote_host); 180715648Sjoerg else { 180831492Swollman pstatus(pp, 180931492Swollman "waiting for queue to be enabled on %s", 181031492Swollman pp->remote_host); 181115648Sjoerg i = 256; 181215648Sjoerg } 181315648Sjoerg } 181415648Sjoerg sleep(i); 181515648Sjoerg } 181631492Swollman pstatus(pp, "sending to %s", pp->remote_host); 181715648Sjoerg} 181815648Sjoerg 18191553Srgrimes/* 18201553Srgrimes * setup tty lines. 18211553Srgrimes */ 18221553Srgrimesstatic void 182378146Sgadsetty(const struct printer *pp) 18241553Srgrimes{ 182515032Ssef struct termios ttybuf; 18261553Srgrimes 18271553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 182831492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 18291553Srgrimes exit(1); 18301553Srgrimes } 183115032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 183231492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 18331553Srgrimes exit(1); 18341553Srgrimes } 183531492Swollman if (pp->baud_rate > 0) 183631492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 183731492Swollman if (pp->mode_set) { 183831492Swollman char *s = strdup(pp->mode_set), *tmp; 183915032Ssef 184031492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 184139084Swollman (void) msearch(tmp, &ttybuf); 18421553Srgrimes } 18431553Srgrimes } 184431492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 184515032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 184631492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 18471553Srgrimes } 18481553Srgrimes } 18491553Srgrimes} 18501553Srgrimes 18511553Srgrimes#include <stdarg.h> 18521553Srgrimes 185315648Sjoergstatic void 185431492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 18551553Srgrimes{ 185631492Swollman int fd; 185731492Swollman char *buf; 18581553Srgrimes va_list ap; 18591553Srgrimes va_start(ap, msg); 18601553Srgrimes 18611553Srgrimes umask(0); 186231492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 186331492Swollman if (fd < 0) { 186431492Swollman syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->status_file); 18651553Srgrimes exit(1); 18661553Srgrimes } 18671553Srgrimes ftruncate(fd, 0); 186831492Swollman vasprintf(&buf, msg, ap); 18691553Srgrimes va_end(ap); 187031492Swollman writel(fd, buf, "\n", (char *)0); 187131492Swollman close(fd); 187231492Swollman free(buf); 18731553Srgrimes} 187430407Sjoerg 187530407Sjoergvoid 187678146Sgadalarmhandler(int signo __unused) 187730407Sjoerg{ 187878146Sgad /* the signal is ignored */ 187978146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 188030407Sjoerg} 1881