cmds.c revision 234826
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: head/usr.sbin/lpr/lpc/cmds.c 234826 2012-04-30 01:10:13Z gad $"); 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> 571553Srgrimes#include <errno.h> 581553Srgrimes#include <dirent.h> 591553Srgrimes#include <unistd.h> 601553Srgrimes#include <stdlib.h> 611553Srgrimes#include <stdio.h> 621553Srgrimes#include <ctype.h> 631553Srgrimes#include <string.h> 641553Srgrimes#include "lp.h" 651553Srgrimes#include "lp.local.h" 661553Srgrimes#include "lpc.h" 671553Srgrimes#include "extern.h" 681553Srgrimes#include "pathnames.h" 691553Srgrimes 7098152Sgad/* 7198152Sgad * Return values from kill_qtask(). 7298152Sgad */ 7398152Sgad#define KQT_LFERROR -2 7498152Sgad#define KQT_KILLFAIL -1 7598152Sgad#define KQT_NODAEMON 0 7698152Sgad#define KQT_KILLOK 1 7798152Sgad 7898267Sgadstatic char *args2line(int argc, char **argv); 7978146Sgadstatic int doarg(char *_job); 80231723Skevlostatic int doselect(const struct dirent *_d); 8198152Sgadstatic int kill_qtask(const char *lf); 82234244Sdelphijstatic int sortq(const struct dirent **a, const struct dirent **b); 8378146Sgadstatic int touch(struct jobqueue *_jq); 8478146Sgadstatic void unlinkf(char *_name); 8598267Sgadstatic void upstat(struct printer *_pp, const char *_msg, int _notify); 8678750Sgadstatic void wrapup_clean(int _laststatus); 871553Srgrimes 881553Srgrimes/* 8931492Swollman * generic framework for commands which operate on all or a specified 9031492Swollman * set of printers 911553Srgrimes */ 9278750Sgadenum qsel_val { /* how a given ptr was selected */ 9378750Sgad QSEL_UNKNOWN = -1, /* ... not selected yet */ 9478750Sgad QSEL_BYNAME = 0, /* ... user specifed it by name */ 9578750Sgad QSEL_ALL = 1 /* ... user wants "all" printers */ 9678750Sgad /* (with more to come) */ 9778750Sgad}; 9878750Sgad 9978750Sgadstatic enum qsel_val generic_qselect; /* indicates how ptr was selected */ 10078750Sgadstatic int generic_initerr; /* result of initrtn processing */ 10198268Sgadstatic char *generic_cmdname; 10298267Sgadstatic char *generic_msg; /* if a -msg was specified */ 10378750Sgadstatic char *generic_nullarg; 10478750Sgadstatic void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ 10578750Sgad 1061553Srgrimesvoid 10798267Sgadgeneric(void (*specificrtn)(struct printer *_pp), int cmdopts, 10878750Sgad void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) 1091553Srgrimes{ 11078750Sgad int cmdstatus, more, targc; 11178750Sgad struct printer myprinter, *pp; 11298268Sgad char **margv, **targv; 1131553Srgrimes 1141553Srgrimes if (argc == 1) { 11598278Sgad /* 11698278Sgad * Usage needs a special case for 'down': The user must 11798278Sgad * either include `-msg', or only the first parameter 11898278Sgad * that they give will be processed as a printer name. 11998278Sgad */ 12098267Sgad printf("usage: %s {all | printer ...}", argv[0]); 12198278Sgad if (strcmp(argv[0], "down") == 0) { 12298278Sgad printf(" -msg [<text> ...]\n"); 12398278Sgad printf(" or: down {all | printer} [<text> ...]"); 12498278Sgad } else if (cmdopts & LPC_MSGOPT) 12598267Sgad printf(" [-msg <text> ...]"); 12698267Sgad printf("\n"); 1271553Srgrimes return; 1281553Srgrimes } 12978750Sgad 13098268Sgad /* The first argument is the command name. */ 13198268Sgad generic_cmdname = *argv++; 13298268Sgad argc--; 13398268Sgad 13478750Sgad /* 13578750Sgad * The initialization routine for a command might set a generic 13678750Sgad * "wrapup" routine, which should be called after processing all 13778750Sgad * the printers in the command. This might print summary info. 13878750Sgad * 13978750Sgad * Note that the initialization routine may also parse (and 14078750Sgad * nullify) some of the parameters given on the command, leaving 14178750Sgad * only the parameters which have to do with printer names. 14278750Sgad */ 14378750Sgad pp = &myprinter; 14478750Sgad generic_wrapup = NULL; 14578750Sgad generic_qselect = QSEL_UNKNOWN; 14678750Sgad cmdstatus = 0; 14778750Sgad /* this just needs to be a distinct value of type 'char *' */ 14878750Sgad if (generic_nullarg == NULL) 14978750Sgad generic_nullarg = strdup(""); 15078750Sgad 15198267Sgad /* 15298267Sgad * Some commands accept a -msg argument, which indicates that 15398267Sgad * all remaining arguments should be combined into a string. 15498267Sgad */ 15598267Sgad generic_msg = NULL; 15698267Sgad if (cmdopts & LPC_MSGOPT) { 15798267Sgad targc = argc; 15898267Sgad targv = argv; 15998268Sgad for (; targc > 0; targc--, targv++) { 16098267Sgad if (strcmp(*targv, "-msg") == 0) { 16198267Sgad argc -= targc; 16298267Sgad generic_msg = args2line(targc - 1, targv + 1); 16398267Sgad break; 16498267Sgad } 16598267Sgad } 166234824Sgad if (argc < 1) { 167234824Sgad printf("error: No printer name(s) specified before" 168234824Sgad " '-msg'.\n"); 169234824Sgad printf("usage: %s {all | printer ...}", 170234824Sgad generic_cmdname); 171234824Sgad printf(" [-msg <text> ...]\n"); 172234824Sgad return; 173234824Sgad } 17498267Sgad } 17598267Sgad 17678750Sgad /* call initialization routine, if there is one for this cmd */ 17778750Sgad if (initrtn != NULL) { 17878750Sgad generic_initerr = 0; 17978750Sgad (*initrtn)(argc, argv); 18078750Sgad if (generic_initerr) 18178750Sgad return; 18298268Sgad /* 18398268Sgad * The initrtn may have null'ed out some of the parameters. 18498268Sgad * Compact the parameter list to remove those nulls, and 18598268Sgad * correct the arg-count. 18698268Sgad */ 18778750Sgad targc = argc; 18878750Sgad targv = argv; 18998268Sgad margv = argv; 19098268Sgad argc = 0; 19198268Sgad for (; targc > 0; targc--, targv++) { 19298268Sgad if (*targv != generic_nullarg) { 19398268Sgad if (targv != margv) 19498268Sgad *margv = *targv; 19598268Sgad margv++; 19698268Sgad argc++; 19798268Sgad } 19878750Sgad } 19978750Sgad } 20078750Sgad 20198268Sgad if (argc == 1 && strcmp(*argv, "all") == 0) { 20278750Sgad generic_qselect = QSEL_ALL; 20378146Sgad more = firstprinter(pp, &cmdstatus); 20478146Sgad if (cmdstatus) 20531492Swollman goto looperr; 20631492Swollman while (more) { 20778146Sgad (*specificrtn)(pp); 20831492Swollman do { 20978146Sgad more = nextprinter(pp, &cmdstatus); 21031492Swollmanlooperr: 21178146Sgad switch (cmdstatus) { 21231492Swollman case PCAPERR_TCOPEN: 21331492Swollman printf("warning: %s: unresolved " 21431492Swollman "tc= reference(s) ", 21531492Swollman pp->printer); 21631492Swollman case PCAPERR_SUCCESS: 21731492Swollman break; 21831492Swollman default: 21979739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 22031492Swollman } 22178146Sgad } while (more && cmdstatus); 2221553Srgrimes } 22378750Sgad goto wrapup; 2241553Srgrimes } 22578750Sgad 22678750Sgad generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 22798268Sgad for (; argc > 0; argc--, argv++) { 22831492Swollman init_printer(pp); 22978146Sgad cmdstatus = getprintcap(*argv, pp); 23078146Sgad switch (cmdstatus) { 23131492Swollman default: 23279739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 23331492Swollman case PCAPERR_NOTFOUND: 23431492Swollman printf("unknown printer %s\n", *argv); 2351553Srgrimes continue; 23631492Swollman case PCAPERR_TCOPEN: 23731492Swollman printf("warning: %s: unresolved tc= reference(s)\n", 23831492Swollman *argv); 23931492Swollman break; 24031492Swollman case PCAPERR_SUCCESS: 24131492Swollman break; 24231492Swollman } 24378146Sgad (*specificrtn)(pp); 2441553Srgrimes } 24578750Sgad 24678750Sgadwrapup: 24778750Sgad if (generic_wrapup) { 24878750Sgad (*generic_wrapup)(cmdstatus); 24978750Sgad } 25099846Sgad free_printer(pp); 25198267Sgad if (generic_msg) 25298267Sgad free(generic_msg); 25398267Sgad} 25478750Sgad 25598267Sgad/* 25698267Sgad * Convert an argv-array of character strings into a single string. 25798267Sgad */ 25898267Sgadstatic char * 25998267Sgadargs2line(int argc, char **argv) 26098267Sgad{ 26198267Sgad char *cp1, *cend; 26298267Sgad const char *cp2; 26398267Sgad char buf[1024]; 26498267Sgad 26598267Sgad if (argc <= 0) 26698267Sgad return strdup("\n"); 26798267Sgad 26898267Sgad cp1 = buf; 26998267Sgad cend = buf + sizeof(buf) - 1; /* save room for '\0' */ 27098267Sgad while (--argc >= 0) { 27198267Sgad cp2 = *argv++; 27298267Sgad while ((cp1 < cend) && (*cp1++ = *cp2++)) 27398267Sgad ; 27498267Sgad cp1[-1] = ' '; 27598267Sgad } 27698267Sgad cp1[-1] = '\n'; 27798267Sgad *cp1 = '\0'; 27898267Sgad return strdup(buf); 2791553Srgrimes} 2801553Srgrimes 28131492Swollman/* 28298152Sgad * Kill the current daemon, to stop printing of the active job. 28398152Sgad */ 28498152Sgadstatic int 28598152Sgadkill_qtask(const char *lf) 28698152Sgad{ 28798152Sgad FILE *fp; 28898152Sgad pid_t pid; 28998152Sgad int errsav, killres, lockres, res; 29098152Sgad 29198152Sgad seteuid(euid); 29298152Sgad fp = fopen(lf, "r"); 29398152Sgad errsav = errno; 29498152Sgad seteuid(uid); 29598152Sgad res = KQT_NODAEMON; 29698152Sgad if (fp == NULL) { 29798152Sgad /* 29898152Sgad * If there is no lock file, then there is no daemon to 29998152Sgad * kill. Any other error return means there is some 30098152Sgad * kind of problem with the lock file. 30198152Sgad */ 30298152Sgad if (errsav != ENOENT) 30398152Sgad res = KQT_LFERROR; 30498152Sgad goto killdone; 30598152Sgad } 30698152Sgad 30798152Sgad /* If the lock file is empty, then there is no daemon to kill */ 30898152Sgad if (getline(fp) == 0) 30998152Sgad goto killdone; 31098152Sgad 31198152Sgad /* 31298152Sgad * If the file can be locked without blocking, then there 31398152Sgad * no daemon to kill, or we should not try to kill it. 31498152Sgad * 31598152Sgad * XXX - not sure I understand the reasoning behind this... 31698152Sgad */ 31798152Sgad lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); 31898152Sgad (void) fclose(fp); 31998152Sgad if (lockres == 0) 32098152Sgad goto killdone; 32198152Sgad 32298152Sgad pid = atoi(line); 32398152Sgad if (pid < 0) { 32498152Sgad /* 32598152Sgad * If we got a negative pid, then the contents of the 32698152Sgad * lock file is not valid. 32798152Sgad */ 32898152Sgad res = KQT_LFERROR; 32998152Sgad goto killdone; 33098152Sgad } 33198152Sgad 33298152Sgad seteuid(uid); 33398152Sgad killres = kill(pid, SIGTERM); 33498152Sgad errsav = errno; 33598152Sgad seteuid(uid); 33698152Sgad if (killres == 0) { 33798152Sgad res = KQT_KILLOK; 33898152Sgad printf("\tdaemon (pid %d) killed\n", pid); 33998152Sgad } else if (errno == ESRCH) { 34098152Sgad res = KQT_NODAEMON; 34198152Sgad } else { 34298152Sgad res = KQT_KILLFAIL; 34398152Sgad printf("\tWarning: daemon (pid %d) not killed:\n", pid); 34498152Sgad printf("\t %s\n", strerror(errsav)); 34598152Sgad } 34698152Sgad 34798152Sgadkilldone: 34898152Sgad switch (res) { 34998152Sgad case KQT_LFERROR: 35098152Sgad printf("\tcannot open lock file: %s\n", 35198152Sgad strerror(errsav)); 35298152Sgad break; 35398152Sgad case KQT_NODAEMON: 35498152Sgad printf("\tno daemon to abort\n"); 35598152Sgad break; 35698152Sgad case KQT_KILLFAIL: 35798152Sgad case KQT_KILLOK: 35898152Sgad /* These two already printed messages to the user. */ 35998152Sgad break; 36098152Sgad default: 36198152Sgad printf("\t<internal error in kill_qtask>\n"); 36298152Sgad break; 36398152Sgad } 36498152Sgad 36598152Sgad return (res); 36698152Sgad} 36798152Sgad 36898152Sgad/* 3691553Srgrimes * Write a message into the status file. 3701553Srgrimes */ 3711553Srgrimesstatic void 37298267Sgadupstat(struct printer *pp, const char *msg, int notifyuser) 3731553Srgrimes{ 37498267Sgad int fd; 37527748Simp char statfile[MAXPATHLEN]; 3761553Srgrimes 37731492Swollman status_file_name(pp, statfile, sizeof statfile); 3781553Srgrimes umask(0); 37998267Sgad seteuid(euid); 38031492Swollman fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 38198267Sgad seteuid(uid); 38231492Swollman if (fd < 0) { 38331492Swollman printf("\tcannot create status file: %s\n", strerror(errno)); 3841553Srgrimes return; 3851553Srgrimes } 3861553Srgrimes (void) ftruncate(fd, 0); 387231723Skevlo if (msg == NULL) 3881553Srgrimes (void) write(fd, "\n", 1); 3891553Srgrimes else 3901553Srgrimes (void) write(fd, msg, strlen(msg)); 3911553Srgrimes (void) close(fd); 39298267Sgad if (notifyuser) { 39398267Sgad if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) 39498267Sgad printf("\tstatus message is now set to nothing.\n"); 39598267Sgad else 39698267Sgad printf("\tstatus message is now: %s", msg); 39798267Sgad } 3981553Srgrimes} 3991553Srgrimes 40078750Sgad/* 40198152Sgad * kill an existing daemon and disable printing. 40298152Sgad */ 40398152Sgadvoid 40498152Sgadabort_q(struct printer *pp) 40598152Sgad{ 40698152Sgad int killres, setres; 40798152Sgad char lf[MAXPATHLEN]; 40898152Sgad 40998152Sgad lock_file_name(pp, lf, sizeof lf); 41098152Sgad printf("%s:\n", pp->printer); 41198152Sgad 41298152Sgad /* 41398152Sgad * Turn on the owner execute bit of the lock file to disable printing. 41498152Sgad */ 41598152Sgad setres = set_qstate(SQS_STOPP, lf); 41698152Sgad 41798152Sgad /* 41898152Sgad * If set_qstate found that there already was a lock file, then 41998152Sgad * call a routine which will read that lock file and kill the 42098152Sgad * lpd-process which is listed in that lock file. If the lock 42198152Sgad * file did not exist, then either there is no daemon running 42298152Sgad * for this queue, or there is one running but *it* could not 42398152Sgad * write a lock file (which means we can not determine the 42498152Sgad * process id of that lpd-process). 42598152Sgad */ 42698152Sgad switch (setres) { 42798152Sgad case SQS_CHGOK: 42898152Sgad case SQS_CHGFAIL: 42998152Sgad /* Kill the process */ 43098152Sgad killres = kill_qtask(lf); 43198152Sgad break; 43298152Sgad case SQS_CREOK: 43398152Sgad case SQS_CREFAIL: 43498152Sgad printf("\tno daemon to abort\n"); 43598152Sgad break; 43698152Sgad case SQS_STATFAIL: 43798152Sgad printf("\tassuming no daemon to abort\n"); 43898152Sgad break; 43998152Sgad default: 44098152Sgad printf("\t<unexpected result (%d) from set_qstate>\n", 44198152Sgad setres); 44298152Sgad break; 44398152Sgad } 44498152Sgad 44598267Sgad if (setres >= 0) 44698267Sgad upstat(pp, "printing disabled\n", 0); 44798152Sgad} 44898152Sgad 44998152Sgad/* 45078750Sgad * "global" variables for all the routines related to 'clean' and 'tclean' 45178750Sgad */ 45278750Sgadstatic time_t cln_now; /* current time */ 45378750Sgadstatic double cln_minage; /* minimum age before file is removed */ 45478750Sgadstatic long cln_sizecnt; /* amount of space freed up */ 45578750Sgadstatic int cln_debug; /* print extra debugging msgs */ 45678750Sgadstatic int cln_filecnt; /* number of files destroyed */ 45778750Sgadstatic int cln_foundcore; /* found a core file! */ 45878750Sgadstatic int cln_queuecnt; /* number of queues checked */ 45978750Sgadstatic int cln_testonly; /* remove-files vs just-print-info */ 46078750Sgad 4611553Srgrimesstatic int 462231723Skevlodoselect(const struct dirent *d) 4631553Srgrimes{ 4641553Srgrimes int c = d->d_name[0]; 4651553Srgrimes 46687034Sgad if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && 46787034Sgad d->d_name[1] == 'f') 46878750Sgad return 1; 46978750Sgad if (c == 'c') { 47078750Sgad if (!strcmp(d->d_name, "core")) 47178750Sgad cln_foundcore = 1; 47278750Sgad } 47378750Sgad if (c == 'e') { 47478750Sgad if (!strncmp(d->d_name, "errs.", 5)) 47578750Sgad return 1; 47678750Sgad } 47778750Sgad return 0; 4781553Srgrimes} 4791553Srgrimes 4801553Srgrimes/* 48184034Sgad * Comparison routine that clean_q() uses for scandir. 48284034Sgad * 48384034Sgad * The purpose of this sort is to have all `df' files end up immediately 48487034Sgad * after the matching `cf' file. For files matching `cf', `df', `rf', or 48587034Sgad * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or 48687034Sgad * `tf', and then by the sequence letter (which is A-Z, or a-z). This 48787034Sgad * routine may also see filenames which do not start with `cf', `df', `rf', 48887034Sgad * or `tf' (such as `errs.*'), and those are simply sorted by the full 48987034Sgad * filename. 49087034Sgad * 49187034Sgad * XXX 49287034Sgad * This assumes that all control files start with `cfA*', and it turns 49387034Sgad * out there are a few implementations of lpr which will create `cfB*' 49487034Sgad * filenames (they will have datafile names which start with `dfB*'). 4951553Srgrimes */ 4961553Srgrimesstatic int 497234244Sdelphijsortq(const struct dirent **a, const struct dirent **b) 4981553Srgrimes{ 49984034Sgad const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; 50084034Sgad const char *fname_a, *fname_b, *jnum_a, *jnum_b; 50184034Sgad int cat_a, cat_b, ch, res, seq_a, seq_b; 5021553Srgrimes 503234244Sdelphij fname_a = (*a)->d_name; 504234244Sdelphij fname_b = (*b)->d_name; 50584034Sgad 50684034Sgad /* 507228990Suqs * First separate filenames into categories. Categories are 50887034Sgad * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in 50987034Sgad * that order. It is critical that the mapping be exactly the 51087034Sgad * same for 'a' vs 'b', so define a macro for the job. 51184034Sgad * 51284034Sgad * [aside: the standard `cf' file has the jobnumber start in 51384034Sgad * position 4, but some implementations have that as an extra 51484034Sgad * file-sequence letter, and start the job number in position 5.] 51584034Sgad */ 51684034Sgad#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ 51784034Sgad cat_X = cat_other; \ 51884034Sgad ch = *(fname_X + 2); \ 51984034Sgad jnum_X = fname_X + 3; \ 52084691Sgad seq_X = 0; \ 52184034Sgad if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ 52284034Sgad seq_X = ch; \ 52384034Sgad if (*fname_X == 'c') \ 52484034Sgad cat_X = 1; \ 52584034Sgad else if (*fname_X == 'd') \ 52684034Sgad cat_X = 2; \ 52787034Sgad else if (*fname_X == 'r') \ 52887034Sgad cat_X = 3; \ 52984034Sgad else if (*fname_X == 't') \ 53087034Sgad cat_X = 4; \ 53184034Sgad if (cat_X != cat_other) { \ 53284034Sgad ch = *jnum_X; \ 53384034Sgad if (!isdigit(ch)) { \ 53484034Sgad if (isalpha(ch)) { \ 53584034Sgad jnum_X++; \ 53684034Sgad ch = *jnum_X; \ 53784034Sgad seq_X = (seq_X << 8) + ch; \ 53884034Sgad } \ 53984034Sgad if (!isdigit(ch)) \ 54084034Sgad cat_X = cat_other; \ 54184034Sgad } \ 54284034Sgad } \ 54384034Sgad } \ 54484034Sgad} while (0) 54584034Sgad 54684034Sgad MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); 54784034Sgad MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); 54884034Sgad 54984034Sgad#undef MAP_TO_CAT 55084034Sgad 55184034Sgad /* First handle all cases which have "other" files */ 55284034Sgad if ((cat_a >= cat_other) || (cat_b >= cat_other)) { 55384034Sgad /* for two "other" files, just compare the full name */ 55484034Sgad if (cat_a == cat_b) 55584034Sgad res = strcmp(fname_a, fname_b); 55684034Sgad else if (cat_a < cat_b) 55784034Sgad res = a_lt_b; 55884034Sgad else 55984034Sgad res = a_gt_b; 56084034Sgad goto have_res; 56184034Sgad } 56284034Sgad 56384034Sgad /* 56487034Sgad * At this point, we know both files are legitimate `cf', `df', `rf', 56584034Sgad * or `tf' files. Compare them by job-number and machine name. 56684034Sgad */ 56784034Sgad res = strcmp(jnum_a, jnum_b); 56884034Sgad if (res != 0) 56984034Sgad goto have_res; 57084034Sgad 57184034Sgad /* 57284034Sgad * We have two files which belong to the same job. Sort based 573228990Suqs * on the category of file (`c' before `d', etc). 57484034Sgad */ 57584034Sgad if (cat_a < cat_b) { 57684034Sgad res = a_lt_b; 57784034Sgad goto have_res; 57884034Sgad } else if (cat_a > cat_b) { 57984034Sgad res = a_gt_b; 58084034Sgad goto have_res; 58184034Sgad } 58284034Sgad 58384034Sgad /* 584228990Suqs * Two files in the same category for a single job. Sort based 585228990Suqs * on the sequence letter(s). (usually `A' through `Z', etc). 58684034Sgad */ 58784034Sgad if (seq_a < seq_b) { 58884034Sgad res = a_lt_b; 58984034Sgad goto have_res; 59084034Sgad } else if (seq_a > seq_b) { 59184034Sgad res = a_gt_b; 59284034Sgad goto have_res; 59384034Sgad } 59484034Sgad 59584034Sgad /* 59684034Sgad * Given that the filenames in a directory are unique, this SHOULD 59784034Sgad * never happen (unless there are logic errors in this routine). 59884034Sgad * But if it does happen, we must return "is equal" or the caller 59984034Sgad * might see inconsistent results in the sorting order, and that 60084034Sgad * can trigger other problems. 60184034Sgad */ 60284034Sgad printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); 60384034Sgad printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, 60484034Sgad seq_a, seq_b); 60584034Sgad res = 0; 60684034Sgad 60784034Sgadhave_res: 60884034Sgad return res; 6091553Srgrimes} 6101553Srgrimes 6111553Srgrimes/* 61231492Swollman * Remove all spool files and temporaries from the spooling area. 61331492Swollman * Or, perhaps: 6141553Srgrimes * Remove incomplete jobs from spooling area. 6151553Srgrimes */ 61678750Sgad 61731492Swollmanvoid 61898279Sgadclean_gi(int argc, char *argv[]) 6191553Srgrimes{ 62078750Sgad 62178750Sgad /* init some fields before 'clean' is called for each queue */ 62278750Sgad cln_queuecnt = 0; 62378750Sgad cln_now = time(NULL); 62478750Sgad cln_minage = 3600.0; /* only delete files >1h old */ 62578750Sgad cln_filecnt = 0; 62678750Sgad cln_sizecnt = 0; 62778750Sgad cln_debug = 0; 62878750Sgad cln_testonly = 0; 62978750Sgad generic_wrapup = &wrapup_clean; 63078750Sgad 63178750Sgad /* see if there are any options specified before the ptr list */ 63298268Sgad for (; argc > 0; argc--, argv++) { 63378750Sgad if (**argv != '-') 63478750Sgad break; 63578750Sgad if (strcmp(*argv, "-d") == 0) { 63678750Sgad /* just an example of an option... */ 63784034Sgad cln_debug++; 63878750Sgad *argv = generic_nullarg; /* "erase" it */ 63978750Sgad } else { 64078750Sgad printf("Invalid option '%s'\n", *argv); 64178750Sgad generic_initerr = 1; 64278750Sgad } 64378750Sgad } 64478750Sgad 64578750Sgad return; 64678750Sgad} 64778750Sgad 64878750Sgadvoid 64998279Sgadtclean_gi(int argc, char *argv[]) 65078750Sgad{ 65178750Sgad 65278750Sgad /* only difference between 'clean' and 'tclean' is one value */ 65378750Sgad /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 65498279Sgad clean_gi(argc, argv); 65578750Sgad cln_testonly = 1; 65678750Sgad 65778750Sgad return; 65878750Sgad} 65978750Sgad 66078750Sgadvoid 66178750Sgadclean_q(struct printer *pp) 66278750Sgad{ 66378750Sgad char *cp, *cp1, *lp; 6641553Srgrimes struct dirent **queue; 66578750Sgad size_t linerem; 66678750Sgad int didhead, i, n, nitems, rmcp; 6671553Srgrimes 66878750Sgad cln_queuecnt++; 6691553Srgrimes 67078750Sgad didhead = 0; 67178750Sgad if (generic_qselect == QSEL_BYNAME) { 67278750Sgad printf("%s:\n", pp->printer); 67378750Sgad didhead = 1; 67478750Sgad } 67578750Sgad 67631492Swollman lp = line; 67731492Swollman cp = pp->spool_dir; 67831492Swollman while (lp < &line[sizeof(line) - 1]) { 67931492Swollman if ((*lp++ = *cp++) == 0) 68031492Swollman break; 68131492Swollman } 6821553Srgrimes lp[-1] = '/'; 68378750Sgad linerem = sizeof(line) - (lp - line); 6841553Srgrimes 68578750Sgad cln_foundcore = 0; 68627618Simp seteuid(euid); 68731492Swollman nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 68827618Simp seteuid(uid); 6891553Srgrimes if (nitems < 0) { 69078750Sgad if (!didhead) { 69178750Sgad printf("%s:\n", pp->printer); 69278750Sgad didhead = 1; 69378750Sgad } 6941553Srgrimes printf("\tcannot examine spool directory\n"); 6951553Srgrimes return; 6961553Srgrimes } 69778750Sgad if (cln_foundcore) { 69878750Sgad if (!didhead) { 69978750Sgad printf("%s:\n", pp->printer); 70078750Sgad didhead = 1; 70178750Sgad } 70278750Sgad printf("\t** found a core file in %s !\n", pp->spool_dir); 70378750Sgad } 7041553Srgrimes if (nitems == 0) 7051553Srgrimes return; 70678750Sgad if (!didhead) 70778750Sgad printf("%s:\n", pp->printer); 70884034Sgad if (cln_debug) { 70984034Sgad printf("\t** ----- Sorted list of files being checked:\n"); 71084034Sgad i = 0; 71184034Sgad do { 71284034Sgad cp = queue[i]->d_name; 71384034Sgad printf("\t** [%3d] = %s\n", i, cp); 71484034Sgad } while (++i < nitems); 71584034Sgad printf("\t** ----- end of sorted list\n"); 71684034Sgad } 7171553Srgrimes i = 0; 7181553Srgrimes do { 7191553Srgrimes cp = queue[i]->d_name; 72078750Sgad rmcp = 0; 7211553Srgrimes if (*cp == 'c') { 72278750Sgad /* 72378750Sgad * A control file. Look for matching data-files. 72478750Sgad */ 72578750Sgad /* XXX 72678750Sgad * Note the logic here assumes that the hostname 72778750Sgad * part of cf-filenames match the hostname part 72878750Sgad * in df-filenames, and that is not necessarily 72978750Sgad * true (eg: for multi-homed hosts). This needs 73078750Sgad * some further thought... 73178750Sgad */ 7321553Srgrimes n = 0; 7331553Srgrimes while (i + 1 < nitems) { 7341553Srgrimes cp1 = queue[i + 1]->d_name; 7351553Srgrimes if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) 7361553Srgrimes break; 7371553Srgrimes i++; 7381553Srgrimes n++; 7391553Srgrimes } 7401553Srgrimes if (n == 0) { 74178750Sgad rmcp = 1; 7421553Srgrimes } 74378750Sgad } else if (*cp == 'e') { 74478750Sgad /* 74578750Sgad * Must be an errrs or email temp file. 74678750Sgad */ 74778750Sgad rmcp = 1; 7481553Srgrimes } else { 7491553Srgrimes /* 7501553Srgrimes * Must be a df with no cf (otherwise, it would have 75187034Sgad * been skipped above) or an rf or tf file (which can 75287034Sgad * always be removed if it is old enough). 7531553Srgrimes */ 75478750Sgad rmcp = 1; 75578750Sgad } 75678750Sgad if (rmcp) { 75778750Sgad if (strlen(cp) >= linerem) { 75878750Sgad printf("\t** internal error: 'line' overflow!\n"); 75978750Sgad printf("\t** spooldir = %s\n", pp->spool_dir); 76078750Sgad printf("\t** cp = %s\n", cp); 76178750Sgad return; 76278750Sgad } 76378750Sgad strlcpy(lp, cp, linerem); 7641553Srgrimes unlinkf(line); 7651553Srgrimes } 7661553Srgrimes } while (++i < nitems); 7671553Srgrimes} 76878750Sgad 76978750Sgadstatic void 77078750Sgadwrapup_clean(int laststatus __unused) 77178750Sgad{ 77278750Sgad 77378750Sgad printf("Checked %d queues, and ", cln_queuecnt); 77478750Sgad if (cln_filecnt < 1) { 77578750Sgad printf("no cruft was found\n"); 77678750Sgad return; 77778750Sgad } 77878750Sgad if (cln_testonly) { 77978750Sgad printf("would have "); 78078750Sgad } 78178750Sgad printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); 78278750Sgad} 78327618Simp 7841553Srgrimesstatic void 78578146Sgadunlinkf(char *name) 7861553Srgrimes{ 78778750Sgad struct stat stbuf; 78878750Sgad double agemod, agestat; 78978750Sgad int res; 79078750Sgad char linkbuf[BUFSIZ]; 79178750Sgad 79278750Sgad /* 79378750Sgad * We have to use lstat() instead of stat(), in case this is a df* 79478750Sgad * "file" which is really a symlink due to 'lpr -s' processing. In 79578750Sgad * that case, we need to check the last-mod time of the symlink, and 79678750Sgad * not the file that the symlink is pointed at. 79778750Sgad */ 79827618Simp seteuid(euid); 79978750Sgad res = lstat(name, &stbuf); 80027618Simp seteuid(uid); 80178750Sgad if (res < 0) { 80278750Sgad printf("\terror return from stat(%s):\n", name); 80378750Sgad printf("\t %s\n", strerror(errno)); 80478750Sgad return; 80578750Sgad } 80678750Sgad 80778750Sgad agemod = difftime(cln_now, stbuf.st_mtime); 80878750Sgad agestat = difftime(cln_now, stbuf.st_ctime); 80984034Sgad if (cln_debug > 1) { 81078750Sgad /* this debugging-aid probably is not needed any more... */ 81178750Sgad printf("\t\t modify age=%g secs, stat age=%g secs\n", 81278750Sgad agemod, agestat); 81378750Sgad } 81478750Sgad if ((agemod <= cln_minage) && (agestat <= cln_minage)) 81578750Sgad return; 81678750Sgad 81778750Sgad /* 81878750Sgad * if this file is a symlink, then find out the target of the 81978750Sgad * symlink before unlink-ing the file itself 82078750Sgad */ 82178750Sgad if (S_ISLNK(stbuf.st_mode)) { 82278750Sgad seteuid(euid); 82378750Sgad res = readlink(name, linkbuf, sizeof(linkbuf)); 82478750Sgad seteuid(uid); 82578750Sgad if (res < 0) { 82678750Sgad printf("\terror return from readlink(%s):\n", name); 82778750Sgad printf("\t %s\n", strerror(errno)); 82878750Sgad return; 82978750Sgad } 83078750Sgad if (res == sizeof(linkbuf)) 83178750Sgad res--; 83278750Sgad linkbuf[res] = '\0'; 83378750Sgad } 83478750Sgad 83578750Sgad cln_filecnt++; 83678750Sgad cln_sizecnt += stbuf.st_size; 83778750Sgad 83878750Sgad if (cln_testonly) { 83978750Sgad printf("\twould remove %s\n", name); 84078750Sgad if (S_ISLNK(stbuf.st_mode)) { 84178750Sgad printf("\t (which is a symlink to %s)\n", linkbuf); 84278750Sgad } 84378750Sgad } else { 84478750Sgad seteuid(euid); 84578750Sgad res = unlink(name); 84678750Sgad seteuid(uid); 84778750Sgad if (res < 0) 84878750Sgad printf("\tcannot remove %s (!)\n", name); 84978750Sgad else 85078750Sgad printf("\tremoved %s\n", name); 85178750Sgad /* XXX 85278750Sgad * Note that for a df* file, this code should also check to see 85378750Sgad * if it is a symlink to some other file, and if the original 85478750Sgad * lpr command included '-r' ("remove file"). Of course, this 85578750Sgad * code would not be removing the df* file unless there was no 85678750Sgad * matching cf* file, and without the cf* file it is currently 85778750Sgad * impossible to determine if '-r' had been specified... 85878750Sgad * 85978750Sgad * As a result of this quandry, we may be leaving behind a 86078750Sgad * user's file that was supposed to have been removed after 86178750Sgad * being printed. This may effect services such as CAP or 86278750Sgad * samba, if they were configured to use 'lpr -r', and if 86378750Sgad * datafiles are not being properly removed. 86478750Sgad */ 86578750Sgad if (S_ISLNK(stbuf.st_mode)) { 86678750Sgad printf("\t (which was a symlink to %s)\n", linkbuf); 86778750Sgad } 86878750Sgad } 8691553Srgrimes} 8701553Srgrimes 8711553Srgrimes/* 87298152Sgad * Enable queuing to the printer (allow lpr to add new jobs to the queue). 87398152Sgad */ 87498152Sgadvoid 87598152Sgadenable_q(struct printer *pp) 87698152Sgad{ 87798152Sgad int setres; 87898152Sgad char lf[MAXPATHLEN]; 87998152Sgad 88098152Sgad lock_file_name(pp, lf, sizeof lf); 88198152Sgad printf("%s:\n", pp->printer); 88298152Sgad 88398152Sgad setres = set_qstate(SQS_ENABLEQ, lf); 88498152Sgad} 88598152Sgad 88698152Sgad/* 8871553Srgrimes * Disable queuing. 8881553Srgrimes */ 8891553Srgrimesvoid 89098152Sgaddisable_q(struct printer *pp) 89198152Sgad{ 89298152Sgad int setres; 89398152Sgad char lf[MAXPATHLEN]; 89498152Sgad 89598152Sgad lock_file_name(pp, lf, sizeof lf); 89698152Sgad printf("%s:\n", pp->printer); 89798152Sgad 89898152Sgad setres = set_qstate(SQS_DISABLEQ, lf); 89998152Sgad} 90098152Sgad 90198152Sgad/* 9021553Srgrimes * Disable queuing and printing and put a message into the status file 90398278Sgad * (reason for being down). If the user specified `-msg', then use 90498278Sgad * everything after that as the message for the status file. If the 90598278Sgad * user did NOT specify `-msg', then the command should take the first 90698278Sgad * parameter as the printer name, and all remaining parameters as the 90798278Sgad * message for the status file. (This is to be compatible with the 90898278Sgad * original definition of 'down', which was implemented long before 90998278Sgad * `-msg' was around). 91098278Sgad */ 91198278Sgadvoid 91298278Sgaddown_gi(int argc, char *argv[]) 91398278Sgad{ 91498278Sgad 91598278Sgad /* If `-msg' was specified, then this routine has nothing to do. */ 91698278Sgad if (generic_msg != NULL) 91798278Sgad return; 91898278Sgad 91998278Sgad /* 92098278Sgad * If the user only gave one parameter, then use a default msg. 92198278Sgad * (if argc == 1 at this point, then *argv == name of printer). 92298278Sgad */ 92398278Sgad if (argc == 1) { 92498278Sgad generic_msg = strdup("printing disabled\n"); 92598278Sgad return; 92698278Sgad } 92798278Sgad 92898278Sgad /* 92998278Sgad * The user specified multiple parameters, and did not specify 93098278Sgad * `-msg'. Build a message from all the parameters after the 93198278Sgad * first one (and nullify those parameters so generic-processing 93298278Sgad * will not process them as printer-queue names). 93398278Sgad */ 93498278Sgad argc--; 93598278Sgad argv++; 93698278Sgad generic_msg = args2line(argc, argv); 93798278Sgad for (; argc > 0; argc--, argv++) 93898278Sgad *argv = generic_nullarg; /* "erase" it */ 93998278Sgad} 94098278Sgad 94198278Sgadvoid 94298278Sgaddown_q(struct printer *pp) 94398278Sgad{ 94498278Sgad int setres; 94598278Sgad char lf[MAXPATHLEN]; 94698278Sgad 94798278Sgad lock_file_name(pp, lf, sizeof lf); 94898278Sgad printf("%s:\n", pp->printer); 94998278Sgad 95098278Sgad setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf); 95198278Sgad if (setres >= 0) 95298278Sgad upstat(pp, generic_msg, 1); 95398278Sgad} 95498278Sgad 95598278Sgad/* 9561553Srgrimes * Exit lpc 9571553Srgrimes */ 9581553Srgrimesvoid 95978146Sgadquit(int argc __unused, char *argv[] __unused) 9601553Srgrimes{ 9611553Srgrimes exit(0); 9621553Srgrimes} 9631553Srgrimes 9641553Srgrimes/* 9651553Srgrimes * Kill and restart the daemon. 9661553Srgrimes */ 9671553Srgrimesvoid 96898152Sgadrestart_q(struct printer *pp) 96998152Sgad{ 97098152Sgad int killres, setres, startok; 97198152Sgad char lf[MAXPATHLEN]; 97298152Sgad 97398152Sgad lock_file_name(pp, lf, sizeof lf); 97498152Sgad printf("%s:\n", pp->printer); 97598152Sgad 97698152Sgad killres = kill_qtask(lf); 97798152Sgad 97898152Sgad /* 97998152Sgad * XXX - if the kill worked, we should probably sleep for 98098152Sgad * a second or so before trying to restart the queue. 98198152Sgad */ 98298152Sgad 98398152Sgad /* make sure the queue is set to print jobs */ 98498152Sgad setres = set_qstate(SQS_STARTP, lf); 98598152Sgad 98698152Sgad seteuid(euid); 98798152Sgad startok = startdaemon(pp); 98898152Sgad seteuid(uid); 98998152Sgad if (!startok) 99098152Sgad printf("\tcouldn't restart daemon\n"); 99198152Sgad else 99298152Sgad printf("\tdaemon restarted\n"); 99398152Sgad} 99498152Sgad 99598152Sgad/* 99698267Sgad * Set the status message of each queue listed. Requires a "-msg" 99798267Sgad * parameter to indicate the end of the queue list and start of msg text. 99898267Sgad */ 99998267Sgadvoid 100098267Sgadsetstatus_gi(int argc __unused, char *argv[] __unused) 100198267Sgad{ 100298267Sgad 100398267Sgad if (generic_msg == NULL) { 100498267Sgad printf("You must specify '-msg' before the text of the new status message.\n"); 100598267Sgad generic_initerr = 1; 100698267Sgad } 100798267Sgad} 100898267Sgad 100998267Sgadvoid 101098267Sgadsetstatus_q(struct printer *pp) 101198267Sgad{ 1012234826Sgad struct stat stbuf; 1013234826Sgad int not_shown; 101498267Sgad char lf[MAXPATHLEN]; 101598267Sgad 101698267Sgad lock_file_name(pp, lf, sizeof lf); 101798267Sgad printf("%s:\n", pp->printer); 101898267Sgad 101998267Sgad upstat(pp, generic_msg, 1); 1020234826Sgad 1021234826Sgad /* 1022234826Sgad * Warn the user if 'lpq' will not display this new status-message. 1023234826Sgad * Note that if lock file does not exist, then the queue is enabled 1024234826Sgad * for both queuing and printing. 1025234826Sgad */ 1026234826Sgad not_shown = 1; 1027234826Sgad if (stat(lf, &stbuf) >= 0) { 1028234826Sgad if (stbuf.st_mode & LFM_PRINT_DIS) 1029234826Sgad not_shown = 0; 1030234826Sgad } 1031234826Sgad if (not_shown) { 1032234826Sgad printf("\tnote: This queue currently has printing enabled,\n"); 1033234826Sgad printf("\t so this -msg will only be shown by 'lpq' if\n"); 1034234826Sgad printf("\t a job is actively printing on it.\n"); 1035234826Sgad } 103698267Sgad} 103798267Sgad 103898267Sgad/* 10391553Srgrimes * Enable printing on the specified printer and startup the daemon. 10401553Srgrimes */ 10411553Srgrimesvoid 104298152Sgadstart_q(struct printer *pp) 104398152Sgad{ 104498152Sgad int setres, startok; 104598152Sgad char lf[MAXPATHLEN]; 104698152Sgad 104798152Sgad lock_file_name(pp, lf, sizeof lf); 104898152Sgad printf("%s:\n", pp->printer); 104998152Sgad 105098152Sgad setres = set_qstate(SQS_STARTP, lf); 105198152Sgad 105298152Sgad seteuid(euid); 105398152Sgad startok = startdaemon(pp); 105498152Sgad seteuid(uid); 105598152Sgad if (!startok) 105698152Sgad printf("\tcouldn't start daemon\n"); 105798152Sgad else 105898152Sgad printf("\tdaemon started\n"); 105998152Sgad seteuid(uid); 106098152Sgad} 106198152Sgad 106298152Sgad/* 106331492Swollman * Print the status of the printer queue. 10641553Srgrimes */ 10651553Srgrimesvoid 106678146Sgadstatus(struct printer *pp) 10671553Srgrimes{ 10681553Srgrimes struct stat stbuf; 10691553Srgrimes register int fd, i; 10701553Srgrimes register struct dirent *dp; 10711553Srgrimes DIR *dirp; 107231492Swollman char file[MAXPATHLEN]; 10731553Srgrimes 107431492Swollman printf("%s:\n", pp->printer); 107531492Swollman lock_file_name(pp, file, sizeof file); 107631492Swollman if (stat(file, &stbuf) >= 0) { 10771553Srgrimes printf("\tqueuing is %s\n", 107831492Swollman ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 107931492Swollman : "enabled")); 10801553Srgrimes printf("\tprinting is %s\n", 108131492Swollman ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 108231492Swollman : "enabled")); 10831553Srgrimes } else { 10841553Srgrimes printf("\tqueuing is enabled\n"); 10851553Srgrimes printf("\tprinting is enabled\n"); 10861553Srgrimes } 108731492Swollman if ((dirp = opendir(pp->spool_dir)) == NULL) { 10881553Srgrimes printf("\tcannot examine spool directory\n"); 10891553Srgrimes return; 10901553Srgrimes } 10911553Srgrimes i = 0; 10921553Srgrimes while ((dp = readdir(dirp)) != NULL) { 10931553Srgrimes if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 10941553Srgrimes i++; 10951553Srgrimes } 10961553Srgrimes closedir(dirp); 10971553Srgrimes if (i == 0) 109831492Swollman printf("\tno entries in spool area\n"); 10991553Srgrimes else if (i == 1) 11001553Srgrimes printf("\t1 entry in spool area\n"); 11011553Srgrimes else 11021553Srgrimes printf("\t%d entries in spool area\n", i); 110331492Swollman fd = open(file, O_RDONLY); 11041553Srgrimes if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 11051553Srgrimes (void) close(fd); /* unlocks as well */ 110619202Simp printf("\tprinter idle\n"); 11071553Srgrimes return; 11081553Srgrimes } 11091553Srgrimes (void) close(fd); 111025789Sbrian /* print out the contents of the status file, if it exists */ 111131492Swollman status_file_name(pp, file, sizeof file); 111231492Swollman fd = open(file, O_RDONLY|O_SHLOCK); 11131553Srgrimes if (fd >= 0) { 111425789Sbrian (void) fstat(fd, &stbuf); 111525789Sbrian if (stbuf.st_size > 0) { 111625789Sbrian putchar('\t'); 111725789Sbrian while ((i = read(fd, line, sizeof(line))) > 0) 111825789Sbrian (void) fwrite(line, 1, i, stdout); 111925789Sbrian } 11201553Srgrimes (void) close(fd); /* unlocks as well */ 11211553Srgrimes } 11221553Srgrimes} 11231553Srgrimes 11241553Srgrimes/* 11251553Srgrimes * Stop the specified daemon after completing the current job and disable 11261553Srgrimes * printing. 11271553Srgrimes */ 11281553Srgrimesvoid 112998152Sgadstop_q(struct printer *pp) 113098152Sgad{ 113198152Sgad int setres; 113298152Sgad char lf[MAXPATHLEN]; 113398152Sgad 113498152Sgad lock_file_name(pp, lf, sizeof lf); 113598152Sgad printf("%s:\n", pp->printer); 113698152Sgad 113798152Sgad setres = set_qstate(SQS_STOPP, lf); 113898152Sgad 113998267Sgad if (setres >= 0) 114098267Sgad upstat(pp, "printing disabled\n", 0); 114198152Sgad} 114298152Sgad 114368401Sgadstruct jobqueue **queue; 11441553Srgrimesint nitems; 11451553Srgrimestime_t mtime; 11461553Srgrimes 11471553Srgrimes/* 11481553Srgrimes * Put the specified jobs at the top of printer queue. 11491553Srgrimes */ 11501553Srgrimesvoid 115178146Sgadtopq(int argc, char *argv[]) 11521553Srgrimes{ 11531553Srgrimes register int i; 11541553Srgrimes struct stat stbuf; 115578146Sgad int cmdstatus, changed; 115631492Swollman struct printer myprinter, *pp = &myprinter; 11571553Srgrimes 11581553Srgrimes if (argc < 3) { 115995258Sdes printf("usage: topq printer [jobnum ...] [user ...]\n"); 11601553Srgrimes return; 11611553Srgrimes } 11621553Srgrimes 11631553Srgrimes --argc; 116431492Swollman ++argv; 116531492Swollman init_printer(pp); 116678146Sgad cmdstatus = getprintcap(*argv, pp); 116778146Sgad switch(cmdstatus) { 116831492Swollman default: 116979739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 117031492Swollman case PCAPERR_NOTFOUND: 117131492Swollman printf("unknown printer %s\n", *argv); 11721553Srgrimes return; 117331492Swollman case PCAPERR_TCOPEN: 117431492Swollman printf("warning: %s: unresolved tc= reference(s)", *argv); 117531492Swollman break; 117631492Swollman case PCAPERR_SUCCESS: 117731492Swollman break; 117831492Swollman } 117931492Swollman printf("%s:\n", pp->printer); 11801553Srgrimes 118127618Simp seteuid(euid); 118231492Swollman if (chdir(pp->spool_dir) < 0) { 118331492Swollman printf("\tcannot chdir to %s\n", pp->spool_dir); 118427618Simp goto out; 11851553Srgrimes } 118627618Simp seteuid(uid); 118731492Swollman nitems = getq(pp, &queue); 11881553Srgrimes if (nitems == 0) 11891553Srgrimes return; 11901553Srgrimes changed = 0; 119168401Sgad mtime = queue[0]->job_time; 11921553Srgrimes for (i = argc; --i; ) { 11931553Srgrimes if (doarg(argv[i]) == 0) { 11941553Srgrimes printf("\tjob %s is not in the queue\n", argv[i]); 11951553Srgrimes continue; 11961553Srgrimes } else 11971553Srgrimes changed++; 11981553Srgrimes } 11991553Srgrimes for (i = 0; i < nitems; i++) 12001553Srgrimes free(queue[i]); 12011553Srgrimes free(queue); 12021553Srgrimes if (!changed) { 12031553Srgrimes printf("\tqueue order unchanged\n"); 12041553Srgrimes return; 12051553Srgrimes } 12061553Srgrimes /* 12071553Srgrimes * Turn on the public execute bit of the lock file to 12081553Srgrimes * get lpd to rebuild the queue after the current job. 12091553Srgrimes */ 121027618Simp seteuid(euid); 121131492Swollman if (changed && stat(pp->lock_file, &stbuf) >= 0) 121231492Swollman (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 12131553Srgrimes 121427618Simpout: 121527618Simp seteuid(uid); 121627618Simp} 121727618Simp 12181553Srgrimes/* 12191553Srgrimes * Reposition the job by changing the modification time of 12201553Srgrimes * the control file. 12211553Srgrimes */ 12221553Srgrimesstatic int 122378146Sgadtouch(struct jobqueue *jq) 12241553Srgrimes{ 12251553Srgrimes struct timeval tvp[2]; 122627618Simp int ret; 12271553Srgrimes 12281553Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 12291553Srgrimes tvp[0].tv_usec = tvp[1].tv_usec = 0; 123027618Simp seteuid(euid); 123178146Sgad ret = utimes(jq->job_cfname, tvp); 123227618Simp seteuid(uid); 123327618Simp return (ret); 12341553Srgrimes} 12351553Srgrimes 12361553Srgrimes/* 12371553Srgrimes * Checks if specified job name is in the printer's queue. 12381553Srgrimes * Returns: negative (-1) if argument name is not in the queue. 12391553Srgrimes */ 12401553Srgrimesstatic int 124178146Sgaddoarg(char *job) 12421553Srgrimes{ 124368401Sgad register struct jobqueue **qq; 12441553Srgrimes register int jobnum, n; 12451553Srgrimes register char *cp, *machine; 12461553Srgrimes int cnt = 0; 12471553Srgrimes FILE *fp; 12481553Srgrimes 12491553Srgrimes /* 125027748Simp * Look for a job item consisting of system name, colon, number 125127748Simp * (example: ucbarpa:114) 12521553Srgrimes */ 125327635Simp if ((cp = strchr(job, ':')) != NULL) { 12541553Srgrimes machine = job; 12551553Srgrimes *cp++ = '\0'; 12561553Srgrimes job = cp; 12571553Srgrimes } else 12581553Srgrimes machine = NULL; 12591553Srgrimes 12601553Srgrimes /* 12611553Srgrimes * Check for job specified by number (example: 112 or 235ucbarpa). 12621553Srgrimes */ 12631553Srgrimes if (isdigit(*job)) { 12641553Srgrimes jobnum = 0; 12651553Srgrimes do 12661553Srgrimes jobnum = jobnum * 10 + (*job++ - '0'); 12671553Srgrimes while (isdigit(*job)); 12681553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 12691553Srgrimes n = 0; 127068401Sgad for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 12711553Srgrimes n = n * 10 + (*cp++ - '0'); 12721553Srgrimes if (jobnum != n) 12731553Srgrimes continue; 12741553Srgrimes if (*job && strcmp(job, cp) != 0) 12751553Srgrimes continue; 12761553Srgrimes if (machine != NULL && strcmp(machine, cp) != 0) 12771553Srgrimes continue; 12781553Srgrimes if (touch(*qq) == 0) { 127968401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 12801553Srgrimes cnt++; 12811553Srgrimes } 12821553Srgrimes } 12831553Srgrimes return(cnt); 12841553Srgrimes } 12851553Srgrimes /* 12861553Srgrimes * Process item consisting of owner's name (example: henry). 12871553Srgrimes */ 12881553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 128927618Simp seteuid(euid); 129068401Sgad fp = fopen((*qq)->job_cfname, "r"); 129127618Simp seteuid(uid); 129227618Simp if (fp == NULL) 12931553Srgrimes continue; 12941553Srgrimes while (getline(fp) > 0) 12951553Srgrimes if (line[0] == 'P') 12961553Srgrimes break; 12971553Srgrimes (void) fclose(fp); 12981553Srgrimes if (line[0] != 'P' || strcmp(job, line+1) != 0) 12991553Srgrimes continue; 13001553Srgrimes if (touch(*qq) == 0) { 130168401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 13021553Srgrimes cnt++; 13031553Srgrimes } 13041553Srgrimes } 13051553Srgrimes return(cnt); 13061553Srgrimes} 13071553Srgrimes 13081553Srgrimes/* 130998152Sgad * Enable both queuing & printing, and start printer (undo `down'). 131098152Sgad */ 131198152Sgadvoid 131298152Sgadup_q(struct printer *pp) 131398152Sgad{ 131498152Sgad int setres, startok; 131598152Sgad char lf[MAXPATHLEN]; 131698152Sgad 131798152Sgad lock_file_name(pp, lf, sizeof lf); 131898152Sgad printf("%s:\n", pp->printer); 131998152Sgad 132098152Sgad setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); 132198152Sgad 132298152Sgad seteuid(euid); 132398152Sgad startok = startdaemon(pp); 132498152Sgad seteuid(uid); 132598152Sgad if (!startok) 132698152Sgad printf("\tcouldn't start daemon\n"); 132798152Sgad else 132898152Sgad printf("\tdaemon started\n"); 132998152Sgad} 1330