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