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