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