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