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