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