/* printjob.c 4.2 83/05/16 */
* printjob -- print jobs in the queue.
* NOTE: the lock file is used to pass information to lpq and lprm.
* it does not need to be removed because file locks are dynamic.
#define DORETURN 0 /* absorb fork error */
#define DOABORT 1 /* abort if dofork fails */
char title
[80]; /* ``pr'' title */
FILE *cfp
; /* control file */
int pfd
; /* printer file descriptor */
int ofd
; /* output filter file descriptor */
int lfd
; /* lock file descriptor */
int pid
; /* pid of lpd process */
int prchild
; /* id of pr process */
int child
; /* id of any filters */
int ofilter
; /* id of output filter, if any */
int tof
= 1; /* top of form; init true if open does ff */
int remote
; /* non zero if sending files to remote */
extern banner(); /* big character printer */
char logname
[32]; /* user's login name */
char jobname
[32]; /* job or file name */
char class[32]; /* classification field */
char width
[10] = "-w"; /* page width in characters */
char length
[10] = "-l"; /* page length in lines */
char pxwidth
[10] = "-x"; /* page width in pixels */
char pxlength
[10] = "-y"; /* page length in pixels */
register struct queue
*q
, **qp
;
init(); /* set up capabilities */
(void) close(2); /* set up log file */
(void) open(LF
, FWRONLY
|FAPPEND
, 0);
dup2(2, 1); /* closes original connection */
pid
= getpid(); /* for use with lprm */
* uses short form file names
log("cannot chdir to %s", SD
);
if (stat(LO
, &stb
) == 0 && (stb
.st_mode
& 0100))
exit(0); /* printing disabled */
if ((lfd
= open(LO
, FWRONLY
|FCREATE
|FTRUNCATE
|FEXLOCK
|FNBLOCK
, 0664)) < 0) {
if (errno
== EWOULDBLOCK
) /* active deamon present */
log("cannot create %s", LO
);
* write process id for others to know
sprintf(line
, "%u\n", pid
);
pidoff
= i
= strlen(line
);
if (write(lfd
, line
, i
) != i
) {
log("cannot write daemon pid");
* search the spool directory for work and sort by queue order.
if ((nitems
= getq(&queue
)) < 0) {
log("can't scan spool directory %s", SD
);
if (nitems
== 0) /* no work to do */
openpr(); /* open printer or remote */
* we found something to do now do it --
* write the name of the current control file into the lock file
* so the spool queue program can tell what we're working on
for (qp
= queue
; nitems
--; free((char *) q
)) {
if (stat(q
->q_name
, &stb
) < 0)
(void) lseek(lfd
, pidoff
, 0);
(void) sprintf(line
, "%s\n", q
->q_name
);
if (write(lfd
, line
, i
) != i
)
log("can't write (%d) control file name", errno
);
* Check to see if we are supposed to stop printing.
if (stat(LO
, &stb
) == 0 && (stb
.st_mode
& 0100))
* Check to see if we should try reprinting the job.
kill(ofilter
, SIGCONT
); /* to be sure */
while ((i
= wait(0)) > 0 && i
!= ofilter
)
(void) close(pfd
); /* close printer */
(void) lseek(lfd
, pidoff
, 0);
if (write(lfd
, "\n", 1) != 1)
log("can't write (%d) control file name", errno
);
openpr(); /* try to reopen printer */
* search the spool directory for more work.
if ((nitems
= getq(&queue
)) < 0) {
log("can't scan spool directory %s", SD
);
if (nitems
== 0) { /* no more work to do */
(void) write(ofd
, FF
, strlen(FF
));
if (TR
!= NULL
) /* output trailer */
(void) write(ofd
, TR
, strlen(TR
));
char fonts
[4][50]; /* fonts for troff */
static char ifonts
[4][18] = {
* The remaining part is the reading of the control file (cf)
* and performing the various actions.
* Returns 0 if everthing was OK, 1 if we should try to reprint the job and
* -1 if a non-recoverable error occured.
if ((cfp
= fopen(file
, "r")) == NULL
) {
log("control file (%s) open failure <errno = %d>", file
, errno
);
strcpy(fonts
[i
], ifonts
[i
]);
* read the control file for work to do
* file format -- first character in the line is a command
* rest of the line is the argument.
* J -- "job name" on banner page
* C -- "class name" on banner page
* L -- "literal" user's name to print on banner
* H -- "host name" of machine where lpr was done
* P -- "person" user's login name
* I -- "indent" changes default indents driver
* must have stty/gtty avaialble
* f -- "file name" name of text file to print
* l -- "file name" text file with control chars
* p -- "file name" text file to print with pr(1)
* t -- "file name" troff(1) file to print
* d -- "file name" dvi file to print
* g -- "file name" plot(1G) file to print
* v -- "file name" plain raster file to print
* c -- "file name" cifplot file to print
* 1 -- "R font file" for troff
* 2 -- "I font file" for troff
* 3 -- "B font file" for troff
* 4 -- "S font file" for troff
* N -- "name" of file (used by lpq)
* U -- "unlink" name of file to remove
* (after we print it. (Pass 2 only)).
* M -- "mail" to user when done printing
* getline reads a line and expands tabs to blanks
if (RS
) { /* restricted */
if (getpwnam(logname
) == (struct passwd
*)0) {
else if (class[0] == '\0')
gethostname(class, sizeof (class));
case 'T': /* header title for pr */
case 'L': /* identification line */
case '1': /* troff fonts */
strcpy(fonts
[line
[0]-'1'], line
+1);
case 'W': /* page width */
default: /* some file to print */
if ((i
= print(line
[0], line
+1)) > 0) {
if (bombed
!= 2) /* already sent if 2 */
* clean-up incase another control file exists
* Set up the chain [ PR [ | {IF, OF} ] ] or {IF, TF, CF, VF}.
* Return -1 if a non-recoverable error occured, 1 if a recoverable error and
* Note: all filters take stdin as the file, stdout as the printer,
* stderr as the log file, and must not ignore SIGINT.
char *av
[15], buf
[BUFSIZ
];
int pid
, p
[2], stopped
= 0;
if ((fi
= open(file
, FRDONLY
, 0)) < 0) {
log("%s: open failure <errno = %d>", file
, errno
);
if (!SF
&& !tof
) { /* start on a fresh page */
(void) write(ofd
, FF
, strlen(FF
));
if (IF
== NULL
&& (format
== 'f' || format
== 'l')) {
while ((n
= read(fi
, buf
, BUFSIZ
)) > 0)
if (write(ofd
, buf
, n
) != n
) {
case 'p': /* print file using 'pr' */
if (IF
== NULL
) { /* use output filter */
av
[4] = *title
? title
: " ";
if ((prchild
= dofork(DORETURN
)) == 0) { /* child */
dup2(fi
, 0); /* file is stdin */
dup2(p
[1], 1); /* pipe is stdout */
for (n
= 3; n
< NOFILE
; n
++)
execl(PR
, "pr", width
, length
, "-h", *title
? title
: " ", 0);
log("cannot execl %s", PR
);
(void) close(p
[1]); /* close output side */
fi
= p
[0]; /* use pipe for input */
case 'f': /* print plain text file */
case 'l': /* like 'f' but pass control characters */
case 'r': /* print a fortran text file */
case 't': /* print troff output */
case 'd': /* print tex output */
(void) unlink(".railmag");
if ((fo
= creat(".railmag", FILMOD
)) < 0) {
log("cannot create .railmag");
(void) unlink(".railmag");
for (n
= 0; n
< 4; n
++) {
(void) write(fo
, "/usr/lib/vfont/", 15);
(void) write(fo
, fonts
[n
], strlen(fonts
[n
]));
(void) write(fo
, "\n", 1);
prog
= (format
== 't') ? TF
: DF
;
case 'c': /* print cifplot output */
case 'g': /* print plot(1G) output */
case 'v': /* print raster output */
log("illegal format character '%c'", format
);
if ((av
[0] = rindex(prog
, '/')) != NULL
)
if (ofilter
> 0) { /* stop output filter */
while ((pid
= wait3(&status
, WUNTRACED
, 0)) > 0 && pid
!= ofilter
)
if (status
.w_stopval
!= WSTOPPED
) {
log("output filter died (%d)", status
.w_retcode
);
if ((child
= dofork(DORETURN
)) == 0) { /* child */
for (n
= 3; n
< NOFILE
; n
++)
log("cannot execl %s", prog
);
while ((pid
= wait(&status
)) > 0 && pid
!= child
)
if (stopped
) { /* restart output filter */
if (kill(ofilter
, SIGCONT
) < 0) {
log("cannot restart output filter");
if (!WIFEXITED(status
) || status
.w_retcode
> 1) {
log("Daemon Filter '%c' Malfunction (%d)", format
, status
.w_retcode
);
} else if (status
.w_retcode
== 1)
* Send the daemon control file (cf) and any data files.
* Return -1 if a non-recoverable error occured, 1 if a recoverable error and
register int linelen
, err
= 0;
if ((cfp
= fopen(file
, "r")) == NULL
) {
log("control file (%s) open failure <errno = %d>", file
, errno
);
* read the control file for work to do
* file format -- first character in the line is a command
* rest of the line is the argument.
* commands of interest are:
* a-z -- "file name" name of file to print
* U -- "unlink" name of file to remove
* (after we print it. (Pass 2 only)).
if (line
[0] >= 'a' && line
[0] <= 'z') {
while (linelen
= getline(cfp
))
if ((err
= sendfile('\3', last
+1)) > 0) {
if (!err
&& sendfile('\2', file
) > 0) {
* clean-up incase another control file exists
* Send a data file to the remote machine and spool it.
* Return positive if we should try resending.
if ((f
= open(file
, FRDONLY
, 0)) < 0 || fstat(f
, &stb
) < 0) {
log("file (%s) open failure <errno = %d>", file
, errno
);
(void) sprintf(buf
, "%c%d %s\n", type
, stb
.st_size
, file
);
if (write(pfd
, buf
, amt
) != amt
)
for (i
= 0; i
< stb
.st_size
; i
+= BUFSIZ
) {
if (i
+ amt
> stb
.st_size
)
if (sizerr
== 0 && read(f
, buf
, amt
) != amt
)
if (write(pfd
, buf
, amt
) != amt
)
log("%s: changed size", file
);
(void) write(pfd
, "\1", 1); /* tell recvjob to ignore this file */
if (write(pfd
, "", 1) != 1)
* Check to make sure there have been no errors and that both programs
* are in sync with eachother.
* Return non-zero if the connection was lost.
if (read(pfd
, &resp
, 1) != 1 || resp
!= '\0') {
log("lost connection or error in recvjob");
(void) write(ofd
, FF
, strlen(FF
));
if (SB
) { /* short banner only */
(void) write(ofd
, class, strlen(class));
(void) write(ofd
, ":", 1);
(void) write(ofd
, name1
, strlen(name1
));
(void) write(ofd
, " Job: ", 7);
(void) write(ofd
, name2
, strlen(name2
));
(void) write(ofd
, " Date: ", 8);
(void) write(ofd
, ctime(&tvec
), 24);
(void) write(ofd
, "\n", 1);
} else { /* normal banner */
(void) write(ofd
, "\n\n\n", 3);
scan_out(ofd
, name1
, '\0');
(void) write(ofd
, "\n\n", 2);
scan_out(ofd
, name2
, '\0');
(void) write(ofd
,"\n\n\n",3);
scan_out(ofd
, class, '\0');
(void) write(ofd
, "\n\n\n\n\t\t\t\t\tJob: ", 15);
(void) write(ofd
, name2
, strlen(name2
));
(void) write(ofd
, "\n\t\t\t\t\tDate: ", 12);
(void) write(ofd
, ctime(&tvec
), 24);
(void) write(ofd
, "\n", 1);
(void) write(ofd
, FF
, strlen(FF
));
for (scnwidth
= WIDTH
; --scnwidth
;) {
*p
++ = key
& 0200 ? c
: BACKGND
;
#define TRC(q) (((q)-' ')&0177)
scan_out(scfd
, scsp
, dlm
)
char outbuf
[LINELEN
+1], *sp
, c
, cc
;
extern char scnkey
[][HEIGHT
]; /* in lpdchar.c */
for (scnhgt
= 0; scnhgt
++ < HEIGHT
+DROP
; ) {
d
= dropit(c
= TRC(cc
= *sp
++));
if ((!d
&& scnhgt
> HEIGHT
) || (scnhgt
<= DROP
&& d
))
strp
= scnline(scnkey
[c
][scnhgt
-1-d
], strp
, cc
);
if (*sp
== dlm
|| *sp
== '\0' || nchrs
++ >= PW
/(WIDTH
+1)-1)
while (*--strp
== BACKGND
&& strp
>= outbuf
)
(void) write(scfd
, outbuf
, strp
-outbuf
);
* tell people about job completion
if ((stat
= dofork(DORETURN
)) == 0) { /* child */
for (i
= 3; i
< NOFILE
; i
++)
if ((cp
= rindex(MAIL
, '/')) != NULL
)
sprintf(buf
, "%s@%s", line
+1, host
);
} else if (stat
> 0) { /* parent */
printf("To: %s\n", line
+1);
printf("Subject: printer job\n\n");
printf("Your printer job ");
printf("(%s) ", jobname
);
printf("\ncompleted successfully\n");
printf("\ncould not be printed\n");
printf("\ncould not be printed without an account on %s\n", host
);
* dofork - fork with retries on failure
for (i
= 0; i
< 20; i
++) {
if ((pid
= fork()) < 0) {
* Child should run as daemon instead of root
log("bad action (%d) to dofork", action
);
* Cleanup child processes when a SIGINT is caught.
if ((status
= pgetent(line
, printer
)) < 0) {
log("can't open printer description file");
} else if (status
== 0) {
if ((LP
= pgetstr("lp", &bp
)) == NULL
)
if ((RP
= pgetstr("rp", &bp
)) == NULL
)
if ((LO
= pgetstr("lo", &bp
)) == NULL
)
if ((ST
= pgetstr("st", &bp
)) == NULL
)
if ((LF
= pgetstr("lf", &bp
)) == NULL
)
if ((SD
= pgetstr("sd", &bp
)) == NULL
)
if ((DU
= pgetnum("du")) < 0)
if ((FF
= pgetstr("ff", &bp
)) == NULL
)
if ((PW
= pgetnum("pw")) < 0)
sprintf(&width
[2], "%d", PW
);
if ((PL
= pgetnum("pl")) < 0)
sprintf(&length
[2], "%d", PL
);
if ((PX
= pgetnum("px")) < 0)
sprintf(&pxwidth
[2], "%d", PX
);
if ((PY
= pgetnum("py")) < 0)
sprintf(&pxlength
[2], "%d", PY
);
if ((FC
= pgetnum("fc")) < 0)
if ((FS
= pgetnum("fs")) < 0)
if ((XC
= pgetnum("xc")) < 0)
if ((XS
= pgetnum("xs")) < 0)
* Acquire line printer or remote connection.
for (i
= 1; ; i
= i
< 32 ? i
<< 1 : i
) {
pfd
= open(LP
, RW
? FRDWR
: FWRONLY
, 0);
log("cannot open %s", LP
);
status("waiting for %s to become ready (offline ?)", printer
);
status("%s is ready and printing", printer
);
for (i
= 1; ; i
= i
< 512 ? i
<< 1 : i
) {
(void) sprintf(line
, "\2%s\n", RP
);
if (write(pfd
, line
, n
) != n
)
status("waiting for %s to come up", RM
);
status("sending to %s", RM
);
log("no line printer device or remote machine name");
* Start up an output filter, if needed.
if ((ofilter
= dofork(DOABORT
)) == 0) { /* child */
dup2(p
[0], 0); /* pipe is std in */
dup2(pfd
, 1); /* printer is std out */
for (i
= 3; i
< NOFILE
; i
++)
if ((cp
= rindex(OF
, '/')) == NULL
)
execl(OF
, cp
, width
, length
, 0);
log("can't execl output filter %s", OF
);
(void) close(p
[0]); /* close input side */
ofd
= p
[1]; /* use pipe for output */
register struct bauds
*bp
;
if (ioctl(pfd
, TIOCEXCL
, (char *)0) < 0) {
log("cannot set exclusive-use");
if (ioctl(pfd
, TIOCGETP
, (char *)&ttybuf
) < 0) {
log("cannot get tty parameters");
for (bp
= bauds
; bp
->baud
; bp
++)
log("illegal baud rate %d", BR
);
ttybuf
.sg_ispeed
= ttybuf
.sg_ospeed
= bp
->speed
;
if (ioctl(pfd
, TIOCSETP
, (char *)&ttybuf
) < 0) {
log("cannot set tty parameters");
if (ioctl(pfd
, TIOCLBIC
, &XC
) < 0) {
log("cannot set local tty parameters");
if (ioctl(pfd
, TIOCLBIS
, &XS
) < 0) {
log("cannot set local tty parameters");
if ((fd
= open(ST
, FWRONLY
|FCREATE
|FTRUNCATE
|FEXLOCK
, 0664)) < 0)
fatal("cannot create status file");
sprintf(buf
, msg
, a1
, a2
, a3
);
(void) write(fd
, buf
, strlen(buf
));