printjob.c revision 139035
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 139035 2004-12-19 05:14:29Z 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; 16868733Sgad int errcnt, jobcount, 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 } 20331492Swollman if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS)) 2041553Srgrimes exit(0); /* printing disabled */ 205139035Sgad umask(S_IWOTH); 20631492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 20731492Swollman LOCK_FILE_MODE); 2081553Srgrimes if (lfd < 0) { 20931492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 21031492Swollman exit(0); 21197792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 21297792Sgad pp->lock_file); 2131553Srgrimes exit(1); 2141553Srgrimes } 21531492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 21631492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 21797792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 21897792Sgad pp->lock_file); 2191553Srgrimes exit(1); 2201553Srgrimes } 2211553Srgrimes ftruncate(lfd, 0); 2221553Srgrimes /* 2231553Srgrimes * write process id for others to know 2241553Srgrimes */ 22597793Sgad sprintf(line, "%u\n", printpid); 2261553Srgrimes pidoff = i = strlen(line); 2271553Srgrimes if (write(lfd, line, i) != i) { 22897792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 22997792Sgad pp->lock_file); 2301553Srgrimes exit(1); 2311553Srgrimes } 2321553Srgrimes /* 2331553Srgrimes * search the spool directory for work and sort by queue order. 2341553Srgrimes */ 23531492Swollman if ((nitems = getq(pp, &queue)) < 0) { 23631492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 23797792Sgad pp->spool_dir); 2381553Srgrimes exit(1); 2391553Srgrimes } 2401553Srgrimes if (nitems == 0) /* no work to do */ 2411553Srgrimes exit(0); 24231492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 24331492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 24497792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 24597792Sgad pp->lock_file); 2461553Srgrimes } 24768664Sgad 24868664Sgad /* create a file which will be used to hold stderr from filters */ 24968664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 25068664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 25197792Sgad tempstderr); 25268732Sgad exit(1); 25368664Sgad } 25468664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 25568664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 25697792Sgad tempstderr); 25768732Sgad exit(1); 25868664Sgad } 25968664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 26068664Sgad close(tempfd); 26168664Sgad 26231492Swollman openpr(pp); /* open printer or remote */ 2631553Srgrimesagain: 2641553Srgrimes /* 2651553Srgrimes * we found something to do now do it -- 2661553Srgrimes * write the name of the current control file into the lock file 2671553Srgrimes * so the spool queue program can tell what we're working on 2681553Srgrimes */ 2691553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2701553Srgrimes q = *qp++; 27168401Sgad if (stat(q->job_cfname, &stb) < 0) 2721553Srgrimes continue; 27315648Sjoerg errcnt = 0; 2741553Srgrimes restart: 27515648Sjoerg (void) lseek(lfd, pidoff, 0); 27668401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2771553Srgrimes i = strlen(line); 2781553Srgrimes if (write(lfd, line, i) != i) 27997792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 28097792Sgad pp->lock_file); 28131492Swollman if (!pp->remote) 28268401Sgad i = printit(pp, q->job_cfname); 2831553Srgrimes else 28468401Sgad i = sendit(pp, q->job_cfname); 2851553Srgrimes /* 2861553Srgrimes * Check to see if we are supposed to stop printing or 2871553Srgrimes * if we are to rebuild the queue. 2881553Srgrimes */ 2891553Srgrimes if (fstat(lfd, &stb) == 0) { 2901553Srgrimes /* stop printing before starting next job? */ 29131492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2921553Srgrimes goto done; 2931553Srgrimes /* rebuild queue (after lpc topq) */ 29431492Swollman if (stb.st_mode & LFM_RESET_QUE) { 29531492Swollman for (free(q); nitems--; free(q)) 2961553Srgrimes q = *qp++; 29731492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 29831492Swollman < 0) 29997792Sgad syslog(LOG_WARNING, 30097792Sgad "%s: fchmod(%s): %m", 30197792Sgad pp->printer, pp->lock_file); 3021553Srgrimes break; 3031553Srgrimes } 3041553Srgrimes } 30568733Sgad if (i == OK) /* all files of this job printed */ 30668733Sgad jobcount++; 30715648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 30815648Sjoerg /* try reprinting the job */ 30931492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 31097793Sgad if (of_pid > 0) { 31197793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3121553Srgrimes (void) close(ofd); 31397793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3141553Srgrimes ; 31579735Sgad if (i < 0) 31679735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 31797793Sgad pp->printer, of_pid); 31897793Sgad of_pid = 0; 3191553Srgrimes } 3201553Srgrimes (void) close(pfd); /* close printer */ 3211553Srgrimes if (ftruncate(lfd, pidoff) < 0) 32297792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 32397792Sgad pp->printer, pp->lock_file); 32431492Swollman openpr(pp); /* try to reopen printer */ 3251553Srgrimes goto restart; 32615648Sjoerg } else { 32731492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 32897792Sgad pp->printer, 32997792Sgad pp->remote ? "sent to remote host" : "printed", 33097792Sgad q->job_cfname); 33115648Sjoerg if (i == REPRINT) { 33227748Simp /* ensure we don't attempt this job again */ 33368401Sgad (void) unlink(q->job_cfname); 33468401Sgad q->job_cfname[0] = 'd'; 33568401Sgad (void) unlink(q->job_cfname); 33615648Sjoerg if (logname[0]) 33731492Swollman sendmail(pp, logname, FATALERR); 33815648Sjoerg } 3391553Srgrimes } 3401553Srgrimes } 34131492Swollman free(queue); 3421553Srgrimes /* 3431553Srgrimes * search the spool directory for more work. 3441553Srgrimes */ 34531492Swollman if ((nitems = getq(pp, &queue)) < 0) { 34631492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 34797792Sgad pp->spool_dir); 3481553Srgrimes exit(1); 3491553Srgrimes } 3501553Srgrimes if (nitems == 0) { /* no more work to do */ 3511553Srgrimes done: 35268733Sgad if (jobcount > 0) { /* jobs actually printed */ 35331492Swollman if (!pp->no_formfeed && !pp->tof) 35431492Swollman (void) write(ofd, pp->form_feed, 35531492Swollman strlen(pp->form_feed)); 35631492Swollman if (pp->trailer != NULL) /* output trailer */ 35731492Swollman (void) write(ofd, pp->trailer, 35831492Swollman strlen(pp->trailer)); 3591553Srgrimes } 36019202Simp (void) close(ofd); 36119202Simp (void) wait(NULL); 36268664Sgad (void) unlink(tempstderr); 3631553Srgrimes exit(0); 3641553Srgrimes } 3651553Srgrimes goto again; 3661553Srgrimes} 3671553Srgrimes 3681553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3691553Srgrimes 3701553Srgrimeschar ifonts[4][40] = { 3711553Srgrimes _PATH_VFONTR, 3721553Srgrimes _PATH_VFONTI, 3731553Srgrimes _PATH_VFONTB, 3741553Srgrimes _PATH_VFONTS, 3751553Srgrimes}; 3761553Srgrimes 3771553Srgrimes/* 3781553Srgrimes * The remaining part is the reading of the control file (cf) 3791553Srgrimes * and performing the various actions. 3801553Srgrimes */ 3811553Srgrimesstatic int 38278146Sgadprintit(struct printer *pp, char *file) 3831553Srgrimes{ 3841553Srgrimes register int i; 38568734Sgad char *cp; 38668734Sgad int bombed, didignorehdr; 3871553Srgrimes 38868734Sgad bombed = OK; 38968734Sgad didignorehdr = 0; 3901553Srgrimes /* 3911553Srgrimes * open control file; ignore if no longer there. 3921553Srgrimes */ 3931553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 39497792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 39597791Sgad return (OK); 3961553Srgrimes } 3971553Srgrimes /* 3981553Srgrimes * Reset troff fonts. 3991553Srgrimes */ 4001553Srgrimes for (i = 0; i < 4; i++) 4011553Srgrimes strcpy(fonts[i], ifonts[i]); 40231492Swollman sprintf(&width[2], "%ld", pp->page_width); 4031553Srgrimes strcpy(indent+2, "0"); 4041553Srgrimes 40568253Sgad /* initialize job-specific count of datafiles processed */ 40668253Sgad job_dfcnt = 0; 40768253Sgad 4081553Srgrimes /* 4091553Srgrimes * read the control file for work to do 4101553Srgrimes * 4111553Srgrimes * file format -- first character in the line is a command 4121553Srgrimes * rest of the line is the argument. 4131553Srgrimes * valid commands are: 4141553Srgrimes * 4151553Srgrimes * S -- "stat info" for symbolic link protection 4161553Srgrimes * J -- "job name" on banner page 4171553Srgrimes * C -- "class name" on banner page 4181553Srgrimes * L -- "literal" user's name to print on banner 4191553Srgrimes * T -- "title" for pr 4201553Srgrimes * H -- "host name" of machine where lpr was done 4211553Srgrimes * P -- "person" user's login name 4221553Srgrimes * I -- "indent" amount to indent output 42315648Sjoerg * R -- laser dpi "resolution" 4241553Srgrimes * f -- "file name" name of text file to print 4251553Srgrimes * l -- "file name" text file with control chars 42683684Sgad * o -- "file name" postscript file, according to 42783684Sgad * the RFC. Here it is treated like an 'f'. 4281553Srgrimes * p -- "file name" text file to print with pr(1) 4291553Srgrimes * t -- "file name" troff(1) file to print 4301553Srgrimes * n -- "file name" ditroff(1) file to print 4311553Srgrimes * d -- "file name" dvi file to print 4321553Srgrimes * g -- "file name" plot(1G) file to print 4331553Srgrimes * v -- "file name" plain raster file to print 4341553Srgrimes * c -- "file name" cifplot file to print 4351553Srgrimes * 1 -- "R font file" for troff 4361553Srgrimes * 2 -- "I font file" for troff 4371553Srgrimes * 3 -- "B font file" for troff 4381553Srgrimes * 4 -- "S font file" for troff 4391553Srgrimes * N -- "name" of file (used by lpq) 4401553Srgrimes * U -- "unlink" name of file to remove 4411553Srgrimes * (after we print it. (Pass 2 only)). 4421553Srgrimes * M -- "mail" to user when done printing 44353956Sache * Z -- "locale" for pr 4441553Srgrimes * 4451553Srgrimes * getline reads a line and expands tabs to blanks 4461553Srgrimes */ 4471553Srgrimes 4481553Srgrimes /* pass 1 */ 4491553Srgrimes 4501553Srgrimes while (getline(cfp)) 4511553Srgrimes switch (line[0]) { 4521553Srgrimes case 'H': 45378300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 45427748Simp if (class[0] == '\0') { 45580133Sgad strlcpy(class, line+1, sizeof(class)); 45627748Simp } 4571553Srgrimes continue; 4581553Srgrimes 4591553Srgrimes case 'P': 46080133Sgad strlcpy(logname, line + 1, sizeof(logname)); 46131492Swollman if (pp->restricted) { /* restricted */ 4621553Srgrimes if (getpwnam(logname) == NULL) { 4631553Srgrimes bombed = NOACCT; 46431492Swollman sendmail(pp, line+1, bombed); 4651553Srgrimes goto pass2; 4661553Srgrimes } 4671553Srgrimes } 4681553Srgrimes continue; 4691553Srgrimes 4701553Srgrimes case 'S': 4711553Srgrimes cp = line+1; 4721553Srgrimes i = 0; 4731553Srgrimes while (*cp >= '0' && *cp <= '9') 4741553Srgrimes i = i * 10 + (*cp++ - '0'); 4751553Srgrimes fdev = i; 4761553Srgrimes cp++; 4771553Srgrimes i = 0; 4781553Srgrimes while (*cp >= '0' && *cp <= '9') 4791553Srgrimes i = i * 10 + (*cp++ - '0'); 4801553Srgrimes fino = i; 4811553Srgrimes continue; 4821553Srgrimes 4831553Srgrimes case 'J': 48427748Simp if (line[1] != '\0') { 48580133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 48627748Simp } else 4871553Srgrimes strcpy(jobname, " "); 4881553Srgrimes continue; 4891553Srgrimes 4901553Srgrimes case 'C': 4911553Srgrimes if (line[1] != '\0') 49280133Sgad strlcpy(class, line + 1, sizeof(class)); 49380133Sgad else if (class[0] == '\0') { 49480133Sgad /* XXX - why call gethostname instead of 49580133Sgad * just strlcpy'ing local_host? */ 4961553Srgrimes gethostname(class, sizeof(class)); 49780133Sgad class[sizeof(class) - 1] = '\0'; 49880133Sgad } 4991553Srgrimes continue; 5001553Srgrimes 5011553Srgrimes case 'T': /* header title for pr */ 50280133Sgad strlcpy(title, line + 1, sizeof(title)); 5031553Srgrimes continue; 5041553Srgrimes 5051553Srgrimes case 'L': /* identification line */ 50631492Swollman if (!pp->no_header && !pp->header_last) 50731492Swollman banner(pp, line+1, jobname); 5081553Srgrimes continue; 5091553Srgrimes 5101553Srgrimes case '1': /* troff fonts */ 5111553Srgrimes case '2': 5121553Srgrimes case '3': 5131553Srgrimes case '4': 51427748Simp if (line[1] != '\0') { 51580133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 51680133Sgad (size_t)50); 51727748Simp } 5181553Srgrimes continue; 5191553Srgrimes 5201553Srgrimes case 'W': /* page width */ 52180133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5221553Srgrimes continue; 5231553Srgrimes 5241553Srgrimes case 'I': /* indent amount */ 52580133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5261553Srgrimes continue; 5271553Srgrimes 52853956Sache case 'Z': /* locale for pr */ 52980133Sgad strlcpy(locale, line + 1, sizeof(locale)); 53053956Sache continue; 53153956Sache 5321553Srgrimes default: /* some file to print */ 53368467Sgad /* only lowercase cmd-codes include a file-to-print */ 53468467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 53568467Sgad /* ignore any other lines */ 53668467Sgad if (lflag <= 1) 53768467Sgad continue; 53868467Sgad if (!didignorehdr) { 53968467Sgad syslog(LOG_INFO, "%s: in %s :", 54097792Sgad pp->printer, file); 54168467Sgad didignorehdr = 1; 54268467Sgad } 54368467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 54497792Sgad pp->printer, line[0], &line[1]); 54568467Sgad continue; 54668467Sgad } 54768467Sgad i = print(pp, line[0], line+1); 54868467Sgad switch (i) { 5491553Srgrimes case ERROR: 5501553Srgrimes if (bombed == OK) 5511553Srgrimes bombed = FATALERR; 5521553Srgrimes break; 5531553Srgrimes case REPRINT: 5541553Srgrimes (void) fclose(cfp); 55597791Sgad return (REPRINT); 5561553Srgrimes case FILTERERR: 5571553Srgrimes case ACCESS: 5581553Srgrimes bombed = i; 55931492Swollman sendmail(pp, logname, bombed); 5601553Srgrimes } 5611553Srgrimes title[0] = '\0'; 5621553Srgrimes continue; 5631553Srgrimes 5641553Srgrimes case 'N': 5651553Srgrimes case 'U': 5661553Srgrimes case 'M': 56715648Sjoerg case 'R': 5681553Srgrimes continue; 5691553Srgrimes } 5701553Srgrimes 5711553Srgrimes /* pass 2 */ 5721553Srgrimes 5731553Srgrimespass2: 5741553Srgrimes fseek(cfp, 0L, 0); 5751553Srgrimes while (getline(cfp)) 5761553Srgrimes switch (line[0]) { 5771553Srgrimes case 'L': /* identification line */ 57831492Swollman if (!pp->no_header && pp->header_last) 57931492Swollman banner(pp, line+1, jobname); 5801553Srgrimes continue; 5811553Srgrimes 5821553Srgrimes case 'M': 5831553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 58431492Swollman sendmail(pp, line+1, bombed); 5851553Srgrimes continue; 5861553Srgrimes 5871553Srgrimes case 'U': 58827748Simp if (strchr(line+1, '/')) 58927748Simp continue; 5901553Srgrimes (void) unlink(line+1); 5911553Srgrimes } 5921553Srgrimes /* 5931553Srgrimes * clean-up in case another control file exists 5941553Srgrimes */ 5951553Srgrimes (void) fclose(cfp); 5961553Srgrimes (void) unlink(file); 59797791Sgad return (bombed == OK ? OK : ERROR); 5981553Srgrimes} 5991553Srgrimes 6001553Srgrimes/* 6011553Srgrimes * Print a file. 6021553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 6031553Srgrimes * Return -1 if a non-recoverable error occured, 6041553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 6051553Srgrimes * 1 if we should try to reprint this job and 6061553Srgrimes * 0 if all is well. 6071553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 6081553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6091553Srgrimes */ 6101553Srgrimesstatic int 61178146Sgadprint(struct printer *pp, int format, char *file) 6121553Srgrimes{ 61353956Sache register int n, i; 6141553Srgrimes register char *prog; 61531492Swollman int fi, fo; 6161553Srgrimes FILE *fp; 617119192Sgad char *av[15], buf[SPL_BUFSIZ]; 61897793Sgad pid_t wpid; 61997793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6201553Srgrimes struct stat stb; 6211553Srgrimes 622138939Sgad /* Make sure the entire data file has arrived. */ 623138939Sgad wait4data(pp, file); 624138939Sgad 62568467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 62668467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 62797792Sgad pp->printer, file, format); 62897791Sgad return (ERROR); 62968467Sgad } 6301553Srgrimes /* 6311553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6321553Srgrimes * still point to the same file or someone is trying to print 6331553Srgrimes * something he shouldn't. 6341553Srgrimes */ 6351553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6361553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 63797791Sgad return (ACCESS); 63868253Sgad 63968253Sgad job_dfcnt++; /* increment datafile counter for this job */ 64068734Sgad stopped = 0; /* output filter is not stopped */ 64168253Sgad 64268253Sgad /* everything seems OK, start it up */ 64331492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 64431492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 64531492Swollman pp->tof = 1; 6461553Srgrimes } 64731492Swollman if (pp->filters[LPF_INPUT] == NULL 64883684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 64931492Swollman pp->tof = 0; 650119192Sgad while ((n = read(fi, buf, SPL_BUFSIZ)) > 0) 6511553Srgrimes if (write(ofd, buf, n) != n) { 6521553Srgrimes (void) close(fi); 65397791Sgad return (REPRINT); 6541553Srgrimes } 6551553Srgrimes (void) close(fi); 65697791Sgad return (OK); 6571553Srgrimes } 6581553Srgrimes switch (format) { 6591553Srgrimes case 'p': /* print file using 'pr' */ 66031492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6611553Srgrimes prog = _PATH_PR; 66253956Sache i = 0; 66353956Sache av[i++] = "pr"; 66453956Sache av[i++] = width; 66553956Sache av[i++] = length; 66653956Sache av[i++] = "-h"; 66753956Sache av[i++] = *title ? title : " "; 66853956Sache av[i++] = "-L"; 66953956Sache av[i++] = *locale ? locale : "C"; 67053956Sache av[i++] = "-F"; 67153956Sache av[i] = 0; 6721553Srgrimes fo = ofd; 6731553Srgrimes goto start; 6741553Srgrimes } 6751553Srgrimes pipe(p); 67631492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 677118881Sgad dup2(fi, STDIN_FILENO); /* file is stdin */ 678118881Sgad dup2(p[1], STDOUT_FILENO); /* pipe is stdout */ 6798094Sjkh closelog(); 68031492Swollman closeallfds(3); 6811553Srgrimes execl(_PATH_PR, "pr", width, length, 68253956Sache "-h", *title ? title : " ", 68353956Sache "-L", *locale ? locale : "C", 68479452Sbrian "-F", (char *)0); 6851553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6861553Srgrimes exit(2); 6871553Srgrimes } 6881553Srgrimes (void) close(p[1]); /* close output side */ 6891553Srgrimes (void) close(fi); 6901553Srgrimes if (prchild < 0) { 6911553Srgrimes prchild = 0; 6921553Srgrimes (void) close(p[0]); 69397791Sgad return (ERROR); 6941553Srgrimes } 6951553Srgrimes fi = p[0]; /* use pipe for input */ 6961553Srgrimes case 'f': /* print plain text file */ 69731492Swollman prog = pp->filters[LPF_INPUT]; 6981553Srgrimes av[1] = width; 6991553Srgrimes av[2] = length; 7001553Srgrimes av[3] = indent; 7011553Srgrimes n = 4; 7021553Srgrimes break; 70386935Sgad case 'o': /* print postscript file */ 70486935Sgad /* 70586935Sgad * Treat this as a "plain file with control characters", and 70686935Sgad * assume the standard LPF_INPUT filter will recognize that 70786935Sgad * the data is postscript and know what to do with it. These 70886935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 70986935Sgad * (later versions of MacOS 10 will explicitly use 'l') 71086935Sgad * A postscript file can contain binary data, which is why 'l' 71186935Sgad * is somewhat more appropriate than 'f'. 71286935Sgad */ 71386935Sgad /* FALLTHROUGH */ 7141553Srgrimes case 'l': /* like 'f' but pass control characters */ 71531492Swollman prog = pp->filters[LPF_INPUT]; 7161553Srgrimes av[1] = "-c"; 7171553Srgrimes av[2] = width; 7181553Srgrimes av[3] = length; 7191553Srgrimes av[4] = indent; 7201553Srgrimes n = 5; 7211553Srgrimes break; 7221553Srgrimes case 'r': /* print a fortran text file */ 72331492Swollman prog = pp->filters[LPF_FORTRAN]; 7241553Srgrimes av[1] = width; 7251553Srgrimes av[2] = length; 7261553Srgrimes n = 3; 7271553Srgrimes break; 7281553Srgrimes case 't': /* print troff output */ 7291553Srgrimes case 'n': /* print ditroff output */ 7301553Srgrimes case 'd': /* print tex output */ 7311553Srgrimes (void) unlink(".railmag"); 7321553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 73331492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 73497792Sgad pp->printer); 7351553Srgrimes (void) unlink(".railmag"); 7361553Srgrimes } else { 7371553Srgrimes for (n = 0; n < 4; n++) { 7381553Srgrimes if (fonts[n][0] != '/') 7391553Srgrimes (void) write(fo, _PATH_VFONT, 7401553Srgrimes sizeof(_PATH_VFONT) - 1); 7411553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7421553Srgrimes (void) write(fo, "\n", 1); 7431553Srgrimes } 7441553Srgrimes (void) close(fo); 7451553Srgrimes } 74631492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 74731492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 74831492Swollman : pp->filters[LPF_DVI]); 7491553Srgrimes av[1] = pxwidth; 7501553Srgrimes av[2] = pxlength; 7511553Srgrimes n = 3; 7521553Srgrimes break; 7531553Srgrimes case 'c': /* print cifplot output */ 75431492Swollman prog = pp->filters[LPF_CIFPLOT]; 7551553Srgrimes av[1] = pxwidth; 7561553Srgrimes av[2] = pxlength; 7571553Srgrimes n = 3; 7581553Srgrimes break; 7591553Srgrimes case 'g': /* print plot(1G) output */ 76031492Swollman prog = pp->filters[LPF_GRAPH]; 7611553Srgrimes av[1] = pxwidth; 7621553Srgrimes av[2] = pxlength; 7631553Srgrimes n = 3; 7641553Srgrimes break; 7651553Srgrimes case 'v': /* print raster output */ 76631492Swollman prog = pp->filters[LPF_RASTER]; 7671553Srgrimes av[1] = pxwidth; 7681553Srgrimes av[2] = pxlength; 7691553Srgrimes n = 3; 7701553Srgrimes break; 7711553Srgrimes default: 7721553Srgrimes (void) close(fi); 7731553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 77497792Sgad pp->printer, format); 77597791Sgad return (ERROR); 7761553Srgrimes } 77715648Sjoerg if (prog == NULL) { 77815648Sjoerg (void) close(fi); 77915648Sjoerg syslog(LOG_ERR, 78015648Sjoerg "%s: no filter found in printcap for format character '%c'", 78131492Swollman pp->printer, format); 78297791Sgad return (ERROR); 78315648Sjoerg } 78427635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7851553Srgrimes av[0]++; 7861553Srgrimes else 7871553Srgrimes av[0] = prog; 7881553Srgrimes av[n++] = "-n"; 7891553Srgrimes av[n++] = logname; 7901553Srgrimes av[n++] = "-h"; 79178300Sgad av[n++] = origin_host; 79231492Swollman av[n++] = pp->acct_file; 7931553Srgrimes av[n] = 0; 7941553Srgrimes fo = pfd; 79597793Sgad if (of_pid > 0) { /* stop output filter */ 7961553Srgrimes write(ofd, "\031\1", 2); 79797793Sgad while ((wpid = 79897793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 7991553Srgrimes ; 80097793Sgad if (wpid < 0) 80197792Sgad syslog(LOG_WARNING, 80297792Sgad "%s: after stopping 'of', wait3() returned: %m", 80379735Sgad pp->printer); 80497781Sgad else if (!WIFSTOPPED(wstatus)) { 8051553Srgrimes (void) close(fi); 80697781Sgad syslog(LOG_WARNING, "%s: output filter died " 80797781Sgad "(pid=%d retcode=%d termsig=%d)", 80897793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 80997781Sgad WTERMSIG(wstatus)); 81097791Sgad return (REPRINT); 8111553Srgrimes } 8121553Srgrimes stopped++; 8131553Srgrimes } 8141553Srgrimesstart: 81531492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 816118881Sgad dup2(fi, STDIN_FILENO); 817118881Sgad dup2(fo, STDOUT_FILENO); 81868664Sgad /* setup stderr for the filter (child process) 81968664Sgad * so it goes to our temporary errors file */ 82068664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8211553Srgrimes if (n >= 0) 822118881Sgad dup2(n, STDERR_FILENO); 8238094Sjkh closelog(); 82431492Swollman closeallfds(3); 8251553Srgrimes execv(prog, av); 82695067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 82795067Sgad prog); 8281553Srgrimes exit(2); 8291553Srgrimes } 8301553Srgrimes (void) close(fi); 83197789Sgad wstatus_set = 0; 8321553Srgrimes if (child < 0) 83397781Sgad retcode = 100; 83479735Sgad else { 83597793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8361553Srgrimes ; 83797793Sgad if (wpid < 0) { 83897781Sgad retcode = 100; 83997792Sgad syslog(LOG_WARNING, 84097792Sgad "%s: after execv(%s), wait() returned: %m", 84179735Sgad pp->printer, prog); 84297789Sgad } else { 84397789Sgad wstatus_set = 1; 84497781Sgad retcode = WEXITSTATUS(wstatus); 84597789Sgad } 84679735Sgad } 8471553Srgrimes child = 0; 8481553Srgrimes prchild = 0; 8491553Srgrimes if (stopped) { /* restart output filter */ 85097793Sgad if (kill(of_pid, SIGCONT) < 0) { 8511553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8521553Srgrimes exit(1); 8531553Srgrimes } 8541553Srgrimes } 85531492Swollman pp->tof = 0; 8561553Srgrimes 85768664Sgad /* Copy the filter's output to "lf" logfile */ 85868664Sgad if ((fp = fopen(tempstderr, "r"))) { 8591553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8601553Srgrimes fputs(buf, stderr); 8611553Srgrimes fclose(fp); 8621553Srgrimes } 8631553Srgrimes 86497789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 86515648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 86697781Sgad pp->printer, format, WTERMSIG(wstatus)); 86797791Sgad return (ERROR); 8681553Srgrimes } 86997781Sgad switch (retcode) { 8701553Srgrimes case 0: 87131492Swollman pp->tof = 1; 87297791Sgad return (OK); 8731553Srgrimes case 1: 87497791Sgad return (REPRINT); 87515648Sjoerg case 2: 87697791Sgad return (ERROR); 8771553Srgrimes default: 87815648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 87997792Sgad pp->printer, format, retcode); 88097791Sgad return (FILTERERR); 8811553Srgrimes } 8821553Srgrimes} 8831553Srgrimes 8841553Srgrimes/* 8851553Srgrimes * Send the daemon control file (cf) and any data files. 8861553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 8871553Srgrimes * 0 if all is well. 8881553Srgrimes */ 8891553Srgrimesstatic int 89078146Sgadsendit(struct printer *pp, char *file) 8911553Srgrimes{ 89295293Sgad int dfcopies, err, i; 893119192Sgad char *cp, last[sizeof(line)]; 8941553Srgrimes 8951553Srgrimes /* 8961553Srgrimes * open control file 8971553Srgrimes */ 8981553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 89997791Sgad return (OK); 90068253Sgad 90168253Sgad /* initialize job-specific count of datafiles processed */ 90268253Sgad job_dfcnt = 0; 90368253Sgad 9041553Srgrimes /* 9051553Srgrimes * read the control file for work to do 9061553Srgrimes * 9071553Srgrimes * file format -- first character in the line is a command 9081553Srgrimes * rest of the line is the argument. 9091553Srgrimes * commands of interest are: 9101553Srgrimes * 9111553Srgrimes * a-z -- "file name" name of file to print 9121553Srgrimes * U -- "unlink" name of file to remove 9131553Srgrimes * (after we print it. (Pass 2 only)). 9141553Srgrimes */ 9151553Srgrimes 9161553Srgrimes /* 9171553Srgrimes * pass 1 9181553Srgrimes */ 91995293Sgad err = OK; 9201553Srgrimes while (getline(cfp)) { 9211553Srgrimes again: 9221553Srgrimes if (line[0] == 'S') { 9231553Srgrimes cp = line+1; 9241553Srgrimes i = 0; 9251553Srgrimes while (*cp >= '0' && *cp <= '9') 9261553Srgrimes i = i * 10 + (*cp++ - '0'); 9271553Srgrimes fdev = i; 9281553Srgrimes cp++; 9291553Srgrimes i = 0; 9301553Srgrimes while (*cp >= '0' && *cp <= '9') 9311553Srgrimes i = i * 10 + (*cp++ - '0'); 9321553Srgrimes fino = i; 93324831Sbrian } else if (line[0] == 'H') { 93478300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 93568343Sgad if (class[0] == '\0') { 93680133Sgad strlcpy(class, line + 1, sizeof(class)); 93768343Sgad } 93824831Sbrian } else if (line[0] == 'P') { 93980133Sgad strlcpy(logname, line + 1, sizeof(logname)); 94031492Swollman if (pp->restricted) { /* restricted */ 94124831Sbrian if (getpwnam(logname) == NULL) { 94231492Swollman sendmail(pp, line+1, NOACCT); 94324831Sbrian err = ERROR; 94424831Sbrian break; 94524831Sbrian } 94624831Sbrian } 94724831Sbrian } else if (line[0] == 'I') { 94880133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 94924831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 95095293Sgad dfcopies = 1; 9511553Srgrimes strcpy(last, line); 95295293Sgad while ((i = getline(cfp)) != 0) { 95395293Sgad if (strcmp(last, line) != 0) 9541553Srgrimes break; 95595293Sgad dfcopies++; 95695293Sgad } 95795293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9581553Srgrimes case OK: 9591553Srgrimes if (i) 9601553Srgrimes goto again; 9611553Srgrimes break; 9621553Srgrimes case REPRINT: 9631553Srgrimes (void) fclose(cfp); 96497791Sgad return (REPRINT); 9651553Srgrimes case ACCESS: 96631492Swollman sendmail(pp, logname, ACCESS); 9671553Srgrimes case ERROR: 9681553Srgrimes err = ERROR; 9691553Srgrimes } 9701553Srgrimes break; 9711553Srgrimes } 9721553Srgrimes } 97395293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9741553Srgrimes (void) fclose(cfp); 97597791Sgad return (REPRINT); 9761553Srgrimes } 9771553Srgrimes /* 9781553Srgrimes * pass 2 9791553Srgrimes */ 9801553Srgrimes fseek(cfp, 0L, 0); 9811553Srgrimes while (getline(cfp)) 98227748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9831553Srgrimes (void) unlink(line+1); 9841553Srgrimes /* 9851553Srgrimes * clean-up in case another control file exists 9861553Srgrimes */ 9871553Srgrimes (void) fclose(cfp); 9881553Srgrimes (void) unlink(file); 98997791Sgad return (err); 9901553Srgrimes} 9911553Srgrimes 9921553Srgrimes/* 9931553Srgrimes * Send a data file to the remote machine and spool it. 9941553Srgrimes * Return positive if we should try resending. 9951553Srgrimes */ 9961553Srgrimesstatic int 99795293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 9981553Srgrimes{ 99994036Sgad int i, amt; 10001553Srgrimes struct stat stb; 100194032Sgad char *av[15], *filtcmd; 1002119192Sgad char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 100395293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 10041553Srgrimes 1005138939Sgad /* Make sure the entire data file has arrived. */ 1006138939Sgad wait4data(pp, file); 1007138939Sgad 100874124Sgad statrc = lstat(file, &stb); 100974124Sgad if (statrc < 0) { 101074124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 101174124Sgad pp->printer, file); 101294036Sgad return (ERROR); 101374124Sgad } 101494036Sgad sfd = open(file, O_RDONLY); 101594036Sgad if (sfd < 0) { 101674124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 101774124Sgad pp->printer, file); 101894036Sgad return (ERROR); 101974124Sgad } 10201553Srgrimes /* 10211553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10221553Srgrimes * still point to the same file or someone is trying to print something 10231553Srgrimes * he shouldn't. 10241553Srgrimes */ 102594036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 102694036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 102794036Sgad close(sfd); 102894036Sgad return (ACCESS); 102994036Sgad } 103024831Sbrian 103194032Sgad /* Everything seems OK for reading the file, now to send it */ 103294032Sgad filtcmd = NULL; 103324831Sbrian sizerr = 0; 103494032Sgad tfd = -1; 103524831Sbrian if (type == '\3') { 103694032Sgad /* 103794032Sgad * Type == 3 means this is a datafile, not a control file. 103894032Sgad * Increment the counter of data-files in this job, and 103994032Sgad * then check for input or output filters (which are only 104094032Sgad * applied to datafiles, not control files). 104194032Sgad */ 104294032Sgad job_dfcnt++; 104394032Sgad 104494032Sgad /* 104594032Sgad * Note that here we are filtering datafiles, one at a time, 104694032Sgad * as they are sent to the remote machine. Here, the *only* 104794032Sgad * difference between an input filter (`if=') and an output 104894032Sgad * filter (`of=') is the argument list that the filter is 104994032Sgad * started up with. Here, the output filter is executed 105094032Sgad * for each individual file as it is sent. This is not the 105194032Sgad * same as local print queues, where the output filter is 105294032Sgad * started up once, and then all jobs are passed thru that 105394032Sgad * single invocation of the output filter. 105494032Sgad * 105594032Sgad * Also note that a queue for a remote-machine can have an 105694032Sgad * input filter or an output filter, but not both. 105794032Sgad */ 105831492Swollman if (pp->filters[LPF_INPUT]) { 105994032Sgad filtcmd = pp->filters[LPF_INPUT]; 106094032Sgad av[0] = filtcmd; 106194032Sgad narg = 0; 106294032Sgad strcpy(opt_c, "-c"); 106394032Sgad strcpy(opt_h, "-h"); 106494032Sgad strcpy(opt_n, "-n"); 106524831Sbrian if (format == 'l') 106694032Sgad av[++narg] = opt_c; 106794032Sgad av[++narg] = width; 106894032Sgad av[++narg] = length; 106994032Sgad av[++narg] = indent; 107094032Sgad av[++narg] = opt_n; 107194032Sgad av[++narg] = logname; 107294032Sgad av[++narg] = opt_h; 107394032Sgad av[++narg] = origin_host; 107494032Sgad av[++narg] = pp->acct_file; 107594032Sgad av[++narg] = NULL; 107694032Sgad } else if (pp->filters[LPF_OUTPUT]) { 107794032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 107894032Sgad av[0] = filtcmd; 107994032Sgad narg = 0; 108094032Sgad av[++narg] = width; 108194032Sgad av[++narg] = length; 108294032Sgad av[++narg] = NULL; 108324831Sbrian } 108424831Sbrian } 108594032Sgad if (filtcmd) { 108694032Sgad /* 108794032Sgad * If there is an input or output filter, we have to run 108894032Sgad * the datafile thru that filter and store the result as 108994032Sgad * a temporary spool file, because the protocol requires 109094032Sgad * that we send the remote host the file-size before we 109194032Sgad * start to send any of the data. 109294032Sgad */ 109394032Sgad strcpy(tfile, TFILENAME); 109494032Sgad tfd = mkstemp(tfile); 109594032Sgad if (tfd == -1) { 109694032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 109794032Sgad TFILENAME); 109894036Sgad sfres = ERROR; 109994036Sgad goto return_sfres; 110094032Sgad } 110194036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 110224831Sbrian 110394032Sgad /* process the return-code from the filter */ 110494032Sgad switch (filtstat) { 110594032Sgad case 0: 110694032Sgad break; 110794032Sgad case 1: 110894036Sgad sfres = REPRINT; 110994036Sgad goto return_sfres; 111094032Sgad case 2: 111194036Sgad sfres = ERROR; 111294036Sgad goto return_sfres; 111394032Sgad default: 111494032Sgad syslog(LOG_WARNING, 111594032Sgad "%s: filter '%c' exited (retcode=%d)", 111694032Sgad pp->printer, format, filtstat); 111794036Sgad sfres = FILTERERR; 111894036Sgad goto return_sfres; 111994032Sgad } 112094032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 112194032Sgad if (statrc < 0) { 112294032Sgad syslog(LOG_ERR, 112394032Sgad "%s: error processing 'if', fstat(%s): %m", 112494032Sgad pp->printer, tfile); 112594036Sgad sfres = ERROR; 112694036Sgad goto return_sfres; 112794032Sgad } 112894036Sgad close(sfd); 112994036Sgad sfd = tfd; 113094036Sgad lseek(sfd, 0, SEEK_SET); 113194032Sgad } 113294032Sgad 113395293Sgad copycnt = 0; 113495293Sgadsendagain: 113595293Sgad copycnt++; 113695293Sgad 113795293Sgad if (copycnt < 2) 113895293Sgad (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 113995293Sgad else 114095293Sgad (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 114195293Sgad file, copycnt); 11421553Srgrimes amt = strlen(buf); 11431553Srgrimes for (i = 0; ; i++) { 11441553Srgrimes if (write(pfd, buf, amt) != amt || 114531492Swollman (resp = response(pp)) < 0 || resp == '\1') { 114694036Sgad sfres = REPRINT; 114794036Sgad goto return_sfres; 11481553Srgrimes } else if (resp == '\0') 11491553Srgrimes break; 11501553Srgrimes if (i == 0) 115131492Swollman pstatus(pp, 115231492Swollman "no space on remote; waiting for queue to drain"); 11531553Srgrimes if (i == 10) 11541553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 115597792Sgad pp->printer, pp->remote_host); 11561553Srgrimes sleep(5 * 60); 11571553Srgrimes } 11581553Srgrimes if (i) 115931492Swollman pstatus(pp, "sending to %s", pp->remote_host); 116095293Sgad /* 116195293Sgad * XXX - we should change trstat_init()/trstat_write() to include 116295293Sgad * the copycnt in the statistics record it may write. 116395293Sgad */ 116468253Sgad if (type == '\3') 116568253Sgad trstat_init(pp, file, job_dfcnt); 1166119192Sgad for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) { 1167119192Sgad amt = SPL_BUFSIZ; 11681553Srgrimes if (i + amt > stb.st_size) 11691553Srgrimes amt = stb.st_size - i; 117094036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11711553Srgrimes sizerr = 1; 11721553Srgrimes if (write(pfd, buf, amt) != amt) { 117394036Sgad sfres = REPRINT; 117494036Sgad goto return_sfres; 11751553Srgrimes } 11761553Srgrimes } 11771553Srgrimes 11781553Srgrimes if (sizerr) { 117931492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11801553Srgrimes /* tell recvjob to ignore this file */ 11811553Srgrimes (void) write(pfd, "\1", 1); 118294036Sgad sfres = ERROR; 118394036Sgad goto return_sfres; 11841553Srgrimes } 118531492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 118694036Sgad sfres = REPRINT; 118794036Sgad goto return_sfres; 118824831Sbrian } 118995293Sgad if (type == '\3') { 119068253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 119195293Sgad pp->remote_host, origin_host); 119295293Sgad /* 119395293Sgad * Usually we only need to send one copy of a datafile, 119495293Sgad * because the control-file will simply print the same 119595293Sgad * file multiple times. However, some printers ignore 119695293Sgad * the control file, and simply print each data file as 119795293Sgad * it arrives. For such "remote hosts", we need to 119895293Sgad * transfer the same data file multiple times. Such a 119995293Sgad * a host is indicated by adding 'rc' to the printcap 120095293Sgad * entry. 120195293Sgad * XXX - Right now this ONLY works for remote hosts which 120295293Sgad * do ignore the name of the data file, because 120395293Sgad * this sends the file multiple times with slight 120495293Sgad * changes to the filename. To do this right would 120595293Sgad * require that we also rewrite the control file 120695293Sgad * to match those filenames. 120795293Sgad */ 120895293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 120995293Sgad lseek(sfd, 0, SEEK_SET); 121095293Sgad goto sendagain; 121195293Sgad } 121295293Sgad } 121394036Sgad sfres = OK; 121494036Sgad 121594036Sgadreturn_sfres: 121694036Sgad (void)close(sfd); 121794036Sgad if (tfd != -1) { 121894036Sgad /* 121994036Sgad * If tfd is set, then it is the same value as sfd, and 122094036Sgad * therefore it is already closed at this point. All 122194036Sgad * we need to do is remove the temporary file. 122294036Sgad */ 122394036Sgad tfd = -1; 122494036Sgad unlink(tfile); 122594036Sgad } 122694036Sgad return (sfres); 12271553Srgrimes} 12281553Srgrimes 12291553Srgrimes/* 1230138939Sgad * Some print servers send the control-file first, and then start sending the 1231138939Sgad * matching data file(s). That is not the correct order. If some queue is 1232138939Sgad * already printing an active job, then when that job is finished the queue 1233138939Sgad * may proceed to the control file of any incoming print job. This turns 1234138939Sgad * into a race between the process which is receiving the data file, and the 1235138939Sgad * process which is actively printing the very same file. When the remote 1236138939Sgad * server sends files in the wrong order, it is even possible that a queue 1237138939Sgad * will start to print a data file before the file has been created! 1238138939Sgad * 1239138939Sgad * So before we start to print() or send() a data file, we call this routine 1240138939Sgad * to make sure the data file is not still changing in size. Note that this 1241138939Sgad * problem will only happen for jobs arriving from a remote host, and that 1242138939Sgad * the process which has decided to print this job (and is thus making this 1243138939Sgad * check) is *not* the process which is receiving the job. 1244138939Sgad * 1245138939Sgad * A second benefit of this is that any incoming job is guaranteed to appear 1246138939Sgad * in a queue listing for at least a few seconds after it has arrived. Some 1247138939Sgad * lpr implementations get confused if they send a job and it disappears 1248138939Sgad * from the queue before they can check on it. 1249138939Sgad */ 1250138939Sgad#define MAXWAIT_ARRIVE 16 /* max to wait for the file to *exist* */ 1251138939Sgad#define MAXWAIT_4DATA (20*60) /* max to wait for it to stop changing */ 1252138939Sgad#define MINWAIT_4DATA 4 /* This value must be >= 1 */ 1253138939Sgad#define DEBUG_MINWAIT 1 1254138939Sgadstatic void 1255138939Sgadwait4data(struct printer *pp, const char *dfile) 1256138939Sgad{ 1257138939Sgad const char *cp; 1258138939Sgad int statres; 1259138939Sgad size_t dlen, hlen; 1260138939Sgad time_t amtslept, checktime; 1261138939Sgad struct stat statdf; 1262138939Sgad 1263138939Sgad /* Skip these checks if the print job is from the local host. */ 1264138939Sgad dlen = strlen(dfile); 1265138939Sgad hlen = strlen(local_host); 1266138939Sgad if (dlen > hlen) { 1267138939Sgad cp = dfile + dlen - hlen; 1268138939Sgad if (strcmp(cp, local_host) == 0) 1269138939Sgad return; 1270138939Sgad } 1271138939Sgad 1272138939Sgad /* 1273138939Sgad * If this data file does not exist, then wait up to MAXWAIT_ARRIVE 1274138939Sgad * seconds for it to arrive. 1275138939Sgad */ 1276138939Sgad amtslept = 0; 1277138939Sgad statres = stat(dfile, &statdf); 1278138939Sgad while (statres < 0 && amtslept < MAXWAIT_ARRIVE) { 1279138939Sgad if (amtslept == 0) 1280138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1281138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1282138939Sgad statres = stat(dfile, &statdf); 1283138939Sgad } 1284138939Sgad if (statres < 0) { 1285138939Sgad /* The file still does not exist, so just give up on it. */ 1286138939Sgad syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s", 1287138939Sgad pp->printer, dfile); 1288138939Sgad return; 1289138939Sgad } 1290138939Sgad 1291138939Sgad /* 1292138939Sgad * The file exists, so keep waiting until the data file has not 1293138939Sgad * changed for some reasonable amount of time. 1294138939Sgad */ 1295138939Sgad while (statres == 0 && amtslept < MAXWAIT_4DATA) { 1296138939Sgad checktime = time(NULL) - MINWAIT_4DATA; 1297138939Sgad if (statdf.st_mtime <= checktime) 1298138939Sgad break; 1299138939Sgad if (amtslept == 0) 1300138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1301138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1302138939Sgad statres = stat(dfile, &statdf); 1303138939Sgad } 1304138939Sgad 1305138939Sgad if (statres != 0) 1306138939Sgad syslog(LOG_WARNING, "%s: %s disappeared during wait4data()", 1307138939Sgad pp->printer, dfile); 1308138939Sgad else if (amtslept > MAXWAIT_4DATA) 1309138939Sgad syslog(LOG_WARNING, 1310138939Sgad "%s: %s still changing after %lu secs in wait4data()", 1311138939Sgad pp->printer, dfile, (unsigned long)amtslept); 1312138939Sgad#if DEBUG_MINWAIT 1313138939Sgad else if (amtslept > MINWAIT_4DATA) 1314138939Sgad syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)", 1315138939Sgad pp->printer, (unsigned long)amtslept, dfile); 1316138939Sgad#endif 1317138939Sgad} 1318138939Sgad#undef MAXWAIT_ARRIVE 1319138939Sgad#undef MAXWAIT_4DATA 1320138939Sgad#undef MINWAIT_4DATA 1321138939Sgad 1322138939Sgad/* 132394032Sgad * This routine is called to execute one of the filters as was 132494036Sgad * specified in a printcap entry. While the child-process will read 132594036Sgad * all of 'infd', it is up to the caller to close that file descriptor 132694036Sgad * in the parent process. 132794032Sgad */ 132894032Sgadstatic int 132994032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 133094032Sgad{ 133197793Sgad pid_t fpid, wpid; 133297793Sgad int errfd, retcode, wstatus; 133394032Sgad FILE *errfp; 133494032Sgad char buf[BUFSIZ], *slash; 133594032Sgad 133694032Sgad fpid = dofork(pp, DORETURN); 133794032Sgad if (fpid != 0) { 133894032Sgad /* 133994032Sgad * This is the parent process, which just waits for the child 134094032Sgad * to complete and then returns the result. Note that it is 134194032Sgad * the child process which reads the input stream. 134294032Sgad */ 134394032Sgad if (fpid < 0) 134497781Sgad retcode = 100; 134594032Sgad else { 134697781Sgad while ((wpid = wait(&wstatus)) > 0 && 134794032Sgad wpid != fpid) 134894032Sgad ; 134994032Sgad if (wpid < 0) { 135097781Sgad retcode = 100; 135194032Sgad syslog(LOG_WARNING, 135294032Sgad "%s: after execv(%s), wait() returned: %m", 135394032Sgad pp->printer, f_cmd); 135497781Sgad } else 135597781Sgad retcode = WEXITSTATUS(wstatus); 135694032Sgad } 135794032Sgad 135894032Sgad /* 135994032Sgad * Copy everything the filter wrote to stderr from our 136094032Sgad * temporary errors file to the "lf=" logfile. 136194032Sgad */ 136294032Sgad errfp = fopen(tempstderr, "r"); 136394032Sgad if (errfp) { 136494032Sgad while (fgets(buf, sizeof(buf), errfp)) 136594032Sgad fputs(buf, stderr); 136694032Sgad fclose(errfp); 136794032Sgad } 136894032Sgad 136997781Sgad return (retcode); 137094032Sgad } 137194032Sgad 137294032Sgad /* 137394032Sgad * This is the child process, which is the one that executes the 137494032Sgad * given filter. 137594032Sgad */ 137694032Sgad /* 137794032Sgad * If the first parameter has any slashes in it, then change it 137894032Sgad * to point to the first character after the last slash. 137994032Sgad */ 138094032Sgad slash = strrchr(f_av[0], '/'); 138194032Sgad if (slash != NULL) 138294032Sgad f_av[0] = slash + 1; 138394032Sgad /* 138494032Sgad * XXX - in the future, this should setup an explicit list of 138594032Sgad * environment variables and use execve()! 138694032Sgad */ 138794032Sgad 138894032Sgad /* 138994032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 139094032Sgad * is running. Stderr is setup so it points to a temporary errors 139194032Sgad * file, and the parent process will copy that temporary file to 139294032Sgad * the real logfile after the filter completes. 139394032Sgad */ 1394118881Sgad dup2(infd, STDIN_FILENO); 1395118881Sgad dup2(outfd, STDOUT_FILENO); 139694032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 139794032Sgad if (errfd >= 0) 1398118881Sgad dup2(errfd, STDERR_FILENO); 139994032Sgad closelog(); 140094032Sgad closeallfds(3); 140194032Sgad execv(f_cmd, f_av); 140295067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 140394032Sgad exit(2); 140494032Sgad /* NOTREACHED */ 140594032Sgad} 140694032Sgad 140794032Sgad/* 14081553Srgrimes * Check to make sure there have been no errors and that both programs 14091553Srgrimes * are in sync with eachother. 14101553Srgrimes * Return non-zero if the connection was lost. 14111553Srgrimes */ 14121553Srgrimesstatic char 141378146Sgadresponse(const struct printer *pp) 14141553Srgrimes{ 14151553Srgrimes char resp; 14161553Srgrimes 14171553Srgrimes if (read(pfd, &resp, 1) != 1) { 141831492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 141997791Sgad return (-1); 14201553Srgrimes } 142197791Sgad return (resp); 14221553Srgrimes} 14231553Srgrimes 14241553Srgrimes/* 14251553Srgrimes * Banner printing stuff 14261553Srgrimes */ 14271553Srgrimesstatic void 142878146Sgadbanner(struct printer *pp, char *name1, char *name2) 14291553Srgrimes{ 14301553Srgrimes time_t tvec; 14311553Srgrimes 14321553Srgrimes time(&tvec); 143331492Swollman if (!pp->no_formfeed && !pp->tof) 143431492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 143531492Swollman if (pp->short_banner) { /* short banner only */ 14361553Srgrimes if (class[0]) { 14371553Srgrimes (void) write(ofd, class, strlen(class)); 14381553Srgrimes (void) write(ofd, ":", 1); 14391553Srgrimes } 14401553Srgrimes (void) write(ofd, name1, strlen(name1)); 14411553Srgrimes (void) write(ofd, " Job: ", 7); 14421553Srgrimes (void) write(ofd, name2, strlen(name2)); 14431553Srgrimes (void) write(ofd, " Date: ", 8); 14441553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14451553Srgrimes (void) write(ofd, "\n", 1); 14461553Srgrimes } else { /* normal banner */ 14471553Srgrimes (void) write(ofd, "\n\n\n", 3); 144831492Swollman scan_out(pp, ofd, name1, '\0'); 14491553Srgrimes (void) write(ofd, "\n\n", 2); 145031492Swollman scan_out(pp, ofd, name2, '\0'); 14511553Srgrimes if (class[0]) { 14521553Srgrimes (void) write(ofd,"\n\n\n",3); 145331492Swollman scan_out(pp, ofd, class, '\0'); 14541553Srgrimes } 14551553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 14561553Srgrimes (void) write(ofd, name2, strlen(name2)); 14571553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 14581553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14591553Srgrimes (void) write(ofd, "\n", 1); 14601553Srgrimes } 146131492Swollman if (!pp->no_formfeed) 146231492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 146331492Swollman pp->tof = 1; 14641553Srgrimes} 14651553Srgrimes 14661553Srgrimesstatic char * 146778146Sgadscnline(int key, char *p, int c) 14681553Srgrimes{ 146939084Swollman register int scnwidth; 14701553Srgrimes 14711553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 14721553Srgrimes key <<= 1; 14731553Srgrimes *p++ = key & 0200 ? c : BACKGND; 14741553Srgrimes } 14751553Srgrimes return (p); 14761553Srgrimes} 14771553Srgrimes 14781553Srgrimes#define TRC(q) (((q)-' ')&0177) 14791553Srgrimes 14801553Srgrimesstatic void 148178146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 14821553Srgrimes{ 14831553Srgrimes register char *strp; 148439084Swollman register int nchrs, j; 14851553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 14861553Srgrimes int d, scnhgt; 14871553Srgrimes 14881553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 14891553Srgrimes strp = &outbuf[0]; 14901553Srgrimes sp = scsp; 14911553Srgrimes for (nchrs = 0; ; ) { 14921553Srgrimes d = dropit(c = TRC(cc = *sp++)); 14931553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 14941553Srgrimes for (j = WIDTH; --j;) 14951553Srgrimes *strp++ = BACKGND; 14961553Srgrimes else 149731492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 149831492Swollman if (*sp == dlm || *sp == '\0' || 149931492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 15001553Srgrimes break; 15011553Srgrimes *strp++ = BACKGND; 15021553Srgrimes *strp++ = BACKGND; 15031553Srgrimes } 15041553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 15051553Srgrimes ; 15061553Srgrimes strp++; 15078857Srgrimes *strp++ = '\n'; 15081553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 15091553Srgrimes } 15101553Srgrimes} 15111553Srgrimes 15121553Srgrimesstatic int 151378146Sgaddropit(int c) 15141553Srgrimes{ 15151553Srgrimes switch(c) { 15161553Srgrimes 15171553Srgrimes case TRC('_'): 15181553Srgrimes case TRC(';'): 15191553Srgrimes case TRC(','): 15201553Srgrimes case TRC('g'): 15211553Srgrimes case TRC('j'): 15221553Srgrimes case TRC('p'): 15231553Srgrimes case TRC('q'): 15241553Srgrimes case TRC('y'): 15251553Srgrimes return (DROP); 15261553Srgrimes 15271553Srgrimes default: 15281553Srgrimes return (0); 15291553Srgrimes } 15301553Srgrimes} 15311553Srgrimes 15321553Srgrimes/* 15331553Srgrimes * sendmail --- 15341553Srgrimes * tell people about job completion 15351553Srgrimes */ 15361553Srgrimesstatic void 153794038Sgadsendmail(struct printer *pp, char *userid, int bombed) 15381553Srgrimes{ 15391553Srgrimes register int i; 15401553Srgrimes int p[2], s; 154178146Sgad register const char *cp; 15421553Srgrimes struct stat stb; 15431553Srgrimes FILE *fp; 15441553Srgrimes 15451553Srgrimes pipe(p); 154631492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1547118881Sgad dup2(p[0], STDIN_FILENO); 15488094Sjkh closelog(); 154931492Swollman closeallfds(3); 155027635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 15511553Srgrimes cp++; 155231492Swollman else 15531553Srgrimes cp = _PATH_SENDMAIL; 155479452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 155531492Swollman _exit(0); 15561553Srgrimes } else if (s > 0) { /* parent */ 1557118881Sgad dup2(p[1], STDOUT_FILENO); 155894038Sgad printf("To: %s@%s\n", userid, origin_host); 155931492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 156015648Sjoerg *jobname ? jobname : "<unknown>"); 156178300Sgad printf("Reply-To: root@%s\n\n", local_host); 15621553Srgrimes printf("Your printer job "); 15631553Srgrimes if (*jobname) 15641553Srgrimes printf("(%s) ", jobname); 156594040Sgad 15661553Srgrimes switch (bombed) { 15671553Srgrimes case OK: 156894040Sgad cp = "OK"; 15691553Srgrimes printf("\ncompleted successfully\n"); 15701553Srgrimes break; 15711553Srgrimes default: 15721553Srgrimes case FATALERR: 157394040Sgad cp = "FATALERR"; 15741553Srgrimes printf("\ncould not be printed\n"); 15751553Srgrimes break; 15761553Srgrimes case NOACCT: 157794040Sgad cp = "NOACCT"; 157878300Sgad printf("\ncould not be printed without an account on %s\n", 157978300Sgad local_host); 15801553Srgrimes break; 15811553Srgrimes case FILTERERR: 158294040Sgad cp = "FILTERERR"; 158368664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 158468664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 158515648Sjoerg printf("\nhad some errors and may not have printed\n"); 15861553Srgrimes break; 15871553Srgrimes } 158815648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 15891553Srgrimes while ((i = getc(fp)) != EOF) 15901553Srgrimes putchar(i); 15911553Srgrimes (void) fclose(fp); 15921553Srgrimes break; 15931553Srgrimes case ACCESS: 159494040Sgad cp = "ACCESS"; 15951553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 15961553Srgrimes } 15971553Srgrimes fflush(stdout); 1598118881Sgad (void) close(STDOUT_FILENO); 159931492Swollman } else { 160094038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 160131492Swollman return; 16021553Srgrimes } 16031553Srgrimes (void) close(p[0]); 16041553Srgrimes (void) close(p[1]); 160515648Sjoerg wait(NULL); 160615648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 160794038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 16081553Srgrimes} 16091553Srgrimes 16101553Srgrimes/* 16111553Srgrimes * dofork - fork with retries on failure 16121553Srgrimes */ 16131553Srgrimesstatic int 161478146Sgaddofork(const struct printer *pp, int action) 16151553Srgrimes{ 161697793Sgad pid_t forkpid; 161797793Sgad int i, fail; 161860871Smpp struct passwd *pwd; 16191553Srgrimes 162080230Sgad forkpid = -1; 162180230Sgad if (daemon_uname == NULL) { 162280230Sgad pwd = getpwuid(pp->daemon_user); 162380230Sgad if (pwd == NULL) { 162480230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 162580230Sgad pp->printer, pp->daemon_user); 162680230Sgad goto error_ret; 162780230Sgad } 162880230Sgad daemon_uname = strdup(pwd->pw_name); 162980230Sgad daemon_defgid = pwd->pw_gid; 163080230Sgad } 163180230Sgad 16321553Srgrimes for (i = 0; i < 20; i++) { 163380230Sgad forkpid = fork(); 163480230Sgad if (forkpid < 0) { 16351553Srgrimes sleep((unsigned)(i*i)); 16361553Srgrimes continue; 16371553Srgrimes } 16381553Srgrimes /* 16391553Srgrimes * Child should run as daemon instead of root 16401553Srgrimes */ 164178146Sgad if (forkpid == 0) { 164280230Sgad errno = 0; 164380230Sgad fail = initgroups(daemon_uname, daemon_defgid); 164480230Sgad if (fail) { 164580230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 164680230Sgad pp->printer, daemon_uname, daemon_defgid); 164760871Smpp break; 164860871Smpp } 164980230Sgad fail = setgid(daemon_defgid); 165080230Sgad if (fail) { 165180230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 165280230Sgad pp->printer, daemon_defgid); 165380230Sgad break; 165480230Sgad } 165580230Sgad fail = setuid(pp->daemon_user); 165680230Sgad if (fail) { 165780230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 165880230Sgad pp->printer, pp->daemon_user); 165980230Sgad break; 166080230Sgad } 166160871Smpp } 166297791Sgad return (forkpid); 16631553Srgrimes } 16641553Srgrimes 166580230Sgad /* 166680230Sgad * An error occurred. If the error is in the child process, then 166780230Sgad * this routine MUST always exit(). DORETURN only effects how 166880230Sgad * errors should be handled in the parent process. 166980230Sgad */ 167080230Sgaderror_ret: 167180230Sgad if (forkpid == 0) { 167280230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 167380230Sgad pp->printer); 167480230Sgad exit(1); 167580230Sgad } 167680230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 167780230Sgad 167880230Sgad sleep(1); /* throttle errors, as a safety measure */ 16791553Srgrimes switch (action) { 16801553Srgrimes case DORETURN: 168197791Sgad return (-1); 16821553Srgrimes default: 16831553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 168480230Sgad /* FALLTHROUGH */ 16851553Srgrimes case DOABORT: 16861553Srgrimes exit(1); 16871553Srgrimes } 16881553Srgrimes /*NOTREACHED*/ 16891553Srgrimes} 16901553Srgrimes 16911553Srgrimes/* 16921553Srgrimes * Kill child processes to abort current job. 16931553Srgrimes */ 16941553Srgrimesstatic void 169578146Sgadabortpr(int signo __unused) 16961553Srgrimes{ 169768664Sgad 169868664Sgad (void) unlink(tempstderr); 16991553Srgrimes kill(0, SIGINT); 170097793Sgad if (of_pid > 0) 170197793Sgad kill(of_pid, SIGCONT); 17021553Srgrimes while (wait(NULL) > 0) 17031553Srgrimes ; 170497793Sgad if (of_pid > 0 && tfd != -1) 170524831Sbrian unlink(tfile); 17061553Srgrimes exit(0); 17071553Srgrimes} 17081553Srgrimes 17091553Srgrimesstatic void 171078146Sgadinit(struct printer *pp) 17111553Srgrimes{ 17121553Srgrimes char *s; 17131553Srgrimes 171431492Swollman sprintf(&width[2], "%ld", pp->page_width); 171531492Swollman sprintf(&length[2], "%ld", pp->page_length); 171631492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 171731492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 171831492Swollman if ((s = checkremote(pp)) != 0) { 171931492Swollman syslog(LOG_WARNING, "%s", s); 172031492Swollman free(s); 172131492Swollman } 172231492Swollman} 172331492Swollman 172431492Swollmanvoid 172578146Sgadstartprinting(const char *printer) 172631492Swollman{ 172731492Swollman struct printer myprinter, *pp = &myprinter; 172831492Swollman int status; 172931492Swollman 173031492Swollman init_printer(pp); 173131492Swollman status = getprintcap(printer, pp); 173231492Swollman switch(status) { 173331492Swollman case PCAPERR_OSERR: 173431492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 17351553Srgrimes exit(1); 173631492Swollman case PCAPERR_NOTFOUND: 17371553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 17381553Srgrimes exit(1); 173931492Swollman case PCAPERR_TCLOOP: 174031492Swollman fatal(pp, "potential reference loop detected in printcap file"); 174131492Swollman default: 174231492Swollman break; 174331492Swollman } 174431492Swollman printjob(pp); 17451553Srgrimes} 17461553Srgrimes 17471553Srgrimes/* 17481553Srgrimes * Acquire line printer or remote connection. 17491553Srgrimes */ 17501553Srgrimesstatic void 175178146Sgadopenpr(const struct printer *pp) 17521553Srgrimes{ 175331492Swollman int p[2]; 175415648Sjoerg char *cp; 17551553Srgrimes 175631492Swollman if (pp->remote) { 175731492Swollman openrem(pp); 175894032Sgad /* 175994032Sgad * Lpd does support the setting of 'of=' filters for 176094032Sgad * jobs going to remote machines, but that does not 176194032Sgad * have the same meaning as 'of=' does when handling 176294032Sgad * local print queues. For remote machines, all 'of=' 176394032Sgad * filter processing is handled in sendfile(), and that 176494032Sgad * does not use these global "output filter" variables. 176594032Sgad */ 176694032Sgad ofd = -1; 176797793Sgad of_pid = 0; 176894032Sgad return; 176931492Swollman } else if (*pp->lp) { 177031492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 177131492Swollman opennet(pp); 177215648Sjoerg else 177331492Swollman opentty(pp); 17741553Srgrimes } else { 17751553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 177697792Sgad pp->printer); 17771553Srgrimes exit(1); 17781553Srgrimes } 177915648Sjoerg 17801553Srgrimes /* 17811553Srgrimes * Start up an output filter, if needed. 17821553Srgrimes */ 178397793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 17841553Srgrimes pipe(p); 178531492Swollman if (pp->remote) { 178631492Swollman strcpy(tfile, TFILENAME); 178724831Sbrian tfd = mkstemp(tfile); 178824831Sbrian } 178997793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 1790118881Sgad dup2(p[0], STDIN_FILENO); /* pipe is std in */ 179124831Sbrian /* tfile/printer is stdout */ 1792118881Sgad dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); 17938094Sjkh closelog(); 179431492Swollman closeallfds(3); 179531492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 179631492Swollman cp = pp->filters[LPF_OUTPUT]; 17971553Srgrimes else 17981553Srgrimes cp++; 179979452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 180079452Sbrian (char *)0); 180197792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 180297792Sgad pp->filters[LPF_OUTPUT]); 18031553Srgrimes exit(1); 18041553Srgrimes } 18051553Srgrimes (void) close(p[0]); /* close input side */ 18061553Srgrimes ofd = p[1]; /* use pipe for output */ 18071553Srgrimes } else { 18081553Srgrimes ofd = pfd; 180997793Sgad of_pid = 0; 18101553Srgrimes } 18111553Srgrimes} 18121553Srgrimes 181315648Sjoerg/* 181415648Sjoerg * Printer connected directly to the network 181515648Sjoerg * or to a terminal server on the net 181615648Sjoerg */ 181715648Sjoergstatic void 181878146Sgadopennet(const struct printer *pp) 181915648Sjoerg{ 182015648Sjoerg register int i; 182131492Swollman int resp; 182231492Swollman u_long port; 182331492Swollman char *ep; 182430407Sjoerg void (*savealrm)(int); 182515648Sjoerg 182631492Swollman port = strtoul(pp->lp, &ep, 0); 182738470Sbrian if (*ep != '@' || port > 65535) { 182831492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 182997792Sgad pp->lp); 183015648Sjoerg exit(1); 183115648Sjoerg } 183231492Swollman ep++; 183315648Sjoerg 183415648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 183515648Sjoerg resp = -1; 183630407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 183731492Swollman alarm(pp->conn_timeout); 183831492Swollman pfd = getport(pp, ep, port); 183931020Sjoerg alarm(0); 184030407Sjoerg (void)signal(SIGALRM, savealrm); 184115648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 184215648Sjoerg resp = 1; 184315648Sjoerg else if (pfd >= 0) { 184415648Sjoerg /* 184515648Sjoerg * need to delay a bit for rs232 lines 184615648Sjoerg * to stabilize in case printer is 184715648Sjoerg * connected via a terminal server 184815648Sjoerg */ 184915648Sjoerg delay(500); 185015648Sjoerg break; 185115648Sjoerg } 185215648Sjoerg if (i == 1) { 185331492Swollman if (resp < 0) 185431492Swollman pstatus(pp, "waiting for %s to come up", 185531492Swollman pp->lp); 185631492Swollman else 185731492Swollman pstatus(pp, 185831492Swollman "waiting for access to printer on %s", 185931492Swollman pp->lp); 186015648Sjoerg } 186115648Sjoerg sleep(i); 186215648Sjoerg } 186379739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 186415648Sjoerg} 186515648Sjoerg 186615648Sjoerg/* 186715648Sjoerg * Printer is connected to an RS232 port on this host 186815648Sjoerg */ 186915648Sjoergstatic void 187078146Sgadopentty(const struct printer *pp) 187115648Sjoerg{ 187215648Sjoerg register int i; 187315648Sjoerg 187415648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 187531492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 187615648Sjoerg if (pfd >= 0) { 187715648Sjoerg delay(500); 187815648Sjoerg break; 187915648Sjoerg } 188015648Sjoerg if (errno == ENOENT) { 188131492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 188215648Sjoerg exit(1); 188315648Sjoerg } 188415648Sjoerg if (i == 1) 188531492Swollman pstatus(pp, 188631492Swollman "waiting for %s to become ready (offline?)", 188731492Swollman pp->printer); 188815648Sjoerg sleep(i); 188915648Sjoerg } 189015648Sjoerg if (isatty(pfd)) 189131492Swollman setty(pp); 189231492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 189315648Sjoerg} 189415648Sjoerg 189515648Sjoerg/* 189615648Sjoerg * Printer is on a remote host 189715648Sjoerg */ 189815648Sjoergstatic void 189978146Sgadopenrem(const struct printer *pp) 190015648Sjoerg{ 190131492Swollman register int i; 190227748Simp int resp; 190330407Sjoerg void (*savealrm)(int); 190415648Sjoerg 190515648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 190615648Sjoerg resp = -1; 190730407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 190831492Swollman alarm(pp->conn_timeout); 190931492Swollman pfd = getport(pp, pp->remote_host, 0); 191031020Sjoerg alarm(0); 191130407Sjoerg (void)signal(SIGALRM, savealrm); 191215648Sjoerg if (pfd >= 0) { 191331492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 191431492Swollman (char *)0) 191531492Swollman == 2 + strlen(pp->remote_queue)) 191631492Swollman && (resp = response(pp)) == 0) 191715648Sjoerg break; 191815648Sjoerg (void) close(pfd); 191915648Sjoerg } 192015648Sjoerg if (i == 1) { 192115648Sjoerg if (resp < 0) 192231492Swollman pstatus(pp, "waiting for %s to come up", 192331492Swollman pp->remote_host); 192415648Sjoerg else { 192531492Swollman pstatus(pp, 192631492Swollman "waiting for queue to be enabled on %s", 192731492Swollman pp->remote_host); 192815648Sjoerg i = 256; 192915648Sjoerg } 193015648Sjoerg } 193115648Sjoerg sleep(i); 193215648Sjoerg } 193331492Swollman pstatus(pp, "sending to %s", pp->remote_host); 193415648Sjoerg} 193515648Sjoerg 19361553Srgrimes/* 19371553Srgrimes * setup tty lines. 19381553Srgrimes */ 19391553Srgrimesstatic void 194078146Sgadsetty(const struct printer *pp) 19411553Srgrimes{ 194215032Ssef struct termios ttybuf; 19431553Srgrimes 19441553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 194531492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 19461553Srgrimes exit(1); 19471553Srgrimes } 194815032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 194931492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 19501553Srgrimes exit(1); 19511553Srgrimes } 195231492Swollman if (pp->baud_rate > 0) 195331492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 195431492Swollman if (pp->mode_set) { 195531492Swollman char *s = strdup(pp->mode_set), *tmp; 195615032Ssef 195731492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 195839084Swollman (void) msearch(tmp, &ttybuf); 19591553Srgrimes } 19601553Srgrimes } 196131492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 196215032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 196331492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 19641553Srgrimes } 19651553Srgrimes } 19661553Srgrimes} 19671553Srgrimes 19681553Srgrimes#include <stdarg.h> 19691553Srgrimes 197015648Sjoergstatic void 197131492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 19721553Srgrimes{ 197331492Swollman int fd; 197431492Swollman char *buf; 19751553Srgrimes va_list ap; 19761553Srgrimes va_start(ap, msg); 19771553Srgrimes 1978139035Sgad umask(S_IWOTH); 197931492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 198031492Swollman if (fd < 0) { 198197792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 198297792Sgad pp->status_file); 19831553Srgrimes exit(1); 19841553Srgrimes } 19851553Srgrimes ftruncate(fd, 0); 198631492Swollman vasprintf(&buf, msg, ap); 19871553Srgrimes va_end(ap); 198831492Swollman writel(fd, buf, "\n", (char *)0); 198931492Swollman close(fd); 199031492Swollman free(buf); 19911553Srgrimes} 199230407Sjoerg 199330407Sjoergvoid 199478146Sgadalarmhandler(int signo __unused) 199530407Sjoerg{ 199678146Sgad /* the signal is ignored */ 199778146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 199830407Sjoerg} 1999