1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 /* 43 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; 44 */ 45 static const char rcsid[] = 46 "$FreeBSD$"; 47 #endif /* not lint */ 48 49 /* 50 * lpc -- line printer control program -- commands: 51 */ 52 53 #include <sys/param.h> 54 #include <sys/time.h> 55 #include <sys/stat.h> 56 #include <sys/file.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 static void abortpr(struct printer *_pp, int _dis); 74 static int doarg(char *_job); 75 static int doselect(struct dirent *_d); 76 static void putmsg(struct printer *_pp, int _argc, char **_argv); 77 static int sortq(const void *_a, const void *_b); 78 static void startpr(struct printer *_pp, int _chgenable); 79 static int touch(struct jobqueue *_jq); 80 static void unlinkf(char *_name); 81 static void upstat(struct printer *_pp, const char *_msg); 82 static void wrapup_clean(int _laststatus); 83 84 /* 85 * generic framework for commands which operate on all or a specified 86 * set of printers 87 */ 88 enum qsel_val { /* how a given ptr was selected */ 89 QSEL_UNKNOWN = -1, /* ... not selected yet */ 90 QSEL_BYNAME = 0, /* ... user specifed it by name */ 91 QSEL_ALL = 1 /* ... user wants "all" printers */ 92 /* (with more to come) */ 93 }; 94 95 static enum qsel_val generic_qselect; /* indicates how ptr was selected */ 96 static int generic_initerr; /* result of initrtn processing */ 97 static char *generic_nullarg; 98 static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ 99 100 void 101 generic(void (*specificrtn)(struct printer *_pp), 102 void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) 103 { 104 int cmdstatus, more, targc; 105 struct printer myprinter, *pp; 106 char **targv; 107 108 if (argc == 1) { 109 printf("Usage: %s {all | printer ...}\n", argv[0]); 110 return; 111 } 112 113 /* 114 * The initialization routine for a command might set a generic 115 * "wrapup" routine, which should be called after processing all 116 * the printers in the command. This might print summary info. 117 * 118 * Note that the initialization routine may also parse (and 119 * nullify) some of the parameters given on the command, leaving 120 * only the parameters which have to do with printer names. 121 */ 122 pp = &myprinter; 123 generic_wrapup = NULL; 124 generic_qselect = QSEL_UNKNOWN; 125 cmdstatus = 0; 126 /* this just needs to be a distinct value of type 'char *' */ 127 if (generic_nullarg == NULL) 128 generic_nullarg = strdup(""); 129 130 /* call initialization routine, if there is one for this cmd */ 131 if (initrtn != NULL) { 132 generic_initerr = 0; 133 (*initrtn)(argc, argv); 134 if (generic_initerr) 135 return; 136 /* skip any initial arguments null-ified by initrtn */ 137 targc = argc; 138 targv = argv; 139 while (--targc) { 140 if (targv[1] != generic_nullarg) 141 break; 142 ++targv; 143 } 144 if (targv != argv) { 145 targv[0] = argv[0]; /* copy the command-name */ 146 argv = targv; 147 argc = targc + 1; 148 } 149 } 150 151 if (argc == 2 && strcmp(argv[1], "all") == 0) { 152 generic_qselect = QSEL_ALL; 153 more = firstprinter(pp, &cmdstatus); 154 if (cmdstatus) 155 goto looperr; 156 while (more) { 157 (*specificrtn)(pp); 158 do { 159 more = nextprinter(pp, &cmdstatus); 160 looperr: 161 switch (cmdstatus) { 162 case PCAPERR_TCOPEN: 163 printf("warning: %s: unresolved " 164 "tc= reference(s) ", 165 pp->printer); 166 case PCAPERR_SUCCESS: 167 break; 168 default: 169 fatal(pp, "%s", pcaperr(cmdstatus)); 170 } 171 } while (more && cmdstatus); 172 } 173 goto wrapup; 174 } 175 176 generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 177 while (--argc) { 178 ++argv; 179 if (*argv == generic_nullarg) 180 continue; 181 init_printer(pp); 182 cmdstatus = getprintcap(*argv, pp); 183 switch (cmdstatus) { 184 default: 185 fatal(pp, "%s", pcaperr(cmdstatus)); 186 case PCAPERR_NOTFOUND: 187 printf("unknown printer %s\n", *argv); 188 continue; 189 case PCAPERR_TCOPEN: 190 printf("warning: %s: unresolved tc= reference(s)\n", 191 *argv); 192 break; 193 case PCAPERR_SUCCESS: 194 break; 195 } 196 (*specificrtn)(pp); 197 } 198 199 wrapup: 200 if (generic_wrapup) { 201 (*generic_wrapup)(cmdstatus); 202 } 203 204 } 205 206 /* 207 * kill an existing daemon and disable printing. 208 */ 209 void 210 doabort(struct printer *pp) 211 { 212 abortpr(pp, 1); 213 } 214 215 static void 216 abortpr(struct printer *pp, int dis) 217 { 218 register FILE *fp; 219 struct stat stbuf; 220 int pid, fd; 221 char lf[MAXPATHLEN]; 222 223 lock_file_name(pp, lf, sizeof lf); 224 printf("%s:\n", pp->printer); 225 226 /* 227 * Turn on the owner execute bit of the lock file to disable printing. 228 */ 229 if (dis) { 230 seteuid(euid); 231 if (stat(lf, &stbuf) >= 0) { 232 if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0) 233 printf("\tcannot disable printing: %s\n", 234 strerror(errno)); 235 else { 236 upstat(pp, "printing disabled\n"); 237 printf("\tprinting disabled\n"); 238 } 239 } else if (errno == ENOENT) { 240 if ((fd = open(lf, O_WRONLY|O_CREAT, 241 LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0) 242 printf("\tcannot create lock file: %s\n", 243 strerror(errno)); 244 else { 245 (void) close(fd); 246 upstat(pp, "printing disabled\n"); 247 printf("\tprinting disabled\n"); 248 printf("\tno daemon to abort\n"); 249 } 250 goto out; 251 } else { 252 printf("\tcannot stat lock file\n"); 253 goto out; 254 } 255 } 256 /* 257 * Kill the current daemon to stop printing now. 258 */ 259 if ((fp = fopen(lf, "r")) == NULL) { 260 printf("\tcannot open lock file\n"); 261 goto out; 262 } 263 if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) { 264 (void) fclose(fp); /* unlocks as well */ 265 printf("\tno daemon to abort\n"); 266 goto out; 267 } 268 (void) fclose(fp); 269 if (kill(pid = atoi(line), SIGTERM) < 0) { 270 if (errno == ESRCH) 271 printf("\tno daemon to abort\n"); 272 else 273 printf("\tWarning: daemon (pid %d) not killed\n", pid); 274 } else 275 printf("\tdaemon (pid %d) killed\n", pid); 276 out: 277 seteuid(uid); 278 } 279 280 /* 281 * Write a message into the status file. 282 */ 283 static void 284 upstat(struct printer *pp, const char *msg) 285 { 286 register int fd; 287 char statfile[MAXPATHLEN]; 288 289 status_file_name(pp, statfile, sizeof statfile); 290 umask(0); 291 fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 292 if (fd < 0) { 293 printf("\tcannot create status file: %s\n", strerror(errno)); 294 return; 295 } 296 (void) ftruncate(fd, 0); 297 if (msg == (char *)NULL) 298 (void) write(fd, "\n", 1); 299 else 300 (void) write(fd, msg, strlen(msg)); 301 (void) close(fd); 302 } 303 304 /* 305 * "global" variables for all the routines related to 'clean' and 'tclean' 306 */ 307 static time_t cln_now; /* current time */ 308 static double cln_minage; /* minimum age before file is removed */ 309 static long cln_sizecnt; /* amount of space freed up */ 310 static int cln_debug; /* print extra debugging msgs */ 311 static int cln_filecnt; /* number of files destroyed */ 312 static int cln_foundcore; /* found a core file! */ 313 static int cln_queuecnt; /* number of queues checked */ 314 static int cln_testonly; /* remove-files vs just-print-info */ 315 316 static int 317 doselect(struct dirent *d) 318 { 319 int c = d->d_name[0]; 320 321 if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f') 322 return 1; 323 if (c == 'c') { 324 if (!strcmp(d->d_name, "core")) 325 cln_foundcore = 1; 326 } 327 if (c == 'e') { 328 if (!strncmp(d->d_name, "errs.", 5)) 329 return 1; 330 } 331 return 0; 332 } 333 334 /* 335 * Comparison routine for scandir. Sort by job number and machine, then 336 * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z. 337 */ 338 static int 339 sortq(const void *a, const void *b) 340 { 341 const struct dirent **d1, **d2; 342 int c1, c2; 343 344 d1 = (const struct dirent **)a; 345 d2 = (const struct dirent **)b; 346 if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3))) 347 return(c1); 348 c1 = (*d1)->d_name[0]; 349 c2 = (*d2)->d_name[0]; 350 if (c1 == c2) 351 return((*d1)->d_name[2] - (*d2)->d_name[2]); 352 if (c1 == 'c') 353 return(-1); 354 if (c1 == 'd' || c2 == 'c') 355 return(1); 356 return(-1); 357 } 358 359 /* 360 * Remove all spool files and temporaries from the spooling area. 361 * Or, perhaps: 362 * Remove incomplete jobs from spooling area. 363 */ 364 365 void 366 init_clean(int argc, char *argv[]) 367 { 368 369 /* init some fields before 'clean' is called for each queue */ 370 cln_queuecnt = 0; 371 cln_now = time(NULL); 372 cln_minage = 3600.0; /* only delete files >1h old */ 373 cln_filecnt = 0; 374 cln_sizecnt = 0; 375 cln_debug = 0; 376 cln_testonly = 0; 377 generic_wrapup = &wrapup_clean; 378 379 /* see if there are any options specified before the ptr list */ 380 while (--argc) { 381 ++argv; 382 if (**argv != '-') 383 break; 384 if (strcmp(*argv, "-d") == 0) { 385 /* just an example of an option... */ 386 cln_debug = 1; 387 *argv = generic_nullarg; /* "erase" it */ 388 } else { 389 printf("Invalid option '%s'\n", *argv); 390 generic_initerr = 1; 391 } 392 } 393 394 return; 395 } 396 397 void 398 init_tclean(int argc, char *argv[]) 399 { 400 401 /* only difference between 'clean' and 'tclean' is one value */ 402 /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 403 init_clean(argc, argv); 404 cln_testonly = 1; 405 406 return; 407 } 408 409 void 410 clean_q(struct printer *pp) 411 { 412 char *cp, *cp1, *lp; 413 struct dirent **queue; 414 size_t linerem; 415 int didhead, i, n, nitems, rmcp; 416 417 cln_queuecnt++; 418 419 didhead = 0; 420 if (generic_qselect == QSEL_BYNAME) { 421 printf("%s:\n", pp->printer); 422 didhead = 1; 423 } 424 425 lp = line; 426 cp = pp->spool_dir; 427 while (lp < &line[sizeof(line) - 1]) { 428 if ((*lp++ = *cp++) == 0) 429 break; 430 } 431 lp[-1] = '/'; 432 linerem = sizeof(line) - (lp - line); 433 434 cln_foundcore = 0; 435 seteuid(euid); 436 nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 437 seteuid(uid); 438 if (nitems < 0) { 439 if (!didhead) { 440 printf("%s:\n", pp->printer); 441 didhead = 1; 442 } 443 printf("\tcannot examine spool directory\n"); 444 return; 445 } 446 if (cln_foundcore) { 447 if (!didhead) { 448 printf("%s:\n", pp->printer); 449 didhead = 1; 450 } 451 printf("\t** found a core file in %s !\n", pp->spool_dir); 452 } 453 if (nitems == 0) 454 return; 455 if (!didhead) 456 printf("%s:\n", pp->printer); 457 i = 0; 458 do { 459 cp = queue[i]->d_name; 460 rmcp = 0; 461 if (*cp == 'c') { 462 /* 463 * A control file. Look for matching data-files. 464 */ 465 /* XXX 466 * Note the logic here assumes that the hostname 467 * part of cf-filenames match the hostname part 468 * in df-filenames, and that is not necessarily 469 * true (eg: for multi-homed hosts). This needs 470 * some further thought... 471 */ 472 n = 0; 473 while (i + 1 < nitems) { 474 cp1 = queue[i + 1]->d_name; 475 if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) 476 break; 477 i++; 478 n++; 479 } 480 if (n == 0) { 481 rmcp = 1; 482 } 483 } else if (*cp == 'e') { 484 /* 485 * Must be an errrs or email temp file. 486 */ 487 rmcp = 1; 488 } else { 489 /* 490 * Must be a df with no cf (otherwise, it would have 491 * been skipped above) or a tf file (which can always 492 * be removed if it's old enough). 493 */ 494 rmcp = 1; 495 } 496 if (rmcp) { 497 if (strlen(cp) >= linerem) { 498 printf("\t** internal error: 'line' overflow!\n"); 499 printf("\t** spooldir = %s\n", pp->spool_dir); 500 printf("\t** cp = %s\n", cp); 501 return; 502 } 503 strlcpy(lp, cp, linerem); 504 unlinkf(line); 505 } 506 } while (++i < nitems); 507 } 508 509 static void 510 wrapup_clean(int laststatus __unused) 511 { 512 513 printf("Checked %d queues, and ", cln_queuecnt); 514 if (cln_filecnt < 1) { 515 printf("no cruft was found\n"); 516 return; 517 } 518 if (cln_testonly) { 519 printf("would have "); 520 } 521 printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); 522 } 523 524 static void 525 unlinkf(char *name) 526 { 527 struct stat stbuf; 528 double agemod, agestat; 529 int res; 530 char linkbuf[BUFSIZ]; 531 532 /* 533 * We have to use lstat() instead of stat(), in case this is a df* 534 * "file" which is really a symlink due to 'lpr -s' processing. In 535 * that case, we need to check the last-mod time of the symlink, and 536 * not the file that the symlink is pointed at. 537 */ 538 seteuid(euid); 539 res = lstat(name, &stbuf); 540 seteuid(uid); 541 if (res < 0) { 542 printf("\terror return from stat(%s):\n", name); 543 printf("\t %s\n", strerror(errno)); 544 return; 545 } 546 547 agemod = difftime(cln_now, stbuf.st_mtime); 548 agestat = difftime(cln_now, stbuf.st_ctime); 549 if (cln_debug) { 550 /* this debugging-aid probably is not needed any more... */ 551 printf("\t\t modify age=%g secs, stat age=%g secs\n", 552 agemod, agestat); 553 } 554 if ((agemod <= cln_minage) && (agestat <= cln_minage)) 555 return; 556 557 /* 558 * if this file is a symlink, then find out the target of the 559 * symlink before unlink-ing the file itself 560 */ 561 if (S_ISLNK(stbuf.st_mode)) { 562 seteuid(euid); 563 res = readlink(name, linkbuf, sizeof(linkbuf)); 564 seteuid(uid); 565 if (res < 0) { 566 printf("\terror return from readlink(%s):\n", name); 567 printf("\t %s\n", strerror(errno)); 568 return; 569 } 570 if (res == sizeof(linkbuf)) 571 res--; 572 linkbuf[res] = '\0'; 573 } 574 575 cln_filecnt++; 576 cln_sizecnt += stbuf.st_size; 577 578 if (cln_testonly) { 579 printf("\twould remove %s\n", name); 580 if (S_ISLNK(stbuf.st_mode)) { 581 printf("\t (which is a symlink to %s)\n", linkbuf); 582 } 583 } else { 584 seteuid(euid); 585 res = unlink(name); 586 seteuid(uid); 587 if (res < 0) 588 printf("\tcannot remove %s (!)\n", name); 589 else 590 printf("\tremoved %s\n", name); 591 /* XXX 592 * Note that for a df* file, this code should also check to see 593 * if it is a symlink to some other file, and if the original 594 * lpr command included '-r' ("remove file"). Of course, this 595 * code would not be removing the df* file unless there was no 596 * matching cf* file, and without the cf* file it is currently 597 * impossible to determine if '-r' had been specified... 598 * 599 * As a result of this quandry, we may be leaving behind a 600 * user's file that was supposed to have been removed after 601 * being printed. This may effect services such as CAP or 602 * samba, if they were configured to use 'lpr -r', and if 603 * datafiles are not being properly removed. 604 */ 605 if (S_ISLNK(stbuf.st_mode)) { 606 printf("\t (which was a symlink to %s)\n", linkbuf); 607 } 608 } 609 } 610 611 /* 612 * Enable queuing to the printer (allow lpr's). 613 */ 614 void 615 enable(struct printer *pp) 616 { 617 struct stat stbuf; 618 char lf[MAXPATHLEN]; 619 620 lock_file_name(pp, lf, sizeof lf); 621 printf("%s:\n", pp->printer); 622 623 /* 624 * Turn off the group execute bit of the lock file to enable queuing. 625 */ 626 seteuid(euid); 627 if (stat(lf, &stbuf) >= 0) { 628 if (chmod(lf, stbuf.st_mode & ~LFM_QUEUE_DIS) < 0) 629 printf("\tcannot enable queuing\n"); 630 else 631 printf("\tqueuing enabled\n"); 632 } 633 seteuid(uid); 634 } 635 636 /* 637 * Disable queuing. 638 */ 639 void 640 disable(struct printer *pp) 641 { 642 register int fd; 643 struct stat stbuf; 644 char lf[MAXPATHLEN]; 645 646 lock_file_name(pp, lf, sizeof lf); 647 printf("%s:\n", pp->printer); 648 /* 649 * Turn on the group execute bit of the lock file to disable queuing. 650 */ 651 seteuid(euid); 652 if (stat(lf, &stbuf) >= 0) { 653 if (chmod(lf, stbuf.st_mode | LFM_QUEUE_DIS) < 0) 654 printf("\tcannot disable queuing: %s\n", 655 strerror(errno)); 656 else 657 printf("\tqueuing disabled\n"); 658 } else if (errno == ENOENT) { 659 if ((fd = open(lf, O_WRONLY|O_CREAT, 660 LOCK_FILE_MODE | LFM_QUEUE_DIS)) < 0) 661 printf("\tcannot create lock file: %s\n", 662 strerror(errno)); 663 else { 664 (void) close(fd); 665 printf("\tqueuing disabled\n"); 666 } 667 } else 668 printf("\tcannot stat lock file\n"); 669 seteuid(uid); 670 } 671 672 /* 673 * Disable queuing and printing and put a message into the status file 674 * (reason for being down). 675 */ 676 void 677 down(int argc, char *argv[]) 678 { 679 int cmdstatus, more; 680 struct printer myprinter, *pp = &myprinter; 681 682 if (argc == 1) { 683 printf("Usage: down {all | printer} [message ...]\n"); 684 return; 685 } 686 if (!strcmp(argv[1], "all")) { 687 more = firstprinter(pp, &cmdstatus); 688 if (cmdstatus) 689 goto looperr; 690 while (more) { 691 putmsg(pp, argc - 2, argv + 2); 692 do { 693 more = nextprinter(pp, &cmdstatus); 694 looperr: 695 switch (cmdstatus) { 696 case PCAPERR_TCOPEN: 697 printf("warning: %s: unresolved " 698 "tc= reference(s) ", 699 pp->printer); 700 case PCAPERR_SUCCESS: 701 break; 702 default: 703 fatal(pp, "%s", pcaperr(cmdstatus)); 704 } 705 } while (more && cmdstatus); 706 } 707 return; 708 } 709 init_printer(pp); 710 cmdstatus = getprintcap(argv[1], pp); 711 switch (cmdstatus) { 712 default: 713 fatal(pp, "%s", pcaperr(cmdstatus)); 714 case PCAPERR_NOTFOUND: 715 printf("unknown printer %s\n", argv[1]); 716 return; 717 case PCAPERR_TCOPEN: 718 printf("warning: %s: unresolved tc= reference(s)", argv[1]); 719 break; 720 case PCAPERR_SUCCESS: 721 break; 722 } 723 putmsg(pp, argc - 2, argv + 2); 724 } 725 726 static void 727 putmsg(struct printer *pp, int argc, char **argv) 728 { 729 register int fd; 730 register char *cp1, *cp2; 731 char buf[1024]; 732 char file[MAXPATHLEN]; 733 struct stat stbuf; 734 735 printf("%s:\n", pp->printer); 736 /* 737 * Turn on the group execute bit of the lock file to disable queuing; 738 * turn on the owner execute bit of the lock file to disable printing. 739 */ 740 lock_file_name(pp, file, sizeof file); 741 seteuid(euid); 742 if (stat(file, &stbuf) >= 0) { 743 if (chmod(file, stbuf.st_mode|LFM_PRINT_DIS|LFM_QUEUE_DIS) < 0) 744 printf("\tcannot disable queuing: %s\n", 745 strerror(errno)); 746 else 747 printf("\tprinter and queuing disabled\n"); 748 } else if (errno == ENOENT) { 749 if ((fd = open(file, O_WRONLY|O_CREAT, 750 LOCK_FILE_MODE|LFM_PRINT_DIS|LFM_QUEUE_DIS)) < 0) 751 printf("\tcannot create lock file: %s\n", 752 strerror(errno)); 753 else { 754 (void) close(fd); 755 printf("\tprinter and queuing disabled\n"); 756 } 757 seteuid(uid); 758 return; 759 } else 760 printf("\tcannot stat lock file\n"); 761 /* 762 * Write the message into the status file. 763 */ 764 status_file_name(pp, file, sizeof file); 765 fd = open(file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 766 if (fd < 0) { 767 printf("\tcannot create status file: %s\n", strerror(errno)); 768 seteuid(uid); 769 return; 770 } 771 seteuid(uid); 772 (void) ftruncate(fd, 0); 773 if (argc <= 0) { 774 (void) write(fd, "\n", 1); 775 (void) close(fd); 776 return; 777 } 778 cp1 = buf; 779 while (--argc >= 0) { 780 cp2 = *argv++; 781 while ((size_t)(cp1 - buf) < sizeof(buf) && (*cp1++ = *cp2++)) 782 ; 783 cp1[-1] = ' '; 784 } 785 cp1[-1] = '\n'; 786 *cp1 = '\0'; 787 (void) write(fd, buf, strlen(buf)); 788 (void) close(fd); 789 } 790 791 /* 792 * Exit lpc 793 */ 794 void 795 quit(int argc __unused, char *argv[] __unused) 796 { 797 exit(0); 798 } 799 800 /* 801 * Kill and restart the daemon. 802 */ 803 void 804 restart(struct printer *pp) 805 { 806 abortpr(pp, 0); 807 startpr(pp, 0); 808 } 809 810 /* 811 * Enable printing on the specified printer and startup the daemon. 812 */ 813 void 814 startcmd(struct printer *pp) 815 { 816 startpr(pp, 1); 817 } 818 819 static void 820 startpr(struct printer *pp, int chgenable) 821 { 822 struct stat stbuf; 823 char lf[MAXPATHLEN]; 824 825 lock_file_name(pp, lf, sizeof lf); 826 printf("%s:\n", pp->printer); 827 828 /* 829 * For chgenable==1 ('start'), turn off the LFM_PRINT_DIS bit of the 830 * lock file to re-enable printing. For chgenable==2 ('up'), also 831 * turn off the LFM_QUEUE_DIS bit to re-enable queueing. 832 */ 833 seteuid(euid); 834 if (chgenable && stat(lf, &stbuf) >= 0) { 835 mode_t bits = (chgenable == 2 ? 0 : LFM_QUEUE_DIS); 836 if (chmod(lf, stbuf.st_mode & (LOCK_FILE_MODE | bits)) < 0) 837 printf("\tcannot enable printing\n"); 838 else 839 printf("\tprinting enabled\n"); 840 } 841 if (!startdaemon(pp)) 842 printf("\tcouldn't start daemon\n"); 843 else 844 printf("\tdaemon started\n"); 845 seteuid(uid); 846 } 847 848 /* 849 * Print the status of the printer queue. 850 */ 851 void 852 status(struct printer *pp) 853 { 854 struct stat stbuf; 855 register int fd, i; 856 register struct dirent *dp; 857 DIR *dirp; 858 char file[MAXPATHLEN]; 859 860 printf("%s:\n", pp->printer); 861 lock_file_name(pp, file, sizeof file); 862 if (stat(file, &stbuf) >= 0) { 863 printf("\tqueuing is %s\n", 864 ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 865 : "enabled")); 866 printf("\tprinting is %s\n", 867 ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 868 : "enabled")); 869 } else { 870 printf("\tqueuing is enabled\n"); 871 printf("\tprinting is enabled\n"); 872 } 873 if ((dirp = opendir(pp->spool_dir)) == NULL) { 874 printf("\tcannot examine spool directory\n"); 875 return; 876 } 877 i = 0; 878 while ((dp = readdir(dirp)) != NULL) { 879 if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 880 i++; 881 } 882 closedir(dirp); 883 if (i == 0) 884 printf("\tno entries in spool area\n"); 885 else if (i == 1) 886 printf("\t1 entry in spool area\n"); 887 else 888 printf("\t%d entries in spool area\n", i); 889 fd = open(file, O_RDONLY); 890 if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 891 (void) close(fd); /* unlocks as well */ 892 printf("\tprinter idle\n"); 893 return; 894 } 895 (void) close(fd); 896 /* print out the contents of the status file, if it exists */ 897 status_file_name(pp, file, sizeof file); 898 fd = open(file, O_RDONLY|O_SHLOCK); 899 if (fd >= 0) { 900 (void) fstat(fd, &stbuf); 901 if (stbuf.st_size > 0) { 902 putchar('\t'); 903 while ((i = read(fd, line, sizeof(line))) > 0) 904 (void) fwrite(line, 1, i, stdout); 905 } 906 (void) close(fd); /* unlocks as well */ 907 } 908 } 909 910 /* 911 * Stop the specified daemon after completing the current job and disable 912 * printing. 913 */ 914 void 915 stop(struct printer *pp) 916 { 917 register int fd; 918 struct stat stbuf; 919 char lf[MAXPATHLEN]; 920 921 lock_file_name(pp, lf, sizeof lf); 922 printf("%s:\n", pp->printer); 923 924 /* 925 * Turn on the owner execute bit of the lock file to disable printing. 926 */ 927 seteuid(euid); 928 if (stat(lf, &stbuf) >= 0) { 929 if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0) 930 printf("\tcannot disable printing: %s\n", 931 strerror(errno)); 932 else { 933 upstat(pp, "printing disabled\n"); 934 printf("\tprinting disabled\n"); 935 } 936 } else if (errno == ENOENT) { 937 if ((fd = open(lf, O_WRONLY|O_CREAT, 938 LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0) 939 printf("\tcannot create lock file: %s\n", 940 strerror(errno)); 941 else { 942 (void) close(fd); 943 upstat(pp, "printing disabled\n"); 944 printf("\tprinting disabled\n"); 945 } 946 } else 947 printf("\tcannot stat lock file\n"); 948 seteuid(uid); 949 } 950 951 struct jobqueue **queue; 952 int nitems; 953 time_t mtime; 954 955 /* 956 * Put the specified jobs at the top of printer queue. 957 */ 958 void 959 topq(int argc, char *argv[]) 960 { 961 register int i; 962 struct stat stbuf; 963 int cmdstatus, changed; 964 struct printer myprinter, *pp = &myprinter; 965 966 if (argc < 3) { 967 printf("Usage: topq printer [jobnum ...] [user ...]\n"); 968 return; 969 } 970 971 --argc; 972 ++argv; 973 init_printer(pp); 974 cmdstatus = getprintcap(*argv, pp); 975 switch(cmdstatus) { 976 default: 977 fatal(pp, "%s", pcaperr(cmdstatus)); 978 case PCAPERR_NOTFOUND: 979 printf("unknown printer %s\n", *argv); 980 return; 981 case PCAPERR_TCOPEN: 982 printf("warning: %s: unresolved tc= reference(s)", *argv); 983 break; 984 case PCAPERR_SUCCESS: 985 break; 986 } 987 printf("%s:\n", pp->printer); 988 989 seteuid(euid); 990 if (chdir(pp->spool_dir) < 0) { 991 printf("\tcannot chdir to %s\n", pp->spool_dir); 992 goto out; 993 } 994 seteuid(uid); 995 nitems = getq(pp, &queue); 996 if (nitems == 0) 997 return; 998 changed = 0; 999 mtime = queue[0]->job_time; 1000 for (i = argc; --i; ) { 1001 if (doarg(argv[i]) == 0) { 1002 printf("\tjob %s is not in the queue\n", argv[i]); 1003 continue; 1004 } else 1005 changed++; 1006 } 1007 for (i = 0; i < nitems; i++) 1008 free(queue[i]); 1009 free(queue); 1010 if (!changed) { 1011 printf("\tqueue order unchanged\n"); 1012 return; 1013 } 1014 /* 1015 * Turn on the public execute bit of the lock file to 1016 * get lpd to rebuild the queue after the current job. 1017 */ 1018 seteuid(euid); 1019 if (changed && stat(pp->lock_file, &stbuf) >= 0) 1020 (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 1021 1022 out: 1023 seteuid(uid); 1024 } 1025 1026 /* 1027 * Reposition the job by changing the modification time of 1028 * the control file. 1029 */ 1030 static int 1031 touch(struct jobqueue *jq) 1032 { 1033 struct timeval tvp[2]; 1034 int ret; 1035 1036 tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 1037 tvp[0].tv_usec = tvp[1].tv_usec = 0; 1038 seteuid(euid); 1039 ret = utimes(jq->job_cfname, tvp); 1040 seteuid(uid); 1041 return (ret); 1042 } 1043 1044 /* 1045 * Checks if specified job name is in the printer's queue. 1046 * Returns: negative (-1) if argument name is not in the queue. 1047 */ 1048 static int 1049 doarg(char *job) 1050 { 1051 register struct jobqueue **qq; 1052 register int jobnum, n; 1053 register char *cp, *machine; 1054 int cnt = 0; 1055 FILE *fp; 1056 1057 /* 1058 * Look for a job item consisting of system name, colon, number 1059 * (example: ucbarpa:114) 1060 */ 1061 if ((cp = strchr(job, ':')) != NULL) { 1062 machine = job; 1063 *cp++ = '\0'; 1064 job = cp; 1065 } else 1066 machine = NULL; 1067 1068 /* 1069 * Check for job specified by number (example: 112 or 235ucbarpa). 1070 */ 1071 if (isdigit(*job)) { 1072 jobnum = 0; 1073 do 1074 jobnum = jobnum * 10 + (*job++ - '0'); 1075 while (isdigit(*job)); 1076 for (qq = queue + nitems; --qq >= queue; ) { 1077 n = 0; 1078 for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 1079 n = n * 10 + (*cp++ - '0'); 1080 if (jobnum != n) 1081 continue; 1082 if (*job && strcmp(job, cp) != 0) 1083 continue; 1084 if (machine != NULL && strcmp(machine, cp) != 0) 1085 continue; 1086 if (touch(*qq) == 0) { 1087 printf("\tmoved %s\n", (*qq)->job_cfname); 1088 cnt++; 1089 } 1090 } 1091 return(cnt); 1092 } 1093 /* 1094 * Process item consisting of owner's name (example: henry). 1095 */ 1096 for (qq = queue + nitems; --qq >= queue; ) { 1097 seteuid(euid); 1098 fp = fopen((*qq)->job_cfname, "r"); 1099 seteuid(uid); 1100 if (fp == NULL) 1101 continue; 1102 while (getline(fp) > 0) 1103 if (line[0] == 'P') 1104 break; 1105 (void) fclose(fp); 1106 if (line[0] != 'P' || strcmp(job, line+1) != 0) 1107 continue; 1108 if (touch(*qq) == 0) { 1109 printf("\tmoved %s\n", (*qq)->job_cfname); 1110 cnt++; 1111 } 1112 } 1113 return(cnt); 1114 } 1115 1116 /* 1117 * Enable everything and start printer (undo `down'). 1118 */ 1119 void 1120 up(struct printer *pp) 1121 { 1122 startpr(pp, 2); 1123 } 1124