aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarance A Drosehn <gad@FreeBSD.org>2001-06-25 02:05:03 +0000
committerGarance A Drosehn <gad@FreeBSD.org>2001-06-25 02:05:03 +0000
commit004c9c5da2723f154106e8955ce5d05250f8e94e (patch)
tree5359327b2674f5a90ee19ea58cfbc8d4d7f0864c
parent6ddb63cad6151fb843c139f45cae730baf10148c (diff)
Notes
-rw-r--r--usr.sbin/lpr/lpc/cmds.c306
-rw-r--r--usr.sbin/lpr/lpc/cmdtab.c36
-rw-r--r--usr.sbin/lpr/lpc/extern.h9
-rw-r--r--usr.sbin/lpr/lpc/lpc.825
-rw-r--r--usr.sbin/lpr/lpc/lpc.c12
-rw-r--r--usr.sbin/lpr/lpc/lpc.h10
6 files changed, 351 insertions, 47 deletions
diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c
index 68bf80400f11..8414448d3b79 100644
--- a/usr.sbin/lpr/lpc/cmds.c
+++ b/usr.sbin/lpr/lpc/cmds.c
@@ -79,22 +79,77 @@ static void startpr(struct printer *_pp, int _chgenable);
static int touch(struct jobqueue *_jq);
static void unlinkf(char *_name);
static void upstat(struct printer *_pp, const char *_msg);
+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_nullarg;
+static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */
+
void
-generic(void (*specificrtn)(struct printer *_pp), int argc, char *argv[])
+generic(void (*specificrtn)(struct printer *_pp),
+ void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[])
{
- int cmdstatus, more;
- struct printer myprinter, *pp = &myprinter;
+ int cmdstatus, more, targc;
+ struct printer myprinter, *pp;
+ char **targv;
if (argc == 1) {
printf("Usage: %s {all | printer ...}\n", argv[0]);
return;
}
+
+ /*
+ * 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("");
+
+ /* call initialization routine, if there is one for this cmd */
+ if (initrtn != NULL) {
+ generic_initerr = 0;
+ (*initrtn)(argc, argv);
+ if (generic_initerr)
+ return;
+ /* skip any initial arguments null-ified by initrtn */
+ targc = argc;
+ targv = argv;
+ while (--targc) {
+ if (targv[1] != generic_nullarg)
+ break;
+ ++targv;
+ }
+ if (targv != argv) {
+ targv[0] = argv[0]; /* copy the command-name */
+ argv = targv;
+ argc = targc + 1;
+ }
+ }
+
if (argc == 2 && strcmp(argv[1], "all") == 0) {
+ generic_qselect = QSEL_ALL;
more = firstprinter(pp, &cmdstatus);
if (cmdstatus)
goto looperr;
@@ -115,10 +170,14 @@ looperr:
}
} while (more && cmdstatus);
}
- return;
+ goto wrapup;
}
+
+ generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */
while (--argc) {
++argv;
+ if (*argv == generic_nullarg)
+ continue;
init_printer(pp);
cmdstatus = getprintcap(*argv, pp);
switch (cmdstatus) {
@@ -136,6 +195,12 @@ looperr:
}
(*specificrtn)(pp);
}
+
+wrapup:
+ if (generic_wrapup) {
+ (*generic_wrapup)(cmdstatus);
+ }
+
}
/*
@@ -236,14 +301,34 @@ upstat(struct printer *pp, const char *msg)
(void) close(fd);
}
+/*
+ * "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(struct dirent *d)
{
int c = d->d_name[0];
if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
- return(1);
- return(0);
+ 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;
}
/*
@@ -276,15 +361,66 @@ sortq(const void *a, const void *b)
* Or, perhaps:
* Remove incomplete jobs from spooling area.
*/
+
void
-clean(struct printer *pp)
+init_clean(int argc, char *argv[])
{
- register int i, n;
- register char *cp, *cp1, *lp;
+
+ /* 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 */
+ while (--argc) {
+ ++argv;
+ if (**argv != '-')
+ break;
+ if (strcmp(*argv, "-d") == 0) {
+ /* just an example of an option... */
+ cln_debug = 1;
+ *argv = generic_nullarg; /* "erase" it */
+ } else {
+ printf("Invalid option '%s'\n", *argv);
+ generic_initerr = 1;
+ }
+ }
+
+ return;
+}
+
+void
+init_tclean(int argc, char *argv[])
+{
+
+ /* only difference between 'clean' and 'tclean' is one value */
+ /* (...and the fact that 'clean' is priv and 'tclean' is not) */
+ init_clean(argc, argv);
+ cln_testonly = 1;
+
+ return;
+}
+
+void
+clean_q(struct printer *pp)
+{
+ char *cp, *cp1, *lp;
struct dirent **queue;
- int nitems;
+ size_t linerem;
+ int didhead, i, n, nitems, rmcp;
- printf("%s:\n", pp->printer);
+ cln_queuecnt++;
+
+ didhead = 0;
+ if (generic_qselect == QSEL_BYNAME) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
lp = line;
cp = pp->spool_dir;
@@ -293,20 +429,46 @@ clean(struct printer *pp)
break;
}
lp[-1] = '/';
+ linerem = sizeof(line) - (lp - line);
+ cln_foundcore = 0;
seteuid(euid);
nitems = scandir(pp->spool_dir, &queue, doselect, sortq);
seteuid(uid);
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);
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;
@@ -316,32 +478,134 @@ clean(struct printer *pp)
n++;
}
if (n == 0) {
- strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
- line[sizeof(line) - 1] = '\0';
- unlinkf(line);
+ 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 a tf file (which can always
- * be removed).
+ * be removed if it's old enough).
*/
- strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
- line[sizeof(line) - 1] = '\0';
+ 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.
+ */
seteuid(euid);
- if (unlink(name) < 0)
- printf("\tcannot remove %s\n", name);
- else
- printf("\tremoved %s\n", name);
+ res = lstat(name, &stbuf);
seteuid(uid);
+ 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) {
+ /* 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)) {
+ seteuid(euid);
+ res = readlink(name, linkbuf, sizeof(linkbuf));
+ seteuid(uid);
+ 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 {
+ seteuid(euid);
+ res = unlink(name);
+ seteuid(uid);
+ 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);
+ }
+ }
}
/*
diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c
index 8e2b6efba848..b27c3124a9c0 100644
--- a/usr.sbin/lpr/lpc/cmdtab.c
+++ b/usr.sbin/lpr/lpc/cmdtab.c
@@ -58,26 +58,30 @@ char restarthelp[] = "kill (if possible) and restart a spooling daemon";
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[] = "put job at top of printer queue";
char uphelp[] = "enable everything and restart spooling daemon";
+#define PR 1 /* a privileged command */
+
struct cmd cmdtab[] = {
- { "abort", aborthelp, 0, 1, doabort },
- { "clean", cleanhelp, 0, 1, clean },
- { "enable", enablehelp, 0, 1, enable },
- { "exit", quithelp, quit, 0 },
- { "disable", disablehelp, 0, 1, disable },
- { "down", downhelp, down, 1 },
- { "help", helphelp, help, 0 },
- { "quit", quithelp, quit, 0 },
- { "restart", restarthelp, 0, 0, restart },
- { "start", starthelp, 0, 1, startcmd },
- { "status", statushelp, 0, 0, status },
- { "stop", stophelp, 0, 1, stop },
- { "topq", topqhelp, topq, 1 },
- { "up", uphelp, 0, 1, up },
- { "?", helphelp, help, 0 },
- { 0 },
+ { "abort", aborthelp, PR, 0, doabort },
+ { "clean", cleanhelp, PR, init_clean, clean_q },
+ { "enable", enablehelp, PR, 0, enable },
+ { "exit", quithelp, 0, quit, 0 },
+ { "disable", disablehelp, PR, 0, disable },
+ { "down", downhelp, PR, down, 0 },
+ { "help", helphelp, 0, help, 0 },
+ { "quit", quithelp, 0, quit, 0 },
+ { "restart", restarthelp, 0, 0, restart },
+ { "start", starthelp, PR, 0, startcmd },
+ { "status", statushelp, 0, 0, status },
+ { "stop", stophelp, PR, 0, stop },
+ { "tclean", tcleanhelp, 0, init_tclean, clean_q },
+ { "topq", topqhelp, PR, topq, 0 },
+ { "up", uphelp, PR, 0, up },
+ { "?", helphelp, 0, help, 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
index ffcfd3263298..229d9e5015d9 100644
--- a/usr.sbin/lpr/lpc/extern.h
+++ b/usr.sbin/lpr/lpc/extern.h
@@ -42,14 +42,17 @@
__BEGIN_DECLS
-void clean(struct printer *_pp);
+void clean_q(struct printer *_pp);
void disable(struct printer *_pp);
void doabort(struct printer *_pp);
void down(int _argc, char *_argv[]);
void enable(struct printer *_pp);
-void generic(void (*_specificrtn)(struct printer *_pp), int _argc,
- char *_argv[]);
+void generic(void (*_specificrtn)(struct printer *_pp),
+ void (*_initcmd)(int _argc, char *_argv[]),
+ int _argc, char *_argv[]);
void help(int _argc, char *_argv[]);
+void init_clean(int _argc, char *_argv[]);
+void init_tclean(int _argc, char *_argv[]);
void quit(int _argc, char *_argv[]);
void restart(struct printer *_pp);
void startcmd(struct printer *_pp);
diff --git a/usr.sbin/lpr/lpc/lpc.8 b/usr.sbin/lpr/lpc/lpc.8
index 542f0d738881..ae1b6a6b53e6 100644
--- a/usr.sbin/lpr/lpc/lpc.8
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -32,7 +32,7 @@
.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95
.\" $FreeBSD$
.\"
-.Dd April 28, 1995
+.Dd June 20, 2001
.Dt LPC 8
.Os BSD 4.2
.Sh NAME
@@ -96,6 +96,11 @@ for the specified printers.
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 'core' files in spool directory
+for each printer queue, and list any that are found.
+It will not remove any 'core' files. See also the
+.Ic tclean
+command.
.Pp
.It Ic disable Xo
.Es \&{ \&}
@@ -165,6 +170,24 @@ Display the status of daemons and queues on the local machine.
Stop a spooling daemon after the current job completes and disable
printing.
.Pp
+.It Ic tclean Xo
+.Es \&{ \&}
+.En Cm all | Ar printer
+.Xc
+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 Xo
.Op Ar jobnum ...
.Op Ar user ...
diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c
index 80e9f3ef9ac1..d01c935d2aa0 100644
--- a/usr.sbin/lpr/lpc/lpc.c
+++ b/usr.sbin/lpr/lpc/lpc.c
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
exit(1);
}
if (c->c_generic != 0)
- generic(c->c_generic, argc, argv);
+ generic(c->c_generic, c->c_handler, argc, argv);
else
(*c->c_handler)(argc, argv);
exit(0);
@@ -200,8 +200,16 @@ cmdscanner(void)
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, margc, margv);
+ generic(c->c_generic, c->c_handler, margc, margv);
else
(*c->c_handler)(margc, margv);
}
diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h
index bc034c357ae7..9511c24945d4 100644
--- a/usr.sbin/lpr/lpc/lpc.h
+++ b/usr.sbin/lpr/lpc/lpc.h
@@ -36,15 +36,17 @@
*/
/*
- * Line printer control program.
+ * Line Printer Control (lpc) program.
*/
struct printer;
struct cmd {
const char *c_name; /* command name */
const char *c_help; /* help message */
- /* routine to do the work */
+ const int c_priv; /* privileged command */
+ /* routine to do all the work for plain cmds, or
+ * initialization work for generic-printer cmds: */
void (*c_handler)(int, char *[]);
- int c_priv; /* privileged command */
- void (*c_generic)(struct printer *); /* generic command */
+ /* routine to do the work for generic-printer cmds: */
+ void (*c_generic)(struct printer *);
};