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