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