1: /* lpr.c 4.3 81/05/19 */ 2: /* 3: * lpr -- off line print 4: * also known as print 5: */ 6: 7: #include <sys/types.h> 8: #include <sys/stat.h> 9: #include <signal.h> 10: #include <pwd.h> 11: #include <stdio.h> 12: #include <ctype.h> 13: #include "lp.local.h" 14: #include <whoami.h> 15: 16: /* 17: * Multiple printer scheme using info from printer data base: 18: * 19: * DN daemon to invoke to print stuff 20: * SD directory used as spool queue 21: * 22: * daemon identifies what printer it should use (in the case of the 23: * same code being shared) by inspecting its argv[1]. 24: */ 25: char *tfname; /* tmp copy of df before linking */ 26: char *cfname; /* copy files */ 27: char *lfname; /* linked files */ 28: char *dfname; /* daemon files, linked from tf's */ 29: 30: int nact; /* number of jobs to act on */ 31: int tff; /* daemon file descriptor */ 32: int mailflg; /* send mail */ 33: int jflag; /* job name specified */ 34: int qflg; /* q job, but don't exec daemon */ 35: int prflag; /* ``pr'' files */ 36: char *person; /* user name */ 37: int inchar; /* location to increment char in file names */ 38: int ncopies = 1; /* # of copies to make */ 39: int iflag; /* indentation wanted */ 40: int indent; /* amount to indent */ 41: char *daemname; /* path name to daemon program */ 42: char *DN; /* daemon name */ 43: char *SD; /* spooling directory */ 44: char *LP; /* line printer device */ 45: int MX; /* max size in BUFSIZ blocks of a print file */ 46: int hdr = 1; /* 1 =>'s default header */ 47: int user; /* user id */ 48: int spgroup; /* daemon's group for creating spool files */ 49: char *title; /* pr'ing title */ 50: char hostname[32]; /* name of local host */ 51: char *class = hostname; /* class title on header page */ 52: char *jobname; /* job name on header page */ 53: char *name; /* program name */ 54: char *printer; /* printer name */ 55: 56: char *pgetstr(); 57: char *mktmp(); 58: char *malloc(); 59: char *getenv(); 60: char *rindex(); 61: 62: main(argc, argv) 63: int argc; 64: char *argv[]; 65: { 66: register char *arg; 67: int i, c, f, flag, out(); 68: char *sp; 69: struct stat sbuf; 70: 71: /* 72: * Strategy to maintain protected spooling area: 73: * 1. Spooling area is writable only by daemon and spooling group 74: * 2. lpr runs setuid root and setgrp spooling group; it uses 75: * root to access any file it wants (verifying things before 76: * with an access call) and group id to know how it should 77: * set up ownership of files in spooling area. 78: * 3. Files in spooling area are owned by printer and spooling 79: * group, with mode 660. 80: * 4. lpd runs setuid daemon and setgrp spooling group to 81: * access files and printer. Users can't get to anything 82: * w/o help of lpq and lprm programs. 83: */ 84: user = getuid(); 85: spgroup = getegid(); 86: if(signal(SIGHUP, SIG_IGN) != SIG_IGN) 87: signal(SIGHUP, out); 88: if(signal(SIGINT, SIG_IGN) != SIG_IGN) 89: signal(SIGINT, out); 90: if(signal(SIGQUIT, SIG_IGN) != SIG_IGN) 91: signal(SIGQUIT, out); 92: if(signal(SIGTERM, SIG_IGN) != SIG_IGN) 93: signal(SIGTERM, out); 94: flag = 0; 95: if ((printer = getenv("PRINTER")) == NULL) 96: printer = DEFLP; 97: name = argv[0]; 98: gethostname(hostname, sizeof (hostname)); 99: 100: while (argc>1 && (arg = argv[1])[0]=='-') { 101: switch (arg[1]) { 102: 103: case 'c': /* force copy of files */ 104: flag = '+'; 105: break; 106: 107: case 'C': /* classification spec */ 108: hdr++; 109: if (arg[2]) 110: class = &arg[2]; 111: else if (argc >= 2) { 112: ++argv; 113: arg = argv[1]; 114: argc--; 115: class = arg; 116: } 117: break; 118: 119: case 'r': /* remove file when done */ 120: flag = '-'; 121: break; 122: 123: case 'm': /* send mail when done */ 124: mailflg++; 125: break; 126: 127: case 'q': /* just q job */ 128: qflg++; 129: break; 130: 131: case 'J': /* job spec */ 132: jflag++, hdr++; 133: if (arg[2]) { 134: jobname = &arg[2]; 135: break; 136: } 137: if (argc>=2) { 138: ++argv; 139: arg = argv[1]; 140: jobname = &arg[0]; 141: argc--; 142: } 143: break; 144: 145: case 'i': /* indent output */ 146: iflag++; 147: indent = arg[2] ? atoi(&arg[2]) : 8; 148: break; 149: 150: case 'p': /* use pr to print files */ 151: prflag++; 152: break; 153: 154: case 'h': /* pr's title line */ 155: if (arg[2]) 156: title = &arg[2]; 157: else if (argc >= 2) { 158: ++argv; 159: arg = argv[1]; 160: argc--; 161: title = arg; 162: } 163: break; 164: 165: case 'P': /* specifiy printer name */ 166: printer = &arg[2]; 167: break; 168: 169: case 'H': /* toggle want of header page */ 170: hdr = !hdr; 171: break; 172: 173: default: /* n copies ? */ 174: if (isdigit(arg[1])) 175: ncopies = atoi(&arg[1]); 176: } 177: argc--; 178: argv++; 179: } 180: if (!chkprinter(printer)) { 181: fprintf(stderr, "%s: no entry for default printer %s\n", name, 182: printer); 183: exit(2); 184: } 185: i = getpid(); 186: f = strlen(SD)+11; 187: tfname = mktmp("tf", i, f); 188: cfname = mktmp("cf", i, f); 189: lfname = mktmp("lf", i, f); 190: dfname = mktmp("df", i, f); 191: inchar = f-7; 192: tff = nfile(tfname); 193: if (jflag == 0) { 194: if(argc == 1) 195: jobname = &dfname[f-10]; 196: else 197: jobname = argv[1]; 198: } 199: ident(); 200: 201: if(argc == 1) 202: copy(0, " "); 203: else while(--argc) { 204: if(test(arg = *++argv) == -1) /* file reasonable */ 205: continue; 206: 207: if (flag == '+') /* force copy flag */ 208: goto cf; 209: if (stat(arg, &sbuf) < 0) { 210: printf("lpr:"); 211: perror(arg); 212: continue; 213: } 214: if((sbuf.st_mode&04) == 0) 215: goto cf; 216: if(*arg == '/' && flag != '-') { 217: for(i=0;i<ncopies;i++) { 218: if (prflag) { 219: if (title) 220: card('H', title); 221: card('R', arg); 222: } else 223: card('F', arg); 224: card('N', arg); 225: } 226: nact++; 227: continue; 228: } 229: if(link(arg, lfname) < 0) 230: goto cf; 231: for(i=0;i<ncopies;i++) { 232: if (prflag) { 233: card('H', title ? title : arg); 234: card('R', lfname); 235: } else 236: card('F', lfname); 237: card('N', arg); 238: } 239: card('U', lfname); 240: lfname[inchar]++; 241: nact++; 242: goto df; 243: 244: cf: 245: if((f = open(arg, 0)) < 0) { 246: printf("%s: cannot open %s\n", name, arg); 247: continue; 248: } 249: copy(f, arg); 250: close(f); 251: 252: df: 253: if(flag == '-' && unlink(arg)) 254: printf("%s: cannot remove %s\n", name, arg); 255: } 256: 257: if(nact) { 258: tfname[inchar]--; 259: if(link(tfname, dfname) < 0) { 260: printf("%s: cannot rename %s\n", name, dfname); 261: tfname[inchar]++; 262: out(); 263: } 264: unlink(tfname); 265: if (qflg) /* just q things up */ 266: exit(0); 267: if (stat(LP, &sbuf) >= 0 && (sbuf.st_mode&0777) == 0) { 268: printf("job queued, but printer down\n"); 269: exit(0); 270: } 271: for(f = 0; f < NOFILE; close(f++)) 272: ; 273: open("/dev/tty", 0); 274: open("/dev/tty", 1); 275: dup2(1, 2); 276: execl(DN, rindex(DN, '/') ? rindex(DN, '/')+1 : DN, printer, 0); 277: dfname[inchar]++; 278: } 279: out(); 280: } 281: 282: copy(f, n) 283: int f; 284: char n[]; 285: { 286: int ff, i, nr, nc; 287: char buf[BUFSIZ]; 288: 289: for(i=0;i<ncopies;i++) { 290: if (prflag) { 291: card('H', title ? title : n); 292: card('R', cfname); 293: } else 294: card('F', cfname); 295: card('N', n); 296: } 297: card('U', cfname); 298: ff = nfile(cfname); 299: nr = nc = 0; 300: while((i = read(f, buf, BUFSIZ)) > 0) { 301: if (write(ff, buf, i) != i) { 302: printf("%s: %s: temp file write error\n", name, n); 303: break; 304: } 305: nc += i; 306: if(nc >= BUFSIZ) { 307: nc -= BUFSIZ; 308: if(nr++ > MX) { 309: printf("%s: %s: copy file is too large\n", name, n); 310: break; 311: } 312: } 313: } 314: close(ff); 315: nact++; 316: } 317: 318: card(c, p2) 319: register char c, *p2; 320: { 321: char buf[BUFSIZ]; 322: register char *p1 = buf; 323: int col = 0; 324: 325: *p1++ = c; 326: while((c = *p2++) != '\0') { 327: *p1++ = c; 328: col++; 329: } 330: *p1++ = '\n'; 331: write(tff, buf, col+2); 332: } 333: 334: ident() 335: { 336: extern char *getlogin(); 337: extern struct passwd *getpwuid(); 338: struct passwd *pw; 339: extern char *itoa(); 340: 341: if ((person = getlogin()) == NULL) { 342: if ((pw = getpwuid(user)) == NULL) 343: person = "Unknown User"; 344: else 345: person = pw->pw_name; 346: } 347: 348: card('J',jobname); 349: card('C',class); 350: card('L', person); 351: if (iflag) 352: card('I', itoa(indent)); 353: if (mailflg) 354: card('M', person); 355: } 356: 357: nfile(n) 358: char *n; 359: { 360: register f; 361: int oldumask = umask(022); /* should hold signals */ 362: 363: f = creat(n, FILMOD); 364: (void) umask(oldumask); 365: if (f < 0) { 366: printf("%s: cannot create %s\n", name, n); 367: out(); 368: } 369: if (chown(n, user, spgroup) < 0) { 370: unlink(n); 371: printf("%s: cannot chown %s\n", name, n); 372: out(); 373: } 374: n[inchar]++; 375: return(f); 376: } 377: 378: out() 379: { 380: register i; 381: 382: signal(SIGHUP, SIG_IGN); 383: signal(SIGINT, SIG_IGN); 384: signal(SIGQUIT, SIG_IGN); 385: signal(SIGTERM, SIG_IGN); 386: i = inchar; 387: if (tfname) 388: while(tfname[i] != 'A') { 389: tfname[i]--; 390: unlink(tfname); 391: } 392: if (cfname) 393: while(cfname[i] != 'A') { 394: cfname[i]--; 395: unlink(cfname); 396: } 397: if (lfname) 398: while(lfname[i] != 'A') { 399: lfname[i]--; 400: unlink(lfname); 401: } 402: if (dfname) 403: while(dfname[i] != 'A') { 404: dfname[i]--; 405: unlink(dfname); 406: } 407: exit(); 408: } 409: 410: test(file) 411: char *file; 412: { 413: struct exec buf; 414: struct stat mbuf; 415: int fd; 416: 417: if (access(file, 4) < 0) { 418: printf("%s: cannot access %s\n", name, file); 419: return(-1); 420: } 421: if(stat(file, &mbuf) < 0) { 422: printf("%s: cannot stat %s\n", name, file); 423: return (-1); 424: } 425: if ((mbuf.st_mode&S_IFMT) == S_IFDIR) { 426: printf("%s: %s is a directory\n", name, file); 427: return(-1); 428: } 429: 430: if((fd = open(file, 0)) < 0) { 431: printf("%s: cannot open %s\n", name, file); 432: return(-1); 433: } 434: if (read(fd, &buf, sizeof(buf)) == sizeof(buf)) 435: switch(buf.a_magic) { 436: case A_MAGIC1: 437: case A_MAGIC2: 438: case A_MAGIC3: 439: #ifdef A_MAGIC4 440: case A_MAGIC4: 441: #endif 442: printf("%s: %s is an executable program", name, file); 443: goto error1; 444: 445: case ARMAG: 446: printf("%s: %s is an archive file", name, file); 447: goto error1; 448: } 449: 450: close(fd); 451: return(0); 452: error1: 453: printf(" and is unprintable\n"); 454: close(fd); 455: return(-1); 456: } 457: 458: /* 459: * itoa - integer to string conversion 460: */ 461: char * 462: itoa(i) 463: register int i; 464: { 465: static char b[10] = "########"; 466: register char *p; 467: 468: p = &b[8]; 469: do 470: *p-- = i%10 + '0'; 471: while (i /= 10); 472: return(++p); 473: } 474: 475: /* 476: * Perform lookup for printer name or abbreviation -- 477: * return pointer to daemon structure 478: */ 479: chkprinter(s) 480: register char *s; 481: { 482: static char buf[BUFSIZ/2]; 483: char b[BUFSIZ]; 484: int stat; 485: char *bp = buf; 486: 487: if ((stat = pgetent(b, s)) < 0) { 488: fprintf(stderr, "%s: can't open printer description file\n", name); 489: exit(3); 490: } else if (stat == 0) 491: return(NULL); 492: if ((DN = pgetstr("dn", &bp)) == NULL) 493: DN = DEFDAEMON; 494: if ((LP = pgetstr("lp", &bp)) == NULL) 495: LP = DEFDEVLP; 496: if ((SD = pgetstr("sd", &bp)) == NULL) 497: SD = DEFSPOOL; 498: if ((MX = pgetnum("mx")) < 0) 499: MX = DEFMX; 500: return(1); 501: } 502: 503: /* 504: * Make a temp file 505: */ 506: char * 507: mktmp(id, pid, n) 508: char *id; 509: { 510: register char *s; 511: 512: if ((s = malloc(n)) == NULL) { 513: fprintf(stderr, "%s: out of memory\n", name); 514: exit(1); 515: } 516: sprintf(s, "%s/%sA%05d", SD, id, pid); 517: return(s); 518: }