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