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