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