printjob.c revision 97791
155714Skris/* 255714Skris * Copyright (c) 1983, 1993 355714Skris * The Regents of the University of California. All rights reserved. 455714Skris * 555714Skris * 655714Skris * Redistribution and use in source and binary forms, with or without 755714Skris * modification, are permitted provided that the following conditions 8296465Sdelphij * are met: 955714Skris * 1. Redistributions of source code must retain the above copyright 1055714Skris * notice, this list of conditions and the following disclaimer. 1155714Skris * 2. Redistributions in binary form must reproduce the above copyright 1255714Skris * notice, this list of conditions and the following disclaimer in the 1355714Skris * documentation and/or other materials provided with the distribution. 1455714Skris * 3. All advertising materials mentioning features or use of this software 15296465Sdelphij * must display the following acknowledgement: 1655714Skris * This product includes software developed by the University of 1755714Skris * California, Berkeley and its contributors. 1855714Skris * 4. Neither the name of the University nor the names of its contributors 1955714Skris * may be used to endorse or promote products derived from this software 2055714Skris * without specific prior written permission. 2155714Skris * 22296465Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2355714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2455714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2555714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2655714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2755714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2855714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2955714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3055714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3155714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3255714Skris * SUCH DAMAGE. 3355714Skris */ 3455714Skris 3555714Skris#ifndef lint 3655714Skrisstatic const char copyright[] = 37296465Sdelphij"@(#) Copyright (c) 1983, 1993\n\ 3855714Skris The Regents of the University of California. All rights reserved.\n"; 3955714Skris#endif /* not lint */ 40296465Sdelphij 4155714Skris#ifndef lint 4255714Skris/* 4355714Skrisstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 4455714Skris*/ 4555714Skrisstatic const char rcsid[] = 4655714Skris "$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 97791 2002-06-04 01:28:15Z gad $"; 4755714Skris#endif /* not lint */ 4855714Skris 4955714Skris 5055714Skris/* 5155714Skris * printjob -- print jobs in the queue. 52296465Sdelphij * 5355714Skris * NOTE: the lock file is used to pass information to lpq and lprm. 5455714Skris * it does not need to be removed because file locks are dynamic. 5555714Skris */ 5655714Skris 5755714Skris#include <sys/param.h> 5855714Skris#include <sys/wait.h> 5955714Skris#include <sys/stat.h> 6055714Skris#include <sys/types.h> 6155714Skris 6255714Skris#include <pwd.h> 6355714Skris#include <unistd.h> 64296465Sdelphij#include <signal.h> 65296465Sdelphij#include <syslog.h> 66296465Sdelphij#include <fcntl.h> 67296465Sdelphij#include <dirent.h> 68296465Sdelphij#include <errno.h> 69296465Sdelphij#include <stdio.h> 70296465Sdelphij#include <string.h> 7155714Skris#include <stdlib.h> 72296465Sdelphij#include <sys/ioctl.h> 73296465Sdelphij#include <termios.h> 74296465Sdelphij#include <time.h> 7555714Skris#include "lp.h" 76296465Sdelphij#include "lp.local.h" 77296465Sdelphij#include "pathnames.h" 78296465Sdelphij#include "extern.h" 79296465Sdelphij 80296465Sdelphij#define DORETURN 0 /* dofork should return "can't fork" error */ 81109998Smarkm#define DOABORT 1 /* dofork should just die if fork() fails */ 82296465Sdelphij 83296465Sdelphij/* 84109998Smarkm * Error tokens 85296465Sdelphij */ 86296465Sdelphij#define REPRINT -2 87296465Sdelphij#define ERROR -1 88296465Sdelphij#define OK 0 89296465Sdelphij#define FATALERR 1 90296465Sdelphij#define NOACCT 2 91109998Smarkm#define FILTERERR 3 92296465Sdelphij#define ACCESS 4 93296465Sdelphij 94109998Smarkmstatic dev_t fdev; /* device of file pointed to by symlink */ 95296465Sdelphijstatic ino_t fino; /* inode of file pointed to by symlink */ 96109998Smarkmstatic FILE *cfp; /* control file */ 97296465Sdelphijstatic int child; /* id of any filters */ 98109998Smarkmstatic int job_dfcnt; /* count of datafiles in current user job */ 99296465Sdelphijstatic int lfd; /* lock file descriptor */ 100296465Sdelphijstatic int ofd; /* output filter file descriptor */ 101296465Sdelphijstatic int ofilter; /* id of output filter, if any */ 102109998Smarkmstatic int tfd = -1; /* output filter temp file output */ 103296465Sdelphijstatic int pfd; /* prstatic inter file descriptor */ 104296465Sdelphijstatic int pid; /* pid of lpd process */ 105109998Smarkmstatic int prchild; /* id of pr process */ 106296465Sdelphijstatic char title[80]; /* ``pr'' title */ 107109998Smarkmstatic char locale[80]; /* ``pr'' locale */ 108109998Smarkm 10959191Skris/* these two are set from pp->daemon_user, but only if they are needed */ 110194206Ssimonstatic char *daemon_uname; /* set from pwd->pw_name */ 11168651Skrisstatic int daemon_defgid; 112296465Sdelphij 113296465Sdelphijstatic char class[32]; /* classification field */ 114296465Sdelphijstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 115296465Sdelphij /* indentation size in static characters */ 116296465Sdelphijstatic char indent[10] = "-i0"; 117296465Sdelphijstatic char jobname[100]; /* job or file name */ 118296465Sdelphijstatic char length[10] = "-l"; /* page length in lines */ 119296465Sdelphijstatic char logname[32]; /* user's login name */ 120194206Ssimonstatic char pxlength[10] = "-y"; /* page length in pixels */ 121296465Sdelphijstatic char pxwidth[10] = "-x"; /* page width in pixels */ 122296465Sdelphij/* tempstderr is the filename used to catch stderr from exec-ing filters */ 123296465Sdelphijstatic char tempstderr[] = "errs.XXXXXXX"; 124296465Sdelphijstatic char width[10] = "-w"; /* page width in static characters */ 125296465Sdelphij#define TFILENAME "fltXXXXXX" 126296465Sdelphijstatic char tfile[] = TFILENAME; /* file name for filter output */ 127194206Ssimon 12859191Skrisstatic void abortpr(int _signo); 129296465Sdelphijstatic void alarmhandler(int _signo); 130296465Sdelphijstatic void banner(struct printer *_pp, char *_name1, char *_name2); 131296465Sdelphijstatic int dofork(const struct printer *_pp, int _action); 132296465Sdelphijstatic int dropit(int _c); 133296465Sdelphijstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 134296465Sdelphij int _infd, int _outfd); 135296465Sdelphijstatic void init(struct printer *_pp); 136296465Sdelphijstatic void openpr(const struct printer *_pp); 137296465Sdelphijstatic void opennet(const struct printer *_pp); 138296465Sdelphijstatic void opentty(const struct printer *_pp); 139296465Sdelphijstatic void openrem(const struct printer *pp); 140194206Ssimonstatic int print(struct printer *_pp, int _format, char *_file); 141296465Sdelphijstatic int printit(struct printer *_pp, char *_file); 142296465Sdelphijstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 143296465Sdelphij __printflike(2, 3); 144296465Sdelphijstatic char response(const struct printer *_pp); 145194206Ssimonstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 14655714Skris int _dlm); 14755714Skrisstatic char *scnline(int _key, char *_p, int _c); 148296465Sdelphijstatic int sendfile(struct printer *_pp, int _type, char *_file, 149296465Sdelphij char _format, int _copyreq); 150296465Sdelphijstatic int sendit(struct printer *_pp, char *_file); 151296465Sdelphijstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 152296465Sdelphijstatic void setty(const struct printer *_pp); 153296465Sdelphij 154296465Sdelphijvoid 155296465Sdelphijprintjob(struct printer *pp) 156296465Sdelphij{ 157296465Sdelphij struct stat stb; 158296465Sdelphij register struct jobqueue *q, **qp; 159296465Sdelphij struct jobqueue **queue; 160296465Sdelphij register int i, nitems; 161296465Sdelphij off_t pidoff; 162296465Sdelphij int errcnt, jobcount, tempfd; 163296465Sdelphij 164296465Sdelphij jobcount = 0; 16555714Skris init(pp); /* set up capabilities */ 166296465Sdelphij (void) write(1, "", 1); /* ack that daemon is started */ 167296465Sdelphij (void) close(2); /* set up log file */ 168296465Sdelphij if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 169296465Sdelphij syslog(LOG_ERR, "%s: %m", pp->log_file); 170296465Sdelphij (void) open(_PATH_DEVNULL, O_WRONLY); 171296465Sdelphij } 172296465Sdelphij setgid(getegid()); 173296465Sdelphij pid = getpid(); /* for use with lprm */ 174296465Sdelphij setpgrp(0, pid); 175296465Sdelphij 176296465Sdelphij /* 177296465Sdelphij * At initial lpd startup, printjob may be called with various 178296465Sdelphij * signal handlers in effect. After that initial startup, any 179296465Sdelphij * calls to printjob will have a *different* set of signal-handlers 180296465Sdelphij * in effect. Make sure all handlers are the ones we want. 181296465Sdelphij */ 182296465Sdelphij signal(SIGCHLD, SIG_DFL); 183296465Sdelphij signal(SIGHUP, abortpr); 18455714Skris signal(SIGINT, abortpr); 185296465Sdelphij signal(SIGQUIT, abortpr); 186296465Sdelphij signal(SIGTERM, abortpr); 187296465Sdelphij 188296465Sdelphij /* 189296465Sdelphij * uses short form file names 190296465Sdelphij */ 191296465Sdelphij if (chdir(pp->spool_dir) < 0) { 192296465Sdelphij syslog(LOG_ERR, "%s: %m", pp->spool_dir); 193296465Sdelphij exit(1); 194296465Sdelphij } 195296465Sdelphij if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS)) 196109998Smarkm exit(0); /* printing disabled */ 197296465Sdelphij lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 198296465Sdelphij LOCK_FILE_MODE); 199296465Sdelphij if (lfd < 0) { 200296465Sdelphij if (errno == EWOULDBLOCK) /* active daemon present */ 201296465Sdelphij exit(0); 202296465Sdelphij syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 203296465Sdelphij exit(1); 204296465Sdelphij } 205296465Sdelphij /* turn off non-blocking mode (was turned on for lock effects only) */ 206296465Sdelphij if (fcntl(lfd, F_SETFL, 0) < 0) { 207296465Sdelphij syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 208296465Sdelphij exit(1); 20955714Skris } 210296465Sdelphij ftruncate(lfd, 0); 211296465Sdelphij /* 212296465Sdelphij * write process id for others to know 213296465Sdelphij */ 214296465Sdelphij sprintf(line, "%u\n", pid); 215296465Sdelphij pidoff = i = strlen(line); 216296465Sdelphij if (write(lfd, line, i) != i) { 217296465Sdelphij syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 218296465Sdelphij exit(1); 219296465Sdelphij } 220296465Sdelphij /* 221296465Sdelphij * search the spool directory for work and sort by queue order. 222296465Sdelphij */ 223296465Sdelphij if ((nitems = getq(pp, &queue)) < 0) { 224296465Sdelphij syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 225296465Sdelphij pp->spool_dir); 226109998Smarkm exit(1); 227296465Sdelphij } 228296465Sdelphij if (nitems == 0) /* no work to do */ 229296465Sdelphij exit(0); 230296465Sdelphij if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 231296465Sdelphij if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 232296465Sdelphij syslog(LOG_ERR, "%s: %s: %m", pp->printer, 233296465Sdelphij pp->lock_file); 234296465Sdelphij } 235296465Sdelphij 23659191Skris /* create a file which will be used to hold stderr from filters */ 237296465Sdelphij if ((tempfd = mkstemp(tempstderr)) == -1) { 238296465Sdelphij syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 239296465Sdelphij tempstderr); 240296465Sdelphij exit(1); 241296465Sdelphij } 242296465Sdelphij if ((i = fchmod(tempfd, 0664)) == -1) { 243296465Sdelphij syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 244296465Sdelphij tempstderr); 245296465Sdelphij exit(1); 246296465Sdelphij } 247296465Sdelphij /* lpd doesn't need it to be open, it just needs it to exist */ 248109998Smarkm close(tempfd); 249296465Sdelphij 250296465Sdelphij openpr(pp); /* open printer or remote */ 251296465Sdelphijagain: 252296465Sdelphij /* 253296465Sdelphij * we found something to do now do it -- 254296465Sdelphij * write the name of the current control file into the lock file 255296465Sdelphij * so the spool queue program can tell what we're working on 256296465Sdelphij */ 257296465Sdelphij for (qp = queue; nitems--; free((char *) q)) { 258296465Sdelphij q = *qp++; 259296465Sdelphij if (stat(q->job_cfname, &stb) < 0) 26055714Skris continue; 261296465Sdelphij errcnt = 0; 262296465Sdelphij restart: 263296465Sdelphij (void) lseek(lfd, pidoff, 0); 264296465Sdelphij (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 265296465Sdelphij i = strlen(line); 266296465Sdelphij if (write(lfd, line, i) != i) 267296465Sdelphij syslog(LOG_ERR, "%s: %s: %m", pp->printer, 268296465Sdelphij pp->lock_file); 269296465Sdelphij if (!pp->remote) 270109998Smarkm i = printit(pp, q->job_cfname); 271296465Sdelphij else 272296465Sdelphij i = sendit(pp, q->job_cfname); 273296465Sdelphij /* 274296465Sdelphij * Check to see if we are supposed to stop printing or 275296465Sdelphij * if we are to rebuild the queue. 276296465Sdelphij */ 277296465Sdelphij if (fstat(lfd, &stb) == 0) { 278296465Sdelphij /* stop printing before starting next job? */ 279296465Sdelphij if (stb.st_mode & LFM_PRINT_DIS) 280296465Sdelphij goto done; 28155714Skris /* rebuild queue (after lpc topq) */ 282296465Sdelphij if (stb.st_mode & LFM_RESET_QUE) { 283296465Sdelphij for (free(q); nitems--; free(q)) 284296465Sdelphij q = *qp++; 285296465Sdelphij if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 286296465Sdelphij < 0) 287296465Sdelphij syslog(LOG_WARNING, "%s: %s: %m", 288296465Sdelphij pp->printer, pp->lock_file); 289296465Sdelphij break; 290296465Sdelphij } 291296465Sdelphij } 292296465Sdelphij if (i == OK) /* all files of this job printed */ 293296465Sdelphij jobcount++; 294296465Sdelphij else if (i == REPRINT && ++errcnt < 5) { 295296465Sdelphij /* try reprinting the job */ 296296465Sdelphij syslog(LOG_INFO, "restarting %s", pp->printer); 297296465Sdelphij if (ofilter > 0) { 298296465Sdelphij kill(ofilter, SIGCONT); /* to be sure */ 299296465Sdelphij (void) close(ofd); 300109998Smarkm while ((i = wait(NULL)) > 0 && i != ofilter) 30159191Skris ; 302296465Sdelphij if (i < 0) 303296465Sdelphij syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 304296465Sdelphij pp->printer, ofilter); 30555714Skris ofilter = 0; 306296465Sdelphij } 307296465Sdelphij (void) close(pfd); /* close printer */ 308111147Snectar if (ftruncate(lfd, pidoff) < 0) 309296465Sdelphij syslog(LOG_WARNING, "%s: %s: %m", 310296465Sdelphij pp->printer, pp->lock_file); 311296465Sdelphij openpr(pp); /* try to reopen printer */ 312296465Sdelphij goto restart; 313296465Sdelphij } else { 314296465Sdelphij syslog(LOG_WARNING, "%s: job could not be %s (%s)", 315109998Smarkm pp->printer, 316296465Sdelphij pp->remote ? "sent to remote host" : "printed", 31759191Skris q->job_cfname); 318296465Sdelphij if (i == REPRINT) { 319296465Sdelphij /* ensure we don't attempt this job again */ 32055714Skris (void) unlink(q->job_cfname); 321296465Sdelphij q->job_cfname[0] = 'd'; 322296465Sdelphij (void) unlink(q->job_cfname); 323296465Sdelphij if (logname[0]) 324296465Sdelphij sendmail(pp, logname, FATALERR); 325296465Sdelphij } 326296465Sdelphij } 327296465Sdelphij } 328109998Smarkm free(queue); 329296465Sdelphij /* 330296465Sdelphij * search the spool directory for more work. 33155714Skris */ 33259191Skris if ((nitems = getq(pp, &queue)) < 0) { 333296465Sdelphij syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 334296465Sdelphij pp->spool_dir); 335296465Sdelphij exit(1); 336109998Smarkm } 337296465Sdelphij if (nitems == 0) { /* no more work to do */ 33855714Skris done: 339296465Sdelphij if (jobcount > 0) { /* jobs actually printed */ 340296465Sdelphij if (!pp->no_formfeed && !pp->tof) 341296465Sdelphij (void) write(ofd, pp->form_feed, 342296465Sdelphij strlen(pp->form_feed)); 34355714Skris if (pp->trailer != NULL) /* output trailer */ 34459191Skris (void) write(ofd, pp->trailer, 345296465Sdelphij strlen(pp->trailer)); 346296465Sdelphij } 347296465Sdelphij (void) close(ofd); 34855714Skris (void) wait(NULL); 349296465Sdelphij (void) unlink(tempstderr); 350296465Sdelphij exit(0); 351111147Snectar } 352296465Sdelphij goto again; 353296465Sdelphij} 354296465Sdelphij 355296465Sdelphijchar fonts[4][50]; /* fonts for troff */ 356296465Sdelphij 357296465Sdelphijchar ifonts[4][40] = { 358109998Smarkm _PATH_VFONTR, 359296465Sdelphij _PATH_VFONTI, 36059191Skris _PATH_VFONTB, 361296465Sdelphij _PATH_VFONTS, 362296465Sdelphij}; 36355714Skris 364296465Sdelphij/* 365296465Sdelphij * The remaining part is the reading of the control file (cf) 366296465Sdelphij * and performing the various actions. 367296465Sdelphij */ 368296465Sdelphijstatic int 369296465Sdelphijprintit(struct printer *pp, char *file) 370296465Sdelphij{ 371109998Smarkm register int i; 372296465Sdelphij char *cp; 373296465Sdelphij int bombed, didignorehdr; 37455714Skris 37559191Skris bombed = OK; 376296465Sdelphij didignorehdr = 0; 377296465Sdelphij /* 37855714Skris * open control file; ignore if no longer there. 379296465Sdelphij */ 380296465Sdelphij if ((cfp = fopen(file, "r")) == NULL) { 381111147Snectar syslog(LOG_INFO, "%s: %s: %m", pp->printer, file); 382296465Sdelphij return (OK); 383296465Sdelphij } 384160814Ssimon /* 385296465Sdelphij * Reset troff fonts. 386296465Sdelphij */ 387296465Sdelphij for (i = 0; i < 4; i++) 388109998Smarkm strcpy(fonts[i], ifonts[i]); 389296465Sdelphij sprintf(&width[2], "%ld", pp->page_width); 390296465Sdelphij strcpy(indent+2, "0"); 391109998Smarkm 392296465Sdelphij /* initialize job-specific count of datafiles processed */ 393296465Sdelphij job_dfcnt = 0; 394101613Snectar 395296465Sdelphij /* 396296465Sdelphij * read the control file for work to do 397109998Smarkm * 398109998Smarkm * file format -- first character in the line is a command 399296465Sdelphij * rest of the line is the argument. 400296465Sdelphij * valid commands are: 401296465Sdelphij * 402109998Smarkm * S -- "stat info" for symbolic link protection 403296465Sdelphij * J -- "job name" on banner page 404296465Sdelphij * C -- "class name" on banner page 405160814Ssimon * L -- "literal" user's name to print on banner 406296465Sdelphij * T -- "title" for pr 407296465Sdelphij * H -- "host name" of machine where lpr was done 408160814Ssimon * P -- "person" user's login name 409296465Sdelphij * I -- "indent" amount to indent output 410296465Sdelphij * R -- laser dpi "resolution" 411296465Sdelphij * f -- "file name" name of text file to print 412296465Sdelphij * l -- "file name" text file with control chars 413296465Sdelphij * o -- "file name" postscript file, according to 414296465Sdelphij * the RFC. Here it is treated like an 'f'. 415234954Sbz * p -- "file name" text file to print with pr(1) 416296465Sdelphij * t -- "file name" troff(1) file to print 417296465Sdelphij * n -- "file name" ditroff(1) file to print 418296465Sdelphij * d -- "file name" dvi file to print 419296465Sdelphij * g -- "file name" plot(1G) file to print 420296465Sdelphij * v -- "file name" plain raster file to print 421296465Sdelphij * c -- "file name" cifplot file to print 422296465Sdelphij * 1 -- "R font file" for troff 423296465Sdelphij * 2 -- "I font file" for troff 424109998Smarkm * 3 -- "B font file" for troff 425296465Sdelphij * 4 -- "S font file" for troff 426296465Sdelphij * N -- "name" of file (used by lpq) 427296465Sdelphij * U -- "unlink" name of file to remove 42855714Skris * (after we print it. (Pass 2 only)). 429296465Sdelphij * M -- "mail" to user when done printing 430296465Sdelphij * Z -- "locale" for pr 43155714Skris * 432296465Sdelphij * getline reads a line and expands tabs to blanks 433296465Sdelphij */ 43455714Skris 43559191Skris /* pass 1 */ 436296465Sdelphij 437296465Sdelphij while (getline(cfp)) 438296465Sdelphij switch (line[0]) { 439109998Smarkm case 'H': 440296465Sdelphij strlcpy(origin_host, line + 1, sizeof(origin_host)); 44155714Skris if (class[0] == '\0') { 442296465Sdelphij strlcpy(class, line+1, sizeof(class)); 443296465Sdelphij } 444296465Sdelphij continue; 445296465Sdelphij 44655714Skris case 'P': 44759191Skris strlcpy(logname, line + 1, sizeof(logname)); 448296465Sdelphij if (pp->restricted) { /* restricted */ 449296465Sdelphij if (getpwnam(logname) == NULL) { 450296465Sdelphij bombed = NOACCT; 451296465Sdelphij sendmail(pp, line+1, bombed); 452296465Sdelphij goto pass2; 453296465Sdelphij } 45455714Skris } 45559191Skris continue; 456296465Sdelphij 457296465Sdelphij case 'S': 458296465Sdelphij cp = line+1; 459296465Sdelphij i = 0; 46055714Skris while (*cp >= '0' && *cp <= '9') 46159191Skris i = i * 10 + (*cp++ - '0'); 462296465Sdelphij fdev = i; 463296465Sdelphij cp++; 464296465Sdelphij i = 0; 465296465Sdelphij while (*cp >= '0' && *cp <= '9') 466296465Sdelphij i = i * 10 + (*cp++ - '0'); 467194206Ssimon fino = i; 468194206Ssimon continue; 469296465Sdelphij 470296465Sdelphij case 'J': 471296465Sdelphij if (line[1] != '\0') { 472296465Sdelphij strlcpy(jobname, line + 1, sizeof(jobname)); 473296465Sdelphij } else 474194206Ssimon strcpy(jobname, " "); 475194206Ssimon continue; 476296465Sdelphij 477296465Sdelphij case 'C': 478296465Sdelphij if (line[1] != '\0') 479296465Sdelphij strlcpy(class, line + 1, sizeof(class)); 480296465Sdelphij else if (class[0] == '\0') { 481194206Ssimon /* XXX - why call gethostname instead of 482194206Ssimon * just strlcpy'ing local_host? */ 483296465Sdelphij gethostname(class, sizeof(class)); 484296465Sdelphij class[sizeof(class) - 1] = '\0'; 485296465Sdelphij } 486296465Sdelphij continue; 487296465Sdelphij 488 case 'T': /* header title for pr */ 489 strlcpy(title, line + 1, sizeof(title)); 490 continue; 491 492 case 'L': /* identification line */ 493 if (!pp->no_header && !pp->header_last) 494 banner(pp, line+1, jobname); 495 continue; 496 497 case '1': /* troff fonts */ 498 case '2': 499 case '3': 500 case '4': 501 if (line[1] != '\0') { 502 strlcpy(fonts[line[0]-'1'], line + 1, 503 (size_t)50); 504 } 505 continue; 506 507 case 'W': /* page width */ 508 strlcpy(width+2, line + 1, sizeof(width) - 2); 509 continue; 510 511 case 'I': /* indent amount */ 512 strlcpy(indent+2, line + 1, sizeof(indent) - 2); 513 continue; 514 515 case 'Z': /* locale for pr */ 516 strlcpy(locale, line + 1, sizeof(locale)); 517 continue; 518 519 default: /* some file to print */ 520 /* only lowercase cmd-codes include a file-to-print */ 521 if ((line[0] < 'a') || (line[0] > 'z')) { 522 /* ignore any other lines */ 523 if (lflag <= 1) 524 continue; 525 if (!didignorehdr) { 526 syslog(LOG_INFO, "%s: in %s :", 527 pp->printer, file); 528 didignorehdr = 1; 529 } 530 syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 531 pp->printer, line[0], &line[1]); 532 continue; 533 } 534 i = print(pp, line[0], line+1); 535 switch (i) { 536 case ERROR: 537 if (bombed == OK) 538 bombed = FATALERR; 539 break; 540 case REPRINT: 541 (void) fclose(cfp); 542 return (REPRINT); 543 case FILTERERR: 544 case ACCESS: 545 bombed = i; 546 sendmail(pp, logname, bombed); 547 } 548 title[0] = '\0'; 549 continue; 550 551 case 'N': 552 case 'U': 553 case 'M': 554 case 'R': 555 continue; 556 } 557 558 /* pass 2 */ 559 560pass2: 561 fseek(cfp, 0L, 0); 562 while (getline(cfp)) 563 switch (line[0]) { 564 case 'L': /* identification line */ 565 if (!pp->no_header && pp->header_last) 566 banner(pp, line+1, jobname); 567 continue; 568 569 case 'M': 570 if (bombed < NOACCT) /* already sent if >= NOACCT */ 571 sendmail(pp, line+1, bombed); 572 continue; 573 574 case 'U': 575 if (strchr(line+1, '/')) 576 continue; 577 (void) unlink(line+1); 578 } 579 /* 580 * clean-up in case another control file exists 581 */ 582 (void) fclose(cfp); 583 (void) unlink(file); 584 return (bombed == OK ? OK : ERROR); 585} 586 587/* 588 * Print a file. 589 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 590 * Return -1 if a non-recoverable error occured, 591 * 2 if the filter detected some errors (but printed the job anyway), 592 * 1 if we should try to reprint this job and 593 * 0 if all is well. 594 * Note: all filters take stdin as the file, stdout as the printer, 595 * stderr as the log file, and must not ignore SIGINT. 596 */ 597static int 598print(struct printer *pp, int format, char *file) 599{ 600 register int n, i; 601 register char *prog; 602 int fi, fo; 603 FILE *fp; 604 char *av[15], buf[BUFSIZ]; 605 int pid, p[2], retcode, stopped, wstatus, wstatus_set; 606 struct stat stb; 607 608 if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 609 syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 610 pp->printer, file, format); 611 return (ERROR); 612 } 613 /* 614 * Check to see if data file is a symbolic link. If so, it should 615 * still point to the same file or someone is trying to print 616 * something he shouldn't. 617 */ 618 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 619 (stb.st_dev != fdev || stb.st_ino != fino)) 620 return (ACCESS); 621 622 job_dfcnt++; /* increment datafile counter for this job */ 623 stopped = 0; /* output filter is not stopped */ 624 625 /* everything seems OK, start it up */ 626 if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 627 (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 628 pp->tof = 1; 629 } 630 if (pp->filters[LPF_INPUT] == NULL 631 && (format == 'f' || format == 'l' || format == 'o')) { 632 pp->tof = 0; 633 while ((n = read(fi, buf, BUFSIZ)) > 0) 634 if (write(ofd, buf, n) != n) { 635 (void) close(fi); 636 return (REPRINT); 637 } 638 (void) close(fi); 639 return (OK); 640 } 641 switch (format) { 642 case 'p': /* print file using 'pr' */ 643 if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 644 prog = _PATH_PR; 645 i = 0; 646 av[i++] = "pr"; 647 av[i++] = width; 648 av[i++] = length; 649 av[i++] = "-h"; 650 av[i++] = *title ? title : " "; 651 av[i++] = "-L"; 652 av[i++] = *locale ? locale : "C"; 653 av[i++] = "-F"; 654 av[i] = 0; 655 fo = ofd; 656 goto start; 657 } 658 pipe(p); 659 if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 660 dup2(fi, 0); /* file is stdin */ 661 dup2(p[1], 1); /* pipe is stdout */ 662 closelog(); 663 closeallfds(3); 664 execl(_PATH_PR, "pr", width, length, 665 "-h", *title ? title : " ", 666 "-L", *locale ? locale : "C", 667 "-F", (char *)0); 668 syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 669 exit(2); 670 } 671 (void) close(p[1]); /* close output side */ 672 (void) close(fi); 673 if (prchild < 0) { 674 prchild = 0; 675 (void) close(p[0]); 676 return (ERROR); 677 } 678 fi = p[0]; /* use pipe for input */ 679 case 'f': /* print plain text file */ 680 prog = pp->filters[LPF_INPUT]; 681 av[1] = width; 682 av[2] = length; 683 av[3] = indent; 684 n = 4; 685 break; 686 case 'o': /* print postscript file */ 687 /* 688 * Treat this as a "plain file with control characters", and 689 * assume the standard LPF_INPUT filter will recognize that 690 * the data is postscript and know what to do with it. These 691 * 'o'-file requests could come from MacOS 10.1 systems. 692 * (later versions of MacOS 10 will explicitly use 'l') 693 * A postscript file can contain binary data, which is why 'l' 694 * is somewhat more appropriate than 'f'. 695 */ 696 /* FALLTHROUGH */ 697 case 'l': /* like 'f' but pass control characters */ 698 prog = pp->filters[LPF_INPUT]; 699 av[1] = "-c"; 700 av[2] = width; 701 av[3] = length; 702 av[4] = indent; 703 n = 5; 704 break; 705 case 'r': /* print a fortran text file */ 706 prog = pp->filters[LPF_FORTRAN]; 707 av[1] = width; 708 av[2] = length; 709 n = 3; 710 break; 711 case 't': /* print troff output */ 712 case 'n': /* print ditroff output */ 713 case 'd': /* print tex output */ 714 (void) unlink(".railmag"); 715 if ((fo = creat(".railmag", FILMOD)) < 0) { 716 syslog(LOG_ERR, "%s: cannot create .railmag", 717 pp->printer); 718 (void) unlink(".railmag"); 719 } else { 720 for (n = 0; n < 4; n++) { 721 if (fonts[n][0] != '/') 722 (void) write(fo, _PATH_VFONT, 723 sizeof(_PATH_VFONT) - 1); 724 (void) write(fo, fonts[n], strlen(fonts[n])); 725 (void) write(fo, "\n", 1); 726 } 727 (void) close(fo); 728 } 729 prog = (format == 't') ? pp->filters[LPF_TROFF] 730 : ((format == 'n') ? pp->filters[LPF_DITROFF] 731 : pp->filters[LPF_DVI]); 732 av[1] = pxwidth; 733 av[2] = pxlength; 734 n = 3; 735 break; 736 case 'c': /* print cifplot output */ 737 prog = pp->filters[LPF_CIFPLOT]; 738 av[1] = pxwidth; 739 av[2] = pxlength; 740 n = 3; 741 break; 742 case 'g': /* print plot(1G) output */ 743 prog = pp->filters[LPF_GRAPH]; 744 av[1] = pxwidth; 745 av[2] = pxlength; 746 n = 3; 747 break; 748 case 'v': /* print raster output */ 749 prog = pp->filters[LPF_RASTER]; 750 av[1] = pxwidth; 751 av[2] = pxlength; 752 n = 3; 753 break; 754 default: 755 (void) close(fi); 756 syslog(LOG_ERR, "%s: illegal format character '%c'", 757 pp->printer, format); 758 return (ERROR); 759 } 760 if (prog == NULL) { 761 (void) close(fi); 762 syslog(LOG_ERR, 763 "%s: no filter found in printcap for format character '%c'", 764 pp->printer, format); 765 return (ERROR); 766 } 767 if ((av[0] = strrchr(prog, '/')) != NULL) 768 av[0]++; 769 else 770 av[0] = prog; 771 av[n++] = "-n"; 772 av[n++] = logname; 773 av[n++] = "-h"; 774 av[n++] = origin_host; 775 av[n++] = pp->acct_file; 776 av[n] = 0; 777 fo = pfd; 778 if (ofilter > 0) { /* stop output filter */ 779 write(ofd, "\031\1", 2); 780 while ((pid = 781 wait3(&wstatus, WUNTRACED, 0)) > 0 && pid != ofilter) 782 ; 783 if (pid < 0) 784 syslog(LOG_WARNING, "%s: after stopping 'of', wait3() returned: %m", 785 pp->printer); 786 else if (!WIFSTOPPED(wstatus)) { 787 (void) close(fi); 788 syslog(LOG_WARNING, "%s: output filter died " 789 "(pid=%d retcode=%d termsig=%d)", 790 pp->printer, ofilter, WEXITSTATUS(wstatus), 791 WTERMSIG(wstatus)); 792 return (REPRINT); 793 } 794 stopped++; 795 } 796start: 797 if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 798 dup2(fi, 0); 799 dup2(fo, 1); 800 /* setup stderr for the filter (child process) 801 * so it goes to our temporary errors file */ 802 n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 803 if (n >= 0) 804 dup2(n, 2); 805 closelog(); 806 closeallfds(3); 807 execv(prog, av); 808 syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 809 prog); 810 exit(2); 811 } 812 (void) close(fi); 813 wstatus_set = 0; 814 if (child < 0) 815 retcode = 100; 816 else { 817 while ((pid = wait(&wstatus)) > 0 && pid != child) 818 ; 819 if (pid < 0) { 820 retcode = 100; 821 syslog(LOG_WARNING, "%s: after execv(%s), wait() returned: %m", 822 pp->printer, prog); 823 } else { 824 wstatus_set = 1; 825 retcode = WEXITSTATUS(wstatus); 826 } 827 } 828 child = 0; 829 prchild = 0; 830 if (stopped) { /* restart output filter */ 831 if (kill(ofilter, SIGCONT) < 0) { 832 syslog(LOG_ERR, "cannot restart output filter"); 833 exit(1); 834 } 835 } 836 pp->tof = 0; 837 838 /* Copy the filter's output to "lf" logfile */ 839 if ((fp = fopen(tempstderr, "r"))) { 840 while (fgets(buf, sizeof(buf), fp)) 841 fputs(buf, stderr); 842 fclose(fp); 843 } 844 845 if (wstatus_set && !WIFEXITED(wstatus)) { 846 syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 847 pp->printer, format, WTERMSIG(wstatus)); 848 return (ERROR); 849 } 850 switch (retcode) { 851 case 0: 852 pp->tof = 1; 853 return (OK); 854 case 1: 855 return (REPRINT); 856 case 2: 857 return (ERROR); 858 default: 859 syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 860 pp->printer, format, retcode); 861 return (FILTERERR); 862 } 863} 864 865/* 866 * Send the daemon control file (cf) and any data files. 867 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 868 * 0 if all is well. 869 */ 870static int 871sendit(struct printer *pp, char *file) 872{ 873 int dfcopies, err, i; 874 char *cp, last[BUFSIZ]; 875 876 /* 877 * open control file 878 */ 879 if ((cfp = fopen(file, "r")) == NULL) 880 return (OK); 881 882 /* initialize job-specific count of datafiles processed */ 883 job_dfcnt = 0; 884 885 /* 886 * read the control file for work to do 887 * 888 * file format -- first character in the line is a command 889 * rest of the line is the argument. 890 * commands of interest are: 891 * 892 * a-z -- "file name" name of file to print 893 * U -- "unlink" name of file to remove 894 * (after we print it. (Pass 2 only)). 895 */ 896 897 /* 898 * pass 1 899 */ 900 err = OK; 901 while (getline(cfp)) { 902 again: 903 if (line[0] == 'S') { 904 cp = line+1; 905 i = 0; 906 while (*cp >= '0' && *cp <= '9') 907 i = i * 10 + (*cp++ - '0'); 908 fdev = i; 909 cp++; 910 i = 0; 911 while (*cp >= '0' && *cp <= '9') 912 i = i * 10 + (*cp++ - '0'); 913 fino = i; 914 } else if (line[0] == 'H') { 915 strlcpy(origin_host, line + 1, sizeof(origin_host)); 916 if (class[0] == '\0') { 917 strlcpy(class, line + 1, sizeof(class)); 918 } 919 } else if (line[0] == 'P') { 920 strlcpy(logname, line + 1, sizeof(logname)); 921 if (pp->restricted) { /* restricted */ 922 if (getpwnam(logname) == NULL) { 923 sendmail(pp, line+1, NOACCT); 924 err = ERROR; 925 break; 926 } 927 } 928 } else if (line[0] == 'I') { 929 strlcpy(indent+2, line + 1, sizeof(indent) - 2); 930 } else if (line[0] >= 'a' && line[0] <= 'z') { 931 dfcopies = 1; 932 strcpy(last, line); 933 while ((i = getline(cfp)) != 0) { 934 if (strcmp(last, line) != 0) 935 break; 936 dfcopies++; 937 } 938 switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { 939 case OK: 940 if (i) 941 goto again; 942 break; 943 case REPRINT: 944 (void) fclose(cfp); 945 return (REPRINT); 946 case ACCESS: 947 sendmail(pp, logname, ACCESS); 948 case ERROR: 949 err = ERROR; 950 } 951 break; 952 } 953 } 954 if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { 955 (void) fclose(cfp); 956 return (REPRINT); 957 } 958 /* 959 * pass 2 960 */ 961 fseek(cfp, 0L, 0); 962 while (getline(cfp)) 963 if (line[0] == 'U' && !strchr(line+1, '/')) 964 (void) unlink(line+1); 965 /* 966 * clean-up in case another control file exists 967 */ 968 (void) fclose(cfp); 969 (void) unlink(file); 970 return (err); 971} 972 973/* 974 * Send a data file to the remote machine and spool it. 975 * Return positive if we should try resending. 976 */ 977static int 978sendfile(struct printer *pp, int type, char *file, char format, int copyreq) 979{ 980 int i, amt; 981 struct stat stb; 982 char *av[15], *filtcmd; 983 char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 984 int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; 985 986 statrc = lstat(file, &stb); 987 if (statrc < 0) { 988 syslog(LOG_ERR, "%s: error from lstat(%s): %m", 989 pp->printer, file); 990 return (ERROR); 991 } 992 sfd = open(file, O_RDONLY); 993 if (sfd < 0) { 994 syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 995 pp->printer, file); 996 return (ERROR); 997 } 998 /* 999 * Check to see if data file is a symbolic link. If so, it should 1000 * still point to the same file or someone is trying to print something 1001 * he shouldn't. 1002 */ 1003 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 1004 (stb.st_dev != fdev || stb.st_ino != fino)) { 1005 close(sfd); 1006 return (ACCESS); 1007 } 1008 1009 /* Everything seems OK for reading the file, now to send it */ 1010 filtcmd = NULL; 1011 sizerr = 0; 1012 tfd = -1; 1013 if (type == '\3') { 1014 /* 1015 * Type == 3 means this is a datafile, not a control file. 1016 * Increment the counter of data-files in this job, and 1017 * then check for input or output filters (which are only 1018 * applied to datafiles, not control files). 1019 */ 1020 job_dfcnt++; 1021 1022 /* 1023 * Note that here we are filtering datafiles, one at a time, 1024 * as they are sent to the remote machine. Here, the *only* 1025 * difference between an input filter (`if=') and an output 1026 * filter (`of=') is the argument list that the filter is 1027 * started up with. Here, the output filter is executed 1028 * for each individual file as it is sent. This is not the 1029 * same as local print queues, where the output filter is 1030 * started up once, and then all jobs are passed thru that 1031 * single invocation of the output filter. 1032 * 1033 * Also note that a queue for a remote-machine can have an 1034 * input filter or an output filter, but not both. 1035 */ 1036 if (pp->filters[LPF_INPUT]) { 1037 filtcmd = pp->filters[LPF_INPUT]; 1038 av[0] = filtcmd; 1039 narg = 0; 1040 strcpy(opt_c, "-c"); 1041 strcpy(opt_h, "-h"); 1042 strcpy(opt_n, "-n"); 1043 if (format == 'l') 1044 av[++narg] = opt_c; 1045 av[++narg] = width; 1046 av[++narg] = length; 1047 av[++narg] = indent; 1048 av[++narg] = opt_n; 1049 av[++narg] = logname; 1050 av[++narg] = opt_h; 1051 av[++narg] = origin_host; 1052 av[++narg] = pp->acct_file; 1053 av[++narg] = NULL; 1054 } else if (pp->filters[LPF_OUTPUT]) { 1055 filtcmd = pp->filters[LPF_OUTPUT]; 1056 av[0] = filtcmd; 1057 narg = 0; 1058 av[++narg] = width; 1059 av[++narg] = length; 1060 av[++narg] = NULL; 1061 } 1062 } 1063 if (filtcmd) { 1064 /* 1065 * If there is an input or output filter, we have to run 1066 * the datafile thru that filter and store the result as 1067 * a temporary spool file, because the protocol requires 1068 * that we send the remote host the file-size before we 1069 * start to send any of the data. 1070 */ 1071 strcpy(tfile, TFILENAME); 1072 tfd = mkstemp(tfile); 1073 if (tfd == -1) { 1074 syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 1075 TFILENAME); 1076 sfres = ERROR; 1077 goto return_sfres; 1078 } 1079 filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 1080 1081 /* process the return-code from the filter */ 1082 switch (filtstat) { 1083 case 0: 1084 break; 1085 case 1: 1086 sfres = REPRINT; 1087 goto return_sfres; 1088 case 2: 1089 sfres = ERROR; 1090 goto return_sfres; 1091 default: 1092 syslog(LOG_WARNING, 1093 "%s: filter '%c' exited (retcode=%d)", 1094 pp->printer, format, filtstat); 1095 sfres = FILTERERR; 1096 goto return_sfres; 1097 } 1098 statrc = fstat(tfd, &stb); /* to find size of tfile */ 1099 if (statrc < 0) { 1100 syslog(LOG_ERR, 1101 "%s: error processing 'if', fstat(%s): %m", 1102 pp->printer, tfile); 1103 sfres = ERROR; 1104 goto return_sfres; 1105 } 1106 close(sfd); 1107 sfd = tfd; 1108 lseek(sfd, 0, SEEK_SET); 1109 } 1110 1111 copycnt = 0; 1112sendagain: 1113 copycnt++; 1114 1115 if (copycnt < 2) 1116 (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 1117 else 1118 (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size, 1119 file, copycnt); 1120 amt = strlen(buf); 1121 for (i = 0; ; i++) { 1122 if (write(pfd, buf, amt) != amt || 1123 (resp = response(pp)) < 0 || resp == '\1') { 1124 sfres = REPRINT; 1125 goto return_sfres; 1126 } else if (resp == '\0') 1127 break; 1128 if (i == 0) 1129 pstatus(pp, 1130 "no space on remote; waiting for queue to drain"); 1131 if (i == 10) 1132 syslog(LOG_ALERT, "%s: can't send to %s; queue full", 1133 pp->printer, pp->remote_host); 1134 sleep(5 * 60); 1135 } 1136 if (i) 1137 pstatus(pp, "sending to %s", pp->remote_host); 1138 /* 1139 * XXX - we should change trstat_init()/trstat_write() to include 1140 * the copycnt in the statistics record it may write. 1141 */ 1142 if (type == '\3') 1143 trstat_init(pp, file, job_dfcnt); 1144 for (i = 0; i < stb.st_size; i += BUFSIZ) { 1145 amt = BUFSIZ; 1146 if (i + amt > stb.st_size) 1147 amt = stb.st_size - i; 1148 if (sizerr == 0 && read(sfd, buf, amt) != amt) 1149 sizerr = 1; 1150 if (write(pfd, buf, amt) != amt) { 1151 sfres = REPRINT; 1152 goto return_sfres; 1153 } 1154 } 1155 1156 if (sizerr) { 1157 syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 1158 /* tell recvjob to ignore this file */ 1159 (void) write(pfd, "\1", 1); 1160 sfres = ERROR; 1161 goto return_sfres; 1162 } 1163 if (write(pfd, "", 1) != 1 || response(pp)) { 1164 sfres = REPRINT; 1165 goto return_sfres; 1166 } 1167 if (type == '\3') { 1168 trstat_write(pp, TR_SENDING, stb.st_size, logname, 1169 pp->remote_host, origin_host); 1170 /* 1171 * Usually we only need to send one copy of a datafile, 1172 * because the control-file will simply print the same 1173 * file multiple times. However, some printers ignore 1174 * the control file, and simply print each data file as 1175 * it arrives. For such "remote hosts", we need to 1176 * transfer the same data file multiple times. Such a 1177 * a host is indicated by adding 'rc' to the printcap 1178 * entry. 1179 * XXX - Right now this ONLY works for remote hosts which 1180 * do ignore the name of the data file, because 1181 * this sends the file multiple times with slight 1182 * changes to the filename. To do this right would 1183 * require that we also rewrite the control file 1184 * to match those filenames. 1185 */ 1186 if (pp->resend_copies && (copycnt < copyreq)) { 1187 lseek(sfd, 0, SEEK_SET); 1188 goto sendagain; 1189 } 1190 } 1191 sfres = OK; 1192 1193return_sfres: 1194 (void)close(sfd); 1195 if (tfd != -1) { 1196 /* 1197 * If tfd is set, then it is the same value as sfd, and 1198 * therefore it is already closed at this point. All 1199 * we need to do is remove the temporary file. 1200 */ 1201 tfd = -1; 1202 unlink(tfile); 1203 } 1204 return (sfres); 1205} 1206 1207/* 1208 * This routine is called to execute one of the filters as was 1209 * specified in a printcap entry. While the child-process will read 1210 * all of 'infd', it is up to the caller to close that file descriptor 1211 * in the parent process. 1212 */ 1213static int 1214execfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 1215{ 1216 int errfd, fpid, retcode, wpid, wstatus; 1217 FILE *errfp; 1218 char buf[BUFSIZ], *slash; 1219 1220 fpid = dofork(pp, DORETURN); 1221 if (fpid != 0) { 1222 /* 1223 * This is the parent process, which just waits for the child 1224 * to complete and then returns the result. Note that it is 1225 * the child process which reads the input stream. 1226 */ 1227 if (fpid < 0) 1228 retcode = 100; 1229 else { 1230 while ((wpid = wait(&wstatus)) > 0 && 1231 wpid != fpid) 1232 ; 1233 if (wpid < 0) { 1234 retcode = 100; 1235 syslog(LOG_WARNING, 1236 "%s: after execv(%s), wait() returned: %m", 1237 pp->printer, f_cmd); 1238 } else 1239 retcode = WEXITSTATUS(wstatus); 1240 } 1241 1242 /* 1243 * Copy everything the filter wrote to stderr from our 1244 * temporary errors file to the "lf=" logfile. 1245 */ 1246 errfp = fopen(tempstderr, "r"); 1247 if (errfp) { 1248 while (fgets(buf, sizeof(buf), errfp)) 1249 fputs(buf, stderr); 1250 fclose(errfp); 1251 } 1252 1253 return (retcode); 1254 } 1255 1256 /* 1257 * This is the child process, which is the one that executes the 1258 * given filter. 1259 */ 1260 /* 1261 * If the first parameter has any slashes in it, then change it 1262 * to point to the first character after the last slash. 1263 */ 1264 slash = strrchr(f_av[0], '/'); 1265 if (slash != NULL) 1266 f_av[0] = slash + 1; 1267 /* 1268 * XXX - in the future, this should setup an explicit list of 1269 * environment variables and use execve()! 1270 */ 1271 1272 /* 1273 * Setup stdin, stdout, and stderr as we want them when the filter 1274 * is running. Stderr is setup so it points to a temporary errors 1275 * file, and the parent process will copy that temporary file to 1276 * the real logfile after the filter completes. 1277 */ 1278 dup2(infd, 0); 1279 dup2(outfd, 1); 1280 errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 1281 if (errfd >= 0) 1282 dup2(errfd, 2); 1283 closelog(); 1284 closeallfds(3); 1285 execv(f_cmd, f_av); 1286 syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 1287 exit(2); 1288 /* NOTREACHED */ 1289} 1290 1291/* 1292 * Check to make sure there have been no errors and that both programs 1293 * are in sync with eachother. 1294 * Return non-zero if the connection was lost. 1295 */ 1296static char 1297response(const struct printer *pp) 1298{ 1299 char resp; 1300 1301 if (read(pfd, &resp, 1) != 1) { 1302 syslog(LOG_INFO, "%s: lost connection", pp->printer); 1303 return (-1); 1304 } 1305 return (resp); 1306} 1307 1308/* 1309 * Banner printing stuff 1310 */ 1311static void 1312banner(struct printer *pp, char *name1, char *name2) 1313{ 1314 time_t tvec; 1315 1316 time(&tvec); 1317 if (!pp->no_formfeed && !pp->tof) 1318 (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 1319 if (pp->short_banner) { /* short banner only */ 1320 if (class[0]) { 1321 (void) write(ofd, class, strlen(class)); 1322 (void) write(ofd, ":", 1); 1323 } 1324 (void) write(ofd, name1, strlen(name1)); 1325 (void) write(ofd, " Job: ", 7); 1326 (void) write(ofd, name2, strlen(name2)); 1327 (void) write(ofd, " Date: ", 8); 1328 (void) write(ofd, ctime(&tvec), 24); 1329 (void) write(ofd, "\n", 1); 1330 } else { /* normal banner */ 1331 (void) write(ofd, "\n\n\n", 3); 1332 scan_out(pp, ofd, name1, '\0'); 1333 (void) write(ofd, "\n\n", 2); 1334 scan_out(pp, ofd, name2, '\0'); 1335 if (class[0]) { 1336 (void) write(ofd,"\n\n\n",3); 1337 scan_out(pp, ofd, class, '\0'); 1338 } 1339 (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 1340 (void) write(ofd, name2, strlen(name2)); 1341 (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 1342 (void) write(ofd, ctime(&tvec), 24); 1343 (void) write(ofd, "\n", 1); 1344 } 1345 if (!pp->no_formfeed) 1346 (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 1347 pp->tof = 1; 1348} 1349 1350static char * 1351scnline(int key, char *p, int c) 1352{ 1353 register int scnwidth; 1354 1355 for (scnwidth = WIDTH; --scnwidth;) { 1356 key <<= 1; 1357 *p++ = key & 0200 ? c : BACKGND; 1358 } 1359 return (p); 1360} 1361 1362#define TRC(q) (((q)-' ')&0177) 1363 1364static void 1365scan_out(struct printer *pp, int scfd, char *scsp, int dlm) 1366{ 1367 register char *strp; 1368 register int nchrs, j; 1369 char outbuf[LINELEN+1], *sp, c, cc; 1370 int d, scnhgt; 1371 1372 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 1373 strp = &outbuf[0]; 1374 sp = scsp; 1375 for (nchrs = 0; ; ) { 1376 d = dropit(c = TRC(cc = *sp++)); 1377 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 1378 for (j = WIDTH; --j;) 1379 *strp++ = BACKGND; 1380 else 1381 strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 1382 if (*sp == dlm || *sp == '\0' || 1383 nchrs++ >= pp->page_width/(WIDTH+1)-1) 1384 break; 1385 *strp++ = BACKGND; 1386 *strp++ = BACKGND; 1387 } 1388 while (*--strp == BACKGND && strp >= outbuf) 1389 ; 1390 strp++; 1391 *strp++ = '\n'; 1392 (void) write(scfd, outbuf, strp-outbuf); 1393 } 1394} 1395 1396static int 1397dropit(int c) 1398{ 1399 switch(c) { 1400 1401 case TRC('_'): 1402 case TRC(';'): 1403 case TRC(','): 1404 case TRC('g'): 1405 case TRC('j'): 1406 case TRC('p'): 1407 case TRC('q'): 1408 case TRC('y'): 1409 return (DROP); 1410 1411 default: 1412 return (0); 1413 } 1414} 1415 1416/* 1417 * sendmail --- 1418 * tell people about job completion 1419 */ 1420static void 1421sendmail(struct printer *pp, char *userid, int bombed) 1422{ 1423 register int i; 1424 int p[2], s; 1425 register const char *cp; 1426 struct stat stb; 1427 FILE *fp; 1428 1429 pipe(p); 1430 if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1431 dup2(p[0], 0); 1432 closelog(); 1433 closeallfds(3); 1434 if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 1435 cp++; 1436 else 1437 cp = _PATH_SENDMAIL; 1438 execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 1439 _exit(0); 1440 } else if (s > 0) { /* parent */ 1441 dup2(p[1], 1); 1442 printf("To: %s@%s\n", userid, origin_host); 1443 printf("Subject: %s printer job \"%s\"\n", pp->printer, 1444 *jobname ? jobname : "<unknown>"); 1445 printf("Reply-To: root@%s\n\n", local_host); 1446 printf("Your printer job "); 1447 if (*jobname) 1448 printf("(%s) ", jobname); 1449 1450 switch (bombed) { 1451 case OK: 1452 cp = "OK"; 1453 printf("\ncompleted successfully\n"); 1454 break; 1455 default: 1456 case FATALERR: 1457 cp = "FATALERR"; 1458 printf("\ncould not be printed\n"); 1459 break; 1460 case NOACCT: 1461 cp = "NOACCT"; 1462 printf("\ncould not be printed without an account on %s\n", 1463 local_host); 1464 break; 1465 case FILTERERR: 1466 cp = "FILTERERR"; 1467 if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 1468 || (fp = fopen(tempstderr, "r")) == NULL) { 1469 printf("\nhad some errors and may not have printed\n"); 1470 break; 1471 } 1472 printf("\nhad the following errors and may not have printed:\n"); 1473 while ((i = getc(fp)) != EOF) 1474 putchar(i); 1475 (void) fclose(fp); 1476 break; 1477 case ACCESS: 1478 cp = "ACCESS"; 1479 printf("\nwas not printed because it was not linked to the original file\n"); 1480 } 1481 fflush(stdout); 1482 (void) close(1); 1483 } else { 1484 syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 1485 return; 1486 } 1487 (void) close(p[0]); 1488 (void) close(p[1]); 1489 wait(NULL); 1490 syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 1491 userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 1492} 1493 1494/* 1495 * dofork - fork with retries on failure 1496 */ 1497static int 1498dofork(const struct printer *pp, int action) 1499{ 1500 int i, fail, forkpid; 1501 struct passwd *pwd; 1502 1503 forkpid = -1; 1504 if (daemon_uname == NULL) { 1505 pwd = getpwuid(pp->daemon_user); 1506 if (pwd == NULL) { 1507 syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 1508 pp->printer, pp->daemon_user); 1509 goto error_ret; 1510 } 1511 daemon_uname = strdup(pwd->pw_name); 1512 daemon_defgid = pwd->pw_gid; 1513 } 1514 1515 for (i = 0; i < 20; i++) { 1516 forkpid = fork(); 1517 if (forkpid < 0) { 1518 sleep((unsigned)(i*i)); 1519 continue; 1520 } 1521 /* 1522 * Child should run as daemon instead of root 1523 */ 1524 if (forkpid == 0) { 1525 errno = 0; 1526 fail = initgroups(daemon_uname, daemon_defgid); 1527 if (fail) { 1528 syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 1529 pp->printer, daemon_uname, daemon_defgid); 1530 break; 1531 } 1532 fail = setgid(daemon_defgid); 1533 if (fail) { 1534 syslog(LOG_ERR, "%s: setgid(%u): %m", 1535 pp->printer, daemon_defgid); 1536 break; 1537 } 1538 fail = setuid(pp->daemon_user); 1539 if (fail) { 1540 syslog(LOG_ERR, "%s: setuid(%ld): %m", 1541 pp->printer, pp->daemon_user); 1542 break; 1543 } 1544 } 1545 return (forkpid); 1546 } 1547 1548 /* 1549 * An error occurred. If the error is in the child process, then 1550 * this routine MUST always exit(). DORETURN only effects how 1551 * errors should be handled in the parent process. 1552 */ 1553error_ret: 1554 if (forkpid == 0) { 1555 syslog(LOG_ERR, "%s: dofork(): aborting child process...", 1556 pp->printer); 1557 exit(1); 1558 } 1559 syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 1560 1561 sleep(1); /* throttle errors, as a safety measure */ 1562 switch (action) { 1563 case DORETURN: 1564 return (-1); 1565 default: 1566 syslog(LOG_ERR, "bad action (%d) to dofork", action); 1567 /* FALLTHROUGH */ 1568 case DOABORT: 1569 exit(1); 1570 } 1571 /*NOTREACHED*/ 1572} 1573 1574/* 1575 * Kill child processes to abort current job. 1576 */ 1577static void 1578abortpr(int signo __unused) 1579{ 1580 1581 (void) unlink(tempstderr); 1582 kill(0, SIGINT); 1583 if (ofilter > 0) 1584 kill(ofilter, SIGCONT); 1585 while (wait(NULL) > 0) 1586 ; 1587 if (ofilter > 0 && tfd != -1) 1588 unlink(tfile); 1589 exit(0); 1590} 1591 1592static void 1593init(struct printer *pp) 1594{ 1595 char *s; 1596 1597 sprintf(&width[2], "%ld", pp->page_width); 1598 sprintf(&length[2], "%ld", pp->page_length); 1599 sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 1600 sprintf(&pxlength[2], "%ld", pp->page_plength); 1601 if ((s = checkremote(pp)) != 0) { 1602 syslog(LOG_WARNING, "%s", s); 1603 free(s); 1604 } 1605} 1606 1607void 1608startprinting(const char *printer) 1609{ 1610 struct printer myprinter, *pp = &myprinter; 1611 int status; 1612 1613 init_printer(pp); 1614 status = getprintcap(printer, pp); 1615 switch(status) { 1616 case PCAPERR_OSERR: 1617 syslog(LOG_ERR, "can't open printer description file: %m"); 1618 exit(1); 1619 case PCAPERR_NOTFOUND: 1620 syslog(LOG_ERR, "unknown printer: %s", printer); 1621 exit(1); 1622 case PCAPERR_TCLOOP: 1623 fatal(pp, "potential reference loop detected in printcap file"); 1624 default: 1625 break; 1626 } 1627 printjob(pp); 1628} 1629 1630/* 1631 * Acquire line printer or remote connection. 1632 */ 1633static void 1634openpr(const struct printer *pp) 1635{ 1636 int p[2]; 1637 char *cp; 1638 1639 if (pp->remote) { 1640 openrem(pp); 1641 /* 1642 * Lpd does support the setting of 'of=' filters for 1643 * jobs going to remote machines, but that does not 1644 * have the same meaning as 'of=' does when handling 1645 * local print queues. For remote machines, all 'of=' 1646 * filter processing is handled in sendfile(), and that 1647 * does not use these global "output filter" variables. 1648 */ 1649 ofd = -1; 1650 ofilter = 0; 1651 return; 1652 } else if (*pp->lp) { 1653 if ((cp = strchr(pp->lp, '@')) != NULL) 1654 opennet(pp); 1655 else 1656 opentty(pp); 1657 } else { 1658 syslog(LOG_ERR, "%s: no line printer device or host name", 1659 pp->printer); 1660 exit(1); 1661 } 1662 1663 /* 1664 * Start up an output filter, if needed. 1665 */ 1666 if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !ofilter) { 1667 pipe(p); 1668 if (pp->remote) { 1669 strcpy(tfile, TFILENAME); 1670 tfd = mkstemp(tfile); 1671 } 1672 if ((ofilter = dofork(pp, DOABORT)) == 0) { /* child */ 1673 dup2(p[0], 0); /* pipe is std in */ 1674 /* tfile/printer is stdout */ 1675 dup2(pp->remote ? tfd : pfd, 1); 1676 closelog(); 1677 closeallfds(3); 1678 if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 1679 cp = pp->filters[LPF_OUTPUT]; 1680 else 1681 cp++; 1682 execl(pp->filters[LPF_OUTPUT], cp, width, length, 1683 (char *)0); 1684 syslog(LOG_ERR, "%s: %s: %m", pp->printer, 1685 pp->filters[LPF_OUTPUT]); 1686 exit(1); 1687 } 1688 (void) close(p[0]); /* close input side */ 1689 ofd = p[1]; /* use pipe for output */ 1690 } else { 1691 ofd = pfd; 1692 ofilter = 0; 1693 } 1694} 1695 1696/* 1697 * Printer connected directly to the network 1698 * or to a terminal server on the net 1699 */ 1700static void 1701opennet(const struct printer *pp) 1702{ 1703 register int i; 1704 int resp; 1705 u_long port; 1706 char *ep; 1707 void (*savealrm)(int); 1708 1709 port = strtoul(pp->lp, &ep, 0); 1710 if (*ep != '@' || port > 65535) { 1711 syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 1712 pp->lp); 1713 exit(1); 1714 } 1715 ep++; 1716 1717 for (i = 1; ; i = i < 256 ? i << 1 : i) { 1718 resp = -1; 1719 savealrm = signal(SIGALRM, alarmhandler); 1720 alarm(pp->conn_timeout); 1721 pfd = getport(pp, ep, port); 1722 alarm(0); 1723 (void)signal(SIGALRM, savealrm); 1724 if (pfd < 0 && errno == ECONNREFUSED) 1725 resp = 1; 1726 else if (pfd >= 0) { 1727 /* 1728 * need to delay a bit for rs232 lines 1729 * to stabilize in case printer is 1730 * connected via a terminal server 1731 */ 1732 delay(500); 1733 break; 1734 } 1735 if (i == 1) { 1736 if (resp < 0) 1737 pstatus(pp, "waiting for %s to come up", 1738 pp->lp); 1739 else 1740 pstatus(pp, 1741 "waiting for access to printer on %s", 1742 pp->lp); 1743 } 1744 sleep(i); 1745 } 1746 pstatus(pp, "sending to %s port %lu", ep, port); 1747} 1748 1749/* 1750 * Printer is connected to an RS232 port on this host 1751 */ 1752static void 1753opentty(const struct printer *pp) 1754{ 1755 register int i; 1756 1757 for (i = 1; ; i = i < 32 ? i << 1 : i) { 1758 pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 1759 if (pfd >= 0) { 1760 delay(500); 1761 break; 1762 } 1763 if (errno == ENOENT) { 1764 syslog(LOG_ERR, "%s: %m", pp->lp); 1765 exit(1); 1766 } 1767 if (i == 1) 1768 pstatus(pp, 1769 "waiting for %s to become ready (offline?)", 1770 pp->printer); 1771 sleep(i); 1772 } 1773 if (isatty(pfd)) 1774 setty(pp); 1775 pstatus(pp, "%s is ready and printing", pp->printer); 1776} 1777 1778/* 1779 * Printer is on a remote host 1780 */ 1781static void 1782openrem(const struct printer *pp) 1783{ 1784 register int i; 1785 int resp; 1786 void (*savealrm)(int); 1787 1788 for (i = 1; ; i = i < 256 ? i << 1 : i) { 1789 resp = -1; 1790 savealrm = signal(SIGALRM, alarmhandler); 1791 alarm(pp->conn_timeout); 1792 pfd = getport(pp, pp->remote_host, 0); 1793 alarm(0); 1794 (void)signal(SIGALRM, savealrm); 1795 if (pfd >= 0) { 1796 if ((writel(pfd, "\2", pp->remote_queue, "\n", 1797 (char *)0) 1798 == 2 + strlen(pp->remote_queue)) 1799 && (resp = response(pp)) == 0) 1800 break; 1801 (void) close(pfd); 1802 } 1803 if (i == 1) { 1804 if (resp < 0) 1805 pstatus(pp, "waiting for %s to come up", 1806 pp->remote_host); 1807 else { 1808 pstatus(pp, 1809 "waiting for queue to be enabled on %s", 1810 pp->remote_host); 1811 i = 256; 1812 } 1813 } 1814 sleep(i); 1815 } 1816 pstatus(pp, "sending to %s", pp->remote_host); 1817} 1818 1819/* 1820 * setup tty lines. 1821 */ 1822static void 1823setty(const struct printer *pp) 1824{ 1825 struct termios ttybuf; 1826 1827 if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 1828 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 1829 exit(1); 1830 } 1831 if (tcgetattr(pfd, &ttybuf) < 0) { 1832 syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 1833 exit(1); 1834 } 1835 if (pp->baud_rate > 0) 1836 cfsetspeed(&ttybuf, pp->baud_rate); 1837 if (pp->mode_set) { 1838 char *s = strdup(pp->mode_set), *tmp; 1839 1840 while ((tmp = strsep(&s, ",")) != NULL) { 1841 (void) msearch(tmp, &ttybuf); 1842 } 1843 } 1844 if (pp->mode_set != 0 || pp->baud_rate > 0) { 1845 if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 1846 syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 1847 } 1848 } 1849} 1850 1851#include <stdarg.h> 1852 1853static void 1854pstatus(const struct printer *pp, const char *msg, ...) 1855{ 1856 int fd; 1857 char *buf; 1858 va_list ap; 1859 va_start(ap, msg); 1860 1861 umask(0); 1862 fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 1863 if (fd < 0) { 1864 syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->status_file); 1865 exit(1); 1866 } 1867 ftruncate(fd, 0); 1868 vasprintf(&buf, msg, ap); 1869 va_end(ap); 1870 writel(fd, buf, "\n", (char *)0); 1871 close(fd); 1872 free(buf); 1873} 1874 1875void 1876alarmhandler(int signo __unused) 1877{ 1878 /* the signal is ignored */ 1879 /* (the '__unused' is just to avoid a compile-time warning) */ 1880} 1881