cmds.c revision 27748
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
36static 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
42static 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
69extern uid_t	uid, euid;
70
71static void	abortpr __P((int));
72static void	cleanpr __P((void));
73static void	disablepr __P((void));
74static int	doarg __P((char *));
75static int	doselect __P((struct dirent *));
76static void	enablepr __P((void));
77static void	prstat __P((void));
78static void	putmsg __P((int, char **));
79static int	sortq __P((const void *, const void *));
80static void	startpr __P((int));
81static void	stoppr __P((void));
82static int	touch __P((struct queue *));
83static void	unlinkf __P((char *));
84static void	upstat __P((char *));
85
86/*
87 * kill an existing daemon and disable printing.
88 */
89void
90doabort(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
129static void
130abortpr(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);
191out:
192	seteuid(uid);
193}
194
195/*
196 * Write a message into the status file.
197 */
198static void
199upstat(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 */
225void
226clean(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
266static int
267doselect(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 */
281static int
282sortq(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 */
306static void
307cleanpr()
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
361static void
362unlinkf(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 */
376void
377enable(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
417static void
418enablepr()
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 */
445void
446disable(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
486static void
487disablepr()
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 */
523void
524down(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
562static void
563putmsg(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 */
634void
635quit(argc, argv)
636	int argc;
637	char *argv[];
638{
639	exit(0);
640}
641
642/*
643 * Kill and restart the daemon.
644 */
645void
646restart(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 */
691void
692startcmd(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
732static void
733startpr(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 */
765void
766status(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) {
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 */
805static void
806prstat()
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 */
872void
873stop(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
913static void
914stoppr()
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
950struct	queue **queue;
951int	nitems;
952time_t	mtime;
953
954/*
955 * Put the specified jobs at the top of printer queue.
956 */
957void
958topq(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
1022out:
1023	seteuid(uid);
1024}
1025
1026/*
1027 * Reposition the job by changing the modification time of
1028 * the control file.
1029 */
1030static int
1031touch(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 */
1049static int
1050doarg(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 */
1121void
1122up(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