printjob.c revision 19202
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 3. All advertising materials mentioning features or use of this software 151553Srgrimes * must display the following acknowledgement: 161553Srgrimes * This product includes software developed by the University of 171553Srgrimes * California, Berkeley and its contributors. 181553Srgrimes * 4. Neither the name of the University nor the names of its contributors 191553Srgrimes * may be used to endorse or promote products derived from this software 201553Srgrimes * without specific prior written permission. 211553Srgrimes * 221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321553Srgrimes * SUCH DAMAGE. 331553Srgrimes */ 341553Srgrimes 351553Srgrimes#ifndef lint 361553Srgrimesstatic char copyright[] = 371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 381553Srgrimes The Regents of the University of California. All rights reserved.\n"; 391553Srgrimes#endif /* not lint */ 401553Srgrimes 411553Srgrimes#ifndef lint 4215648Sjoergstatic char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; 431553Srgrimes#endif /* not lint */ 441553Srgrimes 451553Srgrimes 461553Srgrimes/* 471553Srgrimes * printjob -- print jobs in the queue. 481553Srgrimes * 491553Srgrimes * NOTE: the lock file is used to pass information to lpq and lprm. 501553Srgrimes * it does not need to be removed because file locks are dynamic. 511553Srgrimes */ 521553Srgrimes 531553Srgrimes#include <sys/param.h> 541553Srgrimes#include <sys/wait.h> 551553Srgrimes#include <sys/stat.h> 561553Srgrimes#include <sys/types.h> 571553Srgrimes 581553Srgrimes#include <pwd.h> 591553Srgrimes#include <unistd.h> 601553Srgrimes#include <signal.h> 611553Srgrimes#include <syslog.h> 621553Srgrimes#include <fcntl.h> 631553Srgrimes#include <dirent.h> 641553Srgrimes#include <errno.h> 651553Srgrimes#include <stdio.h> 661553Srgrimes#include <string.h> 671553Srgrimes#include <stdlib.h> 6815032Ssef#include <sys/ioctl.h> 6915032Ssef#include <termios.h> 7015703Sjoerg#include <time.h> 711553Srgrimes#include "lp.h" 721553Srgrimes#include "lp.local.h" 731553Srgrimes#include "pathnames.h" 741553Srgrimes#include "extern.h" 751553Srgrimes 761553Srgrimes#define DORETURN 0 /* absorb fork error */ 771553Srgrimes#define DOABORT 1 /* abort if dofork fails */ 781553Srgrimes 791553Srgrimes/* 801553Srgrimes * Error tokens 811553Srgrimes */ 821553Srgrimes#define REPRINT -2 831553Srgrimes#define ERROR -1 841553Srgrimes#define OK 0 851553Srgrimes#define FATALERR 1 861553Srgrimes#define NOACCT 2 871553Srgrimes#define FILTERERR 3 881553Srgrimes#define ACCESS 4 891553Srgrimes 901553Srgrimesstatic dev_t fdev; /* device of file pointed to by symlink */ 911553Srgrimesstatic ino_t fino; /* inode of file pointed to by symlink */ 921553Srgrimesstatic FILE *cfp; /* control file */ 931553Srgrimesstatic int child; /* id of any filters */ 941553Srgrimesstatic int lfd; /* lock file descriptor */ 951553Srgrimesstatic int ofd; /* output filter file descriptor */ 961553Srgrimesstatic int ofilter; /* id of output filter, if any */ 971553Srgrimesstatic int pfd; /* prstatic inter file descriptor */ 981553Srgrimesstatic int pid; /* pid of lpd process */ 991553Srgrimesstatic int prchild; /* id of pr process */ 1001553Srgrimesstatic char title[80]; /* ``pr'' title */ 1011553Srgrimesstatic int tof; /* true if at top of form */ 1021553Srgrimes 1031553Srgrimesstatic char class[32]; /* classification field */ 1041553Srgrimesstatic char fromhost[32]; /* user's host machine */ 1051553Srgrimes /* indentation size in static characters */ 1068857Srgrimesstatic char indent[10] = "-i0"; 1071553Srgrimesstatic char jobname[100]; /* job or file name */ 1081553Srgrimesstatic char length[10] = "-l"; /* page length in lines */ 1091553Srgrimesstatic char logname[32]; /* user's login name */ 1101553Srgrimesstatic char pxlength[10] = "-y"; /* page length in pixels */ 1111553Srgrimesstatic char pxwidth[10] = "-x"; /* page width in pixels */ 1121553Srgrimesstatic char tempfile[] = "errsXXXXXX"; /* file name for filter output */ 1131553Srgrimesstatic char width[10] = "-w"; /* page width in static characters */ 1141553Srgrimes 1151553Srgrimesstatic void abortpr __P((int)); 1161553Srgrimesstatic void banner __P((char *, char *)); 1171553Srgrimesstatic int dofork __P((int)); 1181553Srgrimesstatic int dropit __P((int)); 1191553Srgrimesstatic void init __P((void)); 1201553Srgrimesstatic void openpr __P((void)); 12115648Sjoergstatic void opennet __P((char *)); 12215648Sjoergstatic void opentty __P((void)); 12315648Sjoergstatic void openrem __P((void)); 1241553Srgrimesstatic int print __P((int, char *)); 1251553Srgrimesstatic int printit __P((char *)); 1261553Srgrimesstatic void pstatus __P((const char *, ...)); 1271553Srgrimesstatic char response __P((void)); 1281553Srgrimesstatic void scan_out __P((int, char *, int)); 1291553Srgrimesstatic char *scnline __P((int, char *, int)); 1301553Srgrimesstatic int sendfile __P((int, char *)); 1311553Srgrimesstatic int sendit __P((char *)); 1321553Srgrimesstatic void sendmail __P((char *, int)); 1331553Srgrimesstatic void setty __P((void)); 1341553Srgrimes 1351553Srgrimesvoid 1361553Srgrimesprintjob() 1371553Srgrimes{ 1381553Srgrimes struct stat stb; 1391553Srgrimes register struct queue *q, **qp; 1401553Srgrimes struct queue **queue; 1411553Srgrimes register int i, nitems; 14215648Sjoerg off_t pidoff; 14315648Sjoerg int errcnt, count = 0; 1441553Srgrimes 1451553Srgrimes init(); /* set up capabilities */ 1461553Srgrimes (void) write(1, "", 1); /* ack that daemon is started */ 1471553Srgrimes (void) close(2); /* set up log file */ 1481553Srgrimes if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) { 1491553Srgrimes syslog(LOG_ERR, "%s: %m", LF); 1501553Srgrimes (void) open(_PATH_DEVNULL, O_WRONLY); 1511553Srgrimes } 1521553Srgrimes setgid(getegid()); 1531553Srgrimes pid = getpid(); /* for use with lprm */ 1541553Srgrimes setpgrp(0, pid); 1551553Srgrimes signal(SIGHUP, abortpr); 1561553Srgrimes signal(SIGINT, abortpr); 1571553Srgrimes signal(SIGQUIT, abortpr); 1581553Srgrimes signal(SIGTERM, abortpr); 1591553Srgrimes 1601553Srgrimes (void) mktemp(tempfile); 1611553Srgrimes 1621553Srgrimes /* 1631553Srgrimes * uses short form file names 1641553Srgrimes */ 1651553Srgrimes if (chdir(SD) < 0) { 1661553Srgrimes syslog(LOG_ERR, "%s: %m", SD); 1671553Srgrimes exit(1); 1681553Srgrimes } 1691553Srgrimes if (stat(LO, &stb) == 0 && (stb.st_mode & 0100)) 1701553Srgrimes exit(0); /* printing disabled */ 1711553Srgrimes lfd = open(LO, O_WRONLY|O_CREAT, 0644); 1721553Srgrimes if (lfd < 0) { 1731553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, LO); 1741553Srgrimes exit(1); 1751553Srgrimes } 1761553Srgrimes if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 1771553Srgrimes if (errno == EWOULDBLOCK) /* active deamon present */ 1781553Srgrimes exit(0); 1791553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, LO); 1801553Srgrimes exit(1); 1811553Srgrimes } 1821553Srgrimes ftruncate(lfd, 0); 1831553Srgrimes /* 1841553Srgrimes * write process id for others to know 1851553Srgrimes */ 1861553Srgrimes sprintf(line, "%u\n", pid); 1871553Srgrimes pidoff = i = strlen(line); 1881553Srgrimes if (write(lfd, line, i) != i) { 1891553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, LO); 1901553Srgrimes exit(1); 1911553Srgrimes } 1921553Srgrimes /* 1931553Srgrimes * search the spool directory for work and sort by queue order. 1941553Srgrimes */ 1951553Srgrimes if ((nitems = getq(&queue)) < 0) { 1961553Srgrimes syslog(LOG_ERR, "%s: can't scan %s", printer, SD); 1971553Srgrimes exit(1); 1981553Srgrimes } 1991553Srgrimes if (nitems == 0) /* no work to do */ 2001553Srgrimes exit(0); 2011553Srgrimes if (stb.st_mode & 01) { /* reset queue flag */ 2021553Srgrimes if (fchmod(lfd, stb.st_mode & 0776) < 0) 2031553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, LO); 2041553Srgrimes } 2051553Srgrimes openpr(); /* open printer or remote */ 2061553Srgrimesagain: 2071553Srgrimes /* 2081553Srgrimes * we found something to do now do it -- 2091553Srgrimes * write the name of the current control file into the lock file 2101553Srgrimes * so the spool queue program can tell what we're working on 2111553Srgrimes */ 2121553Srgrimes for (qp = queue; nitems--; free((char *) q)) { 2131553Srgrimes q = *qp++; 2141553Srgrimes if (stat(q->q_name, &stb) < 0) 2151553Srgrimes continue; 21615648Sjoerg errcnt = 0; 2171553Srgrimes restart: 21815648Sjoerg (void) lseek(lfd, pidoff, 0); 2191553Srgrimes (void) sprintf(line, "%s\n", q->q_name); 2201553Srgrimes i = strlen(line); 2211553Srgrimes if (write(lfd, line, i) != i) 2221553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, LO); 2231553Srgrimes if (!remote) 2241553Srgrimes i = printit(q->q_name); 2251553Srgrimes else 2261553Srgrimes i = sendit(q->q_name); 2271553Srgrimes /* 2281553Srgrimes * Check to see if we are supposed to stop printing or 2291553Srgrimes * if we are to rebuild the queue. 2301553Srgrimes */ 2311553Srgrimes if (fstat(lfd, &stb) == 0) { 2321553Srgrimes /* stop printing before starting next job? */ 2331553Srgrimes if (stb.st_mode & 0100) 2341553Srgrimes goto done; 2351553Srgrimes /* rebuild queue (after lpc topq) */ 2361553Srgrimes if (stb.st_mode & 01) { 2371553Srgrimes for (free((char *) q); nitems--; free((char *) q)) 2381553Srgrimes q = *qp++; 2391553Srgrimes if (fchmod(lfd, stb.st_mode & 0776) < 0) 2401553Srgrimes syslog(LOG_WARNING, "%s: %s: %m", 2411553Srgrimes printer, LO); 2421553Srgrimes break; 2431553Srgrimes } 2441553Srgrimes } 2451553Srgrimes if (i == OK) /* file ok and printed */ 2461553Srgrimes count++; 24715648Sjoerg else if (i == REPRINT && ++errcnt < 5) { 24815648Sjoerg /* try reprinting the job */ 2491553Srgrimes syslog(LOG_INFO, "restarting %s", printer); 2501553Srgrimes if (ofilter > 0) { 2511553Srgrimes kill(ofilter, SIGCONT); /* to be sure */ 2521553Srgrimes (void) close(ofd); 25315648Sjoerg while ((i = wait(NULL)) > 0 && i != ofilter) 2541553Srgrimes ; 2551553Srgrimes ofilter = 0; 2561553Srgrimes } 2571553Srgrimes (void) close(pfd); /* close printer */ 2581553Srgrimes if (ftruncate(lfd, pidoff) < 0) 2591553Srgrimes syslog(LOG_WARNING, "%s: %s: %m", printer, LO); 2601553Srgrimes openpr(); /* try to reopen printer */ 2611553Srgrimes goto restart; 26215648Sjoerg } else { 26315648Sjoerg syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer, 26415648Sjoerg remote ? "sent to remote host" : "printed", q->q_name); 26515648Sjoerg if (i == REPRINT) { 26615648Sjoerg /* insure we don't attempt this job again */ 26715648Sjoerg (void) unlink(q->q_name); 26815648Sjoerg q->q_name[0] = 'd'; 26915648Sjoerg (void) unlink(q->q_name); 27015648Sjoerg if (logname[0]) 27115648Sjoerg sendmail(logname, FATALERR); 27215648Sjoerg } 2731553Srgrimes } 2741553Srgrimes } 2751553Srgrimes free((char *) queue); 2761553Srgrimes /* 2771553Srgrimes * search the spool directory for more work. 2781553Srgrimes */ 2791553Srgrimes if ((nitems = getq(&queue)) < 0) { 2801553Srgrimes syslog(LOG_ERR, "%s: can't scan %s", printer, SD); 2811553Srgrimes exit(1); 2821553Srgrimes } 2831553Srgrimes if (nitems == 0) { /* no more work to do */ 2841553Srgrimes done: 2851553Srgrimes if (count > 0) { /* Files actually printed */ 2861553Srgrimes if (!SF && !tof) 2871553Srgrimes (void) write(ofd, FF, strlen(FF)); 2881553Srgrimes if (TR != NULL) /* output trailer */ 2891553Srgrimes (void) write(ofd, TR, strlen(TR)); 2901553Srgrimes } 29119202Simp (void) close(ofd); 29219202Simp (void) wait(NULL); 2931553Srgrimes (void) unlink(tempfile); 2941553Srgrimes exit(0); 2951553Srgrimes } 2961553Srgrimes goto again; 2971553Srgrimes} 2981553Srgrimes 2991553Srgrimeschar fonts[4][50]; /* fonts for troff */ 3001553Srgrimes 3011553Srgrimeschar ifonts[4][40] = { 3021553Srgrimes _PATH_VFONTR, 3031553Srgrimes _PATH_VFONTI, 3041553Srgrimes _PATH_VFONTB, 3051553Srgrimes _PATH_VFONTS, 3061553Srgrimes}; 3071553Srgrimes 3081553Srgrimes/* 3091553Srgrimes * The remaining part is the reading of the control file (cf) 3101553Srgrimes * and performing the various actions. 3111553Srgrimes */ 3121553Srgrimesstatic int 3131553Srgrimesprintit(file) 3141553Srgrimes char *file; 3151553Srgrimes{ 3161553Srgrimes register int i; 3171553Srgrimes char *cp; 3181553Srgrimes int bombed = OK; 3191553Srgrimes 3201553Srgrimes /* 3211553Srgrimes * open control file; ignore if no longer there. 3221553Srgrimes */ 3231553Srgrimes if ((cfp = fopen(file, "r")) == NULL) { 3241553Srgrimes syslog(LOG_INFO, "%s: %s: %m", printer, file); 3251553Srgrimes return(OK); 3261553Srgrimes } 3271553Srgrimes /* 3281553Srgrimes * Reset troff fonts. 3291553Srgrimes */ 3301553Srgrimes for (i = 0; i < 4; i++) 3311553Srgrimes strcpy(fonts[i], ifonts[i]); 3321553Srgrimes sprintf(&width[2], "%d", PW); 3331553Srgrimes strcpy(indent+2, "0"); 3341553Srgrimes 3351553Srgrimes /* 3361553Srgrimes * read the control file for work to do 3371553Srgrimes * 3381553Srgrimes * file format -- first character in the line is a command 3391553Srgrimes * rest of the line is the argument. 3401553Srgrimes * valid commands are: 3411553Srgrimes * 3421553Srgrimes * S -- "stat info" for symbolic link protection 3431553Srgrimes * J -- "job name" on banner page 3441553Srgrimes * C -- "class name" on banner page 3451553Srgrimes * L -- "literal" user's name to print on banner 3461553Srgrimes * T -- "title" for pr 3471553Srgrimes * H -- "host name" of machine where lpr was done 3481553Srgrimes * P -- "person" user's login name 3491553Srgrimes * I -- "indent" amount to indent output 35015648Sjoerg * R -- laser dpi "resolution" 3511553Srgrimes * f -- "file name" name of text file to print 3521553Srgrimes * l -- "file name" text file with control chars 3531553Srgrimes * p -- "file name" text file to print with pr(1) 3541553Srgrimes * t -- "file name" troff(1) file to print 3551553Srgrimes * n -- "file name" ditroff(1) file to print 3561553Srgrimes * d -- "file name" dvi file to print 3571553Srgrimes * g -- "file name" plot(1G) file to print 3581553Srgrimes * v -- "file name" plain raster file to print 3591553Srgrimes * c -- "file name" cifplot file to print 3601553Srgrimes * 1 -- "R font file" for troff 3611553Srgrimes * 2 -- "I font file" for troff 3621553Srgrimes * 3 -- "B font file" for troff 3631553Srgrimes * 4 -- "S font file" for troff 3641553Srgrimes * N -- "name" of file (used by lpq) 3651553Srgrimes * U -- "unlink" name of file to remove 3661553Srgrimes * (after we print it. (Pass 2 only)). 3671553Srgrimes * M -- "mail" to user when done printing 3681553Srgrimes * 3691553Srgrimes * getline reads a line and expands tabs to blanks 3701553Srgrimes */ 3711553Srgrimes 3721553Srgrimes /* pass 1 */ 3731553Srgrimes 3741553Srgrimes while (getline(cfp)) 3751553Srgrimes switch (line[0]) { 3761553Srgrimes case 'H': 3771553Srgrimes strcpy(fromhost, line+1); 3781553Srgrimes if (class[0] == '\0') 3791553Srgrimes strncpy(class, line+1, sizeof(class)-1); 3801553Srgrimes continue; 3811553Srgrimes 3821553Srgrimes case 'P': 3831553Srgrimes strncpy(logname, line+1, sizeof(logname)-1); 3841553Srgrimes if (RS) { /* restricted */ 3851553Srgrimes if (getpwnam(logname) == NULL) { 3861553Srgrimes bombed = NOACCT; 3871553Srgrimes sendmail(line+1, bombed); 3881553Srgrimes goto pass2; 3891553Srgrimes } 3901553Srgrimes } 3911553Srgrimes continue; 3921553Srgrimes 3931553Srgrimes case 'S': 3941553Srgrimes cp = line+1; 3951553Srgrimes i = 0; 3961553Srgrimes while (*cp >= '0' && *cp <= '9') 3971553Srgrimes i = i * 10 + (*cp++ - '0'); 3981553Srgrimes fdev = i; 3991553Srgrimes cp++; 4001553Srgrimes i = 0; 4011553Srgrimes while (*cp >= '0' && *cp <= '9') 4021553Srgrimes i = i * 10 + (*cp++ - '0'); 4031553Srgrimes fino = i; 4041553Srgrimes continue; 4051553Srgrimes 4061553Srgrimes case 'J': 4071553Srgrimes if (line[1] != '\0') 4081553Srgrimes strncpy(jobname, line+1, sizeof(jobname)-1); 4091553Srgrimes else 4101553Srgrimes strcpy(jobname, " "); 4111553Srgrimes continue; 4121553Srgrimes 4131553Srgrimes case 'C': 4141553Srgrimes if (line[1] != '\0') 4151553Srgrimes strncpy(class, line+1, sizeof(class)-1); 4161553Srgrimes else if (class[0] == '\0') 4171553Srgrimes gethostname(class, sizeof(class)); 4181553Srgrimes continue; 4191553Srgrimes 4201553Srgrimes case 'T': /* header title for pr */ 4211553Srgrimes strncpy(title, line+1, sizeof(title)-1); 4221553Srgrimes continue; 4231553Srgrimes 4241553Srgrimes case 'L': /* identification line */ 4251553Srgrimes if (!SH && !HL) 4261553Srgrimes banner(line+1, jobname); 4271553Srgrimes continue; 4281553Srgrimes 4291553Srgrimes case '1': /* troff fonts */ 4301553Srgrimes case '2': 4311553Srgrimes case '3': 4321553Srgrimes case '4': 4331553Srgrimes if (line[1] != '\0') 4341553Srgrimes strcpy(fonts[line[0]-'1'], line+1); 4351553Srgrimes continue; 4361553Srgrimes 4371553Srgrimes case 'W': /* page width */ 4381553Srgrimes strncpy(width+2, line+1, sizeof(width)-3); 4391553Srgrimes continue; 4401553Srgrimes 4411553Srgrimes case 'I': /* indent amount */ 4421553Srgrimes strncpy(indent+2, line+1, sizeof(indent)-3); 4431553Srgrimes continue; 4441553Srgrimes 4451553Srgrimes default: /* some file to print */ 4461553Srgrimes switch (i = print(line[0], line+1)) { 4471553Srgrimes case ERROR: 4481553Srgrimes if (bombed == OK) 4491553Srgrimes bombed = FATALERR; 4501553Srgrimes break; 4511553Srgrimes case REPRINT: 4521553Srgrimes (void) fclose(cfp); 4531553Srgrimes return(REPRINT); 4541553Srgrimes case FILTERERR: 4551553Srgrimes case ACCESS: 4561553Srgrimes bombed = i; 4571553Srgrimes sendmail(logname, bombed); 4581553Srgrimes } 4591553Srgrimes title[0] = '\0'; 4601553Srgrimes continue; 4611553Srgrimes 4621553Srgrimes case 'N': 4631553Srgrimes case 'U': 4641553Srgrimes case 'M': 46515648Sjoerg case 'R': 4661553Srgrimes continue; 4671553Srgrimes } 4681553Srgrimes 4691553Srgrimes /* pass 2 */ 4701553Srgrimes 4711553Srgrimespass2: 4721553Srgrimes fseek(cfp, 0L, 0); 4731553Srgrimes while (getline(cfp)) 4741553Srgrimes switch (line[0]) { 4751553Srgrimes case 'L': /* identification line */ 4761553Srgrimes if (!SH && HL) 4771553Srgrimes banner(line+1, jobname); 4781553Srgrimes continue; 4791553Srgrimes 4801553Srgrimes case 'M': 4811553Srgrimes if (bombed < NOACCT) /* already sent if >= NOACCT */ 4821553Srgrimes sendmail(line+1, bombed); 4831553Srgrimes continue; 4841553Srgrimes 4851553Srgrimes case 'U': 4861553Srgrimes (void) unlink(line+1); 4871553Srgrimes } 4881553Srgrimes /* 4891553Srgrimes * clean-up in case another control file exists 4901553Srgrimes */ 4911553Srgrimes (void) fclose(cfp); 4921553Srgrimes (void) unlink(file); 4931553Srgrimes return(bombed == OK ? OK : ERROR); 4941553Srgrimes} 4951553Srgrimes 4961553Srgrimes/* 4971553Srgrimes * Print a file. 4981553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. 4991553Srgrimes * Return -1 if a non-recoverable error occured, 5001553Srgrimes * 2 if the filter detected some errors (but printed the job anyway), 5011553Srgrimes * 1 if we should try to reprint this job and 5021553Srgrimes * 0 if all is well. 5031553Srgrimes * Note: all filters take stdin as the file, stdout as the printer, 5041553Srgrimes * stderr as the log file, and must not ignore SIGINT. 5051553Srgrimes */ 5061553Srgrimesstatic int 5071553Srgrimesprint(format, file) 5081553Srgrimes int format; 5091553Srgrimes char *file; 5101553Srgrimes{ 5111553Srgrimes register int n; 5121553Srgrimes register char *prog; 51318569Sbde int dtablesize, fi, fo; 5141553Srgrimes FILE *fp; 5151553Srgrimes char *av[15], buf[BUFSIZ]; 5161553Srgrimes int pid, p[2], stopped = 0; 5171553Srgrimes union wait status; 5181553Srgrimes struct stat stb; 5191553Srgrimes 5201553Srgrimes if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) 5211553Srgrimes return(ERROR); 5221553Srgrimes /* 5231553Srgrimes * Check to see if data file is a symbolic link. If so, it should 5241553Srgrimes * still point to the same file or someone is trying to print 5251553Srgrimes * something he shouldn't. 5261553Srgrimes */ 5271553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && 5281553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 5291553Srgrimes return(ACCESS); 5301553Srgrimes if (!SF && !tof) { /* start on a fresh page */ 5311553Srgrimes (void) write(ofd, FF, strlen(FF)); 5321553Srgrimes tof = 1; 5331553Srgrimes } 5341553Srgrimes if (IF == NULL && (format == 'f' || format == 'l')) { 5351553Srgrimes tof = 0; 5361553Srgrimes while ((n = read(fi, buf, BUFSIZ)) > 0) 5371553Srgrimes if (write(ofd, buf, n) != n) { 5381553Srgrimes (void) close(fi); 5391553Srgrimes return(REPRINT); 5401553Srgrimes } 5411553Srgrimes (void) close(fi); 5421553Srgrimes return(OK); 5431553Srgrimes } 5441553Srgrimes switch (format) { 5451553Srgrimes case 'p': /* print file using 'pr' */ 5461553Srgrimes if (IF == NULL) { /* use output filter */ 5471553Srgrimes prog = _PATH_PR; 5481553Srgrimes av[0] = "pr"; 5491553Srgrimes av[1] = width; 5501553Srgrimes av[2] = length; 5511553Srgrimes av[3] = "-h"; 5521553Srgrimes av[4] = *title ? title : " "; 5535445Sjoerg av[5] = "-F"; 5545445Sjoerg av[6] = 0; 5551553Srgrimes fo = ofd; 5561553Srgrimes goto start; 5571553Srgrimes } 5581553Srgrimes pipe(p); 5591553Srgrimes if ((prchild = dofork(DORETURN)) == 0) { /* child */ 5601553Srgrimes dup2(fi, 0); /* file is stdin */ 5611553Srgrimes dup2(p[1], 1); /* pipe is stdout */ 5628094Sjkh closelog(); 56318569Sbde for (n = 3, dtablesize = getdtablesize(); 56418569Sbde n < dtablesize; n++) 5651553Srgrimes (void) close(n); 5661553Srgrimes execl(_PATH_PR, "pr", width, length, 5675445Sjoerg "-h", *title ? title : " ", "-F", 0); 5681553Srgrimes syslog(LOG_ERR, "cannot execl %s", _PATH_PR); 5691553Srgrimes exit(2); 5701553Srgrimes } 5711553Srgrimes (void) close(p[1]); /* close output side */ 5721553Srgrimes (void) close(fi); 5731553Srgrimes if (prchild < 0) { 5741553Srgrimes prchild = 0; 5751553Srgrimes (void) close(p[0]); 5761553Srgrimes return(ERROR); 5771553Srgrimes } 5781553Srgrimes fi = p[0]; /* use pipe for input */ 5791553Srgrimes case 'f': /* print plain text file */ 5801553Srgrimes prog = IF; 5811553Srgrimes av[1] = width; 5821553Srgrimes av[2] = length; 5831553Srgrimes av[3] = indent; 5841553Srgrimes n = 4; 5851553Srgrimes break; 5861553Srgrimes case 'l': /* like 'f' but pass control characters */ 5871553Srgrimes prog = IF; 5881553Srgrimes av[1] = "-c"; 5891553Srgrimes av[2] = width; 5901553Srgrimes av[3] = length; 5911553Srgrimes av[4] = indent; 5921553Srgrimes n = 5; 5931553Srgrimes break; 5941553Srgrimes case 'r': /* print a fortran text file */ 5951553Srgrimes prog = RF; 5961553Srgrimes av[1] = width; 5971553Srgrimes av[2] = length; 5981553Srgrimes n = 3; 5991553Srgrimes break; 6001553Srgrimes case 't': /* print troff output */ 6011553Srgrimes case 'n': /* print ditroff output */ 6021553Srgrimes case 'd': /* print tex output */ 6031553Srgrimes (void) unlink(".railmag"); 6041553Srgrimes if ((fo = creat(".railmag", FILMOD)) < 0) { 6051553Srgrimes syslog(LOG_ERR, "%s: cannot create .railmag", printer); 6061553Srgrimes (void) unlink(".railmag"); 6071553Srgrimes } else { 6081553Srgrimes for (n = 0; n < 4; n++) { 6091553Srgrimes if (fonts[n][0] != '/') 6101553Srgrimes (void) write(fo, _PATH_VFONT, 6111553Srgrimes sizeof(_PATH_VFONT) - 1); 6121553Srgrimes (void) write(fo, fonts[n], strlen(fonts[n])); 6131553Srgrimes (void) write(fo, "\n", 1); 6141553Srgrimes } 6151553Srgrimes (void) close(fo); 6161553Srgrimes } 6171553Srgrimes prog = (format == 't') ? TF : (format == 'n') ? NF : DF; 6181553Srgrimes av[1] = pxwidth; 6191553Srgrimes av[2] = pxlength; 6201553Srgrimes n = 3; 6211553Srgrimes break; 6221553Srgrimes case 'c': /* print cifplot output */ 6231553Srgrimes prog = CF; 6241553Srgrimes av[1] = pxwidth; 6251553Srgrimes av[2] = pxlength; 6261553Srgrimes n = 3; 6271553Srgrimes break; 6281553Srgrimes case 'g': /* print plot(1G) output */ 6291553Srgrimes prog = GF; 6301553Srgrimes av[1] = pxwidth; 6311553Srgrimes av[2] = pxlength; 6321553Srgrimes n = 3; 6331553Srgrimes break; 6341553Srgrimes case 'v': /* print raster output */ 6351553Srgrimes prog = VF; 6361553Srgrimes av[1] = pxwidth; 6371553Srgrimes av[2] = pxlength; 6381553Srgrimes n = 3; 6391553Srgrimes break; 6401553Srgrimes default: 6411553Srgrimes (void) close(fi); 6421553Srgrimes syslog(LOG_ERR, "%s: illegal format character '%c'", 6431553Srgrimes printer, format); 6441553Srgrimes return(ERROR); 6451553Srgrimes } 64615648Sjoerg if (prog == NULL) { 64715648Sjoerg (void) close(fi); 64815648Sjoerg syslog(LOG_ERR, 64915648Sjoerg "%s: no filter found in printcap for format character '%c'", 65015648Sjoerg printer, format); 65115648Sjoerg return(ERROR); 65215648Sjoerg } 6531553Srgrimes if ((av[0] = rindex(prog, '/')) != NULL) 6541553Srgrimes av[0]++; 6551553Srgrimes else 6561553Srgrimes av[0] = prog; 6571553Srgrimes av[n++] = "-n"; 6581553Srgrimes av[n++] = logname; 6591553Srgrimes av[n++] = "-h"; 6601553Srgrimes av[n++] = fromhost; 6611553Srgrimes av[n++] = AF; 6621553Srgrimes av[n] = 0; 6631553Srgrimes fo = pfd; 6641553Srgrimes if (ofilter > 0) { /* stop output filter */ 6651553Srgrimes write(ofd, "\031\1", 2); 6661553Srgrimes while ((pid = 6671553Srgrimes wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter) 6681553Srgrimes ; 6691553Srgrimes if (status.w_stopval != WSTOPPED) { 6701553Srgrimes (void) close(fi); 67115648Sjoerg syslog(LOG_WARNING, 67215648Sjoerg "%s: output filter died (retcode=%d termsig=%d)", 67315648Sjoerg printer, status.w_retcode, status.w_termsig); 6741553Srgrimes return(REPRINT); 6751553Srgrimes } 6761553Srgrimes stopped++; 6771553Srgrimes } 6781553Srgrimesstart: 6791553Srgrimes if ((child = dofork(DORETURN)) == 0) { /* child */ 6801553Srgrimes dup2(fi, 0); 6811553Srgrimes dup2(fo, 1); 6821553Srgrimes n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664); 6831553Srgrimes if (n >= 0) 6841553Srgrimes dup2(n, 2); 6858094Sjkh closelog(); 68618569Sbde for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++) 6871553Srgrimes (void) close(n); 6881553Srgrimes execv(prog, av); 6891553Srgrimes syslog(LOG_ERR, "cannot execv %s", prog); 6901553Srgrimes exit(2); 6911553Srgrimes } 6921553Srgrimes (void) close(fi); 6931553Srgrimes if (child < 0) 6941553Srgrimes status.w_retcode = 100; 6951553Srgrimes else 6961553Srgrimes while ((pid = wait((int *)&status)) > 0 && pid != child) 6971553Srgrimes ; 6981553Srgrimes child = 0; 6991553Srgrimes prchild = 0; 7001553Srgrimes if (stopped) { /* restart output filter */ 7011553Srgrimes if (kill(ofilter, SIGCONT) < 0) { 7021553Srgrimes syslog(LOG_ERR, "cannot restart output filter"); 7031553Srgrimes exit(1); 7041553Srgrimes } 7051553Srgrimes } 7061553Srgrimes tof = 0; 7071553Srgrimes 7081553Srgrimes /* Copy filter output to "lf" logfile */ 7091553Srgrimes if (fp = fopen(tempfile, "r")) { 7101553Srgrimes while (fgets(buf, sizeof(buf), fp)) 7111553Srgrimes fputs(buf, stderr); 7121553Srgrimes fclose(fp); 7131553Srgrimes } 7141553Srgrimes 7151553Srgrimes if (!WIFEXITED(status)) { 71615648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", 7171553Srgrimes printer, format, status.w_termsig); 7181553Srgrimes return(ERROR); 7191553Srgrimes } 7201553Srgrimes switch (status.w_retcode) { 7211553Srgrimes case 0: 7221553Srgrimes tof = 1; 7231553Srgrimes return(OK); 7241553Srgrimes case 1: 7251553Srgrimes return(REPRINT); 72615648Sjoerg case 2: 72715648Sjoerg return(ERROR); 7281553Srgrimes default: 72915648Sjoerg syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", 7301553Srgrimes printer, format, status.w_retcode); 73115648Sjoerg return(FILTERERR); 7321553Srgrimes } 7331553Srgrimes} 7341553Srgrimes 7351553Srgrimes/* 7361553Srgrimes * Send the daemon control file (cf) and any data files. 7371553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and 7381553Srgrimes * 0 if all is well. 7391553Srgrimes */ 7401553Srgrimesstatic int 7411553Srgrimessendit(file) 7421553Srgrimes char *file; 7431553Srgrimes{ 7441553Srgrimes register int i, err = OK; 7451553Srgrimes char *cp, last[BUFSIZ]; 7461553Srgrimes 7471553Srgrimes /* 7481553Srgrimes * open control file 7491553Srgrimes */ 7501553Srgrimes if ((cfp = fopen(file, "r")) == NULL) 7511553Srgrimes return(OK); 7521553Srgrimes /* 7531553Srgrimes * read the control file for work to do 7541553Srgrimes * 7551553Srgrimes * file format -- first character in the line is a command 7561553Srgrimes * rest of the line is the argument. 7571553Srgrimes * commands of interest are: 7581553Srgrimes * 7591553Srgrimes * a-z -- "file name" name of file to print 7601553Srgrimes * U -- "unlink" name of file to remove 7611553Srgrimes * (after we print it. (Pass 2 only)). 7621553Srgrimes */ 7631553Srgrimes 7641553Srgrimes /* 7651553Srgrimes * pass 1 7661553Srgrimes */ 7671553Srgrimes while (getline(cfp)) { 7681553Srgrimes again: 7691553Srgrimes if (line[0] == 'S') { 7701553Srgrimes cp = line+1; 7711553Srgrimes i = 0; 7721553Srgrimes while (*cp >= '0' && *cp <= '9') 7731553Srgrimes i = i * 10 + (*cp++ - '0'); 7741553Srgrimes fdev = i; 7751553Srgrimes cp++; 7761553Srgrimes i = 0; 7771553Srgrimes while (*cp >= '0' && *cp <= '9') 7781553Srgrimes i = i * 10 + (*cp++ - '0'); 7791553Srgrimes fino = i; 7801553Srgrimes continue; 7811553Srgrimes } 7821553Srgrimes if (line[0] >= 'a' && line[0] <= 'z') { 7831553Srgrimes strcpy(last, line); 7841553Srgrimes while (i = getline(cfp)) 7851553Srgrimes if (strcmp(last, line)) 7861553Srgrimes break; 7871553Srgrimes switch (sendfile('\3', last+1)) { 7881553Srgrimes case OK: 7891553Srgrimes if (i) 7901553Srgrimes goto again; 7911553Srgrimes break; 7921553Srgrimes case REPRINT: 7931553Srgrimes (void) fclose(cfp); 7941553Srgrimes return(REPRINT); 7951553Srgrimes case ACCESS: 7961553Srgrimes sendmail(logname, ACCESS); 7971553Srgrimes case ERROR: 7981553Srgrimes err = ERROR; 7991553Srgrimes } 8001553Srgrimes break; 8011553Srgrimes } 8021553Srgrimes } 8031553Srgrimes if (err == OK && sendfile('\2', file) > 0) { 8041553Srgrimes (void) fclose(cfp); 8051553Srgrimes return(REPRINT); 8061553Srgrimes } 8071553Srgrimes /* 8081553Srgrimes * pass 2 8091553Srgrimes */ 8101553Srgrimes fseek(cfp, 0L, 0); 8111553Srgrimes while (getline(cfp)) 8121553Srgrimes if (line[0] == 'U') 8131553Srgrimes (void) unlink(line+1); 8141553Srgrimes /* 8151553Srgrimes * clean-up in case another control file exists 8161553Srgrimes */ 8171553Srgrimes (void) fclose(cfp); 8181553Srgrimes (void) unlink(file); 8191553Srgrimes return(err); 8201553Srgrimes} 8211553Srgrimes 8221553Srgrimes/* 8231553Srgrimes * Send a data file to the remote machine and spool it. 8241553Srgrimes * Return positive if we should try resending. 8251553Srgrimes */ 8261553Srgrimesstatic int 8271553Srgrimessendfile(type, file) 8281553Srgrimes int type; 8291553Srgrimes char *file; 8301553Srgrimes{ 8311553Srgrimes register int f, i, amt; 8321553Srgrimes struct stat stb; 8331553Srgrimes char buf[BUFSIZ]; 8341553Srgrimes int sizerr, resp; 8351553Srgrimes 8361553Srgrimes if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0) 8371553Srgrimes return(ERROR); 8381553Srgrimes /* 8391553Srgrimes * Check to see if data file is a symbolic link. If so, it should 8401553Srgrimes * still point to the same file or someone is trying to print something 8411553Srgrimes * he shouldn't. 8421553Srgrimes */ 8431553Srgrimes if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 && 8441553Srgrimes (stb.st_dev != fdev || stb.st_ino != fino)) 8451553Srgrimes return(ACCESS); 8461553Srgrimes (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file); 8471553Srgrimes amt = strlen(buf); 8481553Srgrimes for (i = 0; ; i++) { 8491553Srgrimes if (write(pfd, buf, amt) != amt || 8501553Srgrimes (resp = response()) < 0 || resp == '\1') { 8511553Srgrimes (void) close(f); 8521553Srgrimes return(REPRINT); 8531553Srgrimes } else if (resp == '\0') 8541553Srgrimes break; 8551553Srgrimes if (i == 0) 8561553Srgrimes pstatus("no space on remote; waiting for queue to drain"); 8571553Srgrimes if (i == 10) 8581553Srgrimes syslog(LOG_ALERT, "%s: can't send to %s; queue full", 8591553Srgrimes printer, RM); 8601553Srgrimes sleep(5 * 60); 8611553Srgrimes } 8621553Srgrimes if (i) 8631553Srgrimes pstatus("sending to %s", RM); 8641553Srgrimes sizerr = 0; 8651553Srgrimes for (i = 0; i < stb.st_size; i += BUFSIZ) { 8661553Srgrimes amt = BUFSIZ; 8671553Srgrimes if (i + amt > stb.st_size) 8681553Srgrimes amt = stb.st_size - i; 8691553Srgrimes if (sizerr == 0 && read(f, buf, amt) != amt) 8701553Srgrimes sizerr = 1; 8711553Srgrimes if (write(pfd, buf, amt) != amt) { 8721553Srgrimes (void) close(f); 8731553Srgrimes return(REPRINT); 8741553Srgrimes } 8751553Srgrimes } 8761553Srgrimes 8771553Srgrimes 8781553Srgrimes 8791553Srgrimes 8801553Srgrimes (void) close(f); 8811553Srgrimes if (sizerr) { 8821553Srgrimes syslog(LOG_INFO, "%s: %s: changed size", printer, file); 8831553Srgrimes /* tell recvjob to ignore this file */ 8841553Srgrimes (void) write(pfd, "\1", 1); 8851553Srgrimes return(ERROR); 8861553Srgrimes } 8871553Srgrimes if (write(pfd, "", 1) != 1 || response()) 8881553Srgrimes return(REPRINT); 8891553Srgrimes return(OK); 8901553Srgrimes} 8911553Srgrimes 8921553Srgrimes/* 8931553Srgrimes * Check to make sure there have been no errors and that both programs 8941553Srgrimes * are in sync with eachother. 8951553Srgrimes * Return non-zero if the connection was lost. 8961553Srgrimes */ 8971553Srgrimesstatic char 8981553Srgrimesresponse() 8991553Srgrimes{ 9001553Srgrimes char resp; 9011553Srgrimes 9021553Srgrimes if (read(pfd, &resp, 1) != 1) { 9031553Srgrimes syslog(LOG_INFO, "%s: lost connection", printer); 9041553Srgrimes return(-1); 9051553Srgrimes } 9061553Srgrimes return(resp); 9071553Srgrimes} 9081553Srgrimes 9091553Srgrimes/* 9101553Srgrimes * Banner printing stuff 9111553Srgrimes */ 9121553Srgrimesstatic void 9131553Srgrimesbanner(name1, name2) 9141553Srgrimes char *name1, *name2; 9151553Srgrimes{ 9161553Srgrimes time_t tvec; 9171553Srgrimes 9181553Srgrimes time(&tvec); 9191553Srgrimes if (!SF && !tof) 9201553Srgrimes (void) write(ofd, FF, strlen(FF)); 9211553Srgrimes if (SB) { /* short banner only */ 9221553Srgrimes if (class[0]) { 9231553Srgrimes (void) write(ofd, class, strlen(class)); 9241553Srgrimes (void) write(ofd, ":", 1); 9251553Srgrimes } 9261553Srgrimes (void) write(ofd, name1, strlen(name1)); 9271553Srgrimes (void) write(ofd, " Job: ", 7); 9281553Srgrimes (void) write(ofd, name2, strlen(name2)); 9291553Srgrimes (void) write(ofd, " Date: ", 8); 9301553Srgrimes (void) write(ofd, ctime(&tvec), 24); 9311553Srgrimes (void) write(ofd, "\n", 1); 9321553Srgrimes } else { /* normal banner */ 9331553Srgrimes (void) write(ofd, "\n\n\n", 3); 9341553Srgrimes scan_out(ofd, name1, '\0'); 9351553Srgrimes (void) write(ofd, "\n\n", 2); 9361553Srgrimes scan_out(ofd, name2, '\0'); 9371553Srgrimes if (class[0]) { 9381553Srgrimes (void) write(ofd,"\n\n\n",3); 9391553Srgrimes scan_out(ofd, class, '\0'); 9401553Srgrimes } 9411553Srgrimes (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); 9421553Srgrimes (void) write(ofd, name2, strlen(name2)); 9431553Srgrimes (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); 9441553Srgrimes (void) write(ofd, ctime(&tvec), 24); 9451553Srgrimes (void) write(ofd, "\n", 1); 9461553Srgrimes } 9471553Srgrimes if (!SF) 9481553Srgrimes (void) write(ofd, FF, strlen(FF)); 9491553Srgrimes tof = 1; 9501553Srgrimes} 9511553Srgrimes 9521553Srgrimesstatic char * 9531553Srgrimesscnline(key, p, c) 9541553Srgrimes register int key; 9551553Srgrimes register char *p; 9561553Srgrimes int c; 9571553Srgrimes{ 9581553Srgrimes register scnwidth; 9591553Srgrimes 9601553Srgrimes for (scnwidth = WIDTH; --scnwidth;) { 9611553Srgrimes key <<= 1; 9621553Srgrimes *p++ = key & 0200 ? c : BACKGND; 9631553Srgrimes } 9641553Srgrimes return (p); 9651553Srgrimes} 9661553Srgrimes 9671553Srgrimes#define TRC(q) (((q)-' ')&0177) 9681553Srgrimes 9691553Srgrimesstatic void 9701553Srgrimesscan_out(scfd, scsp, dlm) 9711553Srgrimes int scfd, dlm; 9721553Srgrimes char *scsp; 9731553Srgrimes{ 9741553Srgrimes register char *strp; 9751553Srgrimes register nchrs, j; 9761553Srgrimes char outbuf[LINELEN+1], *sp, c, cc; 9771553Srgrimes int d, scnhgt; 9781553Srgrimes 9791553Srgrimes for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { 9801553Srgrimes strp = &outbuf[0]; 9811553Srgrimes sp = scsp; 9821553Srgrimes for (nchrs = 0; ; ) { 9831553Srgrimes d = dropit(c = TRC(cc = *sp++)); 9841553Srgrimes if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) 9851553Srgrimes for (j = WIDTH; --j;) 9861553Srgrimes *strp++ = BACKGND; 9871553Srgrimes else 9881553Srgrimes strp = scnline(scnkey[c][scnhgt-1-d], strp, cc); 9891553Srgrimes if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1) 9901553Srgrimes break; 9911553Srgrimes *strp++ = BACKGND; 9921553Srgrimes *strp++ = BACKGND; 9931553Srgrimes } 9941553Srgrimes while (*--strp == BACKGND && strp >= outbuf) 9951553Srgrimes ; 9961553Srgrimes strp++; 9978857Srgrimes *strp++ = '\n'; 9981553Srgrimes (void) write(scfd, outbuf, strp-outbuf); 9991553Srgrimes } 10001553Srgrimes} 10011553Srgrimes 10021553Srgrimesstatic int 10031553Srgrimesdropit(c) 10041553Srgrimes int c; 10051553Srgrimes{ 10061553Srgrimes switch(c) { 10071553Srgrimes 10081553Srgrimes case TRC('_'): 10091553Srgrimes case TRC(';'): 10101553Srgrimes case TRC(','): 10111553Srgrimes case TRC('g'): 10121553Srgrimes case TRC('j'): 10131553Srgrimes case TRC('p'): 10141553Srgrimes case TRC('q'): 10151553Srgrimes case TRC('y'): 10161553Srgrimes return (DROP); 10171553Srgrimes 10181553Srgrimes default: 10191553Srgrimes return (0); 10201553Srgrimes } 10211553Srgrimes} 10221553Srgrimes 10231553Srgrimes/* 10241553Srgrimes * sendmail --- 10251553Srgrimes * tell people about job completion 10261553Srgrimes */ 10271553Srgrimesstatic void 10281553Srgrimessendmail(user, bombed) 10291553Srgrimes char *user; 10301553Srgrimes int bombed; 10311553Srgrimes{ 10321553Srgrimes register int i; 103318569Sbde int dtablesize; 10341553Srgrimes int p[2], s; 10351553Srgrimes register char *cp; 10361553Srgrimes char buf[100]; 10371553Srgrimes struct stat stb; 10381553Srgrimes FILE *fp; 10391553Srgrimes 10401553Srgrimes pipe(p); 10411553Srgrimes if ((s = dofork(DORETURN)) == 0) { /* child */ 10421553Srgrimes dup2(p[0], 0); 10438094Sjkh closelog(); 104418569Sbde for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++) 10451553Srgrimes (void) close(i); 10461553Srgrimes if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL) 10471553Srgrimes cp++; 10481553Srgrimes else 10491553Srgrimes cp = _PATH_SENDMAIL; 10501553Srgrimes sprintf(buf, "%s@%s", user, fromhost); 10511553Srgrimes execl(_PATH_SENDMAIL, cp, buf, 0); 10521553Srgrimes exit(0); 10531553Srgrimes } else if (s > 0) { /* parent */ 10541553Srgrimes dup2(p[1], 1); 10551553Srgrimes printf("To: %s@%s\n", user, fromhost); 105615648Sjoerg printf("Subject: %s printer job \"%s\"\n", printer, 105715648Sjoerg *jobname ? jobname : "<unknown>"); 105815648Sjoerg printf("Reply-To: root@%s\n\n", host); 10591553Srgrimes printf("Your printer job "); 10601553Srgrimes if (*jobname) 10611553Srgrimes printf("(%s) ", jobname); 10621553Srgrimes switch (bombed) { 10631553Srgrimes case OK: 10641553Srgrimes printf("\ncompleted successfully\n"); 106515648Sjoerg cp = "OK"; 10661553Srgrimes break; 10671553Srgrimes default: 10681553Srgrimes case FATALERR: 10691553Srgrimes printf("\ncould not be printed\n"); 107015648Sjoerg cp = "FATALERR"; 10711553Srgrimes break; 10721553Srgrimes case NOACCT: 10731553Srgrimes printf("\ncould not be printed without an account on %s\n", host); 107415648Sjoerg cp = "NOACCT"; 10751553Srgrimes break; 10761553Srgrimes case FILTERERR: 10771553Srgrimes if (stat(tempfile, &stb) < 0 || stb.st_size == 0 || 10781553Srgrimes (fp = fopen(tempfile, "r")) == NULL) { 107915648Sjoerg printf("\nhad some errors and may not have printed\n"); 10801553Srgrimes break; 10811553Srgrimes } 108215648Sjoerg printf("\nhad the following errors and may not have printed:\n"); 10831553Srgrimes while ((i = getc(fp)) != EOF) 10841553Srgrimes putchar(i); 10851553Srgrimes (void) fclose(fp); 108615648Sjoerg cp = "FILTERERR"; 10871553Srgrimes break; 10881553Srgrimes case ACCESS: 10891553Srgrimes printf("\nwas not printed because it was not linked to the original file\n"); 109015648Sjoerg cp = "ACCESS"; 10911553Srgrimes } 10921553Srgrimes fflush(stdout); 10931553Srgrimes (void) close(1); 10941553Srgrimes } 10951553Srgrimes (void) close(p[0]); 10961553Srgrimes (void) close(p[1]); 109715648Sjoerg wait(NULL); 109815648Sjoerg syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", 109915648Sjoerg user, *jobname ? jobname : "<unknown>", printer, cp); 11001553Srgrimes} 11011553Srgrimes 11021553Srgrimes/* 11031553Srgrimes * dofork - fork with retries on failure 11041553Srgrimes */ 11051553Srgrimesstatic int 11061553Srgrimesdofork(action) 11071553Srgrimes int action; 11081553Srgrimes{ 11091553Srgrimes register int i, pid; 11101553Srgrimes 11111553Srgrimes for (i = 0; i < 20; i++) { 11121553Srgrimes if ((pid = fork()) < 0) { 11131553Srgrimes sleep((unsigned)(i*i)); 11141553Srgrimes continue; 11151553Srgrimes } 11161553Srgrimes /* 11171553Srgrimes * Child should run as daemon instead of root 11181553Srgrimes */ 111915648Sjoerg if (pid == 0) 11201553Srgrimes setuid(DU); 11211553Srgrimes return(pid); 11221553Srgrimes } 11231553Srgrimes syslog(LOG_ERR, "can't fork"); 11241553Srgrimes 11251553Srgrimes switch (action) { 11261553Srgrimes case DORETURN: 11271553Srgrimes return (-1); 11281553Srgrimes default: 11291553Srgrimes syslog(LOG_ERR, "bad action (%d) to dofork", action); 11301553Srgrimes /*FALL THRU*/ 11311553Srgrimes case DOABORT: 11321553Srgrimes exit(1); 11331553Srgrimes } 11341553Srgrimes /*NOTREACHED*/ 11351553Srgrimes} 11361553Srgrimes 11371553Srgrimes/* 11381553Srgrimes * Kill child processes to abort current job. 11391553Srgrimes */ 11401553Srgrimesstatic void 11411553Srgrimesabortpr(signo) 11421553Srgrimes int signo; 11431553Srgrimes{ 11441553Srgrimes (void) unlink(tempfile); 11451553Srgrimes kill(0, SIGINT); 11461553Srgrimes if (ofilter > 0) 11471553Srgrimes kill(ofilter, SIGCONT); 11481553Srgrimes while (wait(NULL) > 0) 11491553Srgrimes ; 11501553Srgrimes exit(0); 11511553Srgrimes} 11521553Srgrimes 11531553Srgrimesstatic void 11541553Srgrimesinit() 11551553Srgrimes{ 11561553Srgrimes int status; 11571553Srgrimes char *s; 11581553Srgrimes 11591553Srgrimes if ((status = cgetent(&bp, printcapdb, printer)) == -2) { 11601553Srgrimes syslog(LOG_ERR, "can't open printer description file"); 11611553Srgrimes exit(1); 11621553Srgrimes } else if (status == -1) { 11631553Srgrimes syslog(LOG_ERR, "unknown printer: %s", printer); 11641553Srgrimes exit(1); 11651553Srgrimes } else if (status == -3) 11661553Srgrimes fatal("potential reference loop detected in printcap file"); 11671553Srgrimes 11681553Srgrimes if (cgetstr(bp, "lp", &LP) == -1) 11691553Srgrimes LP = _PATH_DEFDEVLP; 11701553Srgrimes if (cgetstr(bp, "rp", &RP) == -1) 11711553Srgrimes RP = DEFLP; 11721553Srgrimes if (cgetstr(bp, "lo", &LO) == -1) 11731553Srgrimes LO = DEFLOCK; 11741553Srgrimes if (cgetstr(bp, "st", &ST) == -1) 11751553Srgrimes ST = DEFSTAT; 11761553Srgrimes if (cgetstr(bp, "lf", &LF) == -1) 11771553Srgrimes LF = _PATH_CONSOLE; 11781553Srgrimes if (cgetstr(bp, "sd", &SD) == -1) 11791553Srgrimes SD = _PATH_DEFSPOOL; 11801553Srgrimes if (cgetnum(bp, "du", &DU) < 0) 11811553Srgrimes DU = DEFUID; 11821553Srgrimes if (cgetstr(bp,"ff", &FF) == -1) 11831553Srgrimes FF = DEFFF; 11841553Srgrimes if (cgetnum(bp, "pw", &PW) < 0) 11851553Srgrimes PW = DEFWIDTH; 11861553Srgrimes sprintf(&width[2], "%d", PW); 11871553Srgrimes if (cgetnum(bp, "pl", &PL) < 0) 11881553Srgrimes PL = DEFLENGTH; 11891553Srgrimes sprintf(&length[2], "%d", PL); 11901553Srgrimes if (cgetnum(bp,"px", &PX) < 0) 11911553Srgrimes PX = 0; 11921553Srgrimes sprintf(&pxwidth[2], "%d", PX); 11931553Srgrimes if (cgetnum(bp, "py", &PY) < 0) 11941553Srgrimes PY = 0; 11951553Srgrimes sprintf(&pxlength[2], "%d", PY); 11961553Srgrimes cgetstr(bp, "rm", &RM); 11971553Srgrimes if (s = checkremote()) 11981553Srgrimes syslog(LOG_WARNING, s); 11991553Srgrimes 12001553Srgrimes cgetstr(bp, "af", &AF); 12011553Srgrimes cgetstr(bp, "of", &OF); 12021553Srgrimes cgetstr(bp, "if", &IF); 12031553Srgrimes cgetstr(bp, "rf", &RF); 12041553Srgrimes cgetstr(bp, "tf", &TF); 12051553Srgrimes cgetstr(bp, "nf", &NF); 12061553Srgrimes cgetstr(bp, "df", &DF); 12071553Srgrimes cgetstr(bp, "gf", &GF); 12081553Srgrimes cgetstr(bp, "vf", &VF); 12091553Srgrimes cgetstr(bp, "cf", &CF); 12101553Srgrimes cgetstr(bp, "tr", &TR); 121115032Ssef cgetstr(bp, "ms", &MS); 12121553Srgrimes 12131553Srgrimes RS = (cgetcap(bp, "rs", ':') != NULL); 12141553Srgrimes SF = (cgetcap(bp, "sf", ':') != NULL); 12151553Srgrimes SH = (cgetcap(bp, "sh", ':') != NULL); 12161553Srgrimes SB = (cgetcap(bp, "sb", ':') != NULL); 12171553Srgrimes HL = (cgetcap(bp, "hl", ':') != NULL); 12181553Srgrimes RW = (cgetcap(bp, "rw", ':') != NULL); 12191553Srgrimes 12201553Srgrimes cgetnum(bp, "br", &BR); 12211553Srgrimes 12221553Srgrimes tof = (cgetcap(bp, "fo", ':') == NULL); 12231553Srgrimes} 12241553Srgrimes 12251553Srgrimes/* 12261553Srgrimes * Acquire line printer or remote connection. 12271553Srgrimes */ 12281553Srgrimesstatic void 12291553Srgrimesopenpr() 12301553Srgrimes{ 123115648Sjoerg register int i; 123218569Sbde int dtablesize; 123315648Sjoerg char *cp; 12341553Srgrimes 123515648Sjoerg if (!remote && *LP) { 123615648Sjoerg if (cp = index(LP, '@')) 123715648Sjoerg opennet(cp); 123815648Sjoerg else 123915648Sjoerg opentty(); 124015648Sjoerg } else if (remote) { 124115648Sjoerg openrem(); 12421553Srgrimes } else { 12431553Srgrimes syslog(LOG_ERR, "%s: no line printer device or host name", 12441553Srgrimes printer); 12451553Srgrimes exit(1); 12461553Srgrimes } 124715648Sjoerg 12481553Srgrimes /* 12491553Srgrimes * Start up an output filter, if needed. 12501553Srgrimes */ 12511553Srgrimes if (!remote && OF) { 12521553Srgrimes int p[2]; 12531553Srgrimes 12541553Srgrimes pipe(p); 12551553Srgrimes if ((ofilter = dofork(DOABORT)) == 0) { /* child */ 12561553Srgrimes dup2(p[0], 0); /* pipe is std in */ 12571553Srgrimes dup2(pfd, 1); /* printer is std out */ 12588094Sjkh closelog(); 125918569Sbde for (i = 3, dtablesize = getdtablesize(); 126018569Sbde i < dtablesize; i++) 12611553Srgrimes (void) close(i); 12621553Srgrimes if ((cp = rindex(OF, '/')) == NULL) 12631553Srgrimes cp = OF; 12641553Srgrimes else 12651553Srgrimes cp++; 12661553Srgrimes execl(OF, cp, width, length, 0); 12671553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, OF); 12681553Srgrimes exit(1); 12691553Srgrimes } 12701553Srgrimes (void) close(p[0]); /* close input side */ 12711553Srgrimes ofd = p[1]; /* use pipe for output */ 12721553Srgrimes } else { 12731553Srgrimes ofd = pfd; 12741553Srgrimes ofilter = 0; 12751553Srgrimes } 12761553Srgrimes} 12771553Srgrimes 127815648Sjoerg/* 127915648Sjoerg * Printer connected directly to the network 128015648Sjoerg * or to a terminal server on the net 128115648Sjoerg */ 128215648Sjoergstatic void 128315648Sjoergopennet(cp) 128415648Sjoerg char *cp; 128515648Sjoerg{ 128615648Sjoerg register int i; 128715648Sjoerg int resp, port; 128815648Sjoerg char save_ch; 128915648Sjoerg 129015648Sjoerg save_ch = *cp; 129115648Sjoerg *cp = '\0'; 129215648Sjoerg port = atoi(LP); 129315648Sjoerg if (port <= 0) { 129415648Sjoerg syslog(LOG_ERR, "%s: bad port number: %s", printer, LP); 129515648Sjoerg exit(1); 129615648Sjoerg } 129715648Sjoerg *cp++ = save_ch; 129815648Sjoerg 129915648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 130015648Sjoerg resp = -1; 130115648Sjoerg pfd = getport(cp, port); 130215648Sjoerg if (pfd < 0 && errno == ECONNREFUSED) 130315648Sjoerg resp = 1; 130415648Sjoerg else if (pfd >= 0) { 130515648Sjoerg /* 130615648Sjoerg * need to delay a bit for rs232 lines 130715648Sjoerg * to stabilize in case printer is 130815648Sjoerg * connected via a terminal server 130915648Sjoerg */ 131015648Sjoerg delay(500); 131115648Sjoerg break; 131215648Sjoerg } 131315648Sjoerg if (i == 1) { 131415648Sjoerg if (resp < 0) 131515648Sjoerg pstatus("waiting for %s to come up", LP); 131615648Sjoerg else 131715648Sjoerg pstatus("waiting for access to printer on %s", LP); 131815648Sjoerg } 131915648Sjoerg sleep(i); 132015648Sjoerg } 132115648Sjoerg pstatus("sending to %s port %d", cp, port); 132215648Sjoerg} 132315648Sjoerg 132415648Sjoerg/* 132515648Sjoerg * Printer is connected to an RS232 port on this host 132615648Sjoerg */ 132715648Sjoergstatic void 132815648Sjoergopentty() 132915648Sjoerg{ 133015648Sjoerg register int i; 133115648Sjoerg int resp, port; 133215648Sjoerg 133315648Sjoerg for (i = 1; ; i = i < 32 ? i << 1 : i) { 133415648Sjoerg pfd = open(LP, RW ? O_RDWR : O_WRONLY); 133515648Sjoerg if (pfd >= 0) { 133615648Sjoerg delay(500); 133715648Sjoerg break; 133815648Sjoerg } 133915648Sjoerg if (errno == ENOENT) { 134015648Sjoerg syslog(LOG_ERR, "%s: %m", LP); 134115648Sjoerg exit(1); 134215648Sjoerg } 134315648Sjoerg if (i == 1) 134415648Sjoerg pstatus("waiting for %s to become ready (offline ?)", 134515648Sjoerg printer); 134615648Sjoerg sleep(i); 134715648Sjoerg } 134815648Sjoerg if (isatty(pfd)) 134915648Sjoerg setty(); 135015648Sjoerg pstatus("%s is ready and printing", printer); 135115648Sjoerg} 135215648Sjoerg 135315648Sjoerg/* 135415648Sjoerg * Printer is on a remote host 135515648Sjoerg */ 135615648Sjoergstatic void 135715648Sjoergopenrem() 135815648Sjoerg{ 135915648Sjoerg register int i, n; 136015648Sjoerg int resp, port; 136115648Sjoerg 136215648Sjoerg for (i = 1; ; i = i < 256 ? i << 1 : i) { 136315648Sjoerg resp = -1; 136415648Sjoerg pfd = getport(RM, 0); 136515648Sjoerg if (pfd >= 0) { 136615648Sjoerg (void) sprintf(line, "\2%s\n", RP); 136715648Sjoerg n = strlen(line); 136815648Sjoerg if (write(pfd, line, n) == n && 136915648Sjoerg (resp = response()) == '\0') 137015648Sjoerg break; 137115648Sjoerg (void) close(pfd); 137215648Sjoerg } 137315648Sjoerg if (i == 1) { 137415648Sjoerg if (resp < 0) 137515648Sjoerg pstatus("waiting for %s to come up", RM); 137615648Sjoerg else { 137715648Sjoerg pstatus("waiting for queue to be enabled on %s", 137815648Sjoerg RM); 137915648Sjoerg i = 256; 138015648Sjoerg } 138115648Sjoerg } 138215648Sjoerg sleep(i); 138315648Sjoerg } 138415648Sjoerg pstatus("sending to %s", RM); 138515648Sjoerg} 138615648Sjoerg 13871553Srgrimesstruct bauds { 13881553Srgrimes int baud; 13891553Srgrimes int speed; 13901553Srgrimes} bauds[] = { 13911553Srgrimes 50, B50, 13921553Srgrimes 75, B75, 13931553Srgrimes 110, B110, 13941553Srgrimes 134, B134, 13951553Srgrimes 150, B150, 13961553Srgrimes 200, B200, 13971553Srgrimes 300, B300, 13981553Srgrimes 600, B600, 13991553Srgrimes 1200, B1200, 14001553Srgrimes 1800, B1800, 14011553Srgrimes 2400, B2400, 14021553Srgrimes 4800, B4800, 14031553Srgrimes 9600, B9600, 14041553Srgrimes 19200, EXTA, 14051553Srgrimes 38400, EXTB, 14069821Swpaul 57600, B57600, 14079821Swpaul 115200, B115200, 14081553Srgrimes 0, 0 14091553Srgrimes}; 14101553Srgrimes 14111553Srgrimes/* 14121553Srgrimes * setup tty lines. 14131553Srgrimes */ 14141553Srgrimesstatic void 14151553Srgrimessetty() 14161553Srgrimes{ 141715032Ssef struct termios ttybuf; 141815032Ssef struct bauds *bp; 14191553Srgrimes 14201553Srgrimes if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { 14211553Srgrimes syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer); 14221553Srgrimes exit(1); 14231553Srgrimes } 142415032Ssef if (tcgetattr(pfd, &ttybuf) < 0) { 142515032Ssef syslog(LOG_ERR, "%s: tcgetattr: %m", printer); 14261553Srgrimes exit(1); 14271553Srgrimes } 14281553Srgrimes if (BR > 0) { 14291553Srgrimes for (bp = bauds; bp->baud; bp++) 14301553Srgrimes if (BR == bp->baud) 14311553Srgrimes break; 14321553Srgrimes if (!bp->baud) { 14331553Srgrimes syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR); 14341553Srgrimes exit(1); 14351553Srgrimes } 143615032Ssef cfsetspeed(&ttybuf, bp->speed); 14371553Srgrimes } 143815032Ssef if (MS) { 143915032Ssef char *s = strdup(MS), *tmp; 144015032Ssef 144115032Ssef while (tmp = strsep (&s, ",")) { 144215032Ssef msearch(tmp, &ttybuf); 14431553Srgrimes } 14441553Srgrimes } 144515032Ssef if (MS || (BR > 0)) { 144615032Ssef if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { 144715032Ssef syslog(LOG_ERR, "%s: tcsetattr: %m", printer); 14481553Srgrimes } 14491553Srgrimes } 14501553Srgrimes} 14511553Srgrimes 14521553Srgrimes#if __STDC__ 14531553Srgrimes#include <stdarg.h> 14541553Srgrimes#else 14551553Srgrimes#include <varargs.h> 14561553Srgrimes#endif 14571553Srgrimes 145815648Sjoergstatic void 14591553Srgrimes#if __STDC__ 14601553Srgrimespstatus(const char *msg, ...) 14611553Srgrimes#else 14621553Srgrimespstatus(msg, va_alist) 14631553Srgrimes char *msg; 14641553Srgrimes va_dcl 14651553Srgrimes#endif 14661553Srgrimes{ 14671553Srgrimes register int fd; 14681553Srgrimes char buf[BUFSIZ]; 14691553Srgrimes va_list ap; 14701553Srgrimes#if __STDC__ 14711553Srgrimes va_start(ap, msg); 14721553Srgrimes#else 14731553Srgrimes va_start(ap); 14741553Srgrimes#endif 14751553Srgrimes 14761553Srgrimes umask(0); 14771553Srgrimes fd = open(ST, O_WRONLY|O_CREAT, 0664); 14781553Srgrimes if (fd < 0 || flock(fd, LOCK_EX) < 0) { 14791553Srgrimes syslog(LOG_ERR, "%s: %s: %m", printer, ST); 14801553Srgrimes exit(1); 14811553Srgrimes } 14821553Srgrimes ftruncate(fd, 0); 14831553Srgrimes (void)vsnprintf(buf, sizeof(buf), msg, ap); 14841553Srgrimes va_end(ap); 14851553Srgrimes strcat(buf, "\n"); 14861553Srgrimes (void) write(fd, buf, strlen(buf)); 14871553Srgrimes (void) close(fd); 14881553Srgrimes} 1489