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