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