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