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