printjob.c revision 228990
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 * 4. Neither the name of the University nor the names of its contributors 151553Srgrimes * may be used to endorse or promote products derived from this software 161553Srgrimes * without specific prior written permission. 171553Srgrimes * 181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281553Srgrimes * SUCH DAMAGE. 291553Srgrimes */ 301553Srgrimes 311553Srgrimes#ifndef lint 3231492Swollmanstatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 37117554Sgad#if 0 38117587Sgad#ifndef lint 3915648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 40117587Sgad#endif /* not lint */ 41117554Sgad#endif 421553Srgrimes 43117554Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44117554Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 228990 2011-12-30 10:58:14Z uqs $"); 451553Srgrimes 461553Srgrimes/* 471553Srgrimes * printjob -- print jobs in the queue. 481553Srgrimes * 491553Srgrimes * NOTE: the lock file is used to pass information to lpq and lprm. 501553Srgrimes * it does not need to be removed because file locks are dynamic. 511553Srgrimes */ 521553Srgrimes 531553Srgrimes#include <sys/param.h> 541553Srgrimes#include <sys/wait.h> 551553Srgrimes#include <sys/stat.h> 561553Srgrimes#include <sys/types.h> 571553Srgrimes 581553Srgrimes#include <pwd.h> 591553Srgrimes#include <unistd.h> 601553Srgrimes#include <signal.h> 611553Srgrimes#include <syslog.h> 621553Srgrimes#include <fcntl.h> 631553Srgrimes#include <dirent.h> 641553Srgrimes#include <errno.h> 65228664Sdim#include <inttypes.h> 661553Srgrimes#include <stdio.h> 671553Srgrimes#include <string.h> 681553Srgrimes#include <stdlib.h> 6915032Ssef#include <sys/ioctl.h> 7015032Ssef#include <termios.h> 7115703Sjoerg#include <time.h> 721553Srgrimes#include "lp.h" 731553Srgrimes#include "lp.local.h" 741553Srgrimes#include "pathnames.h" 751553Srgrimes#include "extern.h" 761553Srgrimes 7780230Sgad#define DORETURN 0 /* dofork should return "can't fork" error */ 7880230Sgad#define DOABORT 1 /* dofork should just die if fork() fails */ 791553Srgrimes 801553Srgrimes/* 81119192Sgad * The buffer size to use when reading/writing spool files. 82119192Sgad */ 83119192Sgad#define SPL_BUFSIZ BUFSIZ 84119192Sgad 85119192Sgad/* 861553Srgrimes * Error tokens 871553Srgrimes */ 881553Srgrimes#define REPRINT -2 891553Srgrimes#define ERROR -1 901553Srgrimes#define OK 0 911553Srgrimes#define FATALERR 1 921553Srgrimes#define NOACCT 2 931553Srgrimes#define FILTERERR 3 941553Srgrimes#define ACCESS 4 951553Srgrimes 961553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 971553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 981553Srgrimesstatic FILE *cfp; /* control file */ 9997793Sgadstatic pid_t of_pid; /* process id of output filter, if any */ 1001553Srgrimesstatic int child; /* id of any filters */ 10168253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 1021553Srgrimesstatic int lfd; /* lock file descriptor */ 1031553Srgrimesstatic int ofd; /* output filter file descriptor */ 10424831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1051553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1061553Srgrimesstatic int prchild; /* id of pr process */ 1071553Srgrimesstatic char title[80]; /* ``pr'' title */ 10853956Sachestatic char locale[80]; /* ``pr'' locale */ 1091553Srgrimes 11080230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 11180230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 11280230Sgadstatic int daemon_defgid; 11380230Sgad 1141553Srgrimesstatic char class[32]; /* classification field */ 11578300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1161553Srgrimes /* indentation size in static characters */ 1178857Srgrimesstatic char indent[10] = "-i0"; 1181553Srgrimesstatic char jobname[100]; /* job or file name */ 1191553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1201553Srgrimesstatic char logname[32]; /* user's login name */ 1211553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1221553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 12368664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12468664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1251553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12624831Sbrian#define TFILENAME "fltXXXXXX" 12724831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1281553Srgrimes 12978146Sgadstatic void abortpr(int _signo); 13078146Sgadstatic void alarmhandler(int _signo); 13178146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 13278146Sgadstatic int dofork(const struct printer *_pp, int _action); 13378146Sgadstatic int dropit(int _c); 13494032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13594032Sgad int _infd, int _outfd); 13678146Sgadstatic void init(struct printer *_pp); 13778146Sgadstatic void openpr(const struct printer *_pp); 13878146Sgadstatic void opennet(const struct printer *_pp); 13978146Sgadstatic void opentty(const struct printer *_pp); 14078146Sgadstatic void openrem(const struct printer *pp); 14178146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 14278146Sgadstatic int printit(struct printer *_pp, char *_file); 14379739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14479739Sgad __printflike(2, 3); 14578146Sgadstatic char response(const struct printer *_pp); 14678146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14778146Sgad int _dlm); 14878146Sgadstatic char *scnline(int _key, char *_p, int _c); 14978146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 15095293Sgad char _format, int _copyreq); 15178146Sgadstatic int sendit(struct printer *_pp, char *_file); 15294038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 15378146Sgadstatic void setty(const struct printer *_pp); 154138939Sgadstatic void wait4data(struct printer *_pp, const char *_dfile); 1551553Srgrimes 1561553Srgrimesvoid 15778146Sgadprintjob(struct printer *pp) 1581553Srgrimes{ 1591553Srgrimes struct stat stb; 16068401Sgad register struct jobqueue *q, **qp; 16168401Sgad struct jobqueue **queue; 1621553Srgrimes register int i, nitems; 16368733Sgad off_t pidoff; 16497793Sgad pid_t printpid; 165160147Sgad int errcnt, jobcount, statok, tempfd; 1661553Srgrimes 16768733Sgad jobcount = 0; 16831492Swollman init(pp); /* set up capabilities */ 169118881Sgad (void) write(STDOUT_FILENO, "", 1); /* ack that daemon is started */ 170118881Sgad (void) close(STDERR_FILENO); /* set up log file */ 17131492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 17297792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 17397792Sgad pp->log_file); 1741553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1751553Srgrimes } 1761553Srgrimes setgid(getegid()); 17797793Sgad printpid = getpid(); /* for use with lprm */ 17897793Sgad setpgrp(0, printpid); 17979735Sgad 18079735Sgad /* 18179735Sgad * At initial lpd startup, printjob may be called with various 18279735Sgad * signal handlers in effect. After that initial startup, any 18379735Sgad * calls to printjob will have a *different* set of signal-handlers 18479735Sgad * in effect. Make sure all handlers are the ones we want. 18579735Sgad */ 18679735Sgad signal(SIGCHLD, SIG_DFL); 1871553Srgrimes signal(SIGHUP, abortpr); 1881553Srgrimes signal(SIGINT, abortpr); 1891553Srgrimes signal(SIGQUIT, abortpr); 1901553Srgrimes signal(SIGTERM, abortpr); 1911553Srgrimes 1921553Srgrimes /* 1931553Srgrimes * uses short form file names 1941553Srgrimes */ 19531492Swollman if (chdir(pp->spool_dir) < 0) { 19697792Sgad syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, 19797792Sgad pp->spool_dir); 1981553Srgrimes exit(1); 1991553Srgrimes } 200160147Sgad statok = stat(pp->lock_file, &stb); 201160147Sgad if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS)) 2021553Srgrimes exit(0); /* printing disabled */ 203139035Sgad umask(S_IWOTH); 20431492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 20531492Swollman LOCK_FILE_MODE); 2061553Srgrimes if (lfd < 0) { 20731492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 20831492Swollman exit(0); 20997792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 21097792Sgad pp->lock_file); 2111553Srgrimes exit(1); 2121553Srgrimes } 213160147Sgad /* 214160147Sgad * If the initial call to stat() failed, then lock_file will have 215160147Sgad * been created by open(). Update &stb to match that new file. 216160147Sgad */ 217160147Sgad if (statok != 0) 218160147Sgad statok = stat(pp->lock_file, &stb); 21931492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 22031492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 22197792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 22297792Sgad pp->lock_file); 2231553Srgrimes exit(1); 2241553Srgrimes } 2251553Srgrimes ftruncate(lfd, 0); 2261553Srgrimes /* 2271553Srgrimes * write process id for others to know 2281553Srgrimes */ 22997793Sgad sprintf(line, "%u\n", printpid); 2301553Srgrimes pidoff = i = strlen(line); 2311553Srgrimes if (write(lfd, line, i) != i) { 23297792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 23397792Sgad pp->lock_file); 2341553Srgrimes exit(1); 2351553Srgrimes } 2361553Srgrimes /* 2371553Srgrimes * search the spool directory for work and sort by queue order. 2381553Srgrimes */ 23931492Swollman if ((nitems = getq(pp, &queue)) < 0) { 24031492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 24197792Sgad pp->spool_dir); 2421553Srgrimes exit(1); 2431553Srgrimes } 2441553Srgrimes if (nitems == 0) /* no work to do */ 2451553Srgrimes exit(0); 24631492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 24731492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 24897792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 24997792Sgad pp->lock_file); 2501553Srgrimes } 25168664Sgad 25268664Sgad /* create a file which will be used to hold stderr from filters */ 25368664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 25468664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 25597792Sgad tempstderr); 25668732Sgad exit(1); 25768664Sgad } 25868664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 25968664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 26097792Sgad tempstderr); 26168732Sgad exit(1); 26268664Sgad } 26368664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 26468664Sgad close(tempfd); 26568664Sgad 26631492Swollman openpr(pp); /* open printer or remote */ 2671553Srgrimesagain: 2681553Srgrimes /* 2691553Srgrimes * we found something to do now do it -- 2701553Srgrimes * write the name of the current control file into the lock file 2711553Srgrimes * so the spool queue program can tell what we're working on 2721553Srgrimes */ 2731553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2741553Srgrimes q = *qp++; 27568401Sgad if (stat(q->job_cfname, &stb) < 0) 2761553Srgrimes continue; 27715648Sjoerg errcnt = 0; 2781553Srgrimes restart: 27915648Sjoerg (void) lseek(lfd, pidoff, 0); 28068401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2811553Srgrimes i = strlen(line); 2821553Srgrimes if (write(lfd, line, i) != i) 28397792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 28497792Sgad pp->lock_file); 28531492Swollman if (!pp->remote) 28668401Sgad i = printit(pp, q->job_cfname); 2871553Srgrimes else 28868401Sgad i = sendit(pp, q->job_cfname); 2891553Srgrimes /* 2901553Srgrimes * Check to see if we are supposed to stop printing or 2911553Srgrimes * if we are to rebuild the queue. 2921553Srgrimes */ 2931553Srgrimes if (fstat(lfd, &stb) == 0) { 2941553Srgrimes /* stop printing before starting next job? */ 29531492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2961553Srgrimes goto done; 2971553Srgrimes /* rebuild queue (after lpc topq) */ 29831492Swollman if (stb.st_mode & LFM_RESET_QUE) { 29931492Swollman for (free(q); nitems--; free(q)) 3001553Srgrimes q = *qp++; 30131492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 30231492Swollman < 0) 30397792Sgad syslog(LOG_WARNING, 30497792Sgad "%s: fchmod(%s): %m", 30597792Sgad pp->printer, pp->lock_file); 3061553Srgrimes break; 3071553Srgrimes } 3081553Srgrimes } 30968733Sgad if (i == OK) /* all files of this job printed */ 31068733Sgad jobcount++; 31115648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 31215648Sjoerg /* try reprinting the job */ 31331492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 31497793Sgad if (of_pid > 0) { 31597793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3161553Srgrimes (void) close(ofd); 31797793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3181553Srgrimes ; 31979735Sgad if (i < 0) 32079735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 32197793Sgad pp->printer, of_pid); 32297793Sgad of_pid = 0; 3231553Srgrimes } 3241553Srgrimes (void) close(pfd); /* close printer */ 3251553Srgrimes if (ftruncate(lfd, pidoff) < 0) 32697792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 32797792Sgad pp->printer, pp->lock_file); 32831492Swollman openpr(pp); /* try to reopen printer */ 3291553Srgrimes goto restart; 33015648Sjoerg } else { 33131492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 33297792Sgad pp->printer, 33397792Sgad pp->remote ? "sent to remote host" : "printed", 33497792Sgad q->job_cfname); 33515648Sjoerg if (i == REPRINT) { 33627748Simp /* ensure we don't attempt this job again */ 33768401Sgad (void) unlink(q->job_cfname); 33868401Sgad q->job_cfname[0] = 'd'; 33968401Sgad (void) unlink(q->job_cfname); 34015648Sjoerg if (logname[0]) 34131492Swollman sendmail(pp, logname, FATALERR); 34215648Sjoerg } 3431553Srgrimes } 3441553Srgrimes } 34531492Swollman free(queue); 3461553Srgrimes /* 3471553Srgrimes * search the spool directory for more work. 3481553Srgrimes */ 34931492Swollman if ((nitems = getq(pp, &queue)) < 0) { 35031492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 35197792Sgad pp->spool_dir); 3521553Srgrimes exit(1); 3531553Srgrimes } 3541553Srgrimes if (nitems == 0) { /* no more work to do */ 3551553Srgrimes done: 35668733Sgad if (jobcount > 0) { /* jobs actually printed */ 35731492Swollman if (!pp->no_formfeed && !pp->tof) 35831492Swollman (void) write(ofd, pp->form_feed, 35931492Swollman strlen(pp->form_feed)); 36031492Swollman if (pp->trailer != NULL) /* output trailer */ 36131492Swollman (void) write(ofd, pp->trailer, 36231492Swollman strlen(pp->trailer)); 3631553Srgrimes } 36419202Simp (void) close(ofd); 36519202Simp (void) wait(NULL); 36668664Sgad (void) unlink(tempstderr); 3671553Srgrimes exit(0); 3681553Srgrimes } 3691553Srgrimes goto again; 3701553Srgrimes} 3711553Srgrimes 3721553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3731553Srgrimes 3741553Srgrimeschar ifonts[4][40] = { 3751553Srgrimes _PATH_VFONTR, 3761553Srgrimes _PATH_VFONTI, 3771553Srgrimes _PATH_VFONTB, 3781553Srgrimes _PATH_VFONTS, 3791553Srgrimes}; 3801553Srgrimes 3811553Srgrimes/* 3821553Srgrimes * The remaining part is the reading of the control file (cf) 3831553Srgrimes * and performing the various actions. 3841553Srgrimes */ 3851553Srgrimesstatic int 38678146Sgadprintit(struct printer *pp, char *file) 3871553Srgrimes{ 3881553Srgrimes register int i; 38968734Sgad char *cp; 39068734Sgad int bombed, didignorehdr; 3911553Srgrimes 39268734Sgad bombed = OK; 39368734Sgad didignorehdr = 0; 3941553Srgrimes /* 3951553Srgrimes * open control file; ignore if no longer there. 3961553Srgrimes */ 3971553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 39897792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 39997791Sgad return (OK); 4001553Srgrimes } 4011553Srgrimes /* 4021553Srgrimes * Reset troff fonts. 4031553Srgrimes */ 4041553Srgrimes for (i = 0; i < 4; i++) 4051553Srgrimes strcpy(fonts[i], ifonts[i]); 40631492Swollman sprintf(&width[2], "%ld", pp->page_width); 4071553Srgrimes strcpy(indent+2, "0"); 4081553Srgrimes 40968253Sgad /* initialize job-specific count of datafiles processed */ 41068253Sgad job_dfcnt = 0; 41168253Sgad 4121553Srgrimes /* 4131553Srgrimes * read the control file for work to do 4141553Srgrimes * 4151553Srgrimes * file format -- first character in the line is a command 4161553Srgrimes * rest of the line is the argument. 4171553Srgrimes * valid commands are: 4181553Srgrimes * 4191553Srgrimes * S -- "stat info" for symbolic link protection 4201553Srgrimes * J -- "job name" on banner page 4211553Srgrimes * C -- "class name" on banner page 4221553Srgrimes * L -- "literal" user's name to print on banner 4231553Srgrimes * T -- "title" for pr 4241553Srgrimes * H -- "host name" of machine where lpr was done 4251553Srgrimes * P -- "person" user's login name 4261553Srgrimes * I -- "indent" amount to indent output 42715648Sjoerg * R -- laser dpi "resolution" 4281553Srgrimes * f -- "file name" name of text file to print 4291553Srgrimes * l -- "file name" text file with control chars 43083684Sgad * o -- "file name" postscript file, according to 43183684Sgad * the RFC. Here it is treated like an 'f'. 4321553Srgrimes * p -- "file name" text file to print with pr(1) 4331553Srgrimes * t -- "file name" troff(1) file to print 4341553Srgrimes * n -- "file name" ditroff(1) file to print 4351553Srgrimes * d -- "file name" dvi file to print 4361553Srgrimes * g -- "file name" plot(1G) file to print 4371553Srgrimes * v -- "file name" plain raster file to print 4381553Srgrimes * c -- "file name" cifplot file to print 4391553Srgrimes * 1 -- "R font file" for troff 4401553Srgrimes * 2 -- "I font file" for troff 4411553Srgrimes * 3 -- "B font file" for troff 4421553Srgrimes * 4 -- "S font file" for troff 4431553Srgrimes * N -- "name" of file (used by lpq) 4441553Srgrimes * U -- "unlink" name of file to remove 4451553Srgrimes * (after we print it. (Pass 2 only)). 4461553Srgrimes * M -- "mail" to user when done printing 44753956Sache * Z -- "locale" for pr 4481553Srgrimes * 4491553Srgrimes * getline reads a line and expands tabs to blanks 4501553Srgrimes */ 4511553Srgrimes 4521553Srgrimes /* pass 1 */ 4531553Srgrimes 4541553Srgrimes while (getline(cfp)) 4551553Srgrimes switch (line[0]) { 4561553Srgrimes case 'H': 45778300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 45827748Simp if (class[0] == '\0') { 45980133Sgad strlcpy(class, line+1, sizeof(class)); 46027748Simp } 4611553Srgrimes continue; 4621553Srgrimes 4631553Srgrimes case 'P': 46480133Sgad strlcpy(logname, line + 1, sizeof(logname)); 46531492Swollman if (pp->restricted) { /* restricted */ 4661553Srgrimes if (getpwnam(logname) == NULL) { 4671553Srgrimes bombed = NOACCT; 46831492Swollman sendmail(pp, line+1, bombed); 4691553Srgrimes goto pass2; 4701553Srgrimes } 4711553Srgrimes } 4721553Srgrimes continue; 4731553Srgrimes 4741553Srgrimes case 'S': 4751553Srgrimes cp = line+1; 4761553Srgrimes i = 0; 4771553Srgrimes while (*cp >= '0' && *cp <= '9') 4781553Srgrimes i = i * 10 + (*cp++ - '0'); 4791553Srgrimes fdev = i; 4801553Srgrimes cp++; 4811553Srgrimes i = 0; 4821553Srgrimes while (*cp >= '0' && *cp <= '9') 4831553Srgrimes i = i * 10 + (*cp++ - '0'); 4841553Srgrimes fino = i; 4851553Srgrimes continue; 4861553Srgrimes 4871553Srgrimes case 'J': 48827748Simp if (line[1] != '\0') { 48980133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 49027748Simp } else 4911553Srgrimes strcpy(jobname, " "); 4921553Srgrimes continue; 4931553Srgrimes 4941553Srgrimes case 'C': 4951553Srgrimes if (line[1] != '\0') 49680133Sgad strlcpy(class, line + 1, sizeof(class)); 49780133Sgad else if (class[0] == '\0') { 49880133Sgad /* XXX - why call gethostname instead of 49980133Sgad * just strlcpy'ing local_host? */ 5001553Srgrimes gethostname(class, sizeof(class)); 50180133Sgad class[sizeof(class) - 1] = '\0'; 50280133Sgad } 5031553Srgrimes continue; 5041553Srgrimes 5051553Srgrimes case 'T': /* header title for pr */ 50680133Sgad strlcpy(title, line + 1, sizeof(title)); 5071553Srgrimes continue; 5081553Srgrimes 5091553Srgrimes case 'L': /* identification line */ 51031492Swollman if (!pp->no_header && !pp->header_last) 51131492Swollman banner(pp, line+1, jobname); 5121553Srgrimes continue; 5131553Srgrimes 5141553Srgrimes case '1': /* troff fonts */ 5151553Srgrimes case '2': 5161553Srgrimes case '3': 5171553Srgrimes case '4': 51827748Simp if (line[1] != '\0') { 51980133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 52080133Sgad (size_t)50); 52127748Simp } 5221553Srgrimes continue; 5231553Srgrimes 5241553Srgrimes case 'W': /* page width */ 52580133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5261553Srgrimes continue; 5271553Srgrimes 5281553Srgrimes case 'I': /* indent amount */ 52980133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5301553Srgrimes continue; 5311553Srgrimes 53253956Sache case 'Z': /* locale for pr */ 53380133Sgad strlcpy(locale, line + 1, sizeof(locale)); 53453956Sache continue; 53553956Sache 5361553Srgrimes default: /* some file to print */ 53768467Sgad /* only lowercase cmd-codes include a file-to-print */ 53868467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 53968467Sgad /* ignore any other lines */ 54068467Sgad if (lflag <= 1) 54168467Sgad continue; 54268467Sgad if (!didignorehdr) { 54368467Sgad syslog(LOG_INFO, "%s: in %s :", 54497792Sgad pp->printer, file); 54568467Sgad didignorehdr = 1; 54668467Sgad } 54768467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 54897792Sgad pp->printer, line[0], &line[1]); 54968467Sgad continue; 55068467Sgad } 55168467Sgad i = print(pp, line[0], line+1); 55268467Sgad switch (i) { 5531553Srgrimes case ERROR: 5541553Srgrimes if (bombed == OK) 5551553Srgrimes bombed = FATALERR; 5561553Srgrimes break; 5571553Srgrimes case REPRINT: 5581553Srgrimes (void) fclose(cfp); 55997791Sgad return (REPRINT); 5601553Srgrimes case FILTERERR: 5611553Srgrimes case ACCESS: 5621553Srgrimes bombed = i; 56331492Swollman sendmail(pp, logname, bombed); 5641553Srgrimes } 5651553Srgrimes title[0] = '\0'; 5661553Srgrimes continue; 5671553Srgrimes 5681553Srgrimes case 'N': 5691553Srgrimes case 'U': 5701553Srgrimes case 'M': 57115648Sjoerg case 'R': 5721553Srgrimes continue; 5731553Srgrimes } 5741553Srgrimes 5751553Srgrimes /* pass 2 */ 5761553Srgrimes 5771553Srgrimespass2: 5781553Srgrimes fseek(cfp, 0L, 0); 5791553Srgrimes while (getline(cfp)) 5801553Srgrimes switch (line[0]) { 5811553Srgrimes case 'L': /* identification line */ 58231492Swollman if (!pp->no_header && pp->header_last) 58331492Swollman banner(pp, line+1, jobname); 5841553Srgrimes continue; 5851553Srgrimes 5861553Srgrimes case 'M': 5871553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 58831492Swollman sendmail(pp, line+1, bombed); 5891553Srgrimes continue; 5901553Srgrimes 5911553Srgrimes case 'U': 59227748Simp if (strchr(line+1, '/')) 59327748Simp continue; 5941553Srgrimes (void) unlink(line+1); 5951553Srgrimes } 5961553Srgrimes /* 5971553Srgrimes * clean-up in case another control file exists 5981553Srgrimes */ 5991553Srgrimes (void) fclose(cfp); 6001553Srgrimes (void) unlink(file); 60197791Sgad return (bombed == OK ? OK : ERROR); 6021553Srgrimes} 6031553Srgrimes 6041553Srgrimes/* 6051553Srgrimes * Print a file. 6061553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 607228990Suqs * Return -1 if a non-recoverable error occurred, 6081553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 6091553Srgrimes * 1 if we should try to reprint this job and 6101553Srgrimes * 0 if all is well. 6111553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 6121553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6131553Srgrimes */ 6141553Srgrimesstatic int 61578146Sgadprint(struct printer *pp, int format, char *file) 6161553Srgrimes{ 61753956Sache register int n, i; 6181553Srgrimes register char *prog; 61931492Swollman int fi, fo; 6201553Srgrimes FILE *fp; 621119192Sgad char *av[15], buf[SPL_BUFSIZ]; 62297793Sgad pid_t wpid; 62397793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6241553Srgrimes struct stat stb; 6251553Srgrimes 626138939Sgad /* Make sure the entire data file has arrived. */ 627138939Sgad wait4data(pp, file); 628138939Sgad 62968467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 63068467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 63197792Sgad pp->printer, file, format); 63297791Sgad return (ERROR); 63368467Sgad } 6341553Srgrimes /* 6351553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6361553Srgrimes * still point to the same file or someone is trying to print 6371553Srgrimes * something he shouldn't. 6381553Srgrimes */ 6391553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6401553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 64197791Sgad return (ACCESS); 64268253Sgad 64368253Sgad job_dfcnt++; /* increment datafile counter for this job */ 64468734Sgad stopped = 0; /* output filter is not stopped */ 64568253Sgad 64668253Sgad /* everything seems OK, start it up */ 64731492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 64831492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 64931492Swollman pp->tof = 1; 6501553Srgrimes } 65131492Swollman if (pp->filters[LPF_INPUT] == NULL 65283684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 65331492Swollman pp->tof = 0; 654119192Sgad while ((n = read(fi, buf, SPL_BUFSIZ)) > 0) 6551553Srgrimes if (write(ofd, buf, n) != n) { 6561553Srgrimes (void) close(fi); 65797791Sgad return (REPRINT); 6581553Srgrimes } 6591553Srgrimes (void) close(fi); 66097791Sgad return (OK); 6611553Srgrimes } 6621553Srgrimes switch (format) { 6631553Srgrimes case 'p': /* print file using 'pr' */ 66431492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6651553Srgrimes prog = _PATH_PR; 66653956Sache i = 0; 66753956Sache av[i++] = "pr"; 66853956Sache av[i++] = width; 66953956Sache av[i++] = length; 67053956Sache av[i++] = "-h"; 67153956Sache av[i++] = *title ? title : " "; 67253956Sache av[i++] = "-L"; 67353956Sache av[i++] = *locale ? locale : "C"; 67453956Sache av[i++] = "-F"; 67553956Sache av[i] = 0; 6761553Srgrimes fo = ofd; 6771553Srgrimes goto start; 6781553Srgrimes } 6791553Srgrimes pipe(p); 68031492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 681118881Sgad dup2(fi, STDIN_FILENO); /* file is stdin */ 682118881Sgad dup2(p[1], STDOUT_FILENO); /* pipe is stdout */ 6838094Sjkh closelog(); 68431492Swollman closeallfds(3); 6851553Srgrimes execl(_PATH_PR, "pr", width, length, 68653956Sache "-h", *title ? title : " ", 68753956Sache "-L", *locale ? locale : "C", 68879452Sbrian "-F", (char *)0); 6891553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6901553Srgrimes exit(2); 6911553Srgrimes } 6921553Srgrimes (void) close(p[1]); /* close output side */ 6931553Srgrimes (void) close(fi); 6941553Srgrimes if (prchild < 0) { 6951553Srgrimes prchild = 0; 6961553Srgrimes (void) close(p[0]); 69797791Sgad return (ERROR); 6981553Srgrimes } 6991553Srgrimes fi = p[0]; /* use pipe for input */ 7001553Srgrimes case 'f': /* print plain text file */ 70131492Swollman prog = pp->filters[LPF_INPUT]; 7021553Srgrimes av[1] = width; 7031553Srgrimes av[2] = length; 7041553Srgrimes av[3] = indent; 7051553Srgrimes n = 4; 7061553Srgrimes break; 70786935Sgad case 'o': /* print postscript file */ 70886935Sgad /* 70986935Sgad * Treat this as a "plain file with control characters", and 71086935Sgad * assume the standard LPF_INPUT filter will recognize that 71186935Sgad * the data is postscript and know what to do with it. These 71286935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 71386935Sgad * (later versions of MacOS 10 will explicitly use 'l') 71486935Sgad * A postscript file can contain binary data, which is why 'l' 71586935Sgad * is somewhat more appropriate than 'f'. 71686935Sgad */ 71786935Sgad /* FALLTHROUGH */ 7181553Srgrimes case 'l': /* like 'f' but pass control characters */ 71931492Swollman prog = pp->filters[LPF_INPUT]; 7201553Srgrimes av[1] = "-c"; 7211553Srgrimes av[2] = width; 7221553Srgrimes av[3] = length; 7231553Srgrimes av[4] = indent; 7241553Srgrimes n = 5; 7251553Srgrimes break; 7261553Srgrimes case 'r': /* print a fortran text file */ 72731492Swollman prog = pp->filters[LPF_FORTRAN]; 7281553Srgrimes av[1] = width; 7291553Srgrimes av[2] = length; 7301553Srgrimes n = 3; 7311553Srgrimes break; 7321553Srgrimes case 't': /* print troff output */ 7331553Srgrimes case 'n': /* print ditroff output */ 7341553Srgrimes case 'd': /* print tex output */ 7351553Srgrimes (void) unlink(".railmag"); 7361553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 73731492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 73897792Sgad pp->printer); 7391553Srgrimes (void) unlink(".railmag"); 7401553Srgrimes } else { 7411553Srgrimes for (n = 0; n < 4; n++) { 7421553Srgrimes if (fonts[n][0] != '/') 7431553Srgrimes (void) write(fo, _PATH_VFONT, 7441553Srgrimes sizeof(_PATH_VFONT) - 1); 7451553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7461553Srgrimes (void) write(fo, "\n", 1); 7471553Srgrimes } 7481553Srgrimes (void) close(fo); 7491553Srgrimes } 75031492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 75131492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 75231492Swollman : pp->filters[LPF_DVI]); 7531553Srgrimes av[1] = pxwidth; 7541553Srgrimes av[2] = pxlength; 7551553Srgrimes n = 3; 7561553Srgrimes break; 7571553Srgrimes case 'c': /* print cifplot output */ 75831492Swollman prog = pp->filters[LPF_CIFPLOT]; 7591553Srgrimes av[1] = pxwidth; 7601553Srgrimes av[2] = pxlength; 7611553Srgrimes n = 3; 7621553Srgrimes break; 7631553Srgrimes case 'g': /* print plot(1G) output */ 76431492Swollman prog = pp->filters[LPF_GRAPH]; 7651553Srgrimes av[1] = pxwidth; 7661553Srgrimes av[2] = pxlength; 7671553Srgrimes n = 3; 7681553Srgrimes break; 7691553Srgrimes case 'v': /* print raster output */ 77031492Swollman prog = pp->filters[LPF_RASTER]; 7711553Srgrimes av[1] = pxwidth; 7721553Srgrimes av[2] = pxlength; 7731553Srgrimes n = 3; 7741553Srgrimes break; 7751553Srgrimes default: 7761553Srgrimes (void) close(fi); 7771553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 77897792Sgad pp->printer, format); 77997791Sgad return (ERROR); 7801553Srgrimes } 78115648Sjoerg if (prog == NULL) { 78215648Sjoerg (void) close(fi); 78315648Sjoerg syslog(LOG_ERR, 78415648Sjoerg "%s: no filter found in printcap for format character '%c'", 78531492Swollman pp->printer, format); 78697791Sgad return (ERROR); 78715648Sjoerg } 78827635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7891553Srgrimes av[0]++; 7901553Srgrimes else 7911553Srgrimes av[0] = prog; 7921553Srgrimes av[n++] = "-n"; 7931553Srgrimes av[n++] = logname; 7941553Srgrimes av[n++] = "-h"; 79578300Sgad av[n++] = origin_host; 79631492Swollman av[n++] = pp->acct_file; 7971553Srgrimes av[n] = 0; 7981553Srgrimes fo = pfd; 79997793Sgad if (of_pid > 0) { /* stop output filter */ 8001553Srgrimes write(ofd, "\031\1", 2); 80197793Sgad while ((wpid = 80297793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 8031553Srgrimes ; 80497793Sgad if (wpid < 0) 80597792Sgad syslog(LOG_WARNING, 80697792Sgad "%s: after stopping 'of', wait3() returned: %m", 80779735Sgad pp->printer); 80897781Sgad else if (!WIFSTOPPED(wstatus)) { 8091553Srgrimes (void) close(fi); 81097781Sgad syslog(LOG_WARNING, "%s: output filter died " 81197781Sgad "(pid=%d retcode=%d termsig=%d)", 81297793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 81397781Sgad WTERMSIG(wstatus)); 81497791Sgad return (REPRINT); 8151553Srgrimes } 8161553Srgrimes stopped++; 8171553Srgrimes } 8181553Srgrimesstart: 81931492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 820118881Sgad dup2(fi, STDIN_FILENO); 821118881Sgad dup2(fo, STDOUT_FILENO); 82268664Sgad /* setup stderr for the filter (child process) 82368664Sgad * so it goes to our temporary errors file */ 82468664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8251553Srgrimes if (n >= 0) 826118881Sgad dup2(n, STDERR_FILENO); 8278094Sjkh closelog(); 82831492Swollman closeallfds(3); 8291553Srgrimes execv(prog, av); 83095067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 83195067Sgad prog); 8321553Srgrimes exit(2); 8331553Srgrimes } 8341553Srgrimes (void) close(fi); 83597789Sgad wstatus_set = 0; 8361553Srgrimes if (child < 0) 83797781Sgad retcode = 100; 83879735Sgad else { 83997793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8401553Srgrimes ; 84197793Sgad if (wpid < 0) { 84297781Sgad retcode = 100; 84397792Sgad syslog(LOG_WARNING, 84497792Sgad "%s: after execv(%s), wait() returned: %m", 84579735Sgad pp->printer, prog); 84697789Sgad } else { 84797789Sgad wstatus_set = 1; 84897781Sgad retcode = WEXITSTATUS(wstatus); 84997789Sgad } 85079735Sgad } 8511553Srgrimes child = 0; 8521553Srgrimes prchild = 0; 8531553Srgrimes if (stopped) { /* restart output filter */ 85497793Sgad if (kill(of_pid, SIGCONT) < 0) { 8551553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8561553Srgrimes exit(1); 8571553Srgrimes } 8581553Srgrimes } 85931492Swollman pp->tof = 0; 8601553Srgrimes 86168664Sgad /* Copy the filter's output to "lf" logfile */ 86268664Sgad if ((fp = fopen(tempstderr, "r"))) { 8631553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8641553Srgrimes fputs(buf, stderr); 8651553Srgrimes fclose(fp); 8661553Srgrimes } 8671553Srgrimes 86897789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 86915648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 87097781Sgad pp->printer, format, WTERMSIG(wstatus)); 87197791Sgad return (ERROR); 8721553Srgrimes } 87397781Sgad switch (retcode) { 8741553Srgrimes case 0: 87531492Swollman pp->tof = 1; 87697791Sgad return (OK); 8771553Srgrimes case 1: 87897791Sgad return (REPRINT); 87915648Sjoerg case 2: 88097791Sgad return (ERROR); 8811553Srgrimes default: 88215648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 88397792Sgad pp->printer, format, retcode); 88497791Sgad return (FILTERERR); 8851553Srgrimes } 8861553Srgrimes} 8871553Srgrimes 8881553Srgrimes/* 8891553Srgrimes * Send the daemon control file (cf) and any data files. 890228990Suqs * Return -1 if a non-recoverable error occurred, 1 if a recoverable error and 8911553Srgrimes * 0 if all is well. 8921553Srgrimes */ 8931553Srgrimesstatic int 89478146Sgadsendit(struct printer *pp, char *file) 8951553Srgrimes{ 89695293Sgad int dfcopies, err, i; 897119192Sgad char *cp, last[sizeof(line)]; 8981553Srgrimes 8991553Srgrimes /* 9001553Srgrimes * open control file 9011553Srgrimes */ 9021553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 90397791Sgad return (OK); 90468253Sgad 90568253Sgad /* initialize job-specific count of datafiles processed */ 90668253Sgad job_dfcnt = 0; 90768253Sgad 9081553Srgrimes /* 9091553Srgrimes * read the control file for work to do 9101553Srgrimes * 9111553Srgrimes * file format -- first character in the line is a command 9121553Srgrimes * rest of the line is the argument. 9131553Srgrimes * commands of interest are: 9141553Srgrimes * 9151553Srgrimes * a-z -- "file name" name of file to print 9161553Srgrimes * U -- "unlink" name of file to remove 9171553Srgrimes * (after we print it. (Pass 2 only)). 9181553Srgrimes */ 9191553Srgrimes 9201553Srgrimes /* 9211553Srgrimes * pass 1 9221553Srgrimes */ 92395293Sgad err = OK; 9241553Srgrimes while (getline(cfp)) { 9251553Srgrimes again: 9261553Srgrimes if (line[0] == 'S') { 9271553Srgrimes cp = line+1; 9281553Srgrimes i = 0; 9291553Srgrimes while (*cp >= '0' && *cp <= '9') 9301553Srgrimes i = i * 10 + (*cp++ - '0'); 9311553Srgrimes fdev = i; 9321553Srgrimes cp++; 9331553Srgrimes i = 0; 9341553Srgrimes while (*cp >= '0' && *cp <= '9') 9351553Srgrimes i = i * 10 + (*cp++ - '0'); 9361553Srgrimes fino = i; 93724831Sbrian } else if (line[0] == 'H') { 93878300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 93968343Sgad if (class[0] == '\0') { 94080133Sgad strlcpy(class, line + 1, sizeof(class)); 94168343Sgad } 94224831Sbrian } else if (line[0] == 'P') { 94380133Sgad strlcpy(logname, line + 1, sizeof(logname)); 94431492Swollman if (pp->restricted) { /* restricted */ 94524831Sbrian if (getpwnam(logname) == NULL) { 94631492Swollman sendmail(pp, line+1, NOACCT); 94724831Sbrian err = ERROR; 94824831Sbrian break; 94924831Sbrian } 95024831Sbrian } 95124831Sbrian } else if (line[0] == 'I') { 95280133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 95324831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 95495293Sgad dfcopies = 1; 9551553Srgrimes strcpy(last, line); 95695293Sgad while ((i = getline(cfp)) != 0) { 95795293Sgad if (strcmp(last, line) != 0) 9581553Srgrimes break; 95995293Sgad dfcopies++; 96095293Sgad } 96195293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9621553Srgrimes case OK: 9631553Srgrimes if (i) 9641553Srgrimes goto again; 9651553Srgrimes break; 9661553Srgrimes case REPRINT: 9671553Srgrimes (void) fclose(cfp); 96897791Sgad return (REPRINT); 9691553Srgrimes case ACCESS: 97031492Swollman sendmail(pp, logname, ACCESS); 9711553Srgrimes case ERROR: 9721553Srgrimes err = ERROR; 9731553Srgrimes } 9741553Srgrimes break; 9751553Srgrimes } 9761553Srgrimes } 97795293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9781553Srgrimes (void) fclose(cfp); 97997791Sgad return (REPRINT); 9801553Srgrimes } 9811553Srgrimes /* 9821553Srgrimes * pass 2 9831553Srgrimes */ 9841553Srgrimes fseek(cfp, 0L, 0); 9851553Srgrimes while (getline(cfp)) 98627748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9871553Srgrimes (void) unlink(line+1); 9881553Srgrimes /* 9891553Srgrimes * clean-up in case another control file exists 9901553Srgrimes */ 9911553Srgrimes (void) fclose(cfp); 9921553Srgrimes (void) unlink(file); 99397791Sgad return (err); 9941553Srgrimes} 9951553Srgrimes 9961553Srgrimes/* 9971553Srgrimes * Send a data file to the remote machine and spool it. 9981553Srgrimes * Return positive if we should try resending. 9991553Srgrimes */ 10001553Srgrimesstatic int 100195293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 10021553Srgrimes{ 100394036Sgad int i, amt; 10041553Srgrimes struct stat stb; 100594032Sgad char *av[15], *filtcmd; 1006119192Sgad char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 100795293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 10081553Srgrimes 1009138939Sgad /* Make sure the entire data file has arrived. */ 1010138939Sgad wait4data(pp, file); 1011138939Sgad 101274124Sgad statrc = lstat(file, &stb); 101374124Sgad if (statrc < 0) { 101474124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 101574124Sgad pp->printer, file); 101694036Sgad return (ERROR); 101774124Sgad } 101894036Sgad sfd = open(file, O_RDONLY); 101994036Sgad if (sfd < 0) { 102074124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 102174124Sgad pp->printer, file); 102294036Sgad return (ERROR); 102374124Sgad } 10241553Srgrimes /* 10251553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10261553Srgrimes * still point to the same file or someone is trying to print something 10271553Srgrimes * he shouldn't. 10281553Srgrimes */ 102994036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 103094036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 103194036Sgad close(sfd); 103294036Sgad return (ACCESS); 103394036Sgad } 103424831Sbrian 103594032Sgad /* Everything seems OK for reading the file, now to send it */ 103694032Sgad filtcmd = NULL; 103724831Sbrian sizerr = 0; 103894032Sgad tfd = -1; 103924831Sbrian if (type == '\3') { 104094032Sgad /* 104194032Sgad * Type == 3 means this is a datafile, not a control file. 104294032Sgad * Increment the counter of data-files in this job, and 104394032Sgad * then check for input or output filters (which are only 104494032Sgad * applied to datafiles, not control files). 104594032Sgad */ 104694032Sgad job_dfcnt++; 104794032Sgad 104894032Sgad /* 104994032Sgad * Note that here we are filtering datafiles, one at a time, 105094032Sgad * as they are sent to the remote machine. Here, the *only* 105194032Sgad * difference between an input filter (`if=') and an output 105294032Sgad * filter (`of=') is the argument list that the filter is 105394032Sgad * started up with. Here, the output filter is executed 105494032Sgad * for each individual file as it is sent. This is not the 105594032Sgad * same as local print queues, where the output filter is 105694032Sgad * started up once, and then all jobs are passed thru that 105794032Sgad * single invocation of the output filter. 105894032Sgad * 105994032Sgad * Also note that a queue for a remote-machine can have an 106094032Sgad * input filter or an output filter, but not both. 106194032Sgad */ 106231492Swollman if (pp->filters[LPF_INPUT]) { 106394032Sgad filtcmd = pp->filters[LPF_INPUT]; 106494032Sgad av[0] = filtcmd; 106594032Sgad narg = 0; 106694032Sgad strcpy(opt_c, "-c"); 106794032Sgad strcpy(opt_h, "-h"); 106894032Sgad strcpy(opt_n, "-n"); 106924831Sbrian if (format == 'l') 107094032Sgad av[++narg] = opt_c; 107194032Sgad av[++narg] = width; 107294032Sgad av[++narg] = length; 107394032Sgad av[++narg] = indent; 107494032Sgad av[++narg] = opt_n; 107594032Sgad av[++narg] = logname; 107694032Sgad av[++narg] = opt_h; 107794032Sgad av[++narg] = origin_host; 107894032Sgad av[++narg] = pp->acct_file; 107994032Sgad av[++narg] = NULL; 108094032Sgad } else if (pp->filters[LPF_OUTPUT]) { 108194032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 108294032Sgad av[0] = filtcmd; 108394032Sgad narg = 0; 108494032Sgad av[++narg] = width; 108594032Sgad av[++narg] = length; 108694032Sgad av[++narg] = NULL; 108724831Sbrian } 108824831Sbrian } 108994032Sgad if (filtcmd) { 109094032Sgad /* 109194032Sgad * If there is an input or output filter, we have to run 109294032Sgad * the datafile thru that filter and store the result as 109394032Sgad * a temporary spool file, because the protocol requires 109494032Sgad * that we send the remote host the file-size before we 109594032Sgad * start to send any of the data. 109694032Sgad */ 109794032Sgad strcpy(tfile, TFILENAME); 109894032Sgad tfd = mkstemp(tfile); 109994032Sgad if (tfd == -1) { 110094032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 110194032Sgad TFILENAME); 110294036Sgad sfres = ERROR; 110394036Sgad goto return_sfres; 110494032Sgad } 110594036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 110624831Sbrian 110794032Sgad /* process the return-code from the filter */ 110894032Sgad switch (filtstat) { 110994032Sgad case 0: 111094032Sgad break; 111194032Sgad case 1: 111294036Sgad sfres = REPRINT; 111394036Sgad goto return_sfres; 111494032Sgad case 2: 111594036Sgad sfres = ERROR; 111694036Sgad goto return_sfres; 111794032Sgad default: 111894032Sgad syslog(LOG_WARNING, 111994032Sgad "%s: filter '%c' exited (retcode=%d)", 112094032Sgad pp->printer, format, filtstat); 112194036Sgad sfres = FILTERERR; 112294036Sgad goto return_sfres; 112394032Sgad } 112494032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 112594032Sgad if (statrc < 0) { 112694032Sgad syslog(LOG_ERR, 112794032Sgad "%s: error processing 'if', fstat(%s): %m", 112894032Sgad pp->printer, tfile); 112994036Sgad sfres = ERROR; 113094036Sgad goto return_sfres; 113194032Sgad } 113294036Sgad close(sfd); 113394036Sgad sfd = tfd; 113494036Sgad lseek(sfd, 0, SEEK_SET); 113594032Sgad } 113694032Sgad 113795293Sgad copycnt = 0; 113895293Sgadsendagain: 113995293Sgad copycnt++; 114095293Sgad 114195293Sgad if (copycnt < 2) 1142228664Sdim (void) sprintf(buf, "%c%" PRId64 " %s\n", type, stb.st_size, 1143228664Sdim file); 114495293Sgad else 1145228664Sdim (void) sprintf(buf, "%c%" PRId64 " %s_c%d\n", type, stb.st_size, 114695293Sgad file, copycnt); 11471553Srgrimes amt = strlen(buf); 11481553Srgrimes for (i = 0; ; i++) { 11491553Srgrimes if (write(pfd, buf, amt) != amt || 115031492Swollman (resp = response(pp)) < 0 || resp == '\1') { 115194036Sgad sfres = REPRINT; 115294036Sgad goto return_sfres; 11531553Srgrimes } else if (resp == '\0') 11541553Srgrimes break; 11551553Srgrimes if (i == 0) 115631492Swollman pstatus(pp, 115731492Swollman "no space on remote; waiting for queue to drain"); 11581553Srgrimes if (i == 10) 11591553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 116097792Sgad pp->printer, pp->remote_host); 11611553Srgrimes sleep(5 * 60); 11621553Srgrimes } 11631553Srgrimes if (i) 116431492Swollman pstatus(pp, "sending to %s", pp->remote_host); 116595293Sgad /* 116695293Sgad * XXX - we should change trstat_init()/trstat_write() to include 116795293Sgad * the copycnt in the statistics record it may write. 116895293Sgad */ 116968253Sgad if (type == '\3') 117068253Sgad trstat_init(pp, file, job_dfcnt); 1171119192Sgad for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) { 1172119192Sgad amt = SPL_BUFSIZ; 11731553Srgrimes if (i + amt > stb.st_size) 11741553Srgrimes amt = stb.st_size - i; 117594036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11761553Srgrimes sizerr = 1; 11771553Srgrimes if (write(pfd, buf, amt) != amt) { 117894036Sgad sfres = REPRINT; 117994036Sgad goto return_sfres; 11801553Srgrimes } 11811553Srgrimes } 11821553Srgrimes 11831553Srgrimes if (sizerr) { 118431492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11851553Srgrimes /* tell recvjob to ignore this file */ 11861553Srgrimes (void) write(pfd, "\1", 1); 118794036Sgad sfres = ERROR; 118894036Sgad goto return_sfres; 11891553Srgrimes } 119031492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 119194036Sgad sfres = REPRINT; 119294036Sgad goto return_sfres; 119324831Sbrian } 119495293Sgad if (type == '\3') { 119568253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 119695293Sgad pp->remote_host, origin_host); 119795293Sgad /* 119895293Sgad * Usually we only need to send one copy of a datafile, 119995293Sgad * because the control-file will simply print the same 120095293Sgad * file multiple times. However, some printers ignore 120195293Sgad * the control file, and simply print each data file as 120295293Sgad * it arrives. For such "remote hosts", we need to 120395293Sgad * transfer the same data file multiple times. Such a 120495293Sgad * a host is indicated by adding 'rc' to the printcap 120595293Sgad * entry. 120695293Sgad * XXX - Right now this ONLY works for remote hosts which 120795293Sgad * do ignore the name of the data file, because 120895293Sgad * this sends the file multiple times with slight 120995293Sgad * changes to the filename. To do this right would 121095293Sgad * require that we also rewrite the control file 121195293Sgad * to match those filenames. 121295293Sgad */ 121395293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 121495293Sgad lseek(sfd, 0, SEEK_SET); 121595293Sgad goto sendagain; 121695293Sgad } 121795293Sgad } 121894036Sgad sfres = OK; 121994036Sgad 122094036Sgadreturn_sfres: 122194036Sgad (void)close(sfd); 122294036Sgad if (tfd != -1) { 122394036Sgad /* 122494036Sgad * If tfd is set, then it is the same value as sfd, and 122594036Sgad * therefore it is already closed at this point. All 122694036Sgad * we need to do is remove the temporary file. 122794036Sgad */ 122894036Sgad tfd = -1; 122994036Sgad unlink(tfile); 123094036Sgad } 123194036Sgad return (sfres); 12321553Srgrimes} 12331553Srgrimes 12341553Srgrimes/* 1235138939Sgad * Some print servers send the control-file first, and then start sending the 1236138939Sgad * matching data file(s). That is not the correct order. If some queue is 1237138939Sgad * already printing an active job, then when that job is finished the queue 1238138939Sgad * may proceed to the control file of any incoming print job. This turns 1239138939Sgad * into a race between the process which is receiving the data file, and the 1240138939Sgad * process which is actively printing the very same file. When the remote 1241138939Sgad * server sends files in the wrong order, it is even possible that a queue 1242138939Sgad * will start to print a data file before the file has been created! 1243138939Sgad * 1244138939Sgad * So before we start to print() or send() a data file, we call this routine 1245138939Sgad * to make sure the data file is not still changing in size. Note that this 1246138939Sgad * problem will only happen for jobs arriving from a remote host, and that 1247138939Sgad * the process which has decided to print this job (and is thus making this 1248138939Sgad * check) is *not* the process which is receiving the job. 1249138939Sgad * 1250138939Sgad * A second benefit of this is that any incoming job is guaranteed to appear 1251138939Sgad * in a queue listing for at least a few seconds after it has arrived. Some 1252138939Sgad * lpr implementations get confused if they send a job and it disappears 1253138939Sgad * from the queue before they can check on it. 1254138939Sgad */ 1255138939Sgad#define MAXWAIT_ARRIVE 16 /* max to wait for the file to *exist* */ 1256138939Sgad#define MAXWAIT_4DATA (20*60) /* max to wait for it to stop changing */ 1257138939Sgad#define MINWAIT_4DATA 4 /* This value must be >= 1 */ 1258138939Sgad#define DEBUG_MINWAIT 1 1259138939Sgadstatic void 1260138939Sgadwait4data(struct printer *pp, const char *dfile) 1261138939Sgad{ 1262138939Sgad const char *cp; 1263138939Sgad int statres; 1264211190Sgad u_int sleepreq; 1265138939Sgad size_t dlen, hlen; 1266211190Sgad time_t amtslept, cur_time, prev_mtime; 1267138939Sgad struct stat statdf; 1268138939Sgad 1269138939Sgad /* Skip these checks if the print job is from the local host. */ 1270138939Sgad dlen = strlen(dfile); 1271138939Sgad hlen = strlen(local_host); 1272138939Sgad if (dlen > hlen) { 1273138939Sgad cp = dfile + dlen - hlen; 1274138939Sgad if (strcmp(cp, local_host) == 0) 1275138939Sgad return; 1276138939Sgad } 1277138939Sgad 1278138939Sgad /* 1279138939Sgad * If this data file does not exist, then wait up to MAXWAIT_ARRIVE 1280138939Sgad * seconds for it to arrive. 1281138939Sgad */ 1282138939Sgad amtslept = 0; 1283138939Sgad statres = stat(dfile, &statdf); 1284138939Sgad while (statres < 0 && amtslept < MAXWAIT_ARRIVE) { 1285138939Sgad if (amtslept == 0) 1286138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1287138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1288138939Sgad statres = stat(dfile, &statdf); 1289138939Sgad } 1290138939Sgad if (statres < 0) { 1291138939Sgad /* The file still does not exist, so just give up on it. */ 1292138939Sgad syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s", 1293138939Sgad pp->printer, dfile); 1294138939Sgad return; 1295138939Sgad } 1296138939Sgad 1297138939Sgad /* 1298138939Sgad * The file exists, so keep waiting until the data file has not 1299211190Sgad * changed for some reasonable amount of time. Extra care is 1300211190Sgad * taken when computing wait-times, just in case there are data 1301211190Sgad * files with a last-modify time in the future. While that is 1302211190Sgad * very unlikely to happen, it can happen when the system has 1303211190Sgad * a flakey time-of-day clock. 1304138939Sgad */ 1305211190Sgad prev_mtime = statdf.st_mtime; 1306211190Sgad cur_time = time(NULL); 1307211190Sgad if (statdf.st_mtime >= cur_time - MINWAIT_4DATA) { 1308211190Sgad if (statdf.st_mtime >= cur_time) /* some TOD oddity */ 1309211190Sgad sleepreq = MINWAIT_4DATA; 1310211190Sgad else 1311211190Sgad sleepreq = cur_time - statdf.st_mtime; 1312138939Sgad if (amtslept == 0) 1313138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1314211190Sgad amtslept += sleepreq - sleep(sleepreq); 1315138939Sgad statres = stat(dfile, &statdf); 1316138939Sgad } 1317211190Sgad sleepreq = MINWAIT_4DATA; 1318211190Sgad while (statres == 0 && amtslept < MAXWAIT_4DATA) { 1319211190Sgad if (statdf.st_mtime == prev_mtime) 1320211190Sgad break; 1321211190Sgad prev_mtime = statdf.st_mtime; 1322211190Sgad amtslept += sleepreq - sleep(sleepreq); 1323211190Sgad statres = stat(dfile, &statdf); 1324211190Sgad } 1325138939Sgad 1326138939Sgad if (statres != 0) 1327138939Sgad syslog(LOG_WARNING, "%s: %s disappeared during wait4data()", 1328138939Sgad pp->printer, dfile); 1329138939Sgad else if (amtslept > MAXWAIT_4DATA) 1330138939Sgad syslog(LOG_WARNING, 1331138939Sgad "%s: %s still changing after %lu secs in wait4data()", 1332138939Sgad pp->printer, dfile, (unsigned long)amtslept); 1333138939Sgad#if DEBUG_MINWAIT 1334138939Sgad else if (amtslept > MINWAIT_4DATA) 1335138939Sgad syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)", 1336138939Sgad pp->printer, (unsigned long)amtslept, dfile); 1337138939Sgad#endif 1338138939Sgad} 1339138939Sgad#undef MAXWAIT_ARRIVE 1340138939Sgad#undef MAXWAIT_4DATA 1341138939Sgad#undef MINWAIT_4DATA 1342138939Sgad 1343138939Sgad/* 134494032Sgad * This routine is called to execute one of the filters as was 134594036Sgad * specified in a printcap entry. While the child-process will read 134694036Sgad * all of 'infd', it is up to the caller to close that file descriptor 134794036Sgad * in the parent process. 134894032Sgad */ 134994032Sgadstatic int 135094032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 135194032Sgad{ 135297793Sgad pid_t fpid, wpid; 135397793Sgad int errfd, retcode, wstatus; 135494032Sgad FILE *errfp; 135594032Sgad char buf[BUFSIZ], *slash; 135694032Sgad 135794032Sgad fpid = dofork(pp, DORETURN); 135894032Sgad if (fpid != 0) { 135994032Sgad /* 136094032Sgad * This is the parent process, which just waits for the child 136194032Sgad * to complete and then returns the result. Note that it is 136294032Sgad * the child process which reads the input stream. 136394032Sgad */ 136494032Sgad if (fpid < 0) 136597781Sgad retcode = 100; 136694032Sgad else { 136797781Sgad while ((wpid = wait(&wstatus)) > 0 && 136894032Sgad wpid != fpid) 136994032Sgad ; 137094032Sgad if (wpid < 0) { 137197781Sgad retcode = 100; 137294032Sgad syslog(LOG_WARNING, 137394032Sgad "%s: after execv(%s), wait() returned: %m", 137494032Sgad pp->printer, f_cmd); 137597781Sgad } else 137697781Sgad retcode = WEXITSTATUS(wstatus); 137794032Sgad } 137894032Sgad 137994032Sgad /* 138094032Sgad * Copy everything the filter wrote to stderr from our 138194032Sgad * temporary errors file to the "lf=" logfile. 138294032Sgad */ 138394032Sgad errfp = fopen(tempstderr, "r"); 138494032Sgad if (errfp) { 138594032Sgad while (fgets(buf, sizeof(buf), errfp)) 138694032Sgad fputs(buf, stderr); 138794032Sgad fclose(errfp); 138894032Sgad } 138994032Sgad 139097781Sgad return (retcode); 139194032Sgad } 139294032Sgad 139394032Sgad /* 139494032Sgad * This is the child process, which is the one that executes the 139594032Sgad * given filter. 139694032Sgad */ 139794032Sgad /* 139894032Sgad * If the first parameter has any slashes in it, then change it 139994032Sgad * to point to the first character after the last slash. 140094032Sgad */ 140194032Sgad slash = strrchr(f_av[0], '/'); 140294032Sgad if (slash != NULL) 140394032Sgad f_av[0] = slash + 1; 140494032Sgad /* 140594032Sgad * XXX - in the future, this should setup an explicit list of 140694032Sgad * environment variables and use execve()! 140794032Sgad */ 140894032Sgad 140994032Sgad /* 141094032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 141194032Sgad * is running. Stderr is setup so it points to a temporary errors 141294032Sgad * file, and the parent process will copy that temporary file to 141394032Sgad * the real logfile after the filter completes. 141494032Sgad */ 1415118881Sgad dup2(infd, STDIN_FILENO); 1416118881Sgad dup2(outfd, STDOUT_FILENO); 141794032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 141894032Sgad if (errfd >= 0) 1419118881Sgad dup2(errfd, STDERR_FILENO); 142094032Sgad closelog(); 142194032Sgad closeallfds(3); 142294032Sgad execv(f_cmd, f_av); 142395067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 142494032Sgad exit(2); 142594032Sgad /* NOTREACHED */ 142694032Sgad} 142794032Sgad 142894032Sgad/* 14291553Srgrimes * Check to make sure there have been no errors and that both programs 14301553Srgrimes * are in sync with eachother. 14311553Srgrimes * Return non-zero if the connection was lost. 14321553Srgrimes */ 14331553Srgrimesstatic char 143478146Sgadresponse(const struct printer *pp) 14351553Srgrimes{ 14361553Srgrimes char resp; 14371553Srgrimes 14381553Srgrimes if (read(pfd, &resp, 1) != 1) { 143931492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 144097791Sgad return (-1); 14411553Srgrimes } 144297791Sgad return (resp); 14431553Srgrimes} 14441553Srgrimes 14451553Srgrimes/* 14461553Srgrimes * Banner printing stuff 14471553Srgrimes */ 14481553Srgrimesstatic void 144978146Sgadbanner(struct printer *pp, char *name1, char *name2) 14501553Srgrimes{ 14511553Srgrimes time_t tvec; 14521553Srgrimes 14531553Srgrimes time(&tvec); 145431492Swollman if (!pp->no_formfeed && !pp->tof) 145531492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 145631492Swollman if (pp->short_banner) { /* short banner only */ 14571553Srgrimes if (class[0]) { 14581553Srgrimes (void) write(ofd, class, strlen(class)); 14591553Srgrimes (void) write(ofd, ":", 1); 14601553Srgrimes } 14611553Srgrimes (void) write(ofd, name1, strlen(name1)); 14621553Srgrimes (void) write(ofd, " Job: ", 7); 14631553Srgrimes (void) write(ofd, name2, strlen(name2)); 14641553Srgrimes (void) write(ofd, " Date: ", 8); 14651553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14661553Srgrimes (void) write(ofd, "\n", 1); 14671553Srgrimes } else { /* normal banner */ 14681553Srgrimes (void) write(ofd, "\n\n\n", 3); 146931492Swollman scan_out(pp, ofd, name1, '\0'); 14701553Srgrimes (void) write(ofd, "\n\n", 2); 147131492Swollman scan_out(pp, ofd, name2, '\0'); 14721553Srgrimes if (class[0]) { 14731553Srgrimes (void) write(ofd,"\n\n\n",3); 147431492Swollman scan_out(pp, ofd, class, '\0'); 14751553Srgrimes } 14761553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 14771553Srgrimes (void) write(ofd, name2, strlen(name2)); 14781553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 14791553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14801553Srgrimes (void) write(ofd, "\n", 1); 14811553Srgrimes } 148231492Swollman if (!pp->no_formfeed) 148331492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 148431492Swollman pp->tof = 1; 14851553Srgrimes} 14861553Srgrimes 14871553Srgrimesstatic char * 148878146Sgadscnline(int key, char *p, int c) 14891553Srgrimes{ 149039084Swollman register int scnwidth; 14911553Srgrimes 14921553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 14931553Srgrimes key <<= 1; 14941553Srgrimes *p++ = key & 0200 ? c : BACKGND; 14951553Srgrimes } 14961553Srgrimes return (p); 14971553Srgrimes} 14981553Srgrimes 14991553Srgrimes#define TRC(q) (((q)-' ')&0177) 15001553Srgrimes 15011553Srgrimesstatic void 150278146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 15031553Srgrimes{ 15041553Srgrimes register char *strp; 150539084Swollman register int nchrs, j; 15061553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 15071553Srgrimes int d, scnhgt; 15081553Srgrimes 15091553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 15101553Srgrimes strp = &outbuf[0]; 15111553Srgrimes sp = scsp; 15121553Srgrimes for (nchrs = 0; ; ) { 15131553Srgrimes d = dropit(c = TRC(cc = *sp++)); 15141553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 15151553Srgrimes for (j = WIDTH; --j;) 15161553Srgrimes *strp++ = BACKGND; 15171553Srgrimes else 151831492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 151931492Swollman if (*sp == dlm || *sp == '\0' || 152031492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 15211553Srgrimes break; 15221553Srgrimes *strp++ = BACKGND; 15231553Srgrimes *strp++ = BACKGND; 15241553Srgrimes } 15251553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 15261553Srgrimes ; 15271553Srgrimes strp++; 15288857Srgrimes *strp++ = '\n'; 15291553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 15301553Srgrimes } 15311553Srgrimes} 15321553Srgrimes 15331553Srgrimesstatic int 153478146Sgaddropit(int c) 15351553Srgrimes{ 15361553Srgrimes switch(c) { 15371553Srgrimes 15381553Srgrimes case TRC('_'): 15391553Srgrimes case TRC(';'): 15401553Srgrimes case TRC(','): 15411553Srgrimes case TRC('g'): 15421553Srgrimes case TRC('j'): 15431553Srgrimes case TRC('p'): 15441553Srgrimes case TRC('q'): 15451553Srgrimes case TRC('y'): 15461553Srgrimes return (DROP); 15471553Srgrimes 15481553Srgrimes default: 15491553Srgrimes return (0); 15501553Srgrimes } 15511553Srgrimes} 15521553Srgrimes 15531553Srgrimes/* 15541553Srgrimes * sendmail --- 15551553Srgrimes * tell people about job completion 15561553Srgrimes */ 15571553Srgrimesstatic void 155894038Sgadsendmail(struct printer *pp, char *userid, int bombed) 15591553Srgrimes{ 15601553Srgrimes register int i; 15611553Srgrimes int p[2], s; 156278146Sgad register const char *cp; 15631553Srgrimes struct stat stb; 15641553Srgrimes FILE *fp; 15651553Srgrimes 15661553Srgrimes pipe(p); 156731492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1568118881Sgad dup2(p[0], STDIN_FILENO); 15698094Sjkh closelog(); 157031492Swollman closeallfds(3); 157127635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 15721553Srgrimes cp++; 157331492Swollman else 15741553Srgrimes cp = _PATH_SENDMAIL; 157579452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 157631492Swollman _exit(0); 15771553Srgrimes } else if (s > 0) { /* parent */ 1578118881Sgad dup2(p[1], STDOUT_FILENO); 157994038Sgad printf("To: %s@%s\n", userid, origin_host); 158031492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 158115648Sjoerg *jobname ? jobname : "<unknown>"); 158278300Sgad printf("Reply-To: root@%s\n\n", local_host); 15831553Srgrimes printf("Your printer job "); 15841553Srgrimes if (*jobname) 15851553Srgrimes printf("(%s) ", jobname); 158694040Sgad 15871553Srgrimes switch (bombed) { 15881553Srgrimes case OK: 158994040Sgad cp = "OK"; 15901553Srgrimes printf("\ncompleted successfully\n"); 15911553Srgrimes break; 15921553Srgrimes default: 15931553Srgrimes case FATALERR: 159494040Sgad cp = "FATALERR"; 15951553Srgrimes printf("\ncould not be printed\n"); 15961553Srgrimes break; 15971553Srgrimes case NOACCT: 159894040Sgad cp = "NOACCT"; 159978300Sgad printf("\ncould not be printed without an account on %s\n", 160078300Sgad local_host); 16011553Srgrimes break; 16021553Srgrimes case FILTERERR: 160394040Sgad cp = "FILTERERR"; 160468664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 160568664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 160615648Sjoerg printf("\nhad some errors and may not have printed\n"); 16071553Srgrimes break; 16081553Srgrimes } 160915648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 16101553Srgrimes while ((i = getc(fp)) != EOF) 16111553Srgrimes putchar(i); 16121553Srgrimes (void) fclose(fp); 16131553Srgrimes break; 16141553Srgrimes case ACCESS: 161594040Sgad cp = "ACCESS"; 16161553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 16171553Srgrimes } 16181553Srgrimes fflush(stdout); 1619118881Sgad (void) close(STDOUT_FILENO); 162031492Swollman } else { 162194038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 162231492Swollman return; 16231553Srgrimes } 16241553Srgrimes (void) close(p[0]); 16251553Srgrimes (void) close(p[1]); 162615648Sjoerg wait(NULL); 162715648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 162894038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 16291553Srgrimes} 16301553Srgrimes 16311553Srgrimes/* 16321553Srgrimes * dofork - fork with retries on failure 16331553Srgrimes */ 16341553Srgrimesstatic int 163578146Sgaddofork(const struct printer *pp, int action) 16361553Srgrimes{ 163797793Sgad pid_t forkpid; 163897793Sgad int i, fail; 163960871Smpp struct passwd *pwd; 16401553Srgrimes 164180230Sgad forkpid = -1; 164280230Sgad if (daemon_uname == NULL) { 164380230Sgad pwd = getpwuid(pp->daemon_user); 164480230Sgad if (pwd == NULL) { 164580230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 164680230Sgad pp->printer, pp->daemon_user); 164780230Sgad goto error_ret; 164880230Sgad } 164980230Sgad daemon_uname = strdup(pwd->pw_name); 165080230Sgad daemon_defgid = pwd->pw_gid; 165180230Sgad } 165280230Sgad 16531553Srgrimes for (i = 0; i < 20; i++) { 165480230Sgad forkpid = fork(); 165580230Sgad if (forkpid < 0) { 16561553Srgrimes sleep((unsigned)(i*i)); 16571553Srgrimes continue; 16581553Srgrimes } 16591553Srgrimes /* 16601553Srgrimes * Child should run as daemon instead of root 16611553Srgrimes */ 166278146Sgad if (forkpid == 0) { 166380230Sgad errno = 0; 166480230Sgad fail = initgroups(daemon_uname, daemon_defgid); 166580230Sgad if (fail) { 166680230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 166780230Sgad pp->printer, daemon_uname, daemon_defgid); 166860871Smpp break; 166960871Smpp } 167080230Sgad fail = setgid(daemon_defgid); 167180230Sgad if (fail) { 167280230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 167380230Sgad pp->printer, daemon_defgid); 167480230Sgad break; 167580230Sgad } 167680230Sgad fail = setuid(pp->daemon_user); 167780230Sgad if (fail) { 167880230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 167980230Sgad pp->printer, pp->daemon_user); 168080230Sgad break; 168180230Sgad } 168260871Smpp } 168397791Sgad return (forkpid); 16841553Srgrimes } 16851553Srgrimes 168680230Sgad /* 168780230Sgad * An error occurred. If the error is in the child process, then 168880230Sgad * this routine MUST always exit(). DORETURN only effects how 168980230Sgad * errors should be handled in the parent process. 169080230Sgad */ 169180230Sgaderror_ret: 169280230Sgad if (forkpid == 0) { 169380230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 169480230Sgad pp->printer); 169580230Sgad exit(1); 169680230Sgad } 169780230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 169880230Sgad 169980230Sgad sleep(1); /* throttle errors, as a safety measure */ 17001553Srgrimes switch (action) { 17011553Srgrimes case DORETURN: 170297791Sgad return (-1); 17031553Srgrimes default: 17041553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 170580230Sgad /* FALLTHROUGH */ 17061553Srgrimes case DOABORT: 17071553Srgrimes exit(1); 17081553Srgrimes } 17091553Srgrimes /*NOTREACHED*/ 17101553Srgrimes} 17111553Srgrimes 17121553Srgrimes/* 17131553Srgrimes * Kill child processes to abort current job. 17141553Srgrimes */ 17151553Srgrimesstatic void 171678146Sgadabortpr(int signo __unused) 17171553Srgrimes{ 171868664Sgad 171968664Sgad (void) unlink(tempstderr); 17201553Srgrimes kill(0, SIGINT); 172197793Sgad if (of_pid > 0) 172297793Sgad kill(of_pid, SIGCONT); 17231553Srgrimes while (wait(NULL) > 0) 17241553Srgrimes ; 172597793Sgad if (of_pid > 0 && tfd != -1) 172624831Sbrian unlink(tfile); 17271553Srgrimes exit(0); 17281553Srgrimes} 17291553Srgrimes 17301553Srgrimesstatic void 173178146Sgadinit(struct printer *pp) 17321553Srgrimes{ 17331553Srgrimes char *s; 17341553Srgrimes 173531492Swollman sprintf(&width[2], "%ld", pp->page_width); 173631492Swollman sprintf(&length[2], "%ld", pp->page_length); 173731492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 173831492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 173931492Swollman if ((s = checkremote(pp)) != 0) { 174031492Swollman syslog(LOG_WARNING, "%s", s); 174131492Swollman free(s); 174231492Swollman } 174331492Swollman} 174431492Swollman 174531492Swollmanvoid 174678146Sgadstartprinting(const char *printer) 174731492Swollman{ 174831492Swollman struct printer myprinter, *pp = &myprinter; 174931492Swollman int status; 175031492Swollman 175131492Swollman init_printer(pp); 175231492Swollman status = getprintcap(printer, pp); 175331492Swollman switch(status) { 175431492Swollman case PCAPERR_OSERR: 175531492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 17561553Srgrimes exit(1); 175731492Swollman case PCAPERR_NOTFOUND: 17581553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 17591553Srgrimes exit(1); 176031492Swollman case PCAPERR_TCLOOP: 176131492Swollman fatal(pp, "potential reference loop detected in printcap file"); 176231492Swollman default: 176331492Swollman break; 176431492Swollman } 176531492Swollman printjob(pp); 17661553Srgrimes} 17671553Srgrimes 17681553Srgrimes/* 17691553Srgrimes * Acquire line printer or remote connection. 17701553Srgrimes */ 17711553Srgrimesstatic void 177278146Sgadopenpr(const struct printer *pp) 17731553Srgrimes{ 177431492Swollman int p[2]; 177515648Sjoerg char *cp; 17761553Srgrimes 177731492Swollman if (pp->remote) { 177831492Swollman openrem(pp); 177994032Sgad /* 178094032Sgad * Lpd does support the setting of 'of=' filters for 178194032Sgad * jobs going to remote machines, but that does not 178294032Sgad * have the same meaning as 'of=' does when handling 178394032Sgad * local print queues. For remote machines, all 'of=' 178494032Sgad * filter processing is handled in sendfile(), and that 178594032Sgad * does not use these global "output filter" variables. 178694032Sgad */ 178794032Sgad ofd = -1; 178897793Sgad of_pid = 0; 178994032Sgad return; 179031492Swollman } else if (*pp->lp) { 179131492Swollman if ((cp = strchr(pp->lp, '@')) != NULL) 179231492Swollman opennet(pp); 179315648Sjoerg else 179431492Swollman opentty(pp); 17951553Srgrimes } else { 17961553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 179797792Sgad pp->printer); 17981553Srgrimes exit(1); 17991553Srgrimes } 180015648Sjoerg 18011553Srgrimes /* 18021553Srgrimes * Start up an output filter, if needed. 18031553Srgrimes */ 180497793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 18051553Srgrimes pipe(p); 180631492Swollman if (pp->remote) { 180731492Swollman strcpy(tfile, TFILENAME); 180824831Sbrian tfd = mkstemp(tfile); 180924831Sbrian } 181097793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 1811118881Sgad dup2(p[0], STDIN_FILENO); /* pipe is std in */ 181224831Sbrian /* tfile/printer is stdout */ 1813118881Sgad dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); 18148094Sjkh closelog(); 181531492Swollman closeallfds(3); 181631492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 181731492Swollman cp = pp->filters[LPF_OUTPUT]; 18181553Srgrimes else 18191553Srgrimes cp++; 182079452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 182179452Sbrian (char *)0); 182297792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 182397792Sgad pp->filters[LPF_OUTPUT]); 18241553Srgrimes exit(1); 18251553Srgrimes } 18261553Srgrimes (void) close(p[0]); /* close input side */ 18271553Srgrimes ofd = p[1]; /* use pipe for output */ 18281553Srgrimes } else { 18291553Srgrimes ofd = pfd; 183097793Sgad of_pid = 0; 18311553Srgrimes } 18321553Srgrimes} 18331553Srgrimes 183415648Sjoerg/* 183515648Sjoerg * Printer connected directly to the network 183615648Sjoerg * or to a terminal server on the net 183715648Sjoerg */ 183815648Sjoergstatic void 183978146Sgadopennet(const struct printer *pp) 184015648Sjoerg{ 184115648Sjoerg register int i; 184231492Swollman int resp; 184331492Swollman u_long port; 184431492Swollman char *ep; 184530407Sjoerg void (*savealrm)(int); 184615648Sjoerg 184731492Swollman port = strtoul(pp->lp, &ep, 0); 184838470Sbrian if (*ep != '@' || port > 65535) { 184931492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 185097792Sgad pp->lp); 185115648Sjoerg exit(1); 185215648Sjoerg } 185331492Swollman ep++; 185415648Sjoerg 185515648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 185615648Sjoerg resp = -1; 185730407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 185831492Swollman alarm(pp->conn_timeout); 185931492Swollman pfd = getport(pp, ep, port); 186031020Sjoerg alarm(0); 186130407Sjoerg (void)signal(SIGALRM, savealrm); 186215648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 186315648Sjoerg resp = 1; 186415648Sjoerg else if (pfd >= 0) { 186515648Sjoerg /* 186615648Sjoerg * need to delay a bit for rs232 lines 186715648Sjoerg * to stabilize in case printer is 186815648Sjoerg * connected via a terminal server 186915648Sjoerg */ 187015648Sjoerg delay(500); 187115648Sjoerg break; 187215648Sjoerg } 187315648Sjoerg if (i == 1) { 187431492Swollman if (resp < 0) 187531492Swollman pstatus(pp, "waiting for %s to come up", 187631492Swollman pp->lp); 187731492Swollman else 187831492Swollman pstatus(pp, 187931492Swollman "waiting for access to printer on %s", 188031492Swollman pp->lp); 188115648Sjoerg } 188215648Sjoerg sleep(i); 188315648Sjoerg } 188479739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 188515648Sjoerg} 188615648Sjoerg 188715648Sjoerg/* 188815648Sjoerg * Printer is connected to an RS232 port on this host 188915648Sjoerg */ 189015648Sjoergstatic void 189178146Sgadopentty(const struct printer *pp) 189215648Sjoerg{ 189315648Sjoerg register int i; 189415648Sjoerg 189515648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 189631492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 189715648Sjoerg if (pfd >= 0) { 189815648Sjoerg delay(500); 189915648Sjoerg break; 190015648Sjoerg } 190115648Sjoerg if (errno == ENOENT) { 190231492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 190315648Sjoerg exit(1); 190415648Sjoerg } 190515648Sjoerg if (i == 1) 190631492Swollman pstatus(pp, 190731492Swollman "waiting for %s to become ready (offline?)", 190831492Swollman pp->printer); 190915648Sjoerg sleep(i); 191015648Sjoerg } 191115648Sjoerg if (isatty(pfd)) 191231492Swollman setty(pp); 191331492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 191415648Sjoerg} 191515648Sjoerg 191615648Sjoerg/* 191715648Sjoerg * Printer is on a remote host 191815648Sjoerg */ 191915648Sjoergstatic void 192078146Sgadopenrem(const struct printer *pp) 192115648Sjoerg{ 192231492Swollman register int i; 192327748Simp int resp; 192430407Sjoerg void (*savealrm)(int); 192515648Sjoerg 192615648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 192715648Sjoerg resp = -1; 192830407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 192931492Swollman alarm(pp->conn_timeout); 193031492Swollman pfd = getport(pp, pp->remote_host, 0); 193131020Sjoerg alarm(0); 193230407Sjoerg (void)signal(SIGALRM, savealrm); 193315648Sjoerg if (pfd >= 0) { 193431492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 193531492Swollman (char *)0) 193631492Swollman == 2 + strlen(pp->remote_queue)) 193731492Swollman && (resp = response(pp)) == 0) 193815648Sjoerg break; 193915648Sjoerg (void) close(pfd); 194015648Sjoerg } 194115648Sjoerg if (i == 1) { 194215648Sjoerg if (resp < 0) 194331492Swollman pstatus(pp, "waiting for %s to come up", 194431492Swollman pp->remote_host); 194515648Sjoerg else { 194631492Swollman pstatus(pp, 194731492Swollman "waiting for queue to be enabled on %s", 194831492Swollman pp->remote_host); 194915648Sjoerg i = 256; 195015648Sjoerg } 195115648Sjoerg } 195215648Sjoerg sleep(i); 195315648Sjoerg } 195431492Swollman pstatus(pp, "sending to %s", pp->remote_host); 195515648Sjoerg} 195615648Sjoerg 19571553Srgrimes/* 19581553Srgrimes * setup tty lines. 19591553Srgrimes */ 19601553Srgrimesstatic void 196178146Sgadsetty(const struct printer *pp) 19621553Srgrimes{ 196315032Ssef struct termios ttybuf; 19641553Srgrimes 19651553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 196631492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 19671553Srgrimes exit(1); 19681553Srgrimes } 196915032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 197031492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 19711553Srgrimes exit(1); 19721553Srgrimes } 197331492Swollman if (pp->baud_rate > 0) 197431492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 197531492Swollman if (pp->mode_set) { 197631492Swollman char *s = strdup(pp->mode_set), *tmp; 197715032Ssef 197831492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 197939084Swollman (void) msearch(tmp, &ttybuf); 19801553Srgrimes } 19811553Srgrimes } 198231492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 198315032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 198431492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 19851553Srgrimes } 19861553Srgrimes } 19871553Srgrimes} 19881553Srgrimes 19891553Srgrimes#include <stdarg.h> 19901553Srgrimes 199115648Sjoergstatic void 199231492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 19931553Srgrimes{ 199431492Swollman int fd; 199531492Swollman char *buf; 19961553Srgrimes va_list ap; 19971553Srgrimes va_start(ap, msg); 19981553Srgrimes 1999139035Sgad umask(S_IWOTH); 200031492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 200131492Swollman if (fd < 0) { 200297792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 200397792Sgad pp->status_file); 20041553Srgrimes exit(1); 20051553Srgrimes } 20061553Srgrimes ftruncate(fd, 0); 200731492Swollman vasprintf(&buf, msg, ap); 20081553Srgrimes va_end(ap); 200931492Swollman writel(fd, buf, "\n", (char *)0); 201031492Swollman close(fd); 201131492Swollman free(buf); 20121553Srgrimes} 201330407Sjoerg 201430407Sjoergvoid 201578146Sgadalarmhandler(int signo __unused) 201630407Sjoerg{ 201778146Sgad /* the signal is ignored */ 201878146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 201930407Sjoerg} 2020