written by Eric Allman; add Berkeley specific header
[unix-history] / usr / src / usr.sbin / lpr / lpd / printjob.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
5f84f8f0 7#ifndef lint
9e8db329 8static char sccsid[] = "@(#)printjob.c 5.2 (Berkeley) 9/17/85";
d0aeaf5a 9#endif not lint
5f84f8f0 10
df7d5679
RC
11/*
12 * printjob -- print jobs in the queue.
13 *
14 * NOTE: the lock file is used to pass information to lpq and lprm.
15 * it does not need to be removed because file locks are dynamic.
16 */
17
18#include "lp.h"
19
47f0387b
RC
20#define DORETURN 0 /* absorb fork error */
21#define DOABORT 1 /* abort if dofork fails */
22
98d189de
RC
23/*
24 * Error tokens
25 */
26#define REPRINT -2
27#define ERROR -1
28#define OK 0
29#define FATALERR 1
30#define NOACCT 2
31#define FILTERERR 3
32#define ACCESS 4
33
47f0387b
RC
34char title[80]; /* ``pr'' title */
35FILE *cfp; /* control file */
36int pfd; /* printer file descriptor */
37int ofd; /* output filter file descriptor */
38int lfd; /* lock file descriptor */
39int pid; /* pid of lpd process */
40int prchild; /* id of pr process */
41int child; /* id of any filters */
42int ofilter; /* id of output filter, if any */
43int tof; /* true if at top of form */
44int remote; /* true if sending files to remote */
98d189de
RC
45dev_t fdev; /* device of file pointed to by symlink */
46ino_t fino; /* inode of file pointed to by symlink */
47f0387b
RC
47
48char fromhost[32]; /* user's host machine */
49char logname[32]; /* user's login name */
50char jobname[100]; /* job or file name */
51char class[32]; /* classification field */
52char width[10] = "-w"; /* page width in characters */
53char length[10] = "-l"; /* page length in lines */
54char pxwidth[10] = "-x"; /* page width in pixels */
55char pxlength[10] = "-y"; /* page length in pixels */
56char indent[10] = "-i0"; /* indentation size in characters */
57char tmpfile[] = "errsXXXXXX"; /* file name for filter output */
df7d5679
RC
58
59printjob()
60{
61 struct stat stb;
62 register struct queue *q, **qp;
63 struct queue **queue;
64 register int i, nitems;
65 long pidoff;
47f0387b
RC
66 int count = 0;
67 extern int abortpr();
df7d5679 68
df7d5679 69 init(); /* set up capabilities */
8fed920b 70 (void) write(1, "", 1); /* ack that daemon is started */
126fc76f
EA
71 (void) close(2); /* set up log file */
72 if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
73 syslog(LOG_ERR, "%s: %m", LF);
74 (void) open("/dev/null", O_WRONLY);
75 }
47f0387b 76 setgid(getegid());
88f026f7 77 pid = getpid(); /* for use with lprm */
df7d5679 78 setpgrp(0, pid);
47f0387b
RC
79 signal(SIGHUP, abortpr);
80 signal(SIGINT, abortpr);
81 signal(SIGQUIT, abortpr);
82 signal(SIGTERM, abortpr);
df7d5679 83
fbd83c9f
RC
84 (void) mktemp(tmpfile);
85
df7d5679
RC
86 /*
87 * uses short form file names
88 */
89 if (chdir(SD) < 0) {
47f0387b 90 syslog(LOG_ERR, "%s: %m", SD);
df7d5679
RC
91 exit(1);
92 }
88f026f7
RC
93 if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
94 exit(0); /* printing disabled */
845f1693 95 lfd = open(LO, O_WRONLY|O_CREAT, 0644);
c6d1c018 96 if (lfd < 0) {
47f0387b 97 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
c6d1c018
RC
98 exit(1);
99 }
100 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
df7d5679
RC
101 if (errno == EWOULDBLOCK) /* active deamon present */
102 exit(0);
47f0387b 103 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
df7d5679
RC
104 exit(1);
105 }
adec4d9e 106 ftruncate(lfd, 0);
df7d5679
RC
107 /*
108 * write process id for others to know
109 */
110 sprintf(line, "%u\n", pid);
111 pidoff = i = strlen(line);
88f026f7 112 if (write(lfd, line, i) != i) {
47f0387b 113 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
df7d5679
RC
114 exit(1);
115 }
df7d5679
RC
116 /*
117 * search the spool directory for work and sort by queue order.
118 */
df7d5679 119 if ((nitems = getq(&queue)) < 0) {
47f0387b 120 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
df7d5679
RC
121 exit(1);
122 }
88f026f7 123 if (nitems == 0) /* no work to do */
df7d5679 124 exit(0);
c6d1c018
RC
125 if (stb.st_mode & 01) { /* reset queue flag */
126 if (fchmod(lfd, stb.st_mode & 0776) < 0)
47f0387b 127 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
c6d1c018 128 }
88f026f7
RC
129 openpr(); /* open printer or remote */
130again:
df7d5679
RC
131 /*
132 * we found something to do now do it --
133 * write the name of the current control file into the lock file
134 * so the spool queue program can tell what we're working on
135 */
136 for (qp = queue; nitems--; free((char *) q)) {
137 q = *qp++;
138 if (stat(q->q_name, &stb) < 0)
139 continue;
88f026f7 140 restart:
df7d5679
RC
141 (void) lseek(lfd, pidoff, 0);
142 (void) sprintf(line, "%s\n", q->q_name);
143 i = strlen(line);
144 if (write(lfd, line, i) != i)
47f0387b 145 syslog(LOG_ERR, "%s: %s: %m", printer, LO);
df7d5679
RC
146 if (!remote)
147 i = printit(q->q_name);
148 else
149 i = sendit(q->q_name);
88f026f7 150 /*
c6d1c018
RC
151 * Check to see if we are supposed to stop printing or
152 * if we are to rebuild the queue.
88f026f7 153 */
c6d1c018 154 if (fstat(lfd, &stb) == 0) {
47f0387b 155 /* stop printing before starting next job? */
c6d1c018
RC
156 if (stb.st_mode & 0100)
157 goto done;
47f0387b 158 /* rebuild queue (after lpc topq) */
c6d1c018
RC
159 if (stb.st_mode & 01) {
160 for (free((char *) q); nitems--; free((char *) q))
161 q = *qp++;
162 if (fchmod(lfd, stb.st_mode & 0776) < 0)
47f0387b
RC
163 syslog(LOG_WARNING, "%s: %s: %m",
164 printer, LO);
c6d1c018
RC
165 break;
166 }
167 }
98d189de 168 if (i == OK) /* file ok and printed */
845f1693 169 count++;
98d189de 170 else if (i == REPRINT) { /* try reprinting the job */
47f0387b 171 syslog(LOG_INFO, "restarting %s", printer);
df7d5679
RC
172 if (ofilter > 0) {
173 kill(ofilter, SIGCONT); /* to be sure */
174 (void) close(ofd);
175 while ((i = wait(0)) > 0 && i != ofilter)
176 ;
177 ofilter = 0;
178 }
88f026f7 179 (void) close(pfd); /* close printer */
fbd83c9f 180 if (ftruncate(lfd, pidoff) < 0)
47f0387b 181 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
88f026f7 182 openpr(); /* try to reopen printer */
df7d5679
RC
183 goto restart;
184 }
185 }
186 free((char *) queue);
88f026f7
RC
187 /*
188 * search the spool directory for more work.
189 */
190 if ((nitems = getq(&queue)) < 0) {
47f0387b 191 syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
88f026f7
RC
192 exit(1);
193 }
194 if (nitems == 0) { /* no more work to do */
195 done:
845f1693
RC
196 if (count > 0) { /* Files actually printed */
197 if (!SF && !tof)
198 (void) write(ofd, FF, strlen(FF));
199 if (TR != NULL) /* output trailer */
200 (void) write(ofd, TR, strlen(TR));
201 }
fbd83c9f 202 (void) unlink(tmpfile);
88f026f7
RC
203 exit(0);
204 }
df7d5679
RC
205 goto again;
206}
207
208char fonts[4][50]; /* fonts for troff */
209
47f0387b 210char ifonts[4][18] = {
df7d5679
RC
211 "/usr/lib/vfont/R",
212 "/usr/lib/vfont/I",
213 "/usr/lib/vfont/B",
214 "/usr/lib/vfont/S"
215};
216
217/*
218 * The remaining part is the reading of the control file (cf)
219 * and performing the various actions.
df7d5679
RC
220 */
221printit(file)
222 char *file;
223{
224 register int i;
98d189de
RC
225 char *cp;
226 int bombed = OK;
df7d5679
RC
227
228 /*
98d189de 229 * open control file; ignore if no longer there.
df7d5679
RC
230 */
231 if ((cfp = fopen(file, "r")) == NULL) {
47f0387b 232 syslog(LOG_INFO, "%s: %s: %m", printer, file);
98d189de 233 return(OK);
df7d5679
RC
234 }
235 /*
236 * Reset troff fonts.
237 */
238 for (i = 0; i < 4; i++)
239 strcpy(fonts[i], ifonts[i]);
155b93d2 240 strcpy(width+2, "0");
d2d55096 241 strcpy(indent+2, "0");
df7d5679
RC
242
243 /*
244 * read the control file for work to do
245 *
246 * file format -- first character in the line is a command
247 * rest of the line is the argument.
248 * valid commands are:
249 *
98d189de 250 * S -- "stat info" for symbolic link protection
df7d5679
RC
251 * J -- "job name" on banner page
252 * C -- "class name" on banner page
253 * L -- "literal" user's name to print on banner
254 * T -- "title" for pr
255 * H -- "host name" of machine where lpr was done
256 * P -- "person" user's login name
6bd17dac 257 * I -- "indent" amount to indent output
df7d5679
RC
258 * f -- "file name" name of text file to print
259 * l -- "file name" text file with control chars
260 * p -- "file name" text file to print with pr(1)
261 * t -- "file name" troff(1) file to print
a4f59913 262 * n -- "file name" ditroff(1) file to print
df7d5679
RC
263 * d -- "file name" dvi file to print
264 * g -- "file name" plot(1G) file to print
265 * v -- "file name" plain raster file to print
266 * c -- "file name" cifplot file to print
267 * 1 -- "R font file" for troff
268 * 2 -- "I font file" for troff
269 * 3 -- "B font file" for troff
270 * 4 -- "S font file" for troff
271 * N -- "name" of file (used by lpq)
272 * U -- "unlink" name of file to remove
273 * (after we print it. (Pass 2 only)).
274 * M -- "mail" to user when done printing
275 *
276 * getline reads a line and expands tabs to blanks
277 */
278
279 /* pass 1 */
280
281 while (getline(cfp))
282 switch (line[0]) {
283 case 'H':
845f1693 284 strcpy(fromhost, line+1);
df7d5679 285 if (class[0] == '\0')
731697c1 286 strncpy(class, line+1, sizeof(class)-1);
df7d5679
RC
287 continue;
288
289 case 'P':
731697c1 290 strncpy(logname, line+1, sizeof(logname)-1);
88f026f7
RC
291 if (RS) { /* restricted */
292 if (getpwnam(logname) == (struct passwd *)0) {
98d189de 293 bombed = NOACCT;
fbd83c9f 294 sendmail(line+1, bombed);
88f026f7
RC
295 goto pass2;
296 }
297 }
df7d5679
RC
298 continue;
299
98d189de
RC
300 case 'S':
301 cp = line+1;
302 i = 0;
303 while (*cp >= '0' && *cp <= '9')
304 i = i * 10 + (*cp++ - '0');
305 fdev = i;
306 cp++;
307 i = 0;
308 while (*cp >= '0' && *cp <= '9')
309 i = i * 10 + (*cp++ - '0');
310 fino = i;
311 continue;
312
df7d5679
RC
313 case 'J':
314 if (line[1] != '\0')
731697c1 315 strncpy(jobname, line+1, sizeof(jobname)-1);
df7d5679
RC
316 else
317 strcpy(jobname, " ");
318 continue;
319
320 case 'C':
321 if (line[1] != '\0')
731697c1 322 strncpy(class, line+1, sizeof(class)-1);
df7d5679 323 else if (class[0] == '\0')
fbd83c9f 324 gethostname(class, sizeof(class));
df7d5679
RC
325 continue;
326
327 case 'T': /* header title for pr */
731697c1 328 strncpy(title, line+1, sizeof(title)-1);
df7d5679
RC
329 continue;
330
331 case 'L': /* identification line */
1e7998ee 332 if (!SH && !HL)
df7d5679
RC
333 banner(line+1, jobname);
334 continue;
335
336 case '1': /* troff fonts */
337 case '2':
338 case '3':
339 case '4':
340 if (line[1] != '\0')
341 strcpy(fonts[line[0]-'1'], line+1);
342 continue;
343
344 case 'W': /* page width */
731697c1 345 strncpy(width+2, line+1, sizeof(width)-3);
df7d5679
RC
346 continue;
347
6bd17dac 348 case 'I': /* indent amount */
731697c1 349 strncpy(indent+2, line+1, sizeof(indent)-3);
6bd17dac
RC
350 continue;
351
df7d5679 352 default: /* some file to print */
fbd83c9f 353 switch (i = print(line[0], line+1)) {
98d189de
RC
354 case ERROR:
355 if (bombed == OK)
356 bombed = FATALERR;
fbd83c9f 357 break;
98d189de 358 case REPRINT:
df7d5679 359 (void) fclose(cfp);
98d189de
RC
360 return(REPRINT);
361 case FILTERERR:
362 case ACCESS:
363 bombed = i;
fbd83c9f
RC
364 sendmail(logname, bombed);
365 }
df7d5679
RC
366 title[0] = '\0';
367 continue;
368
df7d5679
RC
369 case 'N':
370 case 'U':
371 case 'M':
372 continue;
373 }
374
375 /* pass 2 */
376
88f026f7 377pass2:
df7d5679
RC
378 fseek(cfp, 0L, 0);
379 while (getline(cfp))
380 switch (line[0]) {
1e7998ee
RC
381 case 'L': /* identification line */
382 if (!SH && HL)
383 banner(line+1, jobname);
384 continue;
385
df7d5679 386 case 'M':
98d189de 387 if (bombed < NOACCT) /* already sent if >= NOACCT */
fbd83c9f 388 sendmail(line+1, bombed);
df7d5679
RC
389 continue;
390
391 case 'U':
392 (void) unlink(line+1);
393 }
394 /*
fbd83c9f 395 * clean-up in case another control file exists
df7d5679
RC
396 */
397 (void) fclose(cfp);
398 (void) unlink(file);
98d189de 399 return(bombed == OK ? OK : ERROR);
df7d5679
RC
400}
401
402/*
403 * Print a file.
a4f59913 404 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
fbd83c9f
RC
405 * Return -1 if a non-recoverable error occured,
406 * 2 if the filter detected some errors (but printed the job anyway),
407 * 1 if we should try to reprint this job and
df7d5679
RC
408 * 0 if all is well.
409 * Note: all filters take stdin as the file, stdout as the printer,
410 * stderr as the log file, and must not ignore SIGINT.
411 */
412print(format, file)
413 int format;
414 char *file;
415{
fbd83c9f 416 register int n;
df7d5679 417 register char *prog;
fbd83c9f 418 int fi, fo;
df7d5679
RC
419 char *av[15], buf[BUFSIZ];
420 int pid, p[2], stopped = 0;
421 union wait status;
98d189de 422 struct stat stb;
df7d5679 423
98d189de
RC
424 if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
425 return(ERROR);
426 /*
427 * Check to see if data file is a symbolic link. If so, it should
428 * still point to the same file or someone is trying to print
429 * something he shouldn't.
430 */
431 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
432 (stb.st_dev != fdev || stb.st_ino != fino))
433 return(ACCESS);
df7d5679
RC
434 if (!SF && !tof) { /* start on a fresh page */
435 (void) write(ofd, FF, strlen(FF));
436 tof = 1;
437 }
438 if (IF == NULL && (format == 'f' || format == 'l')) {
439 tof = 0;
440 while ((n = read(fi, buf, BUFSIZ)) > 0)
441 if (write(ofd, buf, n) != n) {
442 (void) close(fi);
98d189de 443 return(REPRINT);
df7d5679
RC
444 }
445 (void) close(fi);
98d189de 446 return(OK);
df7d5679
RC
447 }
448 switch (format) {
449 case 'p': /* print file using 'pr' */
450 if (IF == NULL) { /* use output filter */
451 prog = PR;
452 av[0] = "pr";
453 av[1] = width;
454 av[2] = length;
455 av[3] = "-h";
456 av[4] = *title ? title : " ";
457 av[5] = 0;
458 fo = ofd;
459 goto start;
460 }
461 pipe(p);
462 if ((prchild = dofork(DORETURN)) == 0) { /* child */
463 dup2(fi, 0); /* file is stdin */
464 dup2(p[1], 1); /* pipe is stdout */
465 for (n = 3; n < NOFILE; n++)
466 (void) close(n);
467 execl(PR, "pr", width, length, "-h", *title ? title : " ", 0);
47f0387b 468 syslog(LOG_ERR, "cannot execl %s", PR);
df7d5679
RC
469 exit(2);
470 }
471 (void) close(p[1]); /* close output side */
472 (void) close(fi);
473 if (prchild < 0) {
474 prchild = 0;
475 (void) close(p[0]);
98d189de 476 return(ERROR);
df7d5679
RC
477 }
478 fi = p[0]; /* use pipe for input */
479 case 'f': /* print plain text file */
480 prog = IF;
481 av[1] = width;
482 av[2] = length;
6bd17dac
RC
483 av[3] = indent;
484 n = 4;
df7d5679
RC
485 break;
486 case 'l': /* like 'f' but pass control characters */
487 prog = IF;
62a109c4 488 av[1] = "-c";
df7d5679
RC
489 av[2] = width;
490 av[3] = length;
6bd17dac
RC
491 av[4] = indent;
492 n = 5;
df7d5679 493 break;
88f026f7
RC
494 case 'r': /* print a fortran text file */
495 prog = RF;
496 av[1] = width;
497 av[2] = length;
498 n = 3;
499 break;
df7d5679 500 case 't': /* print troff output */
a4f59913 501 case 'n': /* print ditroff output */
88f026f7 502 case 'd': /* print tex output */
df7d5679 503 (void) unlink(".railmag");
88f026f7 504 if ((fo = creat(".railmag", FILMOD)) < 0) {
47f0387b 505 syslog(LOG_ERR, "%s: cannot create .railmag", printer);
df7d5679
RC
506 (void) unlink(".railmag");
507 } else {
508 for (n = 0; n < 4; n++) {
509 if (fonts[n][0] != '/')
510 (void) write(fo, "/usr/lib/vfont/", 15);
511 (void) write(fo, fonts[n], strlen(fonts[n]));
512 (void) write(fo, "\n", 1);
513 }
514 (void) close(fo);
515 }
a4f59913 516 prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
88f026f7
RC
517 av[1] = pxwidth;
518 av[2] = pxlength;
519 n = 3;
df7d5679
RC
520 break;
521 case 'c': /* print cifplot output */
522 prog = CF;
88f026f7
RC
523 av[1] = pxwidth;
524 av[2] = pxlength;
525 n = 3;
df7d5679
RC
526 break;
527 case 'g': /* print plot(1G) output */
528 prog = GF;
88f026f7
RC
529 av[1] = pxwidth;
530 av[2] = pxlength;
531 n = 3;
df7d5679
RC
532 break;
533 case 'v': /* print raster output */
534 prog = VF;
88f026f7
RC
535 av[1] = pxwidth;
536 av[2] = pxlength;
537 n = 3;
df7d5679
RC
538 break;
539 default:
540 (void) close(fi);
47f0387b
RC
541 syslog(LOG_ERR, "%s: illegal format character '%c'",
542 printer, format);
98d189de 543 return(ERROR);
df7d5679
RC
544 }
545 if ((av[0] = rindex(prog, '/')) != NULL)
546 av[0]++;
547 else
548 av[0] = prog;
549 av[n++] = "-n";
550 av[n++] = logname;
551 av[n++] = "-h";
845f1693 552 av[n++] = fromhost;
df7d5679
RC
553 av[n++] = AF;
554 av[n] = 0;
555 fo = pfd;
556 if (ofilter > 0) { /* stop output filter */
557 write(ofd, "\031\1", 2);
558 while ((pid = wait3(&status, WUNTRACED, 0)) > 0 && pid != ofilter)
559 ;
560 if (status.w_stopval != WSTOPPED) {
561 (void) close(fi);
47f0387b
RC
562 syslog(LOG_WARNING, "%s: output filter died (%d)",
563 printer, status.w_retcode);
98d189de 564 return(REPRINT);
df7d5679
RC
565 }
566 stopped++;
567 }
568start:
569 if ((child = dofork(DORETURN)) == 0) { /* child */
570 dup2(fi, 0);
571 dup2(fo, 1);
9b492587 572 n = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
fbd83c9f
RC
573 if (n >= 0)
574 dup2(n, 2);
df7d5679
RC
575 for (n = 3; n < NOFILE; n++)
576 (void) close(n);
577 execv(prog, av);
47f0387b 578 syslog(LOG_ERR, "cannot execv %s", prog);
df7d5679
RC
579 exit(2);
580 }
581 (void) close(fi);
582 if (child < 0)
583 status.w_retcode = 100;
584 else
585 while ((pid = wait(&status)) > 0 && pid != child)
586 ;
587 child = 0;
588 prchild = 0;
589 if (stopped) { /* restart output filter */
590 if (kill(ofilter, SIGCONT) < 0) {
47f0387b 591 syslog(LOG_ERR, "cannot restart output filter");
df7d5679
RC
592 exit(1);
593 }
594 }
595 tof = 0;
fbd83c9f 596 if (!WIFEXITED(status)) {
47f0387b
RC
597 syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
598 printer, format, status.w_termsig);
98d189de
RC
599 return(ERROR);
600 }
601 switch (status.w_retcode) {
602 case 0:
603 tof = 1;
604 return(OK);
605 case 1:
606 return(REPRINT);
607 default:
47f0387b
RC
608 syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
609 printer, format, status.w_retcode);
98d189de
RC
610 case 2:
611 return(ERROR);
612 }
df7d5679
RC
613}
614
615/*
616 * Send the daemon control file (cf) and any data files.
617 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
618 * 0 if all is well.
619 */
620sendit(file)
621 char *file;
622{
98d189de
RC
623 register int i, err = OK;
624 char *cp, last[BUFSIZ];
df7d5679
RC
625
626 /*
627 * open control file
628 */
47f0387b 629 if ((cfp = fopen(file, "r")) == NULL)
98d189de 630 return(OK);
df7d5679
RC
631 /*
632 * read the control file for work to do
633 *
634 * file format -- first character in the line is a command
635 * rest of the line is the argument.
636 * commands of interest are:
637 *
638 * a-z -- "file name" name of file to print
639 * U -- "unlink" name of file to remove
640 * (after we print it. (Pass 2 only)).
641 */
642
643 /*
644 * pass 1
645 */
646 while (getline(cfp)) {
647 again:
98d189de
RC
648 if (line[0] == 'S') {
649 cp = line+1;
650 i = 0;
651 while (*cp >= '0' && *cp <= '9')
652 i = i * 10 + (*cp++ - '0');
653 fdev = i;
654 cp++;
655 i = 0;
656 while (*cp >= '0' && *cp <= '9')
657 i = i * 10 + (*cp++ - '0');
658 fino = i;
659 continue;
660 }
df7d5679
RC
661 if (line[0] >= 'a' && line[0] <= 'z') {
662 strcpy(last, line);
98d189de 663 while (i = getline(cfp))
df7d5679
RC
664 if (strcmp(last, line))
665 break;
98d189de
RC
666 switch (sendfile('\3', last+1)) {
667 case OK:
668 if (i)
669 goto again;
df7d5679 670 break;
98d189de
RC
671 case REPRINT:
672 (void) fclose(cfp);
673 return(REPRINT);
674 case ACCESS:
675 sendmail(logname, ACCESS);
676 case ERROR:
677 err = ERROR;
678 }
df7d5679
RC
679 break;
680 }
681 }
98d189de 682 if (err == OK && sendfile('\2', file) > 0) {
df7d5679 683 (void) fclose(cfp);
98d189de 684 return(REPRINT);
df7d5679
RC
685 }
686 /*
687 * pass 2
688 */
689 fseek(cfp, 0L, 0);
690 while (getline(cfp))
691 if (line[0] == 'U')
692 (void) unlink(line+1);
693 /*
98d189de 694 * clean-up in case another control file exists
df7d5679
RC
695 */
696 (void) fclose(cfp);
697 (void) unlink(file);
98d189de 698 return(err);
df7d5679
RC
699}
700
701/*
702 * Send a data file to the remote machine and spool it.
703 * Return positive if we should try resending.
704 */
705sendfile(type, file)
706 char type, *file;
707{
708 register int f, i, amt;
709 struct stat stb;
710 char buf[BUFSIZ];
47f0387b 711 int sizerr, resp;
df7d5679 712
98d189de
RC
713 if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
714 return(ERROR);
715 /*
716 * Check to see if data file is a symbolic link. If so, it should
717 * still point to the same file or someone is trying to print something
718 * he shouldn't.
719 */
720 if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
721 (stb.st_dev != fdev || stb.st_ino != fino))
722 return(ACCESS);
df7d5679
RC
723 (void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
724 amt = strlen(buf);
47f0387b
RC
725 for (i = 0; ; i++) {
726 if (write(pfd, buf, amt) != amt ||
727 (resp = response()) < 0 || resp == '\1') {
728 (void) close(f);
98d189de 729 return(REPRINT);
47f0387b
RC
730 } else if (resp == '\0')
731 break;
732 if (i == 0)
733 status("no space on remote; waiting for queue to drain");
734 if (i == 10)
076ae92c 735 syslog(LOG_ALERT, "%s: can't send to %s; queue full",
47f0387b
RC
736 printer, RM);
737 sleep(5 * 60);
48ca7288 738 }
47f0387b
RC
739 if (i)
740 status("sending to %s", RM);
df7d5679
RC
741 sizerr = 0;
742 for (i = 0; i < stb.st_size; i += BUFSIZ) {
743 amt = BUFSIZ;
744 if (i + amt > stb.st_size)
745 amt = stb.st_size - i;
746 if (sizerr == 0 && read(f, buf, amt) != amt)
747 sizerr = 1;
48ca7288
RC
748 if (write(pfd, buf, amt) != amt) {
749 (void) close(f);
98d189de 750 return(REPRINT);
48ca7288 751 }
df7d5679
RC
752 }
753 (void) close(f);
754 if (sizerr) {
47f0387b 755 syslog(LOG_INFO, "%s: %s: changed size", printer, file);
98d189de
RC
756 /* tell recvjob to ignore this file */
757 (void) write(pfd, "\1", 1);
758 return(ERROR);
759 }
760 if (write(pfd, "", 1) != 1 || response())
761 return(REPRINT);
762 return(OK);
df7d5679
RC
763}
764
765/*
766 * Check to make sure there have been no errors and that both programs
767 * are in sync with eachother.
768 * Return non-zero if the connection was lost.
769 */
47f0387b 770response()
df7d5679
RC
771{
772 char resp;
773
47f0387b
RC
774 if (read(pfd, &resp, 1) != 1) {
775 syslog(LOG_INFO, "%s: lost connection", printer);
776 return(-1);
df7d5679 777 }
47f0387b 778 return(resp);
df7d5679
RC
779}
780
781/*
782 * Banner printing stuff
783 */
784banner(name1, name2)
785 char *name1, *name2;
786{
787 time_t tvec;
788 extern char *ctime();
789
790 time(&tvec);
791 if (!SF && !tof)
792 (void) write(ofd, FF, strlen(FF));
793 if (SB) { /* short banner only */
794 if (class[0]) {
795 (void) write(ofd, class, strlen(class));
796 (void) write(ofd, ":", 1);
797 }
798 (void) write(ofd, name1, strlen(name1));
799 (void) write(ofd, " Job: ", 7);
800 (void) write(ofd, name2, strlen(name2));
801 (void) write(ofd, " Date: ", 8);
802 (void) write(ofd, ctime(&tvec), 24);
803 (void) write(ofd, "\n", 1);
804 } else { /* normal banner */
805 (void) write(ofd, "\n\n\n", 3);
806 scan_out(ofd, name1, '\0');
807 (void) write(ofd, "\n\n", 2);
808 scan_out(ofd, name2, '\0');
809 if (class[0]) {
810 (void) write(ofd,"\n\n\n",3);
811 scan_out(ofd, class, '\0');
812 }
813 (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
814 (void) write(ofd, name2, strlen(name2));
815 (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
816 (void) write(ofd, ctime(&tvec), 24);
817 (void) write(ofd, "\n", 1);
818 }
819 if (!SF)
820 (void) write(ofd, FF, strlen(FF));
821 tof = 1;
822}
823
47f0387b 824char *
df7d5679
RC
825scnline(key, p, c)
826 register char key, *p;
827 char c;
828{
829 register scnwidth;
830
831 for (scnwidth = WIDTH; --scnwidth;) {
832 key <<= 1;
833 *p++ = key & 0200 ? c : BACKGND;
834 }
835 return (p);
836}
837
838#define TRC(q) (((q)-' ')&0177)
839
840scan_out(scfd, scsp, dlm)
841 int scfd;
842 char *scsp, dlm;
843{
844 register char *strp;
845 register nchrs, j;
846 char outbuf[LINELEN+1], *sp, c, cc;
847 int d, scnhgt;
848 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
849
850 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
851 strp = &outbuf[0];
852 sp = scsp;
853 for (nchrs = 0; ; ) {
854 d = dropit(c = TRC(cc = *sp++));
855 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
856 for (j = WIDTH; --j;)
857 *strp++ = BACKGND;
858 else
859 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
860 if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
861 break;
862 *strp++ = BACKGND;
863 *strp++ = BACKGND;
864 }
865 while (*--strp == BACKGND && strp >= outbuf)
866 ;
867 strp++;
868 *strp++ = '\n';
869 (void) write(scfd, outbuf, strp-outbuf);
870 }
871}
872
873dropit(c)
874 char c;
875{
876 switch(c) {
877
878 case TRC('_'):
879 case TRC(';'):
880 case TRC(','):
881 case TRC('g'):
882 case TRC('j'):
883 case TRC('p'):
884 case TRC('q'):
885 case TRC('y'):
886 return (DROP);
887
888 default:
889 return (0);
890 }
891}
892
893/*
894 * sendmail ---
895 * tell people about job completion
896 */
fbd83c9f
RC
897sendmail(user, bombed)
898 char *user;
df7d5679
RC
899 int bombed;
900{
df7d5679 901 register int i;
fbd83c9f 902 int p[2], s;
df7d5679
RC
903 register char *cp;
904 char buf[100];
fbd83c9f
RC
905 struct stat stb;
906 FILE *fp;
df7d5679
RC
907
908 pipe(p);
fbd83c9f 909 if ((s = dofork(DORETURN)) == 0) { /* child */
df7d5679
RC
910 dup2(p[0], 0);
911 for (i = 3; i < NOFILE; i++)
912 (void) close(i);
913 if ((cp = rindex(MAIL, '/')) != NULL)
914 cp++;
915 else
916 cp = MAIL;
fbd83c9f 917 sprintf(buf, "%s@%s", user, fromhost);
df7d5679
RC
918 execl(MAIL, cp, buf, 0);
919 exit(0);
fbd83c9f 920 } else if (s > 0) { /* parent */
df7d5679 921 dup2(p[1], 1);
fbd83c9f 922 printf("To: %s@%s\n", user, fromhost);
df7d5679
RC
923 printf("Subject: printer job\n\n");
924 printf("Your printer job ");
925 if (*jobname)
926 printf("(%s) ", jobname);
88f026f7 927 switch (bombed) {
98d189de 928 case OK:
88f026f7
RC
929 printf("\ncompleted successfully\n");
930 break;
931 default:
98d189de 932 case FATALERR:
88f026f7
RC
933 printf("\ncould not be printed\n");
934 break;
98d189de 935 case NOACCT:
88f026f7
RC
936 printf("\ncould not be printed without an account on %s\n", host);
937 break;
98d189de 938 case FILTERERR:
fbd83c9f
RC
939 if (stat(tmpfile, &stb) < 0 || stb.st_size == 0 ||
940 (fp = fopen(tmpfile, "r")) == NULL) {
941 printf("\nwas printed but had some errors\n");
942 break;
943 }
944 printf("\nwas printed but had the following errors:\n");
945 while ((i = getc(fp)) != EOF)
946 putchar(i);
947 (void) fclose(fp);
98d189de
RC
948 break;
949 case ACCESS:
950 printf("\nwas not printed because it was not linked to the original file\n");
88f026f7 951 }
df7d5679
RC
952 fflush(stdout);
953 (void) close(1);
954 }
955 (void) close(p[0]);
956 (void) close(p[1]);
fbd83c9f 957 wait(&s);
df7d5679
RC
958}
959
960/*
961 * dofork - fork with retries on failure
962 */
963dofork(action)
964 int action;
965{
966 register int i, pid;
967
968 for (i = 0; i < 20; i++) {
88f026f7 969 if ((pid = fork()) < 0) {
df7d5679 970 sleep((unsigned)(i*i));
88f026f7
RC
971 continue;
972 }
973 /*
974 * Child should run as daemon instead of root
975 */
976 if (pid == 0)
977 setuid(DU);
978 return(pid);
df7d5679 979 }
47f0387b 980 syslog(LOG_ERR, "can't fork");
df7d5679
RC
981
982 switch (action) {
983 case DORETURN:
984 return (-1);
985 default:
47f0387b 986 syslog(LOG_ERR, "bad action (%d) to dofork", action);
df7d5679
RC
987 /*FALL THRU*/
988 case DOABORT:
989 exit(1);
990 }
991 /*NOTREACHED*/
992}
993
994/*
47f0387b 995 * Kill child processes to abort current job.
df7d5679 996 */
47f0387b 997abortpr()
df7d5679 998{
fbd83c9f 999 (void) unlink(tmpfile);
df7d5679
RC
1000 kill(0, SIGINT);
1001 if (ofilter > 0)
1002 kill(ofilter, SIGCONT);
1003 while (wait(0) > 0)
1004 ;
1005 exit(0);
1006}
1007
1008init()
1009{
1010 int status;
1011
9e8db329
TF
1012 if ((status = pgetent(line, printer)) < 0) {
1013 syslog(LOG_ERR, "can't open printer description file");
1014 exit(1);
1015 } else if (status == 0) {
1016 syslog(LOG_ERR, "unknown printer: %s", printer);
1017 exit(1);
1018 }
df7d5679
RC
1019 if ((LP = pgetstr("lp", &bp)) == NULL)
1020 LP = DEFDEVLP;
1021 if ((RP = pgetstr("rp", &bp)) == NULL)
88f026f7 1022 RP = DEFLP;
df7d5679
RC
1023 if ((LO = pgetstr("lo", &bp)) == NULL)
1024 LO = DEFLOCK;
1025 if ((ST = pgetstr("st", &bp)) == NULL)
1026 ST = DEFSTAT;
1027 if ((LF = pgetstr("lf", &bp)) == NULL)
1028 LF = DEFLOGF;
1029 if ((SD = pgetstr("sd", &bp)) == NULL)
1030 SD = DEFSPOOL;
1031 if ((DU = pgetnum("du")) < 0)
1032 DU = DEFUID;
1033 if ((FF = pgetstr("ff", &bp)) == NULL)
1034 FF = DEFFF;
1035 if ((PW = pgetnum("pw")) < 0)
1036 PW = DEFWIDTH;
1037 sprintf(&width[2], "%d", PW);
1038 if ((PL = pgetnum("pl")) < 0)
1039 PL = DEFLENGTH;
1040 sprintf(&length[2], "%d", PL);
88f026f7
RC
1041 if ((PX = pgetnum("px")) < 0)
1042 PX = 0;
1043 sprintf(&pxwidth[2], "%d", PX);
1044 if ((PY = pgetnum("py")) < 0)
1045 PY = 0;
1046 sprintf(&pxlength[2], "%d", PY);
df7d5679 1047 RM = pgetstr("rm", &bp);
9e8db329
TF
1048 /*
1049 * Figure out whether the local machine is the same as the remote
1050 * machine entry (if it exists). If not, then ignore the local
1051 * queue information.
1052 */
1053 if (RM != (char *) NULL) {
1054 char name[256];
1055 struct hostent *hp;
1056
1057 /* get the standard network name of the local host */
1058 gethostname(name, sizeof(name));
1059 name[sizeof(name)-1] = '\0';
1060 hp = gethostbyname(name);
1061 if (hp == (struct hostent *) NULL) {
1062 syslog(LOG_ERR,
1063 "unable to get network name for local machine %s",
1064 name);
1065 goto localcheck_done;
1066 } else strcpy(name, hp->h_name);
1067
1068 /* get the standard network name of RM */
1069 hp = gethostbyname(RM);
1070 if (hp == (struct hostent *) NULL) {
1071 syslog(LOG_ERR,
1072 "unable to get hostname for remote machine %s", RM);
1073 goto localcheck_done;
1074 }
1075
1076 /* if printer is not on local machine, ignore LP */
1077 if (strcmp(name, hp->h_name) != 0) *LP = '\0';
1078 }
1079localcheck_done:
1080
df7d5679
RC
1081 AF = pgetstr("af", &bp);
1082 OF = pgetstr("of", &bp);
1083 IF = pgetstr("if", &bp);
88f026f7 1084 RF = pgetstr("rf", &bp);
df7d5679 1085 TF = pgetstr("tf", &bp);
a4f59913 1086 NF = pgetstr("nf", &bp);
df7d5679
RC
1087 DF = pgetstr("df", &bp);
1088 GF = pgetstr("gf", &bp);
1089 VF = pgetstr("vf", &bp);
1090 CF = pgetstr("cf", &bp);
1091 TR = pgetstr("tr", &bp);
88f026f7 1092 RS = pgetflag("rs");
df7d5679
RC
1093 SF = pgetflag("sf");
1094 SH = pgetflag("sh");
1095 SB = pgetflag("sb");
1e7998ee 1096 HL = pgetflag("hl");
df7d5679
RC
1097 RW = pgetflag("rw");
1098 BR = pgetnum("br");
1099 if ((FC = pgetnum("fc")) < 0)
1100 FC = 0;
1101 if ((FS = pgetnum("fs")) < 0)
1102 FS = 0;
1103 if ((XC = pgetnum("xc")) < 0)
1104 XC = 0;
1105 if ((XS = pgetnum("xs")) < 0)
1106 XS = 0;
6bd17dac 1107 tof = !pgetflag("fo");
df7d5679
RC
1108}
1109
88f026f7
RC
1110/*
1111 * Acquire line printer or remote connection.
1112 */
1113openpr()
1114{
1115 register int i, n;
47f0387b 1116 int resp;
88f026f7
RC
1117
1118 if (*LP) {
1119 for (i = 1; ; i = i < 32 ? i << 1 : i) {
adec4d9e 1120 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
88f026f7
RC
1121 if (pfd >= 0)
1122 break;
1123 if (errno == ENOENT) {
47f0387b 1124 syslog(LOG_ERR, "%s: %m", LP);
88f026f7
RC
1125 exit(1);
1126 }
1127 if (i == 1)
1128 status("waiting for %s to become ready (offline ?)", printer);
1129 sleep(i);
1130 }
1131 if (isatty(pfd))
1132 setty();
1133 status("%s is ready and printing", printer);
1134 } else if (RM != NULL) {
47f0387b
RC
1135 for (i = 1; ; i = i < 256 ? i << 1 : i) {
1136 resp = -1;
a87bde97 1137 pfd = getport(RM);
88f026f7
RC
1138 if (pfd >= 0) {
1139 (void) sprintf(line, "\2%s\n", RP);
1140 n = strlen(line);
47f0387b
RC
1141 if (write(pfd, line, n) == n &&
1142 (resp = response()) == '\0')
88f026f7 1143 break;
1d57b303
RC
1144 (void) close(pfd);
1145 }
1146 if (i == 1) {
47f0387b 1147 if (resp < 0)
1d57b303 1148 status("waiting for %s to come up", RM);
47f0387b 1149 else {
1d57b303 1150 status("waiting for queue to be enabled on %s", RM);
47f0387b
RC
1151 i = 256;
1152 }
88f026f7 1153 }
88f026f7
RC
1154 sleep(i);
1155 }
1156 status("sending to %s", RM);
1157 remote = 1;
1158 } else {
47f0387b
RC
1159 syslog(LOG_ERR, "%s: no line printer device or host name",
1160 printer);
88f026f7
RC
1161 exit(1);
1162 }
1163 /*
1164 * Start up an output filter, if needed.
1165 */
1166 if (OF) {
1167 int p[2];
1168 char *cp;
1169
1170 pipe(p);
1171 if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1172 dup2(p[0], 0); /* pipe is std in */
1173 dup2(pfd, 1); /* printer is std out */
1174 for (i = 3; i < NOFILE; i++)
1175 (void) close(i);
1176 if ((cp = rindex(OF, '/')) == NULL)
1177 cp = OF;
1178 else
1179 cp++;
1180 execl(OF, cp, width, length, 0);
47f0387b 1181 syslog(LOG_ERR, "%s: %s: %m", printer, OF);
88f026f7
RC
1182 exit(1);
1183 }
1184 (void) close(p[0]); /* close input side */
1185 ofd = p[1]; /* use pipe for output */
1186 } else {
1187 ofd = pfd;
1188 ofilter = 0;
1189 }
1190}
1191
df7d5679
RC
1192struct bauds {
1193 int baud;
1194 int speed;
1195} bauds[] = {
1196 50, B50,
1197 75, B75,
1198 110, B110,
1199 134, B134,
1200 150, B150,
1201 200, B200,
1202 300, B300,
1203 600, B600,
1204 1200, B1200,
1205 1800, B1800,
1206 2400, B2400,
1207 4800, B4800,
1208 9600, B9600,
1209 19200, EXTA,
1210 38400, EXTB,
1211 0, 0
1212};
1213
1214/*
1215 * setup tty lines.
1216 */
1217setty()
1218{
1219 struct sgttyb ttybuf;
1220 register struct bauds *bp;
1221
1222 if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
47f0387b 1223 syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
df7d5679
RC
1224 exit(1);
1225 }
1226 if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
47f0387b 1227 syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
df7d5679
RC
1228 exit(1);
1229 }
1230 if (BR > 0) {
1231 for (bp = bauds; bp->baud; bp++)
1232 if (BR == bp->baud)
1233 break;
1234 if (!bp->baud) {
47f0387b 1235 syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
df7d5679
RC
1236 exit(1);
1237 }
1238 ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1239 }
c6d1c018
RC
1240 ttybuf.sg_flags &= ~FC;
1241 ttybuf.sg_flags |= FS;
df7d5679 1242 if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
47f0387b 1243 syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
df7d5679
RC
1244 exit(1);
1245 }
81965073
RC
1246 if (XC || XS) {
1247 int ldisc = NTTYDISC;
1248
1249 if (ioctl(pfd, TIOCSETD, &ldisc) < 0) {
1250 syslog(LOG_ERR, "%s: ioctl(TIOCSETD): %m", printer);
1251 exit(1);
1252 }
1253 }
df7d5679
RC
1254 if (XC) {
1255 if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
47f0387b 1256 syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
df7d5679
RC
1257 exit(1);
1258 }
1259 }
1260 if (XS) {
1261 if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
47f0387b 1262 syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
df7d5679
RC
1263 exit(1);
1264 }
1265 }
1266}
88f026f7
RC
1267
1268/*VARARGS1*/
88f026f7
RC
1269status(msg, a1, a2, a3)
1270 char *msg;
1271{
1272 register int fd;
1273 char buf[BUFSIZ];
1274
1275 umask(0);
adec4d9e 1276 fd = open(ST, O_WRONLY|O_CREAT, 0664);
47f0387b
RC
1277 if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1278 syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1279 exit(1);
1280 }
adec4d9e 1281 ftruncate(fd, 0);
88f026f7
RC
1282 sprintf(buf, msg, a1, a2, a3);
1283 strcat(buf, "\n");
1284 (void) write(fd, buf, strlen(buf));
1285 (void) close(fd);
1286}