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