11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 4. Neither the name of the University nor the names of its contributors 151553Srgrimes * may be used to endorse or promote products derived from this software 161553Srgrimes * without specific prior written permission. 171553Srgrimes * 181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281553Srgrimes * SUCH DAMAGE. 291553Srgrimes */ 301553Srgrimes 311553Srgrimes#ifndef lint 3231492Swollmanstatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 37117599Sgad#if 0 381553Srgrimes#ifndef lint 3915648Sjoergstatic char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; 401553Srgrimes#endif /* not lint */ 41117599Sgad#endif 421553Srgrimes 43117599Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44117599Sgad__FBSDID("$FreeBSD$"); 45117599Sgad 461553Srgrimes/* 471553Srgrimes * lpc -- line printer control program -- commands: 481553Srgrimes */ 491553Srgrimes 501553Srgrimes#include <sys/param.h> 511553Srgrimes#include <sys/time.h> 521553Srgrimes#include <sys/stat.h> 5315648Sjoerg#include <sys/file.h> 541553Srgrimes 551553Srgrimes#include <signal.h> 561553Srgrimes#include <fcntl.h> 57241852Seadler#include <err.h> 581553Srgrimes#include <errno.h> 591553Srgrimes#include <dirent.h> 601553Srgrimes#include <unistd.h> 611553Srgrimes#include <stdlib.h> 621553Srgrimes#include <stdio.h> 631553Srgrimes#include <ctype.h> 641553Srgrimes#include <string.h> 651553Srgrimes#include "lp.h" 661553Srgrimes#include "lp.local.h" 671553Srgrimes#include "lpc.h" 681553Srgrimes#include "extern.h" 691553Srgrimes#include "pathnames.h" 701553Srgrimes 7198152Sgad/* 7298152Sgad * Return values from kill_qtask(). 7398152Sgad */ 7498152Sgad#define KQT_LFERROR -2 7598152Sgad#define KQT_KILLFAIL -1 7698152Sgad#define KQT_NODAEMON 0 7798152Sgad#define KQT_KILLOK 1 7898152Sgad 7998267Sgadstatic char *args2line(int argc, char **argv); 8078146Sgadstatic int doarg(char *_job); 81231723Skevlostatic int doselect(const struct dirent *_d); 8298152Sgadstatic int kill_qtask(const char *lf); 83234244Sdelphijstatic int sortq(const struct dirent **a, const struct dirent **b); 8478146Sgadstatic int touch(struct jobqueue *_jq); 8578146Sgadstatic void unlinkf(char *_name); 8698267Sgadstatic void upstat(struct printer *_pp, const char *_msg, int _notify); 8778750Sgadstatic void wrapup_clean(int _laststatus); 881553Srgrimes 891553Srgrimes/* 9031492Swollman * generic framework for commands which operate on all or a specified 9131492Swollman * set of printers 921553Srgrimes */ 9378750Sgadenum qsel_val { /* how a given ptr was selected */ 9478750Sgad QSEL_UNKNOWN = -1, /* ... not selected yet */ 9578750Sgad QSEL_BYNAME = 0, /* ... user specifed it by name */ 9678750Sgad QSEL_ALL = 1 /* ... user wants "all" printers */ 9778750Sgad /* (with more to come) */ 9878750Sgad}; 9978750Sgad 10078750Sgadstatic enum qsel_val generic_qselect; /* indicates how ptr was selected */ 10178750Sgadstatic int generic_initerr; /* result of initrtn processing */ 10298268Sgadstatic char *generic_cmdname; 10398267Sgadstatic char *generic_msg; /* if a -msg was specified */ 10478750Sgadstatic char *generic_nullarg; 10578750Sgadstatic void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ 10678750Sgad 1071553Srgrimesvoid 10898267Sgadgeneric(void (*specificrtn)(struct printer *_pp), int cmdopts, 10978750Sgad void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) 1101553Srgrimes{ 11178750Sgad int cmdstatus, more, targc; 11278750Sgad struct printer myprinter, *pp; 11398268Sgad char **margv, **targv; 1141553Srgrimes 1151553Srgrimes if (argc == 1) { 11698278Sgad /* 11798278Sgad * Usage needs a special case for 'down': The user must 11898278Sgad * either include `-msg', or only the first parameter 11998278Sgad * that they give will be processed as a printer name. 12098278Sgad */ 12198267Sgad printf("usage: %s {all | printer ...}", argv[0]); 12298278Sgad if (strcmp(argv[0], "down") == 0) { 12398278Sgad printf(" -msg [<text> ...]\n"); 12498278Sgad printf(" or: down {all | printer} [<text> ...]"); 12598278Sgad } else if (cmdopts & LPC_MSGOPT) 12698267Sgad printf(" [-msg <text> ...]"); 12798267Sgad printf("\n"); 1281553Srgrimes return; 1291553Srgrimes } 13078750Sgad 13198268Sgad /* The first argument is the command name. */ 13298268Sgad generic_cmdname = *argv++; 13398268Sgad argc--; 13498268Sgad 13578750Sgad /* 13678750Sgad * The initialization routine for a command might set a generic 13778750Sgad * "wrapup" routine, which should be called after processing all 13878750Sgad * the printers in the command. This might print summary info. 13978750Sgad * 14078750Sgad * Note that the initialization routine may also parse (and 14178750Sgad * nullify) some of the parameters given on the command, leaving 14278750Sgad * only the parameters which have to do with printer names. 14378750Sgad */ 14478750Sgad pp = &myprinter; 14578750Sgad generic_wrapup = NULL; 14678750Sgad generic_qselect = QSEL_UNKNOWN; 14778750Sgad cmdstatus = 0; 14878750Sgad /* this just needs to be a distinct value of type 'char *' */ 14978750Sgad if (generic_nullarg == NULL) 15078750Sgad generic_nullarg = strdup(""); 15178750Sgad 15298267Sgad /* 15398267Sgad * Some commands accept a -msg argument, which indicates that 15498267Sgad * all remaining arguments should be combined into a string. 15598267Sgad */ 15698267Sgad generic_msg = NULL; 15798267Sgad if (cmdopts & LPC_MSGOPT) { 15898267Sgad targc = argc; 15998267Sgad targv = argv; 16098268Sgad for (; targc > 0; targc--, targv++) { 16198267Sgad if (strcmp(*targv, "-msg") == 0) { 16298267Sgad argc -= targc; 16398267Sgad generic_msg = args2line(targc - 1, targv + 1); 16498267Sgad break; 16598267Sgad } 16698267Sgad } 167234824Sgad if (argc < 1) { 168234824Sgad printf("error: No printer name(s) specified before" 169234824Sgad " '-msg'.\n"); 170234824Sgad printf("usage: %s {all | printer ...}", 171234824Sgad generic_cmdname); 172234824Sgad printf(" [-msg <text> ...]\n"); 173234824Sgad return; 174234824Sgad } 17598267Sgad } 17698267Sgad 17778750Sgad /* call initialization routine, if there is one for this cmd */ 17878750Sgad if (initrtn != NULL) { 17978750Sgad generic_initerr = 0; 18078750Sgad (*initrtn)(argc, argv); 18178750Sgad if (generic_initerr) 18278750Sgad return; 18398268Sgad /* 18498268Sgad * The initrtn may have null'ed out some of the parameters. 18598268Sgad * Compact the parameter list to remove those nulls, and 18698268Sgad * correct the arg-count. 18798268Sgad */ 18878750Sgad targc = argc; 18978750Sgad targv = argv; 19098268Sgad margv = argv; 19198268Sgad argc = 0; 19298268Sgad for (; targc > 0; targc--, targv++) { 19398268Sgad if (*targv != generic_nullarg) { 19498268Sgad if (targv != margv) 19598268Sgad *margv = *targv; 19698268Sgad margv++; 19798268Sgad argc++; 19898268Sgad } 19978750Sgad } 20078750Sgad } 20178750Sgad 20298268Sgad if (argc == 1 && strcmp(*argv, "all") == 0) { 20378750Sgad generic_qselect = QSEL_ALL; 20478146Sgad more = firstprinter(pp, &cmdstatus); 20578146Sgad if (cmdstatus) 20631492Swollman goto looperr; 20731492Swollman while (more) { 20878146Sgad (*specificrtn)(pp); 20931492Swollman do { 21078146Sgad more = nextprinter(pp, &cmdstatus); 21131492Swollmanlooperr: 21278146Sgad switch (cmdstatus) { 21331492Swollman case PCAPERR_TCOPEN: 21431492Swollman printf("warning: %s: unresolved " 21531492Swollman "tc= reference(s) ", 21631492Swollman pp->printer); 21731492Swollman case PCAPERR_SUCCESS: 21831492Swollman break; 21931492Swollman default: 22079739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 22131492Swollman } 22278146Sgad } while (more && cmdstatus); 2231553Srgrimes } 22478750Sgad goto wrapup; 2251553Srgrimes } 22678750Sgad 22778750Sgad generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 22898268Sgad for (; argc > 0; argc--, argv++) { 22931492Swollman init_printer(pp); 23078146Sgad cmdstatus = getprintcap(*argv, pp); 23178146Sgad switch (cmdstatus) { 23231492Swollman default: 23379739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 23431492Swollman case PCAPERR_NOTFOUND: 23531492Swollman printf("unknown printer %s\n", *argv); 2361553Srgrimes continue; 23731492Swollman case PCAPERR_TCOPEN: 23831492Swollman printf("warning: %s: unresolved tc= reference(s)\n", 23931492Swollman *argv); 24031492Swollman break; 24131492Swollman case PCAPERR_SUCCESS: 24231492Swollman break; 24331492Swollman } 24478146Sgad (*specificrtn)(pp); 2451553Srgrimes } 24678750Sgad 24778750Sgadwrapup: 24878750Sgad if (generic_wrapup) { 24978750Sgad (*generic_wrapup)(cmdstatus); 25078750Sgad } 25199846Sgad free_printer(pp); 25298267Sgad if (generic_msg) 25398267Sgad free(generic_msg); 25498267Sgad} 25578750Sgad 25698267Sgad/* 25798267Sgad * Convert an argv-array of character strings into a single string. 25898267Sgad */ 25998267Sgadstatic char * 26098267Sgadargs2line(int argc, char **argv) 26198267Sgad{ 26298267Sgad char *cp1, *cend; 26398267Sgad const char *cp2; 26498267Sgad char buf[1024]; 26598267Sgad 26698267Sgad if (argc <= 0) 26798267Sgad return strdup("\n"); 26898267Sgad 26998267Sgad cp1 = buf; 27098267Sgad cend = buf + sizeof(buf) - 1; /* save room for '\0' */ 27198267Sgad while (--argc >= 0) { 27298267Sgad cp2 = *argv++; 27398267Sgad while ((cp1 < cend) && (*cp1++ = *cp2++)) 27498267Sgad ; 27598267Sgad cp1[-1] = ' '; 27698267Sgad } 27798267Sgad cp1[-1] = '\n'; 27898267Sgad *cp1 = '\0'; 27998267Sgad return strdup(buf); 2801553Srgrimes} 2811553Srgrimes 28231492Swollman/* 28398152Sgad * Kill the current daemon, to stop printing of the active job. 28498152Sgad */ 28598152Sgadstatic int 28698152Sgadkill_qtask(const char *lf) 28798152Sgad{ 28898152Sgad FILE *fp; 28998152Sgad pid_t pid; 29098152Sgad int errsav, killres, lockres, res; 29198152Sgad 292241852Seadler PRIV_START 29398152Sgad fp = fopen(lf, "r"); 29498152Sgad errsav = errno; 295241852Seadler PRIV_END 29698152Sgad res = KQT_NODAEMON; 29798152Sgad if (fp == NULL) { 29898152Sgad /* 29998152Sgad * If there is no lock file, then there is no daemon to 30098152Sgad * kill. Any other error return means there is some 30198152Sgad * kind of problem with the lock file. 30298152Sgad */ 30398152Sgad if (errsav != ENOENT) 30498152Sgad res = KQT_LFERROR; 30598152Sgad goto killdone; 30698152Sgad } 30798152Sgad 30898152Sgad /* If the lock file is empty, then there is no daemon to kill */ 30998152Sgad if (getline(fp) == 0) 31098152Sgad goto killdone; 31198152Sgad 31298152Sgad /* 31398152Sgad * If the file can be locked without blocking, then there 31498152Sgad * no daemon to kill, or we should not try to kill it. 31598152Sgad * 31698152Sgad * XXX - not sure I understand the reasoning behind this... 31798152Sgad */ 31898152Sgad lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); 31998152Sgad (void) fclose(fp); 32098152Sgad if (lockres == 0) 32198152Sgad goto killdone; 32298152Sgad 32398152Sgad pid = atoi(line); 32498152Sgad if (pid < 0) { 32598152Sgad /* 32698152Sgad * If we got a negative pid, then the contents of the 32798152Sgad * lock file is not valid. 32898152Sgad */ 32998152Sgad res = KQT_LFERROR; 33098152Sgad goto killdone; 33198152Sgad } 33298152Sgad 333241852Seadler PRIV_END 33498152Sgad killres = kill(pid, SIGTERM); 33598152Sgad errsav = errno; 336241852Seadler PRIV_END 33798152Sgad if (killres == 0) { 33898152Sgad res = KQT_KILLOK; 33998152Sgad printf("\tdaemon (pid %d) killed\n", pid); 34098152Sgad } else if (errno == ESRCH) { 34198152Sgad res = KQT_NODAEMON; 34298152Sgad } else { 34398152Sgad res = KQT_KILLFAIL; 34498152Sgad printf("\tWarning: daemon (pid %d) not killed:\n", pid); 34598152Sgad printf("\t %s\n", strerror(errsav)); 34698152Sgad } 34798152Sgad 34898152Sgadkilldone: 34998152Sgad switch (res) { 35098152Sgad case KQT_LFERROR: 35198152Sgad printf("\tcannot open lock file: %s\n", 35298152Sgad strerror(errsav)); 35398152Sgad break; 35498152Sgad case KQT_NODAEMON: 35598152Sgad printf("\tno daemon to abort\n"); 35698152Sgad break; 35798152Sgad case KQT_KILLFAIL: 35898152Sgad case KQT_KILLOK: 35998152Sgad /* These two already printed messages to the user. */ 36098152Sgad break; 36198152Sgad default: 36298152Sgad printf("\t<internal error in kill_qtask>\n"); 36398152Sgad break; 36498152Sgad } 36598152Sgad 36698152Sgad return (res); 36798152Sgad} 36898152Sgad 36998152Sgad/* 3701553Srgrimes * Write a message into the status file. 3711553Srgrimes */ 3721553Srgrimesstatic void 37398267Sgadupstat(struct printer *pp, const char *msg, int notifyuser) 3741553Srgrimes{ 37598267Sgad int fd; 37627748Simp char statfile[MAXPATHLEN]; 3771553Srgrimes 37831492Swollman status_file_name(pp, statfile, sizeof statfile); 3791553Srgrimes umask(0); 380241852Seadler PRIV_START 38131492Swollman fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 382241852Seadler PRIV_END 38331492Swollman if (fd < 0) { 38431492Swollman printf("\tcannot create status file: %s\n", strerror(errno)); 3851553Srgrimes return; 3861553Srgrimes } 3871553Srgrimes (void) ftruncate(fd, 0); 388231723Skevlo if (msg == NULL) 3891553Srgrimes (void) write(fd, "\n", 1); 3901553Srgrimes else 3911553Srgrimes (void) write(fd, msg, strlen(msg)); 3921553Srgrimes (void) close(fd); 39398267Sgad if (notifyuser) { 39498267Sgad if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) 39598267Sgad printf("\tstatus message is now set to nothing.\n"); 39698267Sgad else 39798267Sgad printf("\tstatus message is now: %s", msg); 39898267Sgad } 3991553Srgrimes} 4001553Srgrimes 40178750Sgad/* 40298152Sgad * kill an existing daemon and disable printing. 40398152Sgad */ 40498152Sgadvoid 40598152Sgadabort_q(struct printer *pp) 40698152Sgad{ 40798152Sgad int killres, setres; 40898152Sgad char lf[MAXPATHLEN]; 40998152Sgad 41098152Sgad lock_file_name(pp, lf, sizeof lf); 41198152Sgad printf("%s:\n", pp->printer); 41298152Sgad 41398152Sgad /* 41498152Sgad * Turn on the owner execute bit of the lock file to disable printing. 41598152Sgad */ 41698152Sgad setres = set_qstate(SQS_STOPP, lf); 41798152Sgad 41898152Sgad /* 41998152Sgad * If set_qstate found that there already was a lock file, then 42098152Sgad * call a routine which will read that lock file and kill the 42198152Sgad * lpd-process which is listed in that lock file. If the lock 42298152Sgad * file did not exist, then either there is no daemon running 42398152Sgad * for this queue, or there is one running but *it* could not 42498152Sgad * write a lock file (which means we can not determine the 42598152Sgad * process id of that lpd-process). 42698152Sgad */ 42798152Sgad switch (setres) { 42898152Sgad case SQS_CHGOK: 42998152Sgad case SQS_CHGFAIL: 43098152Sgad /* Kill the process */ 43198152Sgad killres = kill_qtask(lf); 43298152Sgad break; 43398152Sgad case SQS_CREOK: 43498152Sgad case SQS_CREFAIL: 43598152Sgad printf("\tno daemon to abort\n"); 43698152Sgad break; 43798152Sgad case SQS_STATFAIL: 43898152Sgad printf("\tassuming no daemon to abort\n"); 43998152Sgad break; 44098152Sgad default: 44198152Sgad printf("\t<unexpected result (%d) from set_qstate>\n", 44298152Sgad setres); 44398152Sgad break; 44498152Sgad } 44598152Sgad 44698267Sgad if (setres >= 0) 44798267Sgad upstat(pp, "printing disabled\n", 0); 44898152Sgad} 44998152Sgad 45098152Sgad/* 45178750Sgad * "global" variables for all the routines related to 'clean' and 'tclean' 45278750Sgad */ 45378750Sgadstatic time_t cln_now; /* current time */ 45478750Sgadstatic double cln_minage; /* minimum age before file is removed */ 45578750Sgadstatic long cln_sizecnt; /* amount of space freed up */ 45678750Sgadstatic int cln_debug; /* print extra debugging msgs */ 45778750Sgadstatic int cln_filecnt; /* number of files destroyed */ 45878750Sgadstatic int cln_foundcore; /* found a core file! */ 45978750Sgadstatic int cln_queuecnt; /* number of queues checked */ 46078750Sgadstatic int cln_testonly; /* remove-files vs just-print-info */ 46178750Sgad 4621553Srgrimesstatic int 463231723Skevlodoselect(const struct dirent *d) 4641553Srgrimes{ 4651553Srgrimes int c = d->d_name[0]; 4661553Srgrimes 46787034Sgad if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && 46887034Sgad d->d_name[1] == 'f') 46978750Sgad return 1; 47078750Sgad if (c == 'c') { 47178750Sgad if (!strcmp(d->d_name, "core")) 47278750Sgad cln_foundcore = 1; 47378750Sgad } 47478750Sgad if (c == 'e') { 47578750Sgad if (!strncmp(d->d_name, "errs.", 5)) 47678750Sgad return 1; 47778750Sgad } 47878750Sgad return 0; 4791553Srgrimes} 4801553Srgrimes 4811553Srgrimes/* 48284034Sgad * Comparison routine that clean_q() uses for scandir. 48384034Sgad * 48484034Sgad * The purpose of this sort is to have all `df' files end up immediately 48587034Sgad * after the matching `cf' file. For files matching `cf', `df', `rf', or 48687034Sgad * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or 48787034Sgad * `tf', and then by the sequence letter (which is A-Z, or a-z). This 48887034Sgad * routine may also see filenames which do not start with `cf', `df', `rf', 48987034Sgad * or `tf' (such as `errs.*'), and those are simply sorted by the full 49087034Sgad * filename. 49187034Sgad * 49287034Sgad * XXX 49387034Sgad * This assumes that all control files start with `cfA*', and it turns 49487034Sgad * out there are a few implementations of lpr which will create `cfB*' 49587034Sgad * filenames (they will have datafile names which start with `dfB*'). 4961553Srgrimes */ 4971553Srgrimesstatic int 498234244Sdelphijsortq(const struct dirent **a, const struct dirent **b) 4991553Srgrimes{ 50084034Sgad const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; 50184034Sgad const char *fname_a, *fname_b, *jnum_a, *jnum_b; 50284034Sgad int cat_a, cat_b, ch, res, seq_a, seq_b; 5031553Srgrimes 504234244Sdelphij fname_a = (*a)->d_name; 505234244Sdelphij fname_b = (*b)->d_name; 50684034Sgad 50784034Sgad /* 508228990Suqs * First separate filenames into categories. Categories are 50987034Sgad * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in 51087034Sgad * that order. It is critical that the mapping be exactly the 51187034Sgad * same for 'a' vs 'b', so define a macro for the job. 51284034Sgad * 51384034Sgad * [aside: the standard `cf' file has the jobnumber start in 51484034Sgad * position 4, but some implementations have that as an extra 51584034Sgad * file-sequence letter, and start the job number in position 5.] 51684034Sgad */ 51784034Sgad#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ 51884034Sgad cat_X = cat_other; \ 51984034Sgad ch = *(fname_X + 2); \ 52084034Sgad jnum_X = fname_X + 3; \ 52184691Sgad seq_X = 0; \ 52284034Sgad if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ 52384034Sgad seq_X = ch; \ 52484034Sgad if (*fname_X == 'c') \ 52584034Sgad cat_X = 1; \ 52684034Sgad else if (*fname_X == 'd') \ 52784034Sgad cat_X = 2; \ 52887034Sgad else if (*fname_X == 'r') \ 52987034Sgad cat_X = 3; \ 53084034Sgad else if (*fname_X == 't') \ 53187034Sgad cat_X = 4; \ 53284034Sgad if (cat_X != cat_other) { \ 53384034Sgad ch = *jnum_X; \ 53484034Sgad if (!isdigit(ch)) { \ 53584034Sgad if (isalpha(ch)) { \ 53684034Sgad jnum_X++; \ 53784034Sgad ch = *jnum_X; \ 53884034Sgad seq_X = (seq_X << 8) + ch; \ 53984034Sgad } \ 54084034Sgad if (!isdigit(ch)) \ 54184034Sgad cat_X = cat_other; \ 54284034Sgad } \ 54384034Sgad } \ 54484034Sgad } \ 54584034Sgad} while (0) 54684034Sgad 54784034Sgad MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); 54884034Sgad MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); 54984034Sgad 55084034Sgad#undef MAP_TO_CAT 55184034Sgad 55284034Sgad /* First handle all cases which have "other" files */ 55384034Sgad if ((cat_a >= cat_other) || (cat_b >= cat_other)) { 55484034Sgad /* for two "other" files, just compare the full name */ 55584034Sgad if (cat_a == cat_b) 55684034Sgad res = strcmp(fname_a, fname_b); 55784034Sgad else if (cat_a < cat_b) 55884034Sgad res = a_lt_b; 55984034Sgad else 56084034Sgad res = a_gt_b; 56184034Sgad goto have_res; 56284034Sgad } 56384034Sgad 56484034Sgad /* 56587034Sgad * At this point, we know both files are legitimate `cf', `df', `rf', 56684034Sgad * or `tf' files. Compare them by job-number and machine name. 56784034Sgad */ 56884034Sgad res = strcmp(jnum_a, jnum_b); 56984034Sgad if (res != 0) 57084034Sgad goto have_res; 57184034Sgad 57284034Sgad /* 57384034Sgad * We have two files which belong to the same job. Sort based 574228990Suqs * on the category of file (`c' before `d', etc). 57584034Sgad */ 57684034Sgad if (cat_a < cat_b) { 57784034Sgad res = a_lt_b; 57884034Sgad goto have_res; 57984034Sgad } else if (cat_a > cat_b) { 58084034Sgad res = a_gt_b; 58184034Sgad goto have_res; 58284034Sgad } 58384034Sgad 58484034Sgad /* 585228990Suqs * Two files in the same category for a single job. Sort based 586228990Suqs * on the sequence letter(s). (usually `A' through `Z', etc). 58784034Sgad */ 58884034Sgad if (seq_a < seq_b) { 58984034Sgad res = a_lt_b; 59084034Sgad goto have_res; 59184034Sgad } else if (seq_a > seq_b) { 59284034Sgad res = a_gt_b; 59384034Sgad goto have_res; 59484034Sgad } 59584034Sgad 59684034Sgad /* 59784034Sgad * Given that the filenames in a directory are unique, this SHOULD 59884034Sgad * never happen (unless there are logic errors in this routine). 59984034Sgad * But if it does happen, we must return "is equal" or the caller 60084034Sgad * might see inconsistent results in the sorting order, and that 60184034Sgad * can trigger other problems. 60284034Sgad */ 60384034Sgad printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); 60484034Sgad printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, 60584034Sgad seq_a, seq_b); 60684034Sgad res = 0; 60784034Sgad 60884034Sgadhave_res: 60984034Sgad return res; 6101553Srgrimes} 6111553Srgrimes 6121553Srgrimes/* 61331492Swollman * Remove all spool files and temporaries from the spooling area. 61431492Swollman * Or, perhaps: 6151553Srgrimes * Remove incomplete jobs from spooling area. 6161553Srgrimes */ 61778750Sgad 61831492Swollmanvoid 61998279Sgadclean_gi(int argc, char *argv[]) 6201553Srgrimes{ 62178750Sgad 62278750Sgad /* init some fields before 'clean' is called for each queue */ 62378750Sgad cln_queuecnt = 0; 62478750Sgad cln_now = time(NULL); 62578750Sgad cln_minage = 3600.0; /* only delete files >1h old */ 62678750Sgad cln_filecnt = 0; 62778750Sgad cln_sizecnt = 0; 62878750Sgad cln_debug = 0; 62978750Sgad cln_testonly = 0; 63078750Sgad generic_wrapup = &wrapup_clean; 63178750Sgad 63278750Sgad /* see if there are any options specified before the ptr list */ 63398268Sgad for (; argc > 0; argc--, argv++) { 63478750Sgad if (**argv != '-') 63578750Sgad break; 63678750Sgad if (strcmp(*argv, "-d") == 0) { 63778750Sgad /* just an example of an option... */ 63884034Sgad cln_debug++; 63978750Sgad *argv = generic_nullarg; /* "erase" it */ 64078750Sgad } else { 64178750Sgad printf("Invalid option '%s'\n", *argv); 64278750Sgad generic_initerr = 1; 64378750Sgad } 64478750Sgad } 64578750Sgad 64678750Sgad return; 64778750Sgad} 64878750Sgad 64978750Sgadvoid 65098279Sgadtclean_gi(int argc, char *argv[]) 65178750Sgad{ 65278750Sgad 65378750Sgad /* only difference between 'clean' and 'tclean' is one value */ 65478750Sgad /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 65598279Sgad clean_gi(argc, argv); 65678750Sgad cln_testonly = 1; 65778750Sgad 65878750Sgad return; 65978750Sgad} 66078750Sgad 66178750Sgadvoid 66278750Sgadclean_q(struct printer *pp) 66378750Sgad{ 66478750Sgad char *cp, *cp1, *lp; 6651553Srgrimes struct dirent **queue; 66678750Sgad size_t linerem; 66778750Sgad int didhead, i, n, nitems, rmcp; 6681553Srgrimes 66978750Sgad cln_queuecnt++; 6701553Srgrimes 67178750Sgad didhead = 0; 67278750Sgad if (generic_qselect == QSEL_BYNAME) { 67378750Sgad printf("%s:\n", pp->printer); 67478750Sgad didhead = 1; 67578750Sgad } 67678750Sgad 67731492Swollman lp = line; 67831492Swollman cp = pp->spool_dir; 67931492Swollman while (lp < &line[sizeof(line) - 1]) { 68031492Swollman if ((*lp++ = *cp++) == 0) 68131492Swollman break; 68231492Swollman } 6831553Srgrimes lp[-1] = '/'; 68478750Sgad linerem = sizeof(line) - (lp - line); 6851553Srgrimes 68678750Sgad cln_foundcore = 0; 687241852Seadler PRIV_START 68831492Swollman nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 689241852Seadler PRIV_END 6901553Srgrimes if (nitems < 0) { 69178750Sgad if (!didhead) { 69278750Sgad printf("%s:\n", pp->printer); 69378750Sgad didhead = 1; 69478750Sgad } 6951553Srgrimes printf("\tcannot examine spool directory\n"); 6961553Srgrimes return; 6971553Srgrimes } 69878750Sgad if (cln_foundcore) { 69978750Sgad if (!didhead) { 70078750Sgad printf("%s:\n", pp->printer); 70178750Sgad didhead = 1; 70278750Sgad } 70378750Sgad printf("\t** found a core file in %s !\n", pp->spool_dir); 70478750Sgad } 7051553Srgrimes if (nitems == 0) 7061553Srgrimes return; 70778750Sgad if (!didhead) 70878750Sgad printf("%s:\n", pp->printer); 70984034Sgad if (cln_debug) { 71084034Sgad printf("\t** ----- Sorted list of files being checked:\n"); 71184034Sgad i = 0; 71284034Sgad do { 71384034Sgad cp = queue[i]->d_name; 71484034Sgad printf("\t** [%3d] = %s\n", i, cp); 71584034Sgad } while (++i < nitems); 71684034Sgad printf("\t** ----- end of sorted list\n"); 71784034Sgad } 7181553Srgrimes i = 0; 7191553Srgrimes do { 7201553Srgrimes cp = queue[i]->d_name; 72178750Sgad rmcp = 0; 7221553Srgrimes if (*cp == 'c') { 72378750Sgad /* 72478750Sgad * A control file. Look for matching data-files. 72578750Sgad */ 72678750Sgad /* XXX 72778750Sgad * Note the logic here assumes that the hostname 72878750Sgad * part of cf-filenames match the hostname part 72978750Sgad * in df-filenames, and that is not necessarily 73078750Sgad * true (eg: for multi-homed hosts). This needs 73178750Sgad * some further thought... 73278750Sgad */ 7331553Srgrimes n = 0; 7341553Srgrimes while (i + 1 < nitems) { 7351553Srgrimes cp1 = queue[i + 1]->d_name; 7361553Srgrimes if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) 7371553Srgrimes break; 7381553Srgrimes i++; 7391553Srgrimes n++; 7401553Srgrimes } 7411553Srgrimes if (n == 0) { 74278750Sgad rmcp = 1; 7431553Srgrimes } 74478750Sgad } else if (*cp == 'e') { 74578750Sgad /* 74678750Sgad * Must be an errrs or email temp file. 74778750Sgad */ 74878750Sgad rmcp = 1; 7491553Srgrimes } else { 7501553Srgrimes /* 7511553Srgrimes * Must be a df with no cf (otherwise, it would have 75287034Sgad * been skipped above) or an rf or tf file (which can 75387034Sgad * always be removed if it is old enough). 7541553Srgrimes */ 75578750Sgad rmcp = 1; 75678750Sgad } 75778750Sgad if (rmcp) { 75878750Sgad if (strlen(cp) >= linerem) { 75978750Sgad printf("\t** internal error: 'line' overflow!\n"); 76078750Sgad printf("\t** spooldir = %s\n", pp->spool_dir); 76178750Sgad printf("\t** cp = %s\n", cp); 76278750Sgad return; 76378750Sgad } 76478750Sgad strlcpy(lp, cp, linerem); 7651553Srgrimes unlinkf(line); 7661553Srgrimes } 7671553Srgrimes } while (++i < nitems); 7681553Srgrimes} 76978750Sgad 77078750Sgadstatic void 77178750Sgadwrapup_clean(int laststatus __unused) 77278750Sgad{ 77378750Sgad 77478750Sgad printf("Checked %d queues, and ", cln_queuecnt); 77578750Sgad if (cln_filecnt < 1) { 77678750Sgad printf("no cruft was found\n"); 77778750Sgad return; 77878750Sgad } 77978750Sgad if (cln_testonly) { 78078750Sgad printf("would have "); 78178750Sgad } 78278750Sgad printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); 78378750Sgad} 78427618Simp 7851553Srgrimesstatic void 78678146Sgadunlinkf(char *name) 7871553Srgrimes{ 78878750Sgad struct stat stbuf; 78978750Sgad double agemod, agestat; 79078750Sgad int res; 79178750Sgad char linkbuf[BUFSIZ]; 79278750Sgad 79378750Sgad /* 79478750Sgad * We have to use lstat() instead of stat(), in case this is a df* 79578750Sgad * "file" which is really a symlink due to 'lpr -s' processing. In 79678750Sgad * that case, we need to check the last-mod time of the symlink, and 79778750Sgad * not the file that the symlink is pointed at. 79878750Sgad */ 799241852Seadler PRIV_START 80078750Sgad res = lstat(name, &stbuf); 801241852Seadler PRIV_END 80278750Sgad if (res < 0) { 80378750Sgad printf("\terror return from stat(%s):\n", name); 80478750Sgad printf("\t %s\n", strerror(errno)); 80578750Sgad return; 80678750Sgad } 80778750Sgad 80878750Sgad agemod = difftime(cln_now, stbuf.st_mtime); 80978750Sgad agestat = difftime(cln_now, stbuf.st_ctime); 81084034Sgad if (cln_debug > 1) { 81178750Sgad /* this debugging-aid probably is not needed any more... */ 81278750Sgad printf("\t\t modify age=%g secs, stat age=%g secs\n", 81378750Sgad agemod, agestat); 81478750Sgad } 81578750Sgad if ((agemod <= cln_minage) && (agestat <= cln_minage)) 81678750Sgad return; 81778750Sgad 81878750Sgad /* 81978750Sgad * if this file is a symlink, then find out the target of the 82078750Sgad * symlink before unlink-ing the file itself 82178750Sgad */ 82278750Sgad if (S_ISLNK(stbuf.st_mode)) { 823241852Seadler PRIV_START 82478750Sgad res = readlink(name, linkbuf, sizeof(linkbuf)); 825241852Seadler PRIV_END 82678750Sgad if (res < 0) { 82778750Sgad printf("\terror return from readlink(%s):\n", name); 82878750Sgad printf("\t %s\n", strerror(errno)); 82978750Sgad return; 83078750Sgad } 83178750Sgad if (res == sizeof(linkbuf)) 83278750Sgad res--; 83378750Sgad linkbuf[res] = '\0'; 83478750Sgad } 83578750Sgad 83678750Sgad cln_filecnt++; 83778750Sgad cln_sizecnt += stbuf.st_size; 83878750Sgad 83978750Sgad if (cln_testonly) { 84078750Sgad printf("\twould remove %s\n", name); 84178750Sgad if (S_ISLNK(stbuf.st_mode)) { 84278750Sgad printf("\t (which is a symlink to %s)\n", linkbuf); 84378750Sgad } 84478750Sgad } else { 845241852Seadler PRIV_START 84678750Sgad res = unlink(name); 847241852Seadler PRIV_END 84878750Sgad if (res < 0) 84978750Sgad printf("\tcannot remove %s (!)\n", name); 85078750Sgad else 85178750Sgad printf("\tremoved %s\n", name); 85278750Sgad /* XXX 85378750Sgad * Note that for a df* file, this code should also check to see 85478750Sgad * if it is a symlink to some other file, and if the original 85578750Sgad * lpr command included '-r' ("remove file"). Of course, this 85678750Sgad * code would not be removing the df* file unless there was no 85778750Sgad * matching cf* file, and without the cf* file it is currently 85878750Sgad * impossible to determine if '-r' had been specified... 85978750Sgad * 86078750Sgad * As a result of this quandry, we may be leaving behind a 86178750Sgad * user's file that was supposed to have been removed after 86278750Sgad * being printed. This may effect services such as CAP or 86378750Sgad * samba, if they were configured to use 'lpr -r', and if 86478750Sgad * datafiles are not being properly removed. 86578750Sgad */ 86678750Sgad if (S_ISLNK(stbuf.st_mode)) { 86778750Sgad printf("\t (which was a symlink to %s)\n", linkbuf); 86878750Sgad } 86978750Sgad } 8701553Srgrimes} 8711553Srgrimes 8721553Srgrimes/* 87398152Sgad * Enable queuing to the printer (allow lpr to add new jobs to the queue). 87498152Sgad */ 87598152Sgadvoid 87698152Sgadenable_q(struct printer *pp) 87798152Sgad{ 87898152Sgad int setres; 87998152Sgad char lf[MAXPATHLEN]; 88098152Sgad 88198152Sgad lock_file_name(pp, lf, sizeof lf); 88298152Sgad printf("%s:\n", pp->printer); 88398152Sgad 88498152Sgad setres = set_qstate(SQS_ENABLEQ, lf); 88598152Sgad} 88698152Sgad 88798152Sgad/* 8881553Srgrimes * Disable queuing. 8891553Srgrimes */ 8901553Srgrimesvoid 89198152Sgaddisable_q(struct printer *pp) 89298152Sgad{ 89398152Sgad int setres; 89498152Sgad char lf[MAXPATHLEN]; 89598152Sgad 89698152Sgad lock_file_name(pp, lf, sizeof lf); 89798152Sgad printf("%s:\n", pp->printer); 89898152Sgad 89998152Sgad setres = set_qstate(SQS_DISABLEQ, lf); 90098152Sgad} 90198152Sgad 90298152Sgad/* 9031553Srgrimes * Disable queuing and printing and put a message into the status file 90498278Sgad * (reason for being down). If the user specified `-msg', then use 90598278Sgad * everything after that as the message for the status file. If the 90698278Sgad * user did NOT specify `-msg', then the command should take the first 90798278Sgad * parameter as the printer name, and all remaining parameters as the 90898278Sgad * message for the status file. (This is to be compatible with the 90998278Sgad * original definition of 'down', which was implemented long before 91098278Sgad * `-msg' was around). 91198278Sgad */ 91298278Sgadvoid 91398278Sgaddown_gi(int argc, char *argv[]) 91498278Sgad{ 91598278Sgad 91698278Sgad /* If `-msg' was specified, then this routine has nothing to do. */ 91798278Sgad if (generic_msg != NULL) 91898278Sgad return; 91998278Sgad 92098278Sgad /* 92198278Sgad * If the user only gave one parameter, then use a default msg. 92298278Sgad * (if argc == 1 at this point, then *argv == name of printer). 92398278Sgad */ 92498278Sgad if (argc == 1) { 92598278Sgad generic_msg = strdup("printing disabled\n"); 92698278Sgad return; 92798278Sgad } 92898278Sgad 92998278Sgad /* 93098278Sgad * The user specified multiple parameters, and did not specify 93198278Sgad * `-msg'. Build a message from all the parameters after the 93298278Sgad * first one (and nullify those parameters so generic-processing 93398278Sgad * will not process them as printer-queue names). 93498278Sgad */ 93598278Sgad argc--; 93698278Sgad argv++; 93798278Sgad generic_msg = args2line(argc, argv); 93898278Sgad for (; argc > 0; argc--, argv++) 93998278Sgad *argv = generic_nullarg; /* "erase" it */ 94098278Sgad} 94198278Sgad 94298278Sgadvoid 94398278Sgaddown_q(struct printer *pp) 94498278Sgad{ 94598278Sgad int setres; 94698278Sgad char lf[MAXPATHLEN]; 94798278Sgad 94898278Sgad lock_file_name(pp, lf, sizeof lf); 94998278Sgad printf("%s:\n", pp->printer); 95098278Sgad 95198278Sgad setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf); 95298278Sgad if (setres >= 0) 95398278Sgad upstat(pp, generic_msg, 1); 95498278Sgad} 95598278Sgad 95698278Sgad/* 9571553Srgrimes * Exit lpc 9581553Srgrimes */ 9591553Srgrimesvoid 96078146Sgadquit(int argc __unused, char *argv[] __unused) 9611553Srgrimes{ 9621553Srgrimes exit(0); 9631553Srgrimes} 9641553Srgrimes 9651553Srgrimes/* 9661553Srgrimes * Kill and restart the daemon. 9671553Srgrimes */ 9681553Srgrimesvoid 96998152Sgadrestart_q(struct printer *pp) 97098152Sgad{ 97198152Sgad int killres, setres, startok; 97298152Sgad char lf[MAXPATHLEN]; 97398152Sgad 97498152Sgad lock_file_name(pp, lf, sizeof lf); 97598152Sgad printf("%s:\n", pp->printer); 97698152Sgad 97798152Sgad killres = kill_qtask(lf); 97898152Sgad 97998152Sgad /* 98098152Sgad * XXX - if the kill worked, we should probably sleep for 98198152Sgad * a second or so before trying to restart the queue. 98298152Sgad */ 98398152Sgad 98498152Sgad /* make sure the queue is set to print jobs */ 98598152Sgad setres = set_qstate(SQS_STARTP, lf); 98698152Sgad 987241852Seadler PRIV_START 98898152Sgad startok = startdaemon(pp); 989241852Seadler PRIV_END 99098152Sgad if (!startok) 99198152Sgad printf("\tcouldn't restart daemon\n"); 99298152Sgad else 99398152Sgad printf("\tdaemon restarted\n"); 99498152Sgad} 99598152Sgad 99698152Sgad/* 99798267Sgad * Set the status message of each queue listed. Requires a "-msg" 99898267Sgad * parameter to indicate the end of the queue list and start of msg text. 99998267Sgad */ 100098267Sgadvoid 100198267Sgadsetstatus_gi(int argc __unused, char *argv[] __unused) 100298267Sgad{ 100398267Sgad 100498267Sgad if (generic_msg == NULL) { 100598267Sgad printf("You must specify '-msg' before the text of the new status message.\n"); 100698267Sgad generic_initerr = 1; 100798267Sgad } 100898267Sgad} 100998267Sgad 101098267Sgadvoid 101198267Sgadsetstatus_q(struct printer *pp) 101298267Sgad{ 1013234826Sgad struct stat stbuf; 1014234826Sgad int not_shown; 101598267Sgad char lf[MAXPATHLEN]; 101698267Sgad 101798267Sgad lock_file_name(pp, lf, sizeof lf); 101898267Sgad printf("%s:\n", pp->printer); 101998267Sgad 102098267Sgad upstat(pp, generic_msg, 1); 1021234826Sgad 1022234826Sgad /* 1023234826Sgad * Warn the user if 'lpq' will not display this new status-message. 1024234826Sgad * Note that if lock file does not exist, then the queue is enabled 1025234826Sgad * for both queuing and printing. 1026234826Sgad */ 1027234826Sgad not_shown = 1; 1028234826Sgad if (stat(lf, &stbuf) >= 0) { 1029234826Sgad if (stbuf.st_mode & LFM_PRINT_DIS) 1030234826Sgad not_shown = 0; 1031234826Sgad } 1032234826Sgad if (not_shown) { 1033234826Sgad printf("\tnote: This queue currently has printing enabled,\n"); 1034234826Sgad printf("\t so this -msg will only be shown by 'lpq' if\n"); 1035234826Sgad printf("\t a job is actively printing on it.\n"); 1036234826Sgad } 103798267Sgad} 103898267Sgad 103998267Sgad/* 10401553Srgrimes * Enable printing on the specified printer and startup the daemon. 10411553Srgrimes */ 10421553Srgrimesvoid 104398152Sgadstart_q(struct printer *pp) 104498152Sgad{ 104598152Sgad int setres, startok; 104698152Sgad char lf[MAXPATHLEN]; 104798152Sgad 104898152Sgad lock_file_name(pp, lf, sizeof lf); 104998152Sgad printf("%s:\n", pp->printer); 105098152Sgad 105198152Sgad setres = set_qstate(SQS_STARTP, lf); 105298152Sgad 1053241852Seadler PRIV_START 105498152Sgad startok = startdaemon(pp); 1055241852Seadler PRIV_END 105698152Sgad if (!startok) 105798152Sgad printf("\tcouldn't start daemon\n"); 105898152Sgad else 105998152Sgad printf("\tdaemon started\n"); 1060241852Seadler PRIV_END 106198152Sgad} 106298152Sgad 106398152Sgad/* 106431492Swollman * Print the status of the printer queue. 10651553Srgrimes */ 10661553Srgrimesvoid 106778146Sgadstatus(struct printer *pp) 10681553Srgrimes{ 10691553Srgrimes struct stat stbuf; 10701553Srgrimes register int fd, i; 10711553Srgrimes register struct dirent *dp; 10721553Srgrimes DIR *dirp; 107331492Swollman char file[MAXPATHLEN]; 10741553Srgrimes 107531492Swollman printf("%s:\n", pp->printer); 107631492Swollman lock_file_name(pp, file, sizeof file); 107731492Swollman if (stat(file, &stbuf) >= 0) { 10781553Srgrimes printf("\tqueuing is %s\n", 107931492Swollman ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 108031492Swollman : "enabled")); 10811553Srgrimes printf("\tprinting is %s\n", 108231492Swollman ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 108331492Swollman : "enabled")); 10841553Srgrimes } else { 10851553Srgrimes printf("\tqueuing is enabled\n"); 10861553Srgrimes printf("\tprinting is enabled\n"); 10871553Srgrimes } 108831492Swollman if ((dirp = opendir(pp->spool_dir)) == NULL) { 10891553Srgrimes printf("\tcannot examine spool directory\n"); 10901553Srgrimes return; 10911553Srgrimes } 10921553Srgrimes i = 0; 10931553Srgrimes while ((dp = readdir(dirp)) != NULL) { 10941553Srgrimes if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 10951553Srgrimes i++; 10961553Srgrimes } 10971553Srgrimes closedir(dirp); 10981553Srgrimes if (i == 0) 109931492Swollman printf("\tno entries in spool area\n"); 11001553Srgrimes else if (i == 1) 11011553Srgrimes printf("\t1 entry in spool area\n"); 11021553Srgrimes else 11031553Srgrimes printf("\t%d entries in spool area\n", i); 110431492Swollman fd = open(file, O_RDONLY); 11051553Srgrimes if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 11061553Srgrimes (void) close(fd); /* unlocks as well */ 110719202Simp printf("\tprinter idle\n"); 11081553Srgrimes return; 11091553Srgrimes } 11101553Srgrimes (void) close(fd); 111125789Sbrian /* print out the contents of the status file, if it exists */ 111231492Swollman status_file_name(pp, file, sizeof file); 111331492Swollman fd = open(file, O_RDONLY|O_SHLOCK); 11141553Srgrimes if (fd >= 0) { 111525789Sbrian (void) fstat(fd, &stbuf); 111625789Sbrian if (stbuf.st_size > 0) { 111725789Sbrian putchar('\t'); 111825789Sbrian while ((i = read(fd, line, sizeof(line))) > 0) 111925789Sbrian (void) fwrite(line, 1, i, stdout); 112025789Sbrian } 11211553Srgrimes (void) close(fd); /* unlocks as well */ 11221553Srgrimes } 11231553Srgrimes} 11241553Srgrimes 11251553Srgrimes/* 11261553Srgrimes * Stop the specified daemon after completing the current job and disable 11271553Srgrimes * printing. 11281553Srgrimes */ 11291553Srgrimesvoid 113098152Sgadstop_q(struct printer *pp) 113198152Sgad{ 113298152Sgad int setres; 113398152Sgad char lf[MAXPATHLEN]; 113498152Sgad 113598152Sgad lock_file_name(pp, lf, sizeof lf); 113698152Sgad printf("%s:\n", pp->printer); 113798152Sgad 113898152Sgad setres = set_qstate(SQS_STOPP, lf); 113998152Sgad 114098267Sgad if (setres >= 0) 114198267Sgad upstat(pp, "printing disabled\n", 0); 114298152Sgad} 114398152Sgad 114468401Sgadstruct jobqueue **queue; 11451553Srgrimesint nitems; 11461553Srgrimestime_t mtime; 11471553Srgrimes 11481553Srgrimes/* 11491553Srgrimes * Put the specified jobs at the top of printer queue. 11501553Srgrimes */ 11511553Srgrimesvoid 115278146Sgadtopq(int argc, char *argv[]) 11531553Srgrimes{ 11541553Srgrimes register int i; 11551553Srgrimes struct stat stbuf; 115678146Sgad int cmdstatus, changed; 115731492Swollman struct printer myprinter, *pp = &myprinter; 11581553Srgrimes 11591553Srgrimes if (argc < 3) { 116095258Sdes printf("usage: topq printer [jobnum ...] [user ...]\n"); 11611553Srgrimes return; 11621553Srgrimes } 11631553Srgrimes 11641553Srgrimes --argc; 116531492Swollman ++argv; 116631492Swollman init_printer(pp); 116778146Sgad cmdstatus = getprintcap(*argv, pp); 116878146Sgad switch(cmdstatus) { 116931492Swollman default: 117079739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 117131492Swollman case PCAPERR_NOTFOUND: 117231492Swollman printf("unknown printer %s\n", *argv); 11731553Srgrimes return; 117431492Swollman case PCAPERR_TCOPEN: 117531492Swollman printf("warning: %s: unresolved tc= reference(s)", *argv); 117631492Swollman break; 117731492Swollman case PCAPERR_SUCCESS: 117831492Swollman break; 117931492Swollman } 118031492Swollman printf("%s:\n", pp->printer); 11811553Srgrimes 1182241852Seadler PRIV_START 118331492Swollman if (chdir(pp->spool_dir) < 0) { 118431492Swollman printf("\tcannot chdir to %s\n", pp->spool_dir); 118527618Simp goto out; 11861553Srgrimes } 1187241852Seadler PRIV_END 118831492Swollman nitems = getq(pp, &queue); 11891553Srgrimes if (nitems == 0) 11901553Srgrimes return; 11911553Srgrimes changed = 0; 119268401Sgad mtime = queue[0]->job_time; 11931553Srgrimes for (i = argc; --i; ) { 11941553Srgrimes if (doarg(argv[i]) == 0) { 11951553Srgrimes printf("\tjob %s is not in the queue\n", argv[i]); 11961553Srgrimes continue; 11971553Srgrimes } else 11981553Srgrimes changed++; 11991553Srgrimes } 12001553Srgrimes for (i = 0; i < nitems; i++) 12011553Srgrimes free(queue[i]); 12021553Srgrimes free(queue); 12031553Srgrimes if (!changed) { 12041553Srgrimes printf("\tqueue order unchanged\n"); 12051553Srgrimes return; 12061553Srgrimes } 12071553Srgrimes /* 12081553Srgrimes * Turn on the public execute bit of the lock file to 12091553Srgrimes * get lpd to rebuild the queue after the current job. 12101553Srgrimes */ 1211241852Seadler PRIV_START 121231492Swollman if (changed && stat(pp->lock_file, &stbuf) >= 0) 121331492Swollman (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 12141553Srgrimes 121527618Simpout: 1216241852Seadler PRIV_END 121727618Simp} 121827618Simp 12191553Srgrimes/* 12201553Srgrimes * Reposition the job by changing the modification time of 12211553Srgrimes * the control file. 12221553Srgrimes */ 12231553Srgrimesstatic int 122478146Sgadtouch(struct jobqueue *jq) 12251553Srgrimes{ 12261553Srgrimes struct timeval tvp[2]; 122727618Simp int ret; 12281553Srgrimes 12291553Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 12301553Srgrimes tvp[0].tv_usec = tvp[1].tv_usec = 0; 1231241852Seadler PRIV_START 123278146Sgad ret = utimes(jq->job_cfname, tvp); 1233241852Seadler PRIV_END 123427618Simp return (ret); 12351553Srgrimes} 12361553Srgrimes 12371553Srgrimes/* 12381553Srgrimes * Checks if specified job name is in the printer's queue. 12391553Srgrimes * Returns: negative (-1) if argument name is not in the queue. 12401553Srgrimes */ 12411553Srgrimesstatic int 124278146Sgaddoarg(char *job) 12431553Srgrimes{ 124468401Sgad register struct jobqueue **qq; 12451553Srgrimes register int jobnum, n; 12461553Srgrimes register char *cp, *machine; 12471553Srgrimes int cnt = 0; 12481553Srgrimes FILE *fp; 12491553Srgrimes 12501553Srgrimes /* 125127748Simp * Look for a job item consisting of system name, colon, number 125227748Simp * (example: ucbarpa:114) 12531553Srgrimes */ 125427635Simp if ((cp = strchr(job, ':')) != NULL) { 12551553Srgrimes machine = job; 12561553Srgrimes *cp++ = '\0'; 12571553Srgrimes job = cp; 12581553Srgrimes } else 12591553Srgrimes machine = NULL; 12601553Srgrimes 12611553Srgrimes /* 12621553Srgrimes * Check for job specified by number (example: 112 or 235ucbarpa). 12631553Srgrimes */ 12641553Srgrimes if (isdigit(*job)) { 12651553Srgrimes jobnum = 0; 12661553Srgrimes do 12671553Srgrimes jobnum = jobnum * 10 + (*job++ - '0'); 12681553Srgrimes while (isdigit(*job)); 12691553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 12701553Srgrimes n = 0; 127168401Sgad for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 12721553Srgrimes n = n * 10 + (*cp++ - '0'); 12731553Srgrimes if (jobnum != n) 12741553Srgrimes continue; 12751553Srgrimes if (*job && strcmp(job, cp) != 0) 12761553Srgrimes continue; 12771553Srgrimes if (machine != NULL && strcmp(machine, cp) != 0) 12781553Srgrimes continue; 12791553Srgrimes if (touch(*qq) == 0) { 128068401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 12811553Srgrimes cnt++; 12821553Srgrimes } 12831553Srgrimes } 12841553Srgrimes return(cnt); 12851553Srgrimes } 12861553Srgrimes /* 12871553Srgrimes * Process item consisting of owner's name (example: henry). 12881553Srgrimes */ 12891553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 1290241852Seadler PRIV_START 129168401Sgad fp = fopen((*qq)->job_cfname, "r"); 1292241852Seadler PRIV_END 129327618Simp if (fp == NULL) 12941553Srgrimes continue; 12951553Srgrimes while (getline(fp) > 0) 12961553Srgrimes if (line[0] == 'P') 12971553Srgrimes break; 12981553Srgrimes (void) fclose(fp); 12991553Srgrimes if (line[0] != 'P' || strcmp(job, line+1) != 0) 13001553Srgrimes continue; 13011553Srgrimes if (touch(*qq) == 0) { 130268401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 13031553Srgrimes cnt++; 13041553Srgrimes } 13051553Srgrimes } 13061553Srgrimes return(cnt); 13071553Srgrimes} 13081553Srgrimes 13091553Srgrimes/* 131098152Sgad * Enable both queuing & printing, and start printer (undo `down'). 131198152Sgad */ 131298152Sgadvoid 131398152Sgadup_q(struct printer *pp) 131498152Sgad{ 131598152Sgad int setres, startok; 131698152Sgad char lf[MAXPATHLEN]; 131798152Sgad 131898152Sgad lock_file_name(pp, lf, sizeof lf); 131998152Sgad printf("%s:\n", pp->printer); 132098152Sgad 132198152Sgad setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); 132298152Sgad 1323241852Seadler PRIV_START 132498152Sgad startok = startdaemon(pp); 1325241852Seadler PRIV_END 132698152Sgad if (!startok) 132798152Sgad printf("\tcouldn't start daemon\n"); 132898152Sgad else 132998152Sgad printf("\tdaemon started\n"); 133098152Sgad} 1331