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