printjob.c revision 160147
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 3. All advertising materials mentioning features or use of this software 151553Srgrimes * must display the following acknowledgement: 161553Srgrimes * This product includes software developed by the University of 171553Srgrimes * California, Berkeley and its contributors. 181553Srgrimes * 4. Neither the name of the University nor the names of its contributors 191553Srgrimes * may be used to endorse or promote products derived from this software 201553Srgrimes * without specific prior written permission. 211553Srgrimes * 221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321553Srgrimes * SUCH DAMAGE. 331553Srgrimes */ 341553Srgrimes 351553Srgrimes#ifndef lint 3631492Swollmanstatic const char copyright[] = 371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 381553Srgrimes The Regents of the University of California. All rights reserved.\n"; 391553Srgrimes#endif /* not lint */ 401553Srgrimes 41117554Sgad#if 0 42117587Sgad#ifndef lint 4315648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 44117587Sgad#endif /* not lint */ 45117554Sgad#endif 461553Srgrimes 47117554Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 48117554Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 160147 2006-07-07 01:12:26Z gad $"); 491553Srgrimes 501553Srgrimes/* 511553Srgrimes * printjob -- print jobs in the queue. 521553Srgrimes * 531553Srgrimes * NOTE: the lock file is used to pass information to lpq and lprm. 541553Srgrimes * it does not need to be removed because file locks are dynamic. 551553Srgrimes */ 561553Srgrimes 571553Srgrimes#include <sys/param.h> 581553Srgrimes#include <sys/wait.h> 591553Srgrimes#include <sys/stat.h> 601553Srgrimes#include <sys/types.h> 611553Srgrimes 621553Srgrimes#include <pwd.h> 631553Srgrimes#include <unistd.h> 641553Srgrimes#include <signal.h> 651553Srgrimes#include <syslog.h> 661553Srgrimes#include <fcntl.h> 671553Srgrimes#include <dirent.h> 681553Srgrimes#include <errno.h> 691553Srgrimes#include <stdio.h> 701553Srgrimes#include <string.h> 711553Srgrimes#include <stdlib.h> 7215032Ssef#include <sys/ioctl.h> 7315032Ssef#include <termios.h> 7415703Sjoerg#include <time.h> 751553Srgrimes#include "lp.h" 761553Srgrimes#include "lp.local.h" 771553Srgrimes#include "pathnames.h" 781553Srgrimes#include "extern.h" 791553Srgrimes 8080230Sgad#define DORETURN 0 /* dofork should return "can't fork" error */ 8180230Sgad#define DOABORT 1 /* dofork should just die if fork() fails */ 821553Srgrimes 831553Srgrimes/* 84119192Sgad * The buffer size to use when reading/writing spool files. 85119192Sgad */ 86119192Sgad#define SPL_BUFSIZ BUFSIZ 87119192Sgad 88119192Sgad/* 891553Srgrimes * Error tokens 901553Srgrimes */ 911553Srgrimes#define REPRINT -2 921553Srgrimes#define ERROR -1 931553Srgrimes#define OK 0 941553Srgrimes#define FATALERR 1 951553Srgrimes#define NOACCT 2 961553Srgrimes#define FILTERERR 3 971553Srgrimes#define ACCESS 4 981553Srgrimes 991553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 1001553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 1011553Srgrimesstatic FILE *cfp; /* control file */ 10297793Sgadstatic pid_t of_pid; /* process id of output filter, if any */ 1031553Srgrimesstatic int child; /* id of any filters */ 10468253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 1051553Srgrimesstatic int lfd; /* lock file descriptor */ 1061553Srgrimesstatic int ofd; /* output filter file descriptor */ 10724831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1081553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1091553Srgrimesstatic int prchild; /* id of pr process */ 1101553Srgrimesstatic char title[80]; /* ``pr'' title */ 11153956Sachestatic char locale[80]; /* ``pr'' locale */ 1121553Srgrimes 11380230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 11480230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 11580230Sgadstatic int daemon_defgid; 11680230Sgad 1171553Srgrimesstatic char class[32]; /* classification field */ 11878300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1191553Srgrimes /* indentation size in static characters */ 1208857Srgrimesstatic char indent[10] = "-i0"; 1211553Srgrimesstatic char jobname[100]; /* job or file name */ 1221553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1231553Srgrimesstatic char logname[32]; /* user's login name */ 1241553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1251553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 12668664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12768664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1281553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12924831Sbrian#define TFILENAME "fltXXXXXX" 13024831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1311553Srgrimes 13278146Sgadstatic void abortpr(int _signo); 13378146Sgadstatic void alarmhandler(int _signo); 13478146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 13578146Sgadstatic int dofork(const struct printer *_pp, int _action); 13678146Sgadstatic int dropit(int _c); 13794032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13894032Sgad int _infd, int _outfd); 13978146Sgadstatic void init(struct printer *_pp); 14078146Sgadstatic void openpr(const struct printer *_pp); 14178146Sgadstatic void opennet(const struct printer *_pp); 14278146Sgadstatic void opentty(const struct printer *_pp); 14378146Sgadstatic void openrem(const struct printer *pp); 14478146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 14578146Sgadstatic int printit(struct printer *_pp, char *_file); 14679739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14779739Sgad __printflike(2, 3); 14878146Sgadstatic char response(const struct printer *_pp); 14978146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 15078146Sgad int _dlm); 15178146Sgadstatic char *scnline(int _key, char *_p, int _c); 15278146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 15395293Sgad char _format, int _copyreq); 15478146Sgadstatic int sendit(struct printer *_pp, char *_file); 15594038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 15678146Sgadstatic void setty(const struct printer *_pp); 157138939Sgadstatic void wait4data(struct printer *_pp, const char *_dfile); 1581553Srgrimes 1591553Srgrimesvoid 16078146Sgadprintjob(struct printer *pp) 1611553Srgrimes{ 1621553Srgrimes struct stat stb; 16368401Sgad register struct jobqueue *q, **qp; 16468401Sgad struct jobqueue **queue; 1651553Srgrimes register int i, nitems; 16668733Sgad off_t pidoff; 16797793Sgad pid_t printpid; 168160147Sgad int errcnt, jobcount, statok, tempfd; 1691553Srgrimes 17068733Sgad jobcount = 0; 17131492Swollman init(pp); /* set up capabilities */ 172118881Sgad (void) write(STDOUT_FILENO, "", 1); /* ack that daemon is started */ 173118881Sgad (void) close(STDERR_FILENO); /* set up log file */ 17431492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 17597792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 17697792Sgad pp->log_file); 1771553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1781553Srgrimes } 1791553Srgrimes setgid(getegid()); 18097793Sgad printpid = getpid(); /* for use with lprm */ 18197793Sgad setpgrp(0, printpid); 18279735Sgad 18379735Sgad /* 18479735Sgad * At initial lpd startup, printjob may be called with various 18579735Sgad * signal handlers in effect. After that initial startup, any 18679735Sgad * calls to printjob will have a *different* set of signal-handlers 18779735Sgad * in effect. Make sure all handlers are the ones we want. 18879735Sgad */ 18979735Sgad signal(SIGCHLD, SIG_DFL); 1901553Srgrimes signal(SIGHUP, abortpr); 1911553Srgrimes signal(SIGINT, abortpr); 1921553Srgrimes signal(SIGQUIT, abortpr); 1931553Srgrimes signal(SIGTERM, abortpr); 1941553Srgrimes 1951553Srgrimes /* 1961553Srgrimes * uses short form file names 1971553Srgrimes */ 19831492Swollman if (chdir(pp->spool_dir) < 0) { 19997792Sgad syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, 20097792Sgad pp->spool_dir); 2011553Srgrimes exit(1); 2021553Srgrimes } 203160147Sgad statok = stat(pp->lock_file, &stb); 204160147Sgad if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS)) 2051553Srgrimes exit(0); /* printing disabled */ 206139035Sgad umask(S_IWOTH); 20731492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 20831492Swollman LOCK_FILE_MODE); 2091553Srgrimes if (lfd < 0) { 21031492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 21131492Swollman exit(0); 21297792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 21397792Sgad pp->lock_file); 2141553Srgrimes exit(1); 2151553Srgrimes } 216160147Sgad /* 217160147Sgad * If the initial call to stat() failed, then lock_file will have 218160147Sgad * been created by open(). Update &stb to match that new file. 219160147Sgad */ 220160147Sgad if (statok != 0) 221160147Sgad statok = stat(pp->lock_file, &stb); 22231492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 22331492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 22497792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 22597792Sgad pp->lock_file); 2261553Srgrimes exit(1); 2271553Srgrimes } 2281553Srgrimes ftruncate(lfd, 0); 2291553Srgrimes /* 2301553Srgrimes * write process id for others to know 2311553Srgrimes */ 23297793Sgad sprintf(line, "%u\n", printpid); 2331553Srgrimes pidoff = i = strlen(line); 2341553Srgrimes if (write(lfd, line, i) != i) { 23597792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 23697792Sgad pp->lock_file); 2371553Srgrimes exit(1); 2381553Srgrimes } 2391553Srgrimes /* 2401553Srgrimes * search the spool directory for work and sort by queue order. 2411553Srgrimes */ 24231492Swollman if ((nitems = getq(pp, &queue)) < 0) { 24331492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 24497792Sgad pp->spool_dir); 2451553Srgrimes exit(1); 2461553Srgrimes } 2471553Srgrimes if (nitems == 0) /* no work to do */ 2481553Srgrimes exit(0); 24931492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 25031492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 25197792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 25297792Sgad pp->lock_file); 2531553Srgrimes } 25468664Sgad 25568664Sgad /* create a file which will be used to hold stderr from filters */ 25668664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 25768664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 25897792Sgad tempstderr); 25968732Sgad exit(1); 26068664Sgad } 26168664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 26268664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 26397792Sgad tempstderr); 26468732Sgad exit(1); 26568664Sgad } 26668664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 26768664Sgad close(tempfd); 26868664Sgad 26931492Swollman openpr(pp); /* open printer or remote */ 2701553Srgrimesagain: 2711553Srgrimes /* 2721553Srgrimes * we found something to do now do it -- 2731553Srgrimes * write the name of the current control file into the lock file 2741553Srgrimes * so the spool queue program can tell what we're working on 2751553Srgrimes */ 2761553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2771553Srgrimes q = *qp++; 27868401Sgad if (stat(q->job_cfname, &stb) < 0) 2791553Srgrimes continue; 28015648Sjoerg errcnt = 0; 2811553Srgrimes restart: 28215648Sjoerg (void) lseek(lfd, pidoff, 0); 28368401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2841553Srgrimes i = strlen(line); 2851553Srgrimes if (write(lfd, line, i) != i) 28697792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 28797792Sgad pp->lock_file); 28831492Swollman if (!pp->remote) 28968401Sgad i = printit(pp, q->job_cfname); 2901553Srgrimes else 29168401Sgad i = sendit(pp, q->job_cfname); 2921553Srgrimes /* 2931553Srgrimes * Check to see if we are supposed to stop printing or 2941553Srgrimes * if we are to rebuild the queue. 2951553Srgrimes */ 2961553Srgrimes if (fstat(lfd, &stb) == 0) { 2971553Srgrimes /* stop printing before starting next job? */ 29831492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2991553Srgrimes goto done; 3001553Srgrimes /* rebuild queue (after lpc topq) */ 30131492Swollman if (stb.st_mode & LFM_RESET_QUE) { 30231492Swollman for (free(q); nitems--; free(q)) 3031553Srgrimes q = *qp++; 30431492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 30531492Swollman < 0) 30697792Sgad syslog(LOG_WARNING, 30797792Sgad "%s: fchmod(%s): %m", 30897792Sgad pp->printer, pp->lock_file); 3091553Srgrimes break; 3101553Srgrimes } 3111553Srgrimes } 31268733Sgad if (i == OK) /* all files of this job printed */ 31368733Sgad jobcount++; 31415648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 31515648Sjoerg /* try reprinting the job */ 31631492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 31797793Sgad if (of_pid > 0) { 31897793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3191553Srgrimes (void) close(ofd); 32097793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3211553Srgrimes ; 32279735Sgad if (i < 0) 32379735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 32497793Sgad pp->printer, of_pid); 32597793Sgad of_pid = 0; 3261553Srgrimes } 3271553Srgrimes (void) close(pfd); /* close printer */ 3281553Srgrimes if (ftruncate(lfd, pidoff) < 0) 32997792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 33097792Sgad pp->printer, pp->lock_file); 33131492Swollman openpr(pp); /* try to reopen printer */ 3321553Srgrimes goto restart; 33315648Sjoerg } else { 33431492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 33597792Sgad pp->printer, 33697792Sgad pp->remote ? "sent to remote host" : "printed", 33797792Sgad q->job_cfname); 33815648Sjoerg if (i == REPRINT) { 33927748Simp /* ensure we don't attempt this job again */ 34068401Sgad (void) unlink(q->job_cfname); 34168401Sgad q->job_cfname[0] = 'd'; 34268401Sgad (void) unlink(q->job_cfname); 34315648Sjoerg if (logname[0]) 34431492Swollman sendmail(pp, logname, FATALERR); 34515648Sjoerg } 3461553Srgrimes } 3471553Srgrimes } 34831492Swollman free(queue); 3491553Srgrimes /* 3501553Srgrimes * search the spool directory for more work. 3511553Srgrimes */ 35231492Swollman if ((nitems = getq(pp, &queue)) < 0) { 35331492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 35497792Sgad pp->spool_dir); 3551553Srgrimes exit(1); 3561553Srgrimes } 3571553Srgrimes if (nitems == 0) { /* no more work to do */ 3581553Srgrimes done: 35968733Sgad if (jobcount > 0) { /* jobs actually printed */ 36031492Swollman if (!pp->no_formfeed && !pp->tof) 36131492Swollman (void) write(ofd, pp->form_feed, 36231492Swollman strlen(pp->form_feed)); 36331492Swollman if (pp->trailer != NULL) /* output trailer */ 36431492Swollman (void) write(ofd, pp->trailer, 36531492Swollman strlen(pp->trailer)); 3661553Srgrimes } 36719202Simp (void) close(ofd); 36819202Simp (void) wait(NULL); 36968664Sgad (void) unlink(tempstderr); 3701553Srgrimes exit(0); 3711553Srgrimes } 3721553Srgrimes goto again; 3731553Srgrimes} 3741553Srgrimes 3751553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3761553Srgrimes 3771553Srgrimeschar ifonts[4][40] = { 3781553Srgrimes _PATH_VFONTR, 3791553Srgrimes _PATH_VFONTI, 3801553Srgrimes _PATH_VFONTB, 3811553Srgrimes _PATH_VFONTS, 3821553Srgrimes}; 3831553Srgrimes 3841553Srgrimes/* 3851553Srgrimes * The remaining part is the reading of the control file (cf) 3861553Srgrimes * and performing the various actions. 3871553Srgrimes */ 3881553Srgrimesstatic int 38978146Sgadprintit(struct printer *pp, char *file) 3901553Srgrimes{ 3911553Srgrimes register int i; 39268734Sgad char *cp; 39368734Sgad int bombed, didignorehdr; 3941553Srgrimes 39568734Sgad bombed = OK; 39668734Sgad didignorehdr = 0; 3971553Srgrimes /* 3981553Srgrimes * open control file; ignore if no longer there. 3991553Srgrimes */ 4001553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 40197792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 40297791Sgad return (OK); 4031553Srgrimes } 4041553Srgrimes /* 4051553Srgrimes * Reset troff fonts. 4061553Srgrimes */ 4071553Srgrimes for (i = 0; i < 4; i++) 4081553Srgrimes strcpy(fonts[i], ifonts[i]); 40931492Swollman sprintf(&width[2], "%ld", pp->page_width); 4101553Srgrimes strcpy(indent+2, "0"); 4111553Srgrimes 41268253Sgad /* initialize job-specific count of datafiles processed */ 41368253Sgad job_dfcnt = 0; 41468253Sgad 4151553Srgrimes /* 4161553Srgrimes * read the control file for work to do 4171553Srgrimes * 4181553Srgrimes * file format -- first character in the line is a command 4191553Srgrimes * rest of the line is the argument. 4201553Srgrimes * valid commands are: 4211553Srgrimes * 4221553Srgrimes * S -- "stat info" for symbolic link protection 4231553Srgrimes * J -- "job name" on banner page 4241553Srgrimes * C -- "class name" on banner page 4251553Srgrimes * L -- "literal" user's name to print on banner 4261553Srgrimes * T -- "title" for pr 4271553Srgrimes * H -- "host name" of machine where lpr was done 4281553Srgrimes * P -- "person" user's login name 4291553Srgrimes * I -- "indent" amount to indent output 43015648Sjoerg * R -- laser dpi "resolution" 4311553Srgrimes * f -- "file name" name of text file to print 4321553Srgrimes * l -- "file name" text file with control chars 43383684Sgad * o -- "file name" postscript file, according to 43483684Sgad * the RFC. Here it is treated like an 'f'. 4351553Srgrimes * p -- "file name" text file to print with pr(1) 4361553Srgrimes * t -- "file name" troff(1) file to print 4371553Srgrimes * n -- "file name" ditroff(1) file to print 4381553Srgrimes * d -- "file name" dvi file to print 4391553Srgrimes * g -- "file name" plot(1G) file to print 4401553Srgrimes * v -- "file name" plain raster file to print 4411553Srgrimes * c -- "file name" cifplot file to print 4421553Srgrimes * 1 -- "R font file" for troff 4431553Srgrimes * 2 -- "I font file" for troff 4441553Srgrimes * 3 -- "B font file" for troff 4451553Srgrimes * 4 -- "S font file" for troff 4461553Srgrimes * N -- "name" of file (used by lpq) 4471553Srgrimes * U -- "unlink" name of file to remove 4481553Srgrimes * (after we print it. (Pass 2 only)). 4491553Srgrimes * M -- "mail" to user when done printing 45053956Sache * Z -- "locale" for pr 4511553Srgrimes * 4521553Srgrimes * getline reads a line and expands tabs to blanks 4531553Srgrimes */ 4541553Srgrimes 4551553Srgrimes /* pass 1 */ 4561553Srgrimes 4571553Srgrimes while (getline(cfp)) 4581553Srgrimes switch (line[0]) { 4591553Srgrimes case 'H': 46078300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 46127748Simp if (class[0] == '\0') { 46280133Sgad strlcpy(class, line+1, sizeof(class)); 46327748Simp } 4641553Srgrimes continue; 4651553Srgrimes 4661553Srgrimes case 'P': 46780133Sgad strlcpy(logname, line + 1, sizeof(logname)); 46831492Swollman if (pp->restricted) { /* restricted */ 4691553Srgrimes if (getpwnam(logname) == NULL) { 4701553Srgrimes bombed = NOACCT; 47131492Swollman sendmail(pp, line+1, bombed); 4721553Srgrimes goto pass2; 4731553Srgrimes } 4741553Srgrimes } 4751553Srgrimes continue; 4761553Srgrimes 4771553Srgrimes case 'S': 4781553Srgrimes cp = line+1; 4791553Srgrimes i = 0; 4801553Srgrimes while (*cp >= '0' && *cp <= '9') 4811553Srgrimes i = i * 10 + (*cp++ - '0'); 4821553Srgrimes fdev = i; 4831553Srgrimes cp++; 4841553Srgrimes i = 0; 4851553Srgrimes while (*cp >= '0' && *cp <= '9') 4861553Srgrimes i = i * 10 + (*cp++ - '0'); 4871553Srgrimes fino = i; 4881553Srgrimes continue; 4891553Srgrimes 4901553Srgrimes case 'J': 49127748Simp if (line[1] != '\0') { 49280133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 49327748Simp } else 4941553Srgrimes strcpy(jobname, " "); 4951553Srgrimes continue; 4961553Srgrimes 4971553Srgrimes case 'C': 4981553Srgrimes if (line[1] != '\0') 49980133Sgad strlcpy(class, line + 1, sizeof(class)); 50080133Sgad else if (class[0] == '\0') { 50180133Sgad /* XXX - why call gethostname instead of 50280133Sgad * just strlcpy'ing local_host? */ 5031553Srgrimes gethostname(class, sizeof(class)); 50480133Sgad class[sizeof(class) - 1] = '\0'; 50580133Sgad } 5061553Srgrimes continue; 5071553Srgrimes 5081553Srgrimes case 'T': /* header title for pr */ 50980133Sgad strlcpy(title, line + 1, sizeof(title)); 5101553Srgrimes continue; 5111553Srgrimes 5121553Srgrimes case 'L': /* identification line */ 51331492Swollman if (!pp->no_header && !pp->header_last) 51431492Swollman banner(pp, line+1, jobname); 5151553Srgrimes continue; 5161553Srgrimes 5171553Srgrimes case '1': /* troff fonts */ 5181553Srgrimes case '2': 5191553Srgrimes case '3': 5201553Srgrimes case '4': 52127748Simp if (line[1] != '\0') { 52280133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 52380133Sgad (size_t)50); 52427748Simp } 5251553Srgrimes continue; 5261553Srgrimes 5271553Srgrimes case 'W': /* page width */ 52880133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5291553Srgrimes continue; 5301553Srgrimes 5311553Srgrimes case 'I': /* indent amount */ 53280133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5331553Srgrimes continue; 5341553Srgrimes 53553956Sache case 'Z': /* locale for pr */ 53680133Sgad strlcpy(locale, line + 1, sizeof(locale)); 53753956Sache continue; 53853956Sache 5391553Srgrimes default: /* some file to print */ 54068467Sgad /* only lowercase cmd-codes include a file-to-print */ 54168467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 54268467Sgad /* ignore any other lines */ 54368467Sgad if (lflag <= 1) 54468467Sgad continue; 54568467Sgad if (!didignorehdr) { 54668467Sgad syslog(LOG_INFO, "%s: in %s :", 54797792Sgad pp->printer, file); 54868467Sgad didignorehdr = 1; 54968467Sgad } 55068467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 55197792Sgad pp->printer, line[0], &line[1]); 55268467Sgad continue; 55368467Sgad } 55468467Sgad i = print(pp, line[0], line+1); 55568467Sgad switch (i) { 5561553Srgrimes case ERROR: 5571553Srgrimes if (bombed == OK) 5581553Srgrimes bombed = FATALERR; 5591553Srgrimes break; 5601553Srgrimes case REPRINT: 5611553Srgrimes (void) fclose(cfp); 56297791Sgad return (REPRINT); 5631553Srgrimes case FILTERERR: 5641553Srgrimes case ACCESS: 5651553Srgrimes bombed = i; 56631492Swollman sendmail(pp, logname, bombed); 5671553Srgrimes } 5681553Srgrimes title[0] = '\0'; 5691553Srgrimes continue; 5701553Srgrimes 5711553Srgrimes case 'N': 5721553Srgrimes case 'U': 5731553Srgrimes case 'M': 57415648Sjoerg case 'R': 5751553Srgrimes continue; 5761553Srgrimes } 5771553Srgrimes 5781553Srgrimes /* pass 2 */ 5791553Srgrimes 5801553Srgrimespass2: 5811553Srgrimes fseek(cfp, 0L, 0); 5821553Srgrimes while (getline(cfp)) 5831553Srgrimes switch (line[0]) { 5841553Srgrimes case 'L': /* identification line */ 58531492Swollman if (!pp->no_header && pp->header_last) 58631492Swollman banner(pp, line+1, jobname); 5871553Srgrimes continue; 5881553Srgrimes 5891553Srgrimes case 'M': 5901553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 59131492Swollman sendmail(pp, line+1, bombed); 5921553Srgrimes continue; 5931553Srgrimes 5941553Srgrimes case 'U': 59527748Simp if (strchr(line+1, '/')) 59627748Simp continue; 5971553Srgrimes (void) unlink(line+1); 5981553Srgrimes } 5991553Srgrimes /* 6001553Srgrimes * clean-up in case another control file exists 6011553Srgrimes */ 6021553Srgrimes (void) fclose(cfp); 6031553Srgrimes (void) unlink(file); 60497791Sgad return (bombed == OK ? OK : ERROR); 6051553Srgrimes} 6061553Srgrimes 6071553Srgrimes/* 6081553Srgrimes * Print a file. 6091553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 6101553Srgrimes * Return -1 if a non-recoverable error occured, 6111553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 6121553Srgrimes * 1 if we should try to reprint this job and 6131553Srgrimes * 0 if all is well. 6141553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 6151553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6161553Srgrimes */ 6171553Srgrimesstatic int 61878146Sgadprint(struct printer *pp, int format, char *file) 6191553Srgrimes{ 62053956Sache register int n, i; 6211553Srgrimes register char *prog; 62231492Swollman int fi, fo; 6231553Srgrimes FILE *fp; 624119192Sgad char *av[15], buf[SPL_BUFSIZ]; 62597793Sgad pid_t wpid; 62697793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6271553Srgrimes struct stat stb; 6281553Srgrimes 629138939Sgad /* Make sure the entire data file has arrived. */ 630138939Sgad wait4data(pp, file); 631138939Sgad 63268467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 63368467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 63497792Sgad pp->printer, file, format); 63597791Sgad return (ERROR); 63668467Sgad } 6371553Srgrimes /* 6381553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6391553Srgrimes * still point to the same file or someone is trying to print 6401553Srgrimes * something he shouldn't. 6411553Srgrimes */ 6421553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6431553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 64497791Sgad return (ACCESS); 64568253Sgad 64668253Sgad job_dfcnt++; /* increment datafile counter for this job */ 64768734Sgad stopped = 0; /* output filter is not stopped */ 64868253Sgad 64968253Sgad /* everything seems OK, start it up */ 65031492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 65131492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 65231492Swollman pp->tof = 1; 6531553Srgrimes } 65431492Swollman if (pp->filters[LPF_INPUT] == NULL 65583684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 65631492Swollman pp->tof = 0; 657119192Sgad while ((n = read(fi, buf, SPL_BUFSIZ)) > 0) 6581553Srgrimes if (write(ofd, buf, n) != n) { 6591553Srgrimes (void) close(fi); 66097791Sgad return (REPRINT); 6611553Srgrimes } 6621553Srgrimes (void) close(fi); 66397791Sgad return (OK); 6641553Srgrimes } 6651553Srgrimes switch (format) { 6661553Srgrimes case 'p': /* print file using 'pr' */ 66731492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6681553Srgrimes prog = _PATH_PR; 66953956Sache i = 0; 67053956Sache av[i++] = "pr"; 67153956Sache av[i++] = width; 67253956Sache av[i++] = length; 67353956Sache av[i++] = "-h"; 67453956Sache av[i++] = *title ? title : " "; 67553956Sache av[i++] = "-L"; 67653956Sache av[i++] = *locale ? locale : "C"; 67753956Sache av[i++] = "-F"; 67853956Sache av[i] = 0; 6791553Srgrimes fo = ofd; 6801553Srgrimes goto start; 6811553Srgrimes } 6821553Srgrimes pipe(p); 68331492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 684118881Sgad dup2(fi, STDIN_FILENO); /* file is stdin */ 685118881Sgad dup2(p[1], STDOUT_FILENO); /* pipe is stdout */ 6868094Sjkh closelog(); 68731492Swollman closeallfds(3); 6881553Srgrimes execl(_PATH_PR, "pr", width, length, 68953956Sache "-h", *title ? title : " ", 69053956Sache "-L", *locale ? locale : "C", 69179452Sbrian "-F", (char *)0); 6921553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6931553Srgrimes exit(2); 6941553Srgrimes } 6951553Srgrimes (void) close(p[1]); /* close output side */ 6961553Srgrimes (void) close(fi); 6971553Srgrimes if (prchild < 0) { 6981553Srgrimes prchild = 0; 6991553Srgrimes (void) close(p[0]); 70097791Sgad return (ERROR); 7011553Srgrimes } 7021553Srgrimes fi = p[0]; /* use pipe for input */ 7031553Srgrimes case 'f': /* print plain text file */ 70431492Swollman prog = pp->filters[LPF_INPUT]; 7051553Srgrimes av[1] = width; 7061553Srgrimes av[2] = length; 7071553Srgrimes av[3] = indent; 7081553Srgrimes n = 4; 7091553Srgrimes break; 71086935Sgad case 'o': /* print postscript file */ 71186935Sgad /* 71286935Sgad * Treat this as a "plain file with control characters", and 71386935Sgad * assume the standard LPF_INPUT filter will recognize that 71486935Sgad * the data is postscript and know what to do with it. These 71586935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 71686935Sgad * (later versions of MacOS 10 will explicitly use 'l') 71786935Sgad * A postscript file can contain binary data, which is why 'l' 71886935Sgad * is somewhat more appropriate than 'f'. 71986935Sgad */ 72086935Sgad /* FALLTHROUGH */ 7211553Srgrimes case 'l': /* like 'f' but pass control characters */ 72231492Swollman prog = pp->filters[LPF_INPUT]; 7231553Srgrimes av[1] = "-c"; 7241553Srgrimes av[2] = width; 7251553Srgrimes av[3] = length; 7261553Srgrimes av[4] = indent; 7271553Srgrimes n = 5; 7281553Srgrimes break; 7291553Srgrimes case 'r': /* print a fortran text file */ 73031492Swollman prog = pp->filters[LPF_FORTRAN]; 7311553Srgrimes av[1] = width; 7321553Srgrimes av[2] = length; 7331553Srgrimes n = 3; 7341553Srgrimes break; 7351553Srgrimes case 't': /* print troff output */ 7361553Srgrimes case 'n': /* print ditroff output */ 7371553Srgrimes case 'd': /* print tex output */ 7381553Srgrimes (void) unlink(".railmag"); 7391553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 74031492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 74197792Sgad pp->printer); 7421553Srgrimes (void) unlink(".railmag"); 7431553Srgrimes } else { 7441553Srgrimes for (n = 0; n < 4; n++) { 7451553Srgrimes if (fonts[n][0] != '/') 7461553Srgrimes (void) write(fo, _PATH_VFONT, 7471553Srgrimes sizeof(_PATH_VFONT) - 1); 7481553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7491553Srgrimes (void) write(fo, "\n", 1); 7501553Srgrimes } 7511553Srgrimes (void) close(fo); 7521553Srgrimes } 75331492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 75431492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 75531492Swollman : pp->filters[LPF_DVI]); 7561553Srgrimes av[1] = pxwidth; 7571553Srgrimes av[2] = pxlength; 7581553Srgrimes n = 3; 7591553Srgrimes break; 7601553Srgrimes case 'c': /* print cifplot output */ 76131492Swollman prog = pp->filters[LPF_CIFPLOT]; 7621553Srgrimes av[1] = pxwidth; 7631553Srgrimes av[2] = pxlength; 7641553Srgrimes n = 3; 7651553Srgrimes break; 7661553Srgrimes case 'g': /* print plot(1G) output */ 76731492Swollman prog = pp->filters[LPF_GRAPH]; 7681553Srgrimes av[1] = pxwidth; 7691553Srgrimes av[2] = pxlength; 7701553Srgrimes n = 3; 7711553Srgrimes break; 7721553Srgrimes case 'v': /* print raster output */ 77331492Swollman prog = pp->filters[LPF_RASTER]; 7741553Srgrimes av[1] = pxwidth; 7751553Srgrimes av[2] = pxlength; 7761553Srgrimes n = 3; 7771553Srgrimes break; 7781553Srgrimes default: 7791553Srgrimes (void) close(fi); 7801553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 78197792Sgad pp->printer, format); 78297791Sgad return (ERROR); 7831553Srgrimes } 78415648Sjoerg if (prog == NULL) { 78515648Sjoerg (void) close(fi); 78615648Sjoerg syslog(LOG_ERR, 78715648Sjoerg "%s: no filter found in printcap for format character '%c'", 78831492Swollman pp->printer, format); 78997791Sgad return (ERROR); 79015648Sjoerg } 79127635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7921553Srgrimes av[0]++; 7931553Srgrimes else 7941553Srgrimes av[0] = prog; 7951553Srgrimes av[n++] = "-n"; 7961553Srgrimes av[n++] = logname; 7971553Srgrimes av[n++] = "-h"; 79878300Sgad av[n++] = origin_host; 79931492Swollman av[n++] = pp->acct_file; 8001553Srgrimes av[n] = 0; 8011553Srgrimes fo = pfd; 80297793Sgad if (of_pid > 0) { /* stop output filter */ 8031553Srgrimes write(ofd, "\031\1", 2); 80497793Sgad while ((wpid = 80597793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 8061553Srgrimes ; 80797793Sgad if (wpid < 0) 80897792Sgad syslog(LOG_WARNING, 80997792Sgad "%s: after stopping 'of', wait3() returned: %m", 81079735Sgad pp->printer); 81197781Sgad else if (!WIFSTOPPED(wstatus)) { 8121553Srgrimes (void) close(fi); 81397781Sgad syslog(LOG_WARNING, "%s: output filter died " 81497781Sgad "(pid=%d retcode=%d termsig=%d)", 81597793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 81697781Sgad WTERMSIG(wstatus)); 81797791Sgad return (REPRINT); 8181553Srgrimes } 8191553Srgrimes stopped++; 8201553Srgrimes } 8211553Srgrimesstart: 82231492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 823118881Sgad dup2(fi, STDIN_FILENO); 824118881Sgad dup2(fo, STDOUT_FILENO); 82568664Sgad /* setup stderr for the filter (child process) 82668664Sgad * so it goes to our temporary errors file */ 82768664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8281553Srgrimes if (n >= 0) 829118881Sgad dup2(n, STDERR_FILENO); 8308094Sjkh closelog(); 83131492Swollman closeallfds(3); 8321553Srgrimes execv(prog, av); 83395067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 83495067Sgad prog); 8351553Srgrimes exit(2); 8361553Srgrimes } 8371553Srgrimes (void) close(fi); 83897789Sgad wstatus_set = 0; 8391553Srgrimes if (child < 0) 84097781Sgad retcode = 100; 84179735Sgad else { 84297793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8431553Srgrimes ; 84497793Sgad if (wpid < 0) { 84597781Sgad retcode = 100; 84697792Sgad syslog(LOG_WARNING, 84797792Sgad "%s: after execv(%s), wait() returned: %m", 84879735Sgad pp->printer, prog); 84997789Sgad } else { 85097789Sgad wstatus_set = 1; 85197781Sgad retcode = WEXITSTATUS(wstatus); 85297789Sgad } 85379735Sgad } 8541553Srgrimes child = 0; 8551553Srgrimes prchild = 0; 8561553Srgrimes if (stopped) { /* restart output filter */ 85797793Sgad if (kill(of_pid, SIGCONT) < 0) { 8581553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8591553Srgrimes exit(1); 8601553Srgrimes } 8611553Srgrimes } 86231492Swollman pp->tof = 0; 8631553Srgrimes 86468664Sgad /* Copy the filter's output to "lf" logfile */ 86568664Sgad if ((fp = fopen(tempstderr, "r"))) { 8661553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8671553Srgrimes fputs(buf, stderr); 8681553Srgrimes fclose(fp); 8691553Srgrimes } 8701553Srgrimes 87197789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 87215648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 87397781Sgad pp->printer, format, WTERMSIG(wstatus)); 87497791Sgad return (ERROR); 8751553Srgrimes } 87697781Sgad switch (retcode) { 8771553Srgrimes case 0: 87831492Swollman pp->tof = 1; 87997791Sgad return (OK); 8801553Srgrimes case 1: 88197791Sgad return (REPRINT); 88215648Sjoerg case 2: 88397791Sgad return (ERROR); 8841553Srgrimes default: 88515648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 88697792Sgad pp->printer, format, retcode); 88797791Sgad return (FILTERERR); 8881553Srgrimes } 8891553Srgrimes} 8901553Srgrimes 8911553Srgrimes/* 8921553Srgrimes * Send the daemon control file (cf) and any data files. 8931553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 8941553Srgrimes * 0 if all is well. 8951553Srgrimes */ 8961553Srgrimesstatic int 89778146Sgadsendit(struct printer *pp, char *file) 8981553Srgrimes{ 89995293Sgad int dfcopies, err, i; 900119192Sgad char *cp, last[sizeof(line)]; 9011553Srgrimes 9021553Srgrimes /* 9031553Srgrimes * open control file 9041553Srgrimes */ 9051553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 90697791Sgad return (OK); 90768253Sgad 90868253Sgad /* initialize job-specific count of datafiles processed */ 90968253Sgad job_dfcnt = 0; 91068253Sgad 9111553Srgrimes /* 9121553Srgrimes * read the control file for work to do 9131553Srgrimes * 9141553Srgrimes * file format -- first character in the line is a command 9151553Srgrimes * rest of the line is the argument. 9161553Srgrimes * commands of interest are: 9171553Srgrimes * 9181553Srgrimes * a-z -- "file name" name of file to print 9191553Srgrimes * U -- "unlink" name of file to remove 9201553Srgrimes * (after we print it. (Pass 2 only)). 9211553Srgrimes */ 9221553Srgrimes 9231553Srgrimes /* 9241553Srgrimes * pass 1 9251553Srgrimes */ 92695293Sgad err = OK; 9271553Srgrimes while (getline(cfp)) { 9281553Srgrimes again: 9291553Srgrimes if (line[0] == 'S') { 9301553Srgrimes cp = line+1; 9311553Srgrimes i = 0; 9321553Srgrimes while (*cp >= '0' && *cp <= '9') 9331553Srgrimes i = i * 10 + (*cp++ - '0'); 9341553Srgrimes fdev = i; 9351553Srgrimes cp++; 9361553Srgrimes i = 0; 9371553Srgrimes while (*cp >= '0' && *cp <= '9') 9381553Srgrimes i = i * 10 + (*cp++ - '0'); 9391553Srgrimes fino = i; 94024831Sbrian } else if (line[0] == 'H') { 94178300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 94268343Sgad if (class[0] == '\0') { 94380133Sgad strlcpy(class, line + 1, sizeof(class)); 94468343Sgad } 94524831Sbrian } else if (line[0] == 'P') { 94680133Sgad strlcpy(logname, line + 1, sizeof(logname)); 94731492Swollman if (pp->restricted) { /* restricted */ 94824831Sbrian if (getpwnam(logname) == NULL) { 94931492Swollman sendmail(pp, line+1, NOACCT); 95024831Sbrian err = ERROR; 95124831Sbrian break; 95224831Sbrian } 95324831Sbrian } 95424831Sbrian } else if (line[0] == 'I') { 95580133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 95624831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 95795293Sgad dfcopies = 1; 9581553Srgrimes strcpy(last, line); 95995293Sgad while ((i = getline(cfp)) != 0) { 96095293Sgad if (strcmp(last, line) != 0) 9611553Srgrimes break; 96295293Sgad dfcopies++; 96395293Sgad } 96495293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9651553Srgrimes case OK: 9661553Srgrimes if (i) 9671553Srgrimes goto again; 9681553Srgrimes break; 9691553Srgrimes case REPRINT: 9701553Srgrimes (void) fclose(cfp); 97197791Sgad return (REPRINT); 9721553Srgrimes case ACCESS: 97331492Swollman sendmail(pp, logname, ACCESS); 9741553Srgrimes case ERROR: 9751553Srgrimes err = ERROR; 9761553Srgrimes } 9771553Srgrimes break; 9781553Srgrimes } 9791553Srgrimes } 98095293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9811553Srgrimes (void) fclose(cfp); 98297791Sgad return (REPRINT); 9831553Srgrimes } 9841553Srgrimes /* 9851553Srgrimes * pass 2 9861553Srgrimes */ 9871553Srgrimes fseek(cfp, 0L, 0); 9881553Srgrimes while (getline(cfp)) 98927748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9901553Srgrimes (void) unlink(line+1); 9911553Srgrimes /* 9921553Srgrimes * clean-up in case another control file exists 9931553Srgrimes */ 9941553Srgrimes (void) fclose(cfp); 9951553Srgrimes (void) unlink(file); 99697791Sgad return (err); 9971553Srgrimes} 9981553Srgrimes 9991553Srgrimes/* 10001553Srgrimes * Send a data file to the remote machine and spool it. 10011553Srgrimes * Return positive if we should try resending. 10021553Srgrimes */ 10031553Srgrimesstatic int 100495293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 10051553Srgrimes{ 100694036Sgad int i, amt; 10071553Srgrimes struct stat stb; 100894032Sgad char *av[15], *filtcmd; 1009119192Sgad char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 101095293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 10111553Srgrimes 1012138939Sgad /* Make sure the entire data file has arrived. */ 1013138939Sgad wait4data(pp, file); 1014138939Sgad 101574124Sgad statrc = lstat(file, &stb); 101674124Sgad if (statrc < 0) { 101774124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 101874124Sgad pp->printer, file); 101994036Sgad return (ERROR); 102074124Sgad } 102194036Sgad sfd = open(file, O_RDONLY); 102294036Sgad if (sfd < 0) { 102374124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 102474124Sgad pp->printer, file); 102594036Sgad return (ERROR); 102674124Sgad } 10271553Srgrimes /* 10281553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10291553Srgrimes * still point to the same file or someone is trying to print something 10301553Srgrimes * he shouldn't. 10311553Srgrimes */ 103294036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 103394036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 103494036Sgad close(sfd); 103594036Sgad return (ACCESS); 103694036Sgad } 103724831Sbrian 103894032Sgad /* Everything seems OK for reading the file, now to send it */ 103994032Sgad filtcmd = NULL; 104024831Sbrian sizerr = 0; 104194032Sgad tfd = -1; 104224831Sbrian if (type == '\3') { 104394032Sgad /* 104494032Sgad * Type == 3 means this is a datafile, not a control file. 104594032Sgad * Increment the counter of data-files in this job, and 104694032Sgad * then check for input or output filters (which are only 104794032Sgad * applied to datafiles, not control files). 104894032Sgad */ 104994032Sgad job_dfcnt++; 105094032Sgad 105194032Sgad /* 105294032Sgad * Note that here we are filtering datafiles, one at a time, 105394032Sgad * as they are sent to the remote machine. Here, the *only* 105494032Sgad * difference between an input filter (`if=') and an output 105594032Sgad * filter (`of=') is the argument list that the filter is 105694032Sgad * started up with. Here, the output filter is executed 105794032Sgad * for each individual file as it is sent. This is not the 105894032Sgad * same as local print queues, where the output filter is 105994032Sgad * started up once, and then all jobs are passed thru that 106094032Sgad * single invocation of the output filter. 106194032Sgad * 106294032Sgad * Also note that a queue for a remote-machine can have an 106394032Sgad * input filter or an output filter, but not both. 106494032Sgad */ 106531492Swollman if (pp->filters[LPF_INPUT]) { 106694032Sgad filtcmd = pp->filters[LPF_INPUT]; 106794032Sgad av[0] = filtcmd; 106894032Sgad narg = 0; 106994032Sgad strcpy(opt_c, "-c"); 107094032Sgad strcpy(opt_h, "-h"); 107194032Sgad strcpy(opt_n, "-n"); 107224831Sbrian if (format == 'l') 107394032Sgad av[++narg] = opt_c; 107494032Sgad av[++narg] = width; 107594032Sgad av[++narg] = length; 107694032Sgad av[++narg] = indent; 107794032Sgad av[++narg] = opt_n; 107894032Sgad av[++narg] = logname; 107994032Sgad av[++narg] = opt_h; 108094032Sgad av[++narg] = origin_host; 108194032Sgad av[++narg] = pp->acct_file; 108294032Sgad av[++narg] = NULL; 108394032Sgad } else if (pp->filters[LPF_OUTPUT]) { 108494032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 108594032Sgad av[0] = filtcmd; 108694032Sgad narg = 0; 108794032Sgad av[++narg] = width; 108894032Sgad av[++narg] = length; 108994032Sgad av[++narg] = NULL; 109024831Sbrian } 109124831Sbrian } 109294032Sgad if (filtcmd) { 109394032Sgad /* 109494032Sgad * If there is an input or output filter, we have to run 109594032Sgad * the datafile thru that filter and store the result as 109694032Sgad * a temporary spool file, because the protocol requires 109794032Sgad * that we send the remote host the file-size before we 109894032Sgad * start to send any of the data. 109994032Sgad */ 110094032Sgad strcpy(tfile, TFILENAME); 110194032Sgad tfd = mkstemp(tfile); 110294032Sgad if (tfd == -1) { 110394032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 110494032Sgad TFILENAME); 110594036Sgad sfres = ERROR; 110694036Sgad goto return_sfres; 110794032Sgad } 110894036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 110924831Sbrian 111094032Sgad /* process the return-code from the filter */ 111194032Sgad switch (filtstat) { 111294032Sgad case 0: 111394032Sgad break; 111494032Sgad case 1: 111594036Sgad sfres = REPRINT; 111694036Sgad goto return_sfres; 111794032Sgad case 2: 111894036Sgad sfres = ERROR; 111994036Sgad goto return_sfres; 112094032Sgad default: 112194032Sgad syslog(LOG_WARNING, 112294032Sgad "%s: filter '%c' exited (retcode=%d)", 112394032Sgad pp->printer, format, filtstat); 112494036Sgad sfres = FILTERERR; 112594036Sgad goto return_sfres; 112694032Sgad } 112794032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 112894032Sgad if (statrc < 0) { 112994032Sgad syslog(LOG_ERR, 113094032Sgad "%s: error processing 'if', fstat(%s): %m", 113194032Sgad pp->printer, tfile); 113294036Sgad sfres = ERROR; 113394036Sgad goto return_sfres; 113494032Sgad } 113594036Sgad close(sfd); 113694036Sgad sfd = tfd; 113794036Sgad lseek(sfd, 0, SEEK_SET); 113894032Sgad } 113994032Sgad 114095293Sgad copycnt = 0; 114195293Sgadsendagain: 114295293Sgad copycnt++; 114395293Sgad 114495293Sgad if (copycnt < 2) 114595293Sgad (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 114695293Sgad else 114795293Sgad (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 114895293Sgad file, copycnt); 11491553Srgrimes amt = strlen(buf); 11501553Srgrimes for (i = 0; ; i++) { 11511553Srgrimes if (write(pfd, buf, amt) != amt || 115231492Swollman (resp = response(pp)) < 0 || resp == '\1') { 115394036Sgad sfres = REPRINT; 115494036Sgad goto return_sfres; 11551553Srgrimes } else if (resp == '\0') 11561553Srgrimes break; 11571553Srgrimes if (i == 0) 115831492Swollman pstatus(pp, 115931492Swollman "no space on remote; waiting for queue to drain"); 11601553Srgrimes if (i == 10) 11611553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 116297792Sgad pp->printer, pp->remote_host); 11631553Srgrimes sleep(5 * 60); 11641553Srgrimes } 11651553Srgrimes if (i) 116631492Swollman pstatus(pp, "sending to %s", pp->remote_host); 116795293Sgad /* 116895293Sgad * XXX - we should change trstat_init()/trstat_write() to include 116995293Sgad * the copycnt in the statistics record it may write. 117095293Sgad */ 117168253Sgad if (type == '\3') 117268253Sgad trstat_init(pp, file, job_dfcnt); 1173119192Sgad for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) { 1174119192Sgad amt = SPL_BUFSIZ; 11751553Srgrimes if (i + amt > stb.st_size) 11761553Srgrimes amt = stb.st_size - i; 117794036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11781553Srgrimes sizerr = 1; 11791553Srgrimes if (write(pfd, buf, amt) != amt) { 118094036Sgad sfres = REPRINT; 118194036Sgad goto return_sfres; 11821553Srgrimes } 11831553Srgrimes } 11841553Srgrimes 11851553Srgrimes if (sizerr) { 118631492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11871553Srgrimes /* tell recvjob to ignore this file */ 11881553Srgrimes (void) write(pfd, "\1", 1); 118994036Sgad sfres = ERROR; 119094036Sgad goto return_sfres; 11911553Srgrimes } 119231492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 119394036Sgad sfres = REPRINT; 119494036Sgad goto return_sfres; 119524831Sbrian } 119695293Sgad if (type == '\3') { 119768253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 119895293Sgad pp->remote_host, origin_host); 119995293Sgad /* 120095293Sgad * Usually we only need to send one copy of a datafile, 120195293Sgad * because the control-file will simply print the same 120295293Sgad * file multiple times. However, some printers ignore 120395293Sgad * the control file, and simply print each data file as 120495293Sgad * it arrives. For such "remote hosts", we need to 120595293Sgad * transfer the same data file multiple times. Such a 120695293Sgad * a host is indicated by adding 'rc' to the printcap 120795293Sgad * entry. 120895293Sgad * XXX - Right now this ONLY works for remote hosts which 120995293Sgad * do ignore the name of the data file, because 121095293Sgad * this sends the file multiple times with slight 121195293Sgad * changes to the filename. To do this right would 121295293Sgad * require that we also rewrite the control file 121395293Sgad * to match those filenames. 121495293Sgad */ 121595293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 121695293Sgad lseek(sfd, 0, SEEK_SET); 121795293Sgad goto sendagain; 121895293Sgad } 121995293Sgad } 122094036Sgad sfres = OK; 122194036Sgad 122294036Sgadreturn_sfres: 122394036Sgad (void)close(sfd); 122494036Sgad if (tfd != -1) { 122594036Sgad /* 122694036Sgad * If tfd is set, then it is the same value as sfd, and 122794036Sgad * therefore it is already closed at this point. All 122894036Sgad * we need to do is remove the temporary file. 122994036Sgad */ 123094036Sgad tfd = -1; 123194036Sgad unlink(tfile); 123294036Sgad } 123394036Sgad return (sfres); 12341553Srgrimes} 12351553Srgrimes 12361553Srgrimes/* 1237138939Sgad * Some print servers send the control-file first, and then start sending the 1238138939Sgad * matching data file(s). That is not the correct order. If some queue is 1239138939Sgad * already printing an active job, then when that job is finished the queue 1240138939Sgad * may proceed to the control file of any incoming print job. This turns 1241138939Sgad * into a race between the process which is receiving the data file, and the 1242138939Sgad * process which is actively printing the very same file. When the remote 1243138939Sgad * server sends files in the wrong order, it is even possible that a queue 1244138939Sgad * will start to print a data file before the file has been created! 1245138939Sgad * 1246138939Sgad * So before we start to print() or send() a data file, we call this routine 1247138939Sgad * to make sure the data file is not still changing in size. Note that this 1248138939Sgad * problem will only happen for jobs arriving from a remote host, and that 1249138939Sgad * the process which has decided to print this job (and is thus making this 1250138939Sgad * check) is *not* the process which is receiving the job. 1251138939Sgad * 1252138939Sgad * A second benefit of this is that any incoming job is guaranteed to appear 1253138939Sgad * in a queue listing for at least a few seconds after it has arrived. Some 1254138939Sgad * lpr implementations get confused if they send a job and it disappears 1255138939Sgad * from the queue before they can check on it. 1256138939Sgad */ 1257138939Sgad#define MAXWAIT_ARRIVE 16 /* max to wait for the file to *exist* */ 1258138939Sgad#define MAXWAIT_4DATA (20*60) /* max to wait for it to stop changing */ 1259138939Sgad#define MINWAIT_4DATA 4 /* This value must be >= 1 */ 1260138939Sgad#define DEBUG_MINWAIT 1 1261138939Sgadstatic void 1262138939Sgadwait4data(struct printer *pp, const char *dfile) 1263138939Sgad{ 1264138939Sgad const char *cp; 1265138939Sgad int statres; 1266138939Sgad size_t dlen, hlen; 1267138939Sgad time_t amtslept, checktime; 1268138939Sgad struct stat statdf; 1269138939Sgad 1270138939Sgad /* Skip these checks if the print job is from the local host. */ 1271138939Sgad dlen = strlen(dfile); 1272138939Sgad hlen = strlen(local_host); 1273138939Sgad if (dlen > hlen) { 1274138939Sgad cp = dfile + dlen - hlen; 1275138939Sgad if (strcmp(cp, local_host) == 0) 1276138939Sgad return; 1277138939Sgad } 1278138939Sgad 1279138939Sgad /* 1280138939Sgad * If this data file does not exist, then wait up to MAXWAIT_ARRIVE 1281138939Sgad * seconds for it to arrive. 1282138939Sgad */ 1283138939Sgad amtslept = 0; 1284138939Sgad statres = stat(dfile, &statdf); 1285138939Sgad while (statres < 0 && amtslept < MAXWAIT_ARRIVE) { 1286138939Sgad if (amtslept == 0) 1287138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1288138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1289138939Sgad statres = stat(dfile, &statdf); 1290138939Sgad } 1291138939Sgad if (statres < 0) { 1292138939Sgad /* The file still does not exist, so just give up on it. */ 1293138939Sgad syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s", 1294138939Sgad pp->printer, dfile); 1295138939Sgad return; 1296138939Sgad } 1297138939Sgad 1298138939Sgad /* 1299138939Sgad * The file exists, so keep waiting until the data file has not 1300138939Sgad * changed for some reasonable amount of time. 1301138939Sgad */ 1302138939Sgad while (statres == 0 && amtslept < MAXWAIT_4DATA) { 1303138939Sgad checktime = time(NULL) - MINWAIT_4DATA; 1304138939Sgad if (statdf.st_mtime <= checktime) 1305138939Sgad break; 1306138939Sgad if (amtslept == 0) 1307138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1308138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1309138939Sgad statres = stat(dfile, &statdf); 1310138939Sgad } 1311138939Sgad 1312138939Sgad if (statres != 0) 1313138939Sgad syslog(LOG_WARNING, "%s: %s disappeared during wait4data()", 1314138939Sgad pp->printer, dfile); 1315138939Sgad else if (amtslept > MAXWAIT_4DATA) 1316138939Sgad syslog(LOG_WARNING, 1317138939Sgad "%s: %s still changing after %lu secs in wait4data()", 1318138939Sgad pp->printer, dfile, (unsigned long)amtslept); 1319138939Sgad#if DEBUG_MINWAIT 1320138939Sgad else if (amtslept > MINWAIT_4DATA) 1321138939Sgad syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)", 1322138939Sgad pp->printer, (unsigned long)amtslept, dfile); 1323138939Sgad#endif 1324138939Sgad} 1325138939Sgad#undef MAXWAIT_ARRIVE 1326138939Sgad#undef MAXWAIT_4DATA 1327138939Sgad#undef MINWAIT_4DATA 1328138939Sgad 1329138939Sgad/* 133094032Sgad * This routine is called to execute one of the filters as was 133194036Sgad * specified in a printcap entry. While the child-process will read 133294036Sgad * all of 'infd', it is up to the caller to close that file descriptor 133394036Sgad * in the parent process. 133494032Sgad */ 133594032Sgadstatic int 133694032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 133794032Sgad{ 133897793Sgad pid_t fpid, wpid; 133997793Sgad int errfd, retcode, wstatus; 134094032Sgad FILE *errfp; 134194032Sgad char buf[BUFSIZ], *slash; 134294032Sgad 134394032Sgad fpid = dofork(pp, DORETURN); 134494032Sgad if (fpid != 0) { 134594032Sgad /* 134694032Sgad * This is the parent process, which just waits for the child 134794032Sgad * to complete and then returns the result. Note that it is 134894032Sgad * the child process which reads the input stream. 134994032Sgad */ 135094032Sgad if (fpid < 0) 135197781Sgad retcode = 100; 135294032Sgad else { 135397781Sgad while ((wpid = wait(&wstatus)) > 0 && 135494032Sgad wpid != fpid) 135594032Sgad ; 135694032Sgad if (wpid < 0) { 135797781Sgad retcode = 100; 135894032Sgad syslog(LOG_WARNING, 135994032Sgad "%s: after execv(%s), wait() returned: %m", 136094032Sgad pp->printer, f_cmd); 136197781Sgad } else 136297781Sgad retcode = WEXITSTATUS(wstatus); 136394032Sgad } 136494032Sgad 136594032Sgad /* 136694032Sgad * Copy everything the filter wrote to stderr from our 136794032Sgad * temporary errors file to the "lf=" logfile. 136894032Sgad */ 136994032Sgad errfp = fopen(tempstderr, "r"); 137094032Sgad if (errfp) { 137194032Sgad while (fgets(buf, sizeof(buf), errfp)) 137294032Sgad fputs(buf, stderr); 137394032Sgad fclose(errfp); 137494032Sgad } 137594032Sgad 137697781Sgad return (retcode); 137794032Sgad } 137894032Sgad 137994032Sgad /* 138094032Sgad * This is the child process, which is the one that executes the 138194032Sgad * given filter. 138294032Sgad */ 138394032Sgad /* 138494032Sgad * If the first parameter has any slashes in it, then change it 138594032Sgad * to point to the first character after the last slash. 138694032Sgad */ 138794032Sgad slash = strrchr(f_av[0], '/'); 138894032Sgad if (slash != NULL) 138994032Sgad f_av[0] = slash + 1; 139094032Sgad /* 139194032Sgad * XXX - in the future, this should setup an explicit list of 139294032Sgad * environment variables and use execve()! 139394032Sgad */ 139494032Sgad 139594032Sgad /* 139694032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 139794032Sgad * is running. Stderr is setup so it points to a temporary errors 139894032Sgad * file, and the parent process will copy that temporary file to 139994032Sgad * the real logfile after the filter completes. 140094032Sgad */ 1401118881Sgad dup2(infd, STDIN_FILENO); 1402118881Sgad dup2(outfd, STDOUT_FILENO); 140394032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 140494032Sgad if (errfd >= 0) 1405118881Sgad dup2(errfd, STDERR_FILENO); 140694032Sgad closelog(); 140794032Sgad closeallfds(3); 140894032Sgad execv(f_cmd, f_av); 140995067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 141094032Sgad exit(2); 141194032Sgad /* NOTREACHED */ 141294032Sgad} 141394032Sgad 141494032Sgad/* 14151553Srgrimes * Check to make sure there have been no errors and that both programs 14161553Srgrimes * are in sync with eachother. 14171553Srgrimes * Return non-zero if the connection was lost. 14181553Srgrimes */ 14191553Srgrimesstatic char 142078146Sgadresponse(const struct printer *pp) 14211553Srgrimes{ 14221553Srgrimes char resp; 14231553Srgrimes 14241553Srgrimes if (read(pfd, &resp, 1) != 1) { 142531492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 142697791Sgad return (-1); 14271553Srgrimes } 142897791Sgad return (resp); 14291553Srgrimes} 14301553Srgrimes 14311553Srgrimes/* 14321553Srgrimes * Banner printing stuff 14331553Srgrimes */ 14341553Srgrimesstatic void 143578146Sgadbanner(struct printer *pp, char *name1, char *name2) 14361553Srgrimes{ 14371553Srgrimes time_t tvec; 14381553Srgrimes 14391553Srgrimes time(&tvec); 144031492Swollman if (!pp->no_formfeed && !pp->tof) 144131492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 144231492Swollman if (pp->short_banner) { /* short banner only */ 14431553Srgrimes if (class[0]) { 14441553Srgrimes (void) write(ofd, class, strlen(class)); 14451553Srgrimes (void) write(ofd, ":", 1); 14461553Srgrimes } 14471553Srgrimes (void) write(ofd, name1, strlen(name1)); 14481553Srgrimes (void) write(ofd, " Job: ", 7); 14491553Srgrimes (void) write(ofd, name2, strlen(name2)); 14501553Srgrimes (void) write(ofd, " Date: ", 8); 14511553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14521553Srgrimes (void) write(ofd, "\n", 1); 14531553Srgrimes } else { /* normal banner */ 14541553Srgrimes (void) write(ofd, "\n\n\n", 3); 145531492Swollman scan_out(pp, ofd, name1, '\0'); 14561553Srgrimes (void) write(ofd, "\n\n", 2); 145731492Swollman scan_out(pp, ofd, name2, '\0'); 14581553Srgrimes if (class[0]) { 14591553Srgrimes (void) write(ofd,"\n\n\n",3); 146031492Swollman scan_out(pp, ofd, class, '\0'); 14611553Srgrimes } 14621553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 14631553Srgrimes (void) write(ofd, name2, strlen(name2)); 14641553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 14651553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14661553Srgrimes (void) write(ofd, "\n", 1); 14671553Srgrimes } 146831492Swollman if (!pp->no_formfeed) 146931492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 147031492Swollman pp->tof = 1; 14711553Srgrimes} 14721553Srgrimes 14731553Srgrimesstatic char * 147478146Sgadscnline(int key, char *p, int c) 14751553Srgrimes{ 147639084Swollman register int scnwidth; 14771553Srgrimes 14781553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 14791553Srgrimes key <<= 1; 14801553Srgrimes *p++ = key & 0200 ? c : BACKGND; 14811553Srgrimes } 14821553Srgrimes return (p); 14831553Srgrimes} 14841553Srgrimes 14851553Srgrimes#define TRC(q) (((q)-' ')&0177) 14861553Srgrimes 14871553Srgrimesstatic void 148878146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 14891553Srgrimes{ 14901553Srgrimes register char *strp; 149139084Swollman register int nchrs, j; 14921553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 14931553Srgrimes int d, scnhgt; 14941553Srgrimes 14951553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 14961553Srgrimes strp = &outbuf[0]; 14971553Srgrimes sp = scsp; 14981553Srgrimes for (nchrs = 0; ; ) { 14991553Srgrimes d = dropit(c = TRC(cc = *sp++)); 15001553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 15011553Srgrimes for (j = WIDTH; --j;) 15021553Srgrimes *strp++ = BACKGND; 15031553Srgrimes else 150431492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 150531492Swollman if (*sp == dlm || *sp == '\0' || 150631492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 15071553Srgrimes break; 15081553Srgrimes *strp++ = BACKGND; 15091553Srgrimes *strp++ = BACKGND; 15101553Srgrimes } 15111553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 15121553Srgrimes ; 15131553Srgrimes strp++; 15148857Srgrimes *strp++ = '\n'; 15151553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 15161553Srgrimes } 15171553Srgrimes} 15181553Srgrimes 15191553Srgrimesstatic int 152078146Sgaddropit(int c) 15211553Srgrimes{ 15221553Srgrimes switch(c) { 15231553Srgrimes 15241553Srgrimes case TRC('_'): 15251553Srgrimes case TRC(';'): 15261553Srgrimes case TRC(','): 15271553Srgrimes case TRC('g'): 15281553Srgrimes case TRC('j'): 15291553Srgrimes case TRC('p'): 15301553Srgrimes case TRC('q'): 15311553Srgrimes case TRC('y'): 15321553Srgrimes return (DROP); 15331553Srgrimes 15341553Srgrimes default: 15351553Srgrimes return (0); 15361553Srgrimes } 15371553Srgrimes} 15381553Srgrimes 15391553Srgrimes/* 15401553Srgrimes * sendmail --- 15411553Srgrimes * tell people about job completion 15421553Srgrimes */ 15431553Srgrimesstatic void 154494038Sgadsendmail(struct printer *pp, char *userid, int bombed) 15451553Srgrimes{ 15461553Srgrimes register int i; 15471553Srgrimes int p[2], s; 154878146Sgad register const char *cp; 15491553Srgrimes struct stat stb; 15501553Srgrimes FILE *fp; 15511553Srgrimes 15521553Srgrimes pipe(p); 155331492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1554118881Sgad dup2(p[0], STDIN_FILENO); 15558094Sjkh closelog(); 155631492Swollman closeallfds(3); 155727635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 15581553Srgrimes cp++; 155931492Swollman else 15601553Srgrimes cp = _PATH_SENDMAIL; 156179452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 156231492Swollman _exit(0); 15631553Srgrimes } else if (s > 0) { /* parent */ 1564118881Sgad dup2(p[1], STDOUT_FILENO); 156594038Sgad printf("To: %s@%s\n", userid, origin_host); 156631492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 156715648Sjoerg *jobname ? jobname : "<unknown>"); 156878300Sgad printf("Reply-To: root@%s\n\n", local_host); 15691553Srgrimes printf("Your printer job "); 15701553Srgrimes if (*jobname) 15711553Srgrimes printf("(%s) ", jobname); 157294040Sgad 15731553Srgrimes switch (bombed) { 15741553Srgrimes case OK: 157594040Sgad cp = "OK"; 15761553Srgrimes printf("\ncompleted successfully\n"); 15771553Srgrimes break; 15781553Srgrimes default: 15791553Srgrimes case FATALERR: 158094040Sgad cp = "FATALERR"; 15811553Srgrimes printf("\ncould not be printed\n"); 15821553Srgrimes break; 15831553Srgrimes case NOACCT: 158494040Sgad cp = "NOACCT"; 158578300Sgad printf("\ncould not be printed without an account on %s\n", 158678300Sgad local_host); 15871553Srgrimes break; 15881553Srgrimes case FILTERERR: 158994040Sgad cp = "FILTERERR"; 159068664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 159168664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 159215648Sjoerg printf("\nhad some errors and may not have printed\n"); 15931553Srgrimes break; 15941553Srgrimes } 159515648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 15961553Srgrimes while ((i = getc(fp)) != EOF) 15971553Srgrimes putchar(i); 15981553Srgrimes (void) fclose(fp); 15991553Srgrimes break; 16001553Srgrimes case ACCESS: 160194040Sgad cp = "ACCESS"; 16021553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 16031553Srgrimes } 16041553Srgrimes fflush(stdout); 1605118881Sgad (void) close(STDOUT_FILENO); 160631492Swollman } else { 160794038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 160831492Swollman return; 16091553Srgrimes } 16101553Srgrimes (void) close(p[0]); 16111553Srgrimes (void) close(p[1]); 161215648Sjoerg wait(NULL); 161315648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 161494038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 16151553Srgrimes} 16161553Srgrimes 16171553Srgrimes/* 16181553Srgrimes * dofork - fork with retries on failure 16191553Srgrimes */ 16201553Srgrimesstatic int 162178146Sgaddofork(const struct printer *pp, int action) 16221553Srgrimes{ 162397793Sgad pid_t forkpid; 162497793Sgad int i, fail; 162560871Smpp struct passwd *pwd; 16261553Srgrimes 162780230Sgad forkpid = -1; 162880230Sgad if (daemon_uname == NULL) { 162980230Sgad pwd = getpwuid(pp->daemon_user); 163080230Sgad if (pwd == NULL) { 163180230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 163280230Sgad pp->printer, pp->daemon_user); 163380230Sgad goto error_ret; 163480230Sgad } 163580230Sgad daemon_uname = strdup(pwd->pw_name); 163680230Sgad daemon_defgid = pwd->pw_gid; 163780230Sgad } 163880230Sgad 16391553Srgrimes for (i = 0; i < 20; i++) { 164080230Sgad forkpid = fork(); 164180230Sgad if (forkpid < 0) { 16421553Srgrimes sleep((unsigned)(i*i)); 16431553Srgrimes continue; 16441553Srgrimes } 16451553Srgrimes /* 16461553Srgrimes * Child should run as daemon instead of root 16471553Srgrimes */ 164878146Sgad if (forkpid == 0) { 164980230Sgad errno = 0; 165080230Sgad fail = initgroups(daemon_uname, daemon_defgid); 165180230Sgad if (fail) { 165280230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 165380230Sgad pp->printer, daemon_uname, daemon_defgid); 165460871Smpp break; 165560871Smpp } 165680230Sgad fail = setgid(daemon_defgid); 165780230Sgad if (fail) { 165880230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 165980230Sgad pp->printer, daemon_defgid); 166080230Sgad break; 166180230Sgad } 166280230Sgad fail = setuid(pp->daemon_user); 166380230Sgad if (fail) { 166480230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 166580230Sgad pp->printer, pp->daemon_user); 166680230Sgad break; 166780230Sgad } 166860871Smpp } 166997791Sgad return (forkpid); 16701553Srgrimes } 16711553Srgrimes 167280230Sgad /* 167380230Sgad * An error occurred. If the error is in the child process, then 167480230Sgad * this routine MUST always exit(). DORETURN only effects how 167580230Sgad * errors should be handled in the parent process. 167680230Sgad */ 167780230Sgaderror_ret: 167880230Sgad if (forkpid == 0) { 167980230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 168080230Sgad pp->printer); 168180230Sgad exit(1); 168280230Sgad } 168380230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 168480230Sgad 168580230Sgad sleep(1); /* throttle errors, as a safety measure */ 16861553Srgrimes switch (action) { 16871553Srgrimes case DORETURN: 168897791Sgad return (-1); 16891553Srgrimes default: 16901553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 169180230Sgad /* FALLTHROUGH */ 16921553Srgrimes case DOABORT: 16931553Srgrimes exit(1); 16941553Srgrimes } 16951553Srgrimes /*NOTREACHED*/ 16961553Srgrimes} 16971553Srgrimes 16981553Srgrimes/* 16991553Srgrimes * Kill child processes to abort current job. 17001553Srgrimes */ 17011553Srgrimesstatic void 170278146Sgadabortpr(int signo __unused) 17031553Srgrimes{ 170468664Sgad 170568664Sgad (void) unlink(tempstderr); 17061553Srgrimes kill(0, SIGINT); 170797793Sgad if (of_pid > 0) 170897793Sgad kill(of_pid, SIGCONT); 17091553Srgrimes while (wait(NULL) > 0) 17101553Srgrimes ; 171197793Sgad if (of_pid > 0 && tfd != -1) 171224831Sbrian unlink(tfile); 17131553Srgrimes exit(0); 17141553Srgrimes} 17151553Srgrimes 17161553Srgrimesstatic void 171778146Sgadinit(struct printer *pp) 17181553Srgrimes{ 17191553Srgrimes char *s; 17201553Srgrimes 172131492Swollman sprintf(&width[2], "%ld", pp->page_width); 172231492Swollman sprintf(&length[2], "%ld", pp->page_length); 172331492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 172431492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 172531492Swollman if ((s = checkremote(pp)) != 0) { 172631492Swollman syslog(LOG_WARNING, "%s", s); 172731492Swollman free(s); 172831492Swollman } 172931492Swollman} 173031492Swollman 173131492Swollmanvoid 173278146Sgadstartprinting(const char *printer) 173331492Swollman{ 173431492Swollman struct printer myprinter, *pp = &myprinter; 173531492Swollman int status; 173631492Swollman 173731492Swollman init_printer(pp); 173831492Swollman status = getprintcap(printer, pp); 173931492Swollman switch(status) { 174031492Swollman case PCAPERR_OSERR: 174131492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 17421553Srgrimes exit(1); 174331492Swollman case PCAPERR_NOTFOUND: 17441553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 17451553Srgrimes exit(1); 174631492Swollman case PCAPERR_TCLOOP: 174731492Swollman fatal(pp, "potential reference loop detected in printcap file"); 174831492Swollman default: 174931492Swollman break; 175031492Swollman } 175131492Swollman printjob(pp); 17521553Srgrimes} 17531553Srgrimes 17541553Srgrimes/* 17551553Srgrimes * Acquire line printer or remote connection. 17561553Srgrimes */ 17571553Srgrimesstatic void 175878146Sgadopenpr(const struct printer *pp) 17591553Srgrimes{ 176031492Swollman int p[2]; 176115648Sjoerg char *cp; 17621553Srgrimes 176331492Swollman if (pp->remote) { 176431492Swollman openrem(pp); 176594032Sgad /* 176694032Sgad * Lpd does support the setting of 'of=' filters for 176794032Sgad * jobs going to remote machines, but that does not 176894032Sgad * have the same meaning as 'of=' does when handling 176994032Sgad * local print queues. For remote machines, all 'of=' 177094032Sgad * filter processing is handled in sendfile(), and that 177194032Sgad * does not use these global "output filter" variables. 177294032Sgad */ 177394032Sgad ofd = -1; 177497793Sgad of_pid = 0; 177594032Sgad return; 177631492Swollman } else if (*pp->lp) { 177731492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 177831492Swollman opennet(pp); 177915648Sjoerg else 178031492Swollman opentty(pp); 17811553Srgrimes } else { 17821553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 178397792Sgad pp->printer); 17841553Srgrimes exit(1); 17851553Srgrimes } 178615648Sjoerg 17871553Srgrimes /* 17881553Srgrimes * Start up an output filter, if needed. 17891553Srgrimes */ 179097793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 17911553Srgrimes pipe(p); 179231492Swollman if (pp->remote) { 179331492Swollman strcpy(tfile, TFILENAME); 179424831Sbrian tfd = mkstemp(tfile); 179524831Sbrian } 179697793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 1797118881Sgad dup2(p[0], STDIN_FILENO); /* pipe is std in */ 179824831Sbrian /* tfile/printer is stdout */ 1799118881Sgad dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); 18008094Sjkh closelog(); 180131492Swollman closeallfds(3); 180231492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 180331492Swollman cp = pp->filters[LPF_OUTPUT]; 18041553Srgrimes else 18051553Srgrimes cp++; 180679452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 180779452Sbrian (char *)0); 180897792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 180997792Sgad pp->filters[LPF_OUTPUT]); 18101553Srgrimes exit(1); 18111553Srgrimes } 18121553Srgrimes (void) close(p[0]); /* close input side */ 18131553Srgrimes ofd = p[1]; /* use pipe for output */ 18141553Srgrimes } else { 18151553Srgrimes ofd = pfd; 181697793Sgad of_pid = 0; 18171553Srgrimes } 18181553Srgrimes} 18191553Srgrimes 182015648Sjoerg/* 182115648Sjoerg * Printer connected directly to the network 182215648Sjoerg * or to a terminal server on the net 182315648Sjoerg */ 182415648Sjoergstatic void 182578146Sgadopennet(const struct printer *pp) 182615648Sjoerg{ 182715648Sjoerg register int i; 182831492Swollman int resp; 182931492Swollman u_long port; 183031492Swollman char *ep; 183130407Sjoerg void (*savealrm)(int); 183215648Sjoerg 183331492Swollman port = strtoul(pp->lp, &ep, 0); 183438470Sbrian if (*ep != '@' || port > 65535) { 183531492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 183697792Sgad pp->lp); 183715648Sjoerg exit(1); 183815648Sjoerg } 183931492Swollman ep++; 184015648Sjoerg 184115648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 184215648Sjoerg resp = -1; 184330407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 184431492Swollman alarm(pp->conn_timeout); 184531492Swollman pfd = getport(pp, ep, port); 184631020Sjoerg alarm(0); 184730407Sjoerg (void)signal(SIGALRM, savealrm); 184815648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 184915648Sjoerg resp = 1; 185015648Sjoerg else if (pfd >= 0) { 185115648Sjoerg /* 185215648Sjoerg * need to delay a bit for rs232 lines 185315648Sjoerg * to stabilize in case printer is 185415648Sjoerg * connected via a terminal server 185515648Sjoerg */ 185615648Sjoerg delay(500); 185715648Sjoerg break; 185815648Sjoerg } 185915648Sjoerg if (i == 1) { 186031492Swollman if (resp < 0) 186131492Swollman pstatus(pp, "waiting for %s to come up", 186231492Swollman pp->lp); 186331492Swollman else 186431492Swollman pstatus(pp, 186531492Swollman "waiting for access to printer on %s", 186631492Swollman pp->lp); 186715648Sjoerg } 186815648Sjoerg sleep(i); 186915648Sjoerg } 187079739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 187115648Sjoerg} 187215648Sjoerg 187315648Sjoerg/* 187415648Sjoerg * Printer is connected to an RS232 port on this host 187515648Sjoerg */ 187615648Sjoergstatic void 187778146Sgadopentty(const struct printer *pp) 187815648Sjoerg{ 187915648Sjoerg register int i; 188015648Sjoerg 188115648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 188231492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 188315648Sjoerg if (pfd >= 0) { 188415648Sjoerg delay(500); 188515648Sjoerg break; 188615648Sjoerg } 188715648Sjoerg if (errno == ENOENT) { 188831492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 188915648Sjoerg exit(1); 189015648Sjoerg } 189115648Sjoerg if (i == 1) 189231492Swollman pstatus(pp, 189331492Swollman "waiting for %s to become ready (offline?)", 189431492Swollman pp->printer); 189515648Sjoerg sleep(i); 189615648Sjoerg } 189715648Sjoerg if (isatty(pfd)) 189831492Swollman setty(pp); 189931492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 190015648Sjoerg} 190115648Sjoerg 190215648Sjoerg/* 190315648Sjoerg * Printer is on a remote host 190415648Sjoerg */ 190515648Sjoergstatic void 190678146Sgadopenrem(const struct printer *pp) 190715648Sjoerg{ 190831492Swollman register int i; 190927748Simp int resp; 191030407Sjoerg void (*savealrm)(int); 191115648Sjoerg 191215648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 191315648Sjoerg resp = -1; 191430407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 191531492Swollman alarm(pp->conn_timeout); 191631492Swollman pfd = getport(pp, pp->remote_host, 0); 191731020Sjoerg alarm(0); 191830407Sjoerg (void)signal(SIGALRM, savealrm); 191915648Sjoerg if (pfd >= 0) { 192031492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 192131492Swollman (char *)0) 192231492Swollman == 2 + strlen(pp->remote_queue)) 192331492Swollman && (resp = response(pp)) == 0) 192415648Sjoerg break; 192515648Sjoerg (void) close(pfd); 192615648Sjoerg } 192715648Sjoerg if (i == 1) { 192815648Sjoerg if (resp < 0) 192931492Swollman pstatus(pp, "waiting for %s to come up", 193031492Swollman pp->remote_host); 193115648Sjoerg else { 193231492Swollman pstatus(pp, 193331492Swollman "waiting for queue to be enabled on %s", 193431492Swollman pp->remote_host); 193515648Sjoerg i = 256; 193615648Sjoerg } 193715648Sjoerg } 193815648Sjoerg sleep(i); 193915648Sjoerg } 194031492Swollman pstatus(pp, "sending to %s", pp->remote_host); 194115648Sjoerg} 194215648Sjoerg 19431553Srgrimes/* 19441553Srgrimes * setup tty lines. 19451553Srgrimes */ 19461553Srgrimesstatic void 194778146Sgadsetty(const struct printer *pp) 19481553Srgrimes{ 194915032Ssef struct termios ttybuf; 19501553Srgrimes 19511553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 195231492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 19531553Srgrimes exit(1); 19541553Srgrimes } 195515032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 195631492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 19571553Srgrimes exit(1); 19581553Srgrimes } 195931492Swollman if (pp->baud_rate > 0) 196031492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 196131492Swollman if (pp->mode_set) { 196231492Swollman char *s = strdup(pp->mode_set), *tmp; 196315032Ssef 196431492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 196539084Swollman (void) msearch(tmp, &ttybuf); 19661553Srgrimes } 19671553Srgrimes } 196831492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 196915032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 197031492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 19711553Srgrimes } 19721553Srgrimes } 19731553Srgrimes} 19741553Srgrimes 19751553Srgrimes#include <stdarg.h> 19761553Srgrimes 197715648Sjoergstatic void 197831492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 19791553Srgrimes{ 198031492Swollman int fd; 198131492Swollman char *buf; 19821553Srgrimes va_list ap; 19831553Srgrimes va_start(ap, msg); 19841553Srgrimes 1985139035Sgad umask(S_IWOTH); 198631492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 198731492Swollman if (fd < 0) { 198897792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 198997792Sgad pp->status_file); 19901553Srgrimes exit(1); 19911553Srgrimes } 19921553Srgrimes ftruncate(fd, 0); 199331492Swollman vasprintf(&buf, msg, ap); 19941553Srgrimes va_end(ap); 199531492Swollman writel(fd, buf, "\n", (char *)0); 199631492Swollman close(fd); 199731492Swollman free(buf); 19981553Srgrimes} 199930407Sjoerg 200030407Sjoergvoid 200178146Sgadalarmhandler(int signo __unused) 200230407Sjoerg{ 200378146Sgad /* the signal is ignored */ 200478146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 200530407Sjoerg} 2006