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