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