fixed several bugs.
[unix-history] / usr / src / usr.sbin / lpr / lpr / lpr.c
... / ...
CommitLineData
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
58/* lpr.c 4.9 83/03/01 */
59/*
60 * lpr -- off line print
61 *
62 * Allows multiple printers and printers on remote machines by
63 * using information from a printer data base.
64 */
65
66#include <sys/types.h>
67#include <sys/stat.h>
68#include <sys/file.h>
69#include <signal.h>
70#include <pwd.h>
71#include <stdio.h>
72#include <ctype.h>
73#include "lp.local.h"
74
75char *tfname; /* tmp copy of cf before linking */
76char *cfname; /* daemon control files, linked from tf's */
77char *dfname; /* data files */
78
79int nact; /* number of jobs to act on */
80int tfd; /* control file descriptor */
81int mailflg; /* send mail */
82int qflag; /* q job, but don't exec daemon */
83char format = 'f'; /* format char for printing files */
84int rflag; /* remove files upon completion */
85int lflag; /* link flag */
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 */
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 */
95int MX; /* maximum size in blocks of a print file */
96int hdr = 1; /* print header or not (default is yes) */
97int userid; /* user id */
98char *title; /* pr'ing title */
99char *fonts[4]; /* troff font names */
100char *width; /* width for versatec printing */
101char host[32]; /* host name */
102char *class = host; /* class title on header page */
103char *jobname; /* job name on header page */
104char *name; /* program name */
105
106char *pgetstr();
107char *malloc();
108char *getenv();
109char *rindex();
110
111/*ARGSUSED*/
112main(argc, argv)
113 int argc;
114 char *argv[];
115{
116 extern char *getlogin();
117 extern struct passwd *getpwuid(), *getpwnam();
118 struct passwd *pw;
119 extern char *itoa();
120 register char *arg;
121 int i, f, out();
122 char *printer = NULL;
123 struct stat stb;
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.
132 * 3. Files in spooling area are owned by daemon and spooling
133 * group, with mode 660.
134 * 4. lpd runs setuid root and setgrp spooling group to
135 * access files and printer. Users can't get to anything
136 * w/o help of lpq and lprm programs.
137 */
138 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
139 signal(SIGHUP, out);
140 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
141 signal(SIGINT, out);
142 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
143 signal(SIGQUIT, out);
144 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
145 signal(SIGTERM, out);
146
147 gethostname(host, sizeof (host));
148 name = argv[0];
149
150 while (argc > 1 && (arg = argv[1])[0] == '-') {
151 argc--;
152 argv++;
153 switch (arg[1]) {
154
155 case 'P': /* specifiy printer name */
156 printer = &arg[2];
157 break;
158
159 case 'C': /* classification spec */
160 hdr++;
161 if (arg[2])
162 class = &arg[2];
163 else if (argc > 1) {
164 argc--;
165 class = *++argv;
166 }
167 break;
168
169 case 'J': /* job name */
170 hdr++;
171 if (arg[2])
172 jobname = &arg[2];
173 else if (argc > 1) {
174 argc--;
175 jobname = *++argv;
176 }
177 break;
178
179 case 'T': /* pr's title line */
180 if (arg[2])
181 title = &arg[2];
182 else if (argc > 1) {
183 argc--;
184 title = *++argv;
185 }
186 break;
187
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;
209 break;
210
211 case 'r': /* remove file when done */
212 rflag++;
213 break;
214
215 case 'm': /* send mail when done */
216 mailflg++;
217 break;
218
219 case 'h': /* toggle want of header page */
220 hdr = !hdr;
221 break;
222
223 case 's': /* try to link files */
224 lflag++;
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
236 default: /* n copies ? */
237 if (isdigit(arg[1]))
238 ncopies = atoi(&arg[1]);
239 }
240 }
241 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
242 printer = DEFLP;
243 if (!chkprinter(printer)) {
244 printf("%s: unknown printer\n", name, printer);
245 exit(2);
246 }
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 */
259 mktemps();
260 tfd = nfile(tfname);
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);
273 }
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);
284
285 if (argc == 1)
286 copy(0, " ");
287 else while (--argc) {
288 if ((i = test(arg = *++argv)) < 0)
289 continue; /* file unreasonable */
290
291 if (i && lflag && linked(arg)) {
292 if (format == 'p')
293 card('T', title ? title : arg);
294 for (i = 0; i < ncopies; i++)
295 card(format, &dfname[inchar-2]);
296 card('U', &dfname[inchar-2]);
297 card('N', arg);
298 dfname[inchar]++;
299 nact++;
300 } else {
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);
307 }
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);
320 }
321 }
322
323 if (nact) {
324 tfname[inchar]--;
325 if (link(tfname, cfname) < 0) {
326 printf("%s: cannot rename %s\n", name, cfname);
327 tfname[inchar]++;
328 out();
329 }
330 unlink(tfname);
331 if (qflag) /* just q things up */
332 exit(0);
333 if (*LP && stat(LP, &stb) >= 0 && (stb.st_mode & 0777) == 0) {
334 printf("jobs queued, but line printer is down.\n");
335 exit(0);
336 }
337 execl(DN, arg = rindex(DN, "/") ? arg+1 : DN, printer, 0);
338 printf("jobs queued, but cannot start daemon.\n");
339 exit(0);
340 }
341 out();
342 /*NOTREACHED*/
343}
344
345/*
346 * Create the file n and copy from file descriptor f.
347 */
348copy(f, n)
349 int f;
350 char n[];
351{
352 register int fd, i, nr, nc;
353 char buf[BUFSIZ];
354
355 if (format == 'p')
356 card('T', title ? title : n);
357 for (i = 0; i < ncopies; i++)
358 card(format, &dfname[inchar-2]);
359 card('U', &dfname[inchar-2]);
360 card('N', n);
361 fd = nfile(dfname);
362 nr = nc = 0;
363 while ((i = read(f, buf, BUFSIZ)) > 0) {
364 if (write(fd, buf, i) != i) {
365 printf("%s: %s: temp file write error\n", name, n);
366 break;
367 }
368 nc += i;
369 if (nc >= BUFSIZ) {
370 nc -= BUFSIZ;
371 if (nr++ > MX) {
372 printf("%s: %s: copy file is too large\n", name, n);
373 break;
374 }
375 }
376 }
377 (void) close(fd);
378 nact++;
379}
380
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 */
420card(c, p2)
421 register char c, *p2;
422{
423 char buf[BUFSIZ];
424 register char *p1 = buf;
425 register int len = 2;
426
427 *p1++ = c;
428 while ((c = *p2++) != '\0') {
429 *p1++ = c;
430 len++;
431 }
432 *p1++ = '\n';
433 write(tfd, buf, len);
434}
435
436/*
437 * Create a new file in the spool directory.
438 */
439
440nfile(n)
441 char *n;
442{
443 register f;
444 int oldumask = umask(0); /* should block signals */
445
446 f = creat(n, FILMOD);
447 (void) umask(oldumask);
448 if (f < 0) {
449 printf("%s: cannot create %s\n", name, n);
450 out();
451 }
452 if (chown(n, userid, -1) < 0) {
453 unlink(n);
454 printf("%s: cannot chown %s\n", name, n);
455 out();
456 }
457 n[inchar]++;
458 return(f);
459}
460
461/*
462 * Cleanup after interrupts and errors.
463 */
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)
474 while (tfname[i] != 'A') {
475 tfname[i]--;
476 unlink(tfname);
477 }
478 if (cfname)
479 while (cfname[i] != 'A') {
480 cfname[i]--;
481 unlink(cfname);
482 }
483 if (dfname)
484 while (dfname[i] != 'A') {
485 dfname[i]--;
486 unlink(dfname);
487 }
488 exit();
489}
490
491/*
492 * Test to see if this is a printable file.
493 * Return -1 if it is not, 1 if it's publically readable, else 0
494 */
495test(file)
496 char *file;
497{
498 struct exec execb;
499 struct stat statb;
500 int fd;
501
502 if (access(file, 4) < 0) {
503 printf("%s: cannot access %s\n", name, file);
504 return(-1);
505 }
506 if (stat(file, &statb) < 0) {
507 printf("%s: cannot stat %s\n", name, file);
508 return(-1);
509 }
510 if ((statb.st_mode & S_IFMT) == S_IFDIR) {
511 printf("%s: %s is a directory\n", name, file);
512 return(-1);
513 }
514 if ((fd = open(file, 0)) < 0) {
515 printf("%s: cannot open %s\n", name, file);
516 return(-1);
517 }
518 if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
519 switch(execb.a_magic) {
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 }
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)
538 fd = access(".", 2);
539 else {
540 *cp = '\0';
541 fd = access(file, 2);
542 *cp = '/';
543 }
544 if (fd < 0) {
545 printf("%s: cannot remove %s\n", name, file);
546 return(-1);
547 }
548 }
549 if (statb.st_mode & 04)
550 return(1);
551 return(0);
552
553error1:
554 printf(" and is unprintable\n");
555 (void) close(fd);
556 return(-1);
557}
558
559/*
560 * itoa - integer to string conversion
561 */
562char *
563itoa(i)
564 register int i;
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);
573 return(++p);
574}
575
576/*
577 * Perform lookup for printer name or abbreviation --
578 */
579chkprinter(s)
580 register char *s;
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) {
588 printf("%s: can't open printer description file\n", name);
589 exit(3);
590 } else if (stat == 0)
591 return(0);
592 if ((DN = pgetstr("dn", &bp)) == NULL)
593 DN = DEFDAEMON;
594 if ((LP = pgetstr("lp", &bp)) == NULL)
595 LP = DEFDEVLP;
596 if ((SD = pgetstr("sd", &bp)) == NULL)
597 SD = DEFSPOOL;
598 if ((MX = pgetnum("mx")) < 0)
599 MX = DEFMX;
600 RM = pgetstr("rm", &bp);
601 return(1);
602}
603
604/*
605 * Make the temp files.
606 */
607mktemps()
608{
609 register int c, len;
610 int n;
611 char buf[BUFSIZ], *mktemp();
612 FILE *fp;
613
614 (void) sprintf(buf, "%s/.seq", SD);
615 if ((fp = fopen(buf, "r+")) == NULL) {
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;
622 } else {
623 setbuf(fp, buf);
624#ifdef BSD41C
625 if (flock(fileno(fp), FEXLOCK)) {
626 printf("%s: cannot lock %s\n", name, buf);
627 exit(1);
628 }
629#endif
630 n = 0;
631 while ((c = getc(fp)) >= '0' && c <= '9')
632 n = n * 10 + (c - '0');
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;
640 (void) fseek(fp, 0L, 0);
641 fprintf(fp, "%d\n", n);
642 (void) fclose(fp);
643}
644
645/*
646 * Make a temp file name.
647 */
648char *
649mktemp(id, num, len)
650 char *id;
651 int num, len;
652{
653 register char *s;
654
655 if ((s = malloc(len)) == NULL) {
656 printf("%s: out of memory\n", name);
657 exit(1);
658 }
659 (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
660 return(s);
661}