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