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