xref: /netbsd-src/usr.sbin/lpr/lpc/cmds.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: cmds.c,v 1.7 1996/01/14 17:25:21 hpeyerl Exp $	*/
2 /*
3  * Copyright (c) 1983, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1983, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41 
42 #ifndef lint
43 static char sccsid[] = "@(#)cmds.c	8.1 (Berkeley) 6/6/93";
44 #endif /* not lint */
45 
46 /*
47  * lpc -- line printer control program -- commands:
48  */
49 
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 
54 #include <signal.h>
55 #include <fcntl.h>
56 #include <errno.h>
57 #include <dirent.h>
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <ctype.h>
62 #include <string.h>
63 #include "lp.h"
64 #include "lp.local.h"
65 #include "lpc.h"
66 #include "extern.h"
67 #include "pathnames.h"
68 
69 extern uid_t	uid, euid;
70 
71 static void	abortpr __P((int));
72 static void	cleanpr __P((void));
73 static void	disablepr __P((void));
74 static int	doarg __P((char *));
75 static int	doselect __P((struct dirent *));
76 static void	enablepr __P((void));
77 static void	prstat __P((void));
78 static void	putmsg __P((int, char **));
79 static int	sortq __P((const void *, const void *));
80 static void	startpr __P((int));
81 static void	stoppr __P((void));
82 static int	touch __P((struct queue *));
83 static void	unlinkf __P((char *));
84 static void	upstat __P((char *));
85 
86 /*
87  * kill an existing daemon and disable printing.
88  */
89 void
90 doabort(argc, argv)
91 	int argc;
92 	char *argv[];
93 {
94 	register int c, status;
95 	register char *cp1, *cp2;
96 	char prbuf[100];
97 
98 	if (argc == 1) {
99 		printf("Usage: abort {all | printer ...}\n");
100 		return;
101 	}
102 	if (argc == 2 && !strcmp(argv[1], "all")) {
103 		printer = prbuf;
104 		while (cgetnext(&bp, printcapdb) > 0) {
105 			cp1 = prbuf;
106 			cp2 = bp;
107 			while ((c = *cp2++) && c != '|' && c != ':')
108 				*cp1++ = c;
109 			*cp1 = '\0';
110 			abortpr(1);
111 		}
112 		return;
113 	}
114 	while (--argc) {
115 		printer = *++argv;
116 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
117 			printf("cannot open printer description file\n");
118 			continue;
119 		} else if (status == -1) {
120 			printf("unknown printer %s\n", printer);
121 			continue;
122 		} else if (status == -3)
123 			fatal("potential reference loop detected in printcap file");
124 		abortpr(1);
125 	}
126 }
127 
128 static void
129 abortpr(dis)
130 	int dis;
131 {
132 	register FILE *fp;
133 	struct stat stbuf;
134 	int pid, fd, ret;
135 
136 	if (cgetstr(bp, "sd", &SD) == -1)
137 		SD = _PATH_DEFSPOOL;
138 	if (cgetstr(bp, "lo", &LO) == -1)
139 		LO = DEFLOCK;
140 	(void) sprintf(line, "%s/%s", SD, LO);
141 	printf("%s:\n", printer);
142 
143 	/*
144 	 * Turn on the owner execute bit of the lock file to disable printing.
145 	 */
146 	if (dis) {
147 		seteuid(euid);
148 		if (stat(line, &stbuf) >= 0) {
149 			if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
150 				printf("\tcannot disable printing\n");
151 			else {
152 				upstat("printing disabled\n");
153 				printf("\tprinting disabled\n");
154 			}
155 		} else if (errno == ENOENT) {
156 			if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0)
157 				printf("\tcannot create lock file\n");
158 			else {
159 				(void) close(fd);
160 				upstat("printing disabled\n");
161 				printf("\tprinting disabled\n");
162 				printf("\tno daemon to abort\n");
163 			}
164 			goto out;
165 		} else {
166 			printf("\tcannot stat lock file\n");
167 			goto out;
168 		}
169 	}
170 	/*
171 	 * Kill the current daemon to stop printing now.
172 	 */
173 	if ((fp = fopen(line, "r")) == NULL) {
174 		printf("\tcannot open lock file\n");
175 		goto out;
176 	}
177 	if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) {
178 		(void) fclose(fp);	/* unlocks as well */
179 		printf("\tno daemon to abort\n");
180 		goto out;
181 	}
182 	(void) fclose(fp);
183 	if (kill(pid = atoi(line), SIGTERM) < 0)
184 		printf("\tWarning: daemon (pid %d) not killed\n", pid);
185 	else
186 		printf("\tdaemon (pid %d) killed\n", pid);
187 out:
188 	seteuid(uid);
189 }
190 
191 /*
192  * Write a message into the status file.
193  */
194 static void
195 upstat(msg)
196 	char *msg;
197 {
198 	register int fd;
199 	char statfile[BUFSIZ];
200 
201 	if (cgetstr(bp, "st", &ST) == -1)
202 		ST = DEFSTAT;
203 	(void) sprintf(statfile, "%s/%s", SD, ST);
204 	umask(0);
205 	fd = open(statfile, O_WRONLY|O_CREAT, 0664);
206 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
207 		printf("\tcannot create status file\n");
208 		return;
209 	}
210 	(void) ftruncate(fd, 0);
211 	if (msg == (char *)NULL)
212 		(void) write(fd, "\n", 1);
213 	else
214 		(void) write(fd, msg, strlen(msg));
215 	(void) close(fd);
216 }
217 
218 /*
219  * Remove all spool files and temporaries from the spooling area.
220  */
221 void
222 clean(argc, argv)
223 	int argc;
224 	char *argv[];
225 {
226 	register int c, status;
227 	register char *cp1, *cp2;
228 	char prbuf[100];
229 
230 	if (argc == 1) {
231 		printf("Usage: clean {all | printer ...}\n");
232 		return;
233 	}
234 	if (argc == 2 && !strcmp(argv[1], "all")) {
235 		printer = prbuf;
236 		while (cgetnext(&bp, printcapdb) > 0) {
237 			cp1 = prbuf;
238 			cp2 = bp;
239 			while ((c = *cp2++) && c != '|' && c != ':')
240 				*cp1++ = c;
241 			*cp1 = '\0';
242 			cleanpr();
243 		}
244 		return;
245 	}
246 	while (--argc) {
247 		printer = *++argv;
248 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
249 			printf("cannot open printer description file\n");
250 			continue;
251 		} else if (status == -1) {
252 			printf("unknown printer %s\n", printer);
253 			continue;
254 		} else if (status == -3)
255 			fatal("potential reference loop detected in printcap file");
256 
257 		cleanpr();
258 	}
259 }
260 
261 static int
262 doselect(d)
263 	struct dirent *d;
264 {
265 	int c = d->d_name[0];
266 
267 	if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
268 		return(1);
269 	return(0);
270 }
271 
272 /*
273  * Comparison routine for scandir. Sort by job number and machine, then
274  * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z.
275  */
276 static int
277 sortq(a, b)
278 	const void *a, *b;
279 {
280 	struct dirent **d1, **d2;
281 	int c1, c2;
282 
283 	d1 = (struct dirent **)a;
284 	d2 = (struct dirent **)b;
285 	if (c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3))
286 		return(c1);
287 	c1 = (*d1)->d_name[0];
288 	c2 = (*d2)->d_name[0];
289 	if (c1 == c2)
290 		return((*d1)->d_name[2] - (*d2)->d_name[2]);
291 	if (c1 == 'c')
292 		return(-1);
293 	if (c1 == 'd' || c2 == 'c')
294 		return(1);
295 	return(-1);
296 }
297 
298 /*
299  * Remove incomplete jobs from spooling area.
300  */
301 static void
302 cleanpr()
303 {
304 	register int i, n;
305 	register char *cp, *cp1, *lp;
306 	struct dirent **queue;
307 	int nitems;
308 
309 	if (cgetstr(bp, "sd", &SD) == -1)
310 		SD = _PATH_DEFSPOOL;
311 	printf("%s:\n", printer);
312 
313 	for (lp = line, cp = SD; *lp++ = *cp++; )
314 		;
315 	lp[-1] = '/';
316 
317 	seteuid(euid);
318 	nitems = scandir(SD, &queue, doselect, sortq);
319 	seteuid(uid);
320 	if (nitems < 0) {
321 		printf("\tcannot examine spool directory\n");
322 		return;
323 	}
324 	if (nitems == 0)
325 		return;
326 	i = 0;
327 	do {
328 		cp = queue[i]->d_name;
329 		if (*cp == 'c') {
330 			n = 0;
331 			while (i + 1 < nitems) {
332 				cp1 = queue[i + 1]->d_name;
333 				if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
334 					break;
335 				i++;
336 				n++;
337 			}
338 			if (n == 0) {
339 				strcpy(lp, cp);
340 				unlinkf(line);
341 			}
342 		} else {
343 			/*
344 			 * Must be a df with no cf (otherwise, it would have
345 			 * been skipped above) or a tf file (which can always
346 			 * be removed).
347 			 */
348 			strcpy(lp, cp);
349 			unlinkf(line);
350 		}
351      	} while (++i < nitems);
352 }
353 
354 static void
355 unlinkf(name)
356 	char	*name;
357 {
358 	seteuid(euid);
359 	if (unlink(name) < 0)
360 		printf("\tcannot remove %s\n", name);
361 	else
362 		printf("\tremoved %s\n", name);
363 	seteuid(uid);
364 }
365 
366 /*
367  * Enable queuing to the printer (allow lpr's).
368  */
369 void
370 enable(argc, argv)
371 	int argc;
372 	char *argv[];
373 {
374 	register int c, status;
375 	register char *cp1, *cp2;
376 	char prbuf[100];
377 
378 	if (argc == 1) {
379 		printf("Usage: enable {all | printer ...}\n");
380 		return;
381 	}
382 	if (argc == 2 && !strcmp(argv[1], "all")) {
383 		printer = prbuf;
384 		while (cgetnext(&bp, printcapdb) > 0) {
385 			cp1 = prbuf;
386 			cp2 = bp;
387 			while ((c = *cp2++) && c != '|' && c != ':')
388 				*cp1++ = c;
389 			*cp1 = '\0';
390 			enablepr();
391 		}
392 		return;
393 	}
394 	while (--argc) {
395 		printer = *++argv;
396 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
397 			printf("cannot open printer description file\n");
398 			continue;
399 		} else if (status == -1) {
400 			printf("unknown printer %s\n", printer);
401 			continue;
402 		} else if (status == -3)
403 			fatal("potential reference loop detected in printcap file");
404 
405 		enablepr();
406 	}
407 }
408 
409 static void
410 enablepr()
411 {
412 	struct stat stbuf;
413 
414 	if (cgetstr(bp, "sd", &SD) == -1)
415 		SD = _PATH_DEFSPOOL;
416 	if (cgetstr(bp, "lo", &LO) == -1)
417 		LO = DEFLOCK;
418 	(void) sprintf(line, "%s/%s", SD, LO);
419 	printf("%s:\n", printer);
420 
421 	/*
422 	 * Turn off the group execute bit of the lock file to enable queuing.
423 	 */
424 	seteuid(euid);
425 	if (stat(line, &stbuf) >= 0) {
426 		if (chmod(line, stbuf.st_mode & 0767) < 0)
427 			printf("\tcannot enable queuing\n");
428 		else
429 			printf("\tqueuing enabled\n");
430 	}
431 	seteuid(uid);
432 }
433 
434 /*
435  * Disable queuing.
436  */
437 void
438 disable(argc, argv)
439 	int argc;
440 	char *argv[];
441 {
442 	register int c, status;
443 	register char *cp1, *cp2;
444 	char prbuf[100];
445 
446 	if (argc == 1) {
447 		printf("Usage: disable {all | printer ...}\n");
448 		return;
449 	}
450 	if (argc == 2 && !strcmp(argv[1], "all")) {
451 		printer = prbuf;
452 		while (cgetnext(&bp, printcapdb) > 0) {
453 			cp1 = prbuf;
454 			cp2 = bp;
455 			while ((c = *cp2++) && c != '|' && c != ':')
456 				*cp1++ = c;
457 			*cp1 = '\0';
458 			disablepr();
459 		}
460 		return;
461 	}
462 	while (--argc) {
463 		printer = *++argv;
464 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
465 			printf("cannot open printer description file\n");
466 			continue;
467 		} else if (status == -1) {
468 			printf("unknown printer %s\n", printer);
469 			continue;
470 		} else if (status == -3)
471 			fatal("potential reference loop detected in printcap file");
472 
473 		disablepr();
474 	}
475 }
476 
477 static void
478 disablepr()
479 {
480 	register int fd;
481 	struct stat stbuf;
482 
483 	if (cgetstr(bp, "sd", &SD) == -1)
484 		SD = _PATH_DEFSPOOL;
485 	if (cgetstr(bp, "lo", &LO) == -1)
486 		LO = DEFLOCK;
487 	(void) sprintf(line, "%s/%s", SD, LO);
488 	printf("%s:\n", printer);
489 	/*
490 	 * Turn on the group execute bit of the lock file to disable queuing.
491 	 */
492 	seteuid(euid);
493 	if (stat(line, &stbuf) >= 0) {
494 		if (chmod(line, (stbuf.st_mode & 0777) | 010) < 0)
495 			printf("\tcannot disable queuing\n");
496 		else
497 			printf("\tqueuing disabled\n");
498 	} else if (errno == ENOENT) {
499 		if ((fd = open(line, O_WRONLY|O_CREAT, 0670)) < 0)
500 			printf("\tcannot create lock file\n");
501 		else {
502 			(void) close(fd);
503 			printf("\tqueuing disabled\n");
504 		}
505 	} else
506 		printf("\tcannot stat lock file\n");
507 	seteuid(uid);
508 }
509 
510 /*
511  * Disable queuing and printing and put a message into the status file
512  * (reason for being down).
513  */
514 void
515 down(argc, argv)
516 	int argc;
517 	char *argv[];
518 {
519 	register int c, status;
520 	register char *cp1, *cp2;
521 	char prbuf[100];
522 
523 	if (argc == 1) {
524 		printf("Usage: down {all | printer} [message ...]\n");
525 		return;
526 	}
527 	if (!strcmp(argv[1], "all")) {
528 		printer = prbuf;
529 		while (cgetnext(&bp, printcapdb) > 0) {
530 			cp1 = prbuf;
531 			cp2 = bp;
532 			while ((c = *cp2++) && c != '|' && c != ':')
533 				*cp1++ = c;
534 			*cp1 = '\0';
535 			putmsg(argc - 2, argv + 2);
536 		}
537 		return;
538 	}
539 	printer = argv[1];
540 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
541 		printf("cannot open printer description file\n");
542 		return;
543 	} else if (status == -1) {
544 		printf("unknown printer %s\n", printer);
545 		return;
546 	} else if (status == -3)
547 			fatal("potential reference loop detected in printcap file");
548 
549 	putmsg(argc - 2, argv + 2);
550 }
551 
552 static void
553 putmsg(argc, argv)
554 	int argc;
555 	char **argv;
556 {
557 	register int fd;
558 	register char *cp1, *cp2;
559 	char buf[1024];
560 	struct stat stbuf;
561 
562 	if (cgetstr(bp, "sd", &SD) == -1)
563 		SD = _PATH_DEFSPOOL;
564 	if (cgetstr(bp, "lo", &LO) == -1)
565 		LO = DEFLOCK;
566 	if (cgetstr(bp, "st", &ST) == -1)
567 		ST = DEFSTAT;
568 	printf("%s:\n", printer);
569 	/*
570 	 * Turn on the group execute bit of the lock file to disable queuing and
571 	 * turn on the owner execute bit of the lock file to disable printing.
572 	 */
573 	(void) sprintf(line, "%s/%s", SD, LO);
574 	seteuid(euid);
575 	if (stat(line, &stbuf) >= 0) {
576 		if (chmod(line, (stbuf.st_mode & 0777) | 0110) < 0)
577 			printf("\tcannot disable queuing\n");
578 		else
579 			printf("\tprinter and queuing disabled\n");
580 	} else if (errno == ENOENT) {
581 		if ((fd = open(line, O_WRONLY|O_CREAT, 0770)) < 0)
582 			printf("\tcannot create lock file\n");
583 		else {
584 			(void) close(fd);
585 			printf("\tprinter and queuing disabled\n");
586 		}
587 		seteuid(uid);
588 		return;
589 	} else
590 		printf("\tcannot stat lock file\n");
591 	/*
592 	 * Write the message into the status file.
593 	 */
594 	(void) sprintf(line, "%s/%s", SD, ST);
595 	fd = open(line, O_WRONLY|O_CREAT, 0664);
596 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
597 		printf("\tcannot create status file\n");
598 		seteuid(uid);
599 		return;
600 	}
601 	seteuid(uid);
602 	(void) ftruncate(fd, 0);
603 	if (argc <= 0) {
604 		(void) write(fd, "\n", 1);
605 		(void) close(fd);
606 		return;
607 	}
608 	cp1 = buf;
609 	while (--argc >= 0) {
610 		cp2 = *argv++;
611 		while (*cp1++ = *cp2++)
612 			;
613 		cp1[-1] = ' ';
614 	}
615 	cp1[-1] = '\n';
616 	*cp1 = '\0';
617 	(void) write(fd, buf, strlen(buf));
618 	(void) close(fd);
619 }
620 
621 /*
622  * Exit lpc
623  */
624 void
625 quit(argc, argv)
626 	int argc;
627 	char *argv[];
628 {
629 	exit(0);
630 }
631 
632 /*
633  * Kill and restart the daemon.
634  */
635 void
636 restart(argc, argv)
637 	int argc;
638 	char *argv[];
639 {
640 	register int c, status;
641 	register char *cp1, *cp2;
642 	char prbuf[100];
643 
644 	if (argc == 1) {
645 		printf("Usage: restart {all | printer ...}\n");
646 		return;
647 	}
648 	if (argc == 2 && !strcmp(argv[1], "all")) {
649 		printer = prbuf;
650 		while (cgetnext(&bp, printcapdb) > 0) {
651 			cp1 = prbuf;
652 			cp2 = bp;
653 			while ((c = *cp2++) && c != '|' && c != ':')
654 				*cp1++ = c;
655 			*cp1 = '\0';
656 			abortpr(0);
657 			startpr(0);
658 		}
659 		return;
660 	}
661 	while (--argc) {
662 		printer = *++argv;
663 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
664 			printf("cannot open printer description file\n");
665 			continue;
666 		} else if (status == -1) {
667 			printf("unknown printer %s\n", printer);
668 			continue;
669 		} else if (status == -3)
670 			fatal("potential reference loop detected in printcap file");
671 
672 		abortpr(0);
673 		startpr(0);
674 	}
675 }
676 
677 /*
678  * Enable printing on the specified printer and startup the daemon.
679  */
680 void
681 startcmd(argc, argv)
682 	int argc;
683 	char *argv[];
684 {
685 	register int c, status;
686 	register char *cp1, *cp2;
687 	char prbuf[100];
688 
689 	if (argc == 1) {
690 		printf("Usage: start {all | printer ...}\n");
691 		return;
692 	}
693 	if (argc == 2 && !strcmp(argv[1], "all")) {
694 		printer = prbuf;
695 		while (cgetnext(&bp, printcapdb) > 0) {
696 			cp1 = prbuf;
697 			cp2 = bp;
698 			while ((c = *cp2++) && c != '|' && c != ':')
699 				*cp1++ = c;
700 			*cp1 = '\0';
701 			startpr(1);
702 		}
703 		return;
704 	}
705 	while (--argc) {
706 		printer = *++argv;
707 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
708 			printf("cannot open printer description file\n");
709 			continue;
710 		} else if (status == -1) {
711 			printf("unknown printer %s\n", printer);
712 			continue;
713 		} else if (status == -3)
714 			fatal("potential reference loop detected in printcap file");
715 
716 		startpr(1);
717 	}
718 }
719 
720 static void
721 startpr(enable)
722 	int enable;
723 {
724 	struct stat stbuf;
725 
726 	if (cgetstr(bp, "sd", &SD) == -1)
727 		SD = _PATH_DEFSPOOL;
728 	if (cgetstr(bp, "lo", &LO) == -1)
729 		LO = DEFLOCK;
730 	(void) sprintf(line, "%s/%s", SD, LO);
731 	printf("%s:\n", printer);
732 
733 	/*
734 	 * Turn off the owner execute bit of the lock file to enable printing.
735 	 */
736 	seteuid(euid);
737 	if (enable && stat(line, &stbuf) >= 0) {
738 		if (chmod(line, stbuf.st_mode & (enable==2 ? 0666 : 0677)) < 0)
739 			printf("\tcannot enable printing\n");
740 		else
741 			printf("\tprinting enabled\n");
742 	}
743 	if (!startdaemon(printer))
744 		printf("\tcouldn't start daemon\n");
745 	else
746 		printf("\tdaemon started\n");
747 	seteuid(uid);
748 }
749 
750 /*
751  * Print the status of each queue listed or all the queues.
752  */
753 void
754 status(argc, argv)
755 	int argc;
756 	char *argv[];
757 {
758 	register int c, status;
759 	register char *cp1, *cp2;
760 	char prbuf[100];
761 
762 	if (argc == 1) {
763 		printer = prbuf;
764 		while (cgetnext(&bp, printcapdb) > 0) {
765 			cp1 = prbuf;
766 			cp2 = bp;
767 			while ((c = *cp2++) && c != '|' && c != ':')
768 				*cp1++ = c;
769 			*cp1 = '\0';
770 			prstat();
771 		}
772 		return;
773 	}
774 	while (--argc) {
775 		printer = *++argv;
776 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
777 			printf("cannot open printer description file\n");
778 			continue;
779 		} else if (status == -1) {
780 			printf("unknown printer %s\n", printer);
781 			continue;
782 		} else if (status == -3)
783 			fatal("potential reference loop detected in printcap file");
784 
785 		prstat();
786 	}
787 }
788 
789 /*
790  * Print the status of the printer queue.
791  */
792 static void
793 prstat()
794 {
795 	struct stat stbuf;
796 	register int fd, i;
797 	register struct dirent *dp;
798 	DIR *dirp;
799 
800 	if (cgetstr(bp, "sd", &SD) == -1)
801 		SD = _PATH_DEFSPOOL;
802 	if (cgetstr(bp, "lo", &LO) == -1)
803 		LO = DEFLOCK;
804 	if (cgetstr(bp, "st", &ST) == -1)
805 		ST = DEFSTAT;
806 	printf("%s:\n", printer);
807 	(void) sprintf(line, "%s/%s", SD, LO);
808 	if (stat(line, &stbuf) >= 0) {
809 		printf("\tqueuing is %s\n",
810 			(stbuf.st_mode & 010) ? "disabled" : "enabled");
811 		printf("\tprinting is %s\n",
812 			(stbuf.st_mode & 0100) ? "disabled" : "enabled");
813 	} else {
814 		printf("\tqueuing is enabled\n");
815 		printf("\tprinting is enabled\n");
816 	}
817 	if ((dirp = opendir(SD)) == NULL) {
818 		printf("\tcannot examine spool directory\n");
819 		return;
820 	}
821 	i = 0;
822 	while ((dp = readdir(dirp)) != NULL) {
823 		if (*dp->d_name == 'c' && dp->d_name[1] == 'f')
824 			i++;
825 	}
826 	closedir(dirp);
827 	if (i == 0)
828 		printf("\tno entries\n");
829 	else if (i == 1)
830 		printf("\t1 entry in spool area\n");
831 	else
832 		printf("\t%d entries in spool area\n", i);
833 	fd = open(line, O_RDONLY);
834 	if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
835 		(void) close(fd);	/* unlocks as well */
836 		printf("\tprinter idle\n");
837 		return;
838 	}
839 	(void) close(fd);
840 	putchar('\t');
841 	(void) sprintf(line, "%s/%s", SD, ST);
842 	fd = open(line, O_RDONLY);
843 	if (fd >= 0) {
844 		(void) flock(fd, LOCK_SH);
845 		while ((i = read(fd, line, sizeof(line))) > 0)
846 			(void) fwrite(line, 1, i, stdout);
847 		(void) close(fd);	/* unlocks as well */
848 	}
849 }
850 
851 /*
852  * Stop the specified daemon after completing the current job and disable
853  * printing.
854  */
855 void
856 stop(argc, argv)
857 	int argc;
858 	char *argv[];
859 {
860 	register int c, status;
861 	register char *cp1, *cp2;
862 	char prbuf[100];
863 
864 	if (argc == 1) {
865 		printf("Usage: stop {all | printer ...}\n");
866 		return;
867 	}
868 	if (argc == 2 && !strcmp(argv[1], "all")) {
869 		printer = prbuf;
870 		while (cgetnext(&bp, printcapdb) > 0) {
871 			cp1 = prbuf;
872 			cp2 = bp;
873 			while ((c = *cp2++) && c != '|' && c != ':')
874 				*cp1++ = c;
875 			*cp1 = '\0';
876 			stoppr();
877 		}
878 		return;
879 	}
880 	while (--argc) {
881 		printer = *++argv;
882 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
883 			printf("cannot open printer description file\n");
884 			continue;
885 		} else if (status == -1) {
886 			printf("unknown printer %s\n", printer);
887 			continue;
888 		} else if (status == -3)
889 			fatal("potential reference loop detected in printcap file");
890 
891 		stoppr();
892 	}
893 }
894 
895 static void
896 stoppr()
897 {
898 	register int fd;
899 	struct stat stbuf;
900 
901 	if (cgetstr(bp, "sd", &SD) == -1)
902 		SD = _PATH_DEFSPOOL;
903 	if (cgetstr(bp, "lo", &LO) == -1)
904 		LO = DEFLOCK;
905 	(void) sprintf(line, "%s/%s", SD, LO);
906 	printf("%s:\n", printer);
907 
908 	/*
909 	 * Turn on the owner execute bit of the lock file to disable printing.
910 	 */
911 	seteuid(euid);
912 	if (stat(line, &stbuf) >= 0) {
913 		if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
914 			printf("\tcannot disable printing\n");
915 		else {
916 			upstat("printing disabled\n");
917 			printf("\tprinting disabled\n");
918 		}
919 	} else if (errno == ENOENT) {
920 		if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0)
921 			printf("\tcannot create lock file\n");
922 		else {
923 			(void) close(fd);
924 			upstat("printing disabled\n");
925 			printf("\tprinting disabled\n");
926 		}
927 	} else
928 		printf("\tcannot stat lock file\n");
929 	seteuid(uid);
930 }
931 
932 struct	queue **queue;
933 int	nitems;
934 time_t	mtime;
935 
936 /*
937  * Put the specified jobs at the top of printer queue.
938  */
939 void
940 topq(argc, argv)
941 	int argc;
942 	char *argv[];
943 {
944 	register int i;
945 	struct stat stbuf;
946 	int status, changed;
947 
948 	if (argc < 3) {
949 		printf("Usage: topq printer [jobnum ...] [user ...]\n");
950 		return;
951 	}
952 
953 	--argc;
954 	printer = *++argv;
955 	status = cgetent(&bp, printcapdb, printer);
956 	if (status == -2) {
957 		printf("cannot open printer description file\n");
958 		return;
959 	} else if (status == -1) {
960 		printf("%s: unknown printer\n", printer);
961 		return;
962 	} else if (status == -3)
963 		fatal("potential reference loop detected in printcap file");
964 
965 	if (cgetstr(bp, "sd", &SD) == -1)
966 		SD = _PATH_DEFSPOOL;
967 	if (cgetstr(bp, "lo", &LO) == -1)
968 		LO = DEFLOCK;
969 	printf("%s:\n", printer);
970 
971 	seteuid(euid);
972 	if (chdir(SD) < 0) {
973 		printf("\tcannot chdir to %s\n", SD);
974 		goto out;
975 	}
976 	seteuid(uid);
977 	nitems = getq(&queue);
978 	if (nitems == 0)
979 		return;
980 	changed = 0;
981 	mtime = queue[0]->q_time;
982 	for (i = argc; --i; ) {
983 		if (doarg(argv[i]) == 0) {
984 			printf("\tjob %s is not in the queue\n", argv[i]);
985 			continue;
986 		} else
987 			changed++;
988 	}
989 	for (i = 0; i < nitems; i++)
990 		free(queue[i]);
991 	free(queue);
992 	if (!changed) {
993 		printf("\tqueue order unchanged\n");
994 		return;
995 	}
996 	/*
997 	 * Turn on the public execute bit of the lock file to
998 	 * get lpd to rebuild the queue after the current job.
999 	 */
1000 	seteuid(euid);
1001 	if (changed && stat(LO, &stbuf) >= 0)
1002 		(void) chmod(LO, (stbuf.st_mode & 0777) | 01);
1003 
1004 out:
1005 	seteuid(uid);
1006 }
1007 
1008 /*
1009  * Reposition the job by changing the modification time of
1010  * the control file.
1011  */
1012 static int
1013 touch(q)
1014 	struct queue *q;
1015 {
1016 	struct timeval tvp[2];
1017 	int ret;
1018 
1019 	tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
1020 	tvp[0].tv_usec = tvp[1].tv_usec = 0;
1021 	seteuid(euid);
1022 	ret = utimes(q->q_name, tvp);
1023 	seteuid(uid);
1024 	return (ret);
1025 }
1026 
1027 /*
1028  * Checks if specified job name is in the printer's queue.
1029  * Returns:  negative (-1) if argument name is not in the queue.
1030  */
1031 static int
1032 doarg(job)
1033 	char *job;
1034 {
1035 	register struct queue **qq;
1036 	register int jobnum, n;
1037 	register char *cp, *machine;
1038 	int cnt = 0;
1039 	FILE *fp;
1040 
1041 	/*
1042 	 * Look for a job item consisting of system name, colon, number
1043 	 * (example: ucbarpa:114)
1044 	 */
1045 	if ((cp = index(job, ':')) != NULL) {
1046 		machine = job;
1047 		*cp++ = '\0';
1048 		job = cp;
1049 	} else
1050 		machine = NULL;
1051 
1052 	/*
1053 	 * Check for job specified by number (example: 112 or 235ucbarpa).
1054 	 */
1055 	if (isdigit(*job)) {
1056 		jobnum = 0;
1057 		do
1058 			jobnum = jobnum * 10 + (*job++ - '0');
1059 		while (isdigit(*job));
1060 		for (qq = queue + nitems; --qq >= queue; ) {
1061 			n = 0;
1062 			for (cp = (*qq)->q_name+3; isdigit(*cp); )
1063 				n = n * 10 + (*cp++ - '0');
1064 			if (jobnum != n)
1065 				continue;
1066 			if (*job && strcmp(job, cp) != 0)
1067 				continue;
1068 			if (machine != NULL && strcmp(machine, cp) != 0)
1069 				continue;
1070 			if (touch(*qq) == 0) {
1071 				printf("\tmoved %s\n", (*qq)->q_name);
1072 				cnt++;
1073 			}
1074 		}
1075 		return(cnt);
1076 	}
1077 	/*
1078 	 * Process item consisting of owner's name (example: henry).
1079 	 */
1080 	for (qq = queue + nitems; --qq >= queue; ) {
1081 		seteuid(euid);
1082 		fp = fopen((*qq)->q_name, "r");
1083 		seteuid(uid);
1084 		if (fp == NULL)
1085 			continue;
1086 		while (getline(fp) > 0)
1087 			if (line[0] == 'P')
1088 				break;
1089 		(void) fclose(fp);
1090 		if (line[0] != 'P' || strcmp(job, line+1) != 0)
1091 			continue;
1092 		if (touch(*qq) == 0) {
1093 			printf("\tmoved %s\n", (*qq)->q_name);
1094 			cnt++;
1095 		}
1096 	}
1097 	return(cnt);
1098 }
1099 
1100 /*
1101  * Enable everything and start printer (undo `down').
1102  */
1103 void
1104 up(argc, argv)
1105 	int argc;
1106 	char *argv[];
1107 {
1108 	register int c, status;
1109 	register char *cp1, *cp2;
1110 	char prbuf[100];
1111 
1112 	if (argc == 1) {
1113 		printf("Usage: up {all | printer ...}\n");
1114 		return;
1115 	}
1116 	if (argc == 2 && !strcmp(argv[1], "all")) {
1117 		printer = prbuf;
1118 		while (cgetnext(&bp, printcapdb) > 0) {
1119 			cp1 = prbuf;
1120 			cp2 = bp;
1121 			while ((c = *cp2++) && c != '|' && c != ':')
1122 				*cp1++ = c;
1123 			*cp1 = '\0';
1124 			startpr(2);
1125 		}
1126 		return;
1127 	}
1128 	while (--argc) {
1129 		printer = *++argv;
1130 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1131 			printf("cannot open printer description file\n");
1132 			continue;
1133 		} else if (status == -1) {
1134 			printf("unknown printer %s\n", printer);
1135 			continue;
1136 		} else if (status == -3)
1137 			fatal("potential reference loop detected in printcap file");
1138 
1139 		startpr(2);
1140 	}
1141 }
1142