diff options
Diffstat (limited to 'usr.sbin/lpr/lpc')
-rw-r--r-- | usr.sbin/lpr/lpc/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/Makefile.depend | 21 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/cmds.c | 1330 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/cmdtab.c | 90 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/extern.h | 77 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/lpc.8 | 309 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/lpc.c | 419 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/lpc.h | 51 | ||||
-rw-r--r-- | usr.sbin/lpr/lpc/movejobs.c | 271 |
9 files changed, 2586 insertions, 0 deletions
diff --git a/usr.sbin/lpr/lpc/Makefile b/usr.sbin/lpr/lpc/Makefile new file mode 100644 index 0000000..43f1f7a --- /dev/null +++ b/usr.sbin/lpr/lpc/Makefile @@ -0,0 +1,18 @@ +# From: @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ + +.PATH: ${.CURDIR}/../common_source + +PROG= lpc +MAN= lpc.8 +SRCS= lpc.c cmds.c cmdtab.c movejobs.c +BINGRP= daemon +BINMODE= 2555 + +CFLAGS+= -I${.CURDIR}/../common_source + +WARNS?= 0 + +LIBADD= lpr edit + +.include <bsd.prog.mk> diff --git a/usr.sbin/lpr/lpc/Makefile.depend b/usr.sbin/lpr/lpc/Makefile.depend new file mode 100644 index 0000000..69f631f --- /dev/null +++ b/usr.sbin/lpr/lpc/Makefile.depend @@ -0,0 +1,21 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libedit \ + lib/ncurses/ncursesw \ + usr.sbin/lpr/common_source \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c new file mode 100644 index 0000000..f960f7c --- /dev/null +++ b/usr.sbin/lpr/lpc/cmds.c @@ -0,0 +1,1330 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +/* + * lpc -- line printer control program -- commands: + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/file.h> + +#include <signal.h> +#include <fcntl.h> +#include <err.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "lp.h" +#include "lp.local.h" +#include "lpc.h" +#include "extern.h" +#include "pathnames.h" + +/* + * Return values from kill_qtask(). + */ +#define KQT_LFERROR -2 +#define KQT_KILLFAIL -1 +#define KQT_NODAEMON 0 +#define KQT_KILLOK 1 + +static char *args2line(int argc, char **argv); +static int doarg(char *_job); +static int doselect(const struct dirent *_d); +static int kill_qtask(const char *lf); +static int sortq(const struct dirent **a, const struct dirent **b); +static int touch(struct jobqueue *_jq); +static void unlinkf(char *_name); +static void upstat(struct printer *_pp, const char *_msg, int _notify); +static void wrapup_clean(int _laststatus); + +/* + * generic framework for commands which operate on all or a specified + * set of printers + */ +enum qsel_val { /* how a given ptr was selected */ + QSEL_UNKNOWN = -1, /* ... not selected yet */ + QSEL_BYNAME = 0, /* ... user specifed it by name */ + QSEL_ALL = 1 /* ... user wants "all" printers */ + /* (with more to come) */ +}; + +static enum qsel_val generic_qselect; /* indicates how ptr was selected */ +static int generic_initerr; /* result of initrtn processing */ +static char *generic_cmdname; +static char *generic_msg; /* if a -msg was specified */ +static char *generic_nullarg; +static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */ + +void +generic(void (*specificrtn)(struct printer *_pp), int cmdopts, + void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[]) +{ + int cmdstatus, more, targc; + struct printer myprinter, *pp; + char **margv, **targv; + + if (argc == 1) { + /* + * Usage needs a special case for 'down': The user must + * either include `-msg', or only the first parameter + * that they give will be processed as a printer name. + */ + printf("usage: %s {all | printer ...}", argv[0]); + if (strcmp(argv[0], "down") == 0) { + printf(" -msg [<text> ...]\n"); + printf(" or: down {all | printer} [<text> ...]"); + } else if (cmdopts & LPC_MSGOPT) + printf(" [-msg <text> ...]"); + printf("\n"); + return; + } + + /* The first argument is the command name. */ + generic_cmdname = *argv++; + argc--; + + /* + * The initialization routine for a command might set a generic + * "wrapup" routine, which should be called after processing all + * the printers in the command. This might print summary info. + * + * Note that the initialization routine may also parse (and + * nullify) some of the parameters given on the command, leaving + * only the parameters which have to do with printer names. + */ + pp = &myprinter; + generic_wrapup = NULL; + generic_qselect = QSEL_UNKNOWN; + cmdstatus = 0; + /* this just needs to be a distinct value of type 'char *' */ + if (generic_nullarg == NULL) + generic_nullarg = strdup(""); + + /* + * Some commands accept a -msg argument, which indicates that + * all remaining arguments should be combined into a string. + */ + generic_msg = NULL; + if (cmdopts & LPC_MSGOPT) { + targc = argc; + targv = argv; + for (; targc > 0; targc--, targv++) { + if (strcmp(*targv, "-msg") == 0) { + argc -= targc; + generic_msg = args2line(targc - 1, targv + 1); + break; + } + } + if (argc < 1) { + printf("error: No printer name(s) specified before" + " '-msg'.\n"); + printf("usage: %s {all | printer ...}", + generic_cmdname); + printf(" [-msg <text> ...]\n"); + return; + } + } + + /* call initialization routine, if there is one for this cmd */ + if (initrtn != NULL) { + generic_initerr = 0; + (*initrtn)(argc, argv); + if (generic_initerr) + return; + /* + * The initrtn may have null'ed out some of the parameters. + * Compact the parameter list to remove those nulls, and + * correct the arg-count. + */ + targc = argc; + targv = argv; + margv = argv; + argc = 0; + for (; targc > 0; targc--, targv++) { + if (*targv != generic_nullarg) { + if (targv != margv) + *margv = *targv; + margv++; + argc++; + } + } + } + + if (argc == 1 && strcmp(*argv, "all") == 0) { + generic_qselect = QSEL_ALL; + more = firstprinter(pp, &cmdstatus); + if (cmdstatus) + goto looperr; + while (more) { + (*specificrtn)(pp); + do { + more = nextprinter(pp, &cmdstatus); +looperr: + switch (cmdstatus) { + case PCAPERR_TCOPEN: + printf("warning: %s: unresolved " + "tc= reference(s) ", + pp->printer); + case PCAPERR_SUCCESS: + break; + default: + fatal(pp, "%s", pcaperr(cmdstatus)); + } + } while (more && cmdstatus); + } + goto wrapup; + } + + generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */ + for (; argc > 0; argc--, argv++) { + init_printer(pp); + cmdstatus = getprintcap(*argv, pp); + switch (cmdstatus) { + default: + fatal(pp, "%s", pcaperr(cmdstatus)); + case PCAPERR_NOTFOUND: + printf("unknown printer %s\n", *argv); + continue; + case PCAPERR_TCOPEN: + printf("warning: %s: unresolved tc= reference(s)\n", + *argv); + break; + case PCAPERR_SUCCESS: + break; + } + (*specificrtn)(pp); + } + +wrapup: + if (generic_wrapup) { + (*generic_wrapup)(cmdstatus); + } + free_printer(pp); + if (generic_msg) + free(generic_msg); +} + +/* + * Convert an argv-array of character strings into a single string. + */ +static char * +args2line(int argc, char **argv) +{ + char *cp1, *cend; + const char *cp2; + char buf[1024]; + + if (argc <= 0) + return strdup("\n"); + + cp1 = buf; + cend = buf + sizeof(buf) - 1; /* save room for '\0' */ + while (--argc >= 0) { + cp2 = *argv++; + while ((cp1 < cend) && (*cp1++ = *cp2++)) + ; + cp1[-1] = ' '; + } + cp1[-1] = '\n'; + *cp1 = '\0'; + return strdup(buf); +} + +/* + * Kill the current daemon, to stop printing of the active job. + */ +static int +kill_qtask(const char *lf) +{ + FILE *fp; + pid_t pid; + int errsav, killres, lockres, res; + + PRIV_START + fp = fopen(lf, "r"); + errsav = errno; + PRIV_END + res = KQT_NODAEMON; + if (fp == NULL) { + /* + * If there is no lock file, then there is no daemon to + * kill. Any other error return means there is some + * kind of problem with the lock file. + */ + if (errsav != ENOENT) + res = KQT_LFERROR; + goto killdone; + } + + /* If the lock file is empty, then there is no daemon to kill */ + if (getline(fp) == 0) + goto killdone; + + /* + * If the file can be locked without blocking, then there + * no daemon to kill, or we should not try to kill it. + * + * XXX - not sure I understand the reasoning behind this... + */ + lockres = flock(fileno(fp), LOCK_SH|LOCK_NB); + (void) fclose(fp); + if (lockres == 0) + goto killdone; + + pid = atoi(line); + if (pid < 0) { + /* + * If we got a negative pid, then the contents of the + * lock file is not valid. + */ + res = KQT_LFERROR; + goto killdone; + } + + PRIV_END + killres = kill(pid, SIGTERM); + errsav = errno; + PRIV_END + if (killres == 0) { + res = KQT_KILLOK; + printf("\tdaemon (pid %d) killed\n", pid); + } else if (errno == ESRCH) { + res = KQT_NODAEMON; + } else { + res = KQT_KILLFAIL; + printf("\tWarning: daemon (pid %d) not killed:\n", pid); + printf("\t %s\n", strerror(errsav)); + } + +killdone: + switch (res) { + case KQT_LFERROR: + printf("\tcannot open lock file: %s\n", + strerror(errsav)); + break; + case KQT_NODAEMON: + printf("\tno daemon to abort\n"); + break; + case KQT_KILLFAIL: + case KQT_KILLOK: + /* These two already printed messages to the user. */ + break; + default: + printf("\t<internal error in kill_qtask>\n"); + break; + } + + return (res); +} + +/* + * Write a message into the status file. + */ +static void +upstat(struct printer *pp, const char *msg, int notifyuser) +{ + int fd; + char statfile[MAXPATHLEN]; + + status_file_name(pp, statfile, sizeof statfile); + umask(0); + PRIV_START + fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); + PRIV_END + if (fd < 0) { + printf("\tcannot create status file: %s\n", strerror(errno)); + return; + } + (void) ftruncate(fd, 0); + if (msg == NULL) + (void) write(fd, "\n", 1); + else + (void) write(fd, msg, strlen(msg)); + (void) close(fd); + if (notifyuser) { + if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0)) + printf("\tstatus message is now set to nothing.\n"); + else + printf("\tstatus message is now: %s", msg); + } +} + +/* + * kill an existing daemon and disable printing. + */ +void +abort_q(struct printer *pp) +{ + int killres, setres; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + /* + * Turn on the owner execute bit of the lock file to disable printing. + */ + setres = set_qstate(SQS_STOPP, lf); + + /* + * If set_qstate found that there already was a lock file, then + * call a routine which will read that lock file and kill the + * lpd-process which is listed in that lock file. If the lock + * file did not exist, then either there is no daemon running + * for this queue, or there is one running but *it* could not + * write a lock file (which means we can not determine the + * process id of that lpd-process). + */ + switch (setres) { + case SQS_CHGOK: + case SQS_CHGFAIL: + /* Kill the process */ + killres = kill_qtask(lf); + break; + case SQS_CREOK: + case SQS_CREFAIL: + printf("\tno daemon to abort\n"); + break; + case SQS_STATFAIL: + printf("\tassuming no daemon to abort\n"); + break; + default: + printf("\t<unexpected result (%d) from set_qstate>\n", + setres); + break; + } + + if (setres >= 0) + upstat(pp, "printing disabled\n", 0); +} + +/* + * "global" variables for all the routines related to 'clean' and 'tclean' + */ +static time_t cln_now; /* current time */ +static double cln_minage; /* minimum age before file is removed */ +static long cln_sizecnt; /* amount of space freed up */ +static int cln_debug; /* print extra debugging msgs */ +static int cln_filecnt; /* number of files destroyed */ +static int cln_foundcore; /* found a core file! */ +static int cln_queuecnt; /* number of queues checked */ +static int cln_testonly; /* remove-files vs just-print-info */ + +static int +doselect(const struct dirent *d) +{ + int c = d->d_name[0]; + + if ((c == 'c' || c == 'd' || c == 'r' || c == 't') && + d->d_name[1] == 'f') + return 1; + if (c == 'c') { + if (!strcmp(d->d_name, "core")) + cln_foundcore = 1; + } + if (c == 'e') { + if (!strncmp(d->d_name, "errs.", 5)) + return 1; + } + return 0; +} + +/* + * Comparison routine that clean_q() uses for scandir. + * + * The purpose of this sort is to have all `df' files end up immediately + * after the matching `cf' file. For files matching `cf', `df', `rf', or + * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or + * `tf', and then by the sequence letter (which is A-Z, or a-z). This + * routine may also see filenames which do not start with `cf', `df', `rf', + * or `tf' (such as `errs.*'), and those are simply sorted by the full + * filename. + * + * XXX + * This assumes that all control files start with `cfA*', and it turns + * out there are a few implementations of lpr which will create `cfB*' + * filenames (they will have datafile names which start with `dfB*'). + */ +static int +sortq(const struct dirent **a, const struct dirent **b) +{ + const int a_lt_b = -1, a_gt_b = 1, cat_other = 10; + const char *fname_a, *fname_b, *jnum_a, *jnum_b; + int cat_a, cat_b, ch, res, seq_a, seq_b; + + fname_a = (*a)->d_name; + fname_b = (*b)->d_name; + + /* + * First separate filenames into categories. Categories are + * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in + * that order. It is critical that the mapping be exactly the + * same for 'a' vs 'b', so define a macro for the job. + * + * [aside: the standard `cf' file has the jobnumber start in + * position 4, but some implementations have that as an extra + * file-sequence letter, and start the job number in position 5.] + */ +#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \ + cat_X = cat_other; \ + ch = *(fname_X + 2); \ + jnum_X = fname_X + 3; \ + seq_X = 0; \ + if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \ + seq_X = ch; \ + if (*fname_X == 'c') \ + cat_X = 1; \ + else if (*fname_X == 'd') \ + cat_X = 2; \ + else if (*fname_X == 'r') \ + cat_X = 3; \ + else if (*fname_X == 't') \ + cat_X = 4; \ + if (cat_X != cat_other) { \ + ch = *jnum_X; \ + if (!isdigit(ch)) { \ + if (isalpha(ch)) { \ + jnum_X++; \ + ch = *jnum_X; \ + seq_X = (seq_X << 8) + ch; \ + } \ + if (!isdigit(ch)) \ + cat_X = cat_other; \ + } \ + } \ + } \ +} while (0) + + MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a); + MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b); + +#undef MAP_TO_CAT + + /* First handle all cases which have "other" files */ + if ((cat_a >= cat_other) || (cat_b >= cat_other)) { + /* for two "other" files, just compare the full name */ + if (cat_a == cat_b) + res = strcmp(fname_a, fname_b); + else if (cat_a < cat_b) + res = a_lt_b; + else + res = a_gt_b; + goto have_res; + } + + /* + * At this point, we know both files are legitimate `cf', `df', `rf', + * or `tf' files. Compare them by job-number and machine name. + */ + res = strcmp(jnum_a, jnum_b); + if (res != 0) + goto have_res; + + /* + * We have two files which belong to the same job. Sort based + * on the category of file (`c' before `d', etc). + */ + if (cat_a < cat_b) { + res = a_lt_b; + goto have_res; + } else if (cat_a > cat_b) { + res = a_gt_b; + goto have_res; + } + + /* + * Two files in the same category for a single job. Sort based + * on the sequence letter(s). (usually `A' through `Z', etc). + */ + if (seq_a < seq_b) { + res = a_lt_b; + goto have_res; + } else if (seq_a > seq_b) { + res = a_gt_b; + goto have_res; + } + + /* + * Given that the filenames in a directory are unique, this SHOULD + * never happen (unless there are logic errors in this routine). + * But if it does happen, we must return "is equal" or the caller + * might see inconsistent results in the sorting order, and that + * can trigger other problems. + */ + printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b); + printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b, + seq_a, seq_b); + res = 0; + +have_res: + return res; +} + +/* + * Remove all spool files and temporaries from the spooling area. + * Or, perhaps: + * Remove incomplete jobs from spooling area. + */ + +void +clean_gi(int argc, char *argv[]) +{ + + /* init some fields before 'clean' is called for each queue */ + cln_queuecnt = 0; + cln_now = time(NULL); + cln_minage = 3600.0; /* only delete files >1h old */ + cln_filecnt = 0; + cln_sizecnt = 0; + cln_debug = 0; + cln_testonly = 0; + generic_wrapup = &wrapup_clean; + + /* see if there are any options specified before the ptr list */ + for (; argc > 0; argc--, argv++) { + if (**argv != '-') + break; + if (strcmp(*argv, "-d") == 0) { + /* just an example of an option... */ + cln_debug++; + *argv = generic_nullarg; /* "erase" it */ + } else { + printf("Invalid option '%s'\n", *argv); + generic_initerr = 1; + } + } + + return; +} + +void +tclean_gi(int argc, char *argv[]) +{ + + /* only difference between 'clean' and 'tclean' is one value */ + /* (...and the fact that 'clean' is priv and 'tclean' is not) */ + clean_gi(argc, argv); + cln_testonly = 1; + + return; +} + +void +clean_q(struct printer *pp) +{ + char *cp, *cp1, *lp; + struct dirent **queue; + size_t linerem; + int didhead, i, n, nitems, rmcp; + + cln_queuecnt++; + + didhead = 0; + if (generic_qselect == QSEL_BYNAME) { + printf("%s:\n", pp->printer); + didhead = 1; + } + + lp = line; + cp = pp->spool_dir; + while (lp < &line[sizeof(line) - 1]) { + if ((*lp++ = *cp++) == 0) + break; + } + lp[-1] = '/'; + linerem = sizeof(line) - (lp - line); + + cln_foundcore = 0; + PRIV_START + nitems = scandir(pp->spool_dir, &queue, doselect, sortq); + PRIV_END + if (nitems < 0) { + if (!didhead) { + printf("%s:\n", pp->printer); + didhead = 1; + } + printf("\tcannot examine spool directory\n"); + return; + } + if (cln_foundcore) { + if (!didhead) { + printf("%s:\n", pp->printer); + didhead = 1; + } + printf("\t** found a core file in %s !\n", pp->spool_dir); + } + if (nitems == 0) + return; + if (!didhead) + printf("%s:\n", pp->printer); + if (cln_debug) { + printf("\t** ----- Sorted list of files being checked:\n"); + i = 0; + do { + cp = queue[i]->d_name; + printf("\t** [%3d] = %s\n", i, cp); + } while (++i < nitems); + printf("\t** ----- end of sorted list\n"); + } + i = 0; + do { + cp = queue[i]->d_name; + rmcp = 0; + if (*cp == 'c') { + /* + * A control file. Look for matching data-files. + */ + /* XXX + * Note the logic here assumes that the hostname + * part of cf-filenames match the hostname part + * in df-filenames, and that is not necessarily + * true (eg: for multi-homed hosts). This needs + * some further thought... + */ + n = 0; + while (i + 1 < nitems) { + cp1 = queue[i + 1]->d_name; + if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) + break; + i++; + n++; + } + if (n == 0) { + rmcp = 1; + } + } else if (*cp == 'e') { + /* + * Must be an errrs or email temp file. + */ + rmcp = 1; + } else { + /* + * Must be a df with no cf (otherwise, it would have + * been skipped above) or an rf or tf file (which can + * always be removed if it is old enough). + */ + rmcp = 1; + } + if (rmcp) { + if (strlen(cp) >= linerem) { + printf("\t** internal error: 'line' overflow!\n"); + printf("\t** spooldir = %s\n", pp->spool_dir); + printf("\t** cp = %s\n", cp); + return; + } + strlcpy(lp, cp, linerem); + unlinkf(line); + } + } while (++i < nitems); +} + +static void +wrapup_clean(int laststatus __unused) +{ + + printf("Checked %d queues, and ", cln_queuecnt); + if (cln_filecnt < 1) { + printf("no cruft was found\n"); + return; + } + if (cln_testonly) { + printf("would have "); + } + printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt); +} + +static void +unlinkf(char *name) +{ + struct stat stbuf; + double agemod, agestat; + int res; + char linkbuf[BUFSIZ]; + + /* + * We have to use lstat() instead of stat(), in case this is a df* + * "file" which is really a symlink due to 'lpr -s' processing. In + * that case, we need to check the last-mod time of the symlink, and + * not the file that the symlink is pointed at. + */ + PRIV_START + res = lstat(name, &stbuf); + PRIV_END + if (res < 0) { + printf("\terror return from stat(%s):\n", name); + printf("\t %s\n", strerror(errno)); + return; + } + + agemod = difftime(cln_now, stbuf.st_mtime); + agestat = difftime(cln_now, stbuf.st_ctime); + if (cln_debug > 1) { + /* this debugging-aid probably is not needed any more... */ + printf("\t\t modify age=%g secs, stat age=%g secs\n", + agemod, agestat); + } + if ((agemod <= cln_minage) && (agestat <= cln_minage)) + return; + + /* + * if this file is a symlink, then find out the target of the + * symlink before unlink-ing the file itself + */ + if (S_ISLNK(stbuf.st_mode)) { + PRIV_START + res = readlink(name, linkbuf, sizeof(linkbuf)); + PRIV_END + if (res < 0) { + printf("\terror return from readlink(%s):\n", name); + printf("\t %s\n", strerror(errno)); + return; + } + if (res == sizeof(linkbuf)) + res--; + linkbuf[res] = '\0'; + } + + cln_filecnt++; + cln_sizecnt += stbuf.st_size; + + if (cln_testonly) { + printf("\twould remove %s\n", name); + if (S_ISLNK(stbuf.st_mode)) { + printf("\t (which is a symlink to %s)\n", linkbuf); + } + } else { + PRIV_START + res = unlink(name); + PRIV_END + if (res < 0) + printf("\tcannot remove %s (!)\n", name); + else + printf("\tremoved %s\n", name); + /* XXX + * Note that for a df* file, this code should also check to see + * if it is a symlink to some other file, and if the original + * lpr command included '-r' ("remove file"). Of course, this + * code would not be removing the df* file unless there was no + * matching cf* file, and without the cf* file it is currently + * impossible to determine if '-r' had been specified... + * + * As a result of this quandry, we may be leaving behind a + * user's file that was supposed to have been removed after + * being printed. This may effect services such as CAP or + * samba, if they were configured to use 'lpr -r', and if + * datafiles are not being properly removed. + */ + if (S_ISLNK(stbuf.st_mode)) { + printf("\t (which was a symlink to %s)\n", linkbuf); + } + } +} + +/* + * Enable queuing to the printer (allow lpr to add new jobs to the queue). + */ +void +enable_q(struct printer *pp) +{ + int setres; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_ENABLEQ, lf); +} + +/* + * Disable queuing. + */ +void +disable_q(struct printer *pp) +{ + int setres; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_DISABLEQ, lf); +} + +/* + * Disable queuing and printing and put a message into the status file + * (reason for being down). If the user specified `-msg', then use + * everything after that as the message for the status file. If the + * user did NOT specify `-msg', then the command should take the first + * parameter as the printer name, and all remaining parameters as the + * message for the status file. (This is to be compatible with the + * original definition of 'down', which was implemented long before + * `-msg' was around). + */ +void +down_gi(int argc, char *argv[]) +{ + + /* If `-msg' was specified, then this routine has nothing to do. */ + if (generic_msg != NULL) + return; + + /* + * If the user only gave one parameter, then use a default msg. + * (if argc == 1 at this point, then *argv == name of printer). + */ + if (argc == 1) { + generic_msg = strdup("printing disabled\n"); + return; + } + + /* + * The user specified multiple parameters, and did not specify + * `-msg'. Build a message from all the parameters after the + * first one (and nullify those parameters so generic-processing + * will not process them as printer-queue names). + */ + argc--; + argv++; + generic_msg = args2line(argc, argv); + for (; argc > 0; argc--, argv++) + *argv = generic_nullarg; /* "erase" it */ +} + +void +down_q(struct printer *pp) +{ + int setres; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf); + if (setres >= 0) + upstat(pp, generic_msg, 1); +} + +/* + * Exit lpc + */ +void +quit(int argc __unused, char *argv[] __unused) +{ + exit(0); +} + +/* + * Kill and restart the daemon. + */ +void +restart_q(struct printer *pp) +{ + int killres, setres, startok; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + killres = kill_qtask(lf); + + /* + * XXX - if the kill worked, we should probably sleep for + * a second or so before trying to restart the queue. + */ + + /* make sure the queue is set to print jobs */ + setres = set_qstate(SQS_STARTP, lf); + + PRIV_START + startok = startdaemon(pp); + PRIV_END + if (!startok) + printf("\tcouldn't restart daemon\n"); + else + printf("\tdaemon restarted\n"); +} + +/* + * Set the status message of each queue listed. Requires a "-msg" + * parameter to indicate the end of the queue list and start of msg text. + */ +void +setstatus_gi(int argc __unused, char *argv[] __unused) +{ + + if (generic_msg == NULL) { + printf("You must specify '-msg' before the text of the new status message.\n"); + generic_initerr = 1; + } +} + +void +setstatus_q(struct printer *pp) +{ + struct stat stbuf; + int not_shown; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + upstat(pp, generic_msg, 1); + + /* + * Warn the user if 'lpq' will not display this new status-message. + * Note that if lock file does not exist, then the queue is enabled + * for both queuing and printing. + */ + not_shown = 1; + if (stat(lf, &stbuf) >= 0) { + if (stbuf.st_mode & LFM_PRINT_DIS) + not_shown = 0; + } + if (not_shown) { + printf("\tnote: This queue currently has printing enabled,\n"); + printf("\t so this -msg will only be shown by 'lpq' if\n"); + printf("\t a job is actively printing on it.\n"); + } +} + +/* + * Enable printing on the specified printer and startup the daemon. + */ +void +start_q(struct printer *pp) +{ + int setres, startok; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_STARTP, lf); + + PRIV_START + startok = startdaemon(pp); + PRIV_END + if (!startok) + printf("\tcouldn't start daemon\n"); + else + printf("\tdaemon started\n"); + PRIV_END +} + +/* + * Print the status of the printer queue. + */ +void +status(struct printer *pp) +{ + struct stat stbuf; + register int fd, i; + register struct dirent *dp; + DIR *dirp; + char file[MAXPATHLEN]; + + printf("%s:\n", pp->printer); + lock_file_name(pp, file, sizeof file); + if (stat(file, &stbuf) >= 0) { + printf("\tqueuing is %s\n", + ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled" + : "enabled")); + printf("\tprinting is %s\n", + ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled" + : "enabled")); + } else { + printf("\tqueuing is enabled\n"); + printf("\tprinting is enabled\n"); + } + if ((dirp = opendir(pp->spool_dir)) == NULL) { + printf("\tcannot examine spool directory\n"); + return; + } + i = 0; + while ((dp = readdir(dirp)) != NULL) { + if (*dp->d_name == 'c' && dp->d_name[1] == 'f') + i++; + } + closedir(dirp); + if (i == 0) + printf("\tno entries in spool area\n"); + else if (i == 1) + printf("\t1 entry in spool area\n"); + else + printf("\t%d entries in spool area\n", i); + fd = open(file, O_RDONLY); + if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { + (void) close(fd); /* unlocks as well */ + printf("\tprinter idle\n"); + return; + } + (void) close(fd); + /* print out the contents of the status file, if it exists */ + status_file_name(pp, file, sizeof file); + fd = open(file, O_RDONLY|O_SHLOCK); + if (fd >= 0) { + (void) fstat(fd, &stbuf); + if (stbuf.st_size > 0) { + putchar('\t'); + while ((i = read(fd, line, sizeof(line))) > 0) + (void) fwrite(line, 1, i, stdout); + } + (void) close(fd); /* unlocks as well */ + } +} + +/* + * Stop the specified daemon after completing the current job and disable + * printing. + */ +void +stop_q(struct printer *pp) +{ + int setres; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_STOPP, lf); + + if (setres >= 0) + upstat(pp, "printing disabled\n", 0); +} + +struct jobqueue **queue; +int nitems; +time_t mtime; + +/* + * Put the specified jobs at the top of printer queue. + */ +void +topq(int argc, char *argv[]) +{ + register int i; + struct stat stbuf; + int cmdstatus, changed; + struct printer myprinter, *pp = &myprinter; + + if (argc < 3) { + printf("usage: topq printer [jobnum ...] [user ...]\n"); + return; + } + + --argc; + ++argv; + init_printer(pp); + cmdstatus = getprintcap(*argv, pp); + switch(cmdstatus) { + default: + fatal(pp, "%s", pcaperr(cmdstatus)); + case PCAPERR_NOTFOUND: + printf("unknown printer %s\n", *argv); + return; + case PCAPERR_TCOPEN: + printf("warning: %s: unresolved tc= reference(s)", *argv); + break; + case PCAPERR_SUCCESS: + break; + } + printf("%s:\n", pp->printer); + + PRIV_START + if (chdir(pp->spool_dir) < 0) { + printf("\tcannot chdir to %s\n", pp->spool_dir); + goto out; + } + PRIV_END + nitems = getq(pp, &queue); + if (nitems == 0) + return; + changed = 0; + mtime = queue[0]->job_time; + for (i = argc; --i; ) { + if (doarg(argv[i]) == 0) { + printf("\tjob %s is not in the queue\n", argv[i]); + continue; + } else + changed++; + } + for (i = 0; i < nitems; i++) + free(queue[i]); + free(queue); + if (!changed) { + printf("\tqueue order unchanged\n"); + return; + } + /* + * Turn on the public execute bit of the lock file to + * get lpd to rebuild the queue after the current job. + */ + PRIV_START + if (changed && stat(pp->lock_file, &stbuf) >= 0) + (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE); + +out: + PRIV_END +} + +/* + * Reposition the job by changing the modification time of + * the control file. + */ +static int +touch(struct jobqueue *jq) +{ + struct timeval tvp[2]; + int ret; + + tvp[0].tv_sec = tvp[1].tv_sec = --mtime; + tvp[0].tv_usec = tvp[1].tv_usec = 0; + PRIV_START + ret = utimes(jq->job_cfname, tvp); + PRIV_END + return (ret); +} + +/* + * Checks if specified job name is in the printer's queue. + * Returns: negative (-1) if argument name is not in the queue. + */ +static int +doarg(char *job) +{ + register struct jobqueue **qq; + register int jobnum, n; + register char *cp, *machine; + int cnt = 0; + FILE *fp; + + /* + * Look for a job item consisting of system name, colon, number + * (example: ucbarpa:114) + */ + if ((cp = strchr(job, ':')) != NULL) { + machine = job; + *cp++ = '\0'; + job = cp; + } else + machine = NULL; + + /* + * Check for job specified by number (example: 112 or 235ucbarpa). + */ + if (isdigit(*job)) { + jobnum = 0; + do + jobnum = jobnum * 10 + (*job++ - '0'); + while (isdigit(*job)); + for (qq = queue + nitems; --qq >= queue; ) { + n = 0; + for (cp = (*qq)->job_cfname+3; isdigit(*cp); ) + n = n * 10 + (*cp++ - '0'); + if (jobnum != n) + continue; + if (*job && strcmp(job, cp) != 0) + continue; + if (machine != NULL && strcmp(machine, cp) != 0) + continue; + if (touch(*qq) == 0) { + printf("\tmoved %s\n", (*qq)->job_cfname); + cnt++; + } + } + return(cnt); + } + /* + * Process item consisting of owner's name (example: henry). + */ + for (qq = queue + nitems; --qq >= queue; ) { + PRIV_START + fp = fopen((*qq)->job_cfname, "r"); + PRIV_END + if (fp == NULL) + continue; + while (getline(fp) > 0) + if (line[0] == 'P') + break; + (void) fclose(fp); + if (line[0] != 'P' || strcmp(job, line+1) != 0) + continue; + if (touch(*qq) == 0) { + printf("\tmoved %s\n", (*qq)->job_cfname); + cnt++; + } + } + return(cnt); +} + +/* + * Enable both queuing & printing, and start printer (undo `down'). + */ +void +up_q(struct printer *pp) +{ + int setres, startok; + char lf[MAXPATHLEN]; + + lock_file_name(pp, lf, sizeof lf); + printf("%s:\n", pp->printer); + + setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf); + + PRIV_START + startok = startdaemon(pp); + PRIV_END + if (!startok) + printf("\tcouldn't start daemon\n"); + else + printf("\tdaemon started\n"); +} diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c new file mode 100644 index 0000000..1e8e4da --- /dev/null +++ b/usr.sbin/lpr/lpc/cmdtab.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include "lpc.h" +#include "extern.h" + +/* + * lpc -- command tables + */ +char aborthelp[] = "terminate a spooling daemon immediately and disable printing"; +char botmqhelp[] = "move job(s) to the bottom of printer queue"; +char cleanhelp[] = "remove cruft files from a queue"; +char enablehelp[] = "turn a spooling queue on"; +char disablehelp[] = "turn a spooling queue off"; +char downhelp[] = "do a 'stop' followed by 'disable' and put a message in status"; +char helphelp[] = "get help on commands"; +char quithelp[] = "exit lpc"; +char restarthelp[] = "kill (if possible) and restart a spooling daemon"; +char setstatushelp[] = "set the status message of a queue, requires\n" + "\t\t\"-msg\" before the text of the new message"; +char starthelp[] = "enable printing and start a spooling daemon"; +char statushelp[] = "show status of daemon and queue"; +char stophelp[] = "stop a spooling daemon after current job completes and disable printing"; +char tcleanhelp[] = "test to see what files a clean cmd would remove"; +char topqhelp[] = "move job(s) to the top of printer queue"; +char uphelp[] = "enable everything and restart spooling daemon"; + +/* Use some abbreviations so entries won't need to wrap */ +#define PR LPC_PRIVCMD +#define M LPC_MSGOPT + +struct cmd cmdtab[] = { + { "abort", aborthelp, PR, 0, abort_q }, + { "bottomq", botmqhelp, PR, bottomq_cmd, 0 }, + { "clean", cleanhelp, PR, clean_gi, clean_q }, + { "enable", enablehelp, PR, 0, enable_q }, + { "exit", quithelp, 0, quit, 0 }, + { "disable", disablehelp, PR, 0, disable_q }, + { "down", downhelp, PR|M, down_gi, down_q }, + { "help", helphelp, 0, help, 0 }, + { "quit", quithelp, 0, quit, 0 }, + { "restart", restarthelp, 0, 0, restart_q }, + { "start", starthelp, PR, 0, start_q }, + { "status", statushelp, 0, 0, status }, + { "setstatus", setstatushelp, PR|M, setstatus_gi, setstatus_q }, + { "stop", stophelp, PR, 0, stop_q }, + { "tclean", tcleanhelp, 0, tclean_gi, clean_q }, + { "topq", topqhelp, PR, topq_cmd, 0 }, + { "up", uphelp, PR, 0, up_q }, + { "?", helphelp, 0, help, 0 }, + { "xtopq", topqhelp, PR, topq, 0 }, + { 0, 0, 0, 0, 0}, +}; + +int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]); diff --git a/usr.sbin/lpr/lpc/extern.h b/usr.sbin/lpr/lpc/extern.h new file mode 100644 index 0000000..45844a5 --- /dev/null +++ b/usr.sbin/lpr/lpc/extern.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * + * $FreeBSD$ + */ + + +#include <sys/types.h> +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ + +/* + * Options for setup_myprinter(). + */ +#define SUMP_NOHEADER 0x0001 /* Do not print a header line */ +#define SUMP_CHDIR_SD 0x0002 /* chdir into the spool directory */ + +__BEGIN_DECLS +void abort_q(struct printer *_pp); +void bottomq_cmd(int _argc, char *_argv[]); +void clean_gi(int _argc, char *_argv[]); +void clean_q(struct printer *_pp); +void disable_q(struct printer *_pp); +void down_gi(int _argc, char *_argv[]); +void down_q(struct printer *_pp); +void enable_q(struct printer *_pp); +void generic(void (*_specificrtn)(struct printer *_pp), int _cmdopts, + void (*_initcmd)(int _argc, char *_argv[]), + int _argc, char *_argv[]); +void help(int _argc, char *_argv[]); +void quit(int _argc, char *_argv[]); +void restart_q(struct printer *_pp); +void setstatus_gi(int _argc, char *_argv[]); +void setstatus_q(struct printer *_pp); +void start_q(struct printer *_pp); +void status(struct printer *_pp); +void stop_q(struct printer *_pp); +void tclean_gi(int _argc, char *_argv[]); +void topq_cmd(int _argc, char *_argv[]); +void up_q(struct printer *_pp); +void topq(int _argc, char *_argv[]); /* X-version */ + +/* from lpc.c: */ +struct printer *setup_myprinter(char *_pwanted, struct printer *_pp, + int _sump_opts); +__END_DECLS + +extern int NCMDS; +extern struct cmd cmdtab[]; +extern uid_t uid, euid; diff --git a/usr.sbin/lpr/lpc/lpc.8 b/usr.sbin/lpr/lpc/lpc.8 new file mode 100644 index 0000000..5a66865 --- /dev/null +++ b/usr.sbin/lpr/lpc/lpc.8 @@ -0,0 +1,309 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd July 16, 2002 +.Dt LPC 8 +.Os +.Sh NAME +.Nm lpc +.Nd line printer control program +.Sh SYNOPSIS +.Nm +.Op Ar command Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +utility is used by the system administrator to control the +operation of the line printer system. +For each line printer configured in +.Pa /etc/printcap , +.Nm +may be used to: +.Bl -bullet -offset indent +.It +disable or enable a printer, +.It +disable or enable a printer's spooling queue, +.It +rearrange the order of jobs in a spooling queue, +.It +find the status of printers, and their associated +spooling queues and printer daemons, +.It +change the status message for printer queues (the status message +may be seen by users as part of the output of the +.Xr lpq 1 +utility). +.El +.Pp +Without any arguments, +.Nm +will prompt for commands from the standard input. +If arguments are supplied, +.Nm +interprets the first argument as a command and the remaining +arguments as parameters to the command. +The standard input +may be redirected causing +.Nm +to read commands from file. +Commands may be abbreviated; +the following is the list of recognized commands. +.Pp +.Bl -tag -width indent -compact +.It Ic \&? Op Ar command ... +.It Ic help Op Ar command ... +Print a short description of each command specified in the argument list, +or, if no argument is given, a list of the recognized commands. +.Pp +.It Ic abort Brq Cm all | Ar printer +Terminate an active spooling daemon on the local host immediately and +then disable printing (preventing new daemons from being started by +.Xr lpr 1 ) +for the specified printers. +.Pp +.It Ic bottomq Ar printer Op Ar jobspec ... +Take the specified jobs in the order specified and move them to the +bottom of the printer queue. +Each +.Ar jobspec +can match multiple print jobs. +The full description of a +.Ar jobspec +is given below. +.Pp +.It Ic clean Brq Cm all | Ar printer +Remove any temporary files, data files, and control files that cannot +be printed (i.e., do not form a complete printer job) +from the specified printer queue(s) on the local machine. +This command will also look for +.Pa core +files in spool directory +for each printer queue, and list any that are found. +It will not remove any +.Pa core +files. +See also the +.Ic tclean +command. +.Pp +.It Ic disable Brq Cm all | Ar printer +Turn the specified printer queues off. +This prevents new +printer jobs from being entered into the queue by +.Xr lpr 1 . +.Pp +.It Ic down Bro Cm all | Ar printer ... Brc Cm -msg Ar message ... +.It Ic down Bro Cm all | Ar printer Brc Ar message ... +Turn the specified printer queue off, disable printing and put +.Ar message +in the printer status file. +When specifying more than one printer queue, the +.Ic -msg +argument is required to separate the list of printers from the text +that will be the new status message. +The message does not need to be quoted, the +remaining arguments are treated like +.Xr echo 1 . +This is normally used to take a printer down, and let other users +find out why it is down (the +.Xr lpq 1 +utility will indicate that the printer is down and will print the +status message). +.Pp +.It Ic enable Brq Cm all | Ar printer +Enable spooling on the local queue for the listed printers. +This will allow +.Xr lpr 1 +to put new jobs in the spool queue. +.Pp +.It Ic exit +.It Ic quit +Exit from +.Nm . +.Pp +.It Ic restart Brq Cm all | Ar printer +Attempt to start a new printer daemon. +This is useful when some abnormal condition causes the daemon to +die unexpectedly, leaving jobs in the queue. +.Xr lpq 1 +will report that there is no daemon present when this condition occurs. +If the user is the super-user, +try to abort the current daemon first (i.e., kill and restart a stuck daemon). +.Pp +.It Ic setstatus Bro Cm all | Ar printer Brc Cm -msg Ar message ... +Set the status message for the specified printers. +The +.Ic -msg +argument is required to separate the list of printers from the text +that will be the new status message. +This is normally used to change the status message when the printer +queue is no longer active after printing has been disabled, and you +want to change what users will see in the output of the +.Xr lpq 1 +utility. +.Pp +.It Ic start Brq Cm all | Ar printer +Enable printing and start a spooling daemon for the listed printers. +.Pp +.It Ic status Brq Cm all | Ar printer +Display the status of daemons and queues on the local machine. +.Pp +.It Ic stop Brq Cm all | Ar printer +Stop a spooling daemon after the current job completes and disable +printing. +.Pp +.It Ic tclean Brq Cm all | Ar printer +This will do a test-run of the +.Ic clean +command. +All the same checking is done, but the command will only print out +messages saying what a similar +.Ic clean +command would do if the user typed it in. +It will not remove any files. +Note that the +.Ic clean +command is a privileged command, while the +.Ic tclean +command is not restricted. +.Pp +.It Ic topq Ar printer Op Ar jobspec ... +Take the specified jobs in the order specified and move them to the +top of the printer queue. +Each +.Ar jobspec +can match multiple print jobs. +The full description of a +.Ar jobspec +is given below. +.Pp +.It Ic up Brq Cm all | Ar printer +Enable everything and start a new printer daemon. +Undoes the effects of +.Ic down . +.El +.Pp +Commands such as +.Ic topq +and +.Ic bottomq +can take one or more +.Ar jobspec +to specify which jobs the command should operate on. +A +.Ar jobspec +can be: +.Bl -bullet +.It +a single job number, which will match all jobs in the printer's queue +which have the same job number. +Eg: +.Ar 17 , +.It +a range of job numbers, which will match all jobs with a number between +the starting and ending job numbers, inclusive. +Eg: +.Ar 21-32 , +.It +a specific userid, which will match all jobs which were sent by that +user. +Eg: +.Ar jones , +.It +a host name, when prefixed by an `@', which will match all jobs in +the queue which were sent from the given host. +Eg: +.Ar @freebsd.org , +.It +a job range and a userid, separated by a `:', which will match all jobs +which both match the job range and were sent by the specified user. +Eg: +.Ar jones:17 +or +.Ar 21-32:jones , +.It +a job range and/or a userid, followed by a host name, which will match +all jobs which match all the specified criteria. +Eg: +.Ar jones@freebsd.org +or +.Ar 21-32@freebsd.org +or +.Ar jones:17@freebsd.org . +.El +.Pp +The values for userid and host name can also include pattern-matching +characters, similar to the pattern matching done for filenames in +most command shells. +Note that if you enter a +.Ic topq +or +.Ic bottomq +command as parameters on the initial +.Nm +command, then the shell will expand any pattern-matching characters +that it can (based on what files in finds in the current directory) +before +.Nm +processes the command. +In that case, any parameters which include pattern-matching characters +should be enclosed in quotes, so that the shell will not try to +expand them. +.Sh FILES +.Bl -tag -width /var/spool/*/lockx -compact +.It Pa /etc/printcap +printer description file +.It Pa /var/spool/* +spool directories +.It Pa /var/spool/*/lock +lock file for queue control +.El +.Sh DIAGNOSTICS +.Bl -diag +.It "?Ambiguous command" +abbreviation matches more than one command +.It "?Invalid command" +no match was found +.It "?Privileged command" +you must be a member of group "operator" or root to execute this command +.El +.Sh SEE ALSO +.Xr lpq 1 , +.Xr lpr 1 , +.Xr lprm 1 , +.Xr printcap 5 , +.Xr chkprintcap 8 , +.Xr lpd 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c new file mode 100644 index 0000000..cc58bd9 --- /dev/null +++ b/usr.sbin/lpr/lpc/lpc.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <grp.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <histedit.h> + +#include "lp.h" +#include "lpc.h" +#include "extern.h" + +#ifndef LPR_OPER +#define LPR_OPER "operator" /* group name of lpr operators */ +#endif + +/* + * lpc -- line printer control program + */ + +#define MAX_CMDLINE 200 +#define MAX_MARGV 20 +static int fromatty; + +static char cmdline[MAX_CMDLINE]; +static int margc; +static char *margv[MAX_MARGV]; +uid_t uid, euid; + +int main(int _argc, char *_argv[]); +static void cmdscanner(void); +static struct cmd *getcmd(const char *_name); +static void intr(int _signo); +static void makeargv(void); +static int ingroup(const char *_grname); + +int +main(int argc, char *argv[]) +{ + register struct cmd *c; + + euid = geteuid(); + uid = getuid(); + PRIV_END + progname = argv[0]; + openlog("lpd", 0, LOG_LPR); + + if (--argc > 0) { + c = getcmd(*++argv); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + exit(1); + } + if (c == 0) { + printf("?Invalid command\n"); + exit(1); + } + if ((c->c_opts & LPC_PRIVCMD) && getuid() && + ingroup(LPR_OPER) == 0) { + printf("?Privileged command\n"); + exit(1); + } + if (c->c_generic != 0) + generic(c->c_generic, c->c_opts, c->c_handler, + argc, argv); + else + (*c->c_handler)(argc, argv); + exit(0); + } + fromatty = isatty(fileno(stdin)); + if (!fromatty) + signal(SIGINT, intr); + for (;;) { + cmdscanner(); + } +} + +static void +intr(int signo __unused) +{ + /* (the '__unused' is just to avoid a compile-time warning) */ + exit(0); +} + +static const char * +lpc_prompt(void) +{ + return ("lpc> "); +} + +/* + * Command parser. + */ +static void +cmdscanner(void) +{ + register struct cmd *c; + static EditLine *el; + static History *hist; + HistEvent he; + size_t len; + int num; + const char *bp; + + num = 0; + bp = NULL; + el = NULL; + hist = NULL; + for (;;) { + if (fromatty) { + if (!el) { + el = el_init("lpc", stdin, stdout, stderr); + hist = history_init(); + history(hist, &he, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hist); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_PROMPT, lpc_prompt); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + /* + * EditLine init may call 'cgetset()' to set a + * capability-db meant for termcap (eg: to set + * terminal type 'xterm'). Reset that now, or + * that same db-information will be used for + * printcap (giving us an "xterm" printer, with + * all kinds of invalid capabilities...). + */ + cgetset(NULL); + } + if ((bp = el_gets(el, &num)) == NULL || num == 0) + quit(0, NULL); + + len = (num > MAX_CMDLINE - 1) ? MAX_CMDLINE - 1 : num; + memcpy(cmdline, bp, len); + cmdline[len] = 0; + history(hist, &he, H_ENTER, bp); + + } else { + if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL) + quit(0, NULL); + if (cmdline[0] == 0 || cmdline[0] == '\n') + break; + } + + makeargv(); + if (margc == 0) + continue; + if (el != NULL && el_parse(el, margc, margv) != -1) + continue; + + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + if ((c->c_opts & LPC_PRIVCMD) && getuid() && + ingroup(LPR_OPER) == 0) { + printf("?Privileged command\n"); + continue; + } + + /* + * Two different commands might have the same generic rtn + * (eg: "clean" and "tclean"), and just use different + * handler routines for distinct command-setup. The handler + * routine might also be set on a generic routine for + * initial parameter processing. + */ + if (c->c_generic != 0) + generic(c->c_generic, c->c_opts, c->c_handler, + margc, margv); + else + (*c->c_handler)(margc, margv); + } +} + +static struct cmd * +getcmd(const char *name) +{ + register const char *p, *q; + register struct cmd *c, *found; + register int nmatches, longest; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->c_name); c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return(c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return((struct cmd *)-1); + return(found); +} + +/* + * Slice a string up into argc/argv. + */ +static void +makeargv(void) +{ + register char *cp; + register char **argp = margv; + register int n = 0; + + margc = 0; + for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && + n < MAX_MARGV - 1; n++) { + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; +} + +#define HELPINDENT (sizeof ("directory")) + +/* + * Help command. + */ +void +help(int argc, char *argv[]) +{ + register struct cmd *c; + + if (argc == 1) { + register int i, j, w; + int columns, width = 0, lines; + + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->c_name; c++) { + int len = strlen(c->c_name); + + if (len > width) + width = len; + } + width = (width + 8) &~ 7; + columns = 80 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + c = cmdtab + j * lines + i; + if (c->c_name) + printf("%s", c->c_name); + if (c + lines >= &cmdtab[NCMDS]) { + printf("\n"); + break; + } + w = strlen(c->c_name); + while (w < width) { + w = (w + 8) &~ 7; + putchar('\t'); + } + } + } + return; + } + while (--argc > 0) { + register char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%-*s\t%s\n", (int) HELPINDENT, + c->c_name, c->c_help); + } +} + +/* + * return non-zero if the user is a member of the given group + */ +static int +ingroup(const char *grname) +{ + static struct group *gptr=NULL; + static int ngroups = 0; + static long ngroups_max; + static gid_t *groups; + register gid_t gid; + register int i; + + if (gptr == NULL) { + if ((gptr = getgrnam(grname)) == NULL) { + warnx("warning: unknown group '%s'", grname); + return(0); + } + ngroups_max = sysconf(_SC_NGROUPS_MAX); + if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) + err(1, "malloc"); + ngroups = getgroups(ngroups_max, groups); + if (ngroups < 0) + err(1, "getgroups"); + } + gid = gptr->gr_gid; + for (i = 0; i < ngroups; i++) + if (gid == groups[i]) + return(1); + return(0); +} + +/* + * Routine to get the information for a single printer (which will be + * called by the routines which implement individual commands). + * Note: This is for commands operating on a *single* printer. + */ +struct printer * +setup_myprinter(char *pwanted, struct printer *pp, int sump_opts) +{ + int cdres, cmdstatus; + + init_printer(pp); + cmdstatus = getprintcap(pwanted, pp); + switch (cmdstatus) { + default: + fatal(pp, "%s", pcaperr(cmdstatus)); + /* NOTREACHED */ + case PCAPERR_NOTFOUND: + printf("unknown printer %s\n", pwanted); + return (NULL); + case PCAPERR_TCOPEN: + printf("warning: %s: unresolved tc= reference(s)", pwanted); + break; + case PCAPERR_SUCCESS: + break; + } + if ((sump_opts & SUMP_NOHEADER) == 0) + printf("%s:\n", pp->printer); + + if (sump_opts & SUMP_CHDIR_SD) { + PRIV_START + cdres = chdir(pp->spool_dir); + PRIV_END + if (cdres < 0) { + printf("\tcannot chdir to %s\n", pp->spool_dir); + free_printer(pp); + return (NULL); + } + } + + return (pp); +} diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h new file mode 100644 index 0000000..ee52903 --- /dev/null +++ b/usr.sbin/lpr/lpc/lpc.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)lpc.h 8.1 (Berkeley) 6/6/93 + * + * $FreeBSD$ + */ + +/* + * Line Printer Control (lpc) program. + */ +struct printer; + +#define LPC_PRIVCMD 0x0001 /* a privileged command */ +#define LPC_MSGOPT 0x0002 /* command recognizes -msg option */ + +struct cmd { + const char *c_name; /* command name */ + const char *c_help; /* help message */ + const int c_opts; /* flags (eg: privileged command) */ + /* routine to do all the work for plain cmds, or + * initialization work for generic-printer cmds: */ + void (*c_handler)(int, char *[]); + /* routine to do the work for generic-printer cmds: */ + void (*c_generic)(struct printer *); +}; diff --git a/usr.sbin/lpr/lpc/movejobs.c b/usr.sbin/lpr/lpc/movejobs.c new file mode 100644 index 0000000..c349601 --- /dev/null +++ b/usr.sbin/lpr/lpc/movejobs.c @@ -0,0 +1,271 @@ +/* + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2002 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project + * or FreeBSD, Inc. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +/* + * movejobs.c - The lpc commands which move jobs around. + */ + +#include <sys/file.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <dirent.h> /* just for MAXNAMLEN, for job_cfname in lp.h! */ +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "lp.h" +#include "lpc.h" +#include "matchjobs.h" +#include "extern.h" + +/* Values for origcmd in tqbq_common() */ +#define IS_TOPQ 1 +#define IS_BOTQ 2 + +static int process_jobs(int _argc, char *_argv[], process_jqe + _process_rtn, void *myinfo); +static process_jqe touch_jqe; +static void tqbq_common(int _argc, char *_argv[], int _origcmd); + +/* + * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. + * Define a wrapper which can take 'char', either signed or unsigned. + */ +#define isdigitch(Anychar) isdigit(((int) Anychar) & 255) + +struct touchjqe_info { /* for topq/bottomq */ + time_t newtime; +}; + +static int nitems; +static struct jobqueue **queue; + +/* + * Process all the jobs, as specified by the user. + */ +static int +process_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo) +{ + struct jobspec_hdr jobs_wanted; + int i, matchcnt, pjres; + + STAILQ_INIT(&jobs_wanted); + for (i = 0; i < argc; i++) { + pjres = parse_jobspec(argv[i], &jobs_wanted); + if (pjres == 0) { + printf("\tinvalid job specifier: %s\n", argv[i]); + continue; + } + } + matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted, + process_rtn, myinfo); + + free_jobspec(&jobs_wanted); + return (matchcnt); +} + +/* + * Reposition the job by changing the modification time of the + * control file. + */ +static int +touch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec) +{ + struct timeval tvp[2]; + struct touchjqe_info *touch_info; + int ret; + + /* + * If the entire queue has been scanned for the current jobspec, + * then let the user know if there were no jobs matched by that + * specification. + */ + if (jq == NULL) { + if (jspec->matchcnt == 0) { + format_jobspec(jspec, FMTJS_VERBOSE); + if (jspec->pluralfmt) + printf("\tjobs %s are not in the queue\n", + jspec->fmtoutput); + else + printf("\tjob %s is not in the queue\n", + jspec->fmtoutput); + } + return (1); + } + + /* + * Do a little juggling with "matched" vs "processed", so a single + * job can be matched by multiple specifications, and yet it will + * be moved only once. This is so, eg, 'topq lp 7 7' will not + * complain "job 7 is not in queue" for the second specification. + */ + jq->job_matched = 0; + if (jq->job_processed) { + printf("\tmoved %s earlier\n", jq->job_cfname); + return (1); + } + jq->job_processed = 1; + + touch_info = myinfo; + tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime; + tvp[0].tv_usec = tvp[1].tv_usec = 0; + PRIV_START + ret = utimes(jq->job_cfname, tvp); + PRIV_END + + if (ret == 0) { + if (jspec->matcheduser) + printf("\tmoved %s (user %s)\n", jq->job_cfname, + jspec->matcheduser); + else + printf("\tmoved %s\n", jq->job_cfname); + } + return (ret); +} + +/* + * Put the specified jobs at the bottom of printer queue. + */ +void +bottomq_cmd(int argc, char *argv[]) +{ + + if (argc < 3) { + printf("usage: bottomq printer [jobspec ...]\n"); + return; + } + --argc; /* First argv was the command name */ + ++argv; + + tqbq_common(argc, argv, IS_BOTQ); +} + +/* + * Put the specified jobs at the top of printer queue. + */ +void +topq_cmd(int argc, char *argv[]) +{ + + if (argc < 3) { + printf("usage: topq printer [jobspec ...]\n"); + return; + } + --argc; /* First argv was the command name */ + ++argv; + + tqbq_common(argc, argv, IS_TOPQ); +} + +/* + * Processing in common between topq and bottomq commands. + */ +void +tqbq_common(int argc, char *argv[], int origcmd) +{ + struct printer myprinter, *pp; + struct touchjqe_info touch_info; + int i, movecnt, setres; + + pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD); + if (pp == NULL) + return; + --argc; /* Second argv was the printer name */ + ++argv; + + nitems = getq(pp, &queue); + if (nitems == 0) { + printf("\tthere are no jobs in the queue\n"); + free_printer(pp); + return; + } + + /* + * The only real difference between topq and bottomq is the + * initial value used for newtime. + */ + switch (origcmd) { + case IS_BOTQ: + /* + * When moving jobs to the bottom of the queue, pick a + * starting value which is one second after the last job + * in the queue. + */ + touch_info.newtime = queue[nitems - 1]->job_time + 1; + break; + case IS_TOPQ: + /* + * When moving jobs to the top of the queue, the greatest + * number of jobs which could be moved is all the jobs + * that are in the queue. Pick a starting value which + * leaves plenty of room for all existing jobs. + */ + touch_info.newtime = queue[0]->job_time - nitems - 5; + break; + default: + printf("\ninternal error in topq/bottomq processing.\n"); + return; + } + + movecnt = process_jobs(argc, argv, touch_jqe, &touch_info); + + /* + * If any jobs were moved, then chmod the lock file to notify any + * active process for this queue that the queue has changed, so + * it will rescan the queue to find out the new job order. + */ + if (movecnt == 0) + printf("\tqueue order unchanged\n"); + else { + setres = set_qstate(SQS_QCHANGED, pp->lock_file); + if (setres < 0) + printf("\t* queue order changed for %s, but the\n" + "\t* attempt to set_qstate() failed [%d]!\n", + pp->printer, setres); + } + + for (i = 0; i < nitems; i++) + free(queue[i]); + free(queue); + free_printer(pp); +} + |