xref: /openbsd/usr.sbin/lpr/lpc/cmds.c (revision f4147939)
1 /*	$OpenBSD: cmds.c,v 1.28 2018/04/26 12:42:51 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/time.h>
39 #include <sys/stat.h>
40 
41 #include <signal.h>
42 #include <fcntl.h>
43 #include <errno.h>
44 #include <dirent.h>
45 #include <unistd.h>
46 #include <limits.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include "lp.h"
52 #include "lp.local.h"
53 #include "lpc.h"
54 #include "extern.h"
55 #include "pathnames.h"
56 
57 static void	abortpr(int);
58 static void	cleanpr(void);
59 static void	disablepr(void);
60 static int	doarg(char *);
61 static int	doselect(const struct dirent *);
62 static void	enablepr(void);
63 static void	prstat(void);
64 static void	putmsg(int, char **);
65 static int	sortq(const struct dirent **, const struct dirent **);
66 static void	startpr(int);
67 static void	stoppr(void);
68 static int	touch(struct queue *);
69 static void	unlinkf(char *);
70 static void	upstat(char *);
71 
72 /*
73  * kill an existing daemon and disable printing.
74  */
75 void
doabort(int argc,char ** argv)76 doabort(int argc, char **argv)
77 {
78 	int c, status;
79 	char *cp1, *cp2;
80 	char prbuf[100];
81 
82 	if (argc == 1) {
83 		printf("usage: abort {all | printer ...}\n");
84 		return;
85 	}
86 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
87 		printer = prbuf;
88 		while (cgetnext(&bp, printcapdb) > 0) {
89 			cp1 = prbuf;
90 			cp2 = bp;
91 			while ((c = *cp2++) && c != '|' && c != ':' &&
92 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
93 				*cp1++ = c;
94 			*cp1 = '\0';
95 			abortpr(1);
96 		}
97 		return;
98 	}
99 	while (--argc) {
100 		printer = *++argv;
101 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
102 			printf("cannot open printer description file\n");
103 			continue;
104 		} else if (status == -1) {
105 			printf("unknown printer %s\n", printer);
106 			continue;
107 		} else if (status == -3)
108 			fatal("potential reference loop detected in printcap file");
109 		abortpr(1);
110 	}
111 }
112 
113 static void
abortpr(int dis)114 abortpr(int dis)
115 {
116 	FILE *fp;
117 	struct stat stbuf;
118 	int pid, fd;
119 
120 	if (cgetstr(bp, "sd", &SD) == -1)
121 		SD = _PATH_DEFSPOOL;
122 	if (cgetstr(bp, "lo", &LO) == -1)
123 		LO = DEFLOCK;
124 	(void)snprintf(line, sizeof(line), "%s/%s", SD, LO);
125 	printf("%s:\n", printer);
126 
127 	PRIV_START;
128 	/*
129 	 * Turn on the owner execute bit of the lock file to disable printing.
130 	 */
131 	if (dis) {
132 		if (stat(line, &stbuf) >= 0) {
133 			stbuf.st_mode |= S_IXUSR;
134 			if (chmod(line, stbuf.st_mode & 0777) < 0)
135 				printf("\tcannot disable printing\n");
136 			else {
137 				upstat("printing disabled\n");
138 				printf("\tprinting disabled\n");
139 			}
140 		} else if (errno == ENOENT) {
141 			if ((fd = safe_open(line, O_WRONLY|O_CREAT|O_NOFOLLOW,
142 			    0760)) < 0)
143 				printf("\tcannot create lock file\n");
144 			else {
145 				(void)fchown(fd, DEFUID, -1);
146 				(void)close(fd);
147 				upstat("printing disabled\n");
148 				printf("\tprinting disabled\n");
149 				printf("\tno daemon to abort\n");
150 			}
151 			goto out;
152 		} else {
153 			printf("\tcannot stat lock file\n");
154 			goto out;
155 		}
156 	}
157 	/*
158 	 * Kill the current daemon to stop printing now.
159 	 */
160 	fd = safe_open(line, O_RDONLY|O_NOFOLLOW, 0);
161 	if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) {
162 		if (fd >= 0)
163 			close(fd);
164 		printf("\tcannot open lock file\n");
165 		goto out;
166 	}
167 	if (!get_line(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) {
168 		(void)fclose(fp);	/* unlocks as well */
169 		printf("\tno daemon to abort\n");
170 		goto out;
171 	}
172 	(void)fclose(fp);
173 	if (kill(pid = atoi(line), SIGTERM) < 0) {
174 		if (errno == ESRCH)
175 			printf("\tno daemon to abort\n");
176 		else
177 			printf("\tWarning: daemon (pid %d) not killed\n", pid);
178 	} else
179 		printf("\tdaemon (pid %d) killed\n", pid);
180 out:
181 	PRIV_END;
182 }
183 
184 /*
185  * Write a message into the status file (assumes PRIV_START already called)
186  */
187 static void
upstat(char * msg)188 upstat(char *msg)
189 {
190 	int fd;
191 	char statfile[PATH_MAX];
192 
193 	if (cgetstr(bp, "st", &ST) == -1)
194 		ST = DEFSTAT;
195 	(void)snprintf(statfile, sizeof(statfile), "%s/%s", SD, ST);
196 	fd = safe_open(statfile, O_WRONLY|O_CREAT|O_NOFOLLOW, 0660);
197 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
198 		printf("\tcannot create status file\n");
199 		if (fd >= 0)
200 			(void)close(fd);	/* unlocks as well */
201 		return;
202 	}
203 	(void)fchown(fd, DEFUID, -1);
204 	(void)ftruncate(fd, 0);
205 	if (msg == (char *)NULL)
206 		(void)write(fd, "\n", 1);
207 	else
208 		(void)write(fd, msg, strlen(msg));
209 	(void)close(fd);
210 }
211 
212 /*
213  * Remove all spool files and temporaries from the spooling area.
214  */
215 void
clean(int argc,char ** argv)216 clean(int argc, char **argv)
217 {
218 	int c, status;
219 	char *cp1, *cp2;
220 	char prbuf[100];
221 
222 	if (argc == 1) {
223 		printf("usage: clean {all | printer ...}\n");
224 		return;
225 	}
226 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
227 		printer = prbuf;
228 		while (cgetnext(&bp, printcapdb) > 0) {
229 			cp1 = prbuf;
230 			cp2 = bp;
231 			while ((c = *cp2++) && c != '|' && c != ':' &&
232 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
233 				*cp1++ = c;
234 			*cp1 = '\0';
235 			cleanpr();
236 		}
237 		return;
238 	}
239 	while (--argc) {
240 		printer = *++argv;
241 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
242 			printf("cannot open printer description file\n");
243 			continue;
244 		} else if (status == -1) {
245 			printf("unknown printer %s\n", printer);
246 			continue;
247 		} else if (status == -3)
248 			fatal("potential reference loop detected in printcap file");
249 
250 		cleanpr();
251 	}
252 }
253 
254 static int
doselect(const struct dirent * d)255 doselect(const struct dirent *d)
256 {
257 	int c = d->d_name[0];
258 
259 	if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
260 		return(1);
261 	return(0);
262 }
263 
264 /*
265  * Comparison routine for scandir. Sort by job number and machine, then
266  * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z.
267  */
268 static int
sortq(const struct dirent ** d1,const struct dirent ** d2)269 sortq(const struct dirent **d1, const struct dirent **d2)
270 {
271 	int c1, c2;
272 
273 	if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)) != 0)
274 		return(c1);
275 	c1 = (*d1)->d_name[0];
276 	c2 = (*d2)->d_name[0];
277 	if (c1 == c2)
278 		return((*d1)->d_name[2] - (*d2)->d_name[2]);
279 	if (c1 == 'c')
280 		return(-1);
281 	if (c1 == 'd' || c2 == 'c')
282 		return(1);
283 	return(-1);
284 }
285 
286 /*
287  * Remove incomplete jobs from spooling area.
288  */
289 static void
cleanpr(void)290 cleanpr(void)
291 {
292 	int i, n;
293 	char *cp, *cp1, *lp;
294 	struct dirent **queue;
295 	int nitems;
296 
297 	if (cgetstr(bp, "sd", &SD) == -1)
298 		SD = _PATH_DEFSPOOL;
299 	printf("%s:\n", printer);
300 
301 	/* XXX depends on SD being non-NUL */
302 	for (lp = line, cp = SD; (lp - line) < sizeof(line) &&
303 	    (*lp++ = *cp++) != '\0'; )
304 		;
305 	lp[-1] = '/';
306 	if (lp - line >= sizeof(line)) {
307 		printf("\tspool directory name too long\n");
308 		return;
309 	}
310 
311 	PRIV_START;
312 	nitems = scandir(SD, &queue, doselect, sortq);
313 	PRIV_END;
314 	if (nitems < 0) {
315 		printf("\tcannot examine spool directory\n");
316 		return;
317 	}
318 	if (nitems == 0)
319 		return;
320 	i = 0;
321 	do {
322 		cp = queue[i]->d_name;
323 		if (*cp == 'c') {
324 			n = 0;
325 			while (i + 1 < nitems) {
326 				cp1 = queue[i + 1]->d_name;
327 				if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
328 					break;
329 				i++;
330 				n++;
331 			}
332 			if (n == 0) {
333 				if (strlcpy(lp, cp, sizeof(line) - (lp - line))
334 				    >= sizeof(line) - (lp - line))
335 					printf("\tpath too long, %s/%s", SD, cp);
336 				else
337 					unlinkf(line);
338 			}
339 		} else {
340 			/*
341 			 * Must be a df with no cf (otherwise, it would have
342 			 * been skipped above) or a tf file (which can always
343 			 * be removed).
344 			 */
345 			if (strlcpy(lp, cp, sizeof(line) - (lp - line)) >=
346 			    sizeof(line) - (lp - line))
347 				printf("\tpath too long, %s/%s", SD, cp);
348 			else
349 				unlinkf(line);
350 		}
351      	} while (++i < nitems);
352 }
353 
354 static void
unlinkf(char * name)355 unlinkf(char *name)
356 {
357 	PRIV_START;
358 	if (unlink(name) < 0)
359 		printf("\tcannot remove %s\n", name);
360 	else
361 		printf("\tremoved %s\n", name);
362 	PRIV_END;
363 }
364 
365 /*
366  * Enable queuing to the printer (allow lpr's).
367  */
368 void
enable(int argc,char ** argv)369 enable(int argc, char **argv)
370 {
371 	int c, status;
372 	char *cp1, *cp2;
373 	char prbuf[100];
374 
375 	if (argc == 1) {
376 		printf("usage: enable {all | printer ...}\n");
377 		return;
378 	}
379 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
380 		printer = prbuf;
381 		while (cgetnext(&bp, printcapdb) > 0) {
382 			cp1 = prbuf;
383 			cp2 = bp;
384 			while ((c = *cp2++) && c != '|' && c != ':' &&
385 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
386 				*cp1++ = c;
387 			*cp1 = '\0';
388 			enablepr();
389 		}
390 		return;
391 	}
392 	while (--argc) {
393 		printer = *++argv;
394 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
395 			printf("cannot open printer description file\n");
396 			continue;
397 		} else if (status == -1) {
398 			printf("unknown printer %s\n", printer);
399 			continue;
400 		} else if (status == -3)
401 			fatal("potential reference loop detected in printcap file");
402 
403 		enablepr();
404 	}
405 }
406 
407 static void
enablepr(void)408 enablepr(void)
409 {
410 	struct stat stbuf;
411 
412 	if (cgetstr(bp, "sd", &SD) == -1)
413 		SD = _PATH_DEFSPOOL;
414 	if (cgetstr(bp, "lo", &LO) == -1)
415 		LO = DEFLOCK;
416 	(void)snprintf(line, sizeof(line), "%s/%s", SD, LO);
417 	printf("%s:\n", printer);
418 
419 	/*
420 	 * Turn off the group execute bit of the lock file to enable queuing.
421 	 */
422 	PRIV_START;
423 	if (stat(line, &stbuf) >= 0) {
424 		stbuf.st_mode &= ~S_IXGRP;
425 		if (chmod(line, stbuf.st_mode & 0777) < 0)
426 			printf("\tcannot enable queuing\n");
427 		else
428 			printf("\tqueuing enabled\n");
429 	}
430 	PRIV_END;
431 }
432 
433 /*
434  * Disable queuing.
435  */
436 void
disable(int argc,char ** argv)437 disable(int argc, char **argv)
438 {
439 	int c, status;
440 	char *cp1, *cp2;
441 	char prbuf[100];
442 
443 	if (argc == 1) {
444 		printf("usage: disable {all | printer ...}\n");
445 		return;
446 	}
447 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
448 		printer = prbuf;
449 		while (cgetnext(&bp, printcapdb) > 0) {
450 			cp1 = prbuf;
451 			cp2 = bp;
452 			while ((c = *cp2++) && c != '|' && c != ':' &&
453 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
454 				*cp1++ = c;
455 			*cp1 = '\0';
456 			disablepr();
457 		}
458 		return;
459 	}
460 	while (--argc) {
461 		printer = *++argv;
462 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
463 			printf("cannot open printer description file\n");
464 			continue;
465 		} else if (status == -1) {
466 			printf("unknown printer %s\n", printer);
467 			continue;
468 		} else if (status == -3)
469 			fatal("potential reference loop detected in printcap file");
470 
471 		disablepr();
472 	}
473 }
474 
475 static void
disablepr(void)476 disablepr(void)
477 {
478 	int fd;
479 	struct stat stbuf;
480 
481 	if (cgetstr(bp, "sd", &SD) == -1)
482 		SD = _PATH_DEFSPOOL;
483 	if (cgetstr(bp, "lo", &LO) == -1)
484 		LO = DEFLOCK;
485 	(void)snprintf(line, sizeof(line), "%s/%s", SD, LO);
486 	printf("%s:\n", printer);
487 	/*
488 	 * Turn on the group execute bit of the lock file to disable queuing.
489 	 */
490 	PRIV_START;
491 	if (stat(line, &stbuf) >= 0) {
492 		stbuf.st_mode |= S_IXGRP;
493 		if (chmod(line, stbuf.st_mode & 0777) < 0)
494 			printf("\tcannot disable queuing\n");
495 		else
496 			printf("\tqueuing disabled\n");
497 	} else if (errno == ENOENT) {
498 		if ((fd = safe_open(line, O_WRONLY|O_CREAT|O_NOFOLLOW, 0670)) < 0)
499 			printf("\tcannot create lock file\n");
500 		else {
501 			(void)fchown(fd, DEFUID, -1);
502 			(void)close(fd);
503 			printf("\tqueuing disabled\n");
504 		}
505 	} else
506 		printf("\tcannot stat lock file\n");
507 	PRIV_END;
508 }
509 
510 /*
511  * Disable queuing and printing and put a message into the status file
512  * (reason for being down).
513  */
514 void
down(int argc,char ** argv)515 down(int argc, char **argv)
516 {
517 	int c, status;
518 	char *cp1, *cp2;
519 	char prbuf[100];
520 
521 	if (argc == 1) {
522 		printf("usage: down {all | printer} [message ...]\n");
523 		return;
524 	}
525 	if (strcmp(argv[1], "all") == 0) {
526 		printer = prbuf;
527 		while (cgetnext(&bp, printcapdb) > 0) {
528 			cp1 = prbuf;
529 			cp2 = bp;
530 			while ((c = *cp2++) && c != '|' && c != ':' &&
531 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
532 				*cp1++ = c;
533 			*cp1 = '\0';
534 			putmsg(argc - 2, argv + 2);
535 		}
536 		return;
537 	}
538 	printer = argv[1];
539 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
540 		printf("cannot open printer description file\n");
541 		return;
542 	} else if (status == -1) {
543 		printf("unknown printer %s\n", printer);
544 		return;
545 	} else if (status == -3)
546 		fatal("potential reference loop detected in printcap file");
547 
548 	putmsg(argc - 2, argv + 2);
549 }
550 
551 static void
putmsg(int argc,char ** argv)552 putmsg(int argc, char **argv)
553 {
554 	int fd;
555 	char *cp1, *cp2;
556 	char buf[1024];
557 	struct stat stbuf;
558 
559 	if (cgetstr(bp, "sd", &SD) == -1)
560 		SD = _PATH_DEFSPOOL;
561 	if (cgetstr(bp, "lo", &LO) == -1)
562 		LO = DEFLOCK;
563 	if (cgetstr(bp, "st", &ST) == -1)
564 		ST = DEFSTAT;
565 	printf("%s:\n", printer);
566 	/*
567 	 * Turn on the group execute bit of the lock file to disable queuing and
568 	 * turn on the owner execute bit of the lock file to disable printing.
569 	 */
570 	(void)snprintf(line, sizeof(line), "%s/%s", SD, LO);
571 	PRIV_START;
572 	if (stat(line, &stbuf) >= 0) {
573 		stbuf.st_mode |= (S_IXGRP|S_IXUSR);
574 		if (chmod(line, stbuf.st_mode & 0777) < 0)
575 			printf("\tcannot disable queuing\n");
576 		else
577 			printf("\tprinter and queuing disabled\n");
578 	} else if (errno == ENOENT) {
579 		if ((fd = safe_open(line, O_WRONLY|O_CREAT|O_NOFOLLOW, 0770)) < 0)
580 			printf("\tcannot create lock file\n");
581 		else {
582 			(void)fchown(fd, DEFUID, -1);
583 			(void)close(fd);
584 			printf("\tprinter and queuing disabled\n");
585 		}
586 		PRIV_END;
587 		return;
588 	} else
589 		printf("\tcannot stat lock file\n");
590 	/*
591 	 * Write the message into the status file.
592 	 */
593 	(void)snprintf(line, sizeof(line), "%s/%s", SD, ST);
594 	fd = safe_open(line, O_WRONLY|O_CREAT|O_NOFOLLOW, 0660);
595 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
596 		printf("\tcannot create status file\n");
597 		if (fd >= 0)
598 			(void)close(fd);	/* unlocks as well */
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
quit(int argc,char ** argv)627 quit(int argc, char **argv)
628 {
629 	exit(0);
630 }
631 
632 /*
633  * Kill and restart the daemon.
634  */
635 void
restart(int argc,char ** argv)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
startcmd(int argc,char ** argv)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
startpr(int enable)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
status(int argc,char ** argv)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
prstat(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 		printf("\tprinter idle\n");
845 		if (fd >= 0)
846 			(void)close(fd);	/* unlocks as well */
847 		return;
848 	}
849 	(void)close(fd);
850 	(void)snprintf(line, sizeof(line), "%s/%s", SD, ST);
851 	PRIV_START;
852 	fd = safe_open(line, O_RDONLY|O_NOFOLLOW, 0);
853 	PRIV_END;
854 	if (fd >= 0) {
855 		(void)flock(fd, LOCK_SH);
856 		if (fstat(fd, &stbuf) == 0 && stbuf.st_size > 0) {
857 			putchar('\t');
858 			while ((i = read(fd, line, sizeof(line))) > 0)
859 				(void)fwrite(line, 1, i, stdout);
860 		}
861 		(void)close(fd);	/* unlocks as well */
862 	}
863 }
864 
865 /*
866  * Stop the specified daemon after completing the current job and disable
867  * printing.
868  */
869 void
stop(int argc,char ** argv)870 stop(int argc, char **argv)
871 {
872 	int c, status;
873 	char *cp1, *cp2;
874 	char prbuf[100];
875 
876 	if (argc == 1) {
877 		printf("usage: stop {all | printer ...}\n");
878 		return;
879 	}
880 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
881 		printer = prbuf;
882 		while (cgetnext(&bp, printcapdb) > 0) {
883 			cp1 = prbuf;
884 			cp2 = bp;
885 			while ((c = *cp2++) && c != '|' && c != ':' &&
886 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
887 				*cp1++ = c;
888 			*cp1 = '\0';
889 			stoppr();
890 		}
891 		return;
892 	}
893 	while (--argc) {
894 		printer = *++argv;
895 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
896 			printf("cannot open printer description file\n");
897 			continue;
898 		} else if (status == -1) {
899 			printf("unknown printer %s\n", printer);
900 			continue;
901 		} else if (status == -3)
902 			fatal("potential reference loop detected in printcap file");
903 
904 		stoppr();
905 	}
906 }
907 
908 static void
stoppr(void)909 stoppr(void)
910 {
911 	int fd;
912 	struct stat stbuf;
913 
914 	if (cgetstr(bp, "sd", &SD) == -1)
915 		SD = _PATH_DEFSPOOL;
916 	if (cgetstr(bp, "lo", &LO) == -1)
917 		LO = DEFLOCK;
918 	(void)snprintf(line, sizeof(line), "%s/%s", SD, LO);
919 	printf("%s:\n", printer);
920 
921 	/*
922 	 * Turn on the owner execute bit of the lock file to disable printing.
923 	 */
924 	PRIV_START;
925 	if (stat(line, &stbuf) >= 0) {
926 		stbuf.st_mode |= S_IXUSR;
927 		if (chmod(line, stbuf.st_mode & 0777) < 0)
928 			printf("\tcannot disable printing\n");
929 		else {
930 			upstat("printing disabled\n");
931 			printf("\tprinting disabled\n");
932 		}
933 	} else if (errno == ENOENT) {
934 		if ((fd = safe_open(line, O_WRONLY|O_CREAT|O_NOFOLLOW, 0760)) < 0)
935 			printf("\tcannot create lock file\n");
936 		else {
937 			(void)fchown(fd, DEFUID, -1);
938 			(void)close(fd);
939 			upstat("printing disabled\n");
940 			printf("\tprinting disabled\n");
941 		}
942 	} else
943 		printf("\tcannot stat lock file\n");
944 	PRIV_END;
945 }
946 
947 struct	queue **queue;
948 int	nitems;
949 time_t	mtime;
950 
951 /*
952  * Put the specified jobs at the top of printer queue.
953  */
954 void
topq(int argc,char ** argv)955 topq(int argc, char **argv)
956 {
957 	int i;
958 	struct stat stbuf;
959 	int status, changed;
960 
961 	if (argc < 3) {
962 		printf("usage: topq printer [jobnum ...] [user ...]\n");
963 		return;
964 	}
965 
966 	--argc;
967 	printer = *++argv;
968 	status = cgetent(&bp, printcapdb, printer);
969 	if (status == -2) {
970 		printf("cannot open printer description file\n");
971 		return;
972 	} else if (status == -1) {
973 		printf("%s: unknown printer\n", printer);
974 		return;
975 	} else if (status == -3)
976 		fatal("potential reference loop detected in printcap file");
977 
978 	if (cgetstr(bp, "sd", &SD) == -1)
979 		SD = _PATH_DEFSPOOL;
980 	if (cgetstr(bp, "lo", &LO) == -1)
981 		LO = DEFLOCK;
982 	printf("%s:\n", printer);
983 
984 	PRIV_START;
985 	if (chdir(SD) < 0) {
986 		printf("\tcannot chdir to %s\n", SD);
987 		goto out;
988 	}
989 	PRIV_END;
990 	nitems = getq(&queue);
991 	if (nitems == 0)
992 		return;
993 	changed = 0;
994 	mtime = queue[0]->q_time;
995 	for (i = argc; --i; ) {
996 		if (doarg(argv[i]) == 0) {
997 			printf("\tjob %s is not in the queue\n", argv[i]);
998 			continue;
999 		} else
1000 			changed++;
1001 	}
1002 	for (i = 0; i < nitems; i++)
1003 		free(queue[i]);
1004 	free(queue);
1005 	if (!changed) {
1006 		printf("\tqueue order unchanged\n");
1007 		return;
1008 	}
1009 	/*
1010 	 * Turn on the public execute bit of the lock file to
1011 	 * get lpd to rebuild the queue after the current job.
1012 	 */
1013 	PRIV_START;
1014 	if (changed && stat(LO, &stbuf) >= 0) {
1015 		stbuf.st_mode |= S_IXOTH;
1016 		(void)chmod(LO, stbuf.st_mode & 0777);
1017 	}
1018 
1019 out:
1020 	PRIV_END;
1021 }
1022 
1023 /*
1024  * Reposition the job by changing the modification time of
1025  * the control file.
1026  */
1027 static int
touch(struct queue * q)1028 touch(struct queue *q)
1029 {
1030 	struct timeval tvp[2];
1031 	int ret;
1032 
1033 	tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
1034 	tvp[0].tv_usec = tvp[1].tv_usec = 0;
1035 	PRIV_START;
1036 	ret = utimes(q->q_name, tvp);
1037 	PRIV_END;
1038 	return (ret);
1039 }
1040 
1041 /*
1042  * Checks if specified job name is in the printer's queue.
1043  * Returns:  negative (-1) if argument name is not in the queue.
1044  */
1045 int
doarg(char * job)1046 doarg(char *job)
1047 {
1048 	struct queue **qq;
1049 	int jobnum, fd, n;
1050 	char *cp, *machine;
1051 	int cnt = 0;
1052 	FILE *fp;
1053 
1054 	/*
1055 	 * Look for a job item consisting of system name, colon, number
1056 	 * (example: ucbarpa:114)
1057 	 */
1058 	if ((cp = strchr(job, ':')) != NULL) {
1059 		machine = job;
1060 		*cp++ = '\0';
1061 		job = cp;
1062 	} else
1063 		machine = NULL;
1064 
1065 	/*
1066 	 * Check for job specified by number (example: 112 or 235ucbarpa).
1067 	 */
1068 	if (isdigit((unsigned char)*job)) {
1069 		jobnum = 0;
1070 		do
1071 			jobnum = jobnum * 10 + (*job++ - '0');
1072 		while (isdigit((unsigned char)*job));
1073 		for (qq = queue + nitems; --qq >= queue; ) {
1074 			n = 0;
1075 			for (cp = (*qq)->q_name+3; isdigit((unsigned char)*cp); )
1076 				n = n * 10 + (*cp++ - '0');
1077 			if (jobnum != n)
1078 				continue;
1079 			if (*job && strcmp(job, cp) != 0)
1080 				continue;
1081 			if (machine != NULL && strcmp(machine, cp) != 0)
1082 				continue;
1083 			if (touch(*qq) == 0) {
1084 				printf("\tmoved %s\n", (*qq)->q_name);
1085 				cnt++;
1086 			}
1087 		}
1088 		return(cnt);
1089 	}
1090 	/*
1091 	 * Process item consisting of owner's name (example: henry).
1092 	 */
1093 	for (qq = queue + nitems; --qq >= queue; ) {
1094 		PRIV_START;
1095 		fd = safe_open((*qq)->q_name, O_RDONLY|O_NOFOLLOW, 0);
1096 		PRIV_END;
1097 		if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) {
1098 			if (fd >= 0)
1099 				close(fd);
1100 			continue;
1101 		}
1102 		while (get_line(fp) > 0)
1103 			if (line[0] == 'P')
1104 				break;
1105 		(void)fclose(fp);
1106 		if (line[0] != 'P' || strcmp(job, line+1) != 0)
1107 			continue;
1108 		if (touch(*qq) == 0) {
1109 			printf("\tmoved %s\n", (*qq)->q_name);
1110 			cnt++;
1111 		}
1112 	}
1113 	return(cnt);
1114 }
1115 
1116 /*
1117  * Enable everything and start printer (undo `down').
1118  */
1119 void
up(int argc,char ** argv)1120 up(int argc, char **argv)
1121 {
1122 	int c, status;
1123 	char *cp1, *cp2;
1124 	char prbuf[100];
1125 
1126 	if (argc == 1) {
1127 		printf("usage: up {all | printer ...}\n");
1128 		return;
1129 	}
1130 	if (argc == 2 && strcmp(argv[1], "all") == 0) {
1131 		printer = prbuf;
1132 		while (cgetnext(&bp, printcapdb) > 0) {
1133 			cp1 = prbuf;
1134 			cp2 = bp;
1135 			while ((c = *cp2++) && c != '|' && c != ':' &&
1136 			    (cp1 - prbuf) < sizeof(prbuf) - 1)
1137 				*cp1++ = c;
1138 			*cp1 = '\0';
1139 			startpr(2);
1140 		}
1141 		return;
1142 	}
1143 	while (--argc) {
1144 		printer = *++argv;
1145 		if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1146 			printf("cannot open printer description file\n");
1147 			continue;
1148 		} else if (status == -1) {
1149 			printf("unknown printer %s\n", printer);
1150 			continue;
1151 		} else if (status == -3)
1152 			fatal("potential reference loop detected in printcap file");
1153 
1154 		startpr(2);
1155 	}
1156 }
1157