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