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