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