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