1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 /* 43 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; 44 */ 45 static const char rcsid[] = 46 "$FreeBSD$"; 47 #endif /* not lint */ 48 49 /* 50 * lpc -- line printer control program -- commands: 51 */ 52 53 #include <sys/param.h> 54 #include <sys/time.h> 55 #include <sys/stat.h> 56 #include <sys/file.h> 57 58 #include <signal.h> 59 #include <fcntl.h> 60 #include <errno.h> 61 #include <dirent.h> 62 #include <unistd.h> 63 #include <stdlib.h> 64 #include <stdio.h> 65 #include <ctype.h> 66 #include <string.h> 67 #include "lp.h" 68 #include "lp.local.h" 69 #include "lpc.h" 70 #include "extern.h" 71 #include "pathnames.h" 72 73 /* 74 * Return values from kill_qtask(). 75 */ 76 #define KQT_LFERROR -2 77 #define KQT_KILLFAIL -1 78 #define KQT_NODAEMON 0 79 #define KQT_KILLOK 1 80 81 static char *args2line(int argc, char **argv); 82 static int doarg(char *_job); 83 static int doselect(struct dirent *_d); 84 static int kill_qtask(const char *lf); 85 static int sortq(const void *_a, const void *_b); 86 static int touch(struct jobqueue *_jq); 87 static void unlinkf(char *_name); 88 static void upstat(struct printer *_pp, const char *_msg, int _notify); 89 static void wrapup_clean(int _laststatus); 90 91 /* 92 * generic framework for commands which operate on all or a specified 93 * set of printers 94 */ 95 enum qsel_val { /* how a given ptr was selected */ 96 QSEL_UNKNOWN = -1, /* ... not selected yet */ 97 QSEL_BYNAME = 0, /* ... user specifed it by name */ 98 QSEL_ALL = 1 /* ... user wants "all" printers */ 99 /* (with more to come) */ 100 }; 101 102 static enum qsel_val generic_qselect; /* indicates how ptr was selected */ 103 static int generic_initerr; /* result of initrtn processing */ 104 static char *generic_cmdname; 105 static char *generic_msg; /* if a -msg was specified */ 106 static char *generic_nullarg; 107 static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ 108 109 void 110 generic(void (*specificrtn)(struct printer *_pp), int cmdopts, 111 void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) 112 { 113 int cmdstatus, more, targc; 114 struct printer myprinter, *pp; 115 char **margv, **targv; 116 117 if (argc == 1) { 118 /* 119 * Usage needs a special case for 'down': The user must 120 * either include `-msg', or only the first parameter 121 * that they give will be processed as a printer name. 122 */ 123 printf("usage: %s {all | printer ...}", argv[0]); 124 if (strcmp(argv[0], "down") == 0) { 125 printf(" -msg [<text> ...]\n"); 126 printf(" or: down {all | printer} [<text> ...]"); 127 } else if (cmdopts & LPC_MSGOPT) 128 printf(" [-msg <text> ...]"); 129 printf("\n"); 130 return; 131 } 132 133 /* The first argument is the command name. */ 134 generic_cmdname = *argv++; 135 argc--; 136 137 /* 138 * The initialization routine for a command might set a generic 139 * "wrapup" routine, which should be called after processing all 140 * the printers in the command. This might print summary info. 141 * 142 * Note that the initialization routine may also parse (and 143 * nullify) some of the parameters given on the command, leaving 144 * only the parameters which have to do with printer names. 145 */ 146 pp = &myprinter; 147 generic_wrapup = NULL; 148 generic_qselect = QSEL_UNKNOWN; 149 cmdstatus = 0; 150 /* this just needs to be a distinct value of type 'char *' */ 151 if (generic_nullarg == NULL) 152 generic_nullarg = strdup(""); 153 154 /* 155 * Some commands accept a -msg argument, which indicates that 156 * all remaining arguments should be combined into a string. 157 */ 158 generic_msg = NULL; 159 if (cmdopts & LPC_MSGOPT) { 160 targc = argc; 161 targv = argv; 162 for (; targc > 0; targc--, targv++) { 163 if (strcmp(*targv, "-msg") == 0) { 164 argc -= targc; 165 generic_msg = args2line(targc - 1, targv + 1); 166 break; 167 } 168 } 169 } 170 171 /* call initialization routine, if there is one for this cmd */ 172 if (initrtn != NULL) { 173 generic_initerr = 0; 174 (*initrtn)(argc, argv); 175 if (generic_initerr) 176 return; 177 /* 178 * The initrtn may have null'ed out some of the parameters. 179 * Compact the parameter list to remove those nulls, and 180 * correct the arg-count. 181 */ 182 targc = argc; 183 targv = argv; 184 margv = argv; 185 argc = 0; 186 for (; targc > 0; targc--, targv++) { 187 if (*targv != generic_nullarg) { 188 if (targv != margv) 189 *margv = *targv; 190 margv++; 191 argc++; 192 } 193 } 194 } 195 196 if (argc == 1 && strcmp(*argv, "all") == 0) { 197 generic_qselect = QSEL_ALL; 198 more = firstprinter(pp, &cmdstatus); 199 if (cmdstatus) 200 goto looperr; 201 while (more) { 202 (*specificrtn)(pp); 203 do { 204 more = nextprinter(pp, &cmdstatus); 205 looperr: 206 switch (cmdstatus) { 207 case PCAPERR_TCOPEN: 208 printf("warning: %s: unresolved " 209 "tc= reference(s) ", 210 pp->printer); 211 case PCAPERR_SUCCESS: 212 break; 213 default: 214 fatal(pp, "%s", pcaperr(cmdstatus)); 215 } 216 } while (more && cmdstatus); 217 } 218 goto wrapup; 219 } 220 221 generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 222 for (; argc > 0; argc--, argv++) { 223 init_printer(pp); 224 cmdstatus = getprintcap(*argv, pp); 225 switch (cmdstatus) { 226 default: 227 fatal(pp, "%s", pcaperr(cmdstatus)); 228 case PCAPERR_NOTFOUND: 229 printf("unknown printer %s\n", *argv); 230 continue; 231 case PCAPERR_TCOPEN: 232 printf("warning: %s: unresolved tc= reference(s)\n", 233 *argv); 234 break; 235 case PCAPERR_SUCCESS: 236 break; 237 } 238 (*specificrtn)(pp); 239 } 240 241 wrapup: 242 if (generic_wrapup) { 243 (*generic_wrapup)(cmdstatus); 244 } 245 if (generic_msg) 246 free(generic_msg); 247 } 248 249 /* 250 * Convert an argv-array of character strings into a single string. 251 */ 252 static char * 253 args2line(int argc, char **argv) 254 { 255 char *cp1, *cend; 256 const char *cp2; 257 char buf[1024]; 258 259 if (argc <= 0) 260 return strdup("\n"); 261 262 cp1 = buf; 263 cend = buf + sizeof(buf) - 1; /* save room for '\0' */ 264 while (--argc >= 0) { 265 cp2 = *argv++; 266 while ((cp1 < cend) && (*cp1++ = *cp2++)) 267 ; 268 cp1[-1] = ' '; 269 } 270 cp1[-1] = '\n'; 271 *cp1 = '\0'; 272 return strdup(buf); 273 } 274 275 /* 276 * Kill the current daemon, to stop printing of the active job. 277 */ 278 static int 279 kill_qtask(const char *lf) 280 { 281 FILE *fp; 282 pid_t pid; 283 int errsav, killres, lockres, res; 284 285 seteuid(euid); 286 fp = fopen(lf, "r"); 287 errsav = errno; 288 seteuid(uid); 289 res = KQT_NODAEMON; 290 if (fp == NULL) { 291 /* 292 * If there is no lock file, then there is no daemon to 293 * kill. Any other error return means there is some 294 * kind of problem with the lock file. 295 */ 296 if (errsav != ENOENT) 297 res = KQT_LFERROR; 298 goto killdone; 299 } 300 301 /* If the lock file is empty, then there is no daemon to kill */ 302 if (getline(fp) == 0) 303 goto killdone; 304 305 /* 306 * If the file can be locked without blocking, then there 307 * no daemon to kill, or we should not try to kill it. 308 * 309 * XXX - not sure I understand the reasoning behind this... 310 */ 311 lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); 312 (void) fclose(fp); 313 if (lockres == 0) 314 goto killdone; 315 316 pid = atoi(line); 317 if (pid < 0) { 318 /* 319 * If we got a negative pid, then the contents of the 320 * lock file is not valid. 321 */ 322 res = KQT_LFERROR; 323 goto killdone; 324 } 325 326 seteuid(uid); 327 killres = kill(pid, SIGTERM); 328 errsav = errno; 329 seteuid(uid); 330 if (killres == 0) { 331 res = KQT_KILLOK; 332 printf("\tdaemon (pid %d) killed\n", pid); 333 } else if (errno == ESRCH) { 334 res = KQT_NODAEMON; 335 } else { 336 res = KQT_KILLFAIL; 337 printf("\tWarning: daemon (pid %d) not killed:\n", pid); 338 printf("\t %s\n", strerror(errsav)); 339 } 340 341 killdone: 342 switch (res) { 343 case KQT_LFERROR: 344 printf("\tcannot open lock file: %s\n", 345 strerror(errsav)); 346 break; 347 case KQT_NODAEMON: 348 printf("\tno daemon to abort\n"); 349 break; 350 case KQT_KILLFAIL: 351 case KQT_KILLOK: 352 /* These two already printed messages to the user. */ 353 break; 354 default: 355 printf("\t<internal error in kill_qtask>\n"); 356 break; 357 } 358 359 return (res); 360 } 361 362 /* 363 * Write a message into the status file. 364 */ 365 static void 366 upstat(struct printer *pp, const char *msg, int notifyuser) 367 { 368 int fd; 369 char statfile[MAXPATHLEN]; 370 371 status_file_name(pp, statfile, sizeof statfile); 372 umask(0); 373 seteuid(euid); 374 fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 375 seteuid(uid); 376 if (fd < 0) { 377 printf("\tcannot create status file: %s\n", strerror(errno)); 378 return; 379 } 380 (void) ftruncate(fd, 0); 381 if (msg == (char *)NULL) 382 (void) write(fd, "\n", 1); 383 else 384 (void) write(fd, msg, strlen(msg)); 385 (void) close(fd); 386 if (notifyuser) { 387 if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) 388 printf("\tstatus message is now set to nothing.\n"); 389 else 390 printf("\tstatus message is now: %s", msg); 391 } 392 } 393 394 /* 395 * kill an existing daemon and disable printing. 396 */ 397 void 398 abort_q(struct printer *pp) 399 { 400 int killres, setres; 401 char lf[MAXPATHLEN]; 402 403 lock_file_name(pp, lf, sizeof lf); 404 printf("%s:\n", pp->printer); 405 406 /* 407 * Turn on the owner execute bit of the lock file to disable printing. 408 */ 409 setres = set_qstate(SQS_STOPP, lf); 410 411 /* 412 * If set_qstate found that there already was a lock file, then 413 * call a routine which will read that lock file and kill the 414 * lpd-process which is listed in that lock file. If the lock 415 * file did not exist, then either there is no daemon running 416 * for this queue, or there is one running but *it* could not 417 * write a lock file (which means we can not determine the 418 * process id of that lpd-process). 419 */ 420 switch (setres) { 421 case SQS_CHGOK: 422 case SQS_CHGFAIL: 423 /* Kill the process */ 424 killres = kill_qtask(lf); 425 break; 426 case SQS_CREOK: 427 case SQS_CREFAIL: 428 printf("\tno daemon to abort\n"); 429 break; 430 case SQS_STATFAIL: 431 printf("\tassuming no daemon to abort\n"); 432 break; 433 default: 434 printf("\t<unexpected result (%d) from set_qstate>\n", 435 setres); 436 break; 437 } 438 439 if (setres >= 0) 440 upstat(pp, "printing disabled\n", 0); 441 } 442 443 /* 444 * "global" variables for all the routines related to 'clean' and 'tclean' 445 */ 446 static time_t cln_now; /* current time */ 447 static double cln_minage; /* minimum age before file is removed */ 448 static long cln_sizecnt; /* amount of space freed up */ 449 static int cln_debug; /* print extra debugging msgs */ 450 static int cln_filecnt; /* number of files destroyed */ 451 static int cln_foundcore; /* found a core file! */ 452 static int cln_queuecnt; /* number of queues checked */ 453 static int cln_testonly; /* remove-files vs just-print-info */ 454 455 static int 456 doselect(struct dirent *d) 457 { 458 int c = d->d_name[0]; 459 460 if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && 461 d->d_name[1] == 'f') 462 return 1; 463 if (c == 'c') { 464 if (!strcmp(d->d_name, "core")) 465 cln_foundcore = 1; 466 } 467 if (c == 'e') { 468 if (!strncmp(d->d_name, "errs.", 5)) 469 return 1; 470 } 471 return 0; 472 } 473 474 /* 475 * Comparison routine that clean_q() uses for scandir. 476 * 477 * The purpose of this sort is to have all `df' files end up immediately 478 * after the matching `cf' file. For files matching `cf', `df', `rf', or 479 * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or 480 * `tf', and then by the sequence letter (which is A-Z, or a-z). This 481 * routine may also see filenames which do not start with `cf', `df', `rf', 482 * or `tf' (such as `errs.*'), and those are simply sorted by the full 483 * filename. 484 * 485 * XXX 486 * This assumes that all control files start with `cfA*', and it turns 487 * out there are a few implementations of lpr which will create `cfB*' 488 * filenames (they will have datafile names which start with `dfB*'). 489 */ 490 static int 491 sortq(const void *a, const void *b) 492 { 493 const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; 494 const char *fname_a, *fname_b, *jnum_a, *jnum_b; 495 int cat_a, cat_b, ch, res, seq_a, seq_b; 496 497 fname_a = (*(const struct dirent * const *)a)->d_name; 498 fname_b = (*(const struct dirent * const *)b)->d_name; 499 500 /* 501 * First separate filenames into cagatories. Catagories are 502 * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in 503 * that order. It is critical that the mapping be exactly the 504 * same for 'a' vs 'b', so define a macro for the job. 505 * 506 * [aside: the standard `cf' file has the jobnumber start in 507 * position 4, but some implementations have that as an extra 508 * file-sequence letter, and start the job number in position 5.] 509 */ 510 #define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ 511 cat_X = cat_other; \ 512 ch = *(fname_X + 2); \ 513 jnum_X = fname_X + 3; \ 514 seq_X = 0; \ 515 if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ 516 seq_X = ch; \ 517 if (*fname_X == 'c') \ 518 cat_X = 1; \ 519 else if (*fname_X == 'd') \ 520 cat_X = 2; \ 521 else if (*fname_X == 'r') \ 522 cat_X = 3; \ 523 else if (*fname_X == 't') \ 524 cat_X = 4; \ 525 if (cat_X != cat_other) { \ 526 ch = *jnum_X; \ 527 if (!isdigit(ch)) { \ 528 if (isalpha(ch)) { \ 529 jnum_X++; \ 530 ch = *jnum_X; \ 531 seq_X = (seq_X << 8) + ch; \ 532 } \ 533 if (!isdigit(ch)) \ 534 cat_X = cat_other; \ 535 } \ 536 } \ 537 } \ 538 } while (0) 539 540 MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); 541 MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); 542 543 #undef MAP_TO_CAT 544 545 /* First handle all cases which have "other" files */ 546 if ((cat_a >= cat_other) || (cat_b >= cat_other)) { 547 /* for two "other" files, just compare the full name */ 548 if (cat_a == cat_b) 549 res = strcmp(fname_a, fname_b); 550 else if (cat_a < cat_b) 551 res = a_lt_b; 552 else 553 res = a_gt_b; 554 goto have_res; 555 } 556 557 /* 558 * At this point, we know both files are legitimate `cf', `df', `rf', 559 * or `tf' files. Compare them by job-number and machine name. 560 */ 561 res = strcmp(jnum_a, jnum_b); 562 if (res != 0) 563 goto have_res; 564 565 /* 566 * We have two files which belong to the same job. Sort based 567 * on the catagory of file (`c' before `d', etc). 568 */ 569 if (cat_a < cat_b) { 570 res = a_lt_b; 571 goto have_res; 572 } else if (cat_a > cat_b) { 573 res = a_gt_b; 574 goto have_res; 575 } 576 577 /* 578 * Two files in the same catagory for a single job. Sort based 579 * on the sequence letter(s). (usually `A' thru `Z', etc). 580 */ 581 if (seq_a < seq_b) { 582 res = a_lt_b; 583 goto have_res; 584 } else if (seq_a > seq_b) { 585 res = a_gt_b; 586 goto have_res; 587 } 588 589 /* 590 * Given that the filenames in a directory are unique, this SHOULD 591 * never happen (unless there are logic errors in this routine). 592 * But if it does happen, we must return "is equal" or the caller 593 * might see inconsistent results in the sorting order, and that 594 * can trigger other problems. 595 */ 596 printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); 597 printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, 598 seq_a, seq_b); 599 res = 0; 600 601 have_res: 602 return res; 603 } 604 605 /* 606 * Remove all spool files and temporaries from the spooling area. 607 * Or, perhaps: 608 * Remove incomplete jobs from spooling area. 609 */ 610 611 void 612 clean_gi(int argc, char *argv[]) 613 { 614 615 /* init some fields before 'clean' is called for each queue */ 616 cln_queuecnt = 0; 617 cln_now = time(NULL); 618 cln_minage = 3600.0; /* only delete files >1h old */ 619 cln_filecnt = 0; 620 cln_sizecnt = 0; 621 cln_debug = 0; 622 cln_testonly = 0; 623 generic_wrapup = &wrapup_clean; 624 625 /* see if there are any options specified before the ptr list */ 626 for (; argc > 0; argc--, argv++) { 627 if (**argv != '-') 628 break; 629 if (strcmp(*argv, "-d") == 0) { 630 /* just an example of an option... */ 631 cln_debug++; 632 *argv = generic_nullarg; /* "erase" it */ 633 } else { 634 printf("Invalid option '%s'\n", *argv); 635 generic_initerr = 1; 636 } 637 } 638 639 return; 640 } 641 642 void 643 tclean_gi(int argc, char *argv[]) 644 { 645 646 /* only difference between 'clean' and 'tclean' is one value */ 647 /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 648 clean_gi(argc, argv); 649 cln_testonly = 1; 650 651 return; 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 seteuid(euid); 681 nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 682 seteuid(uid); 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 seteuid(euid); 793 res = lstat(name, &stbuf); 794 seteuid(uid); 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 seteuid(euid); 817 res = readlink(name, linkbuf, sizeof(linkbuf)); 818 seteuid(uid); 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 seteuid(euid); 839 res = unlink(name); 840 seteuid(uid); 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 seteuid(euid); 981 startok = startdaemon(pp); 982 seteuid(uid); 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 char lf[MAXPATHLEN]; 1007 1008 lock_file_name(pp, lf, sizeof lf); 1009 printf("%s:\n", pp->printer); 1010 1011 upstat(pp, generic_msg, 1); 1012 } 1013 1014 /* 1015 * Enable printing on the specified printer and startup the daemon. 1016 */ 1017 void 1018 start_q(struct printer *pp) 1019 { 1020 int setres, startok; 1021 char lf[MAXPATHLEN]; 1022 1023 lock_file_name(pp, lf, sizeof lf); 1024 printf("%s:\n", pp->printer); 1025 1026 setres = set_qstate(SQS_STARTP, lf); 1027 1028 seteuid(euid); 1029 startok = startdaemon(pp); 1030 seteuid(uid); 1031 if (!startok) 1032 printf("\tcouldn't start daemon\n"); 1033 else 1034 printf("\tdaemon started\n"); 1035 seteuid(uid); 1036 } 1037 1038 /* 1039 * Print the status of the printer queue. 1040 */ 1041 void 1042 status(struct printer *pp) 1043 { 1044 struct stat stbuf; 1045 register int fd, i; 1046 register struct dirent *dp; 1047 DIR *dirp; 1048 char file[MAXPATHLEN]; 1049 1050 printf("%s:\n", pp->printer); 1051 lock_file_name(pp, file, sizeof file); 1052 if (stat(file, &stbuf) >= 0) { 1053 printf("\tqueuing is %s\n", 1054 ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 1055 : "enabled")); 1056 printf("\tprinting is %s\n", 1057 ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 1058 : "enabled")); 1059 } else { 1060 printf("\tqueuing is enabled\n"); 1061 printf("\tprinting is enabled\n"); 1062 } 1063 if ((dirp = opendir(pp->spool_dir)) == NULL) { 1064 printf("\tcannot examine spool directory\n"); 1065 return; 1066 } 1067 i = 0; 1068 while ((dp = readdir(dirp)) != NULL) { 1069 if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 1070 i++; 1071 } 1072 closedir(dirp); 1073 if (i == 0) 1074 printf("\tno entries in spool area\n"); 1075 else if (i == 1) 1076 printf("\t1 entry in spool area\n"); 1077 else 1078 printf("\t%d entries in spool area\n", i); 1079 fd = open(file, O_RDONLY); 1080 if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 1081 (void) close(fd); /* unlocks as well */ 1082 printf("\tprinter idle\n"); 1083 return; 1084 } 1085 (void) close(fd); 1086 /* print out the contents of the status file, if it exists */ 1087 status_file_name(pp, file, sizeof file); 1088 fd = open(file, O_RDONLY|O_SHLOCK); 1089 if (fd >= 0) { 1090 (void) fstat(fd, &stbuf); 1091 if (stbuf.st_size > 0) { 1092 putchar('\t'); 1093 while ((i = read(fd, line, sizeof(line))) > 0) 1094 (void) fwrite(line, 1, i, stdout); 1095 } 1096 (void) close(fd); /* unlocks as well */ 1097 } 1098 } 1099 1100 /* 1101 * Stop the specified daemon after completing the current job and disable 1102 * printing. 1103 */ 1104 void 1105 stop_q(struct printer *pp) 1106 { 1107 int setres; 1108 char lf[MAXPATHLEN]; 1109 1110 lock_file_name(pp, lf, sizeof lf); 1111 printf("%s:\n", pp->printer); 1112 1113 setres = set_qstate(SQS_STOPP, lf); 1114 1115 if (setres >= 0) 1116 upstat(pp, "printing disabled\n", 0); 1117 } 1118 1119 struct jobqueue **queue; 1120 int nitems; 1121 time_t mtime; 1122 1123 /* 1124 * Put the specified jobs at the top of printer queue. 1125 */ 1126 void 1127 topq(int argc, char *argv[]) 1128 { 1129 register int i; 1130 struct stat stbuf; 1131 int cmdstatus, changed; 1132 struct printer myprinter, *pp = &myprinter; 1133 1134 if (argc < 3) { 1135 printf("usage: topq printer [jobnum ...] [user ...]\n"); 1136 return; 1137 } 1138 1139 --argc; 1140 ++argv; 1141 init_printer(pp); 1142 cmdstatus = getprintcap(*argv, pp); 1143 switch(cmdstatus) { 1144 default: 1145 fatal(pp, "%s", pcaperr(cmdstatus)); 1146 case PCAPERR_NOTFOUND: 1147 printf("unknown printer %s\n", *argv); 1148 return; 1149 case PCAPERR_TCOPEN: 1150 printf("warning: %s: unresolved tc= reference(s)", *argv); 1151 break; 1152 case PCAPERR_SUCCESS: 1153 break; 1154 } 1155 printf("%s:\n", pp->printer); 1156 1157 seteuid(euid); 1158 if (chdir(pp->spool_dir) < 0) { 1159 printf("\tcannot chdir to %s\n", pp->spool_dir); 1160 goto out; 1161 } 1162 seteuid(uid); 1163 nitems = getq(pp, &queue); 1164 if (nitems == 0) 1165 return; 1166 changed = 0; 1167 mtime = queue[0]->job_time; 1168 for (i = argc; --i; ) { 1169 if (doarg(argv[i]) == 0) { 1170 printf("\tjob %s is not in the queue\n", argv[i]); 1171 continue; 1172 } else 1173 changed++; 1174 } 1175 for (i = 0; i < nitems; i++) 1176 free(queue[i]); 1177 free(queue); 1178 if (!changed) { 1179 printf("\tqueue order unchanged\n"); 1180 return; 1181 } 1182 /* 1183 * Turn on the public execute bit of the lock file to 1184 * get lpd to rebuild the queue after the current job. 1185 */ 1186 seteuid(euid); 1187 if (changed && stat(pp->lock_file, &stbuf) >= 0) 1188 (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 1189 1190 out: 1191 seteuid(uid); 1192 } 1193 1194 /* 1195 * Reposition the job by changing the modification time of 1196 * the control file. 1197 */ 1198 static int 1199 touch(struct jobqueue *jq) 1200 { 1201 struct timeval tvp[2]; 1202 int ret; 1203 1204 tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 1205 tvp[0].tv_usec = tvp[1].tv_usec = 0; 1206 seteuid(euid); 1207 ret = utimes(jq->job_cfname, tvp); 1208 seteuid(uid); 1209 return (ret); 1210 } 1211 1212 /* 1213 * Checks if specified job name is in the printer's queue. 1214 * Returns: negative (-1) if argument name is not in the queue. 1215 */ 1216 static int 1217 doarg(char *job) 1218 { 1219 register struct jobqueue **qq; 1220 register int jobnum, n; 1221 register char *cp, *machine; 1222 int cnt = 0; 1223 FILE *fp; 1224 1225 /* 1226 * Look for a job item consisting of system name, colon, number 1227 * (example: ucbarpa:114) 1228 */ 1229 if ((cp = strchr(job, ':')) != NULL) { 1230 machine = job; 1231 *cp++ = '\0'; 1232 job = cp; 1233 } else 1234 machine = NULL; 1235 1236 /* 1237 * Check for job specified by number (example: 112 or 235ucbarpa). 1238 */ 1239 if (isdigit(*job)) { 1240 jobnum = 0; 1241 do 1242 jobnum = jobnum * 10 + (*job++ - '0'); 1243 while (isdigit(*job)); 1244 for (qq = queue + nitems; --qq >= queue; ) { 1245 n = 0; 1246 for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 1247 n = n * 10 + (*cp++ - '0'); 1248 if (jobnum != n) 1249 continue; 1250 if (*job && strcmp(job, cp) != 0) 1251 continue; 1252 if (machine != NULL && strcmp(machine, cp) != 0) 1253 continue; 1254 if (touch(*qq) == 0) { 1255 printf("\tmoved %s\n", (*qq)->job_cfname); 1256 cnt++; 1257 } 1258 } 1259 return(cnt); 1260 } 1261 /* 1262 * Process item consisting of owner's name (example: henry). 1263 */ 1264 for (qq = queue + nitems; --qq >= queue; ) { 1265 seteuid(euid); 1266 fp = fopen((*qq)->job_cfname, "r"); 1267 seteuid(uid); 1268 if (fp == NULL) 1269 continue; 1270 while (getline(fp) > 0) 1271 if (line[0] == 'P') 1272 break; 1273 (void) fclose(fp); 1274 if (line[0] != 'P' || strcmp(job, line+1) != 0) 1275 continue; 1276 if (touch(*qq) == 0) { 1277 printf("\tmoved %s\n", (*qq)->job_cfname); 1278 cnt++; 1279 } 1280 } 1281 return(cnt); 1282 } 1283 1284 /* 1285 * Enable both queuing & printing, and start printer (undo `down'). 1286 */ 1287 void 1288 up_q(struct printer *pp) 1289 { 1290 int setres, startok; 1291 char lf[MAXPATHLEN]; 1292 1293 lock_file_name(pp, lf, sizeof lf); 1294 printf("%s:\n", pp->printer); 1295 1296 setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); 1297 1298 seteuid(euid); 1299 startok = startdaemon(pp); 1300 seteuid(uid); 1301 if (!startok) 1302 printf("\tcouldn't start daemon\n"); 1303 else 1304 printf("\tdaemon started\n"); 1305 } 1306