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$"); 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> 64241852Seadler#include <err.h> 651553Srgrimes#include <errno.h> 66228664Sdim#include <inttypes.h> 671553Srgrimes#include <stdio.h> 681553Srgrimes#include <string.h> 691553Srgrimes#include <stdlib.h> 7015032Ssef#include <sys/ioctl.h> 7115032Ssef#include <termios.h> 7215703Sjoerg#include <time.h> 731553Srgrimes#include "lp.h" 741553Srgrimes#include "lp.local.h" 751553Srgrimes#include "pathnames.h" 761553Srgrimes#include "extern.h" 771553Srgrimes 7880230Sgad#define DORETURN 0 /* dofork should return "can't fork" error */ 7980230Sgad#define DOABORT 1 /* dofork should just die if fork() fails */ 801553Srgrimes 811553Srgrimes/* 82119192Sgad * The buffer size to use when reading/writing spool files. 83119192Sgad */ 84119192Sgad#define SPL_BUFSIZ BUFSIZ 85119192Sgad 86119192Sgad/* 871553Srgrimes * Error tokens 881553Srgrimes */ 891553Srgrimes#define REPRINT -2 901553Srgrimes#define ERROR -1 911553Srgrimes#define OK 0 921553Srgrimes#define FATALERR 1 931553Srgrimes#define NOACCT 2 941553Srgrimes#define FILTERERR 3 951553Srgrimes#define ACCESS 4 961553Srgrimes 971553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 981553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 991553Srgrimesstatic FILE *cfp; /* control file */ 10097793Sgadstatic pid_t of_pid; /* process id of output filter, if any */ 1011553Srgrimesstatic int child; /* id of any filters */ 10268253Sgadstatic int job_dfcnt; /* count of datafiles in current user job */ 1031553Srgrimesstatic int lfd; /* lock file descriptor */ 1041553Srgrimesstatic int ofd; /* output filter file descriptor */ 10524831Sbrianstatic int tfd = -1; /* output filter temp file output */ 1061553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 1071553Srgrimesstatic int prchild; /* id of pr process */ 1081553Srgrimesstatic char title[80]; /* ``pr'' title */ 10953956Sachestatic char locale[80]; /* ``pr'' locale */ 1101553Srgrimes 11180230Sgad/* these two are set from pp->daemon_user, but only if they are needed */ 11280230Sgadstatic char *daemon_uname; /* set from pwd->pw_name */ 11380230Sgadstatic int daemon_defgid; 11480230Sgad 1151553Srgrimesstatic char class[32]; /* classification field */ 11678300Sgadstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 1171553Srgrimes /* indentation size in static characters */ 1188857Srgrimesstatic char indent[10] = "-i0"; 1191553Srgrimesstatic char jobname[100]; /* job or file name */ 1201553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1211553Srgrimesstatic char logname[32]; /* user's login name */ 1221553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1231553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 12468664Sgad/* tempstderr is the filename used to catch stderr from exec-ing filters */ 12568664Sgadstatic char tempstderr[] = "errs.XXXXXXX"; 1261553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 12724831Sbrian#define TFILENAME "fltXXXXXX" 12824831Sbrianstatic char tfile[] = TFILENAME; /* file name for filter output */ 1291553Srgrimes 13078146Sgadstatic void abortpr(int _signo); 13178146Sgadstatic void alarmhandler(int _signo); 13278146Sgadstatic void banner(struct printer *_pp, char *_name1, char *_name2); 13378146Sgadstatic int dofork(const struct printer *_pp, int _action); 13478146Sgadstatic int dropit(int _c); 13594032Sgadstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 13694032Sgad int _infd, int _outfd); 13778146Sgadstatic void init(struct printer *_pp); 13878146Sgadstatic void openpr(const struct printer *_pp); 13978146Sgadstatic void opennet(const struct printer *_pp); 14078146Sgadstatic void opentty(const struct printer *_pp); 14178146Sgadstatic void openrem(const struct printer *pp); 14278146Sgadstatic int print(struct printer *_pp, int _format, char *_file); 14378146Sgadstatic int printit(struct printer *_pp, char *_file); 14479739Sgadstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 14579739Sgad __printflike(2, 3); 14678146Sgadstatic char response(const struct printer *_pp); 14778146Sgadstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14878146Sgad int _dlm); 14978146Sgadstatic char *scnline(int _key, char *_p, int _c); 15078146Sgadstatic int sendfile(struct printer *_pp, int _type, char *_file, 15195293Sgad char _format, int _copyreq); 15278146Sgadstatic int sendit(struct printer *_pp, char *_file); 15394038Sgadstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 15478146Sgadstatic void setty(const struct printer *_pp); 155138939Sgadstatic void wait4data(struct printer *_pp, const char *_dfile); 1561553Srgrimes 1571553Srgrimesvoid 15878146Sgadprintjob(struct printer *pp) 1591553Srgrimes{ 1601553Srgrimes struct stat stb; 16168401Sgad register struct jobqueue *q, **qp; 16268401Sgad struct jobqueue **queue; 1631553Srgrimes register int i, nitems; 16468733Sgad off_t pidoff; 16597793Sgad pid_t printpid; 166160147Sgad int errcnt, jobcount, statok, tempfd; 1671553Srgrimes 16868733Sgad jobcount = 0; 16931492Swollman init(pp); /* set up capabilities */ 170118881Sgad (void) write(STDOUT_FILENO, "", 1); /* ack that daemon is started */ 171118881Sgad (void) close(STDERR_FILENO); /* set up log file */ 17231492Swollman if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 17397792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 17497792Sgad pp->log_file); 1751553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1761553Srgrimes } 177241852Seadler if(setgid(getegid()) != 0) err(1, "setgid() failed"); 17897793Sgad printpid = getpid(); /* for use with lprm */ 17997793Sgad setpgrp(0, printpid); 18079735Sgad 18179735Sgad /* 18279735Sgad * At initial lpd startup, printjob may be called with various 18379735Sgad * signal handlers in effect. After that initial startup, any 18479735Sgad * calls to printjob will have a *different* set of signal-handlers 18579735Sgad * in effect. Make sure all handlers are the ones we want. 18679735Sgad */ 18779735Sgad signal(SIGCHLD, SIG_DFL); 1881553Srgrimes signal(SIGHUP, abortpr); 1891553Srgrimes signal(SIGINT, abortpr); 1901553Srgrimes signal(SIGQUIT, abortpr); 1911553Srgrimes signal(SIGTERM, abortpr); 1921553Srgrimes 1931553Srgrimes /* 1941553Srgrimes * uses short form file names 1951553Srgrimes */ 19631492Swollman if (chdir(pp->spool_dir) < 0) { 19797792Sgad syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, 19897792Sgad pp->spool_dir); 1991553Srgrimes exit(1); 2001553Srgrimes } 201160147Sgad statok = stat(pp->lock_file, &stb); 202160147Sgad if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS)) 2031553Srgrimes exit(0); /* printing disabled */ 204139035Sgad umask(S_IWOTH); 20531492Swollman lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 20631492Swollman LOCK_FILE_MODE); 2071553Srgrimes if (lfd < 0) { 20831492Swollman if (errno == EWOULDBLOCK) /* active daemon present */ 20931492Swollman exit(0); 21097792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 21197792Sgad pp->lock_file); 2121553Srgrimes exit(1); 2131553Srgrimes } 214160147Sgad /* 215160147Sgad * If the initial call to stat() failed, then lock_file will have 216160147Sgad * been created by open(). Update &stb to match that new file. 217160147Sgad */ 218160147Sgad if (statok != 0) 219160147Sgad statok = stat(pp->lock_file, &stb); 22031492Swollman /* turn off non-blocking mode (was turned on for lock effects only) */ 22131492Swollman if (fcntl(lfd, F_SETFL, 0) < 0) { 22297792Sgad syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, 22397792Sgad pp->lock_file); 2241553Srgrimes exit(1); 2251553Srgrimes } 2261553Srgrimes ftruncate(lfd, 0); 2271553Srgrimes /* 2281553Srgrimes * write process id for others to know 2291553Srgrimes */ 23097793Sgad sprintf(line, "%u\n", printpid); 2311553Srgrimes pidoff = i = strlen(line); 2321553Srgrimes if (write(lfd, line, i) != i) { 23397792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 23497792Sgad pp->lock_file); 2351553Srgrimes exit(1); 2361553Srgrimes } 2371553Srgrimes /* 2381553Srgrimes * search the spool directory for work and sort by queue order. 2391553Srgrimes */ 24031492Swollman if ((nitems = getq(pp, &queue)) < 0) { 24131492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 24297792Sgad pp->spool_dir); 2431553Srgrimes exit(1); 2441553Srgrimes } 2451553Srgrimes if (nitems == 0) /* no work to do */ 2461553Srgrimes exit(0); 24731492Swollman if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 24831492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 24997792Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 25097792Sgad pp->lock_file); 2511553Srgrimes } 25268664Sgad 25368664Sgad /* create a file which will be used to hold stderr from filters */ 25468664Sgad if ((tempfd = mkstemp(tempstderr)) == -1) { 25568664Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 25697792Sgad tempstderr); 25768732Sgad exit(1); 25868664Sgad } 25968664Sgad if ((i = fchmod(tempfd, 0664)) == -1) { 26068664Sgad syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 26197792Sgad tempstderr); 26268732Sgad exit(1); 26368664Sgad } 26468664Sgad /* lpd doesn't need it to be open, it just needs it to exist */ 26568664Sgad close(tempfd); 26668664Sgad 26731492Swollman openpr(pp); /* open printer or remote */ 2681553Srgrimesagain: 2691553Srgrimes /* 2701553Srgrimes * we found something to do now do it -- 2711553Srgrimes * write the name of the current control file into the lock file 2721553Srgrimes * so the spool queue program can tell what we're working on 2731553Srgrimes */ 2741553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2751553Srgrimes q = *qp++; 27668401Sgad if (stat(q->job_cfname, &stb) < 0) 2771553Srgrimes continue; 27815648Sjoerg errcnt = 0; 2791553Srgrimes restart: 28015648Sjoerg (void) lseek(lfd, pidoff, 0); 28168401Sgad (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 2821553Srgrimes i = strlen(line); 2831553Srgrimes if (write(lfd, line, i) != i) 28497792Sgad syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, 28597792Sgad pp->lock_file); 28631492Swollman if (!pp->remote) 28768401Sgad i = printit(pp, q->job_cfname); 2881553Srgrimes else 28968401Sgad i = sendit(pp, q->job_cfname); 2901553Srgrimes /* 2911553Srgrimes * Check to see if we are supposed to stop printing or 2921553Srgrimes * if we are to rebuild the queue. 2931553Srgrimes */ 2941553Srgrimes if (fstat(lfd, &stb) == 0) { 2951553Srgrimes /* stop printing before starting next job? */ 29631492Swollman if (stb.st_mode & LFM_PRINT_DIS) 2971553Srgrimes goto done; 2981553Srgrimes /* rebuild queue (after lpc topq) */ 29931492Swollman if (stb.st_mode & LFM_RESET_QUE) { 30031492Swollman for (free(q); nitems--; free(q)) 3011553Srgrimes q = *qp++; 30231492Swollman if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 30331492Swollman < 0) 30497792Sgad syslog(LOG_WARNING, 30597792Sgad "%s: fchmod(%s): %m", 30697792Sgad pp->printer, pp->lock_file); 3071553Srgrimes break; 3081553Srgrimes } 3091553Srgrimes } 31068733Sgad if (i == OK) /* all files of this job printed */ 31168733Sgad jobcount++; 31215648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 31315648Sjoerg /* try reprinting the job */ 31431492Swollman syslog(LOG_INFO, "restarting %s", pp->printer); 31597793Sgad if (of_pid > 0) { 31697793Sgad kill(of_pid, SIGCONT); /* to be sure */ 3171553Srgrimes (void) close(ofd); 31897793Sgad while ((i = wait(NULL)) > 0 && i != of_pid) 3191553Srgrimes ; 32079735Sgad if (i < 0) 32179735Sgad syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 32297793Sgad pp->printer, of_pid); 32397793Sgad of_pid = 0; 3241553Srgrimes } 3251553Srgrimes (void) close(pfd); /* close printer */ 3261553Srgrimes if (ftruncate(lfd, pidoff) < 0) 32797792Sgad syslog(LOG_WARNING, "%s: ftruncate(%s): %m", 32897792Sgad pp->printer, pp->lock_file); 32931492Swollman openpr(pp); /* try to reopen printer */ 3301553Srgrimes goto restart; 33115648Sjoerg } else { 33231492Swollman syslog(LOG_WARNING, "%s: job could not be %s (%s)", 33397792Sgad pp->printer, 33497792Sgad pp->remote ? "sent to remote host" : "printed", 33597792Sgad q->job_cfname); 33615648Sjoerg if (i == REPRINT) { 33727748Simp /* ensure we don't attempt this job again */ 33868401Sgad (void) unlink(q->job_cfname); 33968401Sgad q->job_cfname[0] = 'd'; 34068401Sgad (void) unlink(q->job_cfname); 34115648Sjoerg if (logname[0]) 34231492Swollman sendmail(pp, logname, FATALERR); 34315648Sjoerg } 3441553Srgrimes } 3451553Srgrimes } 34631492Swollman free(queue); 3471553Srgrimes /* 3481553Srgrimes * search the spool directory for more work. 3491553Srgrimes */ 35031492Swollman if ((nitems = getq(pp, &queue)) < 0) { 35131492Swollman syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 35297792Sgad pp->spool_dir); 3531553Srgrimes exit(1); 3541553Srgrimes } 3551553Srgrimes if (nitems == 0) { /* no more work to do */ 3561553Srgrimes done: 35768733Sgad if (jobcount > 0) { /* jobs actually printed */ 35831492Swollman if (!pp->no_formfeed && !pp->tof) 35931492Swollman (void) write(ofd, pp->form_feed, 36031492Swollman strlen(pp->form_feed)); 36131492Swollman if (pp->trailer != NULL) /* output trailer */ 36231492Swollman (void) write(ofd, pp->trailer, 36331492Swollman strlen(pp->trailer)); 3641553Srgrimes } 36519202Simp (void) close(ofd); 36619202Simp (void) wait(NULL); 36768664Sgad (void) unlink(tempstderr); 3681553Srgrimes exit(0); 3691553Srgrimes } 3701553Srgrimes goto again; 3711553Srgrimes} 3721553Srgrimes 3731553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3741553Srgrimes 3751553Srgrimeschar ifonts[4][40] = { 3761553Srgrimes _PATH_VFONTR, 3771553Srgrimes _PATH_VFONTI, 3781553Srgrimes _PATH_VFONTB, 3791553Srgrimes _PATH_VFONTS, 3801553Srgrimes}; 3811553Srgrimes 3821553Srgrimes/* 3831553Srgrimes * The remaining part is the reading of the control file (cf) 3841553Srgrimes * and performing the various actions. 3851553Srgrimes */ 3861553Srgrimesstatic int 38778146Sgadprintit(struct printer *pp, char *file) 3881553Srgrimes{ 3891553Srgrimes register int i; 39068734Sgad char *cp; 39168734Sgad int bombed, didignorehdr; 3921553Srgrimes 39368734Sgad bombed = OK; 39468734Sgad didignorehdr = 0; 3951553Srgrimes /* 3961553Srgrimes * open control file; ignore if no longer there. 3971553Srgrimes */ 3981553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 39997792Sgad syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); 40097791Sgad return (OK); 4011553Srgrimes } 4021553Srgrimes /* 4031553Srgrimes * Reset troff fonts. 4041553Srgrimes */ 4051553Srgrimes for (i = 0; i < 4; i++) 4061553Srgrimes strcpy(fonts[i], ifonts[i]); 40731492Swollman sprintf(&width[2], "%ld", pp->page_width); 4081553Srgrimes strcpy(indent+2, "0"); 4091553Srgrimes 41068253Sgad /* initialize job-specific count of datafiles processed */ 41168253Sgad job_dfcnt = 0; 41268253Sgad 4131553Srgrimes /* 4141553Srgrimes * read the control file for work to do 4151553Srgrimes * 4161553Srgrimes * file format -- first character in the line is a command 4171553Srgrimes * rest of the line is the argument. 4181553Srgrimes * valid commands are: 4191553Srgrimes * 4201553Srgrimes * S -- "stat info" for symbolic link protection 4211553Srgrimes * J -- "job name" on banner page 4221553Srgrimes * C -- "class name" on banner page 4231553Srgrimes * L -- "literal" user's name to print on banner 4241553Srgrimes * T -- "title" for pr 4251553Srgrimes * H -- "host name" of machine where lpr was done 4261553Srgrimes * P -- "person" user's login name 4271553Srgrimes * I -- "indent" amount to indent output 42815648Sjoerg * R -- laser dpi "resolution" 4291553Srgrimes * f -- "file name" name of text file to print 4301553Srgrimes * l -- "file name" text file with control chars 43183684Sgad * o -- "file name" postscript file, according to 43283684Sgad * the RFC. Here it is treated like an 'f'. 4331553Srgrimes * p -- "file name" text file to print with pr(1) 4341553Srgrimes * t -- "file name" troff(1) file to print 4351553Srgrimes * n -- "file name" ditroff(1) file to print 4361553Srgrimes * d -- "file name" dvi file to print 4371553Srgrimes * g -- "file name" plot(1G) file to print 4381553Srgrimes * v -- "file name" plain raster file to print 4391553Srgrimes * c -- "file name" cifplot file to print 4401553Srgrimes * 1 -- "R font file" for troff 4411553Srgrimes * 2 -- "I font file" for troff 4421553Srgrimes * 3 -- "B font file" for troff 4431553Srgrimes * 4 -- "S font file" for troff 4441553Srgrimes * N -- "name" of file (used by lpq) 4451553Srgrimes * U -- "unlink" name of file to remove 4461553Srgrimes * (after we print it. (Pass 2 only)). 4471553Srgrimes * M -- "mail" to user when done printing 44853956Sache * Z -- "locale" for pr 4491553Srgrimes * 4501553Srgrimes * getline reads a line and expands tabs to blanks 4511553Srgrimes */ 4521553Srgrimes 4531553Srgrimes /* pass 1 */ 4541553Srgrimes 4551553Srgrimes while (getline(cfp)) 4561553Srgrimes switch (line[0]) { 4571553Srgrimes case 'H': 45878300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 45927748Simp if (class[0] == '\0') { 46080133Sgad strlcpy(class, line+1, sizeof(class)); 46127748Simp } 4621553Srgrimes continue; 4631553Srgrimes 4641553Srgrimes case 'P': 46580133Sgad strlcpy(logname, line + 1, sizeof(logname)); 46631492Swollman if (pp->restricted) { /* restricted */ 4671553Srgrimes if (getpwnam(logname) == NULL) { 4681553Srgrimes bombed = NOACCT; 46931492Swollman sendmail(pp, line+1, bombed); 4701553Srgrimes goto pass2; 4711553Srgrimes } 4721553Srgrimes } 4731553Srgrimes continue; 4741553Srgrimes 4751553Srgrimes case 'S': 4761553Srgrimes cp = line+1; 4771553Srgrimes i = 0; 4781553Srgrimes while (*cp >= '0' && *cp <= '9') 4791553Srgrimes i = i * 10 + (*cp++ - '0'); 4801553Srgrimes fdev = i; 4811553Srgrimes cp++; 4821553Srgrimes i = 0; 4831553Srgrimes while (*cp >= '0' && *cp <= '9') 4841553Srgrimes i = i * 10 + (*cp++ - '0'); 4851553Srgrimes fino = i; 4861553Srgrimes continue; 4871553Srgrimes 4881553Srgrimes case 'J': 48927748Simp if (line[1] != '\0') { 49080133Sgad strlcpy(jobname, line + 1, sizeof(jobname)); 49127748Simp } else 4921553Srgrimes strcpy(jobname, " "); 4931553Srgrimes continue; 4941553Srgrimes 4951553Srgrimes case 'C': 4961553Srgrimes if (line[1] != '\0') 49780133Sgad strlcpy(class, line + 1, sizeof(class)); 49880133Sgad else if (class[0] == '\0') { 49980133Sgad /* XXX - why call gethostname instead of 50080133Sgad * just strlcpy'ing local_host? */ 5011553Srgrimes gethostname(class, sizeof(class)); 50280133Sgad class[sizeof(class) - 1] = '\0'; 50380133Sgad } 5041553Srgrimes continue; 5051553Srgrimes 5061553Srgrimes case 'T': /* header title for pr */ 50780133Sgad strlcpy(title, line + 1, sizeof(title)); 5081553Srgrimes continue; 5091553Srgrimes 5101553Srgrimes case 'L': /* identification line */ 51131492Swollman if (!pp->no_header && !pp->header_last) 51231492Swollman banner(pp, line+1, jobname); 5131553Srgrimes continue; 5141553Srgrimes 5151553Srgrimes case '1': /* troff fonts */ 5161553Srgrimes case '2': 5171553Srgrimes case '3': 5181553Srgrimes case '4': 51927748Simp if (line[1] != '\0') { 52080133Sgad strlcpy(fonts[line[0]-'1'], line + 1, 52180133Sgad (size_t)50); 52227748Simp } 5231553Srgrimes continue; 5241553Srgrimes 5251553Srgrimes case 'W': /* page width */ 52680133Sgad strlcpy(width+2, line + 1, sizeof(width) - 2); 5271553Srgrimes continue; 5281553Srgrimes 5291553Srgrimes case 'I': /* indent amount */ 53080133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 5311553Srgrimes continue; 5321553Srgrimes 53353956Sache case 'Z': /* locale for pr */ 53480133Sgad strlcpy(locale, line + 1, sizeof(locale)); 53553956Sache continue; 53653956Sache 5371553Srgrimes default: /* some file to print */ 53868467Sgad /* only lowercase cmd-codes include a file-to-print */ 53968467Sgad if ((line[0] < 'a') || (line[0] > 'z')) { 54068467Sgad /* ignore any other lines */ 54168467Sgad if (lflag <= 1) 54268467Sgad continue; 54368467Sgad if (!didignorehdr) { 54468467Sgad syslog(LOG_INFO, "%s: in %s :", 54597792Sgad pp->printer, file); 54668467Sgad didignorehdr = 1; 54768467Sgad } 54868467Sgad syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 54997792Sgad pp->printer, line[0], &line[1]); 55068467Sgad continue; 55168467Sgad } 55268467Sgad i = print(pp, line[0], line+1); 55368467Sgad switch (i) { 5541553Srgrimes case ERROR: 5551553Srgrimes if (bombed == OK) 5561553Srgrimes bombed = FATALERR; 5571553Srgrimes break; 5581553Srgrimes case REPRINT: 5591553Srgrimes (void) fclose(cfp); 56097791Sgad return (REPRINT); 5611553Srgrimes case FILTERERR: 5621553Srgrimes case ACCESS: 5631553Srgrimes bombed = i; 56431492Swollman sendmail(pp, logname, bombed); 5651553Srgrimes } 5661553Srgrimes title[0] = '\0'; 5671553Srgrimes continue; 5681553Srgrimes 5691553Srgrimes case 'N': 5701553Srgrimes case 'U': 5711553Srgrimes case 'M': 57215648Sjoerg case 'R': 5731553Srgrimes continue; 5741553Srgrimes } 5751553Srgrimes 5761553Srgrimes /* pass 2 */ 5771553Srgrimes 5781553Srgrimespass2: 5791553Srgrimes fseek(cfp, 0L, 0); 5801553Srgrimes while (getline(cfp)) 5811553Srgrimes switch (line[0]) { 5821553Srgrimes case 'L': /* identification line */ 58331492Swollman if (!pp->no_header && pp->header_last) 58431492Swollman banner(pp, line+1, jobname); 5851553Srgrimes continue; 5861553Srgrimes 5871553Srgrimes case 'M': 5881553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 58931492Swollman sendmail(pp, line+1, bombed); 5901553Srgrimes continue; 5911553Srgrimes 5921553Srgrimes case 'U': 59327748Simp if (strchr(line+1, '/')) 59427748Simp continue; 5951553Srgrimes (void) unlink(line+1); 5961553Srgrimes } 5971553Srgrimes /* 5981553Srgrimes * clean-up in case another control file exists 5991553Srgrimes */ 6001553Srgrimes (void) fclose(cfp); 6011553Srgrimes (void) unlink(file); 60297791Sgad return (bombed == OK ? OK : ERROR); 6031553Srgrimes} 6041553Srgrimes 6051553Srgrimes/* 6061553Srgrimes * Print a file. 6071553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 608228990Suqs * Return -1 if a non-recoverable error occurred, 6091553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 6101553Srgrimes * 1 if we should try to reprint this job and 6111553Srgrimes * 0 if all is well. 6121553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 6131553Srgrimes * stderr as the log file, and must not ignore SIGINT. 6141553Srgrimes */ 6151553Srgrimesstatic int 61678146Sgadprint(struct printer *pp, int format, char *file) 6171553Srgrimes{ 61853956Sache register int n, i; 6191553Srgrimes register char *prog; 62031492Swollman int fi, fo; 6211553Srgrimes FILE *fp; 622119192Sgad char *av[15], buf[SPL_BUFSIZ]; 62397793Sgad pid_t wpid; 62497793Sgad int p[2], retcode, stopped, wstatus, wstatus_set; 6251553Srgrimes struct stat stb; 6261553Srgrimes 627138939Sgad /* Make sure the entire data file has arrived. */ 628138939Sgad wait4data(pp, file); 629138939Sgad 63068467Sgad if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 63168467Sgad syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 63297792Sgad pp->printer, file, format); 63397791Sgad return (ERROR); 63468467Sgad } 6351553Srgrimes /* 6361553Srgrimes * Check to see if data file is a symbolic link. If so, it should 6371553Srgrimes * still point to the same file or someone is trying to print 6381553Srgrimes * something he shouldn't. 6391553Srgrimes */ 6401553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 6411553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 64297791Sgad return (ACCESS); 64368253Sgad 64468253Sgad job_dfcnt++; /* increment datafile counter for this job */ 64568734Sgad stopped = 0; /* output filter is not stopped */ 64668253Sgad 64768253Sgad /* everything seems OK, start it up */ 64831492Swollman if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 64931492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 65031492Swollman pp->tof = 1; 6511553Srgrimes } 65231492Swollman if (pp->filters[LPF_INPUT] == NULL 65383684Sgad && (format == 'f' || format == 'l' || format == 'o')) { 65431492Swollman pp->tof = 0; 655119192Sgad while ((n = read(fi, buf, SPL_BUFSIZ)) > 0) 6561553Srgrimes if (write(ofd, buf, n) != n) { 6571553Srgrimes (void) close(fi); 65897791Sgad return (REPRINT); 6591553Srgrimes } 6601553Srgrimes (void) close(fi); 66197791Sgad return (OK); 6621553Srgrimes } 6631553Srgrimes switch (format) { 6641553Srgrimes case 'p': /* print file using 'pr' */ 66531492Swollman if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 6661553Srgrimes prog = _PATH_PR; 66753956Sache i = 0; 66853956Sache av[i++] = "pr"; 66953956Sache av[i++] = width; 67053956Sache av[i++] = length; 67153956Sache av[i++] = "-h"; 67253956Sache av[i++] = *title ? title : " "; 67353956Sache av[i++] = "-L"; 67453956Sache av[i++] = *locale ? locale : "C"; 67553956Sache av[i++] = "-F"; 67653956Sache av[i] = 0; 6771553Srgrimes fo = ofd; 6781553Srgrimes goto start; 6791553Srgrimes } 6801553Srgrimes pipe(p); 68131492Swollman if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 682118881Sgad dup2(fi, STDIN_FILENO); /* file is stdin */ 683118881Sgad dup2(p[1], STDOUT_FILENO); /* pipe is stdout */ 6848094Sjkh closelog(); 68531492Swollman closeallfds(3); 6861553Srgrimes execl(_PATH_PR, "pr", width, length, 68753956Sache "-h", *title ? title : " ", 68853956Sache "-L", *locale ? locale : "C", 68979452Sbrian "-F", (char *)0); 6901553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 6911553Srgrimes exit(2); 6921553Srgrimes } 6931553Srgrimes (void) close(p[1]); /* close output side */ 6941553Srgrimes (void) close(fi); 6951553Srgrimes if (prchild < 0) { 6961553Srgrimes prchild = 0; 6971553Srgrimes (void) close(p[0]); 69897791Sgad return (ERROR); 6991553Srgrimes } 7001553Srgrimes fi = p[0]; /* use pipe for input */ 7011553Srgrimes case 'f': /* print plain text file */ 70231492Swollman prog = pp->filters[LPF_INPUT]; 7031553Srgrimes av[1] = width; 7041553Srgrimes av[2] = length; 7051553Srgrimes av[3] = indent; 7061553Srgrimes n = 4; 7071553Srgrimes break; 70886935Sgad case 'o': /* print postscript file */ 70986935Sgad /* 71086935Sgad * Treat this as a "plain file with control characters", and 71186935Sgad * assume the standard LPF_INPUT filter will recognize that 71286935Sgad * the data is postscript and know what to do with it. These 71386935Sgad * 'o'-file requests could come from MacOS 10.1 systems. 71486935Sgad * (later versions of MacOS 10 will explicitly use 'l') 71586935Sgad * A postscript file can contain binary data, which is why 'l' 71686935Sgad * is somewhat more appropriate than 'f'. 71786935Sgad */ 71886935Sgad /* FALLTHROUGH */ 7191553Srgrimes case 'l': /* like 'f' but pass control characters */ 72031492Swollman prog = pp->filters[LPF_INPUT]; 7211553Srgrimes av[1] = "-c"; 7221553Srgrimes av[2] = width; 7231553Srgrimes av[3] = length; 7241553Srgrimes av[4] = indent; 7251553Srgrimes n = 5; 7261553Srgrimes break; 7271553Srgrimes case 'r': /* print a fortran text file */ 72831492Swollman prog = pp->filters[LPF_FORTRAN]; 7291553Srgrimes av[1] = width; 7301553Srgrimes av[2] = length; 7311553Srgrimes n = 3; 7321553Srgrimes break; 7331553Srgrimes case 't': /* print troff output */ 7341553Srgrimes case 'n': /* print ditroff output */ 7351553Srgrimes case 'd': /* print tex output */ 7361553Srgrimes (void) unlink(".railmag"); 7371553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 73831492Swollman syslog(LOG_ERR, "%s: cannot create .railmag", 73997792Sgad pp->printer); 7401553Srgrimes (void) unlink(".railmag"); 7411553Srgrimes } else { 7421553Srgrimes for (n = 0; n < 4; n++) { 7431553Srgrimes if (fonts[n][0] != '/') 7441553Srgrimes (void) write(fo, _PATH_VFONT, 7451553Srgrimes sizeof(_PATH_VFONT) - 1); 7461553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 7471553Srgrimes (void) write(fo, "\n", 1); 7481553Srgrimes } 7491553Srgrimes (void) close(fo); 7501553Srgrimes } 75131492Swollman prog = (format == 't') ? pp->filters[LPF_TROFF] 75231492Swollman : ((format == 'n') ? pp->filters[LPF_DITROFF] 75331492Swollman : pp->filters[LPF_DVI]); 7541553Srgrimes av[1] = pxwidth; 7551553Srgrimes av[2] = pxlength; 7561553Srgrimes n = 3; 7571553Srgrimes break; 7581553Srgrimes case 'c': /* print cifplot output */ 75931492Swollman prog = pp->filters[LPF_CIFPLOT]; 7601553Srgrimes av[1] = pxwidth; 7611553Srgrimes av[2] = pxlength; 7621553Srgrimes n = 3; 7631553Srgrimes break; 7641553Srgrimes case 'g': /* print plot(1G) output */ 76531492Swollman prog = pp->filters[LPF_GRAPH]; 7661553Srgrimes av[1] = pxwidth; 7671553Srgrimes av[2] = pxlength; 7681553Srgrimes n = 3; 7691553Srgrimes break; 7701553Srgrimes case 'v': /* print raster output */ 77131492Swollman prog = pp->filters[LPF_RASTER]; 7721553Srgrimes av[1] = pxwidth; 7731553Srgrimes av[2] = pxlength; 7741553Srgrimes n = 3; 7751553Srgrimes break; 7761553Srgrimes default: 7771553Srgrimes (void) close(fi); 7781553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 77997792Sgad pp->printer, format); 78097791Sgad return (ERROR); 7811553Srgrimes } 78215648Sjoerg if (prog == NULL) { 78315648Sjoerg (void) close(fi); 78415648Sjoerg syslog(LOG_ERR, 78515648Sjoerg "%s: no filter found in printcap for format character '%c'", 78631492Swollman pp->printer, format); 78797791Sgad return (ERROR); 78815648Sjoerg } 78927635Simp if ((av[0] = strrchr(prog, '/')) != NULL) 7901553Srgrimes av[0]++; 7911553Srgrimes else 7921553Srgrimes av[0] = prog; 7931553Srgrimes av[n++] = "-n"; 7941553Srgrimes av[n++] = logname; 7951553Srgrimes av[n++] = "-h"; 79678300Sgad av[n++] = origin_host; 79731492Swollman av[n++] = pp->acct_file; 7981553Srgrimes av[n] = 0; 7991553Srgrimes fo = pfd; 80097793Sgad if (of_pid > 0) { /* stop output filter */ 8011553Srgrimes write(ofd, "\031\1", 2); 80297793Sgad while ((wpid = 80397793Sgad wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) 8041553Srgrimes ; 80597793Sgad if (wpid < 0) 80697792Sgad syslog(LOG_WARNING, 80797792Sgad "%s: after stopping 'of', wait3() returned: %m", 80879735Sgad pp->printer); 80997781Sgad else if (!WIFSTOPPED(wstatus)) { 8101553Srgrimes (void) close(fi); 81197781Sgad syslog(LOG_WARNING, "%s: output filter died " 81297781Sgad "(pid=%d retcode=%d termsig=%d)", 81397793Sgad pp->printer, of_pid, WEXITSTATUS(wstatus), 81497781Sgad WTERMSIG(wstatus)); 81597791Sgad return (REPRINT); 8161553Srgrimes } 8171553Srgrimes stopped++; 8181553Srgrimes } 8191553Srgrimesstart: 82031492Swollman if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 821118881Sgad dup2(fi, STDIN_FILENO); 822118881Sgad dup2(fo, STDOUT_FILENO); 82368664Sgad /* setup stderr for the filter (child process) 82468664Sgad * so it goes to our temporary errors file */ 82568664Sgad n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 8261553Srgrimes if (n >= 0) 827118881Sgad dup2(n, STDERR_FILENO); 8288094Sjkh closelog(); 82931492Swollman closeallfds(3); 8301553Srgrimes execv(prog, av); 83195067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 83295067Sgad prog); 8331553Srgrimes exit(2); 8341553Srgrimes } 8351553Srgrimes (void) close(fi); 83697789Sgad wstatus_set = 0; 8371553Srgrimes if (child < 0) 83897781Sgad retcode = 100; 83979735Sgad else { 84097793Sgad while ((wpid = wait(&wstatus)) > 0 && wpid != child) 8411553Srgrimes ; 84297793Sgad if (wpid < 0) { 84397781Sgad retcode = 100; 84497792Sgad syslog(LOG_WARNING, 84597792Sgad "%s: after execv(%s), wait() returned: %m", 84679735Sgad pp->printer, prog); 84797789Sgad } else { 84897789Sgad wstatus_set = 1; 84997781Sgad retcode = WEXITSTATUS(wstatus); 85097789Sgad } 85179735Sgad } 8521553Srgrimes child = 0; 8531553Srgrimes prchild = 0; 8541553Srgrimes if (stopped) { /* restart output filter */ 85597793Sgad if (kill(of_pid, SIGCONT) < 0) { 8561553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 8571553Srgrimes exit(1); 8581553Srgrimes } 8591553Srgrimes } 86031492Swollman pp->tof = 0; 8611553Srgrimes 86268664Sgad /* Copy the filter's output to "lf" logfile */ 86368664Sgad if ((fp = fopen(tempstderr, "r"))) { 8641553Srgrimes while (fgets(buf, sizeof(buf), fp)) 8651553Srgrimes fputs(buf, stderr); 8661553Srgrimes fclose(fp); 8671553Srgrimes } 8681553Srgrimes 86997789Sgad if (wstatus_set && !WIFEXITED(wstatus)) { 87015648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 87197781Sgad pp->printer, format, WTERMSIG(wstatus)); 87297791Sgad return (ERROR); 8731553Srgrimes } 87497781Sgad switch (retcode) { 8751553Srgrimes case 0: 87631492Swollman pp->tof = 1; 87797791Sgad return (OK); 8781553Srgrimes case 1: 87997791Sgad return (REPRINT); 88015648Sjoerg case 2: 88197791Sgad return (ERROR); 8821553Srgrimes default: 88315648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 88497792Sgad pp->printer, format, retcode); 88597791Sgad return (FILTERERR); 8861553Srgrimes } 8871553Srgrimes} 8881553Srgrimes 8891553Srgrimes/* 8901553Srgrimes * Send the daemon control file (cf) and any data files. 891228990Suqs * Return -1 if a non-recoverable error occurred, 1 if a recoverable error and 8921553Srgrimes * 0 if all is well. 8931553Srgrimes */ 8941553Srgrimesstatic int 89578146Sgadsendit(struct printer *pp, char *file) 8961553Srgrimes{ 89795293Sgad int dfcopies, err, i; 898119192Sgad char *cp, last[sizeof(line)]; 8991553Srgrimes 9001553Srgrimes /* 9011553Srgrimes * open control file 9021553Srgrimes */ 9031553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 90497791Sgad return (OK); 90568253Sgad 90668253Sgad /* initialize job-specific count of datafiles processed */ 90768253Sgad job_dfcnt = 0; 90868253Sgad 9091553Srgrimes /* 9101553Srgrimes * read the control file for work to do 9111553Srgrimes * 9121553Srgrimes * file format -- first character in the line is a command 9131553Srgrimes * rest of the line is the argument. 9141553Srgrimes * commands of interest are: 9151553Srgrimes * 9161553Srgrimes * a-z -- "file name" name of file to print 9171553Srgrimes * U -- "unlink" name of file to remove 9181553Srgrimes * (after we print it. (Pass 2 only)). 9191553Srgrimes */ 9201553Srgrimes 9211553Srgrimes /* 9221553Srgrimes * pass 1 9231553Srgrimes */ 92495293Sgad err = OK; 9251553Srgrimes while (getline(cfp)) { 9261553Srgrimes again: 9271553Srgrimes if (line[0] == 'S') { 9281553Srgrimes cp = line+1; 9291553Srgrimes i = 0; 9301553Srgrimes while (*cp >= '0' && *cp <= '9') 9311553Srgrimes i = i * 10 + (*cp++ - '0'); 9321553Srgrimes fdev = i; 9331553Srgrimes cp++; 9341553Srgrimes i = 0; 9351553Srgrimes while (*cp >= '0' && *cp <= '9') 9361553Srgrimes i = i * 10 + (*cp++ - '0'); 9371553Srgrimes fino = i; 93824831Sbrian } else if (line[0] == 'H') { 93978300Sgad strlcpy(origin_host, line + 1, sizeof(origin_host)); 94068343Sgad if (class[0] == '\0') { 94180133Sgad strlcpy(class, line + 1, sizeof(class)); 94268343Sgad } 94324831Sbrian } else if (line[0] == 'P') { 94480133Sgad strlcpy(logname, line + 1, sizeof(logname)); 94531492Swollman if (pp->restricted) { /* restricted */ 94624831Sbrian if (getpwnam(logname) == NULL) { 94731492Swollman sendmail(pp, line+1, NOACCT); 94824831Sbrian err = ERROR; 94924831Sbrian break; 95024831Sbrian } 95124831Sbrian } 95224831Sbrian } else if (line[0] == 'I') { 95380133Sgad strlcpy(indent+2, line + 1, sizeof(indent) - 2); 95424831Sbrian } else if (line[0] >= 'a' && line[0] <= 'z') { 95595293Sgad dfcopies = 1; 9561553Srgrimes strcpy(last, line); 95795293Sgad while ((i = getline(cfp)) != 0) { 95895293Sgad if (strcmp(last, line) != 0) 9591553Srgrimes break; 96095293Sgad dfcopies++; 96195293Sgad } 96295293Sgad switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 9631553Srgrimes case OK: 9641553Srgrimes if (i) 9651553Srgrimes goto again; 9661553Srgrimes break; 9671553Srgrimes case REPRINT: 9681553Srgrimes (void) fclose(cfp); 96997791Sgad return (REPRINT); 9701553Srgrimes case ACCESS: 97131492Swollman sendmail(pp, logname, ACCESS); 9721553Srgrimes case ERROR: 9731553Srgrimes err = ERROR; 9741553Srgrimes } 9751553Srgrimes break; 9761553Srgrimes } 9771553Srgrimes } 97895293Sgad if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 9791553Srgrimes (void) fclose(cfp); 98097791Sgad return (REPRINT); 9811553Srgrimes } 9821553Srgrimes /* 9831553Srgrimes * pass 2 9841553Srgrimes */ 9851553Srgrimes fseek(cfp, 0L, 0); 9861553Srgrimes while (getline(cfp)) 98727748Simp if (line[0] == 'U' && !strchr(line+1, '/')) 9881553Srgrimes (void) unlink(line+1); 9891553Srgrimes /* 9901553Srgrimes * clean-up in case another control file exists 9911553Srgrimes */ 9921553Srgrimes (void) fclose(cfp); 9931553Srgrimes (void) unlink(file); 99497791Sgad return (err); 9951553Srgrimes} 9961553Srgrimes 9971553Srgrimes/* 9981553Srgrimes * Send a data file to the remote machine and spool it. 9991553Srgrimes * Return positive if we should try resending. 10001553Srgrimes */ 10011553Srgrimesstatic int 100295293Sgadsendfile(struct printer *pp, int type, char *file, char format, int copyreq) 10031553Srgrimes{ 100494036Sgad int i, amt; 10051553Srgrimes struct stat stb; 100694032Sgad char *av[15], *filtcmd; 1007119192Sgad char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 100895293Sgad int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 10091553Srgrimes 1010138939Sgad /* Make sure the entire data file has arrived. */ 1011138939Sgad wait4data(pp, file); 1012138939Sgad 101374124Sgad statrc = lstat(file, &stb); 101474124Sgad if (statrc < 0) { 101574124Sgad syslog(LOG_ERR, "%s: error from lstat(%s): %m", 101674124Sgad pp->printer, file); 101794036Sgad return (ERROR); 101874124Sgad } 101994036Sgad sfd = open(file, O_RDONLY); 102094036Sgad if (sfd < 0) { 102174124Sgad syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 102274124Sgad pp->printer, file); 102394036Sgad return (ERROR); 102474124Sgad } 10251553Srgrimes /* 10261553Srgrimes * Check to see if data file is a symbolic link. If so, it should 10271553Srgrimes * still point to the same file or someone is trying to print something 10281553Srgrimes * he shouldn't. 10291553Srgrimes */ 103094036Sgad if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 103194036Sgad (stb.st_dev != fdev || stb.st_ino != fino)) { 103294036Sgad close(sfd); 103394036Sgad return (ACCESS); 103494036Sgad } 103524831Sbrian 103694032Sgad /* Everything seems OK for reading the file, now to send it */ 103794032Sgad filtcmd = NULL; 103824831Sbrian sizerr = 0; 103994032Sgad tfd = -1; 104024831Sbrian if (type == '\3') { 104194032Sgad /* 104294032Sgad * Type == 3 means this is a datafile, not a control file. 104394032Sgad * Increment the counter of data-files in this job, and 104494032Sgad * then check for input or output filters (which are only 104594032Sgad * applied to datafiles, not control files). 104694032Sgad */ 104794032Sgad job_dfcnt++; 104894032Sgad 104994032Sgad /* 105094032Sgad * Note that here we are filtering datafiles, one at a time, 105194032Sgad * as they are sent to the remote machine. Here, the *only* 105294032Sgad * difference between an input filter (`if=') and an output 105394032Sgad * filter (`of=') is the argument list that the filter is 105494032Sgad * started up with. Here, the output filter is executed 105594032Sgad * for each individual file as it is sent. This is not the 105694032Sgad * same as local print queues, where the output filter is 105794032Sgad * started up once, and then all jobs are passed thru that 105894032Sgad * single invocation of the output filter. 105994032Sgad * 106094032Sgad * Also note that a queue for a remote-machine can have an 106194032Sgad * input filter or an output filter, but not both. 106294032Sgad */ 106331492Swollman if (pp->filters[LPF_INPUT]) { 106494032Sgad filtcmd = pp->filters[LPF_INPUT]; 106594032Sgad av[0] = filtcmd; 106694032Sgad narg = 0; 106794032Sgad strcpy(opt_c, "-c"); 106894032Sgad strcpy(opt_h, "-h"); 106994032Sgad strcpy(opt_n, "-n"); 107024831Sbrian if (format == 'l') 107194032Sgad av[++narg] = opt_c; 107294032Sgad av[++narg] = width; 107394032Sgad av[++narg] = length; 107494032Sgad av[++narg] = indent; 107594032Sgad av[++narg] = opt_n; 107694032Sgad av[++narg] = logname; 107794032Sgad av[++narg] = opt_h; 107894032Sgad av[++narg] = origin_host; 107994032Sgad av[++narg] = pp->acct_file; 108094032Sgad av[++narg] = NULL; 108194032Sgad } else if (pp->filters[LPF_OUTPUT]) { 108294032Sgad filtcmd = pp->filters[LPF_OUTPUT]; 108394032Sgad av[0] = filtcmd; 108494032Sgad narg = 0; 108594032Sgad av[++narg] = width; 108694032Sgad av[++narg] = length; 108794032Sgad av[++narg] = NULL; 108824831Sbrian } 108924831Sbrian } 109094032Sgad if (filtcmd) { 109194032Sgad /* 109294032Sgad * If there is an input or output filter, we have to run 109394032Sgad * the datafile thru that filter and store the result as 109494032Sgad * a temporary spool file, because the protocol requires 109594032Sgad * that we send the remote host the file-size before we 109694032Sgad * start to send any of the data. 109794032Sgad */ 109894032Sgad strcpy(tfile, TFILENAME); 109994032Sgad tfd = mkstemp(tfile); 110094032Sgad if (tfd == -1) { 110194032Sgad syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 110294032Sgad TFILENAME); 110394036Sgad sfres = ERROR; 110494036Sgad goto return_sfres; 110594032Sgad } 110694036Sgad filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 110724831Sbrian 110894032Sgad /* process the return-code from the filter */ 110994032Sgad switch (filtstat) { 111094032Sgad case 0: 111194032Sgad break; 111294032Sgad case 1: 111394036Sgad sfres = REPRINT; 111494036Sgad goto return_sfres; 111594032Sgad case 2: 111694036Sgad sfres = ERROR; 111794036Sgad goto return_sfres; 111894032Sgad default: 111994032Sgad syslog(LOG_WARNING, 112094032Sgad "%s: filter '%c' exited (retcode=%d)", 112194032Sgad pp->printer, format, filtstat); 112294036Sgad sfres = FILTERERR; 112394036Sgad goto return_sfres; 112494032Sgad } 112594032Sgad statrc = fstat(tfd, &stb); /* to find size of tfile */ 112694032Sgad if (statrc < 0) { 112794032Sgad syslog(LOG_ERR, 112894032Sgad "%s: error processing 'if', fstat(%s): %m", 112994032Sgad pp->printer, tfile); 113094036Sgad sfres = ERROR; 113194036Sgad goto return_sfres; 113294032Sgad } 113394036Sgad close(sfd); 113494036Sgad sfd = tfd; 113594036Sgad lseek(sfd, 0, SEEK_SET); 113694032Sgad } 113794032Sgad 113895293Sgad copycnt = 0; 113995293Sgadsendagain: 114095293Sgad copycnt++; 114195293Sgad 114295293Sgad if (copycnt < 2) 1143228664Sdim (void) sprintf(buf, "%c%" PRId64 " %s\n", type, stb.st_size, 1144228664Sdim file); 114595293Sgad else 1146228664Sdim (void) sprintf(buf, "%c%" PRId64 " %s_c%d\n", type, stb.st_size, 114795293Sgad file, copycnt); 11481553Srgrimes amt = strlen(buf); 11491553Srgrimes for (i = 0; ; i++) { 11501553Srgrimes if (write(pfd, buf, amt) != amt || 115131492Swollman (resp = response(pp)) < 0 || resp == '\1') { 115294036Sgad sfres = REPRINT; 115394036Sgad goto return_sfres; 11541553Srgrimes } else if (resp == '\0') 11551553Srgrimes break; 11561553Srgrimes if (i == 0) 115731492Swollman pstatus(pp, 115831492Swollman "no space on remote; waiting for queue to drain"); 11591553Srgrimes if (i == 10) 11601553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 116197792Sgad pp->printer, pp->remote_host); 11621553Srgrimes sleep(5 * 60); 11631553Srgrimes } 11641553Srgrimes if (i) 116531492Swollman pstatus(pp, "sending to %s", pp->remote_host); 116695293Sgad /* 116795293Sgad * XXX - we should change trstat_init()/trstat_write() to include 116895293Sgad * the copycnt in the statistics record it may write. 116995293Sgad */ 117068253Sgad if (type == '\3') 117168253Sgad trstat_init(pp, file, job_dfcnt); 1172119192Sgad for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) { 1173119192Sgad amt = SPL_BUFSIZ; 11741553Srgrimes if (i + amt > stb.st_size) 11751553Srgrimes amt = stb.st_size - i; 117694036Sgad if (sizerr == 0 && read(sfd, buf, amt) != amt) 11771553Srgrimes sizerr = 1; 11781553Srgrimes if (write(pfd, buf, amt) != amt) { 117994036Sgad sfres = REPRINT; 118094036Sgad goto return_sfres; 11811553Srgrimes } 11821553Srgrimes } 11831553Srgrimes 11841553Srgrimes if (sizerr) { 118531492Swollman syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 11861553Srgrimes /* tell recvjob to ignore this file */ 11871553Srgrimes (void) write(pfd, "\1", 1); 118894036Sgad sfres = ERROR; 118994036Sgad goto return_sfres; 11901553Srgrimes } 119131492Swollman if (write(pfd, "", 1) != 1 || response(pp)) { 119294036Sgad sfres = REPRINT; 119394036Sgad goto return_sfres; 119424831Sbrian } 119595293Sgad if (type == '\3') { 119668253Sgad trstat_write(pp, TR_SENDING, stb.st_size, logname, 119795293Sgad pp->remote_host, origin_host); 119895293Sgad /* 119995293Sgad * Usually we only need to send one copy of a datafile, 120095293Sgad * because the control-file will simply print the same 120195293Sgad * file multiple times. However, some printers ignore 120295293Sgad * the control file, and simply print each data file as 120395293Sgad * it arrives. For such "remote hosts", we need to 120495293Sgad * transfer the same data file multiple times. Such a 120595293Sgad * a host is indicated by adding 'rc' to the printcap 120695293Sgad * entry. 120795293Sgad * XXX - Right now this ONLY works for remote hosts which 120895293Sgad * do ignore the name of the data file, because 120995293Sgad * this sends the file multiple times with slight 121095293Sgad * changes to the filename. To do this right would 121195293Sgad * require that we also rewrite the control file 121295293Sgad * to match those filenames. 121395293Sgad */ 121495293Sgad if (pp->resend_copies && (copycnt < copyreq)) { 121595293Sgad lseek(sfd, 0, SEEK_SET); 121695293Sgad goto sendagain; 121795293Sgad } 121895293Sgad } 121994036Sgad sfres = OK; 122094036Sgad 122194036Sgadreturn_sfres: 122294036Sgad (void)close(sfd); 122394036Sgad if (tfd != -1) { 122494036Sgad /* 122594036Sgad * If tfd is set, then it is the same value as sfd, and 122694036Sgad * therefore it is already closed at this point. All 122794036Sgad * we need to do is remove the temporary file. 122894036Sgad */ 122994036Sgad tfd = -1; 123094036Sgad unlink(tfile); 123194036Sgad } 123294036Sgad return (sfres); 12331553Srgrimes} 12341553Srgrimes 12351553Srgrimes/* 1236138939Sgad * Some print servers send the control-file first, and then start sending the 1237138939Sgad * matching data file(s). That is not the correct order. If some queue is 1238138939Sgad * already printing an active job, then when that job is finished the queue 1239138939Sgad * may proceed to the control file of any incoming print job. This turns 1240138939Sgad * into a race between the process which is receiving the data file, and the 1241138939Sgad * process which is actively printing the very same file. When the remote 1242138939Sgad * server sends files in the wrong order, it is even possible that a queue 1243138939Sgad * will start to print a data file before the file has been created! 1244138939Sgad * 1245138939Sgad * So before we start to print() or send() a data file, we call this routine 1246138939Sgad * to make sure the data file is not still changing in size. Note that this 1247138939Sgad * problem will only happen for jobs arriving from a remote host, and that 1248138939Sgad * the process which has decided to print this job (and is thus making this 1249138939Sgad * check) is *not* the process which is receiving the job. 1250138939Sgad * 1251138939Sgad * A second benefit of this is that any incoming job is guaranteed to appear 1252138939Sgad * in a queue listing for at least a few seconds after it has arrived. Some 1253138939Sgad * lpr implementations get confused if they send a job and it disappears 1254138939Sgad * from the queue before they can check on it. 1255138939Sgad */ 1256138939Sgad#define MAXWAIT_ARRIVE 16 /* max to wait for the file to *exist* */ 1257138939Sgad#define MAXWAIT_4DATA (20*60) /* max to wait for it to stop changing */ 1258138939Sgad#define MINWAIT_4DATA 4 /* This value must be >= 1 */ 1259138939Sgad#define DEBUG_MINWAIT 1 1260138939Sgadstatic void 1261138939Sgadwait4data(struct printer *pp, const char *dfile) 1262138939Sgad{ 1263138939Sgad const char *cp; 1264138939Sgad int statres; 1265211190Sgad u_int sleepreq; 1266138939Sgad size_t dlen, hlen; 1267211190Sgad time_t amtslept, cur_time, prev_mtime; 1268138939Sgad struct stat statdf; 1269138939Sgad 1270138939Sgad /* Skip these checks if the print job is from the local host. */ 1271138939Sgad dlen = strlen(dfile); 1272138939Sgad hlen = strlen(local_host); 1273138939Sgad if (dlen > hlen) { 1274138939Sgad cp = dfile + dlen - hlen; 1275138939Sgad if (strcmp(cp, local_host) == 0) 1276138939Sgad return; 1277138939Sgad } 1278138939Sgad 1279138939Sgad /* 1280138939Sgad * If this data file does not exist, then wait up to MAXWAIT_ARRIVE 1281138939Sgad * seconds for it to arrive. 1282138939Sgad */ 1283138939Sgad amtslept = 0; 1284138939Sgad statres = stat(dfile, &statdf); 1285138939Sgad while (statres < 0 && amtslept < MAXWAIT_ARRIVE) { 1286138939Sgad if (amtslept == 0) 1287138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1288138939Sgad amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); 1289138939Sgad statres = stat(dfile, &statdf); 1290138939Sgad } 1291138939Sgad if (statres < 0) { 1292138939Sgad /* The file still does not exist, so just give up on it. */ 1293138939Sgad syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s", 1294138939Sgad pp->printer, dfile); 1295138939Sgad return; 1296138939Sgad } 1297138939Sgad 1298138939Sgad /* 1299138939Sgad * The file exists, so keep waiting until the data file has not 1300211190Sgad * changed for some reasonable amount of time. Extra care is 1301211190Sgad * taken when computing wait-times, just in case there are data 1302211190Sgad * files with a last-modify time in the future. While that is 1303211190Sgad * very unlikely to happen, it can happen when the system has 1304211190Sgad * a flakey time-of-day clock. 1305138939Sgad */ 1306211190Sgad prev_mtime = statdf.st_mtime; 1307211190Sgad cur_time = time(NULL); 1308211190Sgad if (statdf.st_mtime >= cur_time - MINWAIT_4DATA) { 1309211190Sgad if (statdf.st_mtime >= cur_time) /* some TOD oddity */ 1310211190Sgad sleepreq = MINWAIT_4DATA; 1311211190Sgad else 1312211190Sgad sleepreq = cur_time - statdf.st_mtime; 1313138939Sgad if (amtslept == 0) 1314138939Sgad pstatus(pp, "Waiting for data file from remote host"); 1315211190Sgad amtslept += sleepreq - sleep(sleepreq); 1316138939Sgad statres = stat(dfile, &statdf); 1317138939Sgad } 1318211190Sgad sleepreq = MINWAIT_4DATA; 1319211190Sgad while (statres == 0 && amtslept < MAXWAIT_4DATA) { 1320211190Sgad if (statdf.st_mtime == prev_mtime) 1321211190Sgad break; 1322211190Sgad prev_mtime = statdf.st_mtime; 1323211190Sgad amtslept += sleepreq - sleep(sleepreq); 1324211190Sgad statres = stat(dfile, &statdf); 1325211190Sgad } 1326138939Sgad 1327138939Sgad if (statres != 0) 1328138939Sgad syslog(LOG_WARNING, "%s: %s disappeared during wait4data()", 1329138939Sgad pp->printer, dfile); 1330138939Sgad else if (amtslept > MAXWAIT_4DATA) 1331138939Sgad syslog(LOG_WARNING, 1332138939Sgad "%s: %s still changing after %lu secs in wait4data()", 1333138939Sgad pp->printer, dfile, (unsigned long)amtslept); 1334138939Sgad#if DEBUG_MINWAIT 1335138939Sgad else if (amtslept > MINWAIT_4DATA) 1336138939Sgad syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)", 1337138939Sgad pp->printer, (unsigned long)amtslept, dfile); 1338138939Sgad#endif 1339138939Sgad} 1340138939Sgad#undef MAXWAIT_ARRIVE 1341138939Sgad#undef MAXWAIT_4DATA 1342138939Sgad#undef MINWAIT_4DATA 1343138939Sgad 1344138939Sgad/* 134594032Sgad * This routine is called to execute one of the filters as was 134694036Sgad * specified in a printcap entry. While the child-process will read 134794036Sgad * all of 'infd', it is up to the caller to close that file descriptor 134894036Sgad * in the parent process. 134994032Sgad */ 135094032Sgadstatic int 135194032Sgadexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 135294032Sgad{ 135397793Sgad pid_t fpid, wpid; 135497793Sgad int errfd, retcode, wstatus; 135594032Sgad FILE *errfp; 135694032Sgad char buf[BUFSIZ], *slash; 135794032Sgad 135894032Sgad fpid = dofork(pp, DORETURN); 135994032Sgad if (fpid != 0) { 136094032Sgad /* 136194032Sgad * This is the parent process, which just waits for the child 136294032Sgad * to complete and then returns the result. Note that it is 136394032Sgad * the child process which reads the input stream. 136494032Sgad */ 136594032Sgad if (fpid < 0) 136697781Sgad retcode = 100; 136794032Sgad else { 136897781Sgad while ((wpid = wait(&wstatus)) > 0 && 136994032Sgad wpid != fpid) 137094032Sgad ; 137194032Sgad if (wpid < 0) { 137297781Sgad retcode = 100; 137394032Sgad syslog(LOG_WARNING, 137494032Sgad "%s: after execv(%s), wait() returned: %m", 137594032Sgad pp->printer, f_cmd); 137697781Sgad } else 137797781Sgad retcode = WEXITSTATUS(wstatus); 137894032Sgad } 137994032Sgad 138094032Sgad /* 138194032Sgad * Copy everything the filter wrote to stderr from our 138294032Sgad * temporary errors file to the "lf=" logfile. 138394032Sgad */ 138494032Sgad errfp = fopen(tempstderr, "r"); 138594032Sgad if (errfp) { 138694032Sgad while (fgets(buf, sizeof(buf), errfp)) 138794032Sgad fputs(buf, stderr); 138894032Sgad fclose(errfp); 138994032Sgad } 139094032Sgad 139197781Sgad return (retcode); 139294032Sgad } 139394032Sgad 139494032Sgad /* 139594032Sgad * This is the child process, which is the one that executes the 139694032Sgad * given filter. 139794032Sgad */ 139894032Sgad /* 139994032Sgad * If the first parameter has any slashes in it, then change it 140094032Sgad * to point to the first character after the last slash. 140194032Sgad */ 140294032Sgad slash = strrchr(f_av[0], '/'); 140394032Sgad if (slash != NULL) 140494032Sgad f_av[0] = slash + 1; 140594032Sgad /* 140694032Sgad * XXX - in the future, this should setup an explicit list of 140794032Sgad * environment variables and use execve()! 140894032Sgad */ 140994032Sgad 141094032Sgad /* 141194032Sgad * Setup stdin, stdout, and stderr as we want them when the filter 141294032Sgad * is running. Stderr is setup so it points to a temporary errors 141394032Sgad * file, and the parent process will copy that temporary file to 141494032Sgad * the real logfile after the filter completes. 141594032Sgad */ 1416118881Sgad dup2(infd, STDIN_FILENO); 1417118881Sgad dup2(outfd, STDOUT_FILENO); 141894032Sgad errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 141994032Sgad if (errfd >= 0) 1420118881Sgad dup2(errfd, STDERR_FILENO); 142194032Sgad closelog(); 142294032Sgad closeallfds(3); 142394032Sgad execv(f_cmd, f_av); 142495067Sgad syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 142594032Sgad exit(2); 142694032Sgad /* NOTREACHED */ 142794032Sgad} 142894032Sgad 142994032Sgad/* 14301553Srgrimes * Check to make sure there have been no errors and that both programs 14311553Srgrimes * are in sync with eachother. 14321553Srgrimes * Return non-zero if the connection was lost. 14331553Srgrimes */ 14341553Srgrimesstatic char 143578146Sgadresponse(const struct printer *pp) 14361553Srgrimes{ 14371553Srgrimes char resp; 14381553Srgrimes 14391553Srgrimes if (read(pfd, &resp, 1) != 1) { 144031492Swollman syslog(LOG_INFO, "%s: lost connection", pp->printer); 144197791Sgad return (-1); 14421553Srgrimes } 144397791Sgad return (resp); 14441553Srgrimes} 14451553Srgrimes 14461553Srgrimes/* 14471553Srgrimes * Banner printing stuff 14481553Srgrimes */ 14491553Srgrimesstatic void 145078146Sgadbanner(struct printer *pp, char *name1, char *name2) 14511553Srgrimes{ 14521553Srgrimes time_t tvec; 14531553Srgrimes 14541553Srgrimes time(&tvec); 145531492Swollman if (!pp->no_formfeed && !pp->tof) 145631492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 145731492Swollman if (pp->short_banner) { /* short banner only */ 14581553Srgrimes if (class[0]) { 14591553Srgrimes (void) write(ofd, class, strlen(class)); 14601553Srgrimes (void) write(ofd, ":", 1); 14611553Srgrimes } 14621553Srgrimes (void) write(ofd, name1, strlen(name1)); 14631553Srgrimes (void) write(ofd, " Job: ", 7); 14641553Srgrimes (void) write(ofd, name2, strlen(name2)); 14651553Srgrimes (void) write(ofd, " Date: ", 8); 14661553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14671553Srgrimes (void) write(ofd, "\n", 1); 14681553Srgrimes } else { /* normal banner */ 14691553Srgrimes (void) write(ofd, "\n\n\n", 3); 147031492Swollman scan_out(pp, ofd, name1, '\0'); 14711553Srgrimes (void) write(ofd, "\n\n", 2); 147231492Swollman scan_out(pp, ofd, name2, '\0'); 14731553Srgrimes if (class[0]) { 14741553Srgrimes (void) write(ofd,"\n\n\n",3); 147531492Swollman scan_out(pp, ofd, class, '\0'); 14761553Srgrimes } 14771553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 14781553Srgrimes (void) write(ofd, name2, strlen(name2)); 14791553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 14801553Srgrimes (void) write(ofd, ctime(&tvec), 24); 14811553Srgrimes (void) write(ofd, "\n", 1); 14821553Srgrimes } 148331492Swollman if (!pp->no_formfeed) 148431492Swollman (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 148531492Swollman pp->tof = 1; 14861553Srgrimes} 14871553Srgrimes 14881553Srgrimesstatic char * 148978146Sgadscnline(int key, char *p, int c) 14901553Srgrimes{ 149139084Swollman register int scnwidth; 14921553Srgrimes 14931553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 14941553Srgrimes key <<= 1; 14951553Srgrimes *p++ = key & 0200 ? c : BACKGND; 14961553Srgrimes } 14971553Srgrimes return (p); 14981553Srgrimes} 14991553Srgrimes 15001553Srgrimes#define TRC(q) (((q)-' ')&0177) 15011553Srgrimes 15021553Srgrimesstatic void 150378146Sgadscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 15041553Srgrimes{ 15051553Srgrimes register char *strp; 150639084Swollman register int nchrs, j; 15071553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 15081553Srgrimes int d, scnhgt; 15091553Srgrimes 15101553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 15111553Srgrimes strp = &outbuf[0]; 15121553Srgrimes sp = scsp; 15131553Srgrimes for (nchrs = 0; ; ) { 15141553Srgrimes d = dropit(c = TRC(cc = *sp++)); 15151553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 15161553Srgrimes for (j = WIDTH; --j;) 15171553Srgrimes *strp++ = BACKGND; 15181553Srgrimes else 151931492Swollman strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 152031492Swollman if (*sp == dlm || *sp == '\0' || 152131492Swollman nchrs++ >= pp->page_width/(WIDTH+1)-1) 15221553Srgrimes break; 15231553Srgrimes *strp++ = BACKGND; 15241553Srgrimes *strp++ = BACKGND; 15251553Srgrimes } 15261553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 15271553Srgrimes ; 15281553Srgrimes strp++; 15298857Srgrimes *strp++ = '\n'; 15301553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 15311553Srgrimes } 15321553Srgrimes} 15331553Srgrimes 15341553Srgrimesstatic int 153578146Sgaddropit(int c) 15361553Srgrimes{ 15371553Srgrimes switch(c) { 15381553Srgrimes 15391553Srgrimes case TRC('_'): 15401553Srgrimes case TRC(';'): 15411553Srgrimes case TRC(','): 15421553Srgrimes case TRC('g'): 15431553Srgrimes case TRC('j'): 15441553Srgrimes case TRC('p'): 15451553Srgrimes case TRC('q'): 15461553Srgrimes case TRC('y'): 15471553Srgrimes return (DROP); 15481553Srgrimes 15491553Srgrimes default: 15501553Srgrimes return (0); 15511553Srgrimes } 15521553Srgrimes} 15531553Srgrimes 15541553Srgrimes/* 15551553Srgrimes * sendmail --- 15561553Srgrimes * tell people about job completion 15571553Srgrimes */ 15581553Srgrimesstatic void 155994038Sgadsendmail(struct printer *pp, char *userid, int bombed) 15601553Srgrimes{ 15611553Srgrimes register int i; 15621553Srgrimes int p[2], s; 156378146Sgad register const char *cp; 15641553Srgrimes struct stat stb; 15651553Srgrimes FILE *fp; 15661553Srgrimes 15671553Srgrimes pipe(p); 156831492Swollman if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1569118881Sgad dup2(p[0], STDIN_FILENO); 15708094Sjkh closelog(); 157131492Swollman closeallfds(3); 157227635Simp if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 15731553Srgrimes cp++; 157431492Swollman else 15751553Srgrimes cp = _PATH_SENDMAIL; 157679452Sbrian execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 157731492Swollman _exit(0); 15781553Srgrimes } else if (s > 0) { /* parent */ 1579118881Sgad dup2(p[1], STDOUT_FILENO); 158094038Sgad printf("To: %s@%s\n", userid, origin_host); 158131492Swollman printf("Subject: %s printer job \"%s\"\n", pp->printer, 158215648Sjoerg *jobname ? jobname : "<unknown>"); 158378300Sgad printf("Reply-To: root@%s\n\n", local_host); 15841553Srgrimes printf("Your printer job "); 15851553Srgrimes if (*jobname) 15861553Srgrimes printf("(%s) ", jobname); 158794040Sgad 15881553Srgrimes switch (bombed) { 15891553Srgrimes case OK: 159094040Sgad cp = "OK"; 15911553Srgrimes printf("\ncompleted successfully\n"); 15921553Srgrimes break; 15931553Srgrimes default: 15941553Srgrimes case FATALERR: 159594040Sgad cp = "FATALERR"; 15961553Srgrimes printf("\ncould not be printed\n"); 15971553Srgrimes break; 15981553Srgrimes case NOACCT: 159994040Sgad cp = "NOACCT"; 160078300Sgad printf("\ncould not be printed without an account on %s\n", 160178300Sgad local_host); 16021553Srgrimes break; 16031553Srgrimes case FILTERERR: 160494040Sgad cp = "FILTERERR"; 160568664Sgad if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 160668664Sgad || (fp = fopen(tempstderr, "r")) == NULL) { 160715648Sjoerg printf("\nhad some errors and may not have printed\n"); 16081553Srgrimes break; 16091553Srgrimes } 161015648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 16111553Srgrimes while ((i = getc(fp)) != EOF) 16121553Srgrimes putchar(i); 16131553Srgrimes (void) fclose(fp); 16141553Srgrimes break; 16151553Srgrimes case ACCESS: 161694040Sgad cp = "ACCESS"; 16171553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 16181553Srgrimes } 16191553Srgrimes fflush(stdout); 1620118881Sgad (void) close(STDOUT_FILENO); 162131492Swollman } else { 162294038Sgad syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 162331492Swollman return; 16241553Srgrimes } 16251553Srgrimes (void) close(p[0]); 16261553Srgrimes (void) close(p[1]); 162715648Sjoerg wait(NULL); 162815648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 162994038Sgad userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 16301553Srgrimes} 16311553Srgrimes 16321553Srgrimes/* 16331553Srgrimes * dofork - fork with retries on failure 16341553Srgrimes */ 16351553Srgrimesstatic int 163678146Sgaddofork(const struct printer *pp, int action) 16371553Srgrimes{ 163897793Sgad pid_t forkpid; 163997793Sgad int i, fail; 164060871Smpp struct passwd *pwd; 16411553Srgrimes 164280230Sgad forkpid = -1; 164380230Sgad if (daemon_uname == NULL) { 164480230Sgad pwd = getpwuid(pp->daemon_user); 164580230Sgad if (pwd == NULL) { 164680230Sgad syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 164780230Sgad pp->printer, pp->daemon_user); 164880230Sgad goto error_ret; 164980230Sgad } 165080230Sgad daemon_uname = strdup(pwd->pw_name); 165180230Sgad daemon_defgid = pwd->pw_gid; 165280230Sgad } 165380230Sgad 16541553Srgrimes for (i = 0; i < 20; i++) { 165580230Sgad forkpid = fork(); 165680230Sgad if (forkpid < 0) { 16571553Srgrimes sleep((unsigned)(i*i)); 16581553Srgrimes continue; 16591553Srgrimes } 16601553Srgrimes /* 16611553Srgrimes * Child should run as daemon instead of root 16621553Srgrimes */ 166378146Sgad if (forkpid == 0) { 166480230Sgad errno = 0; 166580230Sgad fail = initgroups(daemon_uname, daemon_defgid); 166680230Sgad if (fail) { 166780230Sgad syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 166880230Sgad pp->printer, daemon_uname, daemon_defgid); 166960871Smpp break; 167060871Smpp } 167180230Sgad fail = setgid(daemon_defgid); 167280230Sgad if (fail) { 167380230Sgad syslog(LOG_ERR, "%s: setgid(%u): %m", 167480230Sgad pp->printer, daemon_defgid); 167580230Sgad break; 167680230Sgad } 167780230Sgad fail = setuid(pp->daemon_user); 167880230Sgad if (fail) { 167980230Sgad syslog(LOG_ERR, "%s: setuid(%ld): %m", 168080230Sgad pp->printer, pp->daemon_user); 168180230Sgad break; 168280230Sgad } 168360871Smpp } 168497791Sgad return (forkpid); 16851553Srgrimes } 16861553Srgrimes 168780230Sgad /* 168880230Sgad * An error occurred. If the error is in the child process, then 168980230Sgad * this routine MUST always exit(). DORETURN only effects how 169080230Sgad * errors should be handled in the parent process. 169180230Sgad */ 169280230Sgaderror_ret: 169380230Sgad if (forkpid == 0) { 169480230Sgad syslog(LOG_ERR, "%s: dofork(): aborting child process...", 169580230Sgad pp->printer); 169680230Sgad exit(1); 169780230Sgad } 169880230Sgad syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 169980230Sgad 170080230Sgad sleep(1); /* throttle errors, as a safety measure */ 17011553Srgrimes switch (action) { 17021553Srgrimes case DORETURN: 170397791Sgad return (-1); 17041553Srgrimes default: 17051553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 170680230Sgad /* FALLTHROUGH */ 17071553Srgrimes case DOABORT: 17081553Srgrimes exit(1); 17091553Srgrimes } 17101553Srgrimes /*NOTREACHED*/ 17111553Srgrimes} 17121553Srgrimes 17131553Srgrimes/* 17141553Srgrimes * Kill child processes to abort current job. 17151553Srgrimes */ 17161553Srgrimesstatic void 171778146Sgadabortpr(int signo __unused) 17181553Srgrimes{ 171968664Sgad 172068664Sgad (void) unlink(tempstderr); 17211553Srgrimes kill(0, SIGINT); 172297793Sgad if (of_pid > 0) 172397793Sgad kill(of_pid, SIGCONT); 17241553Srgrimes while (wait(NULL) > 0) 17251553Srgrimes ; 172697793Sgad if (of_pid > 0 && tfd != -1) 172724831Sbrian unlink(tfile); 17281553Srgrimes exit(0); 17291553Srgrimes} 17301553Srgrimes 17311553Srgrimesstatic void 173278146Sgadinit(struct printer *pp) 17331553Srgrimes{ 17341553Srgrimes char *s; 17351553Srgrimes 173631492Swollman sprintf(&width[2], "%ld", pp->page_width); 173731492Swollman sprintf(&length[2], "%ld", pp->page_length); 173831492Swollman sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 173931492Swollman sprintf(&pxlength[2], "%ld", pp->page_plength); 174031492Swollman if ((s = checkremote(pp)) != 0) { 174131492Swollman syslog(LOG_WARNING, "%s", s); 174231492Swollman free(s); 174331492Swollman } 174431492Swollman} 174531492Swollman 174631492Swollmanvoid 174778146Sgadstartprinting(const char *printer) 174831492Swollman{ 174931492Swollman struct printer myprinter, *pp = &myprinter; 175031492Swollman int status; 175131492Swollman 175231492Swollman init_printer(pp); 175331492Swollman status = getprintcap(printer, pp); 175431492Swollman switch(status) { 175531492Swollman case PCAPERR_OSERR: 175631492Swollman syslog(LOG_ERR, "can't open printer description file: %m"); 17571553Srgrimes exit(1); 175831492Swollman case PCAPERR_NOTFOUND: 17591553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 17601553Srgrimes exit(1); 176131492Swollman case PCAPERR_TCLOOP: 176231492Swollman fatal(pp, "potential reference loop detected in printcap file"); 176331492Swollman default: 176431492Swollman break; 176531492Swollman } 176631492Swollman printjob(pp); 17671553Srgrimes} 17681553Srgrimes 17691553Srgrimes/* 17701553Srgrimes * Acquire line printer or remote connection. 17711553Srgrimes */ 17721553Srgrimesstatic void 177378146Sgadopenpr(const struct printer *pp) 17741553Srgrimes{ 177531492Swollman int p[2]; 177615648Sjoerg char *cp; 17771553Srgrimes 177831492Swollman if (pp->remote) { 177931492Swollman openrem(pp); 178094032Sgad /* 178194032Sgad * Lpd does support the setting of 'of=' filters for 178294032Sgad * jobs going to remote machines, but that does not 178394032Sgad * have the same meaning as 'of=' does when handling 178494032Sgad * local print queues. For remote machines, all 'of=' 178594032Sgad * filter processing is handled in sendfile(), and that 178694032Sgad * does not use these global "output filter" variables. 178794032Sgad */ 178894032Sgad ofd = -1; 178997793Sgad of_pid = 0; 179094032Sgad return; 179131492Swollman } else if (*pp->lp) { 1792240351Skevlo if (strchr(pp->lp, '@') != NULL) 179331492Swollman opennet(pp); 179415648Sjoerg else 179531492Swollman opentty(pp); 17961553Srgrimes } else { 17971553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 179897792Sgad pp->printer); 17991553Srgrimes exit(1); 18001553Srgrimes } 180115648Sjoerg 18021553Srgrimes /* 18031553Srgrimes * Start up an output filter, if needed. 18041553Srgrimes */ 180597793Sgad if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { 18061553Srgrimes pipe(p); 180731492Swollman if (pp->remote) { 180831492Swollman strcpy(tfile, TFILENAME); 180924831Sbrian tfd = mkstemp(tfile); 181024831Sbrian } 181197793Sgad if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */ 1812118881Sgad dup2(p[0], STDIN_FILENO); /* pipe is std in */ 181324831Sbrian /* tfile/printer is stdout */ 1814118881Sgad dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); 18158094Sjkh closelog(); 181631492Swollman closeallfds(3); 181731492Swollman if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 181831492Swollman cp = pp->filters[LPF_OUTPUT]; 18191553Srgrimes else 18201553Srgrimes cp++; 182179452Sbrian execl(pp->filters[LPF_OUTPUT], cp, width, length, 182279452Sbrian (char *)0); 182397792Sgad syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, 182497792Sgad pp->filters[LPF_OUTPUT]); 18251553Srgrimes exit(1); 18261553Srgrimes } 18271553Srgrimes (void) close(p[0]); /* close input side */ 18281553Srgrimes ofd = p[1]; /* use pipe for output */ 18291553Srgrimes } else { 18301553Srgrimes ofd = pfd; 183197793Sgad of_pid = 0; 18321553Srgrimes } 18331553Srgrimes} 18341553Srgrimes 183515648Sjoerg/* 183615648Sjoerg * Printer connected directly to the network 183715648Sjoerg * or to a terminal server on the net 183815648Sjoerg */ 183915648Sjoergstatic void 184078146Sgadopennet(const struct printer *pp) 184115648Sjoerg{ 184215648Sjoerg register int i; 184331492Swollman int resp; 184431492Swollman u_long port; 184531492Swollman char *ep; 184630407Sjoerg void (*savealrm)(int); 184715648Sjoerg 184831492Swollman port = strtoul(pp->lp, &ep, 0); 184938470Sbrian if (*ep != '@' || port > 65535) { 185031492Swollman syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 185197792Sgad pp->lp); 185215648Sjoerg exit(1); 185315648Sjoerg } 185431492Swollman ep++; 185515648Sjoerg 185615648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 185715648Sjoerg resp = -1; 185830407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 185931492Swollman alarm(pp->conn_timeout); 186031492Swollman pfd = getport(pp, ep, port); 186131020Sjoerg alarm(0); 186230407Sjoerg (void)signal(SIGALRM, savealrm); 186315648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 186415648Sjoerg resp = 1; 186515648Sjoerg else if (pfd >= 0) { 186615648Sjoerg /* 186715648Sjoerg * need to delay a bit for rs232 lines 186815648Sjoerg * to stabilize in case printer is 186915648Sjoerg * connected via a terminal server 187015648Sjoerg */ 187115648Sjoerg delay(500); 187215648Sjoerg break; 187315648Sjoerg } 187415648Sjoerg if (i == 1) { 187531492Swollman if (resp < 0) 187631492Swollman pstatus(pp, "waiting for %s to come up", 187731492Swollman pp->lp); 187831492Swollman else 187931492Swollman pstatus(pp, 188031492Swollman "waiting for access to printer on %s", 188131492Swollman pp->lp); 188215648Sjoerg } 188315648Sjoerg sleep(i); 188415648Sjoerg } 188579739Sgad pstatus(pp, "sending to %s port %lu", ep, port); 188615648Sjoerg} 188715648Sjoerg 188815648Sjoerg/* 188915648Sjoerg * Printer is connected to an RS232 port on this host 189015648Sjoerg */ 189115648Sjoergstatic void 189278146Sgadopentty(const struct printer *pp) 189315648Sjoerg{ 189415648Sjoerg register int i; 189515648Sjoerg 189615648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 189731492Swollman pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 189815648Sjoerg if (pfd >= 0) { 189915648Sjoerg delay(500); 190015648Sjoerg break; 190115648Sjoerg } 190215648Sjoerg if (errno == ENOENT) { 190331492Swollman syslog(LOG_ERR, "%s: %m", pp->lp); 190415648Sjoerg exit(1); 190515648Sjoerg } 190615648Sjoerg if (i == 1) 190731492Swollman pstatus(pp, 190831492Swollman "waiting for %s to become ready (offline?)", 190931492Swollman pp->printer); 191015648Sjoerg sleep(i); 191115648Sjoerg } 191215648Sjoerg if (isatty(pfd)) 191331492Swollman setty(pp); 191431492Swollman pstatus(pp, "%s is ready and printing", pp->printer); 191515648Sjoerg} 191615648Sjoerg 191715648Sjoerg/* 191815648Sjoerg * Printer is on a remote host 191915648Sjoerg */ 192015648Sjoergstatic void 192178146Sgadopenrem(const struct printer *pp) 192215648Sjoerg{ 192331492Swollman register int i; 192427748Simp int resp; 192530407Sjoerg void (*savealrm)(int); 192615648Sjoerg 192715648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 192815648Sjoerg resp = -1; 192930407Sjoerg savealrm = signal(SIGALRM, alarmhandler); 193031492Swollman alarm(pp->conn_timeout); 193131492Swollman pfd = getport(pp, pp->remote_host, 0); 193231020Sjoerg alarm(0); 193330407Sjoerg (void)signal(SIGALRM, savealrm); 193415648Sjoerg if (pfd >= 0) { 193531492Swollman if ((writel(pfd, "\2", pp->remote_queue, "\n", 193631492Swollman (char *)0) 193731492Swollman == 2 + strlen(pp->remote_queue)) 193831492Swollman && (resp = response(pp)) == 0) 193915648Sjoerg break; 194015648Sjoerg (void) close(pfd); 194115648Sjoerg } 194215648Sjoerg if (i == 1) { 194315648Sjoerg if (resp < 0) 194431492Swollman pstatus(pp, "waiting for %s to come up", 194531492Swollman pp->remote_host); 194615648Sjoerg else { 194731492Swollman pstatus(pp, 194831492Swollman "waiting for queue to be enabled on %s", 194931492Swollman pp->remote_host); 195015648Sjoerg i = 256; 195115648Sjoerg } 195215648Sjoerg } 195315648Sjoerg sleep(i); 195415648Sjoerg } 195531492Swollman pstatus(pp, "sending to %s", pp->remote_host); 195615648Sjoerg} 195715648Sjoerg 19581553Srgrimes/* 19591553Srgrimes * setup tty lines. 19601553Srgrimes */ 19611553Srgrimesstatic void 196278146Sgadsetty(const struct printer *pp) 19631553Srgrimes{ 196415032Ssef struct termios ttybuf; 19651553Srgrimes 19661553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 196731492Swollman syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 19681553Srgrimes exit(1); 19691553Srgrimes } 197015032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 197131492Swollman syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 19721553Srgrimes exit(1); 19731553Srgrimes } 197431492Swollman if (pp->baud_rate > 0) 197531492Swollman cfsetspeed(&ttybuf, pp->baud_rate); 197631492Swollman if (pp->mode_set) { 197731492Swollman char *s = strdup(pp->mode_set), *tmp; 197815032Ssef 197931492Swollman while ((tmp = strsep(&s, ",")) != NULL) { 198039084Swollman (void) msearch(tmp, &ttybuf); 19811553Srgrimes } 19821553Srgrimes } 198331492Swollman if (pp->mode_set != 0 || pp->baud_rate > 0) { 198415032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 198531492Swollman syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 19861553Srgrimes } 19871553Srgrimes } 19881553Srgrimes} 19891553Srgrimes 19901553Srgrimes#include <stdarg.h> 19911553Srgrimes 199215648Sjoergstatic void 199331492Swollmanpstatus(const struct printer *pp, const char *msg, ...) 19941553Srgrimes{ 199531492Swollman int fd; 199631492Swollman char *buf; 19971553Srgrimes va_list ap; 19981553Srgrimes va_start(ap, msg); 19991553Srgrimes 2000139035Sgad umask(S_IWOTH); 200131492Swollman fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 200231492Swollman if (fd < 0) { 200397792Sgad syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, 200497792Sgad pp->status_file); 20051553Srgrimes exit(1); 20061553Srgrimes } 20071553Srgrimes ftruncate(fd, 0); 200831492Swollman vasprintf(&buf, msg, ap); 20091553Srgrimes va_end(ap); 201031492Swollman writel(fd, buf, "\n", (char *)0); 201131492Swollman close(fd); 201231492Swollman free(buf); 20131553Srgrimes} 201430407Sjoerg 201530407Sjoergvoid 201678146Sgadalarmhandler(int signo __unused) 201730407Sjoerg{ 201878146Sgad /* the signal is ignored */ 201978146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 202030407Sjoerg} 2021