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