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 * 4. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #ifndef lint 32 static const char copyright[] = 33 "@(#) Copyright (c) 1983, 1993\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35 #endif /* not lint */ 36 37 #if 0 38 #ifndef lint 39 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; 40 #endif /* not lint */ 41 #endif 42 43 #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44 __FBSDID("$FreeBSD$"); 45 46 /* 47 * lpc -- line printer control program -- commands: 48 */ 49 50 #include <sys/param.h> 51 #include <sys/time.h> 52 #include <sys/stat.h> 53 #include <sys/file.h> 54 55 #include <signal.h> 56 #include <fcntl.h> 57 #include <errno.h> 58 #include <dirent.h> 59 #include <unistd.h> 60 #include <stdlib.h> 61 #include <stdio.h> 62 #include <ctype.h> 63 #include <string.h> 64 #include "lp.h" 65 #include "lp.local.h" 66 #include "lpc.h" 67 #include "extern.h" 68 #include "pathnames.h" 69 70 /* 71 * Return values from kill_qtask(). 72 */ 73 #define KQT_LFERROR -2 74 #define KQT_KILLFAIL -1 75 #define KQT_NODAEMON 0 76 #define KQT_KILLOK 1 77 78 static char *args2line(int argc, char **argv); 79 static int doarg(char *_job); 80 static int doselect(struct dirent *_d); 81 static int kill_qtask(const char *lf); 82 static int sortq(const void *_a, const void *_b); 83 static int touch(struct jobqueue *_jq); 84 static void unlinkf(char *_name); 85 static void upstat(struct printer *_pp, const char *_msg, int _notify); 86 static void wrapup_clean(int _laststatus); 87 88 /* 89 * generic framework for commands which operate on all or a specified 90 * set of printers 91 */ 92 enum qsel_val { /* how a given ptr was selected */ 93 QSEL_UNKNOWN = -1, /* ... not selected yet */ 94 QSEL_BYNAME = 0, /* ... user specifed it by name */ 95 QSEL_ALL = 1 /* ... user wants "all" printers */ 96 /* (with more to come) */ 97 }; 98 99 static enum qsel_val generic_qselect; /* indicates how ptr was selected */ 100 static int generic_initerr; /* result of initrtn processing */ 101 static char *generic_cmdname; 102 static char *generic_msg; /* if a -msg was specified */ 103 static char *generic_nullarg; 104 static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ 105 106 void 107 generic(void (*specificrtn)(struct printer *_pp), int cmdopts, 108 void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) 109 { 110 int cmdstatus, more, targc; 111 struct printer myprinter, *pp; 112 char **margv, **targv; 113 114 if (argc == 1) { 115 /* 116 * Usage needs a special case for 'down': The user must 117 * either include `-msg', or only the first parameter 118 * that they give will be processed as a printer name. 119 */ 120 printf("usage: %s {all | printer ...}", argv[0]); 121 if (strcmp(argv[0], "down") == 0) { 122 printf(" -msg [<text> ...]\n"); 123 printf(" or: down {all | printer} [<text> ...]"); 124 } else if (cmdopts & LPC_MSGOPT) 125 printf(" [-msg <text> ...]"); 126 printf("\n"); 127 return; 128 } 129 130 /* The first argument is the command name. */ 131 generic_cmdname = *argv++; 132 argc--; 133 134 /* 135 * The initialization routine for a command might set a generic 136 * "wrapup" routine, which should be called after processing all 137 * the printers in the command. This might print summary info. 138 * 139 * Note that the initialization routine may also parse (and 140 * nullify) some of the parameters given on the command, leaving 141 * only the parameters which have to do with printer names. 142 */ 143 pp = &myprinter; 144 generic_wrapup = NULL; 145 generic_qselect = QSEL_UNKNOWN; 146 cmdstatus = 0; 147 /* this just needs to be a distinct value of type 'char *' */ 148 if (generic_nullarg == NULL) 149 generic_nullarg = strdup(""); 150 151 /* 152 * Some commands accept a -msg argument, which indicates that 153 * all remaining arguments should be combined into a string. 154 */ 155 generic_msg = NULL; 156 if (cmdopts & LPC_MSGOPT) { 157 targc = argc; 158 targv = argv; 159 for (; targc > 0; targc--, targv++) { 160 if (strcmp(*targv, "-msg") == 0) { 161 argc -= targc; 162 generic_msg = args2line(targc - 1, targv + 1); 163 break; 164 } 165 } 166 } 167 168 /* call initialization routine, if there is one for this cmd */ 169 if (initrtn != NULL) { 170 generic_initerr = 0; 171 (*initrtn)(argc, argv); 172 if (generic_initerr) 173 return; 174 /* 175 * The initrtn may have null'ed out some of the parameters. 176 * Compact the parameter list to remove those nulls, and 177 * correct the arg-count. 178 */ 179 targc = argc; 180 targv = argv; 181 margv = argv; 182 argc = 0; 183 for (; targc > 0; targc--, targv++) { 184 if (*targv != generic_nullarg) { 185 if (targv != margv) 186 *margv = *targv; 187 margv++; 188 argc++; 189 } 190 } 191 } 192 193 if (argc == 1 && strcmp(*argv, "all") == 0) { 194 generic_qselect = QSEL_ALL; 195 more = firstprinter(pp, &cmdstatus); 196 if (cmdstatus) 197 goto looperr; 198 while (more) { 199 (*specificrtn)(pp); 200 do { 201 more = nextprinter(pp, &cmdstatus); 202 looperr: 203 switch (cmdstatus) { 204 case PCAPERR_TCOPEN: 205 printf("warning: %s: unresolved " 206 "tc= reference(s) ", 207 pp->printer); 208 case PCAPERR_SUCCESS: 209 break; 210 default: 211 fatal(pp, "%s", pcaperr(cmdstatus)); 212 } 213 } while (more && cmdstatus); 214 } 215 goto wrapup; 216 } 217 218 generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 219 for (; argc > 0; argc--, argv++) { 220 init_printer(pp); 221 cmdstatus = getprintcap(*argv, pp); 222 switch (cmdstatus) { 223 default: 224 fatal(pp, "%s", pcaperr(cmdstatus)); 225 case PCAPERR_NOTFOUND: 226 printf("unknown printer %s\n", *argv); 227 continue; 228 case PCAPERR_TCOPEN: 229 printf("warning: %s: unresolved tc= reference(s)\n", 230 *argv); 231 break; 232 case PCAPERR_SUCCESS: 233 break; 234 } 235 (*specificrtn)(pp); 236 } 237 238 wrapup: 239 if (generic_wrapup) { 240 (*generic_wrapup)(cmdstatus); 241 } 242 free_printer(pp); 243 if (generic_msg) 244 free(generic_msg); 245 } 246 247 /* 248 * Convert an argv-array of character strings into a single string. 249 */ 250 static char * 251 args2line(int argc, char **argv) 252 { 253 char *cp1, *cend; 254 const char *cp2; 255 char buf[1024]; 256 257 if (argc <= 0) 258 return strdup("\n"); 259 260 cp1 = buf; 261 cend = buf + sizeof(buf) - 1; /* save room for '\0' */ 262 while (--argc >= 0) { 263 cp2 = *argv++; 264 while ((cp1 < cend) && (*cp1++ = *cp2++)) 265 ; 266 cp1[-1] = ' '; 267 } 268 cp1[-1] = '\n'; 269 *cp1 = '\0'; 270 return strdup(buf); 271 } 272 273 /* 274 * Kill the current daemon, to stop printing of the active job. 275 */ 276 static int 277 kill_qtask(const char *lf) 278 { 279 FILE *fp; 280 pid_t pid; 281 int errsav, killres, lockres, res; 282 283 seteuid(euid); 284 fp = fopen(lf, "r"); 285 errsav = errno; 286 seteuid(uid); 287 res = KQT_NODAEMON; 288 if (fp == NULL) { 289 /* 290 * If there is no lock file, then there is no daemon to 291 * kill. Any other error return means there is some 292 * kind of problem with the lock file. 293 */ 294 if (errsav != ENOENT) 295 res = KQT_LFERROR; 296 goto killdone; 297 } 298 299 /* If the lock file is empty, then there is no daemon to kill */ 300 if (getline(fp) == 0) 301 goto killdone; 302 303 /* 304 * If the file can be locked without blocking, then there 305 * no daemon to kill, or we should not try to kill it. 306 * 307 * XXX - not sure I understand the reasoning behind this... 308 */ 309 lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); 310 (void) fclose(fp); 311 if (lockres == 0) 312 goto killdone; 313 314 pid = atoi(line); 315 if (pid < 0) { 316 /* 317 * If we got a negative pid, then the contents of the 318 * lock file is not valid. 319 */ 320 res = KQT_LFERROR; 321 goto killdone; 322 } 323 324 seteuid(uid); 325 killres = kill(pid, SIGTERM); 326 errsav = errno; 327 seteuid(uid); 328 if (killres == 0) { 329 res = KQT_KILLOK; 330 printf("\tdaemon (pid %d) killed\n", pid); 331 } else if (errno == ESRCH) { 332 res = KQT_NODAEMON; 333 } else { 334 res = KQT_KILLFAIL; 335 printf("\tWarning: daemon (pid %d) not killed:\n", pid); 336 printf("\t %s\n", strerror(errsav)); 337 } 338 339 killdone: 340 switch (res) { 341 case KQT_LFERROR: 342 printf("\tcannot open lock file: %s\n", 343 strerror(errsav)); 344 break; 345 case KQT_NODAEMON: 346 printf("\tno daemon to abort\n"); 347 break; 348 case KQT_KILLFAIL: 349 case KQT_KILLOK: 350 /* These two already printed messages to the user. */ 351 break; 352 default: 353 printf("\t<internal error in kill_qtask>\n"); 354 break; 355 } 356 357 return (res); 358 } 359 360 /* 361 * Write a message into the status file. 362 */ 363 static void 364 upstat(struct printer *pp, const char *msg, int notifyuser) 365 { 366 int fd; 367 char statfile[MAXPATHLEN]; 368 369 status_file_name(pp, statfile, sizeof statfile); 370 umask(0); 371 seteuid(euid); 372 fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 373 seteuid(uid); 374 if (fd < 0) { 375 printf("\tcannot create status file: %s\n", strerror(errno)); 376 return; 377 } 378 (void) ftruncate(fd, 0); 379 if (msg == (char *)NULL) 380 (void) write(fd, "\n", 1); 381 else 382 (void) write(fd, msg, strlen(msg)); 383 (void) close(fd); 384 if (notifyuser) { 385 if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) 386 printf("\tstatus message is now set to nothing.\n"); 387 else 388 printf("\tstatus message is now: %s", msg); 389 } 390 } 391 392 /* 393 * kill an existing daemon and disable printing. 394 */ 395 void 396 abort_q(struct printer *pp) 397 { 398 int killres, setres; 399 char lf[MAXPATHLEN]; 400 401 lock_file_name(pp, lf, sizeof lf); 402 printf("%s:\n", pp->printer); 403 404 /* 405 * Turn on the owner execute bit of the lock file to disable printing. 406 */ 407 setres = set_qstate(SQS_STOPP, lf); 408 409 /* 410 * If set_qstate found that there already was a lock file, then 411 * call a routine which will read that lock file and kill the 412 * lpd-process which is listed in that lock file. If the lock 413 * file did not exist, then either there is no daemon running 414 * for this queue, or there is one running but *it* could not 415 * write a lock file (which means we can not determine the 416 * process id of that lpd-process). 417 */ 418 switch (setres) { 419 case SQS_CHGOK: 420 case SQS_CHGFAIL: 421 /* Kill the process */ 422 killres = kill_qtask(lf); 423 break; 424 case SQS_CREOK: 425 case SQS_CREFAIL: 426 printf("\tno daemon to abort\n"); 427 break; 428 case SQS_STATFAIL: 429 printf("\tassuming no daemon to abort\n"); 430 break; 431 default: 432 printf("\t<unexpected result (%d) from set_qstate>\n", 433 setres); 434 break; 435 } 436 437 if (setres >= 0) 438 upstat(pp, "printing disabled\n", 0); 439 } 440 441 /* 442 * "global" variables for all the routines related to 'clean' and 'tclean' 443 */ 444 static time_t cln_now; /* current time */ 445 static double cln_minage; /* minimum age before file is removed */ 446 static long cln_sizecnt; /* amount of space freed up */ 447 static int cln_debug; /* print extra debugging msgs */ 448 static int cln_filecnt; /* number of files destroyed */ 449 static int cln_foundcore; /* found a core file! */ 450 static int cln_queuecnt; /* number of queues checked */ 451 static int cln_testonly; /* remove-files vs just-print-info */ 452 453 static int 454 doselect(struct dirent *d) 455 { 456 int c = d->d_name[0]; 457 458 if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && 459 d->d_name[1] == 'f') 460 return 1; 461 if (c == 'c') { 462 if (!strcmp(d->d_name, "core")) 463 cln_foundcore = 1; 464 } 465 if (c == 'e') { 466 if (!strncmp(d->d_name, "errs.", 5)) 467 return 1; 468 } 469 return 0; 470 } 471 472 /* 473 * Comparison routine that clean_q() uses for scandir. 474 * 475 * The purpose of this sort is to have all `df' files end up immediately 476 * after the matching `cf' file. For files matching `cf', `df', `rf', or 477 * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or 478 * `tf', and then by the sequence letter (which is A-Z, or a-z). This 479 * routine may also see filenames which do not start with `cf', `df', `rf', 480 * or `tf' (such as `errs.*'), and those are simply sorted by the full 481 * filename. 482 * 483 * XXX 484 * This assumes that all control files start with `cfA*', and it turns 485 * out there are a few implementations of lpr which will create `cfB*' 486 * filenames (they will have datafile names which start with `dfB*'). 487 */ 488 static int 489 sortq(const void *a, const void *b) 490 { 491 const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; 492 const char *fname_a, *fname_b, *jnum_a, *jnum_b; 493 int cat_a, cat_b, ch, res, seq_a, seq_b; 494 495 fname_a = (*(const struct dirent * const *)a)->d_name; 496 fname_b = (*(const struct dirent * const *)b)->d_name; 497 498 /* 499 * First separate filenames into cagatories. Catagories are 500 * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in 501 * that order. It is critical that the mapping be exactly the 502 * same for 'a' vs 'b', so define a macro for the job. 503 * 504 * [aside: the standard `cf' file has the jobnumber start in 505 * position 4, but some implementations have that as an extra 506 * file-sequence letter, and start the job number in position 5.] 507 */ 508 #define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ 509 cat_X = cat_other; \ 510 ch = *(fname_X + 2); \ 511 jnum_X = fname_X + 3; \ 512 seq_X = 0; \ 513 if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ 514 seq_X = ch; \ 515 if (*fname_X == 'c') \ 516 cat_X = 1; \ 517 else if (*fname_X == 'd') \ 518 cat_X = 2; \ 519 else if (*fname_X == 'r') \ 520 cat_X = 3; \ 521 else if (*fname_X == 't') \ 522 cat_X = 4; \ 523 if (cat_X != cat_other) { \ 524 ch = *jnum_X; \ 525 if (!isdigit(ch)) { \ 526 if (isalpha(ch)) { \ 527 jnum_X++; \ 528 ch = *jnum_X; \ 529 seq_X = (seq_X << 8) + ch; \ 530 } \ 531 if (!isdigit(ch)) \ 532 cat_X = cat_other; \ 533 } \ 534 } \ 535 } \ 536 } while (0) 537 538 MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); 539 MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); 540 541 #undef MAP_TO_CAT 542 543 /* First handle all cases which have "other" files */ 544 if ((cat_a >= cat_other) || (cat_b >= cat_other)) { 545 /* for two "other" files, just compare the full name */ 546 if (cat_a == cat_b) 547 res = strcmp(fname_a, fname_b); 548 else if (cat_a < cat_b) 549 res = a_lt_b; 550 else 551 res = a_gt_b; 552 goto have_res; 553 } 554 555 /* 556 * At this point, we know both files are legitimate `cf', `df', `rf', 557 * or `tf' files. Compare them by job-number and machine name. 558 */ 559 res = strcmp(jnum_a, jnum_b); 560 if (res != 0) 561 goto have_res; 562 563 /* 564 * We have two files which belong to the same job. Sort based 565 * on the catagory of file (`c' before `d', etc). 566 */ 567 if (cat_a < cat_b) { 568 res = a_lt_b; 569 goto have_res; 570 } else if (cat_a > cat_b) { 571 res = a_gt_b; 572 goto have_res; 573 } 574 575 /* 576 * Two files in the same catagory for a single job. Sort based 577 * on the sequence letter(s). (usually `A' thru `Z', etc). 578 */ 579 if (seq_a < seq_b) { 580 res = a_lt_b; 581 goto have_res; 582 } else if (seq_a > seq_b) { 583 res = a_gt_b; 584 goto have_res; 585 } 586 587 /* 588 * Given that the filenames in a directory are unique, this SHOULD 589 * never happen (unless there are logic errors in this routine). 590 * But if it does happen, we must return "is equal" or the caller 591 * might see inconsistent results in the sorting order, and that 592 * can trigger other problems. 593 */ 594 printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); 595 printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, 596 seq_a, seq_b); 597 res = 0; 598 599 have_res: 600 return res; 601 } 602 603 /* 604 * Remove all spool files and temporaries from the spooling area. 605 * Or, perhaps: 606 * Remove incomplete jobs from spooling area. 607 */ 608 609 void 610 clean_gi(int argc, char *argv[]) 611 { 612 613 /* init some fields before 'clean' is called for each queue */ 614 cln_queuecnt = 0; 615 cln_now = time(NULL); 616 cln_minage = 3600.0; /* only delete files >1h old */ 617 cln_filecnt = 0; 618 cln_sizecnt = 0; 619 cln_debug = 0; 620 cln_testonly = 0; 621 generic_wrapup = &wrapup_clean; 622 623 /* see if there are any options specified before the ptr list */ 624 for (; argc > 0; argc--, argv++) { 625 if (**argv != '-') 626 break; 627 if (strcmp(*argv, "-d") == 0) { 628 /* just an example of an option... */ 629 cln_debug++; 630 *argv = generic_nullarg; /* "erase" it */ 631 } else { 632 printf("Invalid option '%s'\n", *argv); 633 generic_initerr = 1; 634 } 635 } 636 637 return; 638 } 639 640 void 641 tclean_gi(int argc, char *argv[]) 642 { 643 644 /* only difference between 'clean' and 'tclean' is one value */ 645 /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 646 clean_gi(argc, argv); 647 cln_testonly = 1; 648 649 return; 650 } 651 652 void 653 clean_q(struct printer *pp) 654 { 655 char *cp, *cp1, *lp; 656 struct dirent **queue; 657 size_t linerem; 658 int didhead, i, n, nitems, rmcp; 659 660 cln_queuecnt++; 661 662 didhead = 0; 663 if (generic_qselect == QSEL_BYNAME) { 664 printf("%s:\n", pp->printer); 665 didhead = 1; 666 } 667 668 lp = line; 669 cp = pp->spool_dir; 670 while (lp < &line[sizeof(line) - 1]) { 671 if ((*lp++ = *cp++) == 0) 672 break; 673 } 674 lp[-1] = '/'; 675 linerem = sizeof(line) - (lp - line); 676 677 cln_foundcore = 0; 678 seteuid(euid); 679 nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 680 seteuid(uid); 681 if (nitems < 0) { 682 if (!didhead) { 683 printf("%s:\n", pp->printer); 684 didhead = 1; 685 } 686 printf("\tcannot examine spool directory\n"); 687 return; 688 } 689 if (cln_foundcore) { 690 if (!didhead) { 691 printf("%s:\n", pp->printer); 692 didhead = 1; 693 } 694 printf("\t** found a core file in %s !\n", pp->spool_dir); 695 } 696 if (nitems == 0) 697 return; 698 if (!didhead) 699 printf("%s:\n", pp->printer); 700 if (cln_debug) { 701 printf("\t** ----- Sorted list of files being checked:\n"); 702 i = 0; 703 do { 704 cp = queue[i]->d_name; 705 printf("\t** [%3d] = %s\n", i, cp); 706 } while (++i < nitems); 707 printf("\t** ----- end of sorted list\n"); 708 } 709 i = 0; 710 do { 711 cp = queue[i]->d_name; 712 rmcp = 0; 713 if (*cp == 'c') { 714 /* 715 * A control file. Look for matching data-files. 716 */ 717 /* XXX 718 * Note the logic here assumes that the hostname 719 * part of cf-filenames match the hostname part 720 * in df-filenames, and that is not necessarily 721 * true (eg: for multi-homed hosts). This needs 722 * some further thought... 723 */ 724 n = 0; 725 while (i + 1 < nitems) { 726 cp1 = queue[i + 1]->d_name; 727 if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) 728 break; 729 i++; 730 n++; 731 } 732 if (n == 0) { 733 rmcp = 1; 734 } 735 } else if (*cp == 'e') { 736 /* 737 * Must be an errrs or email temp file. 738 */ 739 rmcp = 1; 740 } else { 741 /* 742 * Must be a df with no cf (otherwise, it would have 743 * been skipped above) or an rf or tf file (which can 744 * always be removed if it is old enough). 745 */ 746 rmcp = 1; 747 } 748 if (rmcp) { 749 if (strlen(cp) >= linerem) { 750 printf("\t** internal error: 'line' overflow!\n"); 751 printf("\t** spooldir = %s\n", pp->spool_dir); 752 printf("\t** cp = %s\n", cp); 753 return; 754 } 755 strlcpy(lp, cp, linerem); 756 unlinkf(line); 757 } 758 } while (++i < nitems); 759 } 760 761 static void 762 wrapup_clean(int laststatus __unused) 763 { 764 765 printf("Checked %d queues, and ", cln_queuecnt); 766 if (cln_filecnt < 1) { 767 printf("no cruft was found\n"); 768 return; 769 } 770 if (cln_testonly) { 771 printf("would have "); 772 } 773 printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); 774 } 775 776 static void 777 unlinkf(char *name) 778 { 779 struct stat stbuf; 780 double agemod, agestat; 781 int res; 782 char linkbuf[BUFSIZ]; 783 784 /* 785 * We have to use lstat() instead of stat(), in case this is a df* 786 * "file" which is really a symlink due to 'lpr -s' processing. In 787 * that case, we need to check the last-mod time of the symlink, and 788 * not the file that the symlink is pointed at. 789 */ 790 seteuid(euid); 791 res = lstat(name, &stbuf); 792 seteuid(uid); 793 if (res < 0) { 794 printf("\terror return from stat(%s):\n", name); 795 printf("\t %s\n", strerror(errno)); 796 return; 797 } 798 799 agemod = difftime(cln_now, stbuf.st_mtime); 800 agestat = difftime(cln_now, stbuf.st_ctime); 801 if (cln_debug > 1) { 802 /* this debugging-aid probably is not needed any more... */ 803 printf("\t\t modify age=%g secs, stat age=%g secs\n", 804 agemod, agestat); 805 } 806 if ((agemod <= cln_minage) && (agestat <= cln_minage)) 807 return; 808 809 /* 810 * if this file is a symlink, then find out the target of the 811 * symlink before unlink-ing the file itself 812 */ 813 if (S_ISLNK(stbuf.st_mode)) { 814 seteuid(euid); 815 res = readlink(name, linkbuf, sizeof(linkbuf)); 816 seteuid(uid); 817 if (res < 0) { 818 printf("\terror return from readlink(%s):\n", name); 819 printf("\t %s\n", strerror(errno)); 820 return; 821 } 822 if (res == sizeof(linkbuf)) 823 res--; 824 linkbuf[res] = '\0'; 825 } 826 827 cln_filecnt++; 828 cln_sizecnt += stbuf.st_size; 829 830 if (cln_testonly) { 831 printf("\twould remove %s\n", name); 832 if (S_ISLNK(stbuf.st_mode)) { 833 printf("\t (which is a symlink to %s)\n", linkbuf); 834 } 835 } else { 836 seteuid(euid); 837 res = unlink(name); 838 seteuid(uid); 839 if (res < 0) 840 printf("\tcannot remove %s (!)\n", name); 841 else 842 printf("\tremoved %s\n", name); 843 /* XXX 844 * Note that for a df* file, this code should also check to see 845 * if it is a symlink to some other file, and if the original 846 * lpr command included '-r' ("remove file"). Of course, this 847 * code would not be removing the df* file unless there was no 848 * matching cf* file, and without the cf* file it is currently 849 * impossible to determine if '-r' had been specified... 850 * 851 * As a result of this quandry, we may be leaving behind a 852 * user's file that was supposed to have been removed after 853 * being printed. This may effect services such as CAP or 854 * samba, if they were configured to use 'lpr -r', and if 855 * datafiles are not being properly removed. 856 */ 857 if (S_ISLNK(stbuf.st_mode)) { 858 printf("\t (which was a symlink to %s)\n", linkbuf); 859 } 860 } 861 } 862 863 /* 864 * Enable queuing to the printer (allow lpr to add new jobs to the queue). 865 */ 866 void 867 enable_q(struct printer *pp) 868 { 869 int setres; 870 char lf[MAXPATHLEN]; 871 872 lock_file_name(pp, lf, sizeof lf); 873 printf("%s:\n", pp->printer); 874 875 setres = set_qstate(SQS_ENABLEQ, lf); 876 } 877 878 /* 879 * Disable queuing. 880 */ 881 void 882 disable_q(struct printer *pp) 883 { 884 int setres; 885 char lf[MAXPATHLEN]; 886 887 lock_file_name(pp, lf, sizeof lf); 888 printf("%s:\n", pp->printer); 889 890 setres = set_qstate(SQS_DISABLEQ, lf); 891 } 892 893 /* 894 * Disable queuing and printing and put a message into the status file 895 * (reason for being down). If the user specified `-msg', then use 896 * everything after that as the message for the status file. If the 897 * user did NOT specify `-msg', then the command should take the first 898 * parameter as the printer name, and all remaining parameters as the 899 * message for the status file. (This is to be compatible with the 900 * original definition of 'down', which was implemented long before 901 * `-msg' was around). 902 */ 903 void 904 down_gi(int argc, char *argv[]) 905 { 906 907 /* If `-msg' was specified, then this routine has nothing to do. */ 908 if (generic_msg != NULL) 909 return; 910 911 /* 912 * If the user only gave one parameter, then use a default msg. 913 * (if argc == 1 at this point, then *argv == name of printer). 914 */ 915 if (argc == 1) { 916 generic_msg = strdup("printing disabled\n"); 917 return; 918 } 919 920 /* 921 * The user specified multiple parameters, and did not specify 922 * `-msg'. Build a message from all the parameters after the 923 * first one (and nullify those parameters so generic-processing 924 * will not process them as printer-queue names). 925 */ 926 argc--; 927 argv++; 928 generic_msg = args2line(argc, argv); 929 for (; argc > 0; argc--, argv++) 930 *argv = generic_nullarg; /* "erase" it */ 931 } 932 933 void 934 down_q(struct printer *pp) 935 { 936 int setres; 937 char lf[MAXPATHLEN]; 938 939 lock_file_name(pp, lf, sizeof lf); 940 printf("%s:\n", pp->printer); 941 942 setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf); 943 if (setres >= 0) 944 upstat(pp, generic_msg, 1); 945 } 946 947 /* 948 * Exit lpc 949 */ 950 void 951 quit(int argc __unused, char *argv[] __unused) 952 { 953 exit(0); 954 } 955 956 /* 957 * Kill and restart the daemon. 958 */ 959 void 960 restart_q(struct printer *pp) 961 { 962 int killres, setres, startok; 963 char lf[MAXPATHLEN]; 964 965 lock_file_name(pp, lf, sizeof lf); 966 printf("%s:\n", pp->printer); 967 968 killres = kill_qtask(lf); 969 970 /* 971 * XXX - if the kill worked, we should probably sleep for 972 * a second or so before trying to restart the queue. 973 */ 974 975 /* make sure the queue is set to print jobs */ 976 setres = set_qstate(SQS_STARTP, lf); 977 978 seteuid(euid); 979 startok = startdaemon(pp); 980 seteuid(uid); 981 if (!startok) 982 printf("\tcouldn't restart daemon\n"); 983 else 984 printf("\tdaemon restarted\n"); 985 } 986 987 /* 988 * Set the status message of each queue listed. Requires a "-msg" 989 * parameter to indicate the end of the queue list and start of msg text. 990 */ 991 void 992 setstatus_gi(int argc __unused, char *argv[] __unused) 993 { 994 995 if (generic_msg == NULL) { 996 printf("You must specify '-msg' before the text of the new status message.\n"); 997 generic_initerr = 1; 998 } 999 } 1000 1001 void 1002 setstatus_q(struct printer *pp) 1003 { 1004 char lf[MAXPATHLEN]; 1005 1006 lock_file_name(pp, lf, sizeof lf); 1007 printf("%s:\n", pp->printer); 1008 1009 upstat(pp, generic_msg, 1); 1010 } 1011 1012 /* 1013 * Enable printing on the specified printer and startup the daemon. 1014 */ 1015 void 1016 start_q(struct printer *pp) 1017 { 1018 int setres, startok; 1019 char lf[MAXPATHLEN]; 1020 1021 lock_file_name(pp, lf, sizeof lf); 1022 printf("%s:\n", pp->printer); 1023 1024 setres = set_qstate(SQS_STARTP, lf); 1025 1026 seteuid(euid); 1027 startok = startdaemon(pp); 1028 seteuid(uid); 1029 if (!startok) 1030 printf("\tcouldn't start daemon\n"); 1031 else 1032 printf("\tdaemon started\n"); 1033 seteuid(uid); 1034 } 1035 1036 /* 1037 * Print the status of the printer queue. 1038 */ 1039 void 1040 status(struct printer *pp) 1041 { 1042 struct stat stbuf; 1043 register int fd, i; 1044 register struct dirent *dp; 1045 DIR *dirp; 1046 char file[MAXPATHLEN]; 1047 1048 printf("%s:\n", pp->printer); 1049 lock_file_name(pp, file, sizeof file); 1050 if (stat(file, &stbuf) >= 0) { 1051 printf("\tqueuing is %s\n", 1052 ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 1053 : "enabled")); 1054 printf("\tprinting is %s\n", 1055 ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 1056 : "enabled")); 1057 } else { 1058 printf("\tqueuing is enabled\n"); 1059 printf("\tprinting is enabled\n"); 1060 } 1061 if ((dirp = opendir(pp->spool_dir)) == NULL) { 1062 printf("\tcannot examine spool directory\n"); 1063 return; 1064 } 1065 i = 0; 1066 while ((dp = readdir(dirp)) != NULL) { 1067 if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 1068 i++; 1069 } 1070 closedir(dirp); 1071 if (i == 0) 1072 printf("\tno entries in spool area\n"); 1073 else if (i == 1) 1074 printf("\t1 entry in spool area\n"); 1075 else 1076 printf("\t%d entries in spool area\n", i); 1077 fd = open(file, O_RDONLY); 1078 if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 1079 (void) close(fd); /* unlocks as well */ 1080 printf("\tprinter idle\n"); 1081 return; 1082 } 1083 (void) close(fd); 1084 /* print out the contents of the status file, if it exists */ 1085 status_file_name(pp, file, sizeof file); 1086 fd = open(file, O_RDONLY|O_SHLOCK); 1087 if (fd >= 0) { 1088 (void) fstat(fd, &stbuf); 1089 if (stbuf.st_size > 0) { 1090 putchar('\t'); 1091 while ((i = read(fd, line, sizeof(line))) > 0) 1092 (void) fwrite(line, 1, i, stdout); 1093 } 1094 (void) close(fd); /* unlocks as well */ 1095 } 1096 } 1097 1098 /* 1099 * Stop the specified daemon after completing the current job and disable 1100 * printing. 1101 */ 1102 void 1103 stop_q(struct printer *pp) 1104 { 1105 int setres; 1106 char lf[MAXPATHLEN]; 1107 1108 lock_file_name(pp, lf, sizeof lf); 1109 printf("%s:\n", pp->printer); 1110 1111 setres = set_qstate(SQS_STOPP, lf); 1112 1113 if (setres >= 0) 1114 upstat(pp, "printing disabled\n", 0); 1115 } 1116 1117 struct jobqueue **queue; 1118 int nitems; 1119 time_t mtime; 1120 1121 /* 1122 * Put the specified jobs at the top of printer queue. 1123 */ 1124 void 1125 topq(int argc, char *argv[]) 1126 { 1127 register int i; 1128 struct stat stbuf; 1129 int cmdstatus, changed; 1130 struct printer myprinter, *pp = &myprinter; 1131 1132 if (argc < 3) { 1133 printf("usage: topq printer [jobnum ...] [user ...]\n"); 1134 return; 1135 } 1136 1137 --argc; 1138 ++argv; 1139 init_printer(pp); 1140 cmdstatus = getprintcap(*argv, pp); 1141 switch(cmdstatus) { 1142 default: 1143 fatal(pp, "%s", pcaperr(cmdstatus)); 1144 case PCAPERR_NOTFOUND: 1145 printf("unknown printer %s\n", *argv); 1146 return; 1147 case PCAPERR_TCOPEN: 1148 printf("warning: %s: unresolved tc= reference(s)", *argv); 1149 break; 1150 case PCAPERR_SUCCESS: 1151 break; 1152 } 1153 printf("%s:\n", pp->printer); 1154 1155 seteuid(euid); 1156 if (chdir(pp->spool_dir) < 0) { 1157 printf("\tcannot chdir to %s\n", pp->spool_dir); 1158 goto out; 1159 } 1160 seteuid(uid); 1161 nitems = getq(pp, &queue); 1162 if (nitems == 0) 1163 return; 1164 changed = 0; 1165 mtime = queue[0]->job_time; 1166 for (i = argc; --i; ) { 1167 if (doarg(argv[i]) == 0) { 1168 printf("\tjob %s is not in the queue\n", argv[i]); 1169 continue; 1170 } else 1171 changed++; 1172 } 1173 for (i = 0; i < nitems; i++) 1174 free(queue[i]); 1175 free(queue); 1176 if (!changed) { 1177 printf("\tqueue order unchanged\n"); 1178 return; 1179 } 1180 /* 1181 * Turn on the public execute bit of the lock file to 1182 * get lpd to rebuild the queue after the current job. 1183 */ 1184 seteuid(euid); 1185 if (changed && stat(pp->lock_file, &stbuf) >= 0) 1186 (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 1187 1188 out: 1189 seteuid(uid); 1190 } 1191 1192 /* 1193 * Reposition the job by changing the modification time of 1194 * the control file. 1195 */ 1196 static int 1197 touch(struct jobqueue *jq) 1198 { 1199 struct timeval tvp[2]; 1200 int ret; 1201 1202 tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 1203 tvp[0].tv_usec = tvp[1].tv_usec = 0; 1204 seteuid(euid); 1205 ret = utimes(jq->job_cfname, tvp); 1206 seteuid(uid); 1207 return (ret); 1208 } 1209 1210 /* 1211 * Checks if specified job name is in the printer's queue. 1212 * Returns: negative (-1) if argument name is not in the queue. 1213 */ 1214 static int 1215 doarg(char *job) 1216 { 1217 register struct jobqueue **qq; 1218 register int jobnum, n; 1219 register char *cp, *machine; 1220 int cnt = 0; 1221 FILE *fp; 1222 1223 /* 1224 * Look for a job item consisting of system name, colon, number 1225 * (example: ucbarpa:114) 1226 */ 1227 if ((cp = strchr(job, ':')) != NULL) { 1228 machine = job; 1229 *cp++ = '\0'; 1230 job = cp; 1231 } else 1232 machine = NULL; 1233 1234 /* 1235 * Check for job specified by number (example: 112 or 235ucbarpa). 1236 */ 1237 if (isdigit(*job)) { 1238 jobnum = 0; 1239 do 1240 jobnum = jobnum * 10 + (*job++ - '0'); 1241 while (isdigit(*job)); 1242 for (qq = queue + nitems; --qq >= queue; ) { 1243 n = 0; 1244 for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 1245 n = n * 10 + (*cp++ - '0'); 1246 if (jobnum != n) 1247 continue; 1248 if (*job && strcmp(job, cp) != 0) 1249 continue; 1250 if (machine != NULL && strcmp(machine, cp) != 0) 1251 continue; 1252 if (touch(*qq) == 0) { 1253 printf("\tmoved %s\n", (*qq)->job_cfname); 1254 cnt++; 1255 } 1256 } 1257 return(cnt); 1258 } 1259 /* 1260 * Process item consisting of owner's name (example: henry). 1261 */ 1262 for (qq = queue + nitems; --qq >= queue; ) { 1263 seteuid(euid); 1264 fp = fopen((*qq)->job_cfname, "r"); 1265 seteuid(uid); 1266 if (fp == NULL) 1267 continue; 1268 while (getline(fp) > 0) 1269 if (line[0] == 'P') 1270 break; 1271 (void) fclose(fp); 1272 if (line[0] != 'P' || strcmp(job, line+1) != 0) 1273 continue; 1274 if (touch(*qq) == 0) { 1275 printf("\tmoved %s\n", (*qq)->job_cfname); 1276 cnt++; 1277 } 1278 } 1279 return(cnt); 1280 } 1281 1282 /* 1283 * Enable both queuing & printing, and start printer (undo `down'). 1284 */ 1285 void 1286 up_q(struct printer *pp) 1287 { 1288 int setres, startok; 1289 char lf[MAXPATHLEN]; 1290 1291 lock_file_name(pp, lf, sizeof lf); 1292 printf("%s:\n", pp->printer); 1293 1294 setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); 1295 1296 seteuid(euid); 1297 startok = startdaemon(pp); 1298 seteuid(uid); 1299 if (!startok) 1300 printf("\tcouldn't start daemon\n"); 1301 else 1302 printf("\tdaemon started\n"); 1303 } 1304