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