xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision 6c57d260)
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