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