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