xref: /freebsd/usr.sbin/lpr/lpc/cmds.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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 				strcpy(lp, cp);
333 				unlinkf(line);
334 			}
335 		} else {
336 			/*
337 			 * Must be a df with no cf (otherwise, it would have
338 			 * been skipped above) or a tf file (which can always
339 			 * be removed).
340 			 */
341 			strcpy(lp, cp);
342 			unlinkf(line);
343 		}
344      	} while (++i < nitems);
345 }
346 
347 static void
348 unlinkf(name)
349 	char	*name;
350 {
351 	if (unlink(name) < 0)
352 		printf("\tcannot remove %s\n", name);
353 	else
354 		printf("\tremoved %s\n", name);
355 }
356 
357 /*
358  * Enable queuing to the printer (allow lpr's).
359  */
360 void
361 enable(argc, argv)
362 	int argc;
363 	char *argv[];
364 {
365 	register int c, status;
366 	register char *cp1, *cp2;
367 	char prbuf[100];
368 
369 	if (argc == 1) {
370 		printf("Usage: enable {all | printer ...}\n");
371 		return;
372 	}
373 	if (argc == 2 && !strcmp(argv[1], "all")) {
374 		printer = prbuf;
375 		while (cgetnext(&bp, printcapdb) > 0) {
376 			cp1 = prbuf;
377 			cp2 = bp;
378 			while ((c = *cp2++) && c != '|' && c != ':')
379 				*cp1++ = c;
380 			*cp1 = '\0';
381 			enablepr();
382 		}
383 		return;
384 	}
385 	while (--argc) {
386 		printer = *++argv;
387 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
388 			printf("cannot open printer description file\n");
389 			continue;
390 		} else if (status == -1) {
391 			printf("unknown printer %s\n", printer);
392 			continue;
393 		} else if (status == -3)
394 			fatal("potential reference loop detected in printcap file");
395 
396 		enablepr();
397 	}
398 }
399 
400 static void
401 enablepr()
402 {
403 	struct stat stbuf;
404 
405 	if (cgetstr(bp, "sd", &SD) == -1)
406 		SD = _PATH_DEFSPOOL;
407 	if (cgetstr(bp, "lo", &LO) == -1)
408 		LO = DEFLOCK;
409 	(void) sprintf(line, "%s/%s", SD, LO);
410 	printf("%s:\n", printer);
411 
412 	/*
413 	 * Turn off the group execute bit of the lock file to enable queuing.
414 	 */
415 	if (stat(line, &stbuf) >= 0) {
416 		if (chmod(line, stbuf.st_mode & 0767) < 0)
417 			printf("\tcannot enable queuing\n");
418 		else
419 			printf("\tqueuing enabled\n");
420 	}
421 }
422 
423 /*
424  * Disable queuing.
425  */
426 void
427 disable(argc, argv)
428 	int argc;
429 	char *argv[];
430 {
431 	register int c, status;
432 	register char *cp1, *cp2;
433 	char prbuf[100];
434 
435 	if (argc == 1) {
436 		printf("Usage: disable {all | printer ...}\n");
437 		return;
438 	}
439 	if (argc == 2 && !strcmp(argv[1], "all")) {
440 		printer = prbuf;
441 		while (cgetnext(&bp, printcapdb) > 0) {
442 			cp1 = prbuf;
443 			cp2 = bp;
444 			while ((c = *cp2++) && c != '|' && c != ':')
445 				*cp1++ = c;
446 			*cp1 = '\0';
447 			disablepr();
448 		}
449 		return;
450 	}
451 	while (--argc) {
452 		printer = *++argv;
453 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
454 			printf("cannot open printer description file\n");
455 			continue;
456 		} else if (status == -1) {
457 			printf("unknown printer %s\n", printer);
458 			continue;
459 		} else if (status == -3)
460 			fatal("potential reference loop detected in printcap file");
461 
462 		disablepr();
463 	}
464 }
465 
466 static void
467 disablepr()
468 {
469 	register int fd;
470 	struct stat stbuf;
471 
472 	if (cgetstr(bp, "sd", &SD) == -1)
473 		SD = _PATH_DEFSPOOL;
474 	if (cgetstr(bp, "lo", &LO) == -1)
475 		LO = DEFLOCK;
476 	(void) sprintf(line, "%s/%s", SD, LO);
477 	printf("%s:\n", printer);
478 	/*
479 	 * Turn on the group execute bit of the lock file to disable queuing.
480 	 */
481 	if (stat(line, &stbuf) >= 0) {
482 		if (chmod(line, (stbuf.st_mode & 0777) | 010) < 0)
483 			printf("\tcannot disable queuing\n");
484 		else
485 			printf("\tqueuing disabled\n");
486 	} else if (errno == ENOENT) {
487 		if ((fd = open(line, O_WRONLY|O_CREAT, 0670)) < 0)
488 			printf("\tcannot create lock file\n");
489 		else {
490 			(void) close(fd);
491 			printf("\tqueuing disabled\n");
492 		}
493 		return;
494 	} else
495 		printf("\tcannot stat lock file\n");
496 }
497 
498 /*
499  * Disable queuing and printing and put a message into the status file
500  * (reason for being down).
501  */
502 void
503 down(argc, argv)
504 	int argc;
505 	char *argv[];
506 {
507 	register int c, status;
508 	register char *cp1, *cp2;
509 	char prbuf[100];
510 
511 	if (argc == 1) {
512 		printf("Usage: down {all | printer} [message ...]\n");
513 		return;
514 	}
515 	if (!strcmp(argv[1], "all")) {
516 		printer = prbuf;
517 		while (cgetnext(&bp, printcapdb) > 0) {
518 			cp1 = prbuf;
519 			cp2 = bp;
520 			while ((c = *cp2++) && c != '|' && c != ':')
521 				*cp1++ = c;
522 			*cp1 = '\0';
523 			putmsg(argc - 2, argv + 2);
524 		}
525 		return;
526 	}
527 	printer = argv[1];
528 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
529 		printf("cannot open printer description file\n");
530 		return;
531 	} else if (status == -1) {
532 		printf("unknown printer %s\n", printer);
533 		return;
534 	} else if (status == -3)
535 			fatal("potential reference loop detected in printcap file");
536 
537 	putmsg(argc - 2, argv + 2);
538 }
539 
540 static void
541 putmsg(argc, argv)
542 	int argc;
543 	char **argv;
544 {
545 	register int fd;
546 	register char *cp1, *cp2;
547 	char buf[1024];
548 	struct stat stbuf;
549 
550 	if (cgetstr(bp, "sd", &SD) == -1)
551 		SD = _PATH_DEFSPOOL;
552 	if (cgetstr(bp, "lo", &LO) == -1)
553 		LO = DEFLOCK;
554 	if (cgetstr(bp, "st", &ST) == -1)
555 		ST = DEFSTAT;
556 	printf("%s:\n", printer);
557 	/*
558 	 * Turn on the group execute bit of the lock file to disable queuing and
559 	 * turn on the owner execute bit of the lock file to disable printing.
560 	 */
561 	(void) sprintf(line, "%s/%s", SD, LO);
562 	if (stat(line, &stbuf) >= 0) {
563 		if (chmod(line, (stbuf.st_mode & 0777) | 0110) < 0)
564 			printf("\tcannot disable queuing\n");
565 		else
566 			printf("\tprinter and queuing disabled\n");
567 	} else if (errno == ENOENT) {
568 		if ((fd = open(line, O_WRONLY|O_CREAT, 0770)) < 0)
569 			printf("\tcannot create lock file\n");
570 		else {
571 			(void) close(fd);
572 			printf("\tprinter and queuing disabled\n");
573 		}
574 		return;
575 	} else
576 		printf("\tcannot stat lock file\n");
577 	/*
578 	 * Write the message into the status file.
579 	 */
580 	(void) sprintf(line, "%s/%s", SD, ST);
581 	fd = open(line, O_WRONLY|O_CREAT, 0664);
582 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
583 		printf("\tcannot create status file\n");
584 		return;
585 	}
586 	(void) ftruncate(fd, 0);
587 	if (argc <= 0) {
588 		(void) write(fd, "\n", 1);
589 		(void) close(fd);
590 		return;
591 	}
592 	cp1 = buf;
593 	while (--argc >= 0) {
594 		cp2 = *argv++;
595 		while (*cp1++ = *cp2++)
596 			;
597 		cp1[-1] = ' ';
598 	}
599 	cp1[-1] = '\n';
600 	*cp1 = '\0';
601 	(void) write(fd, buf, strlen(buf));
602 	(void) close(fd);
603 }
604 
605 /*
606  * Exit lpc
607  */
608 void
609 quit(argc, argv)
610 	int argc;
611 	char *argv[];
612 {
613 	exit(0);
614 }
615 
616 /*
617  * Kill and restart the daemon.
618  */
619 void
620 restart(argc, argv)
621 	int argc;
622 	char *argv[];
623 {
624 	register int c, status;
625 	register char *cp1, *cp2;
626 	char prbuf[100];
627 
628 	if (argc == 1) {
629 		printf("Usage: restart {all | printer ...}\n");
630 		return;
631 	}
632 	if (argc == 2 && !strcmp(argv[1], "all")) {
633 		printer = prbuf;
634 		while (cgetnext(&bp, printcapdb) > 0) {
635 			cp1 = prbuf;
636 			cp2 = bp;
637 			while ((c = *cp2++) && c != '|' && c != ':')
638 				*cp1++ = c;
639 			*cp1 = '\0';
640 			abortpr(0);
641 			startpr(0);
642 		}
643 		return;
644 	}
645 	while (--argc) {
646 		printer = *++argv;
647 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
648 			printf("cannot open printer description file\n");
649 			continue;
650 		} else if (status == -1) {
651 			printf("unknown printer %s\n", printer);
652 			continue;
653 		} else if (status == -3)
654 			fatal("potential reference loop detected in printcap file");
655 
656 		abortpr(0);
657 		startpr(0);
658 	}
659 }
660 
661 /*
662  * Enable printing on the specified printer and startup the daemon.
663  */
664 void
665 startcmd(argc, argv)
666 	int argc;
667 	char *argv[];
668 {
669 	register int c, status;
670 	register char *cp1, *cp2;
671 	char prbuf[100];
672 
673 	if (argc == 1) {
674 		printf("Usage: start {all | printer ...}\n");
675 		return;
676 	}
677 	if (argc == 2 && !strcmp(argv[1], "all")) {
678 		printer = prbuf;
679 		while (cgetnext(&bp, printcapdb) > 0) {
680 			cp1 = prbuf;
681 			cp2 = bp;
682 			while ((c = *cp2++) && c != '|' && c != ':')
683 				*cp1++ = c;
684 			*cp1 = '\0';
685 			startpr(1);
686 		}
687 		return;
688 	}
689 	while (--argc) {
690 		printer = *++argv;
691 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
692 			printf("cannot open printer description file\n");
693 			continue;
694 		} else if (status == -1) {
695 			printf("unknown printer %s\n", printer);
696 			continue;
697 		} else if (status == -3)
698 			fatal("potential reference loop detected in printcap file");
699 
700 		startpr(1);
701 	}
702 }
703 
704 static void
705 startpr(enable)
706 	int enable;
707 {
708 	struct stat stbuf;
709 
710 	if (cgetstr(bp, "sd", &SD) == -1)
711 		SD = _PATH_DEFSPOOL;
712 	if (cgetstr(bp, "lo", &LO) == -1)
713 		LO = DEFLOCK;
714 	(void) sprintf(line, "%s/%s", SD, LO);
715 	printf("%s:\n", printer);
716 
717 	/*
718 	 * Turn off the owner execute bit of the lock file to enable printing.
719 	 */
720 	if (enable && stat(line, &stbuf) >= 0) {
721 		if (chmod(line, stbuf.st_mode & (enable==2 ? 0666 : 0677)) < 0)
722 			printf("\tcannot enable printing\n");
723 		else
724 			printf("\tprinting enabled\n");
725 	}
726 	if (!startdaemon(printer))
727 		printf("\tcouldn't start daemon\n");
728 	else
729 		printf("\tdaemon started\n");
730 }
731 
732 /*
733  * Print the status of each queue listed or all the queues.
734  */
735 void
736 status(argc, argv)
737 	int argc;
738 	char *argv[];
739 {
740 	register int c, status;
741 	register char *cp1, *cp2;
742 	char prbuf[100];
743 
744 	if (argc == 1) {
745 		printer = prbuf;
746 		while (cgetnext(&bp, printcapdb) > 0) {
747 			cp1 = prbuf;
748 			cp2 = bp;
749 			while ((c = *cp2++) && c != '|' && c != ':')
750 				*cp1++ = c;
751 			*cp1 = '\0';
752 			prstat();
753 		}
754 		return;
755 	}
756 	while (--argc) {
757 		printer = *++argv;
758 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
759 			printf("cannot open printer description file\n");
760 			continue;
761 		} else if (status == -1) {
762 			printf("unknown printer %s\n", printer);
763 			continue;
764 		} else if (status == -3)
765 			fatal("potential reference loop detected in printcap file");
766 
767 		prstat();
768 	}
769 }
770 
771 /*
772  * Print the status of the printer queue.
773  */
774 static void
775 prstat()
776 {
777 	struct stat stbuf;
778 	register int fd, i;
779 	register struct dirent *dp;
780 	DIR *dirp;
781 
782 	if (cgetstr(bp, "sd", &SD) == -1)
783 		SD = _PATH_DEFSPOOL;
784 	if (cgetstr(bp, "lo", &LO) == -1)
785 		LO = DEFLOCK;
786 	if (cgetstr(bp, "st", &ST) == -1)
787 		ST = DEFSTAT;
788 	printf("%s:\n", printer);
789 	(void) sprintf(line, "%s/%s", SD, LO);
790 	if (stat(line, &stbuf) >= 0) {
791 		printf("\tqueuing is %s\n",
792 			(stbuf.st_mode & 010) ? "disabled" : "enabled");
793 		printf("\tprinting is %s\n",
794 			(stbuf.st_mode & 0100) ? "disabled" : "enabled");
795 	} else {
796 		printf("\tqueuing is enabled\n");
797 		printf("\tprinting is enabled\n");
798 	}
799 	if ((dirp = opendir(SD)) == NULL) {
800 		printf("\tcannot examine spool directory\n");
801 		return;
802 	}
803 	i = 0;
804 	while ((dp = readdir(dirp)) != NULL) {
805 		if (*dp->d_name == 'c' && dp->d_name[1] == 'f')
806 			i++;
807 	}
808 	closedir(dirp);
809 	if (i == 0)
810 		printf("\tno entries\n");
811 	else if (i == 1)
812 		printf("\t1 entry in spool area\n");
813 	else
814 		printf("\t%d entries in spool area\n", i);
815 	fd = open(line, O_RDONLY);
816 	if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
817 		(void) close(fd);	/* unlocks as well */
818 		printf("\tno daemon present\n");
819 		return;
820 	}
821 	(void) close(fd);
822 	putchar('\t');
823 	(void) sprintf(line, "%s/%s", SD, ST);
824 	fd = open(line, O_RDONLY);
825 	if (fd >= 0) {
826 		(void) flock(fd, LOCK_SH);
827 		while ((i = read(fd, line, sizeof(line))) > 0)
828 			(void) fwrite(line, 1, i, stdout);
829 		(void) close(fd);	/* unlocks as well */
830 	}
831 }
832 
833 /*
834  * Stop the specified daemon after completing the current job and disable
835  * printing.
836  */
837 void
838 stop(argc, argv)
839 	int argc;
840 	char *argv[];
841 {
842 	register int c, status;
843 	register char *cp1, *cp2;
844 	char prbuf[100];
845 
846 	if (argc == 1) {
847 		printf("Usage: stop {all | printer ...}\n");
848 		return;
849 	}
850 	if (argc == 2 && !strcmp(argv[1], "all")) {
851 		printer = prbuf;
852 		while (cgetnext(&bp, printcapdb) > 0) {
853 			cp1 = prbuf;
854 			cp2 = bp;
855 			while ((c = *cp2++) && c != '|' && c != ':')
856 				*cp1++ = c;
857 			*cp1 = '\0';
858 			stoppr();
859 		}
860 		return;
861 	}
862 	while (--argc) {
863 		printer = *++argv;
864 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
865 			printf("cannot open printer description file\n");
866 			continue;
867 		} else if (status == -1) {
868 			printf("unknown printer %s\n", printer);
869 			continue;
870 		} else if (status == -3)
871 			fatal("potential reference loop detected in printcap file");
872 
873 		stoppr();
874 	}
875 }
876 
877 static void
878 stoppr()
879 {
880 	register int fd;
881 	struct stat stbuf;
882 
883 	if (cgetstr(bp, "sd", &SD) == -1)
884 		SD = _PATH_DEFSPOOL;
885 	if (cgetstr(bp, "lo", &LO) == -1)
886 		LO = DEFLOCK;
887 	(void) sprintf(line, "%s/%s", SD, LO);
888 	printf("%s:\n", printer);
889 
890 	/*
891 	 * Turn on the owner execute bit of the lock file to disable printing.
892 	 */
893 	if (stat(line, &stbuf) >= 0) {
894 		if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
895 			printf("\tcannot disable printing\n");
896 		else {
897 			upstat("printing disabled\n");
898 			printf("\tprinting disabled\n");
899 		}
900 	} else if (errno == ENOENT) {
901 		if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0)
902 			printf("\tcannot create lock file\n");
903 		else {
904 			(void) close(fd);
905 			upstat("printing disabled\n");
906 			printf("\tprinting disabled\n");
907 		}
908 	} else
909 		printf("\tcannot stat lock file\n");
910 }
911 
912 struct	queue **queue;
913 int	nitems;
914 time_t	mtime;
915 
916 /*
917  * Put the specified jobs at the top of printer queue.
918  */
919 void
920 topq(argc, argv)
921 	int argc;
922 	char *argv[];
923 {
924 	register int i;
925 	struct stat stbuf;
926 	int status, changed;
927 
928 	if (argc < 3) {
929 		printf("Usage: topq printer [jobnum ...] [user ...]\n");
930 		return;
931 	}
932 
933 	--argc;
934 	printer = *++argv;
935 	status = cgetent(&bp, printcapdb, printer);
936 	if (status == -2) {
937 		printf("cannot open printer description file\n");
938 		return;
939 	} else if (status == -1) {
940 		printf("%s: unknown printer\n", printer);
941 		return;
942 	} else if (status == -3)
943 		fatal("potential reference loop detected in printcap file");
944 
945 	if (cgetstr(bp, "sd", &SD) == -1)
946 		SD = _PATH_DEFSPOOL;
947 	if (cgetstr(bp, "lo", &LO) == -1)
948 		LO = DEFLOCK;
949 	printf("%s:\n", printer);
950 
951 	if (chdir(SD) < 0) {
952 		printf("\tcannot chdir to %s\n", SD);
953 		return;
954 	}
955 	nitems = getq(&queue);
956 	if (nitems == 0)
957 		return;
958 	changed = 0;
959 	mtime = queue[0]->q_time;
960 	for (i = argc; --i; ) {
961 		if (doarg(argv[i]) == 0) {
962 			printf("\tjob %s is not in the queue\n", argv[i]);
963 			continue;
964 		} else
965 			changed++;
966 	}
967 	for (i = 0; i < nitems; i++)
968 		free(queue[i]);
969 	free(queue);
970 	if (!changed) {
971 		printf("\tqueue order unchanged\n");
972 		return;
973 	}
974 	/*
975 	 * Turn on the public execute bit of the lock file to
976 	 * get lpd to rebuild the queue after the current job.
977 	 */
978 	if (changed && stat(LO, &stbuf) >= 0)
979 		(void) chmod(LO, (stbuf.st_mode & 0777) | 01);
980 }
981 
982 /*
983  * Reposition the job by changing the modification time of
984  * the control file.
985  */
986 static int
987 touch(q)
988 	struct queue *q;
989 {
990 	struct timeval tvp[2];
991 
992 	tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
993 	tvp[0].tv_usec = tvp[1].tv_usec = 0;
994 	return(utimes(q->q_name, tvp));
995 }
996 
997 /*
998  * Checks if specified job name is in the printer's queue.
999  * Returns:  negative (-1) if argument name is not in the queue.
1000  */
1001 static int
1002 doarg(job)
1003 	char *job;
1004 {
1005 	register struct queue **qq;
1006 	register int jobnum, n;
1007 	register char *cp, *machine;
1008 	int cnt = 0;
1009 	FILE *fp;
1010 
1011 	/*
1012 	 * Look for a job item consisting of system name, colon, number
1013 	 * (example: ucbarpa:114)
1014 	 */
1015 	if ((cp = index(job, ':')) != NULL) {
1016 		machine = job;
1017 		*cp++ = '\0';
1018 		job = cp;
1019 	} else
1020 		machine = NULL;
1021 
1022 	/*
1023 	 * Check for job specified by number (example: 112 or 235ucbarpa).
1024 	 */
1025 	if (isdigit(*job)) {
1026 		jobnum = 0;
1027 		do
1028 			jobnum = jobnum * 10 + (*job++ - '0');
1029 		while (isdigit(*job));
1030 		for (qq = queue + nitems; --qq >= queue; ) {
1031 			n = 0;
1032 			for (cp = (*qq)->q_name+3; isdigit(*cp); )
1033 				n = n * 10 + (*cp++ - '0');
1034 			if (jobnum != n)
1035 				continue;
1036 			if (*job && strcmp(job, cp) != 0)
1037 				continue;
1038 			if (machine != NULL && strcmp(machine, cp) != 0)
1039 				continue;
1040 			if (touch(*qq) == 0) {
1041 				printf("\tmoved %s\n", (*qq)->q_name);
1042 				cnt++;
1043 			}
1044 		}
1045 		return(cnt);
1046 	}
1047 	/*
1048 	 * Process item consisting of owner's name (example: henry).
1049 	 */
1050 	for (qq = queue + nitems; --qq >= queue; ) {
1051 		if ((fp = fopen((*qq)->q_name, "r")) == NULL)
1052 			continue;
1053 		while (getline(fp) > 0)
1054 			if (line[0] == 'P')
1055 				break;
1056 		(void) fclose(fp);
1057 		if (line[0] != 'P' || strcmp(job, line+1) != 0)
1058 			continue;
1059 		if (touch(*qq) == 0) {
1060 			printf("\tmoved %s\n", (*qq)->q_name);
1061 			cnt++;
1062 		}
1063 	}
1064 	return(cnt);
1065 }
1066 
1067 /*
1068  * Enable everything and start printer (undo `down').
1069  */
1070 void
1071 up(argc, argv)
1072 	int argc;
1073 	char *argv[];
1074 {
1075 	register int c, status;
1076 	register char *cp1, *cp2;
1077 	char prbuf[100];
1078 
1079 	if (argc == 1) {
1080 		printf("Usage: up {all | printer ...}\n");
1081 		return;
1082 	}
1083 	if (argc == 2 && !strcmp(argv[1], "all")) {
1084 		printer = prbuf;
1085 		while (cgetnext(&bp, printcapdb) > 0) {
1086 			cp1 = prbuf;
1087 			cp2 = bp;
1088 			while ((c = *cp2++) && c != '|' && c != ':')
1089 				*cp1++ = c;
1090 			*cp1 = '\0';
1091 			startpr(2);
1092 		}
1093 		return;
1094 	}
1095 	while (--argc) {
1096 		printer = *++argv;
1097 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1098 			printf("cannot open printer description file\n");
1099 			continue;
1100 		} else if (status == -1) {
1101 			printf("unknown printer %s\n", printer);
1102 			continue;
1103 		} else if (status == -3)
1104 			fatal("potential reference loop detected in printcap file");
1105 
1106 		startpr(2);
1107 	}
1108 }
1109