xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision 18f6d767)
1 #ifndef lint
2 static char sccsid[] = "@(#)lpr.c	4.29 (Berkeley) 02/14/85";
3 #endif
4 
5 /*
6  *      lpr -- off line print
7  *
8  * Allows multiple printers and printers on remote machines by
9  * using information from a printer data base.
10  */
11 
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/file.h>
15 #include <sys/stat.h>
16 #include <pwd.h>
17 #include <grp.h>
18 #include <signal.h>
19 #include <ctype.h>
20 #include "lp.local.h"
21 
22 char    *tfname;		/* tmp copy of cf before linking */
23 char    *cfname;		/* daemon control files, linked from tf's */
24 char    *dfname;		/* data files */
25 
26 int	nact;			/* number of jobs to act on */
27 int	tfd;			/* control file descriptor */
28 int     mailflg;		/* send mail */
29 int	qflag;			/* q job, but don't exec daemon */
30 char	format = 'f';		/* format char for printing files */
31 int	rflag;			/* remove files upon completion */
32 int	sflag;			/* symbolic link flag */
33 int	inchar;			/* location to increment char in file names */
34 int     ncopies = 1;		/* # of copies to make */
35 int	iflag;			/* indentation wanted */
36 int	indent;			/* amount to indent */
37 int	hdr = 1;		/* print header or not (default is yes) */
38 int     userid;			/* user id */
39 char	*person;		/* user name */
40 char	*title;			/* pr'ing title */
41 char	*fonts[4];		/* troff font names */
42 char	*width;			/* width for versatec printing */
43 char	host[32];		/* host name */
44 char	*class = host;		/* class title on header page */
45 char    *jobname;		/* job name on header page */
46 char	*name;			/* program name */
47 char	*printer;		/* printer name */
48 struct	stat statb;
49 
50 int	MX;			/* maximum number of blocks to copy */
51 int	MC;			/* maximum number of copies allowed */
52 int	DU;			/* daemon user-id */
53 char	*SD;			/* spool directory */
54 char	*LO;			/* lock file name */
55 char	*RG;			/* restrict group */
56 short	SC;			/* suppress multiple copies */
57 
58 char	*getenv();
59 char	*rindex();
60 char	*linked();
61 int	cleanup();
62 
63 /*ARGSUSED*/
64 main(argc, argv)
65 	int argc;
66 	char *argv[];
67 {
68 	extern struct passwd *getpwuid();
69 	struct passwd *pw;
70 	struct group *gptr;
71 	extern char *itoa();
72 	register char *arg, *cp;
73 	char buf[BUFSIZ];
74 	int i, f;
75 	struct stat stb;
76 
77 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
78 		signal(SIGHUP, cleanup);
79 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
80 		signal(SIGINT, cleanup);
81 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
82 		signal(SIGQUIT, cleanup);
83 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
84 		signal(SIGTERM, cleanup);
85 
86 	name = argv[0];
87 	gethostname(host, sizeof (host));
88 
89 	while (argc > 1 && argv[1][0] == '-') {
90 		argc--;
91 		arg = *++argv;
92 		switch (arg[1]) {
93 
94 		case 'P':		/* specifiy printer name */
95 			if (arg[2])
96 				printer = &arg[2];
97 			else if (argc > 1) {
98 				argc--;
99 				printer = *++argv;
100 			}
101 			break;
102 
103 		case 'C':		/* classification spec */
104 			hdr++;
105 			if (arg[2])
106 				class = &arg[2];
107 			else if (argc > 1) {
108 				argc--;
109 				class = *++argv;
110 			}
111 			break;
112 
113 		case 'J':		/* job name */
114 			hdr++;
115 			if (arg[2])
116 				jobname = &arg[2];
117 			else if (argc > 1) {
118 				argc--;
119 				jobname = *++argv;
120 			}
121 			break;
122 
123 		case 'T':		/* pr's title line */
124 			if (arg[2])
125 				title = &arg[2];
126 			else if (argc > 1) {
127 				argc--;
128 				title = *++argv;
129 			}
130 			break;
131 
132 		case 'l':		/* literal output */
133 		case 'p':		/* print using ``pr'' */
134 		case 't':		/* print troff output (cat files) */
135 		case 'n':		/* print ditroff output */
136 		case 'd':		/* print tex output (dvi files) */
137 		case 'g':		/* print graph(1G) output */
138 		case 'c':		/* print cifplot output */
139 		case 'v':		/* print vplot output */
140 			format = arg[1];
141 			break;
142 
143 		case 'f':		/* print fortran output */
144 			format = 'r';
145 			break;
146 
147 		case '4':		/* troff fonts */
148 		case '3':
149 		case '2':
150 		case '1':
151 			if (argc > 1) {
152 				argc--;
153 				fonts[arg[1] - '1'] = *++argv;
154 			}
155 			break;
156 
157 		case 'w':		/* versatec page width */
158 			width = arg+2;
159 			break;
160 
161 		case 'r':		/* remove file when done */
162 			rflag++;
163 			break;
164 
165 		case 'm':		/* send mail when done */
166 			mailflg++;
167 			break;
168 
169 		case 'h':		/* toggle want of header page */
170 			hdr = !hdr;
171 			break;
172 
173 		case 's':		/* try to link files */
174 			sflag++;
175 			break;
176 
177 		case 'q':		/* just q job */
178 			qflag++;
179 			break;
180 
181 		case 'i':		/* indent output */
182 			iflag++;
183 			indent = arg[2] ? atoi(&arg[2]) : 8;
184 			break;
185 
186 		case '#':		/* n copies */
187 			if (isdigit(arg[2])) {
188 				i = atoi(&arg[2]);
189 				if (i > 0)
190 					ncopies = i;
191 			}
192 		}
193 	}
194 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
195 		printer = DEFLP;
196 	chkprinter(printer);
197 	if (SC && ncopies > 1)
198 		fatal("multiple copies are not allowed");
199 	if (MC > 0 && ncopies > MC)
200 		fatal("only %d copies are allowed", MC);
201 	/*
202 	 * Get the identity of the person doing the lpr using the same
203 	 * algorithm as lprm.
204 	 */
205 	userid = getuid();
206 	if ((pw = getpwuid(userid)) == NULL)
207 		fatal("Who are you?");
208 	person = pw->pw_name;
209 	/*
210 	 * Check for restricted group access.
211 	 */
212 	if (RG != NULL) {
213 		if ((gptr = getgrnam(RG)) == NULL)
214 			fatal("Restricted group specified incorrectly");
215 		if (gptr->gr_gid != getgid()) {
216 			while (*gptr->gr_mem != NULL) {
217 				if ((strcmp(person, *gptr->gr_mem)) == 0)
218 					break;
219 				gptr->gr_mem++;
220 			}
221 			if (*gptr->gr_mem == NULL)
222 				fatal("Not a member of the restricted group");
223 		}
224 	}
225 	/*
226 	 * Check to make sure queuing is enabled if userid is not root.
227 	 */
228 	(void) sprintf(buf, "%s/%s", SD, LO);
229 	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
230 		fatal("Printer queue is disabled");
231 	/*
232 	 * Initialize the control file.
233 	 */
234 	mktemps();
235 	tfd = nfile(tfname);
236 	(void) fchown(tfd, DU, -1);	/* owned by daemon for protection */
237 	card('H', host);
238 	card('P', person);
239 	if (hdr) {
240 		if (jobname == NULL) {
241 			if (argc == 1)
242 				jobname = "stdin";
243 			else
244 				jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1];
245 		}
246 		card('J', jobname);
247 		card('C', class);
248 		card('L', person);
249 	}
250 	if (iflag)
251 		card('I', itoa(indent));
252 	if (mailflg)
253 		card('M', person);
254 	if (format == 't' || format == 'n' || format == 'd')
255 		for (i = 0; i < 4; i++)
256 			if (fonts[i] != NULL)
257 				card('1'+i, fonts[i]);
258 	if (width != NULL)
259 		card('W', width);
260 
261 	/*
262 	 * Read the files and spool them.
263 	 */
264 	if (argc == 1)
265 		copy(0, " ");
266 	else while (--argc) {
267 		if ((f = test(arg = *++argv)) < 0)
268 			continue;	/* file unreasonable */
269 
270 		if (sflag && (cp = linked(arg)) != NULL) {
271 			(void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino);
272 			card('S', buf);
273 			if (format == 'p')
274 				card('T', title ? title : arg);
275 			for (i = 0; i < ncopies; i++)
276 				card(format, &dfname[inchar-2]);
277 			card('U', &dfname[inchar-2]);
278 			if (f)
279 				card('U', cp);
280 			card('N', arg);
281 			dfname[inchar]++;
282 			nact++;
283 			continue;
284 		}
285 		if (sflag)
286 			printf("%s: %s: not linked, copying instead\n", name, arg);
287 		if ((i = open(arg, O_RDONLY)) < 0) {
288 			printf("%s: cannot open %s\n", name, arg);
289 			continue;
290 		}
291 		copy(i, arg);
292 		(void) close(i);
293 		if (f && unlink(arg) < 0)
294 			printf("%s: %s: not removed\n", name, arg);
295 	}
296 
297 	if (nact) {
298 		(void) close(tfd);
299 		tfname[inchar]--;
300 		/*
301 		 * Touch the control file to fix position in the queue.
302 		 */
303 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
304 			char c;
305 
306 			if (read(tfd, &c, 1) == 1 && lseek(tfd, 0L, 0) == 0 &&
307 			    write(tfd, &c, 1) != 1) {
308 				printf("%s: cannot touch %s\n", name, tfname);
309 				tfname[inchar]++;
310 				cleanup();
311 			}
312 			(void) close(tfd);
313 		}
314 		if (link(tfname, cfname) < 0) {
315 			printf("%s: cannot rename %s\n", name, cfname);
316 			tfname[inchar]++;
317 			cleanup();
318 		}
319 		unlink(tfname);
320 		if (qflag)		/* just q things up */
321 			exit(0);
322 		if (!startdaemon(printer))
323 			printf("jobs queued, but cannot start daemon.\n");
324 		exit(0);
325 	}
326 	cleanup();
327 	/* NOTREACHED */
328 }
329 
330 /*
331  * Create the file n and copy from file descriptor f.
332  */
333 copy(f, n)
334 	int f;
335 	char n[];
336 {
337 	register int fd, i, nr, nc;
338 	char buf[BUFSIZ];
339 
340 	if (format == 'p')
341 		card('T', title ? title : n);
342 	for (i = 0; i < ncopies; i++)
343 		card(format, &dfname[inchar-2]);
344 	card('U', &dfname[inchar-2]);
345 	card('N', n);
346 	fd = nfile(dfname);
347 	nr = nc = 0;
348 	while ((i = read(f, buf, BUFSIZ)) > 0) {
349 		if (write(fd, buf, i) != i) {
350 			printf("%s: %s: temp file write error\n", name, n);
351 			break;
352 		}
353 		nc += i;
354 		if (nc >= BUFSIZ) {
355 			nc -= BUFSIZ;
356 			nr++;
357 			if (MX > 0 && nr > MX) {
358 				printf("%s: %s: copy file is too large\n", name, n);
359 				break;
360 			}
361 		}
362 	}
363 	(void) close(fd);
364 	if (nc==0 && nr==0)
365 		printf("%s: %s: empty input file\n", name, f ? n : "stdin");
366 	else
367 		nact++;
368 }
369 
370 /*
371  * Try and link the file to dfname. Return a pointer to the full
372  * path name if successful.
373  */
374 char *
375 linked(file)
376 	register char *file;
377 {
378 	register char *cp;
379 	static char buf[BUFSIZ];
380 
381 	if (*file != '/') {
382 		if (getwd(buf) == NULL)
383 			return(NULL);
384 		while (file[0] == '.') {
385 			switch (file[1]) {
386 			case '/':
387 				file += 2;
388 				continue;
389 			case '.':
390 				if (file[2] == '/') {
391 					if ((cp = rindex(buf, '/')) != NULL)
392 						*cp = '\0';
393 					file += 3;
394 					continue;
395 				}
396 			}
397 			break;
398 		}
399 		strcat(buf, "/");
400 		strcat(buf, file);
401 		file = buf;
402 	}
403 	return(symlink(file, dfname) ? NULL : file);
404 }
405 
406 /*
407  * Put a line into the control file.
408  */
409 card(c, p2)
410 	register char c, *p2;
411 {
412 	char buf[BUFSIZ];
413 	register char *p1 = buf;
414 	register int len = 2;
415 
416 	*p1++ = c;
417 	while ((c = *p2++) != '\0') {
418 		*p1++ = c;
419 		len++;
420 	}
421 	*p1++ = '\n';
422 	write(tfd, buf, len);
423 }
424 
425 /*
426  * Create a new file in the spool directory.
427  */
428 nfile(n)
429 	char *n;
430 {
431 	register f;
432 	int oldumask = umask(0);		/* should block signals */
433 
434 	f = creat(n, FILMOD);
435 	(void) umask(oldumask);
436 	if (f < 0) {
437 		printf("%s: cannot create %s\n", name, n);
438 		cleanup();
439 	}
440 	if (fchown(f, userid, -1) < 0) {
441 		printf("%s: cannot chown %s\n", name, n);
442 		cleanup();
443 	}
444 	if (++n[inchar] > 'z') {
445 		if (++n[inchar-2] == 't') {
446 			printf("too many files - break up the job\n");
447 			cleanup();
448 		}
449 		n[inchar] = 'A';
450 	} else if (n[inchar] == '[')
451 		n[inchar] = 'a';
452 	return(f);
453 }
454 
455 /*
456  * Cleanup after interrupts and errors.
457  */
458 cleanup()
459 {
460 	register i;
461 
462 	signal(SIGHUP, SIG_IGN);
463 	signal(SIGINT, SIG_IGN);
464 	signal(SIGQUIT, SIG_IGN);
465 	signal(SIGTERM, SIG_IGN);
466 	i = inchar;
467 	if (tfname)
468 		do
469 			unlink(tfname);
470 		while (tfname[i]-- != 'A');
471 	if (cfname)
472 		do
473 			unlink(cfname);
474 		while (cfname[i]-- != 'A');
475 	if (dfname)
476 		do {
477 			do
478 				unlink(dfname);
479 			while (dfname[i]-- != 'A');
480 			dfname[i] = 'z';
481 		} while (dfname[i-2]-- != 'd');
482 	exit(1);
483 }
484 
485 /*
486  * Test to see if this is a printable file.
487  * Return -1 if it is not, 0 if its printable, and 1 if
488  * we should remove it after printing.
489  */
490 test(file)
491 	char *file;
492 {
493 	struct exec execb;
494 	register int fd;
495 	register char *cp;
496 
497 	if (access(file, 4) < 0) {
498 		printf("%s: cannot access %s\n", name, file);
499 		return(-1);
500 	}
501 	if (stat(file, &statb) < 0) {
502 		printf("%s: cannot stat %s\n", name, file);
503 		return(-1);
504 	}
505 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
506 		printf("%s: %s is a directory\n", name, file);
507 		return(-1);
508 	}
509 	if (statb.st_size == 0) {
510 		printf("%s: %s is an empty file\n", name, file);
511 		return(-1);
512  	}
513 	if ((fd = open(file, O_RDONLY)) < 0) {
514 		printf("%s: cannot open %s\n", name, file);
515 		return(-1);
516 	}
517 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
518 		switch(execb.a_magic) {
519 		case A_MAGIC1:
520 		case A_MAGIC2:
521 		case A_MAGIC3:
522 #ifdef A_MAGIC4
523 		case A_MAGIC4:
524 #endif
525 			printf("%s: %s is an executable program", name, file);
526 			goto error1;
527 
528 		case ARMAG:
529 			printf("%s: %s is an archive file", name, file);
530 			goto error1;
531 		}
532 	(void) close(fd);
533 	if (rflag) {
534 		if ((cp = rindex(file, '/')) == NULL) {
535 			if (access(".", 2) == 0)
536 				return(1);
537 		} else {
538 			*cp = '\0';
539 			fd = access(file, 2);
540 			*cp = '/';
541 			if (fd == 0)
542 				return(1);
543 		}
544 		printf("%s: %s: is not removable by you\n", name, file);
545 	}
546 	return(0);
547 
548 error1:
549 	printf(" and is unprintable\n");
550 	(void) close(fd);
551 	return(-1);
552 }
553 
554 /*
555  * itoa - integer to string conversion
556  */
557 char *
558 itoa(i)
559 	register int i;
560 {
561 	static char b[10] = "########";
562 	register char *p;
563 
564 	p = &b[8];
565 	do
566 		*p-- = i%10 + '0';
567 	while (i /= 10);
568 	return(++p);
569 }
570 
571 /*
572  * Perform lookup for printer name or abbreviation --
573  */
574 chkprinter(s)
575 	char *s;
576 {
577 	int status;
578 	char buf[BUFSIZ];
579 	static char pbuf[BUFSIZ/2];
580 	char *bp = pbuf;
581 	extern char *pgetstr();
582 
583 	if ((status = pgetent(buf, s)) < 0)
584 		fatal("cannot open printer description file");
585 	else if (status == 0)
586 		fatal("%s: unknown printer", s);
587 	if ((SD = pgetstr("sd", &bp)) == NULL)
588 		SD = DEFSPOOL;
589 	if ((LO = pgetstr("lo", &bp)) == NULL)
590 		LO = DEFLOCK;
591 	RG = pgetstr("rg", &bp);
592 	if ((MX = pgetnum("mx")) < 0)
593 		MX = DEFMX;
594 	if ((MC = pgetnum("mc")) < 0)
595 		MC = DEFMAXCOPIES;
596 	if ((DU = pgetnum("du")) < 0)
597 		DU = DEFUID;
598 	SC = pgetflag("sc");
599 }
600 
601 /*
602  * Make the temp files.
603  */
604 mktemps()
605 {
606 	register int c, len, fd, n;
607 	register char *cp;
608 	char buf[BUFSIZ];
609 	char *mktemp();
610 
611 	(void) sprintf(buf, "%s/.seq", SD);
612 	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
613 		printf("%s: cannot create %s\n", name, buf);
614 		exit(1);
615 	}
616 	if (flock(fd, LOCK_EX)) {
617 		printf("%s: cannot lock %s\n", name, buf);
618 		exit(1);
619 	}
620 	n = 0;
621 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
622 		for (cp = buf; len--; ) {
623 			if (*cp < '0' || *cp > '9')
624 				break;
625 			n = n * 10 + (*cp++ - '0');
626 		}
627 	}
628 	len = strlen(SD) + strlen(host) + 8;
629 	tfname = mktemp("tf", n, len);
630 	cfname = mktemp("cf", n, len);
631 	dfname = mktemp("df", n, len);
632 	inchar = strlen(SD) + 3;
633 	n = (n + 1) % 1000;
634 	(void) lseek(fd, 0L, 0);
635 	sprintf(buf, "%03d\n", n);
636 	(void) write(fd, buf, strlen(buf));
637 	(void) close(fd);	/* unlocks as well */
638 }
639 
640 /*
641  * Make a temp file name.
642  */
643 char *
644 mktemp(id, num, len)
645 	char	*id;
646 	int	num, len;
647 {
648 	register char *s;
649 	extern char *malloc();
650 
651 	if ((s = malloc(len)) == NULL)
652 		fatal("out of memory");
653 	(void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
654 	return(s);
655 }
656 
657 /*VARARGS1*/
658 fatal(msg, a1, a2, a3)
659 	char *msg;
660 {
661 	printf("%s: ", name);
662 	printf(msg, a1, a2, a3);
663 	putchar('\n');
664 	exit(1);
665 }
666