summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdscfg/common/cfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg.c')
-rw-r--r--usr/src/lib/libdscfg/common/cfg.c3581
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);
+}