printjob.c revision 95067
1168404Spjd/* 2168404Spjd * Copyright (c) 1983, 1993 3168404Spjd * The Regents of the University of California. All rights reserved. 4168404Spjd * 5168404Spjd * 6168404Spjd * Redistribution and use in source and binary forms, with or without 7168404Spjd * modification, are permitted provided that the following conditions 8168404Spjd * are met: 9168404Spjd * 1. Redistributions of source code must retain the above copyright 10168404Spjd * notice, this list of conditions and the following disclaimer. 11168404Spjd * 2. Redistributions in binary form must reproduce the above copyright 12168404Spjd * notice, this list of conditions and the following disclaimer in the 13168404Spjd * documentation and/or other materials provided with the distribution. 14168404Spjd * 3. All advertising materials mentioning features or use of this software 15168404Spjd * must display the following acknowledgement: 16168404Spjd * This product includes software developed by the University of 17168404Spjd * California, Berkeley and its contributors. 18168404Spjd * 4. Neither the name of the University nor the names of its contributors 19168404Spjd * may be used to endorse or promote products derived from this software 20168404Spjd * without specific prior written permission. 21236884Smm * 22168404Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23219089Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24230438Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25226707Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26247540Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27228103Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28248571Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29249195Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30246586Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31251646Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32168404Spjd * SUCH DAMAGE. 33168404Spjd */ 34248571Smm 35248571Smm#ifndef lint 36248571Smmstatic const char copyright[] = 37248571Smm"@(#) Copyright (c) 1983, 1993\n\ 38248571Smm The Regents of the University of California. All rights reserved.\n"; 39248571Smm#endif /* not lint */ 40248571Smm 41248571Smm#ifndef lint 42248571Smm/* 43248571Smmstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 44248571Smm*/ 45248571Smmstatic const char rcsid[] = 46248571Smm "$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 95067 2002-04-19 18:28:35Z gad $"; 47248571Smm#endif /* not lint */ 48248571Smm 49248571Smm 50248571Smm/* 51248571Smm * printjob -- print jobs in the queue. 52248571Smm * 53248571Smm * NOTE: the lock file is used to pass information to lpq and lprm. 54248571Smm * it does not need to be removed because file locks are dynamic. 55248571Smm */ 56248571Smm 57248571Smm#include <sys/param.h> 58248571Smm#include <sys/wait.h> 59248571Smm#include <sys/stat.h> 60248571Smm#include <sys/types.h> 61248571Smm 62248571Smm#include <pwd.h> 63248571Smm#include <unistd.h> 64248571Smm#include <signal.h> 65248571Smm#include <syslog.h> 66248571Smm#include <fcntl.h> 67248571Smm#include <dirent.h> 68248571Smm#include <errno.h> 69248571Smm#include <stdio.h> 70248571Smm#include <string.h> 71248571Smm#include <stdlib.h> 72248571Smm#include <sys/ioctl.h> 73248571Smm#include <termios.h> 74248571Smm#include <time.h> 75248571Smm#include "lp.h" 76248571Smm#include "lp.local.h" 77248571Smm#include "pathnames.h" 78248571Smm#include "extern.h" 79248571Smm 80248571Smm#define DORETURN 0 /* dofork should return "can't fork" error */ 81248571Smm#define DOABORT 1 /* dofork should just die if fork() fails */ 82248571Smm 83248571Smm/* 84248571Smm * Error tokens 85248571Smm */ 86248571Smm#define REPRINT -2 87248571Smm#define ERROR -1 88248571Smm#define OK 0 89248571Smm#define FATALERR 1 90248571Smm#define NOACCT 2 91248571Smm#define FILTERERR 3 92248571Smm#define ACCESS 4 93248571Smm 94248571Smmstatic dev_t fdev; /* device of file pointed to by symlink */ 95248571Smmstatic ino_t fino; /* inode of file pointed to by symlink */ 96248571Smmstatic FILE *cfp; /* control file */ 97248571Smmstatic int child; /* id of any filters */ 98248571Smmstatic int job_dfcnt; /* count of datafiles in current user job */ 99248571Smmstatic int lfd; /* lock file descriptor */ 100248571Smmstatic int ofd; /* output filter file descriptor */ 101248571Smmstatic int ofilter; /* id of output filter, if any */ 102248571Smmstatic int tfd = -1; /* output filter temp file output */ 103248571Smmstatic int pfd; /* prstatic inter file descriptor */ 104248571Smmstatic int pid; /* pid of lpd process */ 105248571Smmstatic int prchild; /* id of pr process */ 106248571Smmstatic char title[80]; /* ``pr'' title */ 107248571Smmstatic char locale[80]; /* ``pr'' locale */ 108248571Smm 109248571Smm/* these two are set from pp->daemon_user, but only if they are needed */ 110248571Smmstatic char *daemon_uname; /* set from pwd->pw_name */ 111248571Smmstatic int daemon_defgid; 112248571Smm 113248571Smmstatic char class[32]; /* classification field */ 114248571Smmstatic char origin_host[MAXHOSTNAMELEN]; /* user's host machine */ 115248571Smm /* indentation size in static characters */ 116248571Smmstatic char indent[10] = "-i0"; 117248571Smmstatic char jobname[100]; /* job or file name */ 118248571Smmstatic char length[10] = "-l"; /* page length in lines */ 119248571Smmstatic char logname[32]; /* user's login name */ 120248571Smmstatic char pxlength[10] = "-y"; /* page length in pixels */ 121248571Smmstatic char pxwidth[10] = "-x"; /* page width in pixels */ 122248571Smm/* tempstderr is the filename used to catch stderr from exec-ing filters */ 123248571Smmstatic char tempstderr[] = "errs.XXXXXXX"; 124248571Smmstatic char width[10] = "-w"; /* page width in static characters */ 125248571Smm#define TFILENAME "fltXXXXXX" 126248571Smmstatic char tfile[] = TFILENAME; /* file name for filter output */ 127248571Smm 128248571Smmstatic void abortpr(int _signo); 129248571Smmstatic void alarmhandler(int _signo); 130248571Smmstatic void banner(struct printer *_pp, char *_name1, char *_name2); 131248571Smmstatic int dofork(const struct printer *_pp, int _action); 132248571Smmstatic int dropit(int _c); 133248571Smmstatic int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, 134248571Smm int _infd, int _outfd); 135168962Spjdstatic void init(struct printer *_pp); 136168404Spjdstatic void openpr(const struct printer *_pp); 137168404Spjdstatic void opennet(const struct printer *_pp); 138168404Spjdstatic void opentty(const struct printer *_pp); 139168404Spjdstatic void openrem(const struct printer *pp); 140168404Spjdstatic int print(struct printer *_pp, int _format, char *_file); 141168404Spjdstatic int printit(struct printer *_pp, char *_file); 142168404Spjdstatic void pstatus(const struct printer *_pp, const char *_msg, ...) 143168404Spjd __printflike(2, 3); 144168404Spjdstatic char response(const struct printer *_pp); 145168404Spjdstatic void scan_out(struct printer *_pp, int _scfd, char *_scsp, 146168962Spjd int _dlm); 147168404Spjdstatic char *scnline(int _key, char *_p, int _c); 148168404Spjdstatic int sendfile(struct printer *_pp, int _type, char *_file, 149168404Spjd char _format); 150168404Spjdstatic int sendit(struct printer *_pp, char *_file); 151168404Spjdstatic void sendmail(struct printer *_pp, char *_userid, int _bombed); 152168404Spjdstatic void setty(const struct printer *_pp); 153209962Smm 154185029Spjdvoid 155168404Spjdprintjob(struct printer *pp) 156168404Spjd{ 157168404Spjd struct stat stb; 158168404Spjd register struct jobqueue *q, **qp; 159168404Spjd struct jobqueue **queue; 160168404Spjd register int i, nitems; 161168404Spjd off_t pidoff; 162168404Spjd int errcnt, jobcount, tempfd; 163185029Spjd 164185029Spjd jobcount = 0; 165235222Smm init(pp); /* set up capabilities */ 166248571Smm (void) write(1, "", 1); /* ack that daemon is started */ 167168962Spjd (void) close(2); /* set up log file */ 168168962Spjd if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { 169168962Spjd syslog(LOG_ERR, "%s: %m", pp->log_file); 170168404Spjd (void) open(_PATH_DEVNULL, O_WRONLY); 171168404Spjd } 172168404Spjd setgid(getegid()); 173168404Spjd pid = getpid(); /* for use with lprm */ 174168404Spjd setpgrp(0, pid); 175168404Spjd 176168404Spjd /* 177185029Spjd * At initial lpd startup, printjob may be called with various 178219089Spjd * signal handlers in effect. After that initial startup, any 179168404Spjd * calls to printjob will have a *different* set of signal-handlers 180219089Spjd * in effect. Make sure all handlers are the ones we want. 181185029Spjd */ 182248571Smm signal(SIGCHLD, SIG_DFL); 183248571Smm signal(SIGHUP, abortpr); 184248571Smm signal(SIGINT, abortpr); 185246586Sdelphij signal(SIGQUIT, abortpr); 186168404Spjd signal(SIGTERM, abortpr); 187168404Spjd 188168404Spjd /* 189185029Spjd * uses short form file names 190219089Spjd */ 191219089Spjd if (chdir(pp->spool_dir) < 0) { 192168404Spjd syslog(LOG_ERR, "%s: %m", pp->spool_dir); 193219089Spjd exit(1); 194168404Spjd } 195230397Spjd if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS)) 196230397Spjd exit(0); /* printing disabled */ 197230397Spjd lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 198230397Spjd LOCK_FILE_MODE); 199230397Spjd if (lfd < 0) { 200230397Spjd if (errno == EWOULDBLOCK) /* active daemon present */ 201168404Spjd exit(0); 202168404Spjd syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 203168404Spjd exit(1); 204168404Spjd } 205168404Spjd /* turn off non-blocking mode (was turned on for lock effects only) */ 206248571Smm if (fcntl(lfd, F_SETFL, 0) < 0) { 207248571Smm syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 208248571Smm exit(1); 209168404Spjd } 210248571Smm ftruncate(lfd, 0); 211248571Smm /* 212248571Smm * write process id for others to know 213248571Smm */ 214209962Smm sprintf(line, "%u\n", pid); 215209962Smm pidoff = i = strlen(line); 216209962Smm if (write(lfd, line, i) != i) { 217209962Smm syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file); 218209962Smm exit(1); 219209962Smm } 220246688Smm /* 221246688Smm * search the spool directory for work and sort by queue order. 222246688Smm */ 223248571Smm if ((nitems = getq(pp, &queue)) < 0) { 224246688Smm syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 225246688Smm pp->spool_dir); 226168404Spjd exit(1); 227248571Smm } 228168404Spjd if (nitems == 0) /* no work to do */ 229168404Spjd exit(0); 230209962Smm if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ 231248571Smm if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) 232246688Smm syslog(LOG_ERR, "%s: %s: %m", pp->printer, 233248571Smm pp->lock_file); 234248571Smm } 235168404Spjd 236168404Spjd /* create a file which will be used to hold stderr from filters */ 237209962Smm if ((tempfd = mkstemp(tempstderr)) == -1) { 238209962Smm syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 239209962Smm tempstderr); 240209962Smm exit(1); 241209962Smm } 242209962Smm if ((i = fchmod(tempfd, 0664)) == -1) { 243209962Smm syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, 244209962Smm tempstderr); 245209962Smm exit(1); 246219089Spjd } 247219089Spjd /* lpd doesn't need it to be open, it just needs it to exist */ 248219089Spjd close(tempfd); 249219089Spjd 250185029Spjd openpr(pp); /* open printer or remote */ 251185029Spjdagain: 252248571Smm /* 253248571Smm * we found something to do now do it -- 254219089Spjd * write the name of the current control file into the lock file 255219089Spjd * so the spool queue program can tell what we're working on 256185029Spjd */ 257248571Smm for (qp = queue; nitems--; free((char *) q)) { 258246586Sdelphij q = *qp++; 259168404Spjd if (stat(q->job_cfname, &stb) < 0) 260168404Spjd continue; 261168404Spjd errcnt = 0; 262168404Spjd restart: 263168404Spjd (void) lseek(lfd, pidoff, 0); 264219089Spjd (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); 265168404Spjd i = strlen(line); 266168404Spjd if (write(lfd, line, i) != i) 267168404Spjd syslog(LOG_ERR, "%s: %s: %m", pp->printer, 268168404Spjd pp->lock_file); 269168404Spjd if (!pp->remote) 270168404Spjd i = printit(pp, q->job_cfname); 271168404Spjd else 272168404Spjd i = sendit(pp, q->job_cfname); 273168404Spjd /* 274168404Spjd * Check to see if we are supposed to stop printing or 275168404Spjd * if we are to rebuild the queue. 276168404Spjd */ 277168404Spjd if (fstat(lfd, &stb) == 0) { 278168404Spjd /* stop printing before starting next job? */ 279168404Spjd if (stb.st_mode & LFM_PRINT_DIS) 280168404Spjd goto done; 281168404Spjd /* rebuild queue (after lpc topq) */ 282168404Spjd if (stb.st_mode & LFM_RESET_QUE) { 283168404Spjd for (free(q); nitems--; free(q)) 284168404Spjd q = *qp++; 285168404Spjd if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) 286168404Spjd < 0) 287168404Spjd syslog(LOG_WARNING, "%s: %s: %m", 288168404Spjd pp->printer, pp->lock_file); 289168404Spjd break; 290168404Spjd } 291168404Spjd } 292168404Spjd if (i == OK) /* all files of this job printed */ 293168404Spjd jobcount++; 294168404Spjd else if (i == REPRINT && ++errcnt < 5) { 295185029Spjd /* try reprinting the job */ 296185029Spjd syslog(LOG_INFO, "restarting %s", pp->printer); 297185029Spjd if (ofilter > 0) { 298185029Spjd kill(ofilter, SIGCONT); /* to be sure */ 299185029Spjd (void) close(ofd); 300185029Spjd while ((i = wait(NULL)) > 0 && i != ofilter) 301185029Spjd ; 302185029Spjd if (i < 0) 303185029Spjd syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", 304185029Spjd pp->printer, ofilter); 305185029Spjd ofilter = 0; 306185029Spjd } 307185029Spjd (void) close(pfd); /* close printer */ 308185029Spjd if (ftruncate(lfd, pidoff) < 0) 309185029Spjd syslog(LOG_WARNING, "%s: %s: %m", 310185029Spjd pp->printer, pp->lock_file); 311185029Spjd openpr(pp); /* try to reopen printer */ 312185029Spjd goto restart; 313185029Spjd } else { 314185029Spjd syslog(LOG_WARNING, "%s: job could not be %s (%s)", 315185029Spjd pp->printer, 316185029Spjd pp->remote ? "sent to remote host" : "printed", 317185029Spjd q->job_cfname); 318185029Spjd if (i == REPRINT) { 319185029Spjd /* ensure we don't attempt this job again */ 320185029Spjd (void) unlink(q->job_cfname); 321168404Spjd q->job_cfname[0] = 'd'; 322185029Spjd (void) unlink(q->job_cfname); 323185029Spjd if (logname[0]) 324185029Spjd sendmail(pp, logname, FATALERR); 325185029Spjd } 326185029Spjd } 327219089Spjd } 328185029Spjd free(queue); 329219089Spjd /* 330219089Spjd * search the spool directory for more work. 331219089Spjd */ 332219089Spjd if ((nitems = getq(pp, &queue)) < 0) { 333219089Spjd syslog(LOG_ERR, "%s: can't scan %s", pp->printer, 334185029Spjd pp->spool_dir); 335219089Spjd exit(1); 336185029Spjd } 337185029Spjd if (nitems == 0) { /* no more work to do */ 338185029Spjd done: 339251631Sdelphij if (jobcount > 0) { /* jobs actually printed */ 340185029Spjd if (!pp->no_formfeed && !pp->tof) 341185029Spjd (void) write(ofd, pp->form_feed, 342185029Spjd strlen(pp->form_feed)); 343185029Spjd if (pp->trailer != NULL) /* output trailer */ 344185029Spjd (void) write(ofd, pp->trailer, 345185029Spjd strlen(pp->trailer)); 346185029Spjd } 347185029Spjd (void) close(ofd); 348185029Spjd (void) wait(NULL); 349185029Spjd (void) unlink(tempstderr); 350185029Spjd exit(0); 351185029Spjd } 352185029Spjd goto again; 353185029Spjd} 354185029Spjd 355185029Spjdchar fonts[4][50]; /* fonts for troff */ 356185029Spjd 357185029Spjdchar ifonts[4][40] = { 358185029Spjd _PATH_VFONTR, 359185029Spjd _PATH_VFONTI, 360185029Spjd _PATH_VFONTB, 361185029Spjd _PATH_VFONTS, 362185029Spjd}; 363185029Spjd 364185029Spjd/* 365219089Spjd * The remaining part is the reading of the control file (cf) 366185029Spjd * and performing the various actions. 367185029Spjd */ 368219089Spjdstatic int 369219089Spjdprintit(struct printer *pp, char *file) 370219089Spjd{ 371219089Spjd register int i; 372219089Spjd char *cp; 373185029Spjd int bombed, didignorehdr; 374185029Spjd 375219089Spjd bombed = OK; 376185029Spjd didignorehdr = 0; 377185029Spjd /* 378185029Spjd * open control file; ignore if no longer there. 379185029Spjd */ 380185029Spjd if ((cfp = fopen(file, "r")) == NULL) { 381185029Spjd syslog(LOG_INFO, "%s: %s: %m", pp->printer, file); 382185029Spjd return(OK); 383185029Spjd } 384185029Spjd /* 385185029Spjd * Reset troff fonts. 386185029Spjd */ 387185029Spjd for (i = 0; i < 4; i++) 388185029Spjd strcpy(fonts[i], ifonts[i]); 389185029Spjd sprintf(&width[2], "%ld", pp->page_width); 390185029Spjd strcpy(indent+2, "0"); 391248571Smm 392185029Spjd /* initialize job-specific count of datafiles processed */ 393185029Spjd job_dfcnt = 0; 394185029Spjd 395185029Spjd /* 396185029Spjd * read the control file for work to do 397185029Spjd * 398168404Spjd * file format -- first character in the line is a command 399168404Spjd * rest of the line is the argument. 400168404Spjd * valid commands are: 401168404Spjd * 402168404Spjd * S -- "stat info" for symbolic link protection 403248571Smm * J -- "job name" on banner page 404168404Spjd * C -- "class name" on banner page 405168404Spjd * L -- "literal" user's name to print on banner 406168404Spjd * T -- "title" for pr 407168404Spjd * H -- "host name" of machine where lpr was done 408168404Spjd * P -- "person" user's login name 409168404Spjd * I -- "indent" amount to indent output 410168404Spjd * R -- laser dpi "resolution" 411168404Spjd * f -- "file name" name of text file to print 412168404Spjd * l -- "file name" text file with control chars 413168404Spjd * o -- "file name" postscript file, according to 414248571Smm * the RFC. Here it is treated like an 'f'. 415168404Spjd * p -- "file name" text file to print with pr(1) 416185029Spjd * t -- "file name" troff(1) file to print 417185029Spjd * n -- "file name" ditroff(1) file to print 418168404Spjd * d -- "file name" dvi file to print 419168404Spjd * g -- "file name" plot(1G) file to print 420249195Smm * v -- "file name" plain raster file to print 421168404Spjd * c -- "file name" cifplot file to print 422168404Spjd * 1 -- "R font file" for troff 423168404Spjd * 2 -- "I font file" for troff 424219089Spjd * 3 -- "B font file" for troff 425168404Spjd * 4 -- "S font file" for troff 426168404Spjd * N -- "name" of file (used by lpq) 427168404Spjd * U -- "unlink" name of file to remove 428168404Spjd * (after we print it. (Pass 2 only)). 429168404Spjd * M -- "mail" to user when done printing 430168404Spjd * Z -- "locale" for pr 431168404Spjd * 432185029Spjd * getline reads a line and expands tabs to blanks 433168404Spjd */ 434249195Smm 435168404Spjd /* pass 1 */ 436185029Spjd 437168404Spjd while (getline(cfp)) 438168404Spjd switch (line[0]) { 439168404Spjd case 'H': 440168404Spjd strlcpy(origin_host, line + 1, sizeof(origin_host)); 441168404Spjd if (class[0] == '\0') { 442249195Smm strlcpy(class, line+1, sizeof(class)); 443168404Spjd } 444168404Spjd continue; 445168404Spjd 446168404Spjd case 'P': 447168404Spjd strlcpy(logname, line + 1, sizeof(logname)); 448249195Smm if (pp->restricted) { /* restricted */ 449168404Spjd if (getpwnam(logname) == NULL) { 450168404Spjd bombed = NOACCT; 451168404Spjd sendmail(pp, line+1, bombed); 452249195Smm goto pass2; 453168404Spjd } 454168404Spjd } 455168404Spjd continue; 456168404Spjd 457219089Spjd case 'S': 458219089Spjd cp = line+1; 459219089Spjd i = 0; 460219089Spjd while (*cp >= '0' && *cp <= '9') 461219089Spjd i = i * 10 + (*cp++ - '0'); 462219089Spjd fdev = i; 463249195Smm cp++; 464219089Spjd i = 0; 465219089Spjd while (*cp >= '0' && *cp <= '9') 466219089Spjd i = i * 10 + (*cp++ - '0'); 467219089Spjd fino = i; 468219089Spjd continue; 469219089Spjd 470219089Spjd case 'J': 471219089Spjd if (line[1] != '\0') { 472219089Spjd strlcpy(jobname, line + 1, sizeof(jobname)); 473248571Smm } else 474249195Smm strcpy(jobname, " "); 475219089Spjd continue; 476219089Spjd 477219089Spjd case 'C': 478219089Spjd if (line[1] != '\0') 479248571Smm strlcpy(class, line + 1, sizeof(class)); 480248571Smm else if (class[0] == '\0') { 481248571Smm /* XXX - why call gethostname instead of 482168404Spjd * just strlcpy'ing local_host? */ 483168404Spjd gethostname(class, sizeof(class)); 484168404Spjd class[sizeof(class) - 1] = '\0'; 485228103Smm } 486185029Spjd continue; 487185029Spjd 488248571Smm case 'T': /* header title for pr */ 489248571Smm strlcpy(title, line + 1, sizeof(title)); 490185029Spjd continue; 491185029Spjd 492185029Spjd case 'L': /* identification line */ 493185029Spjd if (!pp->no_header && !pp->header_last) 494248571Smm banner(pp, line+1, jobname); 495248571Smm continue; 496219089Spjd 497219089Spjd case '1': /* troff fonts */ 498248571Smm case '2': 499248571Smm case '3': 500219089Spjd case '4': 501248571Smm if (line[1] != '\0') { 502248571Smm strlcpy(fonts[line[0]-'1'], line + 1, 503248571Smm (size_t)50); 504248571Smm } 505248571Smm continue; 506248571Smm 507248571Smm case 'W': /* page width */ 508248571Smm strlcpy(width+2, line + 1, sizeof(width) - 2); 509219089Spjd continue; 510248571Smm 511248571Smm case 'I': /* indent amount */ 512248571Smm strlcpy(indent+2, line + 1, sizeof(indent) - 2); 513248571Smm continue; 514248571Smm 515219089Spjd case 'Z': /* locale for pr */ 516219089Spjd strlcpy(locale, line + 1, sizeof(locale)); 517219089Spjd locale[sizeof(locale) - 1] = '\0'; 518219089Spjd continue; 519219089Spjd 520219089Spjd default: /* some file to print */ 521219089Spjd /* only lowercase cmd-codes include a file-to-print */ 522219089Spjd if ((line[0] < 'a') || (line[0] > 'z')) { 523219089Spjd /* ignore any other lines */ 524185029Spjd if (lflag <= 1) 525219089Spjd continue; 526185029Spjd if (!didignorehdr) { 527219089Spjd syslog(LOG_INFO, "%s: in %s :", 528219089Spjd pp->printer, file); 529219089Spjd didignorehdr = 1; 530219089Spjd } 531219089Spjd syslog(LOG_INFO, "%s: ignoring line: '%c' %s", 532219089Spjd pp->printer, line[0], &line[1]); 533219089Spjd continue; 534219089Spjd } 535219089Spjd i = print(pp, line[0], line+1); 536219089Spjd switch (i) { 537248571Smm case ERROR: 538249195Smm if (bombed == OK) 539219089Spjd bombed = FATALERR; 540219089Spjd break; 541219089Spjd case REPRINT: 542219089Spjd (void) fclose(cfp); 543219089Spjd return(REPRINT); 544219089Spjd case FILTERERR: 545249195Smm case ACCESS: 546219089Spjd bombed = i; 547185029Spjd sendmail(pp, logname, bombed); 548219089Spjd } 549219089Spjd title[0] = '\0'; 550219089Spjd continue; 551219089Spjd 552219089Spjd case 'N': 553219089Spjd case 'U': 554249195Smm case 'M': 555219089Spjd case 'R': 556219089Spjd continue; 557219089Spjd } 558219089Spjd 559219089Spjd /* pass 2 */ 560219089Spjd 561219089Spjdpass2: 562219089Spjd fseek(cfp, 0L, 0); 563219089Spjd while (getline(cfp)) 564219089Spjd switch (line[0]) { 565249195Smm case 'L': /* identification line */ 566219089Spjd if (!pp->no_header && pp->header_last) 567219089Spjd banner(pp, line+1, jobname); 568249195Smm continue; 569219089Spjd 570219089Spjd case 'M': 571219089Spjd if (bombed < NOACCT) /* already sent if >= NOACCT */ 572219089Spjd sendmail(pp, line+1, bombed); 573219089Spjd continue; 574219089Spjd 575219089Spjd case 'U': 576219089Spjd if (strchr(line+1, '/')) 577219089Spjd continue; 578219089Spjd (void) unlink(line+1); 579219089Spjd } 580219089Spjd /* 581219089Spjd * clean-up in case another control file exists 582219089Spjd */ 583219089Spjd (void) fclose(cfp); 584219089Spjd (void) unlink(file); 585219089Spjd return(bombed == OK ? OK : ERROR); 586219089Spjd} 587248571Smm 588249195Smm/* 589219089Spjd * Print a file. 590219089Spjd * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 591219089Spjd * Return -1 if a non-recoverable error occured, 592219089Spjd * 2 if the filter detected some errors (but printed the job anyway), 593219089Spjd * 1 if we should try to reprint this job and 594219089Spjd * 0 if all is well. 595219089Spjd * Note: all filters take stdin as the file, stdout as the printer, 596219089Spjd * stderr as the log file, and must not ignore SIGINT. 597219089Spjd */ 598249195Smmstatic int 599219089Spjdprint(struct printer *pp, int format, char *file) 600219089Spjd{ 601219089Spjd register int n, i; 602219089Spjd register char *prog; 603219089Spjd int fi, fo; 604219089Spjd FILE *fp; 605219089Spjd char *av[15], buf[BUFSIZ]; 606219089Spjd int pid, p[2], stopped; 607219089Spjd union wait status; 608219089Spjd struct stat stb; 609219089Spjd 610219089Spjd if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { 611219089Spjd syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", 612219089Spjd pp->printer, file, format); 613219089Spjd return(ERROR); 614219089Spjd } 615219089Spjd /* 616219089Spjd * Check to see if data file is a symbolic link. If so, it should 617219089Spjd * still point to the same file or someone is trying to print 618219089Spjd * something he shouldn't. 619219089Spjd */ 620219089Spjd if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 621219089Spjd (stb.st_dev != fdev || stb.st_ino != fino)) 622219089Spjd return(ACCESS); 623219089Spjd 624185029Spjd job_dfcnt++; /* increment datafile counter for this job */ 625185029Spjd stopped = 0; /* output filter is not stopped */ 626185029Spjd 627185029Spjd /* everything seems OK, start it up */ 628185029Spjd if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ 629185029Spjd (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 630185029Spjd pp->tof = 1; 631185029Spjd } 632249195Smm if (pp->filters[LPF_INPUT] == NULL 633185029Spjd && (format == 'f' || format == 'l' || format == 'o')) { 634185029Spjd pp->tof = 0; 635185029Spjd while ((n = read(fi, buf, BUFSIZ)) > 0) 636185029Spjd if (write(ofd, buf, n) != n) { 637185029Spjd (void) close(fi); 638185029Spjd return(REPRINT); 639185029Spjd } 640185029Spjd (void) close(fi); 641185029Spjd return(OK); 642185029Spjd } 643185029Spjd switch (format) { 644219089Spjd case 'p': /* print file using 'pr' */ 645185029Spjd if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */ 646249195Smm prog = _PATH_PR; 647219089Spjd i = 0; 648249195Smm av[i++] = "pr"; 649185029Spjd av[i++] = width; 650185029Spjd av[i++] = length; 651219089Spjd av[i++] = "-h"; 652219089Spjd av[i++] = *title ? title : " "; 653219089Spjd av[i++] = "-L"; 654219089Spjd av[i++] = *locale ? locale : "C"; 655249195Smm av[i++] = "-F"; 656219089Spjd av[i] = 0; 657219089Spjd fo = ofd; 658219089Spjd goto start; 659219089Spjd } 660219089Spjd pipe(p); 661219089Spjd if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */ 662219089Spjd dup2(fi, 0); /* file is stdin */ 663219089Spjd dup2(p[1], 1); /* pipe is stdout */ 664219089Spjd closelog(); 665219089Spjd closeallfds(3); 666219089Spjd execl(_PATH_PR, "pr", width, length, 667219089Spjd "-h", *title ? title : " ", 668185029Spjd "-L", *locale ? locale : "C", 669185029Spjd "-F", (char *)0); 670219089Spjd syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 671185029Spjd exit(2); 672185029Spjd } 673248571Smm (void) close(p[1]); /* close output side */ 674248571Smm (void) close(fi); 675248571Smm if (prchild < 0) { 676185029Spjd prchild = 0; 677185029Spjd (void) close(p[0]); 678185029Spjd return(ERROR); 679185029Spjd } 680248571Smm fi = p[0]; /* use pipe for input */ 681168404Spjd case 'f': /* print plain text file */ 682168404Spjd prog = pp->filters[LPF_INPUT]; 683185029Spjd av[1] = width; 684185029Spjd av[2] = length; 685185029Spjd av[3] = indent; 686185029Spjd n = 4; 687185029Spjd break; 688168404Spjd case 'o': /* print postscript file */ 689168404Spjd /* 690248571Smm * Treat this as a "plain file with control characters", and 691248571Smm * assume the standard LPF_INPUT filter will recognize that 692248571Smm * the data is postscript and know what to do with it. These 693185029Spjd * 'o'-file requests could come from MacOS 10.1 systems. 694219089Spjd * (later versions of MacOS 10 will explicitly use 'l') 695219089Spjd * A postscript file can contain binary data, which is why 'l' 696185029Spjd * is somewhat more appropriate than 'f'. 697185029Spjd */ 698248571Smm /* FALLTHROUGH */ 699248571Smm case 'l': /* like 'f' but pass control characters */ 700248571Smm prog = pp->filters[LPF_INPUT]; 701185029Spjd av[1] = "-c"; 702219089Spjd av[2] = width; 703219089Spjd av[3] = length; 704219089Spjd av[4] = indent; 705219089Spjd n = 5; 706219089Spjd break; 707219089Spjd case 'r': /* print a fortran text file */ 708219089Spjd prog = pp->filters[LPF_FORTRAN]; 709219089Spjd av[1] = width; 710219089Spjd av[2] = length; 711219089Spjd n = 3; 712219089Spjd break; 713249195Smm case 't': /* print troff output */ 714248571Smm case 'n': /* print ditroff output */ 715248571Smm case 'd': /* print tex output */ 716219089Spjd (void) unlink(".railmag"); 717219089Spjd if ((fo = creat(".railmag", FILMOD)) < 0) { 718219089Spjd syslog(LOG_ERR, "%s: cannot create .railmag", 719248571Smm pp->printer); 720248571Smm (void) unlink(".railmag"); 721219089Spjd } else { 722248571Smm for (n = 0; n < 4; n++) { 723219089Spjd if (fonts[n][0] != '/') 724219089Spjd (void) write(fo, _PATH_VFONT, 725219089Spjd sizeof(_PATH_VFONT) - 1); 726219089Spjd (void) write(fo, fonts[n], strlen(fonts[n])); 727219089Spjd (void) write(fo, "\n", 1); 728219089Spjd } 729248571Smm (void) close(fo); 730219089Spjd } 731219089Spjd prog = (format == 't') ? pp->filters[LPF_TROFF] 732185029Spjd : ((format == 'n') ? pp->filters[LPF_DITROFF] 733185029Spjd : pp->filters[LPF_DVI]); 734248571Smm av[1] = pxwidth; 735209962Smm av[2] = pxlength; 736248571Smm n = 3; 737209962Smm break; 738248571Smm case 'c': /* print cifplot output */ 739248571Smm prog = pp->filters[LPF_CIFPLOT]; 740248571Smm av[1] = pxwidth; 741248571Smm av[2] = pxlength; 742248571Smm n = 3; 743248571Smm break; 744248571Smm case 'g': /* print plot(1G) output */ 745248571Smm prog = pp->filters[LPF_GRAPH]; 746209962Smm av[1] = pxwidth; 747209962Smm av[2] = pxlength; 748209962Smm n = 3; 749209962Smm break; 750209962Smm case 'v': /* print raster output */ 751209962Smm prog = pp->filters[LPF_RASTER]; 752209962Smm av[1] = pxwidth; 753209962Smm av[2] = pxlength; 754209962Smm n = 3; 755209962Smm break; 756209962Smm default: 757209962Smm (void) close(fi); 758209962Smm syslog(LOG_ERR, "%s: illegal format character '%c'", 759249195Smm pp->printer, format); 760209962Smm return(ERROR); 761209962Smm } 762209962Smm if (prog == NULL) { 763209962Smm (void) close(fi); 764209962Smm syslog(LOG_ERR, 765209962Smm "%s: no filter found in printcap for format character '%c'", 766209962Smm pp->printer, format); 767185029Spjd return(ERROR); 768248571Smm } 769185029Spjd if ((av[0] = strrchr(prog, '/')) != NULL) 770185029Spjd av[0]++; 771249195Smm else 772185029Spjd av[0] = prog; 773185029Spjd av[n++] = "-n"; 774185029Spjd av[n++] = logname; 775185029Spjd av[n++] = "-h"; 776248571Smm av[n++] = origin_host; 777209962Smm av[n++] = pp->acct_file; 778209962Smm av[n] = 0; 779185029Spjd fo = pfd; 780209962Smm if (ofilter > 0) { /* stop output filter */ 781248571Smm write(ofd, "\031\1", 2); 782209962Smm while ((pid = 783209962Smm wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter) 784249195Smm ; 785185029Spjd if (pid < 0) 786209962Smm syslog(LOG_WARNING, "%s: after stopping 'of', wait3() returned: %m", 787209962Smm pp->printer); 788209962Smm else if (status.w_stopval != WSTOPPED) { 789248571Smm (void) close(fi); 790185029Spjd syslog(LOG_WARNING, 791185029Spjd "%s: output filter died " 792185029Spjd "(pid=%d retcode=%d termsig=%d)", 793168404Spjd pp->printer, ofilter, status.w_retcode, 794185029Spjd status.w_termsig); 795168404Spjd return(REPRINT); 796168404Spjd } 797168404Spjd stopped++; 798168404Spjd } 799168404Spjdstart: 800168404Spjd if ((child = dofork(pp, DORETURN)) == 0) { /* child */ 801185029Spjd dup2(fi, 0); 802185029Spjd dup2(fo, 1); 803168404Spjd /* setup stderr for the filter (child process) 804168404Spjd * so it goes to our temporary errors file */ 805168404Spjd n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 806185029Spjd if (n >= 0) 807168404Spjd dup2(n, 2); 808249195Smm closelog(); 809168404Spjd closeallfds(3); 810185029Spjd execv(prog, av); 811168404Spjd syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, 812185029Spjd prog); 813185029Spjd exit(2); 814185029Spjd } 815185029Spjd (void) close(fi); 816185029Spjd if (child < 0) 817185029Spjd status.w_retcode = 100; 818185029Spjd else { 819185029Spjd while ((pid = wait((int *)&status)) > 0 && pid != child) 820185029Spjd ; 821185029Spjd if (pid < 0) { 822185029Spjd status.w_retcode = 100; 823185029Spjd syslog(LOG_WARNING, "%s: after execv(%s), wait() returned: %m", 824185029Spjd pp->printer, prog); 825185029Spjd } 826185029Spjd } 827248571Smm child = 0; 828185029Spjd prchild = 0; 829248571Smm if (stopped) { /* restart output filter */ 830185029Spjd if (kill(ofilter, SIGCONT) < 0) { 831185029Spjd syslog(LOG_ERR, "cannot restart output filter"); 832185029Spjd exit(1); 833185029Spjd } 834185029Spjd } 835219089Spjd pp->tof = 0; 836248571Smm 837185029Spjd /* Copy the filter's output to "lf" logfile */ 838248571Smm if ((fp = fopen(tempstderr, "r"))) { 839185029Spjd while (fgets(buf, sizeof(buf), fp)) 840248571Smm fputs(buf, stderr); 841185029Spjd fclose(fp); 842248571Smm } 843248571Smm 844248571Smm if (!WIFEXITED(status)) { 845219089Spjd syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 846248571Smm pp->printer, format, status.w_termsig); 847249195Smm return(ERROR); 848248571Smm } 849248571Smm switch (status.w_retcode) { 850248571Smm case 0: 851248571Smm pp->tof = 1; 852219089Spjd return(OK); 853248571Smm case 1: 854248571Smm return(REPRINT); 855248571Smm case 2: 856248571Smm return(ERROR); 857248571Smm default: 858248571Smm syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 859248571Smm pp->printer, format, status.w_retcode); 860248571Smm return(FILTERERR); 861219089Spjd } 862248571Smm} 863248571Smm 864248571Smm/* 865248571Smm * Send the daemon control file (cf) and any data files. 866248571Smm * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 867248571Smm * 0 if all is well. 868248571Smm */ 869248571Smmstatic int 870248571Smmsendit(struct printer *pp, char *file) 871248571Smm{ 872248571Smm register int i, err = OK; 873248571Smm char *cp, last[BUFSIZ]; 874248571Smm 875248571Smm /* 876248571Smm * open control file 877248571Smm */ 878248571Smm if ((cfp = fopen(file, "r")) == NULL) 879248571Smm return(OK); 880222050Smm 881219089Spjd /* initialize job-specific count of datafiles processed */ 882185029Spjd job_dfcnt = 0; 883185029Spjd 884185029Spjd /* 885185029Spjd * read the control file for work to do 886185029Spjd * 887219089Spjd * file format -- first character in the line is a command 888185029Spjd * rest of the line is the argument. 889185029Spjd * commands of interest are: 890185029Spjd * 891185029Spjd * a-z -- "file name" name of file to print 892185029Spjd * U -- "unlink" name of file to remove 893185029Spjd * (after we print it. (Pass 2 only)). 894185029Spjd */ 895185029Spjd 896185029Spjd /* 897185029Spjd * pass 1 898185029Spjd */ 899185029Spjd while (getline(cfp)) { 900185029Spjd again: 901185029Spjd if (line[0] == 'S') { 902185029Spjd cp = line+1; 903185029Spjd i = 0; 904185029Spjd while (*cp >= '0' && *cp <= '9') 905185029Spjd i = i * 10 + (*cp++ - '0'); 906185029Spjd fdev = i; 907185029Spjd cp++; 908185029Spjd i = 0; 909185029Spjd while (*cp >= '0' && *cp <= '9') 910185029Spjd i = i * 10 + (*cp++ - '0'); 911185029Spjd fino = i; 912185029Spjd } else if (line[0] == 'H') { 913248571Smm strlcpy(origin_host, line + 1, sizeof(origin_host)); 914185029Spjd if (class[0] == '\0') { 915248571Smm strlcpy(class, line + 1, sizeof(class)); 916185029Spjd } 917240870Spjd } else if (line[0] == 'P') { 918240870Spjd strlcpy(logname, line + 1, sizeof(logname)); 919240870Spjd if (pp->restricted) { /* restricted */ 920240870Spjd if (getpwnam(logname) == NULL) { 921240870Spjd sendmail(pp, line+1, NOACCT); 922240870Spjd err = ERROR; 923240870Spjd break; 924240870Spjd } 925240870Spjd } 926240870Spjd } else if (line[0] == 'I') { 927240870Spjd strlcpy(indent+2, line + 1, sizeof(indent) - 2); 928240870Spjd } else if (line[0] >= 'a' && line[0] <= 'z') { 929240870Spjd strcpy(last, line); 930240870Spjd while ((i = getline(cfp)) != 0) 931240870Spjd if (strcmp(last, line)) 932240870Spjd break; 933240870Spjd switch (sendfile(pp, '\3', last+1, *last)) { 934240870Spjd case OK: 935240870Spjd if (i) 936240870Spjd goto again; 937185029Spjd break; 938185029Spjd case REPRINT: 939248571Smm (void) fclose(cfp); 940185029Spjd return(REPRINT); 941248571Smm case ACCESS: 942185029Spjd sendmail(pp, logname, ACCESS); 943248571Smm case ERROR: 944248571Smm err = ERROR; 945185029Spjd } 946185029Spjd break; 947185029Spjd } 948185029Spjd } 949248571Smm if (err == OK && sendfile(pp, '\2', file, '\0') > 0) { 950185029Spjd (void) fclose(cfp); 951185029Spjd return(REPRINT); 952248571Smm } 953248571Smm /* 954248571Smm * pass 2 955185029Spjd */ 956248571Smm fseek(cfp, 0L, 0); 957248571Smm while (getline(cfp)) 958185029Spjd if (line[0] == 'U' && !strchr(line+1, '/')) 959248571Smm (void) unlink(line+1); 960248571Smm /* 961185029Spjd * clean-up in case another control file exists 962248571Smm */ 963185029Spjd (void) fclose(cfp); 964185029Spjd (void) unlink(file); 965248571Smm return(err); 966248571Smm} 967248571Smm 968248571Smm/* 969185029Spjd * Send a data file to the remote machine and spool it. 970185029Spjd * Return positive if we should try resending. 971185029Spjd */ 972248571Smmstatic int 973185029Spjdsendfile(struct printer *pp, int type, char *file, char format) 974185029Spjd{ 975248571Smm int i, amt; 976248571Smm struct stat stb; 977248571Smm char *av[15], *filtcmd; 978185029Spjd char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; 979248571Smm int filtstat, narg, resp, sfd, sfres, sizerr, statrc; 980248571Smm 981248571Smm statrc = lstat(file, &stb); 982168404Spjd if (statrc < 0) { 983248571Smm syslog(LOG_ERR, "%s: error from lstat(%s): %m", 984185029Spjd pp->printer, file); 985185029Spjd return (ERROR); 986168404Spjd } 987248571Smm sfd = open(file, O_RDONLY); 988185029Spjd if (sfd < 0) { 989248571Smm syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", 990185029Spjd pp->printer, file); 991185029Spjd return (ERROR); 992185029Spjd } 993185029Spjd /* 994185029Spjd * Check to see if data file is a symbolic link. If so, it should 995185029Spjd * still point to the same file or someone is trying to print something 996185029Spjd * he shouldn't. 997185029Spjd */ 998185029Spjd if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && 999185029Spjd (stb.st_dev != fdev || stb.st_ino != fino)) { 1000185029Spjd close(sfd); 1001185029Spjd return (ACCESS); 1002185029Spjd } 1003168404Spjd 1004168404Spjd /* Everything seems OK for reading the file, now to send it */ 1005185029Spjd filtcmd = NULL; 1006185029Spjd sizerr = 0; 1007185029Spjd tfd = -1; 1008219089Spjd if (type == '\3') { 1009219089Spjd /* 1010185029Spjd * Type == 3 means this is a datafile, not a control file. 1011185029Spjd * Increment the counter of data-files in this job, and 1012248571Smm * then check for input or output filters (which are only 1013248571Smm * applied to datafiles, not control files). 1014248571Smm */ 1015248571Smm job_dfcnt++; 1016185029Spjd 1017248571Smm /* 1018185029Spjd * Note that here we are filtering datafiles, one at a time, 1019248571Smm * as they are sent to the remote machine. Here, the *only* 1020248571Smm * difference between an input filter (`if=') and an output 1021248571Smm * filter (`of=') is the argument list that the filter is 1022185029Spjd * started up with. Here, the output filter is executed 1023248571Smm * for each individual file as it is sent. This is not the 1024249195Smm * same as local print queues, where the output filter is 1025248571Smm * started up once, and then all jobs are passed thru that 1026248571Smm * single invocation of the output filter. 1027248571Smm * 1028248571Smm * Also note that a queue for a remote-machine can have an 1029248571Smm * input filter or an output filter, but not both. 1030248571Smm */ 1031249195Smm if (pp->filters[LPF_INPUT]) { 1032248571Smm filtcmd = pp->filters[LPF_INPUT]; 1033248571Smm av[0] = filtcmd; 1034248571Smm narg = 0; 1035248571Smm strcpy(opt_c, "-c"); 1036248571Smm strcpy(opt_h, "-h"); 1037248571Smm strcpy(opt_n, "-n"); 1038248571Smm if (format == 'l') 1039248571Smm av[++narg] = opt_c; 1040248571Smm av[++narg] = width; 1041185029Spjd av[++narg] = length; 1042185029Spjd av[++narg] = indent; 1043248571Smm av[++narg] = opt_n; 1044185029Spjd av[++narg] = logname; 1045248571Smm av[++narg] = opt_h; 1046185029Spjd av[++narg] = origin_host; 1047248571Smm av[++narg] = pp->acct_file; 1048248571Smm av[++narg] = NULL; 1049248571Smm } else if (pp->filters[LPF_OUTPUT]) { 1050248571Smm filtcmd = pp->filters[LPF_OUTPUT]; 1051248571Smm av[0] = filtcmd; 1052249195Smm narg = 0; 1053248571Smm av[++narg] = width; 1054248571Smm av[++narg] = length; 1055248571Smm av[++narg] = NULL; 1056248571Smm } 1057248571Smm } 1058248571Smm if (filtcmd) { 1059219089Spjd /* 1060219089Spjd * If there is an input or output filter, we have to run 1061248571Smm * the datafile thru that filter and store the result as 1062185029Spjd * a temporary spool file, because the protocol requires 1063185029Spjd * that we send the remote host the file-size before we 1064185029Spjd * start to send any of the data. 1065185029Spjd */ 1066185029Spjd strcpy(tfile, TFILENAME); 1067248571Smm tfd = mkstemp(tfile); 1068248571Smm if (tfd == -1) { 1069248571Smm syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, 1070248571Smm TFILENAME); 1071185029Spjd sfres = ERROR; 1072185029Spjd goto return_sfres; 1073185029Spjd } 1074185029Spjd filtstat = execfilter(pp, filtcmd, av, sfd, tfd); 1075185029Spjd 1076248571Smm /* process the return-code from the filter */ 1077248571Smm switch (filtstat) { 1078185029Spjd case 0: 1079185029Spjd break; 1080168404Spjd case 1: 1081168404Spjd sfres = REPRINT; 1082168404Spjd goto return_sfres; 1083168404Spjd case 2: 1084168404Spjd sfres = ERROR; 1085168404Spjd goto return_sfres; 1086248571Smm default: 1087168404Spjd syslog(LOG_WARNING, 1088168404Spjd "%s: filter '%c' exited (retcode=%d)", 1089249195Smm pp->printer, format, filtstat); 1090168404Spjd sfres = FILTERERR; 1091168404Spjd goto return_sfres; 1092168404Spjd } 1093168404Spjd statrc = fstat(tfd, &stb); /* to find size of tfile */ 1094168404Spjd if (statrc < 0) { 1095219089Spjd syslog(LOG_ERR, 1096185029Spjd "%s: error processing 'if', fstat(%s): %m", 1097219089Spjd pp->printer, tfile); 1098185029Spjd sfres = ERROR; 1099248571Smm goto return_sfres; 1100185029Spjd } 1101219089Spjd close(sfd); 1102185029Spjd sfd = tfd; 1103219089Spjd lseek(sfd, 0, SEEK_SET); 1104219089Spjd } 1105219089Spjd 1106219089Spjd (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 1107219089Spjd amt = strlen(buf); 1108185029Spjd for (i = 0; ; i++) { 1109185029Spjd if (write(pfd, buf, amt) != amt || 1110185029Spjd (resp = response(pp)) < 0 || resp == '\1') { 1111168404Spjd sfres = REPRINT; 1112168404Spjd goto return_sfres; 1113168404Spjd } else if (resp == '\0') 1114168404Spjd break; 1115248571Smm if (i == 0) 1116168404Spjd pstatus(pp, 1117168404Spjd "no space on remote; waiting for queue to drain"); 1118168404Spjd if (i == 10) 1119168404Spjd syslog(LOG_ALERT, "%s: can't send to %s; queue full", 1120248571Smm pp->printer, pp->remote_host); 1121185029Spjd sleep(5 * 60); 1122248571Smm } 1123185029Spjd if (i) 1124185029Spjd pstatus(pp, "sending to %s", pp->remote_host); 1125185029Spjd if (type == '\3') 1126185029Spjd trstat_init(pp, file, job_dfcnt); 1127185029Spjd for (i = 0; i < stb.st_size; i += BUFSIZ) { 1128249195Smm amt = BUFSIZ; 1129185029Spjd if (i + amt > stb.st_size) 1130185029Spjd amt = stb.st_size - i; 1131185029Spjd if (sizerr == 0 && read(sfd, buf, amt) != amt) 1132219089Spjd sizerr = 1; 1133219089Spjd if (write(pfd, buf, amt) != amt) { 1134185029Spjd sfres = REPRINT; 1135185029Spjd goto return_sfres; 1136185029Spjd } 1137168404Spjd } 1138248571Smm 1139209962Smm if (sizerr) { 1140248571Smm syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); 1141209962Smm /* tell recvjob to ignore this file */ 1142209962Smm (void) write(pfd, "\1", 1); 1143209962Smm sfres = ERROR; 1144209962Smm goto return_sfres; 1145249195Smm } 1146209962Smm if (write(pfd, "", 1) != 1 || response(pp)) { 1147209962Smm sfres = REPRINT; 1148209962Smm goto return_sfres; 1149209962Smm } 1150209962Smm if (type == '\3') 1151209962Smm trstat_write(pp, TR_SENDING, stb.st_size, logname, 1152209962Smm pp->remote_host, origin_host); 1153209962Smm sfres = OK; 1154209962Smm 1155209962Smmreturn_sfres: 1156209962Smm (void)close(sfd); 1157209962Smm if (tfd != -1) { 1158209962Smm /* 1159209962Smm * If tfd is set, then it is the same value as sfd, and 1160209962Smm * therefore it is already closed at this point. All 1161209962Smm * we need to do is remove the temporary file. 1162209962Smm */ 1163209962Smm tfd = -1; 1164209962Smm unlink(tfile); 1165209962Smm } 1166209962Smm return (sfres); 1167248571Smm} 1168209962Smm 1169248571Smm/* 1170209962Smm * This routine is called to execute one of the filters as was 1171209962Smm * specified in a printcap entry. While the child-process will read 1172209962Smm * all of 'infd', it is up to the caller to close that file descriptor 1173209962Smm * in the parent process. 1174249195Smm */ 1175209962Smmstatic int 1176209962Smmexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) 1177209962Smm{ 1178209962Smm int errfd, fpid, wpid; 1179209962Smm FILE *errfp; 1180248571Smm union wait status; /* XXX */ 1181209962Smm char buf[BUFSIZ], *slash; 1182248571Smm 1183209962Smm fpid = dofork(pp, DORETURN); 1184219089Spjd if (fpid != 0) { 1185219089Spjd /* 1186209962Smm * This is the parent process, which just waits for the child 1187209962Smm * to complete and then returns the result. Note that it is 1188248571Smm * the child process which reads the input stream. 1189219089Spjd */ 1190248571Smm if (fpid < 0) 1191219089Spjd status.w_retcode = 100; 1192248571Smm else { 1193248571Smm while ((wpid = wait((int *)&status)) > 0 && 1194248571Smm wpid != fpid) 1195248571Smm ; 1196248571Smm if (wpid < 0) { 1197248571Smm status.w_retcode = 100; 1198249195Smm syslog(LOG_WARNING, 1199248571Smm "%s: after execv(%s), wait() returned: %m", 1200248571Smm pp->printer, f_cmd); 1201248571Smm } 1202248571Smm } 1203248571Smm 1204248571Smm /* 1205248571Smm * Copy everything the filter wrote to stderr from our 1206248571Smm * temporary errors file to the "lf=" logfile. 1207248571Smm */ 1208248571Smm errfp = fopen(tempstderr, "r"); 1209248571Smm if (errfp) { 1210248571Smm while (fgets(buf, sizeof(buf), errfp)) 1211248571Smm fputs(buf, stderr); 1212219089Spjd fclose(errfp); 1213219089Spjd } 1214248571Smm 1215219089Spjd return (status.w_retcode); 1216248571Smm } 1217219089Spjd 1218248571Smm /* 1219248571Smm * This is the child process, which is the one that executes the 1220248571Smm * given filter. 1221248571Smm */ 1222248571Smm /* 1223248571Smm * If the first parameter has any slashes in it, then change it 1224248571Smm * to point to the first character after the last slash. 1225248571Smm */ 1226248571Smm slash = strrchr(f_av[0], '/'); 1227248571Smm if (slash != NULL) 1228248571Smm f_av[0] = slash + 1; 1229248571Smm /* 1230248571Smm * XXX - in the future, this should setup an explicit list of 1231248571Smm * environment variables and use execve()! 1232248571Smm */ 1233219089Spjd 1234219089Spjd /* 1235168404Spjd * Setup stdin, stdout, and stderr as we want them when the filter 1236219089Spjd * is running. Stderr is setup so it points to a temporary errors 1237219089Spjd * file, and the parent process will copy that temporary file to 1238219089Spjd * the real logfile after the filter completes. 1239248571Smm */ 1240219089Spjd dup2(infd, 0); 1241219089Spjd dup2(outfd, 1); 1242219089Spjd errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); 1243219089Spjd if (errfd >= 0) 1244219089Spjd dup2(errfd, 2); 1245219089Spjd closelog(); 1246219089Spjd closeallfds(3); 1247219089Spjd execv(f_cmd, f_av); 1248219089Spjd syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); 1249219089Spjd exit(2); 1250219089Spjd /* NOTREACHED */ 1251219089Spjd} 1252248571Smm 1253248571Smm/* 1254248571Smm * Check to make sure there have been no errors and that both programs 1255248571Smm * are in sync with eachother. 1256248571Smm * Return non-zero if the connection was lost. 1257248571Smm */ 1258248571Smmstatic char 1259219089Spjdresponse(const struct printer *pp) 1260219089Spjd{ 1261219089Spjd char resp; 1262219089Spjd 1263168404Spjd if (read(pfd, &resp, 1) != 1) { 1264168404Spjd syslog(LOG_INFO, "%s: lost connection", pp->printer); 1265168404Spjd return(-1); 1266219089Spjd } 1267168404Spjd return(resp); 1268168404Spjd} 1269168404Spjd 1270185029Spjd/* 1271168404Spjd * Banner printing stuff 1272168404Spjd */ 1273168404Spjdstatic void 1274168404Spjdbanner(struct printer *pp, char *name1, char *name2) 1275185029Spjd{ 1276249195Smm time_t tvec; 1277168404Spjd 1278168404Spjd time(&tvec); 1279168404Spjd if (!pp->no_formfeed && !pp->tof) 1280219089Spjd (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 1281219089Spjd if (pp->short_banner) { /* short banner only */ 1282168404Spjd if (class[0]) { 1283168404Spjd (void) write(ofd, class, strlen(class)); 1284168404Spjd (void) write(ofd, ":", 1); 1285168404Spjd } 1286185029Spjd (void) write(ofd, name1, strlen(name1)); 1287168404Spjd (void) write(ofd, " Job: ", 7); 1288168404Spjd (void) write(ofd, name2, strlen(name2)); 1289168404Spjd (void) write(ofd, " Date: ", 8); 1290168404Spjd (void) write(ofd, ctime(&tvec), 24); 1291168404Spjd (void) write(ofd, "\n", 1); 1292168404Spjd } else { /* normal banner */ 1293185029Spjd (void) write(ofd, "\n\n\n", 3); 1294168404Spjd scan_out(pp, ofd, name1, '\0'); 1295168404Spjd (void) write(ofd, "\n\n", 2); 1296168404Spjd scan_out(pp, ofd, name2, '\0'); 1297248571Smm if (class[0]) { 1298248571Smm (void) write(ofd,"\n\n\n",3); 1299248571Smm scan_out(pp, ofd, class, '\0'); 1300248571Smm } 1301248571Smm (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 1302248571Smm (void) write(ofd, name2, strlen(name2)); 1303168404Spjd (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 1304248571Smm (void) write(ofd, ctime(&tvec), 24); 1305219089Spjd (void) write(ofd, "\n", 1); 1306219089Spjd } 1307219089Spjd if (!pp->no_formfeed) 1308248571Smm (void) write(ofd, pp->form_feed, strlen(pp->form_feed)); 1309219089Spjd pp->tof = 1; 1310248571Smm} 1311219089Spjd 1312219089Spjdstatic char * 1313219089Spjdscnline(int key, char *p, int c) 1314248571Smm{ 1315249195Smm register int scnwidth; 1316219089Spjd 1317248571Smm for (scnwidth = WIDTH; --scnwidth;) { 1318248571Smm key <<= 1; 1319219089Spjd *p++ = key & 0200 ? c : BACKGND; 1320219089Spjd } 1321248571Smm return (p); 1322219089Spjd} 1323248571Smm 1324219089Spjd#define TRC(q) (((q)-' ')&0177) 1325248571Smm 1326248571Smmstatic void 1327219089Spjdscan_out(struct printer *pp, int scfd, char *scsp, int dlm) 1328248571Smm{ 1329248571Smm register char *strp; 1330248571Smm register int nchrs, j; 1331219089Spjd char outbuf[LINELEN+1], *sp, c, cc; 1332219089Spjd int d, scnhgt; 1333219089Spjd 1334219089Spjd for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 1335219089Spjd strp = &outbuf[0]; 1336219089Spjd sp = scsp; 1337168404Spjd for (nchrs = 0; ; ) { 1338168404Spjd d = dropit(c = TRC(cc = *sp++)); 1339168404Spjd if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 1340219089Spjd for (j = WIDTH; --j;) 1341168404Spjd *strp++ = BACKGND; 1342168404Spjd else 1343248571Smm strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); 1344168404Spjd if (*sp == dlm || *sp == '\0' || 1345168404Spjd nchrs++ >= pp->page_width/(WIDTH+1)-1) 1346168404Spjd break; 1347168404Spjd *strp++ = BACKGND; 1348168404Spjd *strp++ = BACKGND; 1349168404Spjd } 1350168404Spjd while (*--strp == BACKGND && strp >= outbuf) 1351168404Spjd ; 1352168404Spjd strp++; 1353168404Spjd *strp++ = '\n'; 1354168404Spjd (void) write(scfd, outbuf, strp-outbuf); 1355168404Spjd } 1356248571Smm} 1357219089Spjd 1358219089Spjdstatic int 1359249195Smmdropit(int c) 1360248571Smm{ 1361168404Spjd switch(c) { 1362168404Spjd 1363168404Spjd case TRC('_'): 1364248571Smm case TRC(';'): 1365168404Spjd case TRC(','): 1366168404Spjd case TRC('g'): 1367168404Spjd case TRC('j'): 1368168404Spjd case TRC('p'): 1369219089Spjd case TRC('q'): 1370209962Smm case TRC('y'): 1371209962Smm return (DROP); 1372209962Smm 1373209962Smm default: 1374219089Spjd return (0); 1375248571Smm } 1376209962Smm} 1377219089Spjd 1378219089Spjd/* 1379249195Smm * sendmail --- 1380219089Spjd * tell people about job completion 1381209962Smm */ 1382219089Spjdstatic void 1383219089Spjdsendmail(struct printer *pp, char *userid, int bombed) 1384219089Spjd{ 1385219089Spjd register int i; 1386209962Smm int p[2], s; 1387249195Smm register const char *cp; 1388209962Smm struct stat stb; 1389219089Spjd FILE *fp; 1390219089Spjd 1391209962Smm pipe(p); 1392209962Smm if ((s = dofork(pp, DORETURN)) == 0) { /* child */ 1393209962Smm dup2(p[0], 0); 1394209962Smm closelog(); 1395209962Smm closeallfds(3); 1396209962Smm if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) 1397236884Smm cp++; 1398236884Smm else 1399209962Smm cp = _PATH_SENDMAIL; 1400209962Smm execl(_PATH_SENDMAIL, cp, "-t", (char *)0); 1401219089Spjd _exit(0); 1402209962Smm } else if (s > 0) { /* parent */ 1403209962Smm dup2(p[1], 1); 1404209962Smm printf("To: %s@%s\n", userid, origin_host); 1405219089Spjd printf("Subject: %s printer job \"%s\"\n", pp->printer, 1406219089Spjd *jobname ? jobname : "<unknown>"); 1407209962Smm printf("Reply-To: root@%s\n\n", local_host); 1408219089Spjd printf("Your printer job "); 1409219089Spjd if (*jobname) 1410219089Spjd printf("(%s) ", jobname); 1411209962Smm 1412209962Smm switch (bombed) { 1413209962Smm case OK: 1414209962Smm cp = "OK"; 1415209962Smm printf("\ncompleted successfully\n"); 1416219089Spjd break; 1417249195Smm default: 1418209962Smm case FATALERR: 1419209962Smm cp = "FATALERR"; 1420209962Smm printf("\ncould not be printed\n"); 1421209962Smm break; 1422209962Smm case NOACCT: 1423209962Smm cp = "NOACCT"; 1424209962Smm printf("\ncould not be printed without an account on %s\n", 1425209962Smm local_host); 1426209962Smm break; 1427209962Smm case FILTERERR: 1428209962Smm cp = "FILTERERR"; 1429209962Smm if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 1430209962Smm || (fp = fopen(tempstderr, "r")) == NULL) { 1431219089Spjd printf("\nhad some errors and may not have printed\n"); 1432209962Smm break; 1433209962Smm } 1434209962Smm printf("\nhad the following errors and may not have printed:\n"); 1435209962Smm while ((i = getc(fp)) != EOF) 1436209962Smm putchar(i); 1437168404Spjd (void) fclose(fp); 1438168404Spjd break; 1439168404Spjd case ACCESS: 1440185029Spjd cp = "ACCESS"; 1441185029Spjd printf("\nwas not printed because it was not linked to the original file\n"); 1442185029Spjd } 1443168404Spjd fflush(stdout); 1444185029Spjd (void) close(1); 1445219089Spjd } else { 1446168404Spjd syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); 1447168404Spjd return; 1448185029Spjd } 1449219089Spjd (void) close(p[0]); 1450219089Spjd (void) close(p[1]); 1451185029Spjd wait(NULL); 1452185029Spjd syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 1453185029Spjd userid, *jobname ? jobname : "<unknown>", pp->printer, cp); 1454168404Spjd} 1455185029Spjd 1456185029Spjd/* 1457185029Spjd * dofork - fork with retries on failure 1458185029Spjd */ 1459185029Spjdstatic int 1460185029Spjddofork(const struct printer *pp, int action) 1461236884Smm{ 1462249195Smm int i, fail, forkpid; 1463185029Spjd struct passwd *pwd; 1464185029Spjd 1465185029Spjd forkpid = -1; 1466185029Spjd if (daemon_uname == NULL) { 1467185029Spjd pwd = getpwuid(pp->daemon_user); 1468185029Spjd if (pwd == NULL) { 1469185029Spjd syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", 1470185029Spjd pp->printer, pp->daemon_user); 1471185029Spjd goto error_ret; 1472185029Spjd } 1473185029Spjd daemon_uname = strdup(pwd->pw_name); 1474185029Spjd daemon_defgid = pwd->pw_gid; 1475185029Spjd } 1476185029Spjd 1477185029Spjd for (i = 0; i < 20; i++) { 1478248571Smm forkpid = fork(); 1479185029Spjd if (forkpid < 0) { 1480185029Spjd sleep((unsigned)(i*i)); 1481185029Spjd continue; 1482248571Smm } 1483185029Spjd /* 1484185029Spjd * Child should run as daemon instead of root 1485185029Spjd */ 1486185029Spjd if (forkpid == 0) { 1487219089Spjd errno = 0; 1488219089Spjd fail = initgroups(daemon_uname, daemon_defgid); 1489185029Spjd if (fail) { 1490185029Spjd syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", 1491185029Spjd pp->printer, daemon_uname, daemon_defgid); 1492185029Spjd break; 1493185029Spjd } 1494168404Spjd fail = setgid(daemon_defgid); 1495185029Spjd if (fail) { 1496168404Spjd syslog(LOG_ERR, "%s: setgid(%u): %m", 1497168404Spjd pp->printer, daemon_defgid); 1498168404Spjd break; 1499168404Spjd } 1500168404Spjd fail = setuid(pp->daemon_user); 1501168404Spjd if (fail) { 1502168404Spjd syslog(LOG_ERR, "%s: setuid(%ld): %m", 1503185029Spjd pp->printer, pp->daemon_user); 1504185029Spjd break; 1505185029Spjd } 1506219089Spjd } 1507219089Spjd return forkpid; 1508185029Spjd } 1509168404Spjd 1510168404Spjd /* 1511168404Spjd * An error occurred. If the error is in the child process, then 1512168404Spjd * this routine MUST always exit(). DORETURN only effects how 1513168404Spjd * errors should be handled in the parent process. 1514185029Spjd */ 1515168404Spjderror_ret: 1516219089Spjd if (forkpid == 0) { 1517168404Spjd syslog(LOG_ERR, "%s: dofork(): aborting child process...", 1518185029Spjd pp->printer); 1519219089Spjd exit(1); 1520168404Spjd } 1521168404Spjd syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); 1522185029Spjd 1523219089Spjd sleep(1); /* throttle errors, as a safety measure */ 1524219089Spjd switch (action) { 1525185029Spjd case DORETURN: 1526185029Spjd return -1; 1527185029Spjd default: 1528185029Spjd syslog(LOG_ERR, "bad action (%d) to dofork", action); 1529168404Spjd /* FALLTHROUGH */ 1530168404Spjd case DOABORT: 1531249195Smm exit(1); 1532168404Spjd } 1533219089Spjd /*NOTREACHED*/ 1534168404Spjd} 1535219089Spjd 1536219089Spjd/* 1537219089Spjd * Kill child processes to abort current job. 1538219089Spjd */ 1539219089Spjdstatic void 1540219089Spjdabortpr(int signo __unused) 1541219089Spjd{ 1542168404Spjd 1543168404Spjd (void) unlink(tempstderr); 1544185029Spjd kill(0, SIGINT); 1545185029Spjd if (ofilter > 0) 1546185029Spjd kill(ofilter, SIGCONT); 1547168404Spjd while (wait(NULL) > 0) 1548168404Spjd ; 1549168404Spjd if (ofilter > 0 && tfd != -1) 1550168404Spjd unlink(tfile); 1551168404Spjd exit(0); 1552168404Spjd} 1553185029Spjd 1554185029Spjdstatic void 1555207670Smminit(struct printer *pp) 1556185029Spjd{ 1557185029Spjd char *s; 1558207670Smm 1559219089Spjd sprintf(&width[2], "%ld", pp->page_width); 1560219089Spjd sprintf(&length[2], "%ld", pp->page_length); 1561185029Spjd sprintf(&pxwidth[2], "%ld", pp->page_pwidth); 1562168404Spjd sprintf(&pxlength[2], "%ld", pp->page_plength); 1563168404Spjd if ((s = checkremote(pp)) != 0) { 1564168404Spjd syslog(LOG_WARNING, "%s", s); 1565168404Spjd free(s); 1566168404Spjd } 1567168404Spjd} 1568168404Spjd 1569168404Spjdvoid 1570168404Spjdstartprinting(const char *printer) 1571249195Smm{ 1572168404Spjd struct printer myprinter, *pp = &myprinter; 1573168404Spjd int status; 1574168404Spjd 1575168404Spjd init_printer(pp); 1576168404Spjd status = getprintcap(printer, pp); 1577168404Spjd switch(status) { 1578168404Spjd case PCAPERR_OSERR: 1579168404Spjd syslog(LOG_ERR, "can't open printer description file: %m"); 1580236884Smm exit(1); 1581236884Smm case PCAPERR_NOTFOUND: 1582236884Smm syslog(LOG_ERR, "unknown printer: %s", printer); 1583236884Smm exit(1); 1584236884Smm case PCAPERR_TCLOOP: 1585236884Smm fatal(pp, "potential reference loop detected in printcap file"); 1586236884Smm default: 1587236884Smm break; 1588236884Smm } 1589168404Spjd printjob(pp); 1590168404Spjd} 1591168404Spjd 1592168404Spjd/* 1593168404Spjd * Acquire line printer or remote connection. 1594168404Spjd */ 1595168404Spjdstatic void 1596168404Spjdopenpr(const struct printer *pp) 1597168404Spjd{ 1598168404Spjd int p[2]; 1599168404Spjd char *cp; 1600168404Spjd 1601168404Spjd if (pp->remote) { 1602168404Spjd openrem(pp); 1603168404Spjd /* 1604168404Spjd * Lpd does support the setting of 'of=' filters for 1605168404Spjd * jobs going to remote machines, but that does not 1606168404Spjd * have the same meaning as 'of=' does when handling 1607168404Spjd * local print queues. For remote machines, all 'of=' 1608168404Spjd * filter processing is handled in sendfile(), and that 1609168404Spjd * does not use these global "output filter" variables. 1610168404Spjd */ 1611168404Spjd ofd = -1; 1612168404Spjd ofilter = 0; 1613168404Spjd return; 1614168404Spjd } else if (*pp->lp) { 1615168404Spjd if ((cp = strchr(pp->lp, '@')) != NULL) 1616168404Spjd opennet(pp); 1617168404Spjd else 1618168404Spjd opentty(pp); 1619168404Spjd } else { 1620168404Spjd syslog(LOG_ERR, "%s: no line printer device or host name", 1621168404Spjd pp->printer); 1622168404Spjd exit(1); 1623168404Spjd } 1624168404Spjd 1625168404Spjd /* 1626185029Spjd * Start up an output filter, if needed. 1627219089Spjd */ 1628168404Spjd if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !ofilter) { 1629168404Spjd pipe(p); 1630168404Spjd if (pp->remote) { 1631168404Spjd strcpy(tfile, TFILENAME); 1632168404Spjd tfd = mkstemp(tfile); 1633168404Spjd } 1634168404Spjd if ((ofilter = dofork(pp, DOABORT)) == 0) { /* child */ 1635249195Smm dup2(p[0], 0); /* pipe is std in */ 1636168404Spjd /* tfile/printer is stdout */ 1637168404Spjd dup2(pp->remote ? tfd : pfd, 1); 1638168404Spjd closelog(); 1639168404Spjd closeallfds(3); 1640168404Spjd if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) 1641168404Spjd cp = pp->filters[LPF_OUTPUT]; 1642168404Spjd else 1643219089Spjd cp++; 1644219089Spjd execl(pp->filters[LPF_OUTPUT], cp, width, length, 1645219089Spjd (char *)0); 1646219089Spjd syslog(LOG_ERR, "%s: %s: %m", pp->printer, 1647219089Spjd pp->filters[LPF_OUTPUT]); 1648168404Spjd exit(1); 1649219089Spjd } 1650168404Spjd (void) close(p[0]); /* close input side */ 1651168404Spjd ofd = p[1]; /* use pipe for output */ 1652168404Spjd } else { 1653168404Spjd ofd = pfd; 1654168404Spjd ofilter = 0; 1655168404Spjd } 1656168404Spjd} 1657219089Spjd 1658219089Spjd/* 1659219089Spjd * Printer connected directly to the network 1660219089Spjd * or to a terminal server on the net 1661168404Spjd */ 1662168404Spjdstatic void 1663168404Spjdopennet(const struct printer *pp) 1664168404Spjd{ 1665168404Spjd register int i; 1666168404Spjd int resp; 1667168404Spjd u_long port; 1668168404Spjd char *ep; 1669168404Spjd void (*savealrm)(int); 1670168404Spjd 1671168404Spjd port = strtoul(pp->lp, &ep, 0); 1672168404Spjd if (*ep != '@' || port > 65535) { 1673168404Spjd syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, 1674168404Spjd pp->lp); 1675168404Spjd exit(1); 1676168404Spjd } 1677168404Spjd ep++; 1678168404Spjd 1679168404Spjd for (i = 1; ; i = i < 256 ? i << 1 : i) { 1680168404Spjd resp = -1; 1681168404Spjd savealrm = signal(SIGALRM, alarmhandler); 1682168404Spjd alarm(pp->conn_timeout); 1683168404Spjd pfd = getport(pp, ep, port); 1684168404Spjd alarm(0); 1685168404Spjd (void)signal(SIGALRM, savealrm); 1686168404Spjd if (pfd < 0 && errno == ECONNREFUSED) 1687168404Spjd resp = 1; 1688168404Spjd else if (pfd >= 0) { 1689168404Spjd /* 1690236884Smm * need to delay a bit for rs232 lines 1691236884Smm * to stabilize in case printer is 1692185029Spjd * connected via a terminal server 1693249195Smm */ 1694185029Spjd delay(500); 1695168404Spjd break; 1696185029Spjd } 1697168404Spjd if (i == 1) { 1698168404Spjd if (resp < 0) 1699168404Spjd pstatus(pp, "waiting for %s to come up", 1700168404Spjd pp->lp); 1701168404Spjd else 1702168404Spjd pstatus(pp, 1703168404Spjd "waiting for access to printer on %s", 1704168404Spjd pp->lp); 1705168404Spjd } 1706168404Spjd sleep(i); 1707168404Spjd } 1708168404Spjd pstatus(pp, "sending to %s port %lu", ep, port); 1709168404Spjd} 1710168404Spjd 1711249195Smm/* 1712168404Spjd * Printer is connected to an RS232 port on this host 1713168404Spjd */ 1714168404Spjdstatic void 1715168404Spjdopentty(const struct printer *pp) 1716185029Spjd{ 1717168404Spjd register int i; 1718249195Smm 1719168404Spjd for (i = 1; ; i = i < 32 ? i << 1 : i) { 1720168404Spjd pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); 1721168404Spjd if (pfd >= 0) { 1722168404Spjd delay(500); 1723168404Spjd break; 1724219089Spjd } 1725219089Spjd if (errno == ENOENT) { 1726219089Spjd syslog(LOG_ERR, "%s: %m", pp->lp); 1727168404Spjd exit(1); 1728168404Spjd } 1729168404Spjd if (i == 1) 1730168404Spjd pstatus(pp, 1731168404Spjd "waiting for %s to become ready (offline?)", 1732168404Spjd pp->printer); 1733168404Spjd sleep(i); 1734168404Spjd } 1735228103Smm if (isatty(pfd)) 1736228103Smm setty(pp); 1737228103Smm pstatus(pp, "%s is ready and printing", pp->printer); 1738228103Smm} 1739228103Smm 1740228103Smm/* 1741228103Smm * Printer is on a remote host 1742228103Smm */ 1743228103Smmstatic void 1744228103Smmopenrem(const struct printer *pp) 1745228103Smm{ 1746228103Smm register int i; 1747228103Smm int resp; 1748228103Smm void (*savealrm)(int); 1749168404Spjd 1750168404Spjd for (i = 1; ; i = i < 256 ? i << 1 : i) { 1751248571Smm resp = -1; 1752168404Spjd savealrm = signal(SIGALRM, alarmhandler); 1753168404Spjd alarm(pp->conn_timeout); 1754219089Spjd pfd = getport(pp, pp->remote_host, 0); 1755219089Spjd alarm(0); 1756219089Spjd (void)signal(SIGALRM, savealrm); 1757219089Spjd if (pfd >= 0) { 1758219089Spjd if ((writel(pfd, "\2", pp->remote_queue, "\n", 1759219089Spjd (char *)0) 1760219089Spjd == 2 + strlen(pp->remote_queue)) 1761219089Spjd && (resp = response(pp)) == 0) 1762168404Spjd break; 1763168404Spjd (void) close(pfd); 1764168404Spjd } 1765219089Spjd if (i == 1) { 1766168404Spjd if (resp < 0) 1767168404Spjd pstatus(pp, "waiting for %s to come up", 1768219089Spjd pp->remote_host); 1769219089Spjd else { 1770168404Spjd pstatus(pp, 1771219089Spjd "waiting for queue to be enabled on %s", 1772219089Spjd pp->remote_host); 1773249195Smm i = 256; 1774219089Spjd } 1775219089Spjd } 1776168404Spjd sleep(i); 1777219089Spjd } 1778168404Spjd pstatus(pp, "sending to %s", pp->remote_host); 1779168404Spjd} 1780168404Spjd 1781168404Spjd/* 1782219089Spjd * setup tty lines. 1783219089Spjd */ 1784219089Spjdstatic void 1785219089Spjdsetty(const struct printer *pp) 1786219089Spjd{ 1787219089Spjd struct termios ttybuf; 1788219089Spjd 1789219089Spjd if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 1790219089Spjd syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); 1791168404Spjd exit(1); 1792219089Spjd } 1793219089Spjd if (tcgetattr(pfd, &ttybuf) < 0) { 1794219089Spjd syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); 1795219089Spjd exit(1); 1796219089Spjd } 1797219089Spjd if (pp->baud_rate > 0) 1798219089Spjd cfsetspeed(&ttybuf, pp->baud_rate); 1799219089Spjd if (pp->mode_set) { 1800219089Spjd char *s = strdup(pp->mode_set), *tmp; 1801219089Spjd 1802249195Smm while ((tmp = strsep(&s, ",")) != NULL) { 1803219089Spjd (void) msearch(tmp, &ttybuf); 1804219089Spjd } 1805219089Spjd } 1806219089Spjd if (pp->mode_set != 0 || pp->baud_rate > 0) { 1807219089Spjd if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 1808219089Spjd syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); 1809219089Spjd } 1810219089Spjd } 1811219089Spjd} 1812168404Spjd 1813168404Spjd#ifdef __STDC__ 1814168404Spjd#include <stdarg.h> 1815168404Spjd#else 1816185029Spjd#include <varargs.h> 1817185029Spjd#endif 1818168404Spjd 1819168404Spjdstatic void 1820168404Spjd#ifdef __STDC__ 1821168404Spjdpstatus(const struct printer *pp, const char *msg, ...) 1822168404Spjd#else 1823185029Spjdpstatus(pp, msg, va_alist) 1824219089Spjd const struct printer *pp; 1825185029Spjd char *msg; 1826185029Spjd va_dcl 1827185029Spjd#endif 1828185029Spjd{ 1829185029Spjd int fd; 1830185029Spjd char *buf; 1831254714Savg va_list ap; 1832168404Spjd#ifdef __STDC__ 1833168404Spjd va_start(ap, msg); 1834185029Spjd#else 1835185029Spjd va_start(ap); 1836185029Spjd#endif 1837185029Spjd 1838185029Spjd umask(0); 1839185029Spjd fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 1840185029Spjd if (fd < 0) { 1841168404Spjd syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->status_file); 1842219089Spjd exit(1); 1843219089Spjd } 1844168404Spjd ftruncate(fd, 0); 1845249195Smm vasprintf(&buf, msg, ap); 1846168404Spjd va_end(ap); 1847254714Savg writel(fd, buf, "\n", (char *)0); 1848168404Spjd close(fd); 1849185029Spjd free(buf); 1850168404Spjd} 1851168404Spjd 1852168404Spjdvoid 1853168404Spjdalarmhandler(int signo __unused) 1854168404Spjd{ 1855168404Spjd /* the signal is ignored */ 1856168404Spjd /* (the '__unused' is just to avoid a compile-time warning) */ 1857219089Spjd} 1858219089Spjd