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