Index: usr.sbin/newsyslog/newsyslog.c =========================================================================== --- usr.sbin/newsyslog/newsyslog.c 2010/04/22 11:22:44 #69 +++ usr.sbin/newsyslog/newsyslog.c 2010/04/22 11:22:44 @@ -111,8 +111,10 @@ #define DEFAULT_MARKER "" #define DEBUG_MARKER "" +#define INCLUDE_MARKER "" struct conf_entry { + STAILQ_ENTRY(conf_entry) cf_nextp; char *log; /* Name of the log */ char *pid_file; /* PID file */ char *r_reason; /* The reason this file is being rotated */ @@ -129,7 +131,6 @@ int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ int sig; /* Signal to send */ int def_cfg; /* Using the rule for this file */ - struct conf_entry *next;/* Linked list pointer */ }; struct sigwork_entry { @@ -149,12 +150,19 @@ char zw_fname[1]; /* the file to compress */ }; +struct include_entry { + STAILQ_ENTRY(include_entry) inc_nextp; + const char *file; /* Name of file to process */ +}; + typedef enum { FREE_ENT, KEEP_ENT } fk_entry; +STAILQ_HEAD(cflist, conf_entry); SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead); SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead); +STAILQ_HEAD(ilist, include_entry); int dbg_at_times; /* -D Show details of 'trim_at' code */ @@ -186,12 +194,14 @@ * used for rotation-tracking messages. */ char hostname[MAXHOSTNAMELEN]; /* hostname */ -static struct conf_entry *get_worklist(char **files); -static void parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, - struct conf_entry **glob_p, struct conf_entry **defconf_p); +static struct cflist *get_worklist(char **files); +static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, + struct conf_entry *defconf_p, struct ilist *inclist); +static void add_to_queue(const char *fname, struct ilist *inclist); static char *sob(char *p); static char *son(char *p); static int isnumberstr(const char *); +static int isglobstr(const char *); static char *missing_field(char *p, char *errline); static void change_attrs(const char *, const struct conf_entry *); static fk_entry do_entry(struct conf_entry *); @@ -205,9 +215,8 @@ sigwork_entry *, int, const char *); static void set_swpid(struct sigwork_entry *, const struct conf_entry *); static int sizefile(const char *); -static void expand_globs(struct conf_entry **work_p, - struct conf_entry **glob_p); -static void free_clist(struct conf_entry **firstent); +static void expand_globs(struct cflist *work_p, struct cflist *glob_p); +static void free_clist(struct cflist *list); static void free_entry(struct conf_entry *ent); static struct conf_entry *init_entry(const char *fname, struct conf_entry *src_entry); @@ -233,8 +242,8 @@ int main(int argc, char **argv) { - fk_entry free_or_keep; - struct conf_entry *p, *q; + struct cflist *worklist; + struct conf_entry *p; struct sigwork_entry *stmp; struct zipwork_entry *ztmp; @@ -247,18 +256,17 @@ if (needroot && getuid() && geteuid()) errx(1, "must have root privs"); - p = q = get_worklist(argv); + worklist = get_worklist(argv); /* * Rotate all the files which need to be rotated. Note that * some users have *hundreds* of entries in newsyslog.conf! */ - while (p) { - free_or_keep = do_entry(p); - p = p->next; - if (free_or_keep == FREE_ENT) - free_entry(q); - q = p; + while (!STAILQ_EMPTY(worklist)) { + p = STAILQ_FIRST(worklist); + STAILQ_REMOVE_HEAD(worklist, cf_nextp); + if (do_entry(p) == FREE_ENT) + free_entry(p); } /* @@ -365,7 +373,6 @@ tempwork->sig = SIGHUP; tempwork->def_cfg = 0; } - tempwork->next = NULL; return (tempwork); } @@ -403,21 +410,18 @@ } static void -free_clist(struct conf_entry **firstent) +free_clist(struct cflist *list) { - struct conf_entry *ent, *nextent; + struct conf_entry *ent; - if (firstent == NULL) - return; /* There is nothing to do. */ - - ent = *firstent; - firstent = NULL; - - while (ent) { - nextent = ent->next; + while (!STAILQ_EMPTY(list)) { + ent = STAILQ_FIRST(list); + STAILQ_REMOVE_HEAD(list, cf_nextp); free_entry(ent); - ent = nextent; } + + free(list); + list = NULL; } static fk_entry @@ -732,47 +736,66 @@ * Parse a configuration file and return a linked list of all the logs * which should be processed. */ -static struct conf_entry * +static struct cflist * get_worklist(char **files) { FILE *f; - const char *fname; char **given; - struct conf_entry *defconf, *dupent, *ent, *firstnew; - struct conf_entry *globlist, *lastnew, *worklist; + struct cflist *cmdlist, *filelist, *globlist; + struct conf_entry *defconf, *dupent, *ent; + struct ilist inclist; + struct include_entry *inc; int gmatch, fnres; - defconf = globlist = worklist = NULL; + defconf = NULL; + STAILQ_INIT(&inclist); + + filelist = malloc(sizeof(struct cflist)); + if (filelist == NULL) + err(1, "malloc of filelist"); + STAILQ_INIT(filelist); + globlist = malloc(sizeof(struct cflist)); + if (globlist == NULL) + err(1, "malloc of globlist"); + STAILQ_INIT(globlist); + + inc = malloc(sizeof(struct include_entry)); + if (inc == NULL) + err(1, "malloc of inc"); + inc->file = conf; + if (inc->file == NULL) + inc->file = _PATH_CONF; + STAILQ_INSERT_TAIL(&inclist, inc, inc_nextp); - fname = conf; - if (fname == NULL) - fname = _PATH_CONF; + STAILQ_FOREACH(inc, &inclist, inc_nextp) { + if (strcmp(inc->file, "-") != 0) + f = fopen(inc->file, "r"); + else { + f = stdin; + inc->file = ""; + } + if (!f) + err(1, "%s", inc->file); - if (strcmp(fname, "-") != 0) - f = fopen(fname, "r"); - else { - f = stdin; - fname = ""; + if (verbose) + printf("Processing %s\n", inc->file); + parse_file(f, filelist, globlist, defconf, &inclist); + (void) fclose(f); } - if (!f) - err(1, "%s", fname); - parse_file(f, fname, &worklist, &globlist, &defconf); - (void) fclose(f); - /* * All config-file information has been read in and turned into - * a worklist and a globlist. If there were no specific files + * a filelist and a globlist. If there were no specific files * given on the run command, then the only thing left to do is to * call a routine which finds all files matched by the globlist - * and adds them to the worklist. Then return the worklist. + * and adds them to the filelist. Then return the worklist. */ if (*files == NULL) { - expand_globs(&worklist, &globlist); - free_clist(&globlist); + expand_globs(filelist, globlist); + free_clist(globlist); if (defconf != NULL) free_entry(defconf); - return (worklist); + return (filelist); /* NOTREACHED */ } @@ -794,7 +817,7 @@ * If newsyslog was run with a list of specific filenames, * then create a new worklist which has only those files in * it, picking up the rotation-rules for those files from - * the original worklist. + * the original filelist. * * XXX - Note that this will copy multiple rules for a single * logfile, if multiple entries are an exact match for @@ -802,21 +825,21 @@ * we want to continue to allow it? If so, it should * probably be handled more intelligently. */ - firstnew = lastnew = NULL; + cmdlist = malloc(sizeof(struct cflist)); + if (cmdlist == NULL) + err(1, "malloc of cmdlist"); + STAILQ_INIT(cmdlist); + for (given = files; *given; ++given) { /* * First try to find exact-matches for this given file. */ gmatch = 0; - for (ent = worklist; ent; ent = ent->next) { + STAILQ_FOREACH(ent, filelist, cf_nextp) { if (strcmp(ent->log, *given) == 0) { gmatch++; dupent = init_entry(*given, ent); - if (!firstnew) - firstnew = dupent; - else - lastnew->next = dupent; - lastnew = dupent; + STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); } } if (gmatch) { @@ -832,7 +855,7 @@ gmatch = 0; if (verbose > 2 && globlist != NULL) printf("\t+ Checking globs for %s\n", *given); - for (ent = globlist; ent; ent = ent->next) { + STAILQ_FOREACH(ent, globlist, cf_nextp) { fnres = fnmatch(ent->log, *given, FNM_PATHNAME); if (verbose > 2) printf("\t+ = %d for pattern %s\n", fnres, @@ -840,13 +863,9 @@ if (fnres == 0) { gmatch++; dupent = init_entry(*given, ent); - if (!firstnew) - firstnew = dupent; - else - lastnew->next = dupent; - lastnew = dupent; /* This new entry is not a glob! */ dupent->flags &= ~CE_GLOB; + STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); /* Only allow a match to one glob-entry */ break; } @@ -866,25 +885,21 @@ printf("\t+ No entry matched %s (will use %s)\n", *given, DEFAULT_MARKER); dupent = init_entry(*given, defconf); - if (!firstnew) - firstnew = dupent; - else - lastnew->next = dupent; /* Mark that it was *not* found in a config file */ dupent->def_cfg = 1; - lastnew = dupent; + STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); } /* * Free all the entries in the original work list, the list of * glob entries, and the default entry. */ - free_clist(&worklist); - free_clist(&globlist); + free_clist(filelist); + free_clist(globlist); free_entry(defconf); /* And finally, return a worklist which matches the given files. */ - return (firstnew); + return (cmdlist); } /* @@ -892,19 +907,15 @@ * which match those glob-entries onto the worklist. */ static void -expand_globs(struct conf_entry **work_p, struct conf_entry **glob_p) +expand_globs(struct cflist *work_p, struct cflist *glob_p) { int gmatch, gres; size_t i; char *mfname; - struct conf_entry *dupent, *ent, *firstmatch, *globent; - struct conf_entry *lastmatch; + struct conf_entry *dupent, *ent, *globent; glob_t pglob; struct stat st_fm; - if ((glob_p == NULL) || (*glob_p == NULL)) - return; /* There is nothing to do. */ - /* * The worklist contains all fully-specified (non-GLOB) names. * @@ -913,9 +924,7 @@ * that already exist. Do not add a glob-related entry for any * file which already exists in the fully-specified list. */ - firstmatch = lastmatch = NULL; - for (globent = *glob_p; globent; globent = globent->next) { - + STAILQ_FOREACH(globent, glob_p, cf_nextp) { gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob); if (gres != 0) { warn("cannot expand pattern (%d): %s", gres, @@ -930,7 +939,7 @@ /* See if this file already has a specific entry. */ gmatch = 0; - for (ent = *work_p; ent; ent = ent->next) { + STAILQ_FOREACH(ent, work_p, cf_nextp) { if (strcmp(mfname, ent->log) == 0) { gmatch++; break; @@ -957,29 +966,16 @@ if (verbose > 2) printf("\t+ . add file %s\n", mfname); dupent = init_entry(mfname, globent); - if (!firstmatch) - firstmatch = dupent; - else - lastmatch->next = dupent; - lastmatch = dupent; /* This new entry is not a glob! */ dupent->flags &= ~CE_GLOB; + + /* Add to the worklist. */ + STAILQ_INSERT_TAIL(work_p, dupent, cf_nextp); } globfree(&pglob); if (verbose > 2) printf("\t+ Done with pattern %s\n", globent->log); } - - /* Add the list of matched files to the end of the worklist. */ - if (!*work_p) - *work_p = firstmatch; - else { - ent = *work_p; - while (ent->next) - ent = ent->next; - ent->next = firstmatch; - } - } /* @@ -987,21 +983,17 @@ * process. */ static void -parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, - struct conf_entry **glob_p, struct conf_entry **defconf_p) +parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, + struct conf_entry *defconf_p, struct ilist *inclist) { char line[BUFSIZ], *parse, *q; char *cp, *errline, *group; - struct conf_entry *lastglob, *lastwork, *working; + struct conf_entry *working; struct passwd *pwd; struct group *grp; + glob_t pglob; int eol, ptm_opts, res, special; - - /* - * XXX - for now, assume that only one config file will be read, - * ie, this routine is only called one time. - */ - lastglob = lastwork = NULL; + size_t i; errline = NULL; while (fgets(line, BUFSIZ, cf)) { @@ -1032,7 +1024,7 @@ /* * Allow people to set debug options via the config file. - * (NOTE: debug optons are undocumented, and may disappear + * (NOTE: debug options are undocumented, and may disappear * at any time, etc). */ if (strcasecmp(DEBUG_MARKER, q) == 0) { @@ -1046,23 +1038,49 @@ parse_doption(q); } continue; + } else if (strcasecmp(INCLUDE_MARKER, q) == 0) { + if (verbose) + printf("Found: %s", errline); + q = parse = missing_field(sob(++parse), errline); + parse = son(parse); + if (!*parse) { + warnx("include line missing argument:\n%s", + errline); + continue; + } + + *parse = '\0'; + + if (isglobstr(q)) { + res = glob(q, GLOB_NOCHECK, NULL, &pglob); + if (res != 0) { + warn("cannot expand pattern (%d): %s", + res, q); + continue; + } + + if (verbose > 2) + printf("\t+ Expanding pattern %s\n", q); + + for (i = 0; i < pglob.gl_matchc; i++) + add_to_queue(pglob.gl_pathv[i], + inclist); + globfree(&pglob); + } else + add_to_queue(q, inclist); + continue; } special = 0; working = init_entry(q, NULL); if (strcasecmp(DEFAULT_MARKER, q) == 0) { special = 1; - if (defconf_p == NULL) { - warnx("Ignoring entry for %s in %s!", q, - cfname); - free_entry(working); - continue; - } else if (*defconf_p != NULL) { + if (defconf_p != NULL) { warnx("Ignoring duplicate entry for %s!", q); free_entry(working); continue; } - *defconf_p = working; + defconf_p = working; } q = parse = missing_field(sob(++parse), errline); @@ -1328,17 +1346,9 @@ if (special) { ; /* Do not add to any list */ } else if (working->flags & CE_GLOB) { - if (!*glob_p) - *glob_p = working; - else - lastglob->next = working; - lastglob = working; + STAILQ_INSERT_TAIL(glob_p, working, cf_nextp); } else { - if (!*work_p) - *work_p = working; - else - lastwork->next = working; - lastwork = working; + STAILQ_INSERT_TAIL(work_p, working, cf_nextp); } } if (errline != NULL) @@ -1354,6 +1364,33 @@ return (p); } +/* + * Only add to the queue if the file hasn't already been added. This is + * done to prevent circular include loops. + */ +static void +add_to_queue(const char *fname, struct ilist *inclist) +{ + struct include_entry *inc; + + STAILQ_FOREACH(inc, inclist, inc_nextp) { + if (strcmp(fname, inc->file) == 0) { + warnx("duplicate include detected: %s", fname); + return; + } + } + + inc = malloc(sizeof(struct include_entry)); + if (inc == NULL) + err(1, "malloc of inc"); + inc->file = strdup(fname); + + if (verbose > 2) + printf("\t+ Adding %s to the processing queue.\n", fname); + + STAILQ_INSERT_TAIL(inclist, inc, inc_nextp); +} + static fk_entry do_rotate(const struct conf_entry *ent) { @@ -1958,6 +1995,19 @@ return (1); } +/* Check if string contains a glob */ +static int +isglobstr(const char *string) +{ + char chr; + + while ((chr = *string++)) { + if (chr == '*' || chr == '?' || chr == '[') + return (1); + } + return (0); +} + /* * Save the active log file under a new name. A link to the new name * is the quick-and-easy way to do this. If that fails (which it will Index: usr.sbin/newsyslog/newsyslog.conf.5 =========================================================================== --- usr.sbin/newsyslog/newsyslog.conf.5 2010/04/22 11:22:44 #8 +++ usr.sbin/newsyslog/newsyslog.conf.5 2010/04/22 11:22:44 @@ -69,13 +69,18 @@ The fields of the configuration file are as follows: .Bl -tag -width indent .It Ar logfile_name -Name of the system log file to be archived, or the literal string -.Dq Aq Li default . +Name of the system log file to be archived, +or one of the literal strings +.Dq Aq Li default , +or +.Dq Aq Li include . The special default entry will only be used if a log file name is given as a command line argument to .Xr newsyslog 8 , and if that log file name is not matched by any other line in the configuration file. +The include entry is used to include other configuration +files and supports globbing. .It Ar owner : Ns Ar group This optional field specifies the owner and group for the archive file. The