1 /* lpr.c 4.4 82/12/02 */ 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 directory 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 classbuf[32]; /* class title on header page */ 50 char *class = classbuf; 51 char *jobname; /* job name on header page */ 52 char *name; /* program name */ 53 char *printer; /* printer name */ 54 55 char *pgetstr(); 56 char *mktmp(); 57 char *malloc(); 58 char *getenv(); 59 char *rindex(); 60 61 /* ARGSUSED */ 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 sq and dq programs. 83 */ 84 gethostname(classbuf, sizeof (classbuf)); 85 user = getuid(); 86 spgroup = getegid(); 87 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 88 signal(SIGHUP, out); 89 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 90 signal(SIGINT, out); 91 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 92 signal(SIGQUIT, out); 93 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 94 signal(SIGTERM, out); 95 flag = 0; 96 if ((printer = getenv("PRINTER")) == NULL) 97 printer = DEFLP; 98 name = argv[0]; 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(SA)+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("%s:", name); 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 if (hdr) { 349 card('J',jobname); 350 card('C',class); 351 card('L', person); 352 } 353 if (iflag) 354 card('I', itoa(indent)); 355 if (mailflg) 356 card('M', person); 357 } 358 359 nfile(n) 360 char *n; 361 { 362 register f; 363 int oldumask = umask(022); /* should block signals */ 364 365 f = creat(n, FILMOD); 366 (void) umask(oldumask); 367 if (f < 0) { 368 printf("%s: cannot create %s\n", name, n); 369 out(); 370 } 371 if (chown(n, user, spgroup) < 0) { 372 unlink(n); 373 printf("%s: cannot chown %s\n", name, n); 374 out(); 375 } 376 n[inchar]++; 377 return (f); 378 } 379 380 out() 381 { 382 register i; 383 384 signal(SIGHUP, SIG_IGN); 385 signal(SIGINT, SIG_IGN); 386 signal(SIGQUIT, SIG_IGN); 387 signal(SIGTERM, SIG_IGN); 388 i = inchar; 389 if (tfname) 390 while (tfname[i] != 'A') { 391 tfname[i]--; 392 unlink(tfname); 393 } 394 if (cfname) 395 while (cfname[i] != 'A') { 396 cfname[i]--; 397 unlink(cfname); 398 } 399 if (lfname) 400 while (lfname[i] != 'A') { 401 lfname[i]--; 402 unlink(lfname); 403 } 404 if (dfname) 405 while (dfname[i] != 'A') { 406 dfname[i]--; 407 unlink(dfname); 408 } 409 exit(); 410 } 411 412 test(file) 413 char *file; 414 { 415 struct exec buf; 416 struct stat mbuf; 417 int fd; 418 419 if (access(file, 4) < 0) { 420 printf("%s: cannot access %s\n", name, file); 421 return (-1); 422 } 423 if (stat(file, &mbuf) < 0) { 424 printf("%s: cannot stat %s\n", name, file); 425 return (-1); 426 } 427 if ((mbuf.st_mode&S_IFMT) == S_IFDIR) { 428 printf("%s: %s is a directory\n", name, file); 429 return (-1); 430 } 431 432 if ((fd = open(file, 0)) < 0) { 433 printf("%s: cannot open %s\n", name, file); 434 return (-1); 435 } 436 if (read(fd, &buf, sizeof(buf)) == sizeof(buf)) 437 switch(buf.a_magic) { 438 case A_MAGIC1: 439 case A_MAGIC2: 440 case A_MAGIC3: 441 #ifdef A_MAGIC4 442 case A_MAGIC4: 443 #endif 444 printf("%s: %s is an executable program", name, file); 445 goto error1; 446 447 case ARMAG: 448 printf("%s: %s is an archive file", name, file); 449 goto error1; 450 } 451 452 close(fd); 453 return (0); 454 error1: 455 printf(" and is unprintable\n"); 456 close(fd); 457 return (-1); 458 } 459 460 /* 461 * itoa - integer to string conversion 462 */ 463 char * 464 itoa(i) 465 register int i; 466 { 467 static char b[10] = "########"; 468 register char *p; 469 470 p = &b[8]; 471 do 472 *p-- = i%10 + '0'; 473 while (i /= 10); 474 return (++p); 475 } 476 477 /* 478 * Perform lookup for printer name or abbreviation -- 479 * return pointer to daemon structure 480 */ 481 chkprinter(s) 482 register char *s; 483 { 484 static char buf[BUFSIZ/2]; 485 char b[BUFSIZ]; 486 int stat; 487 char *bp = buf; 488 489 if ((stat = pgetent(b, s)) < 0) { 490 fprintf(stderr, "%s: can't open printer description file\n", name); 491 exit(3); 492 } else if (stat == 0) 493 return (NULL); 494 if ((DN = pgetstr("dn", &bp)) == NULL) 495 DN = DEFDAEMON; 496 if ((LP = pgetstr("lp", &bp)) == NULL) 497 LP = DEFDEVLP; 498 if ((SA = pgetstr("sa", &bp)) == NULL) 499 SA = DEFSPOOL; 500 if ((MX = pgetnum("mx")) < 0) 501 MX = DEFMX; 502 return (1); 503 } 504 505 /* 506 * Make a temp file 507 */ 508 char * 509 mktmp(id, pid, n) 510 char *id; 511 { 512 register char *s; 513 514 if ((s = malloc(n)) == NULL) { 515 fprintf(stderr, "%s: out of memory\n", name); 516 exit(1); 517 } 518 sprintf(s, "%s/%sA%05d", SA, id, pid); 519 return (s); 520 } 521