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