diff options
| author | Garance A Drosehn <gad@FreeBSD.org> | 2001-06-25 02:05:03 +0000 |
|---|---|---|
| committer | Garance A Drosehn <gad@FreeBSD.org> | 2001-06-25 02:05:03 +0000 |
| commit | 004c9c5da2723f154106e8955ce5d05250f8e94e (patch) | |
| tree | 5359327b2674f5a90ee19ea58cfbc8d4d7f0864c | |
| parent | 6ddb63cad6151fb843c139f45cae730baf10148c (diff) | |
Notes
| -rw-r--r-- | usr.sbin/lpr/lpc/cmds.c | 306 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpc/cmdtab.c | 36 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpc/extern.h | 9 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpc/lpc.8 | 25 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpc/lpc.c | 12 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpc/lpc.h | 10 |
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 *); }; |
