Don't pass EX_TEMPFAIL code out (since we have already taken responsibility
[unix-history] / usr / src / usr.sbin / lpr / lpr / lpr.c
CommitLineData
51d1035a
BJ
1/*
2 *
3 * 1.1 mike 5/77 - all files copied to spooler space
4 * (see note below)
5 *
6 * 1.2 dlm 26 Sep 1977
7 * fix for allowing variable indents (-i flag)
8 *
9 * 1.3 dlm/njl 12 Dec 1977
10 * fix for open pipe when spawning progess (affects lpd)
11 *
12 * 1.4 dlm 24 Feb 1978
13 * test for data files; add -C option for classification
14 * and change format of $L card; reversed 1.1 on copies
15 *
16 * 1.5 dlm 27 Mar 1978
17 * add job name and -h option
18 *
19 * 2.0 was 21 Dec 1978
20 * -h option changed to -J
21 * added -H option to cause header page to be
22 * printed, default is no header page
23 * added -p option to cause files to be printed
24 * using pr
25 * default for classification changed
26 *
27 * 2.1 was 14 Mar 1979
28 * multiple printer logic changed,
29 * printer number sent to daemon.
30 *
31 * 3.0 sjl 6 May 1980
32 * Mods for Version 7, plus portability
33 * (in preparation for UNIX/24V)
34 *
35 * 3.1 sjl Oct 28 1980
36 * Mods for protected spooling area, see note below
37 *
38 * 3.2 sjl Mar 13 1981
39 * Add N card for file names to help out sq
40 *
41 * 3.3 sjl Mar 27 1981 from decvax!shannon
42 * Mods and cleanup for 4bsd.
43 * Send mail option added.
44 *
45 * 4.0 sjl Mar 28 1981
46 * Support multiple printers and daemons through termcap-like
47 * data base
48 *
49 * 4.1 sjl Apr 28 1981
50 * Check for printer being down (mode 0 on device)
51 *
52 * 4.2 was May 1 1981
53 * Clean up handling of printcap database, add more defaults
54 */
55
56char lpr_id[] = "~|^`lpr.c:\t4.2\t1 May 1981\n";
57
09d5163a 58/* lpr.c 4.9 83/03/01 */
51d1035a
BJ
59/*
60 * lpr -- off line print
fdaeefb7
BJ
61 *
62 * Allows multiple printers and printers on remote machines by
63 * using information from a printer data base.
51d1035a
BJ
64 */
65
66#include <sys/types.h>
67#include <sys/stat.h>
fdaeefb7 68#include <sys/file.h>
51d1035a
BJ
69#include <signal.h>
70#include <pwd.h>
71#include <stdio.h>
72#include <ctype.h>
73#include "lp.local.h"
74
fdaeefb7
BJ
75char *tfname; /* tmp copy of cf before linking */
76char *cfname; /* daemon control files, linked from tf's */
77char *dfname; /* data files */
51d1035a
BJ
78
79int nact; /* number of jobs to act on */
fdaeefb7 80int tfd; /* control file descriptor */
51d1035a 81int mailflg; /* send mail */
fdaeefb7 82int qflag; /* q job, but don't exec daemon */
878a0818 83char format = 'f'; /* format char for printing files */
fdaeefb7 84int rflag; /* remove files upon completion */
e95c0734 85int lflag; /* link flag */
51d1035a
BJ
86char *person; /* user name */
87int inchar; /* location to increment char in file names */
88int ncopies = 1; /* # of copies to make */
89int iflag; /* indentation wanted */
90int indent; /* amount to indent */
fdaeefb7
BJ
91char *DN; /* path name to daemon program */
92char *LP; /* line printer device name */
93char *RM; /* remote machine name if no local printer */
94char *SD; /* spool directory */
51d1035a 95int MX; /* maximum size in blocks of a print file */
fdaeefb7 96int hdr = 1; /* print header or not (default is yes) */
878a0818 97int userid; /* user id */
51d1035a 98char *title; /* pr'ing title */
878a0818
RC
99char *fonts[4]; /* troff font names */
100char *width; /* width for versatec printing */
fdaeefb7
BJ
101char host[32]; /* host name */
102char *class = host; /* class title on header page */
51d1035a
BJ
103char *jobname; /* job name on header page */
104char *name; /* program name */
51d1035a
BJ
105
106char *pgetstr();
51d1035a
BJ
107char *malloc();
108char *getenv();
109char *rindex();
110
fdaeefb7 111/*ARGSUSED*/
51d1035a 112main(argc, argv)
6f038d7b
RC
113 int argc;
114 char *argv[];
51d1035a 115{
878a0818
RC
116 extern char *getlogin();
117 extern struct passwd *getpwuid(), *getpwnam();
118 struct passwd *pw;
119 extern char *itoa();
51d1035a 120 register char *arg;
fdaeefb7
BJ
121 int i, f, out();
122 char *printer = NULL;
123 struct stat stb;
51d1035a
BJ
124
125 /*
126 * Strategy to maintain protected spooling area:
127 * 1. Spooling area is writable only by daemon and spooling group
128 * 2. lpr runs setuid root and setgrp spooling group; it uses
129 * root to access any file it wants (verifying things before
130 * with an access call) and group id to know how it should
131 * set up ownership of files in spooling area.
fdaeefb7 132 * 3. Files in spooling area are owned by daemon and spooling
51d1035a 133 * group, with mode 660.
fdaeefb7 134 * 4. lpd runs setuid root and setgrp spooling group to
51d1035a 135 * access files and printer. Users can't get to anything
fdaeefb7 136 * w/o help of lpq and lprm programs.
51d1035a 137 */
6f038d7b 138 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
51d1035a 139 signal(SIGHUP, out);
6f038d7b 140 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
51d1035a 141 signal(SIGINT, out);
6f038d7b 142 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
51d1035a 143 signal(SIGQUIT, out);
6f038d7b 144 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
51d1035a 145 signal(SIGTERM, out);
fdaeefb7
BJ
146
147 gethostname(host, sizeof (host));
51d1035a
BJ
148 name = argv[0];
149
fdaeefb7
BJ
150 while (argc > 1 && (arg = argv[1])[0] == '-') {
151 argc--;
152 argv++;
51d1035a
BJ
153 switch (arg[1]) {
154
fdaeefb7
BJ
155 case 'P': /* specifiy printer name */
156 printer = &arg[2];
51d1035a
BJ
157 break;
158
159 case 'C': /* classification spec */
160 hdr++;
161 if (arg[2])
162 class = &arg[2];
878a0818 163 else if (argc > 1) {
51d1035a 164 argc--;
fdaeefb7 165 class = *++argv;
51d1035a
BJ
166 }
167 break;
168
fdaeefb7
BJ
169 case 'J': /* job name */
170 hdr++;
171 if (arg[2])
51d1035a 172 jobname = &arg[2];
878a0818 173 else if (argc > 1) {
51d1035a 174 argc--;
fdaeefb7 175 jobname = *++argv;
51d1035a
BJ
176 }
177 break;
178
fdaeefb7
BJ
179 case 'T': /* pr's title line */
180 if (arg[2])
181 title = &arg[2];
878a0818 182 else if (argc > 1) {
fdaeefb7
BJ
183 argc--;
184 title = *++argv;
185 }
51d1035a
BJ
186 break;
187
878a0818
RC
188 case 'l': /* literal output */
189 case 'p': /* print using ``pr'' */
190 case 't': /* print troff output */
191 case 'c': /* print cifplot output */
192 case 'v': /* print vplot output */
193 format = arg[1];
194 break;
195
196 case '4': /* troff fonts */
197 case '3':
198 case '2':
199 case '1':
200 if (argc > 1) {
201 argc--;
202 fonts[arg[1] - '1'] = *++argv;
203 format = 't';
204 }
205 break;
206
207 case 'w': /* versatec page width */
208 width = arg+2;
51d1035a
BJ
209 break;
210
fdaeefb7
BJ
211 case 'r': /* remove file when done */
212 rflag++;
51d1035a
BJ
213 break;
214
fdaeefb7
BJ
215 case 'm': /* send mail when done */
216 mailflg++;
51d1035a
BJ
217 break;
218
fdaeefb7 219 case 'h': /* toggle want of header page */
51d1035a
BJ
220 hdr = !hdr;
221 break;
222
878a0818 223 case 's': /* try to link files */
e95c0734 224 lflag++;
fdaeefb7
BJ
225 break;
226
227 case 'q': /* just q job */
228 qflag++;
229 break;
230
231 case 'i': /* indent output */
232 iflag++;
233 indent = arg[2] ? atoi(&arg[2]) : 8;
234 break;
235
51d1035a
BJ
236 default: /* n copies ? */
237 if (isdigit(arg[1]))
238 ncopies = atoi(&arg[1]);
239 }
51d1035a 240 }
fdaeefb7
BJ
241 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
242 printer = DEFLP;
51d1035a 243 if (!chkprinter(printer)) {
fdaeefb7 244 printf("%s: unknown printer\n", name, printer);
51d1035a
BJ
245 exit(2);
246 }
878a0818
RC
247 /*
248 * Get the identity of the person doing the lpr and initialize the
249 * control file.
250 */
251 userid = getuid();
252 if ((person = getlogin()) == NULL) {
253 if ((pw = getpwuid(userid)) == NULL)
254 person = "Unknown User";
255 else
256 person = pw->pw_name;
257 } else if ((pw = getpwnam(person)) != NULL)
258 userid = pw->pw_uid; /* in case of su */
fdaeefb7
BJ
259 mktemps();
260 tfd = nfile(tfname);
878a0818
RC
261 card('H', host);
262 card('P', person);
263 if (hdr) {
264 if (jobname == NULL) {
265 if (argc == 1)
266 jobname = &cfname[inchar-2];
267 else
268 jobname = argv[1];
269 }
270 card('J', jobname);
271 card('C', class);
272 card('L', person);
51d1035a 273 }
878a0818
RC
274 if (iflag)
275 card('I', itoa(indent));
276 if (mailflg)
277 card('M', person);
278 if (format == 't')
279 for (i = 0; i < 4; i++)
280 if (fonts[i] != NULL)
281 card('1'+i, fonts[i]);
282 else if ((format == 'f' || format == 'l' || format == 'p') && width)
283 card('W', width);
51d1035a 284
6f038d7b 285 if (argc == 1)
51d1035a 286 copy(0, " ");
6f038d7b 287 else while (--argc) {
c249bc99 288 if ((i = test(arg = *++argv)) < 0)
fdaeefb7
BJ
289 continue; /* file unreasonable */
290
c249bc99 291 if (i && lflag && linked(arg)) {
878a0818 292 if (format == 'p')
fdaeefb7 293 card('T', title ? title : arg);
878a0818
RC
294 for (i = 0; i < ncopies; i++)
295 card(format, &dfname[inchar-2]);
fdaeefb7
BJ
296 card('U', &dfname[inchar-2]);
297 card('N', arg);
298 dfname[inchar]++;
51d1035a 299 nact++;
c249bc99 300 } else {
fdaeefb7
BJ
301 if ((f = open(arg, 0)) < 0) {
302 printf("%s: cannot open %s\n", name, arg);
303 continue;
304 }
305 copy(f, arg);
306 (void) close(f);
51d1035a 307 }
fdaeefb7
BJ
308 if (rflag) {
309 register char *cp;
310
311 if ((cp = rindex(arg, '/')) == NULL)
312 f = access(".", 2);
313 else {
314 *cp = '\0';
315 f = access(arg, 2);
316 *cp = '/';
317 }
318 if (f || unlink(arg))
319 printf("%s: cannot remove %s\n", name, arg);
51d1035a 320 }
51d1035a
BJ
321 }
322
6f038d7b 323 if (nact) {
51d1035a 324 tfname[inchar]--;
fdaeefb7
BJ
325 if (link(tfname, cfname) < 0) {
326 printf("%s: cannot rename %s\n", name, cfname);
51d1035a
BJ
327 tfname[inchar]++;
328 out();
329 }
330 unlink(tfname);
fdaeefb7 331 if (qflag) /* just q things up */
51d1035a 332 exit(0);
fdaeefb7
BJ
333 if (*LP && stat(LP, &stb) >= 0 && (stb.st_mode & 0777) == 0) {
334 printf("jobs queued, but line printer is down.\n");
51d1035a
BJ
335 exit(0);
336 }
fdaeefb7
BJ
337 execl(DN, arg = rindex(DN, "/") ? arg+1 : DN, printer, 0);
338 printf("jobs queued, but cannot start daemon.\n");
339 exit(0);
51d1035a
BJ
340 }
341 out();
fdaeefb7 342 /*NOTREACHED*/
51d1035a
BJ
343}
344
fdaeefb7
BJ
345/*
346 * Create the file n and copy from file descriptor f.
347 */
51d1035a 348copy(f, n)
6f038d7b
RC
349 int f;
350 char n[];
51d1035a 351{
fdaeefb7 352 register int fd, i, nr, nc;
51d1035a
BJ
353 char buf[BUFSIZ];
354
878a0818 355 if (format == 'p')
fdaeefb7
BJ
356 card('T', title ? title : n);
357 for (i = 0; i < ncopies; i++)
878a0818 358 card(format, &dfname[inchar-2]);
fdaeefb7
BJ
359 card('U', &dfname[inchar-2]);
360 card('N', n);
361 fd = nfile(dfname);
51d1035a 362 nr = nc = 0;
6f038d7b 363 while ((i = read(f, buf, BUFSIZ)) > 0) {
fdaeefb7 364 if (write(fd, buf, i) != i) {
51d1035a
BJ
365 printf("%s: %s: temp file write error\n", name, n);
366 break;
367 }
368 nc += i;
6f038d7b 369 if (nc >= BUFSIZ) {
51d1035a 370 nc -= BUFSIZ;
6f038d7b 371 if (nr++ > MX) {
51d1035a
BJ
372 printf("%s: %s: copy file is too large\n", name, n);
373 break;
374 }
375 }
376 }
fdaeefb7 377 (void) close(fd);
51d1035a
BJ
378 nact++;
379}
380
fdaeefb7
BJ
381/*
382 * Try and link the file to dfname. Return true if successful.
383 */
384linked(file)
385 register char *file;
386{
387 register char *cp;
388 char buf[BUFSIZ];
389
390 if (link(file, dfname) == 0)
391 return(1);
392 if (*file != '/') {
393 if (getwd(buf) == NULL)
394 return(0);
395 while (file[0] == '.') {
396 switch (file[1]) {
397 case '/':
398 file += 2;
399 continue;
400 case '.':
401 if (file[2] == '/') {
402 if ((cp = rindex(buf, '/')) != NULL)
403 *cp = '\0';
404 file += 3;
405 continue;
406 }
407 }
408 break;
409 }
410 strcat(buf, "/");
411 strcat(buf, file);
412 file = buf;
413 }
414 return(symlink(file, dfname) == 0);
415}
416
417/*
418 * Put a line into the control file.
419 */
51d1035a 420card(c, p2)
6f038d7b 421 register char c, *p2;
51d1035a
BJ
422{
423 char buf[BUFSIZ];
424 register char *p1 = buf;
fdaeefb7 425 register int len = 2;
51d1035a
BJ
426
427 *p1++ = c;
6f038d7b 428 while ((c = *p2++) != '\0') {
51d1035a 429 *p1++ = c;
fdaeefb7 430 len++;
51d1035a
BJ
431 }
432 *p1++ = '\n';
fdaeefb7 433 write(tfd, buf, len);
51d1035a
BJ
434}
435
fdaeefb7
BJ
436/*
437 * Create a new file in the spool directory.
438 */
439
51d1035a 440nfile(n)
6f038d7b 441 char *n;
51d1035a
BJ
442{
443 register f;
fdaeefb7 444 int oldumask = umask(0); /* should block signals */
51d1035a 445
6f038d7b
RC
446 f = creat(n, FILMOD);
447 (void) umask(oldumask);
448 if (f < 0) {
51d1035a
BJ
449 printf("%s: cannot create %s\n", name, n);
450 out();
451 }
878a0818 452 if (chown(n, userid, -1) < 0) {
51d1035a
BJ
453 unlink(n);
454 printf("%s: cannot chown %s\n", name, n);
455 out();
456 }
457 n[inchar]++;
fdaeefb7 458 return(f);
51d1035a
BJ
459}
460
fdaeefb7
BJ
461/*
462 * Cleanup after interrupts and errors.
463 */
51d1035a
BJ
464out()
465{
466 register i;
467
468 signal(SIGHUP, SIG_IGN);
469 signal(SIGINT, SIG_IGN);
470 signal(SIGQUIT, SIG_IGN);
471 signal(SIGTERM, SIG_IGN);
472 i = inchar;
473 if (tfname)
6f038d7b 474 while (tfname[i] != 'A') {
51d1035a
BJ
475 tfname[i]--;
476 unlink(tfname);
477 }
478 if (cfname)
6f038d7b 479 while (cfname[i] != 'A') {
51d1035a
BJ
480 cfname[i]--;
481 unlink(cfname);
482 }
51d1035a 483 if (dfname)
6f038d7b 484 while (dfname[i] != 'A') {
51d1035a
BJ
485 dfname[i]--;
486 unlink(dfname);
487 }
488 exit();
489}
490
fdaeefb7
BJ
491/*
492 * Test to see if this is a printable file.
c249bc99 493 * Return -1 if it is not, 1 if it's publically readable, else 0
fdaeefb7 494 */
51d1035a 495test(file)
6f038d7b 496 char *file;
51d1035a 497{
fdaeefb7
BJ
498 struct exec execb;
499 struct stat statb;
51d1035a
BJ
500 int fd;
501
502 if (access(file, 4) < 0) {
503 printf("%s: cannot access %s\n", name, file);
fdaeefb7 504 return(-1);
51d1035a 505 }
fdaeefb7 506 if (stat(file, &statb) < 0) {
51d1035a 507 printf("%s: cannot stat %s\n", name, file);
fdaeefb7 508 return(-1);
51d1035a 509 }
fdaeefb7 510 if ((statb.st_mode & S_IFMT) == S_IFDIR) {
51d1035a 511 printf("%s: %s is a directory\n", name, file);
fdaeefb7 512 return(-1);
51d1035a 513 }
6f038d7b 514 if ((fd = open(file, 0)) < 0) {
51d1035a 515 printf("%s: cannot open %s\n", name, file);
fdaeefb7 516 return(-1);
51d1035a 517 }
fdaeefb7
BJ
518 if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
519 switch(execb.a_magic) {
51d1035a
BJ
520 case A_MAGIC1:
521 case A_MAGIC2:
522 case A_MAGIC3:
523#ifdef A_MAGIC4
524 case A_MAGIC4:
525#endif
526 printf("%s: %s is an executable program", name, file);
527 goto error1;
528
529 case ARMAG:
530 printf("%s: %s is an archive file", name, file);
531 goto error1;
532 }
fdaeefb7
BJ
533 (void) close(fd);
534 if (rflag) { /* check to make sure user can delete this file */
535 register char *cp = rindex(file, '/');
536
537 if (cp == NULL)
878a0818
RC
538 fd = access(".", 2);
539 else {
fdaeefb7 540 *cp = '\0';
878a0818 541 fd = access(file, 2);
fdaeefb7 542 *cp = '/';
878a0818
RC
543 }
544 if (fd < 0) {
fdaeefb7
BJ
545 printf("%s: cannot remove %s\n", name, file);
546 return(-1);
547 }
fdaeefb7 548 }
c249bc99
RC
549 if (statb.st_mode & 04)
550 return(1);
fdaeefb7 551 return(0);
51d1035a 552
51d1035a
BJ
553error1:
554 printf(" and is unprintable\n");
fdaeefb7
BJ
555 (void) close(fd);
556 return(-1);
51d1035a
BJ
557}
558
559/*
560 * itoa - integer to string conversion
561 */
562char *
563itoa(i)
6f038d7b 564 register int i;
51d1035a
BJ
565{
566 static char b[10] = "########";
567 register char *p;
568
569 p = &b[8];
570 do
571 *p-- = i%10 + '0';
572 while (i /= 10);
fdaeefb7 573 return(++p);
51d1035a
BJ
574}
575
576/*
577 * Perform lookup for printer name or abbreviation --
51d1035a
BJ
578 */
579chkprinter(s)
6f038d7b 580 register char *s;
51d1035a
BJ
581{
582 static char buf[BUFSIZ/2];
583 char b[BUFSIZ];
584 int stat;
585 char *bp = buf;
586
587 if ((stat = pgetent(b, s)) < 0) {
fdaeefb7 588 printf("%s: can't open printer description file\n", name);
51d1035a
BJ
589 exit(3);
590 } else if (stat == 0)
fdaeefb7 591 return(0);
51d1035a
BJ
592 if ((DN = pgetstr("dn", &bp)) == NULL)
593 DN = DEFDAEMON;
594 if ((LP = pgetstr("lp", &bp)) == NULL)
595 LP = DEFDEVLP;
fdaeefb7
BJ
596 if ((SD = pgetstr("sd", &bp)) == NULL)
597 SD = DEFSPOOL;
51d1035a
BJ
598 if ((MX = pgetnum("mx")) < 0)
599 MX = DEFMX;
fdaeefb7
BJ
600 RM = pgetstr("rm", &bp);
601 return(1);
602}
603
604/*
605 * Make the temp files.
606 */
607mktemps()
608{
c249bc99 609 register int c, len;
fdaeefb7
BJ
610 int n;
611 char buf[BUFSIZ], *mktemp();
c249bc99 612 FILE *fp;
fdaeefb7
BJ
613
614 (void) sprintf(buf, "%s/.seq", SD);
c249bc99 615 if ((fp = fopen(buf, "r+")) == NULL) {
878a0818
RC
616 if ((fp = fopen(buf, "w")) == NULL) {
617 printf("%s: cannot create %s\n", name, buf);
618 exit(1);
619 }
620 setbuf(fp, buf);
621 n = 0;
c249bc99
RC
622 } else {
623 setbuf(fp, buf);
878a0818 624#ifdef BSD41C
c249bc99 625 if (flock(fileno(fp), FEXLOCK)) {
fdaeefb7
BJ
626 printf("%s: cannot lock %s\n", name, buf);
627 exit(1);
628 }
878a0818 629#endif
c249bc99
RC
630 n = 0;
631 while ((c = getc(fp)) >= '0' && c <= '9')
632 n = n * 10 + (c - '0');
fdaeefb7
BJ
633 }
634 len = strlen(SD) + strlen(host) + 8;
635 tfname = mktemp("tf", n, len);
636 cfname = mktemp("cf", n, len);
637 dfname = mktemp("df", n, len);
638 inchar = strlen(SD) + 3;
639 n = (n + 1) % 1000;
c249bc99
RC
640 (void) fseek(fp, 0L, 0);
641 fprintf(fp, "%d\n", n);
642 (void) fclose(fp);
51d1035a
BJ
643}
644
645/*
fdaeefb7 646 * Make a temp file name.
51d1035a
BJ
647 */
648char *
fdaeefb7
BJ
649mktemp(id, num, len)
650 char *id;
651 int num, len;
51d1035a
BJ
652{
653 register char *s;
654
fdaeefb7
BJ
655 if ((s = malloc(len)) == NULL) {
656 printf("%s: out of memory\n", name);
51d1035a
BJ
657 exit(1);
658 }
fdaeefb7
BJ
659 (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
660 return(s);
51d1035a 661}