cmds.c revision 234244
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 234244 2012-04-13 22:34:01Z delphij $"); 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 } 16698267Sgad } 16798267Sgad 16878750Sgad /* call initialization routine, if there is one for this cmd */ 16978750Sgad if (initrtn != NULL) { 17078750Sgad generic_initerr = 0; 17178750Sgad (*initrtn)(argc, argv); 17278750Sgad if (generic_initerr) 17378750Sgad return; 17498268Sgad /* 17598268Sgad * The initrtn may have null'ed out some of the parameters. 17698268Sgad * Compact the parameter list to remove those nulls, and 17798268Sgad * correct the arg-count. 17898268Sgad */ 17978750Sgad targc = argc; 18078750Sgad targv = argv; 18198268Sgad margv = argv; 18298268Sgad argc = 0; 18398268Sgad for (; targc > 0; targc--, targv++) { 18498268Sgad if (*targv != generic_nullarg) { 18598268Sgad if (targv != margv) 18698268Sgad *margv = *targv; 18798268Sgad margv++; 18898268Sgad argc++; 18998268Sgad } 19078750Sgad } 19178750Sgad } 19278750Sgad 19398268Sgad if (argc == 1 && strcmp(*argv, "all") == 0) { 19478750Sgad generic_qselect = QSEL_ALL; 19578146Sgad more = firstprinter(pp, &cmdstatus); 19678146Sgad if (cmdstatus) 19731492Swollman goto looperr; 19831492Swollman while (more) { 19978146Sgad (*specificrtn)(pp); 20031492Swollman do { 20178146Sgad more = nextprinter(pp, &cmdstatus); 20231492Swollmanlooperr: 20378146Sgad switch (cmdstatus) { 20431492Swollman case PCAPERR_TCOPEN: 20531492Swollman printf("warning: %s: unresolved " 20631492Swollman "tc= reference(s) ", 20731492Swollman pp->printer); 20831492Swollman case PCAPERR_SUCCESS: 20931492Swollman break; 21031492Swollman default: 21179739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 21231492Swollman } 21378146Sgad } while (more && cmdstatus); 2141553Srgrimes } 21578750Sgad goto wrapup; 2161553Srgrimes } 21778750Sgad 21878750Sgad generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ 21998268Sgad for (; argc > 0; argc--, argv++) { 22031492Swollman init_printer(pp); 22178146Sgad cmdstatus = getprintcap(*argv, pp); 22278146Sgad switch (cmdstatus) { 22331492Swollman default: 22479739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 22531492Swollman case PCAPERR_NOTFOUND: 22631492Swollman printf("unknown printer %s\n", *argv); 2271553Srgrimes continue; 22831492Swollman case PCAPERR_TCOPEN: 22931492Swollman printf("warning: %s: unresolved tc= reference(s)\n", 23031492Swollman *argv); 23131492Swollman break; 23231492Swollman case PCAPERR_SUCCESS: 23331492Swollman break; 23431492Swollman } 23578146Sgad (*specificrtn)(pp); 2361553Srgrimes } 23778750Sgad 23878750Sgadwrapup: 23978750Sgad if (generic_wrapup) { 24078750Sgad (*generic_wrapup)(cmdstatus); 24178750Sgad } 24299846Sgad free_printer(pp); 24398267Sgad if (generic_msg) 24498267Sgad free(generic_msg); 24598267Sgad} 24678750Sgad 24798267Sgad/* 24898267Sgad * Convert an argv-array of character strings into a single string. 24998267Sgad */ 25098267Sgadstatic char * 25198267Sgadargs2line(int argc, char **argv) 25298267Sgad{ 25398267Sgad char *cp1, *cend; 25498267Sgad const char *cp2; 25598267Sgad char buf[1024]; 25698267Sgad 25798267Sgad if (argc <= 0) 25898267Sgad return strdup("\n"); 25998267Sgad 26098267Sgad cp1 = buf; 26198267Sgad cend = buf + sizeof(buf) - 1; /* save room for '\0' */ 26298267Sgad while (--argc >= 0) { 26398267Sgad cp2 = *argv++; 26498267Sgad while ((cp1 < cend) && (*cp1++ = *cp2++)) 26598267Sgad ; 26698267Sgad cp1[-1] = ' '; 26798267Sgad } 26898267Sgad cp1[-1] = '\n'; 26998267Sgad *cp1 = '\0'; 27098267Sgad return strdup(buf); 2711553Srgrimes} 2721553Srgrimes 27331492Swollman/* 27498152Sgad * Kill the current daemon, to stop printing of the active job. 27598152Sgad */ 27698152Sgadstatic int 27798152Sgadkill_qtask(const char *lf) 27898152Sgad{ 27998152Sgad FILE *fp; 28098152Sgad pid_t pid; 28198152Sgad int errsav, killres, lockres, res; 28298152Sgad 28398152Sgad seteuid(euid); 28498152Sgad fp = fopen(lf, "r"); 28598152Sgad errsav = errno; 28698152Sgad seteuid(uid); 28798152Sgad res = KQT_NODAEMON; 28898152Sgad if (fp == NULL) { 28998152Sgad /* 29098152Sgad * If there is no lock file, then there is no daemon to 29198152Sgad * kill. Any other error return means there is some 29298152Sgad * kind of problem with the lock file. 29398152Sgad */ 29498152Sgad if (errsav != ENOENT) 29598152Sgad res = KQT_LFERROR; 29698152Sgad goto killdone; 29798152Sgad } 29898152Sgad 29998152Sgad /* If the lock file is empty, then there is no daemon to kill */ 30098152Sgad if (getline(fp) == 0) 30198152Sgad goto killdone; 30298152Sgad 30398152Sgad /* 30498152Sgad * If the file can be locked without blocking, then there 30598152Sgad * no daemon to kill, or we should not try to kill it. 30698152Sgad * 30798152Sgad * XXX - not sure I understand the reasoning behind this... 30898152Sgad */ 30998152Sgad lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); 31098152Sgad (void) fclose(fp); 31198152Sgad if (lockres == 0) 31298152Sgad goto killdone; 31398152Sgad 31498152Sgad pid = atoi(line); 31598152Sgad if (pid < 0) { 31698152Sgad /* 31798152Sgad * If we got a negative pid, then the contents of the 31898152Sgad * lock file is not valid. 31998152Sgad */ 32098152Sgad res = KQT_LFERROR; 32198152Sgad goto killdone; 32298152Sgad } 32398152Sgad 32498152Sgad seteuid(uid); 32598152Sgad killres = kill(pid, SIGTERM); 32698152Sgad errsav = errno; 32798152Sgad seteuid(uid); 32898152Sgad if (killres == 0) { 32998152Sgad res = KQT_KILLOK; 33098152Sgad printf("\tdaemon (pid %d) killed\n", pid); 33198152Sgad } else if (errno == ESRCH) { 33298152Sgad res = KQT_NODAEMON; 33398152Sgad } else { 33498152Sgad res = KQT_KILLFAIL; 33598152Sgad printf("\tWarning: daemon (pid %d) not killed:\n", pid); 33698152Sgad printf("\t %s\n", strerror(errsav)); 33798152Sgad } 33898152Sgad 33998152Sgadkilldone: 34098152Sgad switch (res) { 34198152Sgad case KQT_LFERROR: 34298152Sgad printf("\tcannot open lock file: %s\n", 34398152Sgad strerror(errsav)); 34498152Sgad break; 34598152Sgad case KQT_NODAEMON: 34698152Sgad printf("\tno daemon to abort\n"); 34798152Sgad break; 34898152Sgad case KQT_KILLFAIL: 34998152Sgad case KQT_KILLOK: 35098152Sgad /* These two already printed messages to the user. */ 35198152Sgad break; 35298152Sgad default: 35398152Sgad printf("\t<internal error in kill_qtask>\n"); 35498152Sgad break; 35598152Sgad } 35698152Sgad 35798152Sgad return (res); 35898152Sgad} 35998152Sgad 36098152Sgad/* 3611553Srgrimes * Write a message into the status file. 3621553Srgrimes */ 3631553Srgrimesstatic void 36498267Sgadupstat(struct printer *pp, const char *msg, int notifyuser) 3651553Srgrimes{ 36698267Sgad int fd; 36727748Simp char statfile[MAXPATHLEN]; 3681553Srgrimes 36931492Swollman status_file_name(pp, statfile, sizeof statfile); 3701553Srgrimes umask(0); 37198267Sgad seteuid(euid); 37231492Swollman fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); 37398267Sgad seteuid(uid); 37431492Swollman if (fd < 0) { 37531492Swollman printf("\tcannot create status file: %s\n", strerror(errno)); 3761553Srgrimes return; 3771553Srgrimes } 3781553Srgrimes (void) ftruncate(fd, 0); 379231723Skevlo if (msg == NULL) 3801553Srgrimes (void) write(fd, "\n", 1); 3811553Srgrimes else 3821553Srgrimes (void) write(fd, msg, strlen(msg)); 3831553Srgrimes (void) close(fd); 38498267Sgad if (notifyuser) { 38598267Sgad if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) 38698267Sgad printf("\tstatus message is now set to nothing.\n"); 38798267Sgad else 38898267Sgad printf("\tstatus message is now: %s", msg); 38998267Sgad } 3901553Srgrimes} 3911553Srgrimes 39278750Sgad/* 39398152Sgad * kill an existing daemon and disable printing. 39498152Sgad */ 39598152Sgadvoid 39698152Sgadabort_q(struct printer *pp) 39798152Sgad{ 39898152Sgad int killres, setres; 39998152Sgad char lf[MAXPATHLEN]; 40098152Sgad 40198152Sgad lock_file_name(pp, lf, sizeof lf); 40298152Sgad printf("%s:\n", pp->printer); 40398152Sgad 40498152Sgad /* 40598152Sgad * Turn on the owner execute bit of the lock file to disable printing. 40698152Sgad */ 40798152Sgad setres = set_qstate(SQS_STOPP, lf); 40898152Sgad 40998152Sgad /* 41098152Sgad * If set_qstate found that there already was a lock file, then 41198152Sgad * call a routine which will read that lock file and kill the 41298152Sgad * lpd-process which is listed in that lock file. If the lock 41398152Sgad * file did not exist, then either there is no daemon running 41498152Sgad * for this queue, or there is one running but *it* could not 41598152Sgad * write a lock file (which means we can not determine the 41698152Sgad * process id of that lpd-process). 41798152Sgad */ 41898152Sgad switch (setres) { 41998152Sgad case SQS_CHGOK: 42098152Sgad case SQS_CHGFAIL: 42198152Sgad /* Kill the process */ 42298152Sgad killres = kill_qtask(lf); 42398152Sgad break; 42498152Sgad case SQS_CREOK: 42598152Sgad case SQS_CREFAIL: 42698152Sgad printf("\tno daemon to abort\n"); 42798152Sgad break; 42898152Sgad case SQS_STATFAIL: 42998152Sgad printf("\tassuming no daemon to abort\n"); 43098152Sgad break; 43198152Sgad default: 43298152Sgad printf("\t<unexpected result (%d) from set_qstate>\n", 43398152Sgad setres); 43498152Sgad break; 43598152Sgad } 43698152Sgad 43798267Sgad if (setres >= 0) 43898267Sgad upstat(pp, "printing disabled\n", 0); 43998152Sgad} 44098152Sgad 44198152Sgad/* 44278750Sgad * "global" variables for all the routines related to 'clean' and 'tclean' 44378750Sgad */ 44478750Sgadstatic time_t cln_now; /* current time */ 44578750Sgadstatic double cln_minage; /* minimum age before file is removed */ 44678750Sgadstatic long cln_sizecnt; /* amount of space freed up */ 44778750Sgadstatic int cln_debug; /* print extra debugging msgs */ 44878750Sgadstatic int cln_filecnt; /* number of files destroyed */ 44978750Sgadstatic int cln_foundcore; /* found a core file! */ 45078750Sgadstatic int cln_queuecnt; /* number of queues checked */ 45178750Sgadstatic int cln_testonly; /* remove-files vs just-print-info */ 45278750Sgad 4531553Srgrimesstatic int 454231723Skevlodoselect(const struct dirent *d) 4551553Srgrimes{ 4561553Srgrimes int c = d->d_name[0]; 4571553Srgrimes 45887034Sgad if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && 45987034Sgad d->d_name[1] == 'f') 46078750Sgad return 1; 46178750Sgad if (c == 'c') { 46278750Sgad if (!strcmp(d->d_name, "core")) 46378750Sgad cln_foundcore = 1; 46478750Sgad } 46578750Sgad if (c == 'e') { 46678750Sgad if (!strncmp(d->d_name, "errs.", 5)) 46778750Sgad return 1; 46878750Sgad } 46978750Sgad return 0; 4701553Srgrimes} 4711553Srgrimes 4721553Srgrimes/* 47384034Sgad * Comparison routine that clean_q() uses for scandir. 47484034Sgad * 47584034Sgad * The purpose of this sort is to have all `df' files end up immediately 47687034Sgad * after the matching `cf' file. For files matching `cf', `df', `rf', or 47787034Sgad * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or 47887034Sgad * `tf', and then by the sequence letter (which is A-Z, or a-z). This 47987034Sgad * routine may also see filenames which do not start with `cf', `df', `rf', 48087034Sgad * or `tf' (such as `errs.*'), and those are simply sorted by the full 48187034Sgad * filename. 48287034Sgad * 48387034Sgad * XXX 48487034Sgad * This assumes that all control files start with `cfA*', and it turns 48587034Sgad * out there are a few implementations of lpr which will create `cfB*' 48687034Sgad * filenames (they will have datafile names which start with `dfB*'). 4871553Srgrimes */ 4881553Srgrimesstatic int 489234244Sdelphijsortq(const struct dirent **a, const struct dirent **b) 4901553Srgrimes{ 49184034Sgad const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; 49284034Sgad const char *fname_a, *fname_b, *jnum_a, *jnum_b; 49384034Sgad int cat_a, cat_b, ch, res, seq_a, seq_b; 4941553Srgrimes 495234244Sdelphij fname_a = (*a)->d_name; 496234244Sdelphij fname_b = (*b)->d_name; 49784034Sgad 49884034Sgad /* 499228990Suqs * First separate filenames into categories. Categories are 50087034Sgad * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in 50187034Sgad * that order. It is critical that the mapping be exactly the 50287034Sgad * same for 'a' vs 'b', so define a macro for the job. 50384034Sgad * 50484034Sgad * [aside: the standard `cf' file has the jobnumber start in 50584034Sgad * position 4, but some implementations have that as an extra 50684034Sgad * file-sequence letter, and start the job number in position 5.] 50784034Sgad */ 50884034Sgad#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ 50984034Sgad cat_X = cat_other; \ 51084034Sgad ch = *(fname_X + 2); \ 51184034Sgad jnum_X = fname_X + 3; \ 51284691Sgad seq_X = 0; \ 51384034Sgad if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ 51484034Sgad seq_X = ch; \ 51584034Sgad if (*fname_X == 'c') \ 51684034Sgad cat_X = 1; \ 51784034Sgad else if (*fname_X == 'd') \ 51884034Sgad cat_X = 2; \ 51987034Sgad else if (*fname_X == 'r') \ 52087034Sgad cat_X = 3; \ 52184034Sgad else if (*fname_X == 't') \ 52287034Sgad cat_X = 4; \ 52384034Sgad if (cat_X != cat_other) { \ 52484034Sgad ch = *jnum_X; \ 52584034Sgad if (!isdigit(ch)) { \ 52684034Sgad if (isalpha(ch)) { \ 52784034Sgad jnum_X++; \ 52884034Sgad ch = *jnum_X; \ 52984034Sgad seq_X = (seq_X << 8) + ch; \ 53084034Sgad } \ 53184034Sgad if (!isdigit(ch)) \ 53284034Sgad cat_X = cat_other; \ 53384034Sgad } \ 53484034Sgad } \ 53584034Sgad } \ 53684034Sgad} while (0) 53784034Sgad 53884034Sgad MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); 53984034Sgad MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); 54084034Sgad 54184034Sgad#undef MAP_TO_CAT 54284034Sgad 54384034Sgad /* First handle all cases which have "other" files */ 54484034Sgad if ((cat_a >= cat_other) || (cat_b >= cat_other)) { 54584034Sgad /* for two "other" files, just compare the full name */ 54684034Sgad if (cat_a == cat_b) 54784034Sgad res = strcmp(fname_a, fname_b); 54884034Sgad else if (cat_a < cat_b) 54984034Sgad res = a_lt_b; 55084034Sgad else 55184034Sgad res = a_gt_b; 55284034Sgad goto have_res; 55384034Sgad } 55484034Sgad 55584034Sgad /* 55687034Sgad * At this point, we know both files are legitimate `cf', `df', `rf', 55784034Sgad * or `tf' files. Compare them by job-number and machine name. 55884034Sgad */ 55984034Sgad res = strcmp(jnum_a, jnum_b); 56084034Sgad if (res != 0) 56184034Sgad goto have_res; 56284034Sgad 56384034Sgad /* 56484034Sgad * We have two files which belong to the same job. Sort based 565228990Suqs * on the category of file (`c' before `d', etc). 56684034Sgad */ 56784034Sgad if (cat_a < cat_b) { 56884034Sgad res = a_lt_b; 56984034Sgad goto have_res; 57084034Sgad } else if (cat_a > cat_b) { 57184034Sgad res = a_gt_b; 57284034Sgad goto have_res; 57384034Sgad } 57484034Sgad 57584034Sgad /* 576228990Suqs * Two files in the same category for a single job. Sort based 577228990Suqs * on the sequence letter(s). (usually `A' through `Z', etc). 57884034Sgad */ 57984034Sgad if (seq_a < seq_b) { 58084034Sgad res = a_lt_b; 58184034Sgad goto have_res; 58284034Sgad } else if (seq_a > seq_b) { 58384034Sgad res = a_gt_b; 58484034Sgad goto have_res; 58584034Sgad } 58684034Sgad 58784034Sgad /* 58884034Sgad * Given that the filenames in a directory are unique, this SHOULD 58984034Sgad * never happen (unless there are logic errors in this routine). 59084034Sgad * But if it does happen, we must return "is equal" or the caller 59184034Sgad * might see inconsistent results in the sorting order, and that 59284034Sgad * can trigger other problems. 59384034Sgad */ 59484034Sgad printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); 59584034Sgad printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, 59684034Sgad seq_a, seq_b); 59784034Sgad res = 0; 59884034Sgad 59984034Sgadhave_res: 60084034Sgad return res; 6011553Srgrimes} 6021553Srgrimes 6031553Srgrimes/* 60431492Swollman * Remove all spool files and temporaries from the spooling area. 60531492Swollman * Or, perhaps: 6061553Srgrimes * Remove incomplete jobs from spooling area. 6071553Srgrimes */ 60878750Sgad 60931492Swollmanvoid 61098279Sgadclean_gi(int argc, char *argv[]) 6111553Srgrimes{ 61278750Sgad 61378750Sgad /* init some fields before 'clean' is called for each queue */ 61478750Sgad cln_queuecnt = 0; 61578750Sgad cln_now = time(NULL); 61678750Sgad cln_minage = 3600.0; /* only delete files >1h old */ 61778750Sgad cln_filecnt = 0; 61878750Sgad cln_sizecnt = 0; 61978750Sgad cln_debug = 0; 62078750Sgad cln_testonly = 0; 62178750Sgad generic_wrapup = &wrapup_clean; 62278750Sgad 62378750Sgad /* see if there are any options specified before the ptr list */ 62498268Sgad for (; argc > 0; argc--, argv++) { 62578750Sgad if (**argv != '-') 62678750Sgad break; 62778750Sgad if (strcmp(*argv, "-d") == 0) { 62878750Sgad /* just an example of an option... */ 62984034Sgad cln_debug++; 63078750Sgad *argv = generic_nullarg; /* "erase" it */ 63178750Sgad } else { 63278750Sgad printf("Invalid option '%s'\n", *argv); 63378750Sgad generic_initerr = 1; 63478750Sgad } 63578750Sgad } 63678750Sgad 63778750Sgad return; 63878750Sgad} 63978750Sgad 64078750Sgadvoid 64198279Sgadtclean_gi(int argc, char *argv[]) 64278750Sgad{ 64378750Sgad 64478750Sgad /* only difference between 'clean' and 'tclean' is one value */ 64578750Sgad /* (...and the fact that 'clean' is priv and 'tclean' is not) */ 64698279Sgad clean_gi(argc, argv); 64778750Sgad cln_testonly = 1; 64878750Sgad 64978750Sgad return; 65078750Sgad} 65178750Sgad 65278750Sgadvoid 65378750Sgadclean_q(struct printer *pp) 65478750Sgad{ 65578750Sgad char *cp, *cp1, *lp; 6561553Srgrimes struct dirent **queue; 65778750Sgad size_t linerem; 65878750Sgad int didhead, i, n, nitems, rmcp; 6591553Srgrimes 66078750Sgad cln_queuecnt++; 6611553Srgrimes 66278750Sgad didhead = 0; 66378750Sgad if (generic_qselect == QSEL_BYNAME) { 66478750Sgad printf("%s:\n", pp->printer); 66578750Sgad didhead = 1; 66678750Sgad } 66778750Sgad 66831492Swollman lp = line; 66931492Swollman cp = pp->spool_dir; 67031492Swollman while (lp < &line[sizeof(line) - 1]) { 67131492Swollman if ((*lp++ = *cp++) == 0) 67231492Swollman break; 67331492Swollman } 6741553Srgrimes lp[-1] = '/'; 67578750Sgad linerem = sizeof(line) - (lp - line); 6761553Srgrimes 67778750Sgad cln_foundcore = 0; 67827618Simp seteuid(euid); 67931492Swollman nitems = scandir(pp->spool_dir, &queue, doselect, sortq); 68027618Simp seteuid(uid); 6811553Srgrimes if (nitems < 0) { 68278750Sgad if (!didhead) { 68378750Sgad printf("%s:\n", pp->printer); 68478750Sgad didhead = 1; 68578750Sgad } 6861553Srgrimes printf("\tcannot examine spool directory\n"); 6871553Srgrimes return; 6881553Srgrimes } 68978750Sgad if (cln_foundcore) { 69078750Sgad if (!didhead) { 69178750Sgad printf("%s:\n", pp->printer); 69278750Sgad didhead = 1; 69378750Sgad } 69478750Sgad printf("\t** found a core file in %s !\n", pp->spool_dir); 69578750Sgad } 6961553Srgrimes if (nitems == 0) 6971553Srgrimes return; 69878750Sgad if (!didhead) 69978750Sgad printf("%s:\n", pp->printer); 70084034Sgad if (cln_debug) { 70184034Sgad printf("\t** ----- Sorted list of files being checked:\n"); 70284034Sgad i = 0; 70384034Sgad do { 70484034Sgad cp = queue[i]->d_name; 70584034Sgad printf("\t** [%3d] = %s\n", i, cp); 70684034Sgad } while (++i < nitems); 70784034Sgad printf("\t** ----- end of sorted list\n"); 70884034Sgad } 7091553Srgrimes i = 0; 7101553Srgrimes do { 7111553Srgrimes cp = queue[i]->d_name; 71278750Sgad rmcp = 0; 7131553Srgrimes if (*cp == 'c') { 71478750Sgad /* 71578750Sgad * A control file. Look for matching data-files. 71678750Sgad */ 71778750Sgad /* XXX 71878750Sgad * Note the logic here assumes that the hostname 71978750Sgad * part of cf-filenames match the hostname part 72078750Sgad * in df-filenames, and that is not necessarily 72178750Sgad * true (eg: for multi-homed hosts). This needs 72278750Sgad * some further thought... 72378750Sgad */ 7241553Srgrimes n = 0; 7251553Srgrimes while (i + 1 < nitems) { 7261553Srgrimes cp1 = queue[i + 1]->d_name; 7271553Srgrimes if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) 7281553Srgrimes break; 7291553Srgrimes i++; 7301553Srgrimes n++; 7311553Srgrimes } 7321553Srgrimes if (n == 0) { 73378750Sgad rmcp = 1; 7341553Srgrimes } 73578750Sgad } else if (*cp == 'e') { 73678750Sgad /* 73778750Sgad * Must be an errrs or email temp file. 73878750Sgad */ 73978750Sgad rmcp = 1; 7401553Srgrimes } else { 7411553Srgrimes /* 7421553Srgrimes * Must be a df with no cf (otherwise, it would have 74387034Sgad * been skipped above) or an rf or tf file (which can 74487034Sgad * always be removed if it is old enough). 7451553Srgrimes */ 74678750Sgad rmcp = 1; 74778750Sgad } 74878750Sgad if (rmcp) { 74978750Sgad if (strlen(cp) >= linerem) { 75078750Sgad printf("\t** internal error: 'line' overflow!\n"); 75178750Sgad printf("\t** spooldir = %s\n", pp->spool_dir); 75278750Sgad printf("\t** cp = %s\n", cp); 75378750Sgad return; 75478750Sgad } 75578750Sgad strlcpy(lp, cp, linerem); 7561553Srgrimes unlinkf(line); 7571553Srgrimes } 7581553Srgrimes } while (++i < nitems); 7591553Srgrimes} 76078750Sgad 76178750Sgadstatic void 76278750Sgadwrapup_clean(int laststatus __unused) 76378750Sgad{ 76478750Sgad 76578750Sgad printf("Checked %d queues, and ", cln_queuecnt); 76678750Sgad if (cln_filecnt < 1) { 76778750Sgad printf("no cruft was found\n"); 76878750Sgad return; 76978750Sgad } 77078750Sgad if (cln_testonly) { 77178750Sgad printf("would have "); 77278750Sgad } 77378750Sgad printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); 77478750Sgad} 77527618Simp 7761553Srgrimesstatic void 77778146Sgadunlinkf(char *name) 7781553Srgrimes{ 77978750Sgad struct stat stbuf; 78078750Sgad double agemod, agestat; 78178750Sgad int res; 78278750Sgad char linkbuf[BUFSIZ]; 78378750Sgad 78478750Sgad /* 78578750Sgad * We have to use lstat() instead of stat(), in case this is a df* 78678750Sgad * "file" which is really a symlink due to 'lpr -s' processing. In 78778750Sgad * that case, we need to check the last-mod time of the symlink, and 78878750Sgad * not the file that the symlink is pointed at. 78978750Sgad */ 79027618Simp seteuid(euid); 79178750Sgad res = lstat(name, &stbuf); 79227618Simp seteuid(uid); 79378750Sgad if (res < 0) { 79478750Sgad printf("\terror return from stat(%s):\n", name); 79578750Sgad printf("\t %s\n", strerror(errno)); 79678750Sgad return; 79778750Sgad } 79878750Sgad 79978750Sgad agemod = difftime(cln_now, stbuf.st_mtime); 80078750Sgad agestat = difftime(cln_now, stbuf.st_ctime); 80184034Sgad if (cln_debug > 1) { 80278750Sgad /* this debugging-aid probably is not needed any more... */ 80378750Sgad printf("\t\t modify age=%g secs, stat age=%g secs\n", 80478750Sgad agemod, agestat); 80578750Sgad } 80678750Sgad if ((agemod <= cln_minage) && (agestat <= cln_minage)) 80778750Sgad return; 80878750Sgad 80978750Sgad /* 81078750Sgad * if this file is a symlink, then find out the target of the 81178750Sgad * symlink before unlink-ing the file itself 81278750Sgad */ 81378750Sgad if (S_ISLNK(stbuf.st_mode)) { 81478750Sgad seteuid(euid); 81578750Sgad res = readlink(name, linkbuf, sizeof(linkbuf)); 81678750Sgad seteuid(uid); 81778750Sgad if (res < 0) { 81878750Sgad printf("\terror return from readlink(%s):\n", name); 81978750Sgad printf("\t %s\n", strerror(errno)); 82078750Sgad return; 82178750Sgad } 82278750Sgad if (res == sizeof(linkbuf)) 82378750Sgad res--; 82478750Sgad linkbuf[res] = '\0'; 82578750Sgad } 82678750Sgad 82778750Sgad cln_filecnt++; 82878750Sgad cln_sizecnt += stbuf.st_size; 82978750Sgad 83078750Sgad if (cln_testonly) { 83178750Sgad printf("\twould remove %s\n", name); 83278750Sgad if (S_ISLNK(stbuf.st_mode)) { 83378750Sgad printf("\t (which is a symlink to %s)\n", linkbuf); 83478750Sgad } 83578750Sgad } else { 83678750Sgad seteuid(euid); 83778750Sgad res = unlink(name); 83878750Sgad seteuid(uid); 83978750Sgad if (res < 0) 84078750Sgad printf("\tcannot remove %s (!)\n", name); 84178750Sgad else 84278750Sgad printf("\tremoved %s\n", name); 84378750Sgad /* XXX 84478750Sgad * Note that for a df* file, this code should also check to see 84578750Sgad * if it is a symlink to some other file, and if the original 84678750Sgad * lpr command included '-r' ("remove file"). Of course, this 84778750Sgad * code would not be removing the df* file unless there was no 84878750Sgad * matching cf* file, and without the cf* file it is currently 84978750Sgad * impossible to determine if '-r' had been specified... 85078750Sgad * 85178750Sgad * As a result of this quandry, we may be leaving behind a 85278750Sgad * user's file that was supposed to have been removed after 85378750Sgad * being printed. This may effect services such as CAP or 85478750Sgad * samba, if they were configured to use 'lpr -r', and if 85578750Sgad * datafiles are not being properly removed. 85678750Sgad */ 85778750Sgad if (S_ISLNK(stbuf.st_mode)) { 85878750Sgad printf("\t (which was a symlink to %s)\n", linkbuf); 85978750Sgad } 86078750Sgad } 8611553Srgrimes} 8621553Srgrimes 8631553Srgrimes/* 86498152Sgad * Enable queuing to the printer (allow lpr to add new jobs to the queue). 86598152Sgad */ 86698152Sgadvoid 86798152Sgadenable_q(struct printer *pp) 86898152Sgad{ 86998152Sgad int setres; 87098152Sgad char lf[MAXPATHLEN]; 87198152Sgad 87298152Sgad lock_file_name(pp, lf, sizeof lf); 87398152Sgad printf("%s:\n", pp->printer); 87498152Sgad 87598152Sgad setres = set_qstate(SQS_ENABLEQ, lf); 87698152Sgad} 87798152Sgad 87898152Sgad/* 8791553Srgrimes * Disable queuing. 8801553Srgrimes */ 8811553Srgrimesvoid 88298152Sgaddisable_q(struct printer *pp) 88398152Sgad{ 88498152Sgad int setres; 88598152Sgad char lf[MAXPATHLEN]; 88698152Sgad 88798152Sgad lock_file_name(pp, lf, sizeof lf); 88898152Sgad printf("%s:\n", pp->printer); 88998152Sgad 89098152Sgad setres = set_qstate(SQS_DISABLEQ, lf); 89198152Sgad} 89298152Sgad 89398152Sgad/* 8941553Srgrimes * Disable queuing and printing and put a message into the status file 89598278Sgad * (reason for being down). If the user specified `-msg', then use 89698278Sgad * everything after that as the message for the status file. If the 89798278Sgad * user did NOT specify `-msg', then the command should take the first 89898278Sgad * parameter as the printer name, and all remaining parameters as the 89998278Sgad * message for the status file. (This is to be compatible with the 90098278Sgad * original definition of 'down', which was implemented long before 90198278Sgad * `-msg' was around). 90298278Sgad */ 90398278Sgadvoid 90498278Sgaddown_gi(int argc, char *argv[]) 90598278Sgad{ 90698278Sgad 90798278Sgad /* If `-msg' was specified, then this routine has nothing to do. */ 90898278Sgad if (generic_msg != NULL) 90998278Sgad return; 91098278Sgad 91198278Sgad /* 91298278Sgad * If the user only gave one parameter, then use a default msg. 91398278Sgad * (if argc == 1 at this point, then *argv == name of printer). 91498278Sgad */ 91598278Sgad if (argc == 1) { 91698278Sgad generic_msg = strdup("printing disabled\n"); 91798278Sgad return; 91898278Sgad } 91998278Sgad 92098278Sgad /* 92198278Sgad * The user specified multiple parameters, and did not specify 92298278Sgad * `-msg'. Build a message from all the parameters after the 92398278Sgad * first one (and nullify those parameters so generic-processing 92498278Sgad * will not process them as printer-queue names). 92598278Sgad */ 92698278Sgad argc--; 92798278Sgad argv++; 92898278Sgad generic_msg = args2line(argc, argv); 92998278Sgad for (; argc > 0; argc--, argv++) 93098278Sgad *argv = generic_nullarg; /* "erase" it */ 93198278Sgad} 93298278Sgad 93398278Sgadvoid 93498278Sgaddown_q(struct printer *pp) 93598278Sgad{ 93698278Sgad int setres; 93798278Sgad char lf[MAXPATHLEN]; 93898278Sgad 93998278Sgad lock_file_name(pp, lf, sizeof lf); 94098278Sgad printf("%s:\n", pp->printer); 94198278Sgad 94298278Sgad setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf); 94398278Sgad if (setres >= 0) 94498278Sgad upstat(pp, generic_msg, 1); 94598278Sgad} 94698278Sgad 94798278Sgad/* 9481553Srgrimes * Exit lpc 9491553Srgrimes */ 9501553Srgrimesvoid 95178146Sgadquit(int argc __unused, char *argv[] __unused) 9521553Srgrimes{ 9531553Srgrimes exit(0); 9541553Srgrimes} 9551553Srgrimes 9561553Srgrimes/* 9571553Srgrimes * Kill and restart the daemon. 9581553Srgrimes */ 9591553Srgrimesvoid 96098152Sgadrestart_q(struct printer *pp) 96198152Sgad{ 96298152Sgad int killres, setres, startok; 96398152Sgad char lf[MAXPATHLEN]; 96498152Sgad 96598152Sgad lock_file_name(pp, lf, sizeof lf); 96698152Sgad printf("%s:\n", pp->printer); 96798152Sgad 96898152Sgad killres = kill_qtask(lf); 96998152Sgad 97098152Sgad /* 97198152Sgad * XXX - if the kill worked, we should probably sleep for 97298152Sgad * a second or so before trying to restart the queue. 97398152Sgad */ 97498152Sgad 97598152Sgad /* make sure the queue is set to print jobs */ 97698152Sgad setres = set_qstate(SQS_STARTP, lf); 97798152Sgad 97898152Sgad seteuid(euid); 97998152Sgad startok = startdaemon(pp); 98098152Sgad seteuid(uid); 98198152Sgad if (!startok) 98298152Sgad printf("\tcouldn't restart daemon\n"); 98398152Sgad else 98498152Sgad printf("\tdaemon restarted\n"); 98598152Sgad} 98698152Sgad 98798152Sgad/* 98898267Sgad * Set the status message of each queue listed. Requires a "-msg" 98998267Sgad * parameter to indicate the end of the queue list and start of msg text. 99098267Sgad */ 99198267Sgadvoid 99298267Sgadsetstatus_gi(int argc __unused, char *argv[] __unused) 99398267Sgad{ 99498267Sgad 99598267Sgad if (generic_msg == NULL) { 99698267Sgad printf("You must specify '-msg' before the text of the new status message.\n"); 99798267Sgad generic_initerr = 1; 99898267Sgad } 99998267Sgad} 100098267Sgad 100198267Sgadvoid 100298267Sgadsetstatus_q(struct printer *pp) 100398267Sgad{ 100498267Sgad char lf[MAXPATHLEN]; 100598267Sgad 100698267Sgad lock_file_name(pp, lf, sizeof lf); 100798267Sgad printf("%s:\n", pp->printer); 100898267Sgad 100998267Sgad upstat(pp, generic_msg, 1); 101098267Sgad} 101198267Sgad 101298267Sgad/* 10131553Srgrimes * Enable printing on the specified printer and startup the daemon. 10141553Srgrimes */ 10151553Srgrimesvoid 101698152Sgadstart_q(struct printer *pp) 101798152Sgad{ 101898152Sgad int setres, startok; 101998152Sgad char lf[MAXPATHLEN]; 102098152Sgad 102198152Sgad lock_file_name(pp, lf, sizeof lf); 102298152Sgad printf("%s:\n", pp->printer); 102398152Sgad 102498152Sgad setres = set_qstate(SQS_STARTP, lf); 102598152Sgad 102698152Sgad seteuid(euid); 102798152Sgad startok = startdaemon(pp); 102898152Sgad seteuid(uid); 102998152Sgad if (!startok) 103098152Sgad printf("\tcouldn't start daemon\n"); 103198152Sgad else 103298152Sgad printf("\tdaemon started\n"); 103398152Sgad seteuid(uid); 103498152Sgad} 103598152Sgad 103698152Sgad/* 103731492Swollman * Print the status of the printer queue. 10381553Srgrimes */ 10391553Srgrimesvoid 104078146Sgadstatus(struct printer *pp) 10411553Srgrimes{ 10421553Srgrimes struct stat stbuf; 10431553Srgrimes register int fd, i; 10441553Srgrimes register struct dirent *dp; 10451553Srgrimes DIR *dirp; 104631492Swollman char file[MAXPATHLEN]; 10471553Srgrimes 104831492Swollman printf("%s:\n", pp->printer); 104931492Swollman lock_file_name(pp, file, sizeof file); 105031492Swollman if (stat(file, &stbuf) >= 0) { 10511553Srgrimes printf("\tqueuing is %s\n", 105231492Swollman ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" 105331492Swollman : "enabled")); 10541553Srgrimes printf("\tprinting is %s\n", 105531492Swollman ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" 105631492Swollman : "enabled")); 10571553Srgrimes } else { 10581553Srgrimes printf("\tqueuing is enabled\n"); 10591553Srgrimes printf("\tprinting is enabled\n"); 10601553Srgrimes } 106131492Swollman if ((dirp = opendir(pp->spool_dir)) == NULL) { 10621553Srgrimes printf("\tcannot examine spool directory\n"); 10631553Srgrimes return; 10641553Srgrimes } 10651553Srgrimes i = 0; 10661553Srgrimes while ((dp = readdir(dirp)) != NULL) { 10671553Srgrimes if (*dp->d_name == 'c' && dp->d_name[1] == 'f') 10681553Srgrimes i++; 10691553Srgrimes } 10701553Srgrimes closedir(dirp); 10711553Srgrimes if (i == 0) 107231492Swollman printf("\tno entries in spool area\n"); 10731553Srgrimes else if (i == 1) 10741553Srgrimes printf("\t1 entry in spool area\n"); 10751553Srgrimes else 10761553Srgrimes printf("\t%d entries in spool area\n", i); 107731492Swollman fd = open(file, O_RDONLY); 10781553Srgrimes if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { 10791553Srgrimes (void) close(fd); /* unlocks as well */ 108019202Simp printf("\tprinter idle\n"); 10811553Srgrimes return; 10821553Srgrimes } 10831553Srgrimes (void) close(fd); 108425789Sbrian /* print out the contents of the status file, if it exists */ 108531492Swollman status_file_name(pp, file, sizeof file); 108631492Swollman fd = open(file, O_RDONLY|O_SHLOCK); 10871553Srgrimes if (fd >= 0) { 108825789Sbrian (void) fstat(fd, &stbuf); 108925789Sbrian if (stbuf.st_size > 0) { 109025789Sbrian putchar('\t'); 109125789Sbrian while ((i = read(fd, line, sizeof(line))) > 0) 109225789Sbrian (void) fwrite(line, 1, i, stdout); 109325789Sbrian } 10941553Srgrimes (void) close(fd); /* unlocks as well */ 10951553Srgrimes } 10961553Srgrimes} 10971553Srgrimes 10981553Srgrimes/* 10991553Srgrimes * Stop the specified daemon after completing the current job and disable 11001553Srgrimes * printing. 11011553Srgrimes */ 11021553Srgrimesvoid 110398152Sgadstop_q(struct printer *pp) 110498152Sgad{ 110598152Sgad int setres; 110698152Sgad char lf[MAXPATHLEN]; 110798152Sgad 110898152Sgad lock_file_name(pp, lf, sizeof lf); 110998152Sgad printf("%s:\n", pp->printer); 111098152Sgad 111198152Sgad setres = set_qstate(SQS_STOPP, lf); 111298152Sgad 111398267Sgad if (setres >= 0) 111498267Sgad upstat(pp, "printing disabled\n", 0); 111598152Sgad} 111698152Sgad 111768401Sgadstruct jobqueue **queue; 11181553Srgrimesint nitems; 11191553Srgrimestime_t mtime; 11201553Srgrimes 11211553Srgrimes/* 11221553Srgrimes * Put the specified jobs at the top of printer queue. 11231553Srgrimes */ 11241553Srgrimesvoid 112578146Sgadtopq(int argc, char *argv[]) 11261553Srgrimes{ 11271553Srgrimes register int i; 11281553Srgrimes struct stat stbuf; 112978146Sgad int cmdstatus, changed; 113031492Swollman struct printer myprinter, *pp = &myprinter; 11311553Srgrimes 11321553Srgrimes if (argc < 3) { 113395258Sdes printf("usage: topq printer [jobnum ...] [user ...]\n"); 11341553Srgrimes return; 11351553Srgrimes } 11361553Srgrimes 11371553Srgrimes --argc; 113831492Swollman ++argv; 113931492Swollman init_printer(pp); 114078146Sgad cmdstatus = getprintcap(*argv, pp); 114178146Sgad switch(cmdstatus) { 114231492Swollman default: 114379739Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 114431492Swollman case PCAPERR_NOTFOUND: 114531492Swollman printf("unknown printer %s\n", *argv); 11461553Srgrimes return; 114731492Swollman case PCAPERR_TCOPEN: 114831492Swollman printf("warning: %s: unresolved tc= reference(s)", *argv); 114931492Swollman break; 115031492Swollman case PCAPERR_SUCCESS: 115131492Swollman break; 115231492Swollman } 115331492Swollman printf("%s:\n", pp->printer); 11541553Srgrimes 115527618Simp seteuid(euid); 115631492Swollman if (chdir(pp->spool_dir) < 0) { 115731492Swollman printf("\tcannot chdir to %s\n", pp->spool_dir); 115827618Simp goto out; 11591553Srgrimes } 116027618Simp seteuid(uid); 116131492Swollman nitems = getq(pp, &queue); 11621553Srgrimes if (nitems == 0) 11631553Srgrimes return; 11641553Srgrimes changed = 0; 116568401Sgad mtime = queue[0]->job_time; 11661553Srgrimes for (i = argc; --i; ) { 11671553Srgrimes if (doarg(argv[i]) == 0) { 11681553Srgrimes printf("\tjob %s is not in the queue\n", argv[i]); 11691553Srgrimes continue; 11701553Srgrimes } else 11711553Srgrimes changed++; 11721553Srgrimes } 11731553Srgrimes for (i = 0; i < nitems; i++) 11741553Srgrimes free(queue[i]); 11751553Srgrimes free(queue); 11761553Srgrimes if (!changed) { 11771553Srgrimes printf("\tqueue order unchanged\n"); 11781553Srgrimes return; 11791553Srgrimes } 11801553Srgrimes /* 11811553Srgrimes * Turn on the public execute bit of the lock file to 11821553Srgrimes * get lpd to rebuild the queue after the current job. 11831553Srgrimes */ 118427618Simp seteuid(euid); 118531492Swollman if (changed && stat(pp->lock_file, &stbuf) >= 0) 118631492Swollman (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); 11871553Srgrimes 118827618Simpout: 118927618Simp seteuid(uid); 119027618Simp} 119127618Simp 11921553Srgrimes/* 11931553Srgrimes * Reposition the job by changing the modification time of 11941553Srgrimes * the control file. 11951553Srgrimes */ 11961553Srgrimesstatic int 119778146Sgadtouch(struct jobqueue *jq) 11981553Srgrimes{ 11991553Srgrimes struct timeval tvp[2]; 120027618Simp int ret; 12011553Srgrimes 12021553Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = --mtime; 12031553Srgrimes tvp[0].tv_usec = tvp[1].tv_usec = 0; 120427618Simp seteuid(euid); 120578146Sgad ret = utimes(jq->job_cfname, tvp); 120627618Simp seteuid(uid); 120727618Simp return (ret); 12081553Srgrimes} 12091553Srgrimes 12101553Srgrimes/* 12111553Srgrimes * Checks if specified job name is in the printer's queue. 12121553Srgrimes * Returns: negative (-1) if argument name is not in the queue. 12131553Srgrimes */ 12141553Srgrimesstatic int 121578146Sgaddoarg(char *job) 12161553Srgrimes{ 121768401Sgad register struct jobqueue **qq; 12181553Srgrimes register int jobnum, n; 12191553Srgrimes register char *cp, *machine; 12201553Srgrimes int cnt = 0; 12211553Srgrimes FILE *fp; 12221553Srgrimes 12231553Srgrimes /* 122427748Simp * Look for a job item consisting of system name, colon, number 122527748Simp * (example: ucbarpa:114) 12261553Srgrimes */ 122727635Simp if ((cp = strchr(job, ':')) != NULL) { 12281553Srgrimes machine = job; 12291553Srgrimes *cp++ = '\0'; 12301553Srgrimes job = cp; 12311553Srgrimes } else 12321553Srgrimes machine = NULL; 12331553Srgrimes 12341553Srgrimes /* 12351553Srgrimes * Check for job specified by number (example: 112 or 235ucbarpa). 12361553Srgrimes */ 12371553Srgrimes if (isdigit(*job)) { 12381553Srgrimes jobnum = 0; 12391553Srgrimes do 12401553Srgrimes jobnum = jobnum * 10 + (*job++ - '0'); 12411553Srgrimes while (isdigit(*job)); 12421553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 12431553Srgrimes n = 0; 124468401Sgad for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) 12451553Srgrimes n = n * 10 + (*cp++ - '0'); 12461553Srgrimes if (jobnum != n) 12471553Srgrimes continue; 12481553Srgrimes if (*job && strcmp(job, cp) != 0) 12491553Srgrimes continue; 12501553Srgrimes if (machine != NULL && strcmp(machine, cp) != 0) 12511553Srgrimes continue; 12521553Srgrimes if (touch(*qq) == 0) { 125368401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 12541553Srgrimes cnt++; 12551553Srgrimes } 12561553Srgrimes } 12571553Srgrimes return(cnt); 12581553Srgrimes } 12591553Srgrimes /* 12601553Srgrimes * Process item consisting of owner's name (example: henry). 12611553Srgrimes */ 12621553Srgrimes for (qq = queue + nitems; --qq >= queue; ) { 126327618Simp seteuid(euid); 126468401Sgad fp = fopen((*qq)->job_cfname, "r"); 126527618Simp seteuid(uid); 126627618Simp if (fp == NULL) 12671553Srgrimes continue; 12681553Srgrimes while (getline(fp) > 0) 12691553Srgrimes if (line[0] == 'P') 12701553Srgrimes break; 12711553Srgrimes (void) fclose(fp); 12721553Srgrimes if (line[0] != 'P' || strcmp(job, line+1) != 0) 12731553Srgrimes continue; 12741553Srgrimes if (touch(*qq) == 0) { 127568401Sgad printf("\tmoved %s\n", (*qq)->job_cfname); 12761553Srgrimes cnt++; 12771553Srgrimes } 12781553Srgrimes } 12791553Srgrimes return(cnt); 12801553Srgrimes} 12811553Srgrimes 12821553Srgrimes/* 128398152Sgad * Enable both queuing & printing, and start printer (undo `down'). 128498152Sgad */ 128598152Sgadvoid 128698152Sgadup_q(struct printer *pp) 128798152Sgad{ 128898152Sgad int setres, startok; 128998152Sgad char lf[MAXPATHLEN]; 129098152Sgad 129198152Sgad lock_file_name(pp, lf, sizeof lf); 129298152Sgad printf("%s:\n", pp->printer); 129398152Sgad 129498152Sgad setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); 129598152Sgad 129698152Sgad seteuid(euid); 129798152Sgad startok = startdaemon(pp); 129898152Sgad seteuid(uid); 129998152Sgad if (!startok) 130098152Sgad printf("\tcouldn't start daemon\n"); 130198152Sgad else 130298152Sgad printf("\tdaemon started\n"); 130398152Sgad} 1304