diff options
Diffstat (limited to 'usr/src/cmd/logadm/conf.c')
-rw-r--r-- | usr/src/cmd/logadm/conf.c | 515 |
1 files changed, 361 insertions, 154 deletions
diff --git a/usr/src/cmd/logadm/conf.c b/usr/src/cmd/logadm/conf.c index 936238e848..5e0f053333 100644 --- a/usr/src/cmd/logadm/conf.c +++ b/usr/src/cmd/logadm/conf.c @@ -20,12 +20,9 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * logadm/conf.c -- configuration file module */ @@ -40,6 +37,7 @@ #include <strings.h> #include <unistd.h> #include <stdlib.h> +#include <limits.h> #include "err.h" #include "lut.h" #include "fn.h" @@ -47,17 +45,27 @@ #include "conf.h" /* forward declarations of functions private to this module */ -static void fillconflist(int lineno, const char *entry, char **args, +static void fillconflist(int lineno, const char *entry, struct opts *opts, const char *com, int flags); static void fillargs(char *arg); static char *nexttok(char **ptrptr); -static void conf_print(FILE *stream); +static void conf_print(FILE *cstream, FILE *tstream); static const char *Confname; /* name of the confile file */ -static char *Confbuf; /* copy of the config file (a la mmap()) */ -static int Conflen; /* length of mmap'd area */ static int Conffd = -1; /* file descriptor for config file */ -static boolean_t Confchanged; /* true if we need to write changes back */ +static char *Confbuf; /* copy of the config file (a la mmap()) */ +static int Conflen; /* length of mmap'd config file area */ +static const char *Timesname; /* name of the timestamps file */ +static int Timesfd = -1; /* file descriptor for timestamps file */ +static char *Timesbuf; /* copy of the timestamps file (a la mmap()) */ +static int Timeslen; /* length of mmap'd timestamps area */ +static int Singlefile; /* Conf and Times in the same file */ +static int Changed; /* what changes need to be written back */ +static int Canchange; /* what changes can be written back */ +static int Changing; /* what changes have been requested */ +#define CHG_NONE 0 +#define CHG_TIMES 1 +#define CHG_BOTH 3 /* * our structured representation of the configuration file @@ -67,7 +75,6 @@ struct confinfo { struct confinfo *cf_next; int cf_lineno; /* line number in file */ const char *cf_entry; /* name of entry, if line has an entry */ - char **cf_args; /* raw rhs of entry */ struct opts *cf_opts; /* parsed rhs of entry */ const char *cf_com; /* any comment text found */ int cf_flags; @@ -82,7 +89,7 @@ static struct fn_list *Confentries; /* list of valid entry names */ /* allocate & fill in another entry in our list */ static void -fillconflist(int lineno, const char *entry, char **args, +fillconflist(int lineno, const char *entry, struct opts *opts, const char *com, int flags) { struct confinfo *cp = MALLOC(sizeof (*cp)); @@ -90,7 +97,6 @@ fillconflist(int lineno, const char *entry, char **args, cp->cf_next = NULL; cp->cf_lineno = lineno; cp->cf_entry = entry; - cp->cf_args = args; cp->cf_opts = opts; cp->cf_com = com; cp->cf_flags = flags; @@ -163,54 +169,33 @@ nexttok(char **ptrptr) } /* - * conf_open -- open the configuration file, lock it if we have write perms + * scan the memory image of a file + * returns: 0: error, 1: ok, 3: -P option found */ -void -conf_open(const char *fname, int needwrite) +static int +conf_scan(const char *fname, char *buf, int buflen, int timescan, + struct opts *cliopts) { - struct stat stbuf; + int ret = 1; int lineno = 0; char *line; char *eline; char *ebuf; - char *comment; - - Confname = fname; - Confentries = fn_list_new(NULL); - - /* special case this so we don't even try locking the file */ - if (strcmp(Confname, "/dev/null") == 0) - return; - - if ((Conffd = open(Confname, (needwrite) ? O_RDWR : O_RDONLY)) < 0) - err(EF_SYS, "%s", Confname); + char *entry, *comment; - if (fstat(Conffd, &stbuf) < 0) - err(EF_SYS, "fstat on %s", Confname); + ebuf = &buf[buflen]; - if (needwrite && lockf(Conffd, F_LOCK, 0) < 0) - err(EF_SYS, "lockf on %s", Confname); + if (buf[buflen - 1] != '\n') + err(EF_WARN|EF_FILE, "file %s doesn't end with newline, " + "last line ignored.", fname); - if (stbuf.st_size == 0) - return; /* empty file, don't bother parsing it */ + for (line = buf; line < ebuf; line = eline) { + char *ap; + struct opts *opts = NULL; + struct confinfo *cp; - if ((Confbuf = (char *)mmap(0, stbuf.st_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE, Conffd, 0)) == (char *)-1) - err(EF_SYS, "mmap on %s", Confname); - - Conflen = stbuf.st_size; - Confchanged = B_FALSE; - - ebuf = &Confbuf[Conflen]; - - if (Confbuf[Conflen - 1] != '\n') - err(EF_WARN|EF_FILE, "config file doesn't end with " - "newline, last line ignored."); - - line = Confbuf; - while (line < ebuf) { lineno++; - err_fileline(Confname, lineno); + err_fileline(fname, lineno); eline = line; comment = NULL; for (; eline < ebuf; eline++) { @@ -220,7 +205,7 @@ conf_open(const char *fname, int needwrite) *eline = ' '; *(eline + 1) = ' '; lineno++; - err_fileline(Confname, lineno); + err_fileline(fname, lineno); continue; } @@ -237,64 +222,213 @@ conf_open(const char *fname, int needwrite) } if (comment >= ebuf) comment = NULL; - if (eline < ebuf) { - char *entry; - - *eline++ = '\0'; - + if (eline >= ebuf) { + /* discard trailing unterminated line */ + continue; + } + *eline++ = '\0'; + + /* + * now we have the entry, if any, at "line" + * and the comment, if any, at "comment" + */ + + /* entry is first token */ + entry = nexttok(&line); + if (entry == NULL) { + /* it's just a comment line */ + if (!timescan) + fillconflist(lineno, entry, NULL, comment, 0); + continue; + } + if (strcmp(entry, "logadm-version") == 0) { /* - * now we have the entry, if any, at "line" - * and the comment, if any, at "comment" + * we somehow opened some future format + * conffile that we likely don't understand. + * if the given version is "1" then go on, + * otherwise someone is mixing versions + * and we can't help them other than to + * print an error and exit. */ - - /* entry is first token */ if ((entry = nexttok(&line)) != NULL && - strcmp(entry, "logadm-version") == 0) { - /* - * we somehow opened some future format - * conffile that we likely don't understand. - * if the given version is "1" then go on, - * otherwise someone is mixing versions - * and we can't help them other than to - * print an error and exit. - */ - if ((entry = nexttok(&line)) != NULL && - strcmp(entry, "1") != 0) - err(0, "%s version not " - "supported by " - "this version of logadm.", - Confname); - } else if (entry) { - char *ap; - char **args; - int i; - - ArgsI = 0; - while (ap = nexttok(&line)) - fillargs(ap); - if (ArgsI == 0) { - /* short entry allowed */ - fillconflist(lineno, entry, - NULL, NULL, comment, 0); - } else { - Args[ArgsI++] = NULL; - args = MALLOC(sizeof (char *) * ArgsI); - for (i = 0; i < ArgsI; i++) - args[i] = Args[i]; - fillconflist(lineno, entry, - args, NULL, comment, 0); + strcmp(entry, "1") != 0) + err(0, "%s version not supported " + "by this version of logadm.", + fname); + continue; + } + + /* form an argv array */ + ArgsI = 0; + while (ap = nexttok(&line)) + fillargs(ap); + Args[ArgsI] = NULL; + + LOCAL_ERR_BEGIN { + if (SETJMP) { + err(EF_FILE, "cannot process invalid entry %s", + entry); + ret = 0; + LOCAL_ERR_BREAK; + } + + if (timescan) { + /* append to config options */ + cp = lut_lookup(Conflut, entry); + if (cp == NULL) { + /* orphaned entry */ + if (opts_count(cliopts, "v")) + err(EF_FILE, "stale timestamp " + "for %s", entry); + LOCAL_ERR_BREAK; } - } else - fillconflist(lineno, entry, NULL, NULL, - comment, 0); + opts = cp->cf_opts; + } + opts = opts_parse(opts, Args, OPTF_CONF); + if (!timescan) { + fillconflist(lineno, entry, opts, comment, 0); + } + LOCAL_ERR_END } + + if (ret == 1 && opts && opts_optarg(opts, "P") != NULL) + ret = 3; + } + + err_fileline(NULL, 0); + return (ret); +} + +/* + * conf_open -- open the configuration file, lock it if we have write perms + */ +int +conf_open(const char *cfname, const char *tfname, struct opts *cliopts) +{ + struct stat stbuf1, stbuf2, stbuf3; + struct flock flock; + int ret; + + Confname = cfname; + Timesname = tfname; + Confentries = fn_list_new(NULL); + Changed = CHG_NONE; + + Changing = CHG_TIMES; + if (opts_count(cliopts, "Vn") != 0) + Changing = CHG_NONE; + else if (opts_count(cliopts, "rw") != 0) + Changing = CHG_BOTH; + + Singlefile = strcmp(Confname, Timesname) == 0; + if (Singlefile && Changing == CHG_TIMES) + Changing = CHG_BOTH; + + /* special case this so we don't even try locking the file */ + if (strcmp(Confname, "/dev/null") == 0) + return (0); + + while (Conffd == -1) { + Canchange = CHG_BOTH; + if ((Conffd = open(Confname, O_RDWR)) < 0) { + if (Changing == CHG_BOTH) + err(EF_SYS, "open %s", Confname); + Canchange = CHG_TIMES; + if ((Conffd = open(Confname, O_RDONLY)) < 0) + err(EF_SYS, "open %s", Confname); + } + + flock.l_type = (Canchange == CHG_BOTH) ? F_WRLCK : F_RDLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + if (fcntl(Conffd, F_SETLKW, &flock) < 0) + err(EF_SYS, "flock on %s", Confname); + + /* wait until after file is locked to get filesize */ + if (fstat(Conffd, &stbuf1) < 0) + err(EF_SYS, "fstat on %s", Confname); + + /* verify that we've got a lock on the active file */ + if (stat(Confname, &stbuf2) < 0 || + !(stbuf2.st_dev == stbuf1.st_dev && + stbuf2.st_ino == stbuf1.st_ino)) { + /* wrong config file, try again */ + (void) close(Conffd); + Conffd = -1; + } + } + + while (!Singlefile && Timesfd == -1) { + if ((Timesfd = open(Timesname, O_CREAT|O_RDWR, 0644)) < 0) { + if (Changing != CHG_NONE) + err(EF_SYS, "open %s", Timesname); + Canchange = CHG_NONE; + if ((Timesfd = open(Timesname, O_RDONLY)) < 0) + err(EF_SYS, "open %s", Timesname); + } + + flock.l_type = (Canchange != CHG_NONE) ? F_WRLCK : F_RDLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + if (fcntl(Timesfd, F_SETLKW, &flock) < 0) + err(EF_SYS, "flock on %s", Timesname); + + /* wait until after file is locked to get filesize */ + if (fstat(Timesfd, &stbuf2) < 0) + err(EF_SYS, "fstat on %s", Timesname); + + /* verify that we've got a lock on the active file */ + if (stat(Timesname, &stbuf3) < 0 || + !(stbuf2.st_dev == stbuf3.st_dev && + stbuf2.st_ino == stbuf3.st_ino)) { + /* wrong timestamp file, try again */ + (void) close(Timesfd); + Timesfd = -1; + continue; } - line = eline; + + /* check that Timesname isn't an alias for Confname */ + if (stbuf2.st_dev == stbuf1.st_dev && + stbuf2.st_ino == stbuf1.st_ino) + err(0, "Timestamp file %s can't refer to " + "Configuration file %s", Timesname, Confname); } + + Conflen = stbuf1.st_size; + Timeslen = stbuf2.st_size; + + if (Conflen == 0) + return (1); /* empty file, don't bother parsing it */ + + if ((Confbuf = (char *)mmap(0, Conflen, + PROT_READ | PROT_WRITE, MAP_PRIVATE, Conffd, 0)) == (char *)-1) + err(EF_SYS, "mmap on %s", Confname); + + ret = conf_scan(Confname, Confbuf, Conflen, 0, cliopts); + if (ret == 3 && !Singlefile && Canchange == CHG_BOTH) { + /* + * arrange to transfer any timestamps + * from conf_file to timestamps_file + */ + Changing = Changed = CHG_BOTH; + } + + if (Timesfd != -1 && Timeslen != 0) { + if ((Timesbuf = (char *)mmap(0, Timeslen, + PROT_READ | PROT_WRITE, MAP_PRIVATE, + Timesfd, 0)) == (char *)-1) + err(EF_SYS, "mmap on %s", Timesname); + ret &= conf_scan(Timesname, Timesbuf, Timeslen, 1, cliopts); + } + /* * possible future enhancement: go through and mark any entries: * logfile -P <date> * as DELETED if the logfile doesn't exist */ + + return (ret); } /* @@ -303,35 +437,100 @@ conf_open(const char *fname, int needwrite) void conf_close(struct opts *opts) { - FILE *fp; + char cuname[PATH_MAX], tuname[PATH_MAX]; + int cfd, tfd; + FILE *cfp = NULL, *tfp = NULL; + boolean_t safe_update = B_TRUE; - if (Confchanged && opts_count(opts, "n") == 0 && Conffd != -1) { + if (Changed == CHG_NONE || opts_count(opts, "n") != 0) { if (opts_count(opts, "v")) - (void) out("# writing changes to %s\n", Confname); - if (Debug > 1) { - (void) fprintf(stderr, "conf_close, %s changed to:\n", + (void) out("# %s and %s unchanged\n", + Confname, Timesname); + goto cleanup; + } + + if (Debug > 1) { + (void) fprintf(stderr, "conf_close, saving logadm context:\n"); + conf_print(stderr, NULL); + } + + cuname[0] = tuname[0] = '\0'; + LOCAL_ERR_BEGIN { + if (SETJMP) { + safe_update = B_FALSE; + LOCAL_ERR_BREAK; + } + if (Changed == CHG_BOTH) { + if (Canchange != CHG_BOTH) + err(EF_JMP, "internal error: attempting " + "to update %s without locking", Confname); + (void) snprintf(cuname, sizeof (cuname), "%sXXXXXX", Confname); - conf_print(stderr); + if ((cfd = mkstemp(cuname)) == -1) + err(EF_SYS|EF_JMP, "open %s replacement", + Confname); + if (opts_count(opts, "v")) + (void) out("# writing changes to %s\n", cuname); + if (fchmod(cfd, 0644) == -1) + err(EF_SYS|EF_JMP, "chmod %s", cuname); + if ((cfp = fdopen(cfd, "w")) == NULL) + err(EF_SYS|EF_JMP, "fdopen on %s", cuname); + } else { + /* just toss away the configuration data */ + cfp = fopen("/dev/null", "w"); } - if (lseek(Conffd, (off_t)0, SEEK_SET) < 0) - err(EF_SYS, "lseek on %s", Confname); - if (ftruncate(Conffd, (off_t)0) < 0) - err(EF_SYS, "ftruncate on %s", Confname); - if ((fp = fdopen(Conffd, "w")) == NULL) - err(EF_SYS, "fdopen on %s", Confname); - conf_print(fp); - if (fclose(fp) < 0) - err(EF_SYS, "fclose on %s", Confname); - Conffd = -1; - Confchanged = B_FALSE; - } else if (opts_count(opts, "v")) { - (void) out("# %s unchanged\n", Confname); + if (!Singlefile) { + if (Canchange == CHG_NONE) + err(EF_JMP, "internal error: attempting " + "to update %s without locking", Timesname); + (void) snprintf(tuname, sizeof (tuname), "%sXXXXXX", + Timesname); + if ((tfd = mkstemp(tuname)) == -1) + err(EF_SYS|EF_JMP, "open %s replacement", + Timesname); + if (opts_count(opts, "v")) + (void) out("# writing changes to %s\n", tuname); + if (fchmod(tfd, 0644) == -1) + err(EF_SYS|EF_JMP, "chmod %s", tuname); + if ((tfp = fdopen(tfd, "w")) == NULL) + err(EF_SYS|EF_JMP, "fdopen on %s", tuname); + } + + conf_print(cfp, tfp); + if (fclose(cfp) < 0) + err(EF_SYS|EF_JMP, "fclose on %s", Confname); + if (tfp != NULL && fclose(tfp) < 0) + err(EF_SYS|EF_JMP, "fclose on %s", Timesname); + LOCAL_ERR_END } + + if (!safe_update) { + if (cuname[0] != 0) + (void) unlink(cuname); + if (tuname[0] != 0) + (void) unlink(tuname); + err(EF_JMP, "unsafe to update configuration file " + "or timestamps"); + return; } + /* rename updated files into place */ + if (cuname[0] != '\0') + if (rename(cuname, Confname) < 0) + err(EF_SYS, "rename %s to %s", cuname, Confname); + if (tuname[0] != '\0') + if (rename(tuname, Timesname) < 0) + err(EF_SYS, "rename %s to %s", tuname, Timesname); + Changed = CHG_NONE; + +cleanup: if (Conffd != -1) { (void) close(Conffd); Conffd = -1; } + if (Timesfd != -1) { + (void) close(Timesfd); + Timesfd = -1; + } if (Conflut) { lut_free(Conflut, free); Conflut = NULL; @@ -345,16 +544,14 @@ conf_close(struct opts *opts) /* * conf_lookup -- lookup an entry in the config file */ -char ** +void * conf_lookup(const char *lhs) { struct confinfo *cp = lut_lookup(Conflut, lhs); - if (cp != NULL) { + if (cp != NULL) err_fileline(Confname, cp->cf_lineno); - return (cp->cf_args); - } else - return (NULL); + return (cp); } /* @@ -365,14 +562,9 @@ conf_opts(const char *lhs) { struct confinfo *cp = lut_lookup(Conflut, lhs); - if (cp != NULL) { - if (cp->cf_opts) - return (cp->cf_opts); /* already parsed */ - err_fileline(Confname, cp->cf_lineno); - cp->cf_opts = opts_parse(cp->cf_args, OPTF_CONF); + if (cp != NULL) return (cp->cf_opts); - } - return (opts_parse(NULL, OPTF_CONF)); + return (opts_parse(NULL, NULL, OPTF_CONF)); } /* @@ -388,12 +580,13 @@ conf_replace(const char *lhs, struct opts *newopts) if (cp != NULL) { cp->cf_opts = newopts; - cp->cf_args = NULL; + /* cp->cf_args = NULL; */ if (newopts == NULL) cp->cf_flags |= CONFF_DELETED; } else - fillconflist(0, lhs, NULL, newopts, NULL, 0); - Confchanged = B_TRUE; + fillconflist(0, lhs, newopts, NULL, 0); + + Changed = CHG_BOTH; } /* @@ -408,17 +601,18 @@ conf_set(const char *entry, char *o, const char *optarg) return; if (cp != NULL) { - if (cp->cf_opts == NULL) - cp->cf_opts = opts_parse(cp->cf_args, OPTF_CONF); cp->cf_flags &= ~CONFF_DELETED; } else { - fillconflist(0, STRDUP(entry), NULL, - opts_parse(NULL, OPTF_CONF), NULL, 0); + fillconflist(0, STRDUP(entry), + opts_parse(NULL, NULL, OPTF_CONF), NULL, 0); if ((cp = lut_lookup(Conflut, entry)) == NULL) err(0, "conf_set internal error"); } (void) opts_set(cp->cf_opts, o, optarg); - Confchanged = B_TRUE; + if (strcmp(o, "P") == 0) + Changed |= CHG_TIMES; + else + Changed = CHG_BOTH; } /* @@ -432,33 +626,41 @@ conf_entries(void) /* print the config file */ static void -conf_print(FILE *stream) +conf_print(FILE *cstream, FILE *tstream) { struct confinfo *cp; + char *exclude_opts = "PFfhnrvVw"; + const char *timestamp; + if (tstream == NULL) { + exclude_opts++; /* -P option goes to config file */ + } else { + (void) fprintf(tstream, gettext( + "# This file holds internal data for logadm(1M).\n" + "# Do not edit.\n")); + } for (cp = Confinfo; cp; cp = cp->cf_next) { if (cp->cf_flags & CONFF_DELETED) continue; if (cp->cf_entry) { - char **p; - - opts_printword(cp->cf_entry, stream); - if (cp->cf_opts) { - /* existence of opts overrides args */ - opts_print(cp->cf_opts, stream, "fhnrvVw"); - } else if (cp->cf_args) { - for (p = cp->cf_args; *p; p++) { - (void) fprintf(stream, " "); - opts_printword(*p, stream); - } + opts_printword(cp->cf_entry, cstream); + if (cp->cf_opts) + opts_print(cp->cf_opts, cstream, exclude_opts); + /* output timestamps to tstream */ + if (tstream != NULL && (timestamp = + opts_optarg(cp->cf_opts, "P")) != NULL) { + opts_printword(cp->cf_entry, tstream); + (void) fprintf(tstream, " -P "); + opts_printword(timestamp, tstream); + (void) fprintf(tstream, "\n"); } } if (cp->cf_com) { if (cp->cf_entry) - (void) fprintf(stream, " "); - (void) fprintf(stream, "#%s", cp->cf_com); + (void) fprintf(cstream, " "); + (void) fprintf(cstream, "#%s", cp->cf_com); } - (void) fprintf(stream, "\n"); + (void) fprintf(cstream, "\n"); } } @@ -470,18 +672,23 @@ conf_print(FILE *stream) int main(int argc, char *argv[]) { + struct opts *opts; + err_init(argv[0]); setbuf(stdout, NULL); + opts_init(Opttable, Opttable_cnt); + + opts = opts_parse(NULL, NULL, 0); if (argc != 2) err(EF_RAW, "usage: %s conffile\n", argv[0]); - conf_open(argv[1], 1); + conf_open(argv[1], argv[1], opts); printf("conffile <%s>:\n", argv[1]); - conf_print(stdout); + conf_print(stdout, NULL); - conf_close(opts_parse(NULL, 0)); + conf_close(opts); err_done(0); /* NOTREACHED */ |