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