diff options
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg.c')
-rw-r--r-- | usr/src/lib/libdscfg/common/cfg.c | 3581 |
1 files changed, 3581 insertions, 0 deletions
diff --git a/usr/src/lib/libdscfg/common/cfg.c b/usr/src/lib/libdscfg/common/cfg.c new file mode 100644 index 0000000000..72355963e0 --- /dev/null +++ b/usr/src/lib/libdscfg/common/cfg.c @@ -0,0 +1,3581 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <sys/mnttab.h> +#include <sys/vtoc.h> +#include <errno.h> +#include <limits.h> +#include <fcntl.h> +#include <string.h> +#include <stdarg.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <locale.h> +#include <langinfo.h> +#include <libintl.h> +#include <stdarg.h> +#include <netdb.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/utsname.h> + +#include "cfg_impl.h" +#include "cfg.h" +#include "cfg_lockd.h" + +#if 0 +#define DEBUG_CFGLIST +#define DEBUG_EXTRA +#define DEBUG_LIB +#define DEBUG_NOISY +#define DEBUG_OUT +#endif + +#define MAX_CFG 16 /* Max. number of lines in /etc/dscfg_format */ +#define MAX_SET 12 /* number of chars in a set name */ + + +/* parser tree for config section */ +static struct parser chead[MAX_CFG] = { NULL }; +static int chead_loaded = 0; +static char config_file[CFG_MAX_BUF]; +static char dectohex[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +#define CHARS_TO_ENCODE "=;\t " +#define min(a, b) ((a) > (b) ? (b) : (a)) + +/* field to be sorted on in sorting routines */ +static struct sortby_s { + char section[CFG_MAX_KEY]; + char field[CFG_MAX_KEY]; + int offset; + int comperror; +} sortby; + +int cfg_severity = 0; +char *cfg_perror_str; +static int cfg_read(cfp_t *); +static void cfg_read_parser_config(cfp_t *); +static int cfg_rdlock(CFGFILE *); +static int cfg_wrlock(CFGFILE *); +static int cfg_lockd; +void cfg_replace_lists(cfp_t *); +void cfg_free_parser_tree(); +void cfg_invalidate_hsizes(int, const char *); +int cfg_map_cfglists(cfp_t *); +int cfg_hdrcmp(cfp_t *); +void cfg_free_cfglist(cfp_t *); + +extern cfg_io_t *cfg_block_io_provider(void); +extern cfg_io_t *cfg_raw_io_provider(void); +extern int cl_initialized; + +#ifdef DEBUG_LIB +static void +dump_status(cfp_t *cfp, char *str) +{ + printf("called from %s\n", str); + printf(gettext("Header info:\n" + "\tmagic: %x\tstate: %x\n"), + cfp->cf_head->h_magic, cfp->cf_head->h_state); + printf(gettext("Parser section:\n" + "Start: %x\tsize: %d\toffset: %d\n"), + cfp->cf_mapped, cfp->cf_head->h_parsesize, + cfp->cf_head->h_parseoff); + printf(gettext("Config section:\n" + "Start: %x\tsize:%d\tacsize: %d\n"), + cfp->cf_head->h_cparse, cfp->cf_head->h_csize, + cfp->cf_head->h_acsize); + printf("\n\tccopy1: %x\tccopy2: %x\n", + cfp->cf_head->h_ccopy1, cfp->cf_head->h_ccopy2); + printf(gettext("Sequence:\n" + "\tseq1: %d\t\tseq2: %d\n"), + cfp->cf_head->h_seq1, cfp->cf_head->h_seq2); +} +#endif /* DEBUG */ + +/* + * cfg_get_item + * return position from parser config given tag and field + */ +static int +cfg_get_item(struct parser *tbl, const char *tag, const char *field) +{ + int i; + struct lookup *p; + + for (i = 0; i < MAX_CFG; i++) { + /* only as many lists as defined */ + if (tbl[i].tag.l_word[0] == '\0') { + i = MAX_CFG; + break; + } + if (strcmp(tbl[i].tag.l_word, tag) == 0) + break; + } + + /* Handle table size */ + if (i < MAX_CFG) { + p = tbl[i].fld; + while (p) { + if (strcmp(p->l_word, field) == 0) + return (p->l_value); + p = p->l_next; + } + } + + /* Handle failure */ + return (-1); +} + +/* + * cfg_get_num_flds + * return number of fields for given parser tag + */ +static int +cfg_get_num_flds(struct parser *tbl, const char *tag, int *table_index) +{ + int i; + int pos = 0; + struct lookup *p; + + for (i = 0; i < MAX_CFG; i++) { + /* only as many lists as defined */ + if (tbl[i].tag.l_word[0] == '\0') { + i = MAX_CFG; + break; + } + if (strcmp(tbl[i].tag.l_word, tag) == 0) { + *table_index = i; + break; + } + } + + /* Handle table size */ + if (i < MAX_CFG) { + p = tbl[i].fld; + while (p) { + pos++; + p = p->l_next; + } + return (pos); + } + + return (0); +} + +/* + * count white space fields + */ +static int +cfg_cnt_flds(char *value) +{ + char *ptr; + char buf[CFG_MAX_BUF]; + int flds = 0; + + if ((value == NULL) || (strlen(value) >= CFG_MAX_BUF)) + return (0); + + bzero(buf, CFG_MAX_BUF); + strcpy(buf, value); + ptr = strtok(buf, " "); + while (ptr) { + flds++; + ptr = strtok(NULL, " "); + } + return (flds); +} + +/* + * cfg_get_parser_offset + * returns the index for each + * section of the parser.. + * ie. parser info for sndr is chead[3].tag.l_word + * this will help us find sndr quicker, as the + * the memory picture of the sets mimic this ordering + */ +static int +cfg_get_parser_offset(const char *section) +{ + int i; + + for (i = 0; i < MAX_CFG; i++) { + /* only as many lists as defined */ + if (chead[i].tag.l_word[0] == '\0') { + i = MAX_CFG; + break; + } + if (strcmp(chead[i].tag.l_word, section) == 0) + break; + } + + /* Handle table size */ + if (i < MAX_CFG) + return (i); + + /* Handle failure */ + cfg_perror_str = dgettext("cfg", + "cfg_get_parser_offset: section not found"); + cfg_severity = CFG_EFATAL; + errno = ESRCH; + return (-1); +} + +/* + * cfg_fld_mov + * move fields from old buffer to new + * moving only specified fields + * concates newbuf + * returns fields moved + */ +static int +cfg_fld_mov(char *newbuf, char *oldbuf, int start, int end) +{ + char buf[CFG_MAX_BUF]; + char *ptr; + int flds = 0; + + bzero(buf, CFG_MAX_BUF); + if (oldbuf == NULL) + return (0); + + if ((start > end) || (strlen(oldbuf) >= CFG_MAX_BUF)) { + return (0); + } + if (!start || !end) + return (-1); + strcpy(buf, oldbuf); + ptr = strtok(buf, " "); + while (ptr) { + flds++; + if (flds >= start && flds <= end) { + strcat(newbuf, ptr); + strcat(newbuf, " "); + } + ptr = strtok(NULL, " "); + } + + return (flds); +} + +/* + * cfg_filter_node + * return indication if this raw buf should be returned + * checks cfg->cf_node for filtering + * We already know that this buf meets most of our criteria + * find the cnode field in the buf and see if it matches + * returns + * TRUE Good entry + * FALSE Don't use it + */ +static int +cfg_filter_node(CFGFILE *cfg, struct parser *tbl, char *buf, char *tag) +{ + char tmpbuf[CFG_MAX_BUF]; + int i = 1; + int fld; + char *ptr; + + if (!cfg->cf_node) /* no filter always good */ + return (TRUE); + bzero(tmpbuf, CFG_MAX_BUF); + fld = cfg_get_item(tbl, tag, "cnode"); + if (fld < 0) /* no cnode field always good */ + return (TRUE); + strncpy(tmpbuf, buf, CFG_MAX_BUF); + if (tmpbuf[CFG_MAX_BUF - 1] != '\0') + return (FALSE); + ptr = strtok(tmpbuf, " "); + while (ptr && (i < fld)) { + ptr = strtok(NULL, " "); + i++; + } + if (!ptr) + return (FALSE); +#ifdef DEBUG_EXTRA + (void) fprintf(stderr, "cfg_filter_node: node=%s:%d cnode=%s:%d\n", + cfg->cf_node, strlen(cfg->cf_node), ptr, strlen(ptr)); +#endif + if (strcmp(ptr, cfg->cf_node) == 0) + return (TRUE); + return (FALSE); +} +/* + * cfg_insert_node + * insert resource in bufs which contain cnode parser field + */ +static void +cfg_insert_node(CFGFILE *cfg, struct parser *tbl, char *buf, char *tag) +{ + char tmpbuf[CFG_MAX_BUF]; + int fld; + int nflds; + int table_index; + + bzero(tmpbuf, CFG_MAX_BUF); + strcpy(tmpbuf, " "); + fld = cfg_get_item(tbl, tag, "cnode"); + nflds = cfg_get_num_flds(tbl, tag, &table_index); + if ((fld < 0) && !(cfg->cf_node)) /* no cnode field always good */ + return; + + cfg_fld_mov(tmpbuf, buf, 1, (fld - 1)); + if (cfg->cf_node) + strcat(tmpbuf, cfg->cf_node); + else + strcat(tmpbuf, "-"); + strcat(tmpbuf, " "); + cfg_fld_mov(tmpbuf, buf, (fld + 1), nflds); + bcopy(tmpbuf, buf, strlen(tmpbuf) + 1); +} + +/* + * cfg_is_cnode + * Parser current buffer to see if a non-empty " - " cnode exists + */ +/*ARGSUSED*/ +static int +cfg_is_cnode(CFGFILE *cfg, struct parser *tbl, char *buf, char *tag) +{ + char tmpbuf[CFG_MAX_BUF]; + int fld = cfg_get_item(tbl, tag, "cnode"); + + if (fld >= 0) { + tmpbuf[0] = '\0'; + cfg_fld_mov(tmpbuf, buf, fld, fld); + return (strcmp(tmpbuf, "- ") ? TRUE : FALSE); + } + return (FALSE); +} +/* + * cfg_get_cstring + * key determines section and value + * special considerations: + * AA.BB.CC... + * AA = data service tag + * BB = set number relative to first set (1..n) + * CC = field of set or if absent, all + */ +int +cfg_get_cstring(CFGFILE *cfg, const char *key, void *value, int value_len) +{ + cfp_t *cfp; + char buf[CFG_MAX_BUF]; + char tmpkey[CFG_MAX_KEY]; + char *section; + char set[MAX_SET]; + char *setp; + char *itemp; + char *p; + int pos = 1; + int setnum; + int relnum; + int secnum; + int numfound; + int needed; + int table_offset; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (!cfg_rdlock(cfg)) { + cfg_perror_str = dgettext("cfg", CFG_NOTLOCKED); + cfg_severity = CFG_EFATAL; + return (-1); + } + + bzero(buf, sizeof (buf)); + bzero(set, sizeof (set)); + bzero(tmpkey, sizeof (tmpkey)); + strcpy(tmpkey, key); + section = strtok(tmpkey, "."); + setp = strtok(NULL, "."); + itemp = strtok(NULL, "."); + +#ifdef DEBUG_EXTRA + if (!itemp) + (void) fprintf(stderr, "cfg_get_cstring:section:%s setp=%s\n", + section, setp); + else + (void) fprintf(stderr, + "cfg_get_cstring:section:%s setp=%s fld=%s\n", + section, setp, itemp); +#endif + + table_offset = cfg_get_parser_offset(section); + setnum = atoi(setp + 3); + if ((setnum < 1) || (setnum > 0x7ffd)) { + errno = EINVAL; + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + + /* + * we have to figure out where this set is + * in relation to other sets + */ + relnum = 1; + secnum = 0; + numfound = 0; + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + while (numfound < setnum) { + if ((*cfp->cf_pp->readcf) + (cfp, buf, table_offset, relnum - secnum) == NULL) { + secnum = relnum - 1; + break; + } + if (cfg_filter_node(cfg, &chead[0], buf, section)) + numfound++; + + if (numfound == setnum) + break; + + relnum++; + } + if (numfound == setnum) + break; + } + + /* Fail to find anything? */ + if (cfp >= &cfg->cf[2]) { + errno = ESRCH; + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + + if (buf) { + if (!itemp) { + strncpy(value, buf, value_len); + return (0); + } + + if (itemp) { + needed = cfg_get_item(&chead[0], section, itemp); + p = strtok(buf, " "); + while (p) { + if (needed == pos) { + errno = 0; + if (*p == '-') { + strcpy(value, ""); + return (0); + } + else + if (strlen(p) > value_len) { + errno = E2BIG; + cfg_perror_str = + dgettext("cfg", + strerror(errno)); + cfg_severity = + CFG_ENONFATAL; + return (-1); + } + + strncpy(value, p, value_len); + + return (pos); + } + p = strtok(NULL, " "); + if (!p) + break; + pos++; + } + } + } + errno = ESRCH; + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (-1); +} + +/* + * cfg_find_cstring() + * search for a string in the specified section + * in the specified field(s) + * if nfld is 0, then the string is searched for in + * every field of the entry + * the set number of the first occurence of target is returned + * ie. if /dev/vx/rdsk/vol10 is found in sndr.set9, 9 will be returned + * that is, of course, if the correct field was searched on. + * -1 on error + * + */ +int +cfg_find_cstring(CFGFILE *cfg, const char *target, + const char *section, int numflds, ...) { + + char **list = NULL; + va_list ap; + char buf[CFG_MAX_BUF]; + char *field, *p; + char **fldbuf = NULL; + int i, j, rc; + int pos = 1; + int fieldnum; + int nflds; + int tbl_off; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (numflds == 0) { + nflds = cfg_get_num_flds(&chead[0], section, &tbl_off); + + } else { + nflds = numflds; + } + if ((fldbuf = calloc(nflds, CFG_MAX_KEY)) == NULL) { + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (numflds == 0) { /* search the whole string */ + if ((rc = cfg_get_section(cfg, &list, section)) <= 0) { + for (i = 0; i < nflds; i++) + free(fldbuf[i]); + free(fldbuf); + return (rc); + } + for (i = 0; i < rc; i++) { + bzero(buf, sizeof (buf)); + strcpy(buf, list[i]); + p = strtok(buf, " "); + while (p) { + if (strcmp(p, target) == 0) { /* we found it! */ + for (j = 0; j < rc; j++) + free(list[j]); + free(list); + for (j = 0; j < nflds; j++) + free(fldbuf[j]); + free(fldbuf); + return (i + 1); + } + p = strtok(NULL, " "); + } + } + for (i = 0; i < nflds; i++) + free(fldbuf[j]); + for (i = 0; i < rc; i++) + free(list[i]); + free(fldbuf); + free(list); + return (0); + } + + if ((rc = cfg_get_section(cfg, &list, section)) <= 0) { + for (i = 0; i < nflds; i++) + free(fldbuf[i]); + free(fldbuf); + return (rc); + } + + va_start(ap, numflds); + for (i = 0; i < numflds; i++) { + fldbuf[i] = strdup(va_arg(ap, char *)); + } + + fldbuf[i] = NULL; + + for (j = 0; j < numflds; j++) { + fieldnum = cfg_get_item(&chead[0], section, fldbuf[j]); + for (i = 0; i < rc; i++) { + bzero(buf, sizeof (buf)); + strcpy(buf, list[i]); + + field = strtok(buf, " "); + pos = 1; + while (pos < fieldnum) { + field = strtok(NULL, " "); + pos++; + } + if (field == NULL) { + for (j = 0; j < numflds; j++) + free(fldbuf[j]); + for (j = 0; j < rc; j++) + free(list[j]); + free(fldbuf); + free(list); + return (-1); + } + + if (strcmp(field, target) == 0) { + for (j = 0; j < numflds; j++) + free(fldbuf[j]); + for (j = 0; j < rc; j++) + free(list[j]); + free(fldbuf); + free(list); + + return (i + 1); + } + + } + + } + for (i = 0; i < nflds; i++) + free(fldbuf[i]); + for (i = 0; i < rc; i++) + free(list[i]); + free(fldbuf); + free(list); + return (0); +} + +/* + * cfg_put_cstring + * modify entry or add an entry to configuration section + * Key syntax supported + * tag Add entry (in entirely) to config + * tag.setn Add entry to setn If it exists overwrite old entry + * tag.setn.field Change field in setn + * value + * string to change + * NULL delete specified key + * + */ + +int +cfg_put_cstring(CFGFILE *cfg, const char *key, void *value, int val_len) +{ + cfp_t *cfp; + char buf[CFG_MAX_BUF]; + char newbuf[CFG_MAX_BUF]; + char *bufp; + char tmpkey[CFG_MAX_KEY]; + char *section; + char *setp; + char *itemp; + int nofield = 0; + int noset = 0; + int fldnum; + int setnum = 0; + int relnum; + int secnum; + int numfound; + int addcnode = 1; + int table_index; + int table_offset; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + bzero(buf, sizeof (buf)); + strcpy(tmpkey, key); + section = strtok(tmpkey, "."); + setp = strtok(NULL, "."); + itemp = strtok(NULL, "."); + + if (!cfg_wrlock(cfg)) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (!key) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + if (value && val_len == 0) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + if (!itemp) + nofield++; + if (!setp) + noset++; + else if (setp) { + setnum = atoi(setp + 3); + if (setnum < 1 || setnum > 0x7ffd) { + errno = EINVAL; + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + } + + table_offset = cfg_get_parser_offset(section); + + /* + * we have to figure out where this set is + * in relation to other sets + */ + relnum = 1; + secnum = 0; + numfound = 0; + + if (setp && nofield) { + char tmpbuf[CFG_MAX_BUF]; + int rc; + int nflds; + int got; + + /* + * Set specified but no field + */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = + dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + while (numfound < setnum) { + if ((*cfp->cf_pp->readcf) + (cfp, tmpbuf, table_offset, relnum - secnum) + == NULL) { + secnum = relnum - 1; + break; + } + if (cfg_filter_node(cfg, &chead[0], tmpbuf, + section)) + numfound++; + + if (numfound == setnum) + break; + + relnum++; + } + if (numfound == setnum) + break; + } + + /* Fail to find anything? */ + if (cfp >= &cfg->cf[2]) { + errno = ESRCH; + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + + nflds = cfg_get_num_flds(&chead[0], section, &table_index); + + if (value == NULL) { + /* Remove entry completely */ + + if ((rc = ((*cfp->cf_pp->remcf) + (cfp, table_index, relnum - secnum))) < 0) + return (rc); + return (0); + } + + got = cfg_cnt_flds(value); + bzero(buf, sizeof (buf)); + + strncpy(buf, " ", 1); + if (strlen(value) > sizeof (buf) - 2) { + errno = E2BIG; + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + strncat(buf, value, val_len); + if (got < nflds) { + for (/* CSTYLED */; got < nflds; got++) + strncat(buf, " - ", 3); + } else if (got > nflds) { + return (-1); + } else { + /* got == nflds, so cnode was included */ + addcnode = 0; + } + + bufp = buf; + if (addcnode) { + cfg_insert_node(cfg, &chead[0], buf, section); + } + + (*cfp->cf_pp->replacecf) + (cfp, bufp, table_index, relnum - secnum); + + return (TRUE); + } + + /* + * Both Set and field are specified + * needs to get current whole entry and old requested field + * copy good fields to buf, replace new field in buf + * move everything depending of new size + * replace entry so set# does not change + */ + if (setp && itemp) { + int rc; + int nflds; + int cnodepos; + + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = + dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + while (numfound < setnum) { + if ((*cfp->cf_pp->readcf) + (cfp, buf, table_offset, relnum - secnum) + == NULL) { + secnum = relnum - 1; + break; + } + if (cfg_filter_node(cfg, &chead[0], buf, + section)) + numfound++; + + if (numfound == setnum) + break; + + relnum++; + } + if (numfound == setnum) + break; + } + + /* Fail to find anything? */ + if (cfp >= &cfg->cf[2]) { + errno = ESRCH; + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (-1); + } + + nflds = cfg_get_num_flds(&chead[0], section, &table_index); + fldnum = cfg_get_item(&chead[0], section, itemp); + bzero(newbuf, sizeof (newbuf)); + strncpy(newbuf, " ", 1); + + /* move good flds in */ + rc = cfg_fld_mov(newbuf, buf, 1, fldnum - 1); + if (rc < 0) + return (rc); + + /* move new fld in */ + strncat(newbuf, value, strlen(value)); + strcat(newbuf, " "); + + /* move remaining flds in */ + rc = cfg_fld_mov(newbuf, buf, fldnum + 1, nflds); + if (rc < 0) + return (rc); + + cnodepos = cfg_get_item(&chead[0], section, "cnode"); + if ((cnodepos >= 0) && strcmp(itemp, "cnode") != 0) { + /* add cnode if user didn't specify it */ + cfg_insert_node(cfg, &chead[0], + newbuf, section); + } + + (*cfp->cf_pp->replacecf) + (cfp, newbuf, table_index, relnum - secnum); + + return (TRUE); + } + + if (noset) { /* blast entire thing in */ + int nflds; + int got; + int cnodepos; + + bufp = buf; + if (!value) { /* we shouldn't be here */ + errno = EINVAL; + return (-1); + } + strncat(buf, " ", 1); + if (strlen(value) > sizeof (buf) - 2) { + errno = E2BIG; + return (-1); + } + + strncat(buf, value, val_len); + nflds = cfg_get_num_flds(&chead[0], section, &table_index); + got = cfg_cnt_flds(value); + + cnodepos = cfg_get_item(&chead[0], section, "cnode"); + if (cnodepos < 0 || got >= cnodepos) { + /* no cnode, or cnode was specified by caller */ + addcnode = 0; + } + + if (got < nflds) { + for (/* CSTYLED */; got < nflds; got++) + strncat(buf, " - ", 3); + } else if (got > nflds) { + errno = EINVAL; /* specified too many fields */ + return (-1); + } else { + /* got == nflds, so cnode was included */ + addcnode = 0; + } + + if (addcnode) { + cfg_insert_node(cfg, &chead[0], buf, section); + } + + /* Make sure we put this entry in the right database */ + if (cfg_is_cnode(cfg, &chead[0], buf, section) && + cfg->cf[1].cf_fd) + cfp = &cfg->cf[1]; + else + cfp = &cfg->cf[0]; + + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + if (cfp->cf_head->h_csize + strlen(buf) > CFG_DEFAULT_SSIZE) { + errno = ENOSPC; + return (-1); + } + + (*cfp->cf_pp->addcf)(cfp, bufp, table_index); + + return (TRUE); + } + + errno = EINVAL; + cfg_perror_str = strerror(errno); + cfg_severity = CFG_ENONFATAL; + return (-1); +} + +/* + * cfg_encode_char + * + * Encode a single character into % + hex ascii value + */ +static void +cfg_encode_char(char *result, char ch) +{ + *result++ = '%'; + *result++ = dectohex[ (ch >> 4) & 0xf ]; + *result++ = dectohex[ ch & 0xf ]; +} + +/* + * cfg_decode_char + * + * Reverses cfg_encode_char + */ +static char +cfg_decode_char(char *code) +{ + char retval; + if (*code != '%') { + return ('\0'); + } + ++code; + if (!isxdigit(*code)) + return ('\0'); + retval = (isdigit(*code)? *code - '0' : *code - 'a' + 10); + retval <<= 4; + ++code; + if (!isxdigit(*code)) + return ('\0'); + retval |= (isdigit(*code)? *code - '0' : *code - 'a' + 10); + + return (retval); +} + +/* + * cfg_encode_option + * + * Transforms the key and value strings so that special characters + * can be used within the options field. + * + * Returns: + * Length of encoded string; -1 on failure + */ +static int +cfg_encode_string(char *str, char *output, int outlen) +{ + char *mem, *p, *q; + int curlen; + + + /* first, scan through the tag string converting %-signs */ + p = str; + q = output; + curlen = 0; + while (*p && curlen < outlen) { + if (*p == '%') { + if (curlen + 3 >= outlen) { + return (-1); + } + cfg_encode_char(q, *p); + curlen += 3; + q += 3; + } else { + *q++ = *p; + ++curlen; + } + ++p; + } + if (curlen < outlen) + *q = '\0'; + + /* now encode special characters */ + p = mem = strdup(output); + q = output; + curlen = 0; + while (*p && curlen < outlen) { + if (strchr(CHARS_TO_ENCODE, *p) != 0) { + if (curlen + 3 >= outlen) { + free(mem); + return (-1); + } + cfg_encode_char(q, *p); + curlen += 3; + q += 3; + } else { + *q++ = *p; + ++curlen; + } + ++p; + } + free(mem); + + if (curlen < outlen) + *q = '\0'; + /* LINTED possible ptrdiff_t overflow */ + return (q - output); +} + +/* + * cfg_decode_option + * + * Given a string, decodes any %-encodes on it. + */ +static void +cfg_decode_string(char *str, char *output, int outlen) +{ + char *p, *q; + int curlen; + + p = str; + q = output; + curlen = 0; + while (*p && curlen < outlen) { + if (*p == '%') { + char ch = cfg_decode_char(p); + if (!ch) { + *q++ = *p++; + ++curlen; + } else { + *q++ = ch; + p += 3; + ++curlen; + } + } else { + *q++ = *p++; + ++curlen; + } + } + if (curlen < outlen) + *q = '\0'; +} + +/* + * cfg_get_options + * return first options set from basekey + * Subsequent calls with basekey = NULL return next option if any + * into tag and val + * returns + * true success and more options data + * -1 no options data + */ + +int +cfg_get_options(CFGFILE *cfg, int section, const char *basekey, char *tag, + int tag_len, char *val, int val_len) +{ + static char buf[CFG_MAX_BUF]; + char decode_buf[CFG_MAX_BUF]; + int rc; + char *ttag, *tval; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + errno = ENOSYS; + if (basekey == 0) { + ttag = strtok(NULL, "="); + } else { + bzero(buf, CFG_MAX_BUF); + if (section == CFG_SEC_CONF) { + rc = cfg_get_cstring(cfg, basekey, buf, CFG_MAX_BUF); + } else + return (-1); + if (rc < 0) + return (rc); + /* buf now contains raw options data */ + ttag = strtok(buf, "="); + } + tval = strtok(NULL, ";"); + if (!(tval) || !(ttag)) + return (-1); + if ((strlen(tval) > val_len) || (strlen(ttag) > tag_len)) { + errno = E2BIG; + return (-1); + } + cfg_decode_string(tval, decode_buf, CFG_MAX_BUF); + strncpy(val, decode_buf, val_len); + cfg_decode_string(ttag, decode_buf, CFG_MAX_BUF); + strncpy(tag, decode_buf, tag_len); + errno = 0; + return (TRUE); +} + +/* + * cfg_put_options + * + * Replaces existing tag with new val. If tag doesn't exist, + * then it adds a new tag with the specified val. + * + * Return: + * true success + * -1 incorrect section, or read error from cfg DB + */ +int +cfg_put_options(CFGFILE *cfg, int section, const char *basekey, char *tag, + char *val) +{ + char buf[CFG_MAX_BUF]; + char encode_buf[CFG_MAX_BUF]; + char *p; + int enclen; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + errno = ENOSYS; + bzero(buf, CFG_MAX_BUF); + if (section != CFG_SEC_CONF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + return (-1); + } + if (!tag || !*tag || !val || !*val) + return (-1); + if (cfg_get_cstring(cfg, basekey, buf, CFG_MAX_BUF) < 0) { + /* cfg severity & perror_str set up cfg_get_cstring() */ + return (-1); + } + *encode_buf = ';'; + enclen = cfg_encode_string(tag, &encode_buf[1], CFG_MAX_BUF - 1) + 1; + if (enclen < 1 || (enclen + 1) >= CFG_MAX_BUF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Buffer too small"); + return (-1); + } + encode_buf[enclen] = '='; + encode_buf[enclen + 1] = '\0'; + + /* check the start of the string */ + if (strncmp(buf, &encode_buf[1], enclen) == 0) { + /* locate the end of this option */ + p = strchr(buf, ';'); + if (p && *(p + 1) != '\0') { + /* add the new tag to the end */ + ++p; + strcat(p, &encode_buf[1]); + } else { + /* completely overwrite the existing tag */ + p = buf; + strcpy(p, &encode_buf[1]); + } + if (cfg_encode_string(val, encode_buf, CFG_MAX_BUF) < 0) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Buffer too small"); + return (-1); + } + strcat(p, encode_buf); + strcat(p, ";"); + if (cfg_put_cstring(cfg, basekey, p, strlen(p)) < 0) { + /* severity & perror_str set by cfg_put_cstring */ + return (-1); + } + errno = 0; + return (TRUE); + } + + /* it's hiding somewhere inside... */ + p = strstr(buf, encode_buf); + if (p) { + /* delete the old value */ + char *q = strchr(p + 1, ';'); + if (q) { + strcpy(p + 1, q + 1); + } else { + *p = '\0'; + } + strcat(buf, &encode_buf[1]); + } else if (*buf) { + strcat(buf, &encode_buf[1]); + } else { + strcpy(buf, &encode_buf[1]); + } + enclen = cfg_encode_string(val, encode_buf, CFG_MAX_BUF); + if (enclen < 0 || (strlen(buf) + enclen) >= CFG_MAX_BUF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Buffer too small"); + return (-1); + } + strcat(buf, encode_buf); + strcat(buf, ";"); + if (cfg_put_cstring(cfg, basekey, buf, CFG_MAX_BUF) < 0) { + /* severity & perror_str set by cfg_put_cstring */ + return (-1); + } + errno = 0; + return (TRUE); +} + +/* + * cfg_get_single_option + * + * Scans the options string for the specified option and returns + * the decoded value + * + * Return: + * true success + * -1 incorrect section, or read error from cfg DB + */ +int +cfg_get_single_option(CFGFILE *cfg, int section, const char *basekey, char *tag, + char *val, int val_len) +{ + char buf[CFG_MAX_BUF]; + char encode_buf[CFG_MAX_BUF]; + char *p, *q; + int enclen; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + errno = ENOSYS; + bzero(buf, CFG_MAX_BUF); + if (section != CFG_SEC_CONF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + return (-1); + } + if (cfg_get_cstring(cfg, basekey, buf, CFG_MAX_BUF) < 0) { + /* severity & perror_str set by cfg_get_cstring */ + return (-1); + } + + *encode_buf = ';'; + enclen = cfg_encode_string(tag, &encode_buf[1], CFG_MAX_BUF - 1) + 1; + if (enclen < 1 || (enclen + 1) >= CFG_MAX_BUF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Buffer too small"); + return (-1); + } + encode_buf[enclen] = '='; + encode_buf[enclen + 1] = '\0'; + + /* check the start of the string */ + if (strncmp(buf, &encode_buf[1], enclen) == 0) { + p = strchr(buf, '='); + if (!p) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Option not found"); + return (-1); + } + ++p; + q = strchr(p, ';'); + if (q) { + *q = '\0'; + } + cfg_decode_string(p, val, val_len); + errno = 0; + return (TRUE); + } + + /* it's hiding somewhere inside... */ + p = strstr(buf, encode_buf); + if (p) { + p += enclen + 1; + q = strchr(p, ';'); + if (q) { + *q = '\0'; + } + cfg_decode_string(p, val, val_len); + errno = 0; + return (TRUE); + } + + /* key not found */ + return (-1); + +} + +/* + * cfg_del_option + * + * Removes a single key=val pair from the specified option field + * + * Return: + * true success + * -1 unable to update config + */ +int +cfg_del_option(CFGFILE *cfg, int section, const char *basekey, char *tag) +{ + char buf[CFG_MAX_BUF]; + char encode_buf[CFG_MAX_BUF]; + char *p, *q; + int enclen, rc; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + bzero(buf, CFG_MAX_BUF); + if (section != CFG_SEC_CONF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + return (-1); + } + if (cfg_get_cstring(cfg, basekey, buf, CFG_MAX_BUF) < 0) { + /* severity & perror_str are set by cfg_get_cstring */ + return (-1); + } + + *encode_buf = ';'; + enclen = cfg_encode_string(tag, &encode_buf[1], CFG_MAX_BUF - 1) + 1; + if (enclen < 1 || (enclen + 1) >= CFG_MAX_BUF) { + cfg_severity = CFG_ENONFATAL; + cfg_perror_str = dgettext("cfg", "Buffer too small"); + return (-1); + } + encode_buf[enclen] = '='; + encode_buf[enclen + 1] = '\0'; + + /* check the start of the string */ + if (strncmp(buf, &encode_buf[1], enclen) == 0) { + p = strchr(buf, ';'); + if (p && (*(p + 1) != '\0')) { + rc = cfg_put_cstring(cfg, basekey, p + 1, strlen(p + 1)); + } else { + rc = cfg_put_cstring(cfg, basekey, "-", 1); + } + /* severity & perror_str are set by cfg_put_cstring */ + return (rc); + } + + /* sigh */ + p = strstr(buf, encode_buf); + if (!p) { + /* already removed */ + return (TRUE); + } + q = strchr(p + 1, ';'); + + /* + * Now the string looks like: + * | first few options | *p | option to remove | *q | rest | '\0' + */ + + if (!q) { + /* hum... */ + *p = '\0'; + } else { + strcpy(p, q); + } + + return (cfg_put_cstring(cfg, basekey, buf, strlen(buf))); +} + +static void +cfg_set_memorymap(cfp_t *cfp) +{ + cfgheader_t *hd = cfp->cf_head; + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "callocing %d for initial reads\n", hd->h_csize); +#endif + + hd->h_ccopy1 = (char *)calloc(hd->h_csize, sizeof (char)); + hd->h_ccopy2 = (char *)calloc(hd->h_csize, sizeof (char)); + hd->h_sizes1 = (int *)calloc(CFG_DEFAULT_PSIZE, sizeof (int)); + hd->h_sizes2 = (int *)calloc(CFG_DEFAULT_PSIZE, sizeof (int)); +} + +/* + * cfg_init_header + * fill in default header info + */ +static void +cfg_init_header(cfp_t *cfp) +{ + time_t tloc; + cfgheader_t *hd = cfp->cf_head; + + hd->h_magic = (int32_t)CFG_NEW_MAGIC; + hd->h_stamp = time(&tloc); + hd->h_lock = 0; + /* parser config */ + hd->h_parsesize = 0; + hd->h_parseoff = 0; + hd->h_csize = 0; + hd->h_psize = 0; + hd->h_cfgs = NULL; + hd->h_ncfgs = 0; + hd->h_seq1 = hd->h_seq2 = 1; + bzero(hd->h_cfgsizes, MAX_CFG * sizeof (int)); +} +/* + * cfg_read + * read header and all sections of configuration file + * gets new data for incore copy + * removes invalid header state + * works even if config and persistent sections are empty + * + */ +static int +cfg_read(cfp_t *cfp) +{ + int rc; + cfgheader_t *hd; + int readsize = 0; +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_read\n"); +#endif + + if (!cfp->cf_head) { + if ((hd = calloc(1, sizeof (*hd))) == NULL) + return (FALSE); +#ifdef DEBUG_HDR + (void) fprintf(stderr, "initial cfg header read\n"); +#endif + cfp->cf_head = hd; + } + + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: seek header failed\n"); +#endif + return (FALSE); + } + + rc = (*cfp->cf_pp->read)(cfp, (char *)cfp->cf_head, 4); + if (rc < 4) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: read magic number failed\n"); +#endif + return (FALSE); + } + + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: seek header failed\n"); +#endif + return (FALSE); + } + + rc = (*cfp->cf_pp->read)(cfp, (char *)cfp->cf_head, sizeof (*hd)); + if (rc < sizeof (*hd)) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: read header failed\n"); +#endif + return (FALSE); + } + + cfp->cf_head->h_cfgs = NULL; + cfg_set_memorymap(cfp); + if (cfp->cf_head->h_magic != CFG_NEW_MAGIC) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg_read: wrong MAGIC number %x\n", + cfp->cf_head->h_magic); +#endif + return (FALSE); + } + + cfp->cf_head->h_state &= ~(CFG_HDR_INVALID); + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "reading parser\n"); +#endif + rc = (*cfp->cf_pp->read) + (cfp, (char *)cfp->cf_mapped, CFG_DEFAULT_PARSE_SIZE); + if (rc < sizeof (*hd)) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg: read parse config failed\n"); +#endif + return (FALSE); + } + + readsize = cfp->cf_head->h_csize; + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "reading copy1 readsize = %d\n", readsize); +#endif + rc = (*cfp->cf_pp->read) + (cfp, (char *)cfp->cf_head->h_ccopy1, readsize); + if (rc < 0) { + /* don't fail just return */ +#ifdef DEBUG + (void) fprintf(stderr, "cfg: read ccopy1 section failed\n"); +#endif + return (FALSE); + } + + if ((*cfp->cf_pp->seek) + (cfp, CFG_DEFAULT_SSIZE - rc, SEEK_CUR) < 0) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg: seek (SEEK_CUR) failed\n"); +#endif + return (FALSE); + } + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "reading copy2 readsize = %d\n", readsize); +#endif + + rc = (*cfp->cf_pp->read) + (cfp, (char *)cfp->cf_head->h_ccopy2, readsize); + if (rc < 0) { + /* don't fail just return */ +#ifdef DEBUG + (void) fprintf(stderr, "cfg: read ccopy2 section failed\n"); +#endif + return (FALSE); + } + + /* read the sizes of the lists from disk */ + if ((*cfp->cf_pp->seek) + (cfp, CFG_DEFAULT_SSIZE - rc, SEEK_CUR) < 0) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg: seek (SEEK_CUR) failed\n"); +#endif + return (FALSE); + } + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "reading sizes\n"); +#endif + rc = (*cfp->cf_pp->read) + (cfp, (int *)cfp->cf_head->h_sizes1, CFG_DEFAULT_PSIZE); + if (rc < 0) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg: read h_sizes1 failed\n"); +#endif + return (FALSE); + } + + rc = (*cfp->cf_pp->read) + (cfp, (int *)cfp->cf_head->h_sizes2, CFG_DEFAULT_PSIZE); + if (rc < 0) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg: read h_sizes2 failed\n"); +#endif + return (FALSE); + } + + /* + * If initial or invalid sequence, use first section + */ + if ((cfp->cf_head->h_seq1 <= 0) && (cfp->cf_head->h_seq2 <= 0)) { + cfp->cf_head->h_cparse = cfp->cf_head->h_ccopy1; + cfp->cf_head->h_sizes = cfp->cf_head->h_sizes1; + } + + if (cfp->cf_head->h_seq1 >= cfp->cf_head->h_seq2) { + cfp->cf_head->h_cparse = cfp->cf_head->h_ccopy1; + cfp->cf_head->h_sizes = cfp->cf_head->h_sizes1; + } else { + cfp->cf_head->h_cparse = cfp->cf_head->h_ccopy2; + cfp->cf_head->h_sizes = cfp->cf_head->h_sizes2; + } + +#ifdef DEBUG_LIB + dump_status(cfp, "cfg_read"); +#endif + + return (TRUE); +} + +/* + * cfg_lock + * Read-write locking of the configuration + * reads into core all sections + * builds parser trees for each section + * Returns: TRUE if the lock was acquired, FALSE otherwise. + */ +int +cfg_lock(CFGFILE *cfg, CFGLOCK mode) +{ + cfp_t *cfp; + int is_locked = 0; + int rc; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + if (mode == CFG_UPGRADE) { + mode = CFG_WRLOCK; + } + + if (mode == CFG_WRLOCK && (cfg->cf[0].cf_flag & CFG_RDONLY)) { + goto fail; + } + + /* + * if you don't even give me the right lock request, + * why should I give you one? + */ + if (mode != CFG_RDLOCK && mode != CFG_WRLOCK) + goto fail; + + if (cfg_lockd) { + if (mode == CFG_WRLOCK) + cfg_lockd_wrlock(); + else + cfg_lockd_rdlock(); + is_locked = 1; + } else { + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_lock\n"); +#endif + /* Lock is always based on local file pointer */ + cfg->cf[1].cf_lock = cfg->cf[0].cf_lock = cfg->cf[0].cf_fd; + + if (!((cfg->cf[0].cf_flag & CFG_RDONLY) && + (mode == CFG_RDLOCK))) { + + struct flock lk = {0}; + lk.l_type = (mode == CFG_RDLOCK ? F_RDLCK : F_WRLCK); + lk.l_whence = SEEK_SET; + lk.l_start = (off_t)0; + lk.l_len = (off_t)0; + + if (fcntl(cfg->cf[0].cf_lock, F_SETLKW, &lk) < 0) + goto fail; + } + } + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if ((cfp->cf_head) && + (cfp->cf_head->h_state & CFG_HDR_INVALID)) { + if ((rc = cfg_hdrcmp(cfp)) == 0) { +#ifdef DEBUG_HDR + (void) fprintf(stderr, + "cfg header match, skipping re-read\n"); +#endif + cfp->cf_head->h_state |= CFG_HDR_RDLOCK; + if (mode == CFG_WRLOCK) + cfp->cf_head->h_state |= CFG_HDR_WRLOCK; + + cfp->cf_head->h_state &= ~(CFG_HDR_INVALID); + continue; + } +#ifdef DEBUG_HDR + (void) fprintf(stderr, "re-reading cfg, header mismatch\n"); +#endif + /* + * dump what we have, info is stale + */ + cfg_free_cfglist(cfp); + cfg_free_parser_tree(); + + if (cfp->cf_head->h_ccopy1) { + free(cfp->cf_head->h_ccopy1); + cfp->cf_head->h_ccopy1 = NULL; + } + if (cfp->cf_head->h_ccopy2) { + free(cfp->cf_head->h_ccopy2); + cfp->cf_head->h_ccopy2 = NULL; + } + if (cfp->cf_head->h_sizes1) { + free(cfp->cf_head->h_sizes1); + cfp->cf_head->h_sizes1 = NULL; + } + if (cfp->cf_head->h_sizes2) { + free(cfp->cf_head->h_sizes2); + cfp->cf_head->h_sizes2 = NULL; + } + + if (cfp->cf_head) + free(cfp->cf_head); + cfp->cf_head = NULL; + } + + if (cfp->cf_head == NULL) { + if (!cfg_read(cfp)) { + if (cfp->cf_head != NULL) + cfg_init_header(cfp); + else + goto fail; + } else { +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "reading parser config\n"); +#endif + /* build parser trees */ + cfg_read_parser_config(cfp); + } + + } + cfp->cf_head->h_state |= CFG_HDR_RDLOCK; + if (mode == CFG_WRLOCK) { + if (cfp->cf_head->h_seq1 >= cfp->cf_head->h_seq2) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, + "cfg_lock: WRLOCK copying 1 to 2\n"); +#endif + memcpy(cfp->cf_head->h_ccopy2, + cfp->cf_head->h_ccopy1, + cfp->cf_head->h_csize); + memcpy(cfp->cf_head->h_sizes2, + cfp->cf_head->h_sizes1, + CFG_DEFAULT_PSIZE); + + cfp->cf_head->h_cparse = cfp->cf_head->h_ccopy2; + cfp->cf_head->h_sizes = cfp->cf_head->h_sizes2; + } else { +#ifdef DEBUG_LIB + (void) fprintf(stderr, + "cfg_lock: WRLOCK copying 2 to 1\n"); +#endif + memcpy(cfp->cf_head->h_ccopy1, + cfp->cf_head->h_ccopy2, + cfp->cf_head->h_csize); + memcpy(cfp->cf_head->h_sizes1, + cfp->cf_head->h_sizes2, + CFG_DEFAULT_PSIZE); + + cfp->cf_head->h_cparse = cfp->cf_head->h_ccopy1; + cfp->cf_head->h_sizes = cfp->cf_head->h_sizes1; + } + + cfp->cf_head->h_state |= CFG_HDR_WRLOCK; + } + + if (cfg_map_cfglists(cfp) < 0) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: map_cfglists failed\n"); +#endif + goto fail; + } + +#ifdef DEBUG_LIB + dump_status(cfp, "cfg_lock"); +#endif + } + + return (TRUE); + +fail: + if (is_locked) { + cfg_lockd_unlock(); + } + cfg_perror_str = dgettext("cfg", CFG_EGENERIC); + cfg_severity = CFG_ENONFATAL; + return (FALSE); +} + +/* + * Unlock the database + */ +void +cfp_unlock(cfp_t *cfp) +{ + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_unlock\n"); +#endif + if (cfg_lockd) { + cfg_lockd_unlock(); + } else { + struct flock lk = {0}; + lk.l_type = F_UNLCK; + lk.l_whence = SEEK_SET; + lk.l_start = (off_t)0; + lk.l_len = (off_t)0; + (void) fcntl(cfp->cf_lock, F_SETLKW, &lk); + } + + if (cfp->cf_head != NULL) { + cfp->cf_head->h_state &= ~(CFG_HDR_RDLOCK|CFG_HDR_WRLOCK); + cfp->cf_head->h_state |= CFG_HDR_INVALID; + } +} +void +cfg_unlock(CFGFILE *cfg) +{ + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return; + } + + cfp_unlock(&cfg->cf[0]); + cfp_unlock(&cfg->cf[1]); +} + +/* + * Test for a read lock, set errno if failed. + */ +static int +cfg_rdlock(CFGFILE *cfg) +{ + int rc; + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head == NULL) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg_rdlock: cf_head == NULL\n"); +#endif + /* + * 6335583, if header == NULL, + * we can't call cfg_read to fill the header again + * since it will change the lock state to + * CFG_HDR_WRLOCK and dscfg will be the processer + * that hold the lock, + * just returning a FALSE if the case, + * then retrieve the lock state from flock structure. + */ + rc = FALSE; + break; + } else { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg_rdlock: cf_head != NULL\n"); +#endif + if ((cfp->cf_head->h_state & CFG_HDR_RDLOCK) + == CFG_HDR_RDLOCK) + rc = TRUE; + else { + rc = FALSE; + break; + } + } + } + + if (!rc) + errno = EPERM; + + return (rc); +} + +/* + * Test for a write lock, set errno if failed. + */ +static int +cfg_wrlock(CFGFILE *cfg) +{ + int rc; + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head == NULL) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg wrlock: cf_head == NULL\n"); +#endif + /* + * 6335583, see comments on cfg_rdlock + */ + rc = FALSE; + break; + } else { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg wrlock: cf_head != NULL\n"); +#endif + if ((cfp->cf_head->h_state & CFG_HDR_WRLOCK) + == CFG_HDR_WRLOCK) + rc = TRUE; + else { + rc = FALSE; + break; + } + } + } + + if (!rc) + errno = EPERM; + + return (rc); +} + +/* + * cfg_get_lock + * Find lock status of CFG database. + * Returns: TRUE and sets lock and pid if the lock is held, FALSE otherwise. + */ +int +cfg_get_lock(CFGFILE *cfg, CFGLOCK *lock, pid_t *pid) +{ + struct flock lk; + int rc; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + if (cfg_lockd) { + switch (cfg_lockedby(pid)) { + case LOCK_READ: + *lock = CFG_RDLOCK; + return (TRUE); + case LOCK_WRITE: + *lock = CFG_WRLOCK; + return (TRUE); + case LOCK_NOTLOCKED: + default: + return (FALSE); + } + } else { + if (cfg_wrlock(cfg)) { + *lock = CFG_WRLOCK; + *pid = getpid(); + return (TRUE); + } + + if (cfg_rdlock(cfg)) { + *lock = CFG_RDLOCK; + *pid = getpid(); + return (TRUE); + } + } + /* Lock is always based on local file pointer */ + cfg->cf[1].cf_lock = cfg->cf[0].cf_lock = cfg->cf[0].cf_fd; + + bzero(&lk, sizeof (lk)); + lk.l_type = F_WRLCK; + lk.l_whence = SEEK_SET; + lk.l_start = (off_t)0; + lk.l_len = (off_t)0; + + if (fcntl(cfg->cf[0].cf_lock, F_GETLK, &lk) < 0) + rc = FALSE; + else { + if (lk.l_type == F_UNLCK) + rc = FALSE; + else { + rc = TRUE; + *pid = lk.l_pid; + *lock = lk.l_type == F_WRLCK ? CFG_WRLOCK : CFG_RDLOCK; + } + } + + return (rc); +} + +/* + * cfg_commit + * Write modified version of header, configuration and persistent + * data using 2 stage commit. + * If no valid data is found in header, it is assumed to be an initial + * write and we will create the default header (could be dangerous) + * another tricky part, if we are doing an upgrade we may be dealing + * with an old database. we need to take care seeking and writing + * until such time that it is upgraded. + * + * Mutual exclusion is checked using cfg_lock + */ + +int +cfg_commit(CFGFILE *cfg) +{ + cfp_t *cfp; + int rc; + time_t tloc; + int section; + int wrsize, *ip; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + if (!cfg_wrlock(cfg)) + return (FALSE); + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + + /* + * lets put everything back into one char * + */ + cfg_replace_lists(cfp); + + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) { +#ifdef DEBUG_LIB + (void) fprintf(stderr, "cfg: seek header failed\n"); +#endif + return (FALSE); + } + + cfp->cf_head->h_size = cfp->cf_head->h_parsesize + + cfp->cf_head->h_csize + cfp->cf_head->h_psize; + cfp->cf_head->h_stamp = time(&tloc); + + /* seeking into database */ + if ((*cfp->cf_pp->seek) + (cfp, sizeof (cfgheader_t), SEEK_CUR) < 0) + return (FALSE); + + if (cfp->cf_head->h_ccopy1 == cfp->cf_head->h_cparse) { + if (cfp->cf_head->h_seq1 < 0) + cfp->cf_head->h_seq1 = 1; + else + cfp->cf_head->h_seq1 = cfp->cf_head->h_seq2 + 1; + section = 1; + } else { + if (cfp->cf_head->h_seq2 < 0) + cfp->cf_head->h_seq2 = 1; + else + cfp->cf_head->h_seq2 = cfp->cf_head->h_seq1 + 1; + section = 2; + } +#ifdef DEBUG_LIB + dump_status(cfp, "cfg_commit"); +#endif + rc = (*cfp->cf_pp->write) + (cfp, cfp->cf_mapped, CFG_DEFAULT_PARSE_SIZE); +#ifdef DEBUG + if (rc < 0) { + (void) fprintf(stderr, + "parse commit: rc %d h_parsesize %d\n", + rc, cfp->cf_head->h_parsesize); + } +#endif + if (section == 1) { + rc = (*cfp->cf_pp->write) + (cfp, cfp->cf_head->h_ccopy1, + cfp->cf_head->h_csize); +#ifdef DEBUG + if (rc < 0) { + (void) fprintf(stderr, + "csection commit 1: rc %d h_csize %d\n", + rc, cfp->cf_head->h_csize); + } +#endif + if ((*cfp->cf_pp->seek) + (cfp, (2 * CFG_DEFAULT_SSIZE) - rc, SEEK_CUR) < 0) + return (FALSE); + + /* + * limit the write to only what we need + */ + ip = cfp->cf_head->h_sizes1; + for (wrsize = 0; *ip; ip += *ip + 1) + wrsize += *ip + 1; + + rc = (*cfp->cf_pp->write)(cfp, + cfp->cf_head->h_sizes1, wrsize * sizeof (int)); +#ifdef DEBUG + if (rc < 0) { + (void) fprintf(stderr, + "cfg: write list sizes1 failed rc\n"); + } +#endif + } else { + if ((*cfp->cf_pp->seek)(cfp, + CFG_DEFAULT_SSIZE, SEEK_CUR) < 0) + return (FALSE); + + rc = (*cfp->cf_pp->write)(cfp, + cfp->cf_head->h_ccopy2, cfp->cf_head->h_csize); +#ifdef DEBUG + if (rc < 0) { + (void) fprintf(stderr, + "csection commit 2: rc %d h_csize %d\n", + rc, cfp->cf_head->h_csize); + } +#endif + if ((*cfp->cf_pp->seek) + (cfp, (CFG_DEFAULT_SSIZE + CFG_DEFAULT_PSIZE) - rc, + SEEK_CUR) < 0) + return (FALSE); + + /* + * limit the write to only what we need + */ + ip = cfp->cf_head->h_sizes2; + for (wrsize = 0; *ip; ip += *ip + 1) + wrsize += *ip + 1; + + rc = (*cfp->cf_pp->write)(cfp, cfp->cf_head->h_sizes2, + wrsize * sizeof (int)); +#ifdef DEBUG + if (rc < 0) { + (void) fprintf(stderr, + "cfg: write list sizes2 failed\n"); + } +#endif + + } + + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "writing h_csize %d\n", cfp->cf_head->h_csize); +#endif + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) + return (FALSE); + + cfp->cf_head->h_size = cfp->cf_head->h_parsesize + + cfp->cf_head->h_csize + cfp->cf_head->h_psize; + + rc = (*cfp->cf_pp->write) + (cfp, cfp->cf_head, sizeof (cfgheader_t)); + if (rc < 0) { + cfg_perror_str = dgettext("cfg", + "cfg_commit: header write failed"); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + } + + return (TRUE); +} + +/* + * cfg_rewind + * rewind internal file pointer for specified section + * empty now, rewind not necessary. But don't break + * old code. + */ +/*ARGSUSED*/ +void +cfg_rewind(CFGFILE *cfg, int section) +{ + switch (section) { + case CFG_SEC_CONF: + break; + case CFG_SEC_ALL: + break; + }; +} + +/* + * cfg_location + * set or return the default location file to + * determine the partition name of the configuration partition + * location is stored in well known file location + */ +char * +cfg_location(char *location, int mode, char *altroot) +{ + int fd; + int fmode; + int rc; + char wellknown[NSC_MAXPATH]; + char loc[NSC_MAXPATH]; + + if (mode == CFG_LOC_GET_LOCAL) { + return (CFG_LOCAL_LOCATION); + } else if (mode == CFG_LOC_GET_CLUSTER) { + fmode = O_RDONLY; + } else { + fmode = O_RDWR | O_CREAT; + } + + if (altroot) { + strcpy(wellknown, altroot); + strcat(wellknown, CFG_CLUSTER_LOCATION); + } else + strcpy(wellknown, CFG_CLUSTER_LOCATION); + + fd = open(wellknown, fmode, 0644); + if (fd < 0) { + cfg_perror_str = dgettext("cfg", strerror(errno)); + cfg_severity = CFG_ENONFATAL; + return (NULL); + } + + if (mode == CFG_LOC_SET_CLUSTER) { + if (location == NULL || (strlen(location) > NSC_MAXPATH)) { + cfg_perror_str = dgettext("cfg", + "cfg_location: filename too big or missing"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + + /* + * 5082142 + * If we're in a cluster, make sure that the config location + * is a raw device. Using non-raw did devices in a cluster + * can result in data corruption, since inconsistent data + * may reside in the block cache on one node, but has not + * been flushed to disk. + */ + if (cfg_iscluster() > 0) { + struct stat dscfg_stat; + if (stat(location, &dscfg_stat) != 0) { + cfg_perror_str = dgettext("cfg", + "Unable to access dscfg location"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + if (!S_ISCHR(dscfg_stat.st_mode)) { + cfg_perror_str = dgettext("cfg", + "dscfg location must be a raw device"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + } + + if (ftruncate(fd, 0) < 0) + return (NULL); + + rc = write(fd, location, strlen(location)); + if (rc < 0) { + cfg_perror_str = dgettext("cfg", + "cfg_location: write to well known failed"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + bzero(config_file, sizeof (config_file)); + } + if (lseek(fd, 0, SEEK_SET) < 0) + return (NULL); + + bzero(config_file, sizeof (config_file)); + rc = read(fd, config_file, sizeof (config_file)); + if (rc < 0) { + cfg_perror_str = dgettext("cfg", + "cfg_location: read from well known failed"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + close(fd); + if (altroot) { + strcpy(loc, altroot); + strcat(loc, config_file); + bzero(config_file, sizeof (config_file)); + strcpy(config_file, loc); + } + + /* + * scan string out of config_file, to strip whitespace + */ + sscanf(config_file, "%s", loc); + strcpy(config_file, loc); + + return (config_file); +} + +/* + * cfg_update_parser_config + * If tag and key exist return -1 + * + * XXX Currently does not append new field to existing parser rule + */ + +int +cfg_update_parser_config(CFGFILE *cfg, const char *key, int section) +{ + cfp_t *cfp; + int size; + char buf[CFG_MAX_BUF]; + struct parser *tbl; + char tmpkey[CFG_MAX_KEY]; + char *ky, *fld; + errno = 0; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + cfp = FP_SUN_CLUSTER(cfg); + if (!cfg_wrlock(cfg)) + return (-1); + + bzero(buf, CFG_MAX_BUF); + bzero(tmpkey, sizeof (tmpkey)); + strcpy(tmpkey, key); + if (section == CFG_PARSE_CONF) { + strcat(buf, "C:"); + tbl = chead; + } else { + errno = EINVAL; + return (-1); + } + ky = strtok(tmpkey, "."); + fld = strtok(NULL, "."); + while (fld) { + size = cfg_get_item(tbl, ky, fld); + + /* + * Assure we are loading a clean table, with do duplicates + * based on our File Descriptor + */ + if (chead_loaded && (chead_loaded != cfp->cf_fd)) { + if (size <= 0) + return (-1); + } else { + if (size > 0) + return (-1); + } + fld = strtok(NULL, "."); + } + size = strlen(key) + 2; + strncat(buf, key, size); +#ifdef DEBUG_LIB + (void) fprintf(stderr, "update parser config %s size %d\n", buf, size); +#endif + if ((size + cfp->cf_head->h_parseoff) > CFG_DEFAULT_PARSE_SIZE) { + cfg_perror_str = dgettext("cfg", + "cfg_update_parser_config: header overrun"); + cfg_severity = CFG_EFATAL; +#ifdef DEBUG_LIB + (void) fprintf(stderr, "update parser config: " + "overrun siz %d poff %d parsesize %d\n", + size, cfp->cf_head->h_parseoff, cfp->cf_head->h_parsesize); +#endif + errno = E2BIG; + return (-1); + } + bcopy(buf, (cfp->cf_mapped + cfp->cf_head->h_parseoff), size); + cfp->cf_head->h_parseoff += size; + cfp->cf_head->h_state |= CFG_HDR_INVALID; + if (cfp->cf_mapped[cfp->cf_head->h_parseoff - 1] != '\n') { + cfp->cf_mapped[cfp->cf_head->h_parseoff] = '\n'; + cfp->cf_head->h_parseoff++; + } + cfp->cf_head->h_parsesize = cfp->cf_head->h_parseoff; + cfg_read_parser_config(cfp); + return (TRUE); +} +/* + * cfg_read_parser_config + * reads parser config from file + * converts it to internal tree for parsing + * chead for configuration parser entries + * + */ +static +void +cfg_read_parser_config(cfp_t *cfp) +{ + struct lookup *p, *q; + struct parser *thead; + int off, foff; + char *part; + char *key; + char *fld; + int fldnum; + char c; + char buf[CFG_MAX_BUF]; + int i = 0; + int n = 0; + + off = foff = 0; + /*CONSTCOND*/ + while (TRUE) { + off = 0; + bzero(buf, CFG_MAX_BUF); + /* LINTED it assigns value to c */ + while (c = cfp->cf_mapped[foff++]) { + if (c == '\n') + break; + buf[off++] = c; + } + part = strtok(buf, ":"); + if (!part) + break; + if (*part == 'C') { + thead = chead; + n = i; + } + key = strtok(NULL, "."); + if (!key) + break; + strcpy(thead[n].tag.l_word, key); + thead[n].tag.l_value = 0; + thead[n].fld = NULL; + fldnum = 1; + while ((fld = strtok(NULL, ".")) != NULL) { + p = thead[n].fld; + if (p == NULL) { + q = thead[n].fld = calloc(1, + sizeof (struct lookup)); + } else { + for (q = thead[n].fld; q; q = q->l_next) + p = q; + q = calloc(1, sizeof (struct lookup)); + p->l_next = q; + } + strcpy(q->l_word, fld); + q->l_value = fldnum; + q->l_next = NULL; +#ifdef DEBUG_EXTRA + (void) fprintf(stderr, + "read parser: q: word %s value %d\n", + q->l_word, q->l_value); +#endif + fldnum++; + } + if (*part == 'C') + i++; + } + + /* All done, indicate parser table is loaded */ + if (i && (chead_loaded == 0)) + chead_loaded = cfp->cf_fd; + + /* + * before I go and alloc, why am I here? + * do I need a bunch of cfglists, or do I just + * need to accommodate a just added parser entry + * if the latter, we already have a base, just set + * i to the index of the cfg which members need allocing + */ + if ((cfp->cf_head->h_cfgs == NULL) || + (cfp->cf_head->h_cfgs[n-1].l_entry == NULL)) { + cfp->cf_head->h_cfgs = (cfglist_t *)calloc(MAX_CFG, + sizeof (cfglist_t)); + i = 0; + } + else + i = n; + + if (cfp->cf_head->h_cfgs) { + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "alloced %d cfg lists \n", n + 1); +#endif + for (cfp->cf_head->h_ncfgs = n + 1; + i < min(cfp->cf_head->h_ncfgs, MAX_CFG); i++) { + cfp->cf_head->h_cfgs[i].l_name = '\0'; + cfp->cf_head->h_cfgs[i].l_name = + strdup(chead[i].tag.l_word); + cfp->cf_head->h_cfgs[i].l_index = i; + cfp->cf_head->h_cfgs[i].l_entry = + calloc(DEFAULT_ENTRY_SIZE, sizeof (char)); + cfp->cf_head->h_cfgs[i].l_nentry = 0; + cfp->cf_head->h_cfgs[i].l_esiz = + calloc(DEFAULT_NENTRIES, sizeof (int)); + cfp->cf_head->h_cfgs[i].l_size = 0; + cfp->cf_head->h_cfgs[i].l_free = DEFAULT_ENTRY_SIZE; + if ((cfp->cf_head->h_cfgs[i].l_entry == NULL) || + (cfp->cf_head->h_cfgs[i].l_esiz == NULL)) { + cfg_perror_str = dgettext("cfg", "unable to" + " allocate cfglist members"); + cfg_severity = CFG_EFATAL; + } + } + } else { + cfg_perror_str = dgettext("cfg", "unable to alloc cfglist"); + cfg_severity = CFG_EFATAL; + } +} + +/* + * cfg_map_cfglists() + * go through list of list sizes in header + * and create separate lists + */ +int +cfg_map_cfglists(cfp_t *cfp) +{ + int i; + int offset = 0; + int *ip; + int list_size = 0; + int slot_inc; + char *p; + cfgheader_t *ch; + + ch = cfp->cf_head; + p = ch->h_cparse; + + /* get the first list size */ + ip = ch->h_sizes; + + for (i = 0; i < min(ch->h_ncfgs, MAX_CFG); i++) { + if (ch->h_cfgsizes[i] > 0) { + if (ch->h_cfgsizes[i] > DEFAULT_ENTRY_SIZE) { + + ch->h_cfgs[i].l_entry = (char *) + realloc(ch->h_cfgs[i].l_entry, + ch->h_cfgsizes[i] * sizeof (char)); + /* set free to 0, we'll get more when we add */ + ch->h_cfgs[i].l_free = 0; + + } else + ch->h_cfgs[i].l_free -= ch->h_cfgsizes[i]; + + /* get lists and marry up to each cfgs structure */ + + + list_size = *ip; + ip++; + + if (list_size > DEFAULT_NENTRIES) { + /* + * we're gonna need more slots + * we want to alloc on DEFAULT_NENTRIES + * boundry. ie. always a multiple of it + * later on, when we add to the list + * we can see if we need to add by mod'ding + * l_nentry and DEFAULT_NENTRIES and check for 0 + */ + slot_inc = DEFAULT_NENTRIES - + (list_size % DEFAULT_NENTRIES); + if (slot_inc == DEFAULT_NENTRIES) + slot_inc = 0; /* addcfline reallocs */ + + ch->h_cfgs[i].l_esiz = (int *)realloc( + ch->h_cfgs[i].l_esiz, + (list_size + slot_inc) * sizeof (int)); + } + memcpy(ch->h_cfgs[i].l_esiz, ip, + list_size * sizeof (int)); + + ch->h_cfgs[i].l_nentry = list_size; + + ip += list_size; + + } else + + continue; + + if (ch->h_cfgs[i].l_entry != NULL) { + p = ch->h_cparse + offset; +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "mapping list %d size %d offset %d, addr 0x%x\n", + i, ch->h_cfgsizes[i], offset, p); +#endif + memcpy(ch->h_cfgs[i].l_entry, + p, ch->h_cfgsizes[i]); + ch->h_cfgs[i].l_size = ch->h_cfgsizes[i]; + offset += ch->h_cfgsizes[i]; + } else { +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "NULL l_entry\n"); +#endif + return (-1); + } + } + + + return (1); + +} + +void +cfg_replace_lists(cfp_t *cfp) +{ + int i; + int offset = 0; + int size_offset = 0; + + int section = 0; + cfgheader_t *cf; + cfglist_t *cfl; + + cf = cfp->cf_head; + + if ((cfl = cfp->cf_head->h_cfgs) == NULL) + return; + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_replace_lists\n"); +#endif + + if (cf->h_cparse == cf->h_ccopy1) + section = 1; + + /* + * check to see if we are using copy1 or 2, + * grow or shrink the size, fix h_cparse reference + * in case realloc gave us a funky new address. + * put stuff in it. + */ + cf->h_ccopy1 = (char *) + realloc(cf->h_ccopy1, cf->h_csize * sizeof (char)); + cf->h_ccopy2 = (char *) + realloc(cf->h_ccopy2, cf->h_csize * sizeof (char)); + if (section == 1) { + /* we used copy1 */ + cf->h_cparse = cf->h_ccopy1; + } else + cf->h_cparse = cf->h_ccopy2; + + /* + * just because, we'll zero out h_csize and recalc + * after all, this is the number the next guy gets + */ + cf->h_csize = cf->h_sizes[0] = 0; + for (i = 0; i < MAX_CFG; i++) { + /* only as many lists as chead has */ + if (chead[i].tag.l_word[0] == '\0') { + break; + } + if (cfl[i].l_entry && cfl[i].l_entry[0] != '\0') { +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "copying list %d at %x size %d\n", + i, cf->h_cparse + offset, + cfl[i].l_size); +#endif + memcpy((cf->h_cparse + offset), + cfl[i].l_entry, cfl[i].l_size); + offset += cfl[i].l_size; +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "cfl[%d].l_nentry %d cfl[%d].l_esiz[%d] %d" + " size offset %d\n", + i, cfl[i].l_nentry, i, cfl[i].l_nentry - 1, + cfl[i].l_esiz[cfl[i].l_nentry - 1], size_offset); +#endif + /* + * first write the number of entries + * then copy over the array ie. + * a list with 5 elements would be copied + * as a 6 element array slot 0 being the + * number of elements + */ + cf->h_sizes[size_offset++] = cfl[i].l_nentry; + memcpy((cf->h_sizes + size_offset), cfl[i].l_esiz, + cfl[i].l_nentry * sizeof (int)); + size_offset += cfl[i].l_nentry; + cf->h_sizes[size_offset] = 0; + } + cf->h_csize += cfl[i].l_size; + } +} + +void +cfg_free_cfglist(cfp_t *cfp) +{ + int i; + + if (!cfp->cf_head || !cfp->cf_head->h_cfgs) + return; + + for (i = 0; cfp->cf_head && i < MAX_CFG; i++) { + if (cfp->cf_head->h_cfgs[i].l_entry) { + free(cfp->cf_head->h_cfgs[i].l_entry); + cfp->cf_head->h_cfgs[i].l_entry = NULL; + } + + if (cfp->cf_head->h_cfgs[i].l_name) { + free(cfp->cf_head->h_cfgs[i].l_name); + cfp->cf_head->h_cfgs[i].l_entry = NULL; + } + + if (cfp->cf_head->h_cfgs[i].l_esiz) { + free(cfp->cf_head->h_cfgs[i].l_esiz); + cfp->cf_head->h_cfgs[i].l_esiz = NULL; + } + } + + if (cfp->cf_head) { + free(cfp->cf_head->h_cfgs); + cfp->cf_head->h_cfgs = NULL; + } +} + +void +cfg_free_parser_tree() +{ + struct lookup *p = NULL; + struct lookup *q = NULL; + int i; + + for (i = 0; i < MAX_CFG; i++) { + if (chead) + p = chead[i].fld; + while (p) { + q = p->l_next; + if (p) { + free(p); + p = NULL; + } + p = q; + } + } + bzero(chead, MAX_CFG * sizeof (struct parser)); +} + +void +cfg_close(CFGFILE *cfg) +{ + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return; + } + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + + (*cfp->cf_pp->close)(cfp); +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "freeing cfglists\n"); +#endif + cfg_free_cfglist(cfp); + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "freeing cfp->cf_mapped\n"); +#endif + free(cfp->cf_mapped); + cfp->cf_mapped = NULL; + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "freeing copy1, copy2, h_sizes and cf\n"); +#endif + if (cfp->cf_head) { + if (cfp->cf_head->h_ccopy1) { + free(cfp->cf_head->h_ccopy1); + cfp->cf_head->h_ccopy1 = NULL; + } + if (cfp->cf_head->h_ccopy2) { + free(cfp->cf_head->h_ccopy2); + cfp->cf_head->h_ccopy2 = NULL; + } + if (cfp->cf_head->h_sizes1) { + free(cfp->cf_head->h_sizes1); + cfp->cf_head->h_sizes1 = NULL; + } + if (cfp->cf_head->h_sizes2) { + free(cfp->cf_head->h_sizes2); + cfp->cf_head->h_sizes2 = NULL; + } + + } + if (cfp->cf_head) + free(cfp->cf_head); + } + + free(cfg); + cfg = NULL; + cfg_free_parser_tree(); + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_close\n"); +#endif +} + + +char * +cfg_get_resource(CFGFILE *cfg) +{ + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (NULL); + } + + return (cfg->cf_node); +} + +/* + * cfg_resource + * set or clear the cluster node filter for get/put + */ + +void +cfg_resource(CFGFILE *cfg, const char *node) +{ + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return; + } + + if (cfg->cf_node) { +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, + "cfg_resource: changing node from %s to %s\n", + cfg->cf_node, (node?node:"NULL")); +#endif + free(cfg->cf_node); + cfg->cf_node = NULL; + } + + /* + * just in case someone passes in a non-NULL + * node, but has no valid value + */ + if ((node) && (node[0] != '\0')) { + cfg->cf_node = strdup(node); + } +} + +/* + * cfg_open + * Open the current configuration file + */ +CFGFILE * +cfg_open(char *name) +{ + CFGFILE *cfg; + cfp_t *cfp; + int32_t magic; + long needed; + int rc; + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_open\n"); +#endif + + cfg_severity = 0; + if ((cfg = (CFGFILE *)calloc(1, sizeof (*cfg))) == NULL) { + cfg_perror_str = dgettext("cfg", + "cfg_open: malloc failed"); + cfg_severity = CFG_EFATAL; + return (NULL); + } + + cfp = &cfg->cf[0]; + if ((name) && strlen(name)) { +#ifdef DEBUG + (void) fprintf(stderr, "cfg_open: Using non-standard name\n"); +#endif + cfp->cf_name = name; + cfp->cf_pp = (strstr(cfp->cf_name, "/rdsk/") == NULL) + ? cfg_block_io_provider() + : cfg_raw_io_provider(); + } else { + cfp->cf_name = cfg_location(NULL, CFG_LOC_GET_LOCAL, NULL); + cfp->cf_pp = cfg_block_io_provider(); + + /* Handle cfg_open(""), which is an open from boot tools */ + if (name) + cl_initialized = 1; + if (cfg_iscluster() > 0) { + cfp = &cfg->cf[1]; + cfp->cf_name = + cfg_location(NULL, CFG_LOC_GET_CLUSTER, NULL); + if (cfp->cf_name) { + cfp->cf_pp = cfg_raw_io_provider(); + } + } + } + + /* + * Open one or two configuration files + */ + for (cfp = &cfg->cf[0]; cfp->cf_name && (cfp <= &cfg->cf[1]); cfp++) { + if ((*cfp->cf_pp->open)(cfp, cfp->cf_name) == NULL) { + cfg_perror_str = dgettext("cfg", + "cfg_open: unable to open configuration location"); + cfg_severity = CFG_EFATAL; + break; + } + + /* block device smaller than repository? */ + rc = (*cfp->cf_pp->read)(cfp, &magic, sizeof (magic)); + if (rc < sizeof (magic)) { + cfg_perror_str = dgettext("cfg", + "cfg_open: unable to read configuration header"); + cfg_severity = CFG_EFATAL; + break; + } + + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) { + cfg_perror_str = dgettext("cfg", + "cfg_open: unable to seek configuration header"); + cfg_severity = CFG_EFATAL; + break; + } + + /* + * we can't enforce size rules on an old database + * so check the magic number before we test for size + */ + if (magic == CFG_NEW_MAGIC) { + needed = FBA_NUM(FBA_SIZE(1) - 1 + + (sizeof (struct cfgheader) + CFG_CONFIG_SIZE)); + } else { + needed = 0; + } + + if (cfp->cf_size < needed) { + cfg_perror_str = dgettext("cfg", + "cfg_open: configuration file too small"); + cfg_severity = CFG_EFATAL; + errno = ENOMEM; + break; + } + + cfp->cf_mapped = (char *)malloc(CFG_DEFAULT_PARSE_SIZE); + if (cfp->cf_mapped == NULL) { + cfg_perror_str = dgettext("cfg", + "cfg_open: malloc failed"); + cfg_severity = CFG_EFATAL; + break; + } + + bzero(cfp->cf_mapped, CFG_DEFAULT_PARSE_SIZE); + cfp->cf_lock = -1; + } + + /* Processing errors, take care of one or more cfp pointers */ + if (cfg_severity && (cfp <= &cfg->cf[1])) { + cfp = &cfg->cf[0]; + if (cfp->cf_fd) + (*cfp->cf_pp->close)(cfp); + cfp = &cfg->cf[1]; + if (cfp->cf_fd) + (*cfp->cf_pp->close)(cfp); + free(cfg); + return (NULL); + } + + cfg_lockd = cfg_lockd_init(); + + +#ifdef DEBUG_CFGLIST + (void) fprintf(stderr, "cfg_open ok\n"); +#endif + return (cfg); +} + +void +cfg_invalidate_hsizes(int fd, const char *loc) { + int offset; + int rc = -1; + int hdrsz; + + char buf[2 * CFG_DEFAULT_PSIZE]; + + hdrsz = sizeof (cfgheader_t) + 512 - + (sizeof (cfgheader_t) % 512); + + offset = hdrsz + CFG_DEFAULT_PARSE_SIZE + + (CFG_DEFAULT_SSIZE * 2); + + if (cfg_shldskip_vtoc(fd, loc) > 0) + offset += CFG_VTOC_SKIP; + + bzero(buf, sizeof (buf)); + + if (lseek(fd, offset, SEEK_SET) > 0) + rc = write(fd, buf, sizeof (buf)); + if (rc < 0) + (void) fprintf(stderr, "cfg: invalidate hsizes failed\n"); + +} + +char * +cfg_error(int *severity) +{ + if (severity != NULL) + *severity = cfg_severity; + return (cfg_perror_str ? cfg_perror_str : CFG_EGENERIC); +} +/* + * cfg_cfg_isempty + */ +int +cfg_cfg_isempty(CFGFILE *cfg) +{ + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + cfp = FP_SUN_CLUSTER(cfg); + if (cfp->cf_head->h_csize == 0) + return (TRUE); + else + return (FALSE); +} + +/* + * cfg_get_num_entries + * return the number of entries in a given section of database + * sndr, ii, ndr_ii... + */ +int +cfg_get_num_entries(CFGFILE *cfg, char *section) +{ + int count = 0; + int table_offset; + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if ((table_offset = cfg_get_parser_offset(section)) < 0) { + errno = ESRCH; + return (-1); + } + + /* Determine number of files open */ + for (cfp = &cfg->cf[0]; cfp->cf_fd && (cfp <= &cfg->cf[1]); cfp++) + count += cfp->cf_head->h_cfgs[table_offset].l_nentry; + + return (count); +} + +/* + * cfg_get_section + * all etries in a config file section is placed in + * buf, allocation is done inside + * freeing buf is responisbility of the caller + * number of entries in section is returned + * -1 on failure, errno is set + */ +int +cfg_get_section(CFGFILE *cfg, char ***list, const char *section) +{ + int table_offset; + int i, count; + cfglist_t *cfl; + char *p = NULL; + char **buf; + cfp_t *cfp; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + if ((table_offset = cfg_get_parser_offset(section)) < 0) { + errno = ESRCH; + return (-1); + } + + /* Determine number of files open */ + count = 0; + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + + cfl = &cfp->cf_head->h_cfgs[table_offset]; + if (cfl->l_nentry == 0) /* empty list */ + continue; + + if (count == 0) + buf = (char **)malloc(cfl->l_nentry * sizeof (char *)); + else + buf = (char **)realloc(buf, (cfl->l_nentry + count) * + sizeof (char *)); + if (buf == NULL) { + errno = ENOMEM; + return (-1); + } else { + bzero(&buf[count], cfl->l_nentry * sizeof (char *)); + } + + p = cfl->l_entry; + for (i = 0; i < cfl->l_nentry; i++) { + if ((buf[i + count] = strdup(p)) == NULL) { + errno = ENOMEM; + return (-1); + } + p += cfl->l_esiz[i]; + } + count += cfl->l_nentry; + } + + *list = buf; + return (count); +} + +/* + * cluster upgrade helper functions. These support old database operations + * while upgrading nodes on a cluster. + */ + +/* + * returns the list of configured tags + * return -1 on error, else the number + * of tags returned in taglist + * caller frees + */ +int +cfg_get_tags(CFGFILE *cfg, char ***taglist) +{ + char **list; + int i = 0; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (!cfg_rdlock(cfg)) { + return (-1); + } + list = calloc(1, MAX_CFG * sizeof (char *)); + if (list == NULL) { + errno = ENOMEM; + return (-1); + } + + while ((i < MAX_CFG) && (chead[i].tag.l_word[0] != '\0')) { + list[i] = strdup(chead[i].tag.l_word); + if (list[i] == NULL) { + for (/* CSTYLE */; i >= 0; i--) { + if (list[i]) + free(list[i]); + } + free(list); + errno = ENOMEM; + return (-1); + } + i++; + } + *taglist = list; + return (i); + +} + +/* + * is this a database? + * check the header for the magic number + * 0 no match 1 match, -1 on error + */ +int +cfg_is_cfg(CFGFILE *cfg) +{ + int32_t magic; + int rc; + cfp_t *cfp = FP_SUN_CLUSTER(cfg); + + rc = (cfp->cf_pp->read)(cfp, &magic, sizeof (magic)); + if (rc < sizeof (magic)) { + cfg_perror_str = dgettext("cfg", "Fail to read configuration"); + cfg_severity = CFG_EFATAL; + return (-1); + } + + if (magic == CFG_NEW_MAGIC) + return (1); + + cfg_perror_str = dgettext("cfg", + "configuration not initialized, bad magic"); + cfg_severity = CFG_EFATAL; + + return (0); +} + +int +compare(const void* a, const void *b) +{ + char *p; + char *pbuf; + char *q; + char *qbuf; + int needed; + int cmp; + int pos; + + pbuf = strdup(a); + qbuf = strdup(b); + + if (!qbuf || !pbuf) + return (0); + + pos = 1; + needed = sortby.offset; + + p = strtok(pbuf, " "); + while (p) { + if (needed == pos) { + break; + } + p = strtok(NULL, " "); + if (!p) + break; + pos++; + } + + pos = 1; + q = strtok(qbuf, " "); + while (q) { + if (needed == pos) { + break; + } + q = strtok(NULL, " "); + if (!q) + break; + pos++; + } + if (!p || !q) { + sortby.comperror++; + free(pbuf); + free(qbuf); + return (0); + } + cmp = strcmp(p, q); + free(pbuf); + free(qbuf); + return (cmp); + + +} +/* + * cfg_get_srtdsec + * returns the section, sorted by supplied field + * caller frees mem + */ +int +cfg_get_srtdsec(CFGFILE *cfg, char ***list, const char *section, + const char *field) +{ + cfglist_t *cfl; + cfp_t *cfp; + char **buf; + char *tmplst; + char *p, *q; + int table_offset; + int count, i; + + if (cfg == NULL) { + cfg_perror_str = dgettext("cfg", CFG_EINVAL); + cfg_severity = CFG_EFATAL; + return (FALSE); + } + + if ((table_offset = cfg_get_parser_offset(section)) < 0) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + errno = ESRCH; + return (-1); + } + + /* + * do essentially what get_section does, + * except stick entries in a static size + * buf to make things easier to qsort + */ + count = 0; + for (cfp = &cfg->cf[0]; cfp <= &cfg->cf[1]; cfp++) { + if (!cfp->cf_fd) continue; + if (cfp->cf_head->h_state & CFG_HDR_INVALID) { + if (!cfg_read(cfp)) { + cfg_perror_str = dgettext("cfg", CFG_RDFAILED); + cfg_severity = CFG_EFATAL; + return (-1); + } + } + + cfl = &cfp->cf_head->h_cfgs[table_offset]; + if (cfl->l_nentry == 0) /* empty list */ + continue; + + if (count == 0) + buf = (char **)malloc(cfl->l_nentry * sizeof (char *)); + else + buf = (char **)realloc(buf, (cfl->l_nentry + count) * + sizeof (char *)); + if (buf == NULL) { + errno = ENOMEM; + cfg_perror_str = dgettext("cfg", "cfg_get_srtdsec: " + "malloc failed"); + cfg_severity = CFG_EFATAL; + return (-1); + } else { + bzero(&buf[count], cfl->l_nentry * sizeof (char *)); + } + + /* + * allocate each line + */ + for (i = count; i < cfl->l_nentry + count; i++) { + buf[i] = calloc(1, CFG_MAX_BUF); + if (buf[i] == NULL) { + free(buf); + errno = ENOMEM; + return (-1); + } + } + + if (count == 0) + tmplst = (char *)malloc(cfl->l_nentry * CFG_MAX_BUF); + else + tmplst = (char *)realloc(tmplst, + (cfl->l_nentry + count) * CFG_MAX_BUF); + if (tmplst == NULL) { + cfg_perror_str = dgettext("cfg", "cfg_get_srtdsec: " + "malloc failed"); + cfg_severity = CFG_EFATAL; + free(buf); + return (-1); + } else { + bzero(&tmplst[count], cfl->l_nentry * CFG_MAX_BUF); + } + + /* + * put the section in tmplst and sort + */ + p = &tmplst[count]; + q = cfl->l_entry; + for (i = 0; i < cfl->l_nentry; i++) { + bcopy(q, p, cfl->l_esiz[i]); + p += CFG_MAX_BUF; + q += cfl->l_esiz[i]; + } + count += cfl->l_nentry; + } + + bzero(sortby.section, CFG_MAX_KEY); + bzero(sortby.field, CFG_MAX_KEY); + + strcpy(sortby.section, section); + strcpy(sortby.field, field); + sortby.comperror = 0; + sortby.offset = cfg_get_item(&chead[0], section, field); + + qsort(tmplst, count, CFG_MAX_BUF, compare); + + if (sortby.comperror) { + sortby.comperror = 0; + cfg_perror_str = dgettext("cfg", "cfg_get_srtdsec: " + "comparison error"); + cfg_severity = CFG_ENONFATAL; + cfg_free_section(&buf, cfl->l_nentry); + free(tmplst); + *list = NULL; + return (-1); + } + + p = tmplst; + for (i = 0; i < count; i++) { + bcopy(p, buf[i], CFG_MAX_BUF); + p += CFG_MAX_BUF; + } + + free(tmplst); + *list = buf; + return (count); +} + +/* + * free an array alloc'd by get_*section + * or some other array of size size + */ + +void +cfg_free_section(char ***section, int size) +{ + int i; + char **secpp = *section; + + for (i = 0; i < size; i++) { + if (secpp[i]) { + free(secpp[i]); + secpp[i] = NULL; + } + } + if (secpp) { + free(secpp); + secpp = NULL; + } + section = NULL; +} + + +int +cfg_shldskip_vtoc(int fd, const char *loc) +{ + struct vtoc vtoc; + struct stat sb; + int slice; + int rfd; + char char_name[PATH_MAX]; + char *p; + + if (fstat(fd, &sb) == -1) { + cfg_perror_str = dgettext("cfg", "unable to stat config"); + cfg_severity = CFG_EFATAL; + return (-1); + } + if (S_ISREG(sb.st_mode)) + return (0); + + if (S_ISCHR(sb.st_mode)) { + if ((slice = read_vtoc(fd, &vtoc)) < 0) + return (-1); + + if (vtoc.v_part[slice].p_start < CFG_VTOC_SIZE) + return (1); + else + return (0); + } + + if (S_ISBLK(sb.st_mode)) { + p = strstr(loc, "/dsk/"); + if (p == NULL) + return (-1); + strcpy(char_name, loc); + char_name[strlen(loc) - strlen(p)] = 0; + strcat(char_name, "/rdsk/"); + strcat(char_name, p + 5); + + if ((rfd = open(char_name, O_RDONLY)) < 0) { + return (-1); + } + if ((slice = read_vtoc(rfd, &vtoc)) < 0) { + close(rfd); + return (-1); + } + close(rfd); + if (vtoc.v_part[slice].p_start < CFG_VTOC_SIZE) + return (1); + else + return (0); + } + + return (-1); +} + +/* + * comapares incore header with one on disk + * returns 0 if equal, 1 if not, -1 error + */ +int +cfg_hdrcmp(cfp_t *cfp) +{ + cfgheader_t *dskhdr, *memhdr; + int rc; + + if ((dskhdr = calloc(1, sizeof (*dskhdr))) == NULL) { + cfg_perror_str = dgettext("cfg", "cfg_hdrcmp: No memory"); + cfg_severity = CFG_ENONFATAL; + } + + if ((*cfp->cf_pp->seek)(cfp, 0, SEEK_SET) < 0) { + cfg_perror_str = dgettext("cfg", "cfg_hdrcmp: seek failed"); + cfg_severity = CFG_ENONFATAL; + free(dskhdr); + return (-1); + } + + rc = (*cfp->cf_pp->read)(cfp, (char *)dskhdr, sizeof (*dskhdr)); + if (rc < 0) { + cfg_perror_str = dgettext("cfg", "cfg_hdrcmp: read failed"); + cfg_severity = CFG_ENONFATAL; + free(dskhdr); + return (-1); + } + + memhdr = cfp->cf_head; + + if ((memhdr->h_seq1 == dskhdr->h_seq1) && + (memhdr->h_seq2 == dskhdr->h_seq2)) + rc = 0; + else + rc = 1; + + + free(dskhdr); + return (rc); +} |