summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdscfg/common
diff options
context:
space:
mode:
authorJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
committerJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
commitfcf3ce441efd61da9bb2884968af01cb7c1452cc (patch)
tree0e80d59ad41702571586195bf099ccc14222ce02 /usr/src/lib/libdscfg/common
parent247b82a1f1cb5ebd2d163bd9afdb1a3065611962 (diff)
downloadillumos-joyent-fcf3ce441efd61da9bb2884968af01cb7c1452cc.tar.gz
6745433 Merge NWS consolidation into OS/Net consolidation
Diffstat (limited to 'usr/src/lib/libdscfg/common')
-rw-r--r--usr/src/lib/libdscfg/common/cfg.c3581
-rw-r--r--usr/src/lib/libdscfg/common/cfg.h180
-rw-r--r--usr/src/lib/libdscfg/common/cfg_cluster.c598
-rw-r--r--usr/src/lib/libdscfg/common/cfg_cluster.h344
-rw-r--r--usr/src/lib/libdscfg/common/cfg_impl.h247
-rw-r--r--usr/src/lib/libdscfg/common/cfg_local.c653
-rw-r--r--usr/src/lib/libdscfg/common/cfg_local.h42
-rw-r--r--usr/src/lib/libdscfg/common/cfg_lockd.h72
-rw-r--r--usr/src/lib/libdscfg/common/cfg_lockdlck.c135
-rw-r--r--usr/src/lib/libdscfg/common/cfg_lockdmsg.c324
-rw-r--r--usr/src/lib/libdscfg/common/cfg_vols.c1286
-rw-r--r--usr/src/lib/libdscfg/common/llib-ldscfg72
-rw-r--r--usr/src/lib/libdscfg/common/mapfile-vers93
13 files changed, 7627 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);
+}
diff --git a/usr/src/lib/libdscfg/common/cfg.h b/usr/src/lib/libdscfg/common/cfg.h
new file mode 100644
index 0000000000..84e64b8ebe
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg.h
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+#ifndef _CFG_H
+#define _CFG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/nsctl/nsctl.h>
+
+#define CFG_MAX_BUF 1024 /* maximum buffer size for cfg_get_?string() */
+#define CFG_MAX_KEY 256 /* maximum key size for cfg_get_?string() */
+
+#ifndef _CFG_IMPL_H
+/*
+ * These are really declared in cfg_impl.h, declare as dummy's here to
+ * allow clients to compile without including cfg_impl.h.
+ */
+typedef struct cfgfile CFGFILE;
+typedef struct cfp cfp_t;
+
+#endif /* _CFG_IMPL_H */
+
+int cfg_get_cstring(CFGFILE *cfg, const char *key, void *value, int value_len);
+int cfg_put_cstring(CFGFILE *cfg, const char *key, void *value, int value_len);
+int cfg_find_cstring(CFGFILE *cfg, const char *target, const char *section,
+ int numflds, ...);
+int cfg_get_options(CFGFILE *cfg, int section, const char *basekey,
+ char *tag, int tag_len, char *val, int val_len);
+int cfg_put_options(CFGFILE *cfg, int section, const char *basekey,
+ char *tag, char *val);
+int cfg_get_single_option(CFGFILE *, int, const char *, char *, char *, int);
+int cfg_del_option(CFGFILE *, int, const char *, char *);
+
+int cfg_get_num_entries(CFGFILE *cfg, char *section);
+
+int cfg_get_tags(CFGFILE *cfg, char ***taglist);
+
+int cfg_cfg_isempty(CFGFILE *cfg);
+int cfg_get_section(CFGFILE *cfg, char ***buf, const char *section);
+CFGFILE *cfg_open(char *filename);
+void cfg_rewind(CFGFILE *cfg, int section);
+int cfg_is_cfg(CFGFILE *cfg);
+int cfg_shldskip_vtoc(int fd, const char *loc);
+int cfg_get_srtdsec(CFGFILE *cfg, char ***list,
+ const char *sec, const char *field);
+void cfg_free_section(char ***, int);
+
+
+/*
+ * Handle cluster configuration
+ */
+#define FP_SUN_CLUSTER(x) \
+ (((x->cf_node) && (x->cf[1].cf_fd)) ? &x->cf[1] : &x->cf[0])
+
+/*
+ * rewind sections
+ */
+#define CFG_SEC_CONF 0 /* configuration section */
+#define CFG_SEC_PERS 1 /* persistent section */
+#define CFG_SEC_ALL 2 /* rewind both sections */
+
+int cfg_update_parser_config(CFGFILE *, const char *key, int section);
+/*
+ * parser sections
+ */
+#define CFG_PARSE_CONF 0 /* config section key */
+#define CFG_PARSE_PERS 1 /* persistent section key */
+
+char *cfg_error(int *severity);
+/*
+ * error codes
+ */
+#define CFG_ENONFATAL 0 /* non fatal error */
+#define CFG_EFATAL 1 /* fatal error exit */
+
+/*
+ * some error strings
+ */
+#define CFG_NOTLOCKED "Configuration not locked"
+#define CFG_RDFAILED "Unable to read configuration"
+#define CFG_EINVAL "Invalid Argument"
+#define CFG_EGENERIC "Generic cfg failure"
+
+
+char *cfg_location(char *location, int mode, char *altroot);
+
+/*
+ * location modes
+ */
+#define CFG_LOC_SET_LOCAL 0
+#define CFG_LOC_GET_LOCAL 1
+#define CFG_LOC_SET_CLUSTER 2
+#define CFG_LOC_GET_CLUSTER 3
+
+/*
+ * location strings
+ */
+#define CFG_LOCAL_LOCATION "/etc/dscfg_local"
+#define CFG_CLUSTER_LOCATION "/etc/dscfg_cluster"
+
+void cfg_close(CFGFILE *);
+
+/*
+ * lock mode
+ */
+typedef enum {
+ CFG_RDLOCK,
+ CFG_WRLOCK,
+ CFG_UPGRADE
+} CFGLOCK;
+
+int cfg_lock(CFGFILE *, CFGLOCK); /* lock the configuration */
+void cfp_unlock(cfp_t *); /* unlock the configuration */
+void cfg_unlock(CFGFILE *);
+int cfg_get_lock(CFGFILE *, CFGLOCK *, pid_t *); /* get config lock */
+
+int cfg_commit(CFGFILE *);
+void cfg_resource(CFGFILE *, const char *); /* Set/clear cluster node */
+char *cfg_get_resource(CFGFILE *); /* get current cluster node */
+char *cfg_dgname(const char *, char *, size_t); /* parse dg from pathname */
+char *cfg_l_dgname(const char *, char *, size_t); /* parse dg from pathname */
+int cfg_dgname_islocal(char *, char **); /* find locality of dg */
+int cfg_iscluster(void); /* running in a cluster? */
+int cfg_issuncluster(void); /* running in a Sun Cluster? */
+void cfg_invalidate_sizes(int);
+
+/*
+ * add/rem result codes
+ */
+#define CFG_USER_ERR 1
+#define CFG_USER_OK 2
+#define CFG_USER_FIRST 3
+#define CFG_USER_LAST 4
+#define CFG_USER_GONE 5
+#define CFG_USER_REPEAT 6
+
+int cfg_add_user(CFGFILE *, char *, char *, char *); /* add volume user */
+int cfg_rem_user(CFGFILE *, char *, char *, char *); /* remove vol user */
+int cfg_vol_enable(CFGFILE *, char *, char *, char *); /* enable volume */
+int cfg_vol_disable(CFGFILE *, char *, char *, char *); /* disable volume */
+
+int cfg_load_dsvols(CFGFILE *); /* load dsvol: section */
+void cfg_unload_dsvols(); /* unload dsvol: section */
+int cfg_load_svols(CFGFILE *); /* load sv: section */
+void cfg_unload_svols(); /* unload sv: section */
+int cfg_load_shadows(CFGFILE *); /* load shadows & bitmaps from ii: */
+void cfg_unload_shadows(); /* unload ii: */
+
+int cfg_get_canonical_name(CFGFILE *, const char *, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_H */
diff --git a/usr/src/lib/libdscfg/common/cfg_cluster.c b/usr/src/lib/libdscfg/common/cfg_cluster.c
new file mode 100644
index 0000000000..2524986e56
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_cluster.c
@@ -0,0 +1,598 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file contains the glue code that allows the NWS software to
+ * determine whether a cluster disk service is local to this node or
+ * not.
+ *
+ * See PSARC/1999/462 for more information on the interfaces from
+ * suncluster that are used here.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include <sys/ncall/ncall.h>
+#include <sys/nsctl/nsc_hash.h>
+
+#include "cfg_cluster.h"
+#include "cfg_impl.h"
+#include "cfg.h"
+
+/*
+ * Static variables
+ */
+
+static scconf_nodeid_t cl_nodeid = (uint_t)0xffff;
+static char *cl_nodename = NULL;
+
+static void *libscstat;
+static void *libscconf;
+
+static hash_node_t **schash;
+static int init_sc_entry();
+
+typedef struct hash_data_s {
+ scstat_node_name_t scstat_node_name;
+} hash_data_t;
+
+/*
+ * Global variables
+ */
+int cl_initialized = 0;
+
+
+/*
+ * Tell the linker to keep quiet.
+ */
+
+#pragma weak scconf_get_nodename
+#pragma weak scconf_strerr
+#pragma weak scconf_get_ds_by_devt
+
+#pragma weak scstat_get_ds_status
+#pragma weak scstat_free_ds_status
+#pragma weak scstat_strerr
+
+
+/*
+ * Initialise the library if we have not done so before.
+ *
+ * - IMPORTANT -
+ *
+ * This must -never- be called from any command that can be started
+ * from /usr/cluster/lib/sc/run_reserve (and hence
+ * /usr/cluster/sbin/reconfig) or the system will deadlock
+ * during switchover. This includes:
+ *
+ * - svadm (no options, "print") -- called during sv switchover
+ * - all boot commands
+ *
+ * - grab this node's cluster nodeid
+ * - attempt to dlopen() the suncluster shared libraries we need
+ * - grab this node's cluster nodename
+ *
+ * Returns:
+ * 0 - success
+ * -1 - error, errno is set
+ */
+
+int
+cfg_cluster_init(void)
+{
+ const char *scconf = "/usr/cluster/lib/libscconf.so.1";
+ const char *scstat = "/usr/cluster/lib/libscstat.so.1";
+#ifdef DEBUG
+ char errbuf[SCCONF_MAXSTRINGLEN];
+#endif
+ scconf_nodeid_t id;
+ scconf_errno_t err;
+ char *name;
+ FILE *pipe;
+ int rc;
+
+ /*
+ * First check to see if we really are a cluster as clinfo -n can lie
+ */
+ if (cl_nodeid == 0xffff) {
+ rc = system("/usr/sbin/clinfo");
+ if (rc != -1 && WEXITSTATUS(rc) == 1) {
+ /* not a cluster */
+ cl_initialized = 1;
+ cl_nodeid = 0;
+ return (0);
+ }
+
+ pipe = popen("/usr/sbin/clinfo -n 2>/dev/null || echo 0", "r");
+ if (pipe == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "unable to get nodeid: %s\n",
+ strerror(errno));
+#endif
+ return (-1);
+ }
+
+ if ((rc = fscanf(pipe, "%d", &id)) != 1) {
+#ifdef DEBUG
+ fprintf(stderr, "unable to get nodeid: %s\n",
+ strerror(errno));
+#endif
+ return (-1);
+ }
+
+ pclose(pipe);
+
+ cl_nodeid = id;
+ }
+
+ /* Already loaded the Sun Cluster device tree */
+ if (cl_initialized)
+ return (0);
+
+ /*
+ * Try and dlopen the various libraries that we need
+ */
+
+ libscconf = dlopen(scconf, RTLD_LAZY | RTLD_GLOBAL);
+ if (libscconf == NULL)
+ goto error;
+
+ libscstat = dlopen(scstat, RTLD_LAZY | RTLD_GLOBAL);
+ if (libscstat == NULL)
+ goto error;
+
+ err = scconf_get_nodename(id, &name);
+ if (err == SCCONF_EPERM) {
+ cl_nodename = NULL;
+ } else if (err != SCCONF_NOERR) {
+#ifdef DEBUG
+ scconf_strerr(errbuf, err);
+ fprintf(stderr, "scconf_get_nodename: %d: %s\n", err, errbuf);
+#endif
+ goto error;
+ } else
+ cl_nodename = name;
+
+ /* Load the Sun Cluster device tree */
+ init_sc_entry();
+ cl_initialized = 1;
+ return (0);
+
+error: /* error cleanup */
+ if (libscconf)
+ dlclose(libscconf);
+
+ if (libscstat)
+ dlclose(libscstat);
+
+ libscconf = NULL;
+ libscstat = NULL;
+
+ errno = ENOSYS;
+ return (-1);
+}
+
+
+/*
+ * cfg_issuncluster()
+ *
+ * Description:
+ * Return the SunCluster nodeid of this node.
+ *
+ * Returns:
+ * >0 - running in a SunCluster (value is nodeid of this node)
+ * 0 - not running in a cluster
+ * -1 - failure; errno is set
+ */
+
+int
+cfg_issuncluster()
+{
+ if (cfg_cluster_init() >= 0)
+ return ((int)cl_nodeid);
+ else
+ return (-1);
+}
+int
+cfg_iscluster()
+{
+ return (cfg_issuncluster());
+}
+
+/*
+ * cfg_l_dgname_islocal()
+ * Check if disk group is local on a non-SunCluster.
+ *
+ * Returns as cfg_dgname_islocal().
+ */
+#ifndef lint
+static int
+cfg_l_dgname_islocal(char *dgname, char **othernode)
+{
+ const char *metaset = "/usr/sbin/metaset -s %s -o > /dev/null 2>&1";
+ char command[1024];
+ int rc;
+
+ if (snprintf(command, sizeof (command), metaset, dgname) >=
+ sizeof (command)) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ rc = system(command);
+ if (rc < 0) {
+ return (-1);
+ }
+
+ if (WEXITSTATUS(rc) != 0) {
+ if (othernode) {
+ /* metaset doesn't tell us */
+ *othernode = "unknown";
+ }
+
+ return (0);
+ }
+
+ return (1);
+}
+#endif
+
+/*
+ * cfg_dgname_islocal(char *dgname, char **othernode)
+ * -- determine if the named disk service is mastered on this node
+ *
+ * If the disk service is mastered on another node, that nodename
+ * will be returned in othernode (if not NULL). It is up to the
+ * calling program to call free() on this value at a later time to
+ * free the memory allocated.
+ *
+ * Returns:
+ * 1 - disk service is mastered on this node
+ * 0 - disk service is not mastered on this node (*othernode set)
+ * -1 - error (errno will be set)
+ */
+
+int
+cfg_dgname_islocal(char *dgname, char **othernode)
+{
+ hash_data_t *data;
+
+ if (dgname == NULL || *dgname == '\0' || othernode == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Handle non-cluster configurations */
+ if (cfg_cluster_init() < 0) {
+ return (-1);
+ } else if (cl_nodeid == 0) {
+ /* it has to be local */
+ return (1);
+ }
+
+ /*
+ * lookup the current diskgroup name
+ */
+ if (data = (hash_data_t *)nsc_lookup(schash, dgname)) {
+ if (strcmp(data->scstat_node_name, cl_nodename)) {
+ if (othernode)
+ *othernode = strdup(data->scstat_node_name);
+ return (0);
+ } else {
+ return (1);
+ }
+ } else {
+ errno = ENODEV;
+ return (-1);
+ }
+}
+
+/*
+ * cfg_l_dgname()
+ * parse the disk group name from the a device pathname on a non-SunCluster.
+ *
+ * Returns as cfg_dgname().
+ */
+
+char *
+cfg_l_dgname(const char *pathname, char *buffer, size_t buflen)
+{
+ const char *dev = "/dev/";
+ const char *vx = "vx/";
+ const char *md = "md/";
+ const char *dsk = "dsk/";
+ const char *start, *cp;
+ int ll, len, chkdsk;
+
+ bzero(buffer, buflen);
+ chkdsk = 0;
+
+ ll = strlen(dev);
+ if (strncmp(pathname, dev, ll) != 0) {
+ /* not a device pathname */
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ start = pathname + ll;
+
+ if (strncmp(start, md, (ll = strlen(md))) == 0) {
+ /*
+ * SVM --
+ * /dev/md/dgname/{r}dsk/partition
+ */
+
+ start += ll;
+
+ if (strncmp(start, dsk, strlen(dsk)) == 0 ||
+ (*start == 'r' &&
+ strncmp((start + 1), dsk, strlen(dsk)) == 0)) {
+ /* no dgname */
+ return (buffer);
+ }
+
+ chkdsk = 1; /* check for trailing {r}dsk */
+ } else if (strncmp(start, vx, (ll = strlen(vx))) == 0) {
+ /*
+ * Veritas --
+ * /dev/vx/{r}dsk/dgname/partition
+ */
+
+ start += ll;
+
+ ll = strlen(dsk);
+
+ if (*start == 'r' && strncmp((start + 1), dsk, ll) == 0)
+ start += ll + 1;
+ else if (strncmp(start, dsk, ll) == 0)
+ start += ll;
+ else {
+ /* no dgname */
+ return (buffer);
+ }
+ } else {
+ /* no dgname */
+ return (buffer);
+ }
+
+ for (cp = start, len = 0; *cp != '\0' && *cp != '/'; cp++)
+ len++; /* count length of dgname */
+
+ if (*cp == '\0') {
+ /* no dgname */
+ return (buffer);
+ }
+
+#ifdef DEBUG
+ if (*cp != '/') {
+ fprintf(stderr,
+ "cfg_dgname: parse error: *cp = '%c', expected '/'\n", *cp);
+ errno = EPROTO;
+ return ((char *)NULL);
+ }
+#endif
+
+ if (chkdsk) {
+ cp++; /* skip the NULL */
+
+ ll = strlen(dsk);
+
+ if ((*cp != 'r' || strncmp((cp + 1), dsk, ll) != 0) &&
+ strncmp(cp, dsk, ll) != 0) {
+ /* no dgname */
+ return (buffer);
+ }
+ }
+
+ if (len >= buflen) {
+ errno = E2BIG;
+ return ((char *)NULL);
+ }
+
+ (void) strncpy(buffer, start, len);
+ return (buffer);
+}
+
+
+/*
+ * cfg_dgname()
+ * determine which cluster resource group the pathname belongs to, if any
+ *
+ * Returns:
+ * NULL - error (errno is set)
+ * ptr to NULL-string - no dgname
+ * pointer to string - dgname
+ */
+
+char *
+cfg_dgname(const char *pathname, char *buffer, size_t buflen)
+{
+ scconf_errno_t conferr;
+ char *dsname = NULL;
+ struct stat stb;
+#ifdef DEBUG
+ char errbuf[SCCONF_MAXSTRINGLEN];
+#endif
+
+ bzero(buffer, buflen);
+
+ if (pathname == NULL || *pathname == '\0') {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ /* Handle non-cluster configurations */
+ if (cfg_cluster_init() < 0) {
+ errno = EINVAL;
+ return ((char *)NULL);
+ } else if (cl_nodeid == 0) {
+ /* must be local - return NULL-string dgname */
+ return (buffer);
+ }
+
+ if (stat(pathname, &stb) < 0) {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ conferr = scconf_get_ds_by_devt(major(stb.st_rdev),
+ minor(stb.st_rdev), &dsname);
+
+ if (conferr == SCCONF_ENOEXIST) {
+ return (buffer);
+ } else if (conferr != SCCONF_NOERR) {
+#ifdef DEBUG
+ scconf_strerr(errbuf, conferr);
+ fprintf(stderr,
+ "scconf_get_ds_by_devt: %d: %s\n", conferr, errbuf);
+#endif
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ strncpy(buffer, dsname, buflen);
+ free(dsname);
+
+ return (buffer);
+}
+
+
+/*
+ * init_sc_entry
+ *
+ * Add an entry into the sclist and the schash for future lookups.
+ *
+ * - IMPORTANT -
+ *
+ * This must -never- be called from any command that can be started
+ * from /usr/cluster/lib/sc/run_reserve (and hence
+ * /usr/cluster/sbin/reconfig) or the system will deadlock
+ * during switchover. This includes:
+ *
+ * - svadm (no options, "print") -- called during sv switchover
+ * - all boot commands
+ *
+ * Return values:
+ * -1 An error occurred.
+ * 0 Entry added
+ * 1 Entry already exists.
+ */
+static int
+init_sc_entry()
+{
+ scstat_ds_node_state_t *dsn;
+ scstat_ds_name_t dsname;
+ scstat_ds_t *dsstatus, *dsp;
+ scstat_errno_t err;
+#ifdef DEBUG
+ char errbuf[SCCONF_MAXSTRINGLEN];
+#endif
+
+ hash_data_t *hdp;
+
+ /*
+ * Allocate a hash table
+ */
+ if ((schash = nsc_create_hash()) == NULL)
+ return (-1);
+
+ /*
+ * the API is broken here - the function is written to expect
+ * the first argument to be (scstat_ds_name_t), but the function
+ * declaration in scstat.h requires (scstat_ds_name_t *).
+ *
+ * We just cast it to get rid of the compiler warnings.
+ * If "dsname" is NULL, information for all device services is returned
+ */
+ dsstatus = NULL;
+ dsname = NULL;
+ /* LINTED pointer alignment */
+ err = scstat_get_ds_status((scstat_ds_name_t *)dsname, &dsstatus);
+ if (err != SCSTAT_ENOERR) {
+#ifdef DEBUG
+ scstat_strerr(err, errbuf);
+ fprintf(stderr, "scstat_get_ds_status(): %d: %s\n",
+ err, errbuf);
+#endif
+ errno = ENOSYS;
+ return (-1);
+ }
+
+ if (dsstatus == NULL) {
+ errno = ENODEV;
+ return (-1);
+ }
+
+ /*
+ * Traverse scstat_ds list, saving away resource in out hash table
+ */
+ for (dsp = dsstatus; dsp; dsp = dsp->scstat_ds_next) {
+
+ /* Skip over NULL scstat_ds_name's */
+ if ((dsp->scstat_ds_name == NULL) ||
+ (dsp->scstat_ds_name[0] == '\0'))
+ continue;
+
+ /* See element exits already, error if so */
+ if (nsc_lookup(schash, dsp->scstat_ds_name)) {
+ fprintf(stderr, "scstat_get_ds_status: duplicate %s",
+ dsp->scstat_ds_name);
+ errno = EEXIST;
+ return (-1);
+ }
+
+ /* Traverse the node status list */
+ for (dsn = dsp->scstat_node_state_list; dsn;
+ dsn = dsn->scstat_node_next) {
+ /*
+ * Only keep trace of primary nodes
+ */
+ if (dsn->scstat_node_state != SCSTAT_PRIMARY)
+ continue;
+
+ /* Create an element to insert */
+ hdp = (hash_data_t *)malloc(sizeof (hash_data_t));
+ hdp->scstat_node_name = strdup(dsn->scstat_node_name);
+ nsc_insert_node(schash, hdp, dsp->scstat_ds_name);
+ }
+ }
+
+ /*
+ * Free up scstat resources
+ */
+ scstat_free_ds_status(dsstatus);
+ return (0);
+}
diff --git a/usr/src/lib/libdscfg/common/cfg_cluster.h b/usr/src/lib/libdscfg/common/cfg_cluster.h
new file mode 100644
index 0000000000..56f07219f7
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_cluster.h
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+#ifndef _CFG_CLUSTER_H
+#define _CFG_CLUSTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This file is a combination of interfaces culled from scstat.h,
+ * scconf.h and the header files that they include.
+ *
+ * It exposes a subset of the interfaces defined in PSARC/2001/261
+ * for use in NWS software.
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+
+/*
+ * From sc_syslog_msg.h
+ */
+
+typedef enum sc_state_code_enum {
+ ONLINE = 1, /* resource is running */
+ OFFLINE, /* resource is stopped due to user action */
+ FAULTED, /* resource is stopped due to a failure */
+ DEGRADED, /* resource is running but has a minor problem */
+ WAIT, /* resource is in transition from a state to another */
+
+ /*
+ * resource is monitored but state of the resource is
+ * not known because either the monitor went down or
+ * the monitor cannot report resource state temporarily.
+ */
+ UNKNOWN,
+
+ NOT_MONITORED /* There is no monitor to check state of the resource */
+} sc_state_code_t;
+
+/*
+ * End sc_syslog_msg.h
+ */
+
+
+/*
+ * From scstat.h
+ */
+
+#define SCSTAT_MAX_STRING_LEN 1024
+
+/* Error codes returned by scstat functions. */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef enum scstat_errno {
+ SCSTAT_ENOERR, /* normal return - no error */
+ SCSTAT_EUSAGE, /* syntax error */
+ SCSTAT_ENOMEM, /* not enough memory */
+ SCSTAT_ENOTCLUSTER, /* not a cluster node */
+ SCSTAT_ENOTCONFIGURED, /* not found in CCR */
+ SCSTAT_ESERVICENAME, /* dcs: invalid service name */
+ SCSTAT_EINVAL, /* scconf: invalid argument */
+ SCSTAT_EPERM, /* not root */
+ SCSTAT_ECLUSTERRECONFIG, /* cluster is reconfiguring */
+ SCSTAT_ERGRECONFIG, /* RG is reconfiguring */
+ SCSTAT_EOBSOLETE, /* Resource/RG has been updated */
+ SCSTAT_EUNEXPECTED /* internal or unexpected error */
+} scstat_errno_t;
+
+/* States a resource can be in */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef enum scstat_state_code {
+ SCSTAT_ONLINE = ONLINE, /* resource is running */
+ SCSTAT_OFFLINE = OFFLINE, /* resource stopped due to user action */
+ SCSTAT_FAULTED = FAULTED, /* resource stopped due to a failure */
+ SCSTAT_DEGRADED = DEGRADED, /* resource running with a minor problem */
+ SCSTAT_WAIT = WAIT, /* resource is in transition */
+ SCSTAT_UNKNOWN = UNKNOWN, /* resource state is unknown */
+ SCSTAT_NOTMONITORED = NOT_MONITORED /* resource is not monitored */
+} scstat_state_code_t;
+
+/* States a replica of a resource can be in */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef enum scstat_node_pref {
+ SCSTAT_PRIMARY, /* replica is a primary */
+ SCSTAT_SECONDARY, /* replica is a secondary */
+ SCSTAT_SPARE, /* replica is a spare */
+ SCSTAT_INACTIVE, /* replica is inactive */
+ SCSTAT_TRANSITION, /* replica is changing state */
+ SCSTAT_INVALID /* replica is in an invalid state */
+} scstat_node_pref_t;
+
+/* component name */
+typedef char *scstat_name_t;
+typedef scstat_name_t scstat_cluster_name_t; /* cluster name */
+typedef scstat_name_t scstat_node_name_t; /* node name */
+typedef scstat_name_t scstat_adapter_name_t; /* adapter name */
+typedef scstat_name_t scstat_path_name_t; /* path name */
+typedef scstat_name_t scstat_ds_name_t; /* device service name */
+typedef scstat_name_t scstat_quorumdev_name_t; /* quorum device name */
+typedef scstat_name_t scstat_rs_name_t; /* resource name */
+typedef scstat_name_t scstat_rg_name_t; /* rg name */
+
+/* status string */
+typedef char *scstat_statstr_t;
+typedef scstat_statstr_t scstat_node_statstr_t; /* node status */
+typedef scstat_statstr_t scstat_path_statstr_t; /* path status */
+typedef scstat_statstr_t scstat_ds_statstr_t; /* DS status */
+typedef scstat_statstr_t scstat_node_quorum_statstr_t; /* node quorum status */
+typedef scstat_statstr_t scstat_quorumdev_statstr_t; /* quorum device stat */
+
+/* ha device node status list */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef struct scstat_ds_node_state_struct {
+ /* node name */
+ scstat_node_name_t scstat_node_name;
+ /* node status */
+ scstat_node_pref_t scstat_node_state;
+ /* next */
+ struct scstat_ds_node_state_struct *scstat_node_next;
+} scstat_ds_node_state_t;
+
+/* Cluster node status */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef struct scstat_node_struct {
+ scstat_node_name_t scstat_node_name; /* node name */
+ scstat_state_code_t scstat_node_status; /* cluster membership */
+ scstat_node_statstr_t scstat_node_statstr; /* node status string */
+ void *pad; /* Padding for */
+ /* PSARC/2001/261. */
+ struct scstat_node_struct *scstat_node_next; /* next */
+} scstat_node_t;
+
+/* Cluster ha device status */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef struct scstat_ds_struct {
+ /* ha device name */
+ scstat_ds_name_t scstat_ds_name;
+ /* ha device status */
+ scstat_state_code_t scstat_ds_status;
+ /* ha device statstr */
+ scstat_ds_statstr_t scstat_ds_statstr;
+ /* node preference list */
+ scstat_ds_node_state_t *scstat_node_state_list;
+ /* next */
+ struct scstat_ds_struct *scstat_ds_next;
+} scstat_ds_t;
+
+/*
+ * scstat_strerr
+ *
+ * Map scstat_errno_t to a string.
+ *
+ * The supplied "errbuffer" should be of at least SCSTAT_MAX_STRING_LEN
+ * in length.
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+void scstat_strerr(scstat_errno_t, char *);
+
+/*
+ * Upon success, a list of objects of scstat_node_t are returned.
+ * The caller is responsible for freeing the space.
+ *
+ * Possible return values:
+ *
+ * SCSTAT_NOERR - success
+ * SCSTAT_ENOMEM - not enough memory
+ * SCSTAT_EPERM - not root
+ * SCSTAT_ENOTCLUSTER - there is no cluster
+ * SCCONF_EINVAL - invalid argument
+ * SCSTAT_EUNEXPECTED - internal or unexpected error
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+scstat_errno_t scstat_get_nodes(scstat_node_t **pplnodes);
+
+/*
+ * Free all memory associated with a scstat_node_t structure.
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+void scstat_free_nodes(scstat_node_t *plnodes);
+
+/*
+ * If the device service name passed in is NULL, then this function returns
+ * the status of all device services, otherwise it returns the status of the
+ * device service specified.
+ * The caller is responsible for freeing the space.
+ *
+ * Possible return values:
+ *
+ * SCSTAT_ENOERR - success
+ * SCSTAT_ENOMEM - not enough memory
+ * SCSTAT_EPERM - not root
+ * SCSTAT_ENOTCLUSTER - there is no cluster
+ * SCCONF_EINVAL - invalid argument
+ * SCSTAT_ESERVICENAME - invalid device group name
+ * SCSTAT_EUNEXPECTED - internal or unexpected error
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+scstat_errno_t scstat_get_ds_status(scstat_ds_name_t *dsname,
+ scstat_ds_t **dsstatus);
+
+/*
+ * Free memory associated with a scstat_ds_t structure.
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+void scstat_free_ds_status(scstat_ds_t *dsstatus);
+
+/*
+ * End scstat.h
+ */
+
+/*
+ * From scconf.h
+ */
+
+/* Maximum message string length */
+#define SCCONF_MAXSTRINGLEN 1024
+
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef enum scconf_errno {
+ SCCONF_NOERR = 0, /* normal return - no error */
+ SCCONF_EPERM = 1, /* permission denied */
+ SCCONF_EEXIST = 2, /* object already exists */
+ SCCONF_ENOEXIST = 3, /* object does not exist */
+ SCCONF_ESTALE = 4, /* object or handle is stale */
+ SCCONF_EUNKNOWN = 5, /* unkown type */
+ SCCONF_ENOCLUSTER = 6, /* cluster does not exist */
+ SCCONF_ENODEID = 7, /* ID used in place of node name */
+ SCCONF_EINVAL = 8, /* invalid argument */
+ SCCONF_EUSAGE = 9, /* command usage error */
+ SCCONF_ETIMEDOUT = 10, /* call timed out */
+ SCCONF_EINUSE = 11, /* already in use */
+ SCCONF_EBUSY = 12, /* busy, try again later */
+ SCCONF_EINSTALLMODE = 13, /* install mode */
+ SCCONF_ENOMEM = 14, /* not enough memory */
+ SCCONF_ESETUP = 15, /* setup attempt failed */
+ SCCONF_EUNEXPECTED = 16, /* internal or unexpected error */
+ SCCONF_EBADVALUE = 17, /* bad ccr table value */
+ SCCONF_EOVERFLOW = 18, /* message buffer overflow */
+ SCCONF_EQUORUM = 19, /* operation would compromise quorum */
+ SCCONF_TM_EBADOPTS = 20, /* bad transport TM "options" */
+ SCCONF_TM_EINVAL = 21, /* other transport TM error */
+ SCCONF_DS_ESUSPENDED = 22, /* Device service in suspended state */
+ SCCONF_DS_ENODEINVAL = 23, /* Node specified is not in cluster */
+ SCCONF_EAUTH = 24, /* authentication error */
+ SCCONF_DS_EINVAL = 25, /* Device service in an invalid state */
+ SCCONF_EIO = 26 /* IO error */
+} scconf_errno_t;
+
+/* IDs */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef uint_t scconf_id_t;
+
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef scconf_id_t scconf_nodeid_t; /* node ID */
+
+/* Cluster transport handle */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+typedef void * scconf_cltr_handle_t;
+
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+extern scconf_errno_t scconf_get_nodeid(char *nodename,
+ scconf_nodeid_t *nodeidp);
+
+/*
+ * Get the name of a node from its "nodeid". Upon success,
+ * a pointer to the nodename is left in "nodenamep".
+ *
+ * It is the caller's responsibility to free memory allocated
+ * for "nodename" using free(3C).
+ *
+ * Possible return values:
+ *
+ * SCCONF_NOERR - success
+ * SCCONF_EPERM - not root
+ * SCCONF_ENOCLUSTER - there is no cluster
+ * SCCONF_ENOMEM - not enough memory
+ * SCCONF_EINVAL - invalid argument
+ * SCCONF_EUNEXPECTED - internal or unexpected error
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+extern scconf_errno_t scconf_get_nodename(scconf_nodeid_t nodeid,
+ char **nodenamep);
+
+/*
+ * Map scconf_errno_t to a string.
+ *
+ * The supplied "errbuffer" should be of at least SCCONF_MAXSTRINGLEN
+ * in length.
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+extern void scconf_strerr(char *errbuffer, scconf_errno_t err);
+
+/*
+ * Given a dev_t value, return the name of device service that contains this
+ * device.
+ *
+ * The caller is responsible for freeing the memory returned in "name".
+ *
+ * Possible return values:
+ *
+ * SCCONF_NOERR - success
+ * SCCONF_EPERM - not root
+ * SCCONF_ENOEXIST - the given device is not configured
+ * SCCONF_ENOMEM - not enough memory
+ * SCCONF_ENOCLUSTER - cluster config does not exist
+ * SCCONF_EUNEXPECTED - internal or unexpected error
+ */
+/* This definition is covered by PSARC/2001/261. DO NOT change it. */
+extern scconf_errno_t scconf_get_ds_by_devt(major_t maj, minor_t min,
+ char **dsname);
+
+/*
+ * End scconf.h
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_CLUSTER_H */
diff --git a/usr/src/lib/libdscfg/common/cfg_impl.h b/usr/src/lib/libdscfg/common/cfg_impl.h
new file mode 100644
index 0000000000..a972b2525f
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_impl.h
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ */
+
+#ifndef _CFG_IMPL_H
+#define _CFG_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_CFG 16 /* Max. number of lines in /etc/dscfg_format */
+
+#define CFG_MAX_KEY 256
+#define CFG_MAX_BUF 1024
+#define CFG_BLOCK_SIZE 512
+#define CFG_VTOC_SIZE 16
+#define CFG_VTOC_SKIP CFG_VTOC_SIZE * CFG_BLOCK_SIZE
+
+/*
+ * Parser and file handling routines for Configuration parser.
+ *
+ * General layout on disk
+ *
+ * header cfgheader_t
+ * parser configuration tag.field1.field2\n
+ * configuration data copy1 freeform strings
+ * configuration data copy2 freeform strings
+ *
+ * Strings in freeform fields are seperated by whitespace.
+ * End of entry seperated by null.
+ */
+
+struct lookup {
+ char l_word[CFG_MAX_KEY];
+ int l_value;
+ struct lookup *l_next;
+};
+
+struct parser {
+ struct lookup tag;
+ struct lookup *fld;
+ struct parser *next;
+};
+
+
+/*
+ * cfglist description
+ *
+ * ________
+ * | | the header has (with other things) an array
+ * | header | of h_cfg[n].l_size entries. index 4
+ * disk layout | | contains cfp->cf_head->h_cfg[4].l_size.
+ * |________|
+ * cfgfile-mapped->| |
+ * CFG_DEFAULT_PARSE_SIZE | parser | cache_hint.device.wrthru.nordcache.cnode
+ * | |
+ * |________|
+ * cfp->cf_head->h_ccopy1>| |
+ * CFG_DEFAULT_SSIZE | data | null terminated strings grouped together
+ * | copy 1 | in order of cfglist offset. ie data at
+ * |________| offset 0 is from h_cfgs[0].l_entry
+ * cfp->cf_head->h_ccopy2>| |
+ * CFG_DEFAULT_SSIZE | data |
+ * | copy 2 | same as above, used for two stage commit
+ * |________|
+ * cfp->cf_head->h_sizes1>| | here is where lists of sizes go for each
+ * CFG_DEFAULT_PSIZE | sizes | cfglist. each array is preceded by the num
+ * | copy 1 | of entries. |5|120|130|140|103|125|10|25 is
+ * |________| a list with 5 entries 120,130,140,103,125
+ * cfp->cf_head->h_sizes2>| | these numbers are used to rebuild l_nentry
+ * CFG_DEFAULT_PSIZE | sizes | and l_esiz fields in h_cfg[n]
+ * | copy 2 | this list is done as a two stage commit
+ * |________|
+ *
+ *
+ *
+ * Data is read into cfp->cf_head->h_ccopy1 and cfp->cf_head->h_ccopy2
+ * along with thier corresponding size metadata in cfp->cf_head->h_sizes1
+ * and cfp->cf_head->h_sizes2. This infomation is used to rebuild the
+ * cfglist structures seen below. The data in the cfglist structure is then
+ * the ONLY valid data. Additions and/or deletions to the database is done
+ * by moving around the cfglists and doing the right things with the size
+ * arrays, the actual entries, total list sizes, the total of all the sizes of
+ * all the cfglists and memory allocation. After addition/deletions are done,
+ * and cfg_close is called, all of the lists are placed back into h_cparse
+ * (which is really h_ccopy1 or h_ccopy2) the persistent lists are placed
+ * into h_sizes (which is really h_sizes1 or h_sizes2).
+ * A copy of each cfglist[n].l_size is kept in the header
+ * (cfgheader->cfgsizes[n]).
+ *
+ *
+ *
+ *
+ * h_cfgs h_cfgs[3]
+ * head |-[0]- /|-l_name == sndr
+ * |- /|-[1]- / |-l_entry == host dev bmap host..ip sync '\0' ...
+ * file |- / |-[2]- / |-l_esiz[0..l_nentry - 1] == [130, 132, 135, 133,..]
+ * |--|---------|-[3]---- |-l_enabled[0..l_nentry - 1] == [1,0,0,1,1]
+ * |- \ |-[4]- \ |-l_nentry == 5
+ * |- \|-[5]- \ |-l_index == 3
+ * |-[n]- \|-l_free == 50537
+ * |-l_size == 663 (130 + 132 + 135 + 133 + 133)
+ *
+ *
+ *
+ * l_name - is set when the parser is read.
+ * It is the first tag of a line of parser text.
+ * l_entry - is a pointer to the beginning of the null terminated string
+ * list that belongs to the cfglist tagged with l_name.
+ * l_esiz - is a list of sizes of the strings contained in l_entry.
+ * l_esiz[0] tells the size of the string at l_entry[0].
+ * l_esiz[n] is the size of the string that begins
+ * at l_entry + l_esiz[0] + l_esiz[1]..+ l_esize[n - 1]
+ * l_enabled - is a list of ones and zeros telling if this entry is alive
+ * in the kernel. indexing is the same as l_esiz. (not implemented)
+ * l_index - is the index of the parser tree that corresponds to l_name
+ * and is set when the parser tree is built
+ * l_free - is how memory is managed. Memory is allocated on a
+ * DEFAULT_ENTRY_SIZE boundry.
+ * the size of the balance of available memory at the end of l_entry
+ * is kept here. when this number is lower than the string we need to add,
+ * another block of memory is allocated for l_entry and the balance of
+ * the size is added to l_free.
+ * l_size - is size of this list. It is the summation of l_esiz[0..n]
+ *
+ */
+
+typedef struct cfglist {
+ char *l_name; /* name of list sndr, ii.. */
+ char *l_entry; /* start of list */
+ int *l_esiz; /* array of sizes of entries */
+ int l_nentry; /* number of entries */
+ int l_index; /* index in relation to parser position */
+ uint_t l_free; /* num of characters available */
+ int l_size; /* size of list */
+} cfglist_t;
+
+/* note: this does not imply DEFAULT_NENTRIES * DEFAULT_ENTRY_SIZE */
+#define DEFAULT_NENTRIES 100 /* value for l_esiz sizes array */
+#define DEFAULT_ENTRY_SIZE (50 * CFG_MAX_BUF) /* 50K for each l_entry */
+
+
+typedef struct cfgheader {
+ int32_t h_magic;
+ int h_state; /* State flag see below */
+ time_t h_stamp; /* time stamp of last update */
+ long h_lock; /* lock for update */
+ long h_size; /* total file size */
+ int h_parseoff; /* parser config offset */
+ int h_parsesize; /* parser config size */
+ char *h_cparse; /* start of configuration */
+ int h_csize; /* size of config section */
+ int h_acsize; /* size of alternate config section */
+ int *h_sizes; /* sizes of lists */
+ int h_psize; /* size of persistent section */
+ int h_apsize; /* size of alternate persistent section */
+ char *h_ccopy1; /* base of config section 1 */
+ char *h_ccopy2; /* base of config section 2 */
+ int *h_sizes1; /* sizes of lists on disk 1 */
+ int *h_sizes2; /* sizes of lists on disk 2 */
+ int h_seq1; /* Sequenece number copy 1 both sections */
+ int h_seq2; /* Sequenece number copy 2 both sections */
+ char h_ncfgs; /* number of cfgs */
+ cfglist_t *h_cfgs; /* start of cfg lists */
+ int h_cfgsizes[MAX_CFG]; /* Sizes of configs */
+} cfgheader_t;
+
+#define CFG_HDR_GOOD 0x1
+#define CFG_HDR_INVALID 0x2
+#define CFG_HDR_RDLOCK 0x4
+#define CFG_HDR_WRLOCK 0x8
+
+struct cfg_io_s; /* forward reference */
+typedef struct cfp {
+ int cf_fd; /* file descriptor */
+ int cf_flag; /* flags - see below */
+ long cf_size; /* size of file in fbas */
+ int cf_lock; /* lock file descriptor */
+ char *cf_mapped; /* mapped location via mmap */
+ char *cf_name; /* file name */
+ cfgheader_t *cf_head; /* header */
+ struct cfg_io_s *cf_pp; /* i/o provider */
+} cfp_t;
+
+typedef struct cfgfile {
+ void *cf_node; /* node filter */
+ cfp_t cf[2]; /* local & optional cluster file */
+} CFGFILE;
+
+typedef struct cfg_io_s {
+ struct cfg_io_s *next; /* Link to next module */
+ char *name; /* name of provider */
+ cfp_t *(*open)(cfp_t *, char *); /* Open device */
+ void (*close)(cfp_t *); /* Close device */
+ int (*seek)(cfp_t *, int, int); /* Seek */
+ int (*read)(cfp_t *, void *, int); /* read */
+ int (*write)(cfp_t *, void *, int); /* write */
+ char *(*readcf)(cfp_t *, char *, int, int); /* Read mem config */
+ int (*addcf)(cfp_t *, char *, int); /* add to mem config */
+ int (*remcf)(cfp_t *, int, int); /* remove an entry */
+ int (*replacecf)(cfp_t *, char *, int, int); /* replace entry */
+} cfg_io_t;
+
+#define CFG_FILE 0x1 /* database is in a regular file */
+#define CFG_NOREWIND 0x4 /* don't rewind for each get_string */
+#define CFG_NOWRVTOC 0x8 /* sector starts in vtoc land, skip it */
+#define CFG_RDONLY 0x10 /* database is read only */
+
+/*
+ * constants
+ */
+#define CFG_RDEV_LOCKFILE "/var/tmp/.dscfg.lck"
+#define CFG_NEW_MAGIC 0x4d414749 /* MAGI */
+#define CFG_DEFAULT_PARSE_SIZE (16 * 1024)
+#define CFG_DEFAULT_SSIZE (2 * 1024 * 1024)
+#define CFG_DEFAULT_PSIZE (512 * 1024)
+#define CFG_DEFAULT_OLDSIZE (96 * 1024)
+#define CFG_CONFIG_SIZE (CFG_DEFAULT_PARSE_SIZE + \
+ (2 * CFG_DEFAULT_SSIZE) + \
+ (2 * CFG_DEFAULT_PSIZE))
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_IMPL_H */
diff --git a/usr/src/lib/libdscfg/common/cfg_local.c b/usr/src/lib/libdscfg/common/cfg_local.c
new file mode 100644
index 0000000000..1b243b1a6d
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_local.c
@@ -0,0 +1,653 @@
+/*
+ * 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/vtoc.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <sys/mnttab.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.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_local.h"
+
+#if 0
+#define DEBUG_CFGLIST
+#define DEBUG_CFGLISTRM
+#endif
+
+extern int cfg_severity;
+extern char *cfg_perror_str;
+
+long
+get_bsize(cfp_t *cfp, char *name)
+{
+ char char_name[PATH_MAX];
+ char *rest;
+ struct vtoc vtoc;
+ int slice;
+ int fd;
+
+ if (strlen(name) >= PATH_MAX - 1)
+ return (0);
+
+ rest = strstr(name, "/dsk/");
+ if (rest == NULL) {
+ if ((rest = strstr(name, "/rdsk/")) == NULL)
+ return (0);
+ strcpy(char_name, name);
+ goto do_open;
+
+ }
+ strcpy(char_name, name);
+ char_name[strlen(name) - strlen(rest)] = 0;
+ strcat(char_name, "/rdsk/");
+ strcat(char_name, rest + 5);
+
+do_open:
+ fd = open(char_name, O_RDONLY);
+ if (fd < 0)
+ return (0);
+
+ slice = read_vtoc(fd, &vtoc);
+ if (slice < 0) {
+ (void) close(fd);
+ return (0);
+ }
+
+ (void) close(fd);
+ if (vtoc.v_part[slice].p_start < CFG_VTOC_SIZE)
+ cfp->cf_flag |= CFG_NOWRVTOC;
+
+ return (vtoc.v_part[slice].p_size);
+}
+
+/*
+ * round up to the next block size
+ */
+int
+get_block_size(int size)
+{
+ int ret;
+
+ if (size % CFG_BLOCK_SIZE != 0)
+ ret = size + CFG_BLOCK_SIZE - (size % CFG_BLOCK_SIZE);
+ else
+ ret = size;
+ return (ret);
+}
+
+/*
+ * get a chunk of mem rounded up to next block size
+ */
+char *
+get_block_buf(int size)
+{
+ int blk_size;
+ char *blk_buf;
+
+ blk_size = get_block_size(size);
+
+ if ((blk_buf = (char *)calloc(blk_size, sizeof (char))) == NULL) {
+ cfg_severity = CFG_EFATAL;
+ cfg_perror_str = dgettext("cfg", strerror(errno));
+ return (NULL);
+ }
+ return (blk_buf);
+}
+
+void
+free_block_buf(char *buf)
+{
+ if (buf)
+ free(buf);
+}
+
+void
+localcf_close(cfp_t *cfp)
+{
+ fsync(cfp->cf_fd);
+ cfp_unlock(cfp);
+ close(cfp->cf_fd);
+}
+
+
+/*
+ * cfg_open
+ * Open the current configuration file
+ * Sets file descriptor in cfp->cf_fd for use by other routines
+ */
+cfp_t *
+localcf_open(cfp_t *cfp, char *name)
+{
+ struct stat sb;
+ int rc;
+
+
+ if (name == NULL) {
+ cfg_perror_str = dgettext("cfg",
+ "cfg_open: unable to open configuration location");
+ cfg_severity = CFG_EFATAL;
+ return (NULL);
+ }
+
+ cfp->cf_fd = open(name, O_RDWR|O_CREAT|O_DSYNC|O_RSYNC, 0640);
+ if (cfp->cf_fd == -1) {
+ if ((cfp->cf_fd = open(name, O_RDONLY, 0640)) == -1) {
+ cfg_perror_str = dgettext("cfg",
+ "cfg_open: unable to open configuration location");
+ cfg_severity = CFG_EFATAL;
+ return (NULL);
+ }
+ cfp->cf_flag |= CFG_RDONLY;
+ }
+
+ if (fstat(cfp->cf_fd, &sb) == -1) {
+ close(cfp->cf_fd);
+ cfg_perror_str = dgettext("cfg",
+ "cfg_open: unable to stat configuration location");
+ cfg_severity = CFG_EFATAL;
+ return (NULL);
+ }
+
+
+ if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+ cfp->cf_size = get_bsize(cfp, name);
+
+ /* skip the vtoc if necessary */
+ if (cfp->cf_flag & CFG_NOWRVTOC) {
+ do {
+ rc = lseek(cfp->cf_fd, CFG_VTOC_SKIP, SEEK_SET);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1) {
+ cfg_perror_str = dgettext("cfg",
+ strerror(errno));
+ cfg_severity = CFG_EFATAL;
+ close(cfp->cf_fd);
+ return (NULL);
+ }
+ }
+
+ } else if (S_ISREG(sb.st_mode)) {
+ cfp->cf_flag |= CFG_FILE;
+ cfp->cf_size = FBA_NUM(FBA_SIZE(1) - 1 + sb.st_size);
+ } else {
+ cfg_perror_str = dgettext("cfg", "cfg_open: unknown file type");
+ cfg_severity = CFG_EFATAL;
+ close(cfp->cf_fd);
+ cfp->cf_fd = NULL;
+ return (NULL);
+ }
+ return (cfp);
+}
+
+int
+localcf_seekblk(cfp_t *cfp, int off, int mode)
+{
+ int rc;
+
+ do {
+ rc = lseek(cfp->cf_fd, off, mode);
+ } while (rc == -1 && errno == EINTR);
+
+ return (rc);
+}
+
+int
+localcf_readblk(cfp_t *cfp, void *buf, int size)
+{
+ int rc;
+
+ do {
+ rc = read(cfp->cf_fd, buf, size);
+ } while (rc == -1 && errno == EINTR);
+
+ return (rc);
+}
+
+int
+localcf_writeblk(cfp_t *cfp, void *buf, int size)
+{
+ int rc;
+
+ do {
+ rc = write(cfp->cf_fd, buf, size);
+ } while (rc == -1 && errno == EINTR);
+
+ return (rc);
+}
+
+int
+localcf_seek(cfp_t *cfp, int off, int mode)
+{
+ int rc;
+ int offset;
+
+ offset = get_block_size(off);
+
+ if ((mode == SEEK_SET) && (cfp->cf_flag & CFG_NOWRVTOC)) {
+ offset += CFG_VTOC_SKIP;
+ }
+
+ do {
+ rc = lseek(cfp->cf_fd, offset, mode);
+ } while (rc == -1 && errno == EINTR);
+
+ return (rc);
+}
+
+int
+localcf_read(cfp_t *cfp, void *buf, int size)
+{
+ int rc;
+ int blk_size;
+ char *blk_buf;
+
+ blk_size = get_block_size(size);
+ if ((blk_buf = get_block_buf(size)) == NULL)
+ return (-1);
+
+ do {
+ rc = read(cfp->cf_fd, blk_buf, blk_size);
+ } while (rc == -1 && errno == EINTR);
+
+ bcopy(blk_buf, buf, size);
+ free_block_buf(blk_buf);
+
+ return (rc);
+}
+
+int
+localcf_write(cfp_t *cfp, void *buf, int size)
+{
+ int rc;
+ int blk_size;
+ char *blk_buf;
+
+ blk_size = get_block_size(size);
+ if ((blk_buf = get_block_buf(size)) == NULL)
+ return (-1);
+
+ bcopy(buf, blk_buf, size);
+
+ do {
+ rc = write(cfp->cf_fd, blk_buf, blk_size);
+ } while (rc == -1 && errno == EINTR);
+
+ free_block_buf(blk_buf);
+
+ return (rc);
+}
+/*
+ * Routines which operate on internal version of configuration
+ */
+
+/*
+ * Add entry to end of configuration section
+ */
+
+int
+addcfline(cfp_t *cfp, char *line, int table_index)
+{
+ int len = strlen(line)+1;
+ int newsize = DEFAULT_ENTRY_SIZE / 2;
+ cfgheader_t *hd;
+ cfglist_t *cfl;
+ char *q;
+
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr, "addcfline: pre l_size %d h_cfgsizes[%d]"
+ " %d l_free %u adding len %d\n",
+ cfp->cf_head->h_cfgs[table_index].l_size, table_index,
+ cfp->cf_head->h_cfgsizes[table_index],
+ cfp->cf_head->h_cfgs[table_index].l_free, len);
+#endif
+
+ hd = cfp->cf_head;
+ cfl = &cfp->cf_head->h_cfgs[table_index];
+ if (cfl->l_free < len) {
+
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr, "resizing l_entry from %d to %d\n",
+ cfl->l_size + cfl->l_free, cfl->l_size +
+ cfl->l_free + newsize);
+#endif
+ cfl->l_entry = (char *)realloc(cfl->l_entry, (cfl->l_size +
+ cfl->l_free + newsize) * sizeof (char));
+ if (cfl->l_entry == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ cfl->l_free += newsize;
+
+ }
+ cfl->l_free -= len;
+
+ /* out of list slots, get some more */
+ if (cfl->l_nentry % DEFAULT_NENTRIES == 0) {
+ /*
+ * first, figure out how much bigger, than realloc
+ */
+
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr,
+ "list %d getting more nentries, I have %d\n",
+ table_index, cfl->l_nentry);
+#endif
+ cfl->l_esiz = (int *)
+ realloc(cfl->l_esiz, (cfl->l_nentry + DEFAULT_NENTRIES) *
+ sizeof (int));
+ if (cfl->l_esiz == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ }
+
+
+ cfl->l_esiz[cfl->l_nentry] = len;
+ cfl->l_nentry++;
+
+ /* add line to end of list */
+ q = cfl->l_entry + cfl->l_size;
+
+ strcpy(q, line);
+ q += len;
+
+ /* set sizes */
+ hd->h_cfgs[table_index].l_size += len;
+ hd->h_cfgsizes[table_index] = cfl->l_size;
+ cfp->cf_head->h_csize += len;
+
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr, "addcfline: post l_size %d h_cfgsizes[%d]"
+ " %d l_free %u\n h_csize %d\n",
+ cfp->cf_head->h_cfgs[table_index].l_size,
+ table_index, cfp->cf_head->h_cfgsizes[table_index],
+ cfp->cf_head->h_cfgs[table_index].l_free, cfp->cf_head->h_csize);
+#endif
+
+ return (1);
+}
+
+/*
+ * remove entry from configuration section
+ */
+int
+remcfline(cfp_t *cfp, int table_offset, int setnum)
+{
+ cfgheader_t *ch;
+ char *p, *q;
+ int len;
+ int copylen;
+ int i;
+ cfglist_t *cfl;
+ ch = cfp->cf_head;
+
+ cfl = &cfp->cf_head->h_cfgs[table_offset];
+
+ q = cfl->l_entry;
+
+ if (cfl->l_size == 0) {
+ /* list is empty */
+ return (-1);
+ }
+
+ if (!q) { /* somethings wrong here */
+ return (-1);
+ }
+
+
+ for (i = 1; i < setnum; i++) {
+ q += cfl->l_esiz[i - 1];
+ if (i >= cfl->l_nentry) { /* end of list */
+ return (-1);
+ }
+ }
+
+ if (q >= cfl->l_entry + cfl->l_size)
+ return (-1);
+
+ len = cfl->l_esiz[i - 1];
+
+
+#ifdef DEBUG_CFGLISTRM
+ fprintf(stderr, "remcfline: pre: l_size %d h_cfgsizes[%d] %d free %d"
+ " removing len %d\n",
+ ch->h_cfgs[table_offset].l_size, table_offset,
+ ch->h_cfgsizes[table_offset],
+ ch->h_cfgs[table_offset].l_free, len);
+#endif
+
+ p = q + len; /* next string */
+
+ if (!(p >= cfl->l_entry + cfl->l_size)) {
+ /* if we didn't delete the last string in list */
+ /* LINTED possible overflow */
+ copylen = cfl->l_entry + cfl->l_size - p;
+ bcopy(p, q, copylen);
+ copylen = (cfl->l_nentry - i) * sizeof (int);
+ bcopy(&cfl->l_esiz[i], &cfl->l_esiz[i - 1], copylen);
+ }
+
+ /* decrement the number of sets in this list */
+ cfl->l_nentry--;
+ /* not really necessary, but.. */
+ cfl->l_esiz[cfl->l_nentry] = 0;
+
+ cfl->l_size -= len;
+ cfl->l_free += len;
+
+ p = cfl->l_entry + cfl->l_size;
+ bzero(p, cfl->l_free);
+
+ ch->h_cfgsizes[table_offset] = cfl->l_size;
+ ch->h_csize -= len;
+
+
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr,
+ "remcfline: post: l_size %d h_cfgsizes[%d] %d free %d\n ",
+ ch->h_cfgs[table_offset].l_size, table_offset,
+ ch->h_cfgsizes[table_offset], ch->h_cfgs[table_offset].l_free);
+#endif
+
+ return (0);
+
+}
+/*
+ * Read entry from configuration section
+ */
+char *
+readcfline(cfp_t *cfp, char *buf, int table_offset, int num)
+{
+
+ char *q;
+ int i;
+ cfgheader_t *ch;
+ cfglist_t *cfl;
+
+ /* this means they couldn't even find it in the parser tree */
+ if (table_offset < 0)
+ return (NULL);
+
+ ch = cfp->cf_head;
+ cfl = &ch->h_cfgs[table_offset];
+
+ q = cfl->l_entry;
+
+ for (i = 1; i < num; i++) {
+ q += cfl->l_esiz[i - 1];
+ if (i >= cfl->l_nentry) /* end of list */
+ return (NULL);
+ }
+
+ if (q >= cfl->l_entry + cfl->l_size)
+ return (NULL);
+ strcpy(buf, q);
+ return (q);
+}
+
+
+/*
+ * overwrite from current position with new value
+ */
+int
+replacecfline(cfp_t *cfp, char *line, int table_offset, int num)
+{
+/*
+ * take a table offset and a num to replace
+ * index in, bump the list up, leaving a hole big
+ * enough for the new string, or bcopying the rest of the list
+ * down only leaving a hole big enough.
+ * make sure not to overflow the
+ * allocated list size.
+ */
+ cfgheader_t *ch;
+ cfglist_t *cfl;
+ char *p, *q;
+ int len = strlen(line) + 1;
+ int diff = 0;
+ int i;
+ int newsize = DEFAULT_ENTRY_SIZE / 2;
+
+
+ ch = cfp->cf_head;
+ cfl = &ch->h_cfgs[table_offset];
+
+ q = cfl->l_entry;
+ for (i = 1; i < num; i++) {
+ q += cfl->l_esiz[i - 1];
+ if (i >= cfl->l_nentry) /* end of list */
+ return (-1);
+ }
+ diff = len - cfl->l_esiz[i - 1];
+ /* check for > 0, comparing uint to int */
+ if ((diff > 0) && (diff > cfl->l_free)) {
+ /*
+ * we are going to overflow, get more mem, but only
+ * 1/2 as much as initial calloc, we don't need to be greedy
+ */
+#ifdef DEBUG_CFGLIST
+ fprintf(stderr,
+ "resizing at replacecfline from %d to %d \n",
+ cfl->l_size + cfl->l_free, cfl->l_size +
+ cfl->l_free + newsize);
+#endif
+ cfl->l_entry = (char *)realloc(cfl->l_entry,
+ (cfl->l_size + cfl->l_free + newsize) * sizeof (char));
+ if (cfl->l_entry == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ cfl->l_free += (DEFAULT_ENTRY_SIZE / 2);
+
+ /* re-find q, we could have a whole new chunk of memory here */
+ q = cfl->l_entry;
+ for (i = 1; i < num; i++) {
+ q += cfl->l_esiz[i - 1];
+ if (i >= cfl->l_nentry) /* end of list */
+ return (-1);
+ }
+ }
+
+ p = q + cfl->l_esiz[i - 1]; /* next string */
+ cfl->l_esiz[i - 1] += diff; /* the new entry size */
+ if (diff != 0) { /* move stuff over/back for correct fit */
+ /* LINTED possible overflow */
+ bcopy(p, p + diff, (cfl->l_entry + cfl->l_size - p));
+ cfl->l_free -= diff; /* 0 - (-1) = 1 */
+ cfl->l_size += diff;
+
+ /* total of all h_cfgs[n].l_entry */
+ cfp->cf_head->h_csize += diff;
+ cfp->cf_head->h_cfgsizes[table_offset] = cfl->l_size; /* disk */
+ bzero((cfl->l_entry + cfl->l_size), cfl->l_free);
+ }
+
+ strcpy(q, line);
+ return (1);
+
+}
+
+static cfg_io_t _cfg_raw_io_def = {
+ NULL,
+ "Local",
+ localcf_open,
+ localcf_close,
+ localcf_seek,
+ localcf_read,
+ localcf_write,
+ readcfline,
+ addcfline,
+ remcfline,
+ replacecfline,
+
+};
+
+static cfg_io_t _cfg_block_io_def = {
+ NULL,
+ "Local",
+ localcf_open,
+ localcf_close,
+ localcf_seekblk,
+ localcf_readblk,
+ localcf_writeblk,
+ readcfline,
+ addcfline,
+ remcfline,
+ replacecfline,
+};
+
+cfg_io_t *
+cfg_raw_io_provider(void)
+{
+ return (&_cfg_raw_io_def);
+}
+
+cfg_io_t *
+cfg_block_io_provider(void)
+{
+ return (&_cfg_block_io_def);
+}
diff --git a/usr/src/lib/libdscfg/common/cfg_local.h b/usr/src/lib/libdscfg/common/cfg_local.h
new file mode 100644
index 0000000000..5dff884baf
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_local.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef _CFG_LOCAL_H
+#define _CFG_LOCAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *readcfline(cfp_t *cfp, char *buf, int table_index, int offset);
+int addcfline(cfp_t *cfp, char *line, int table_index);
+int replacecfline(cfp_t *cfp, char *line, int table_index, int offset);
+int remcfline(cfp_t *cfp, int table_index, int offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_LOCAL_H */
diff --git a/usr/src/lib/libdscfg/common/cfg_lockd.h b/usr/src/lib/libdscfg/common/cfg_lockd.h
new file mode 100644
index 0000000000..42fbc6d676
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_lockd.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef _CFG_LOCKD_H
+#define _CFG_LOCKD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum { LOCK_NOTLOCKED, /* Unlock message */
+ LOCK_READ, /* ask for read lock */
+ LOCK_WRITE, /* ask for write lock */
+ LOCK_LOCKED, /* lock has been taken */
+ LOCK_LOCKEDBY, /* who has lock? */
+ LOCK_STAT, /* ask daemon to print its state */
+ LOCK_ACK /* acknowledge a notlocked msg */
+ } cfglockd_t;
+
+typedef struct sockaddr_in daemonaddr_t;
+
+struct lock_msg {
+ int32_t message;
+ pid_t pid;
+ int32_t order;
+ uint8_t seq;
+};
+
+#define CFG_PIDFILE "/var/tmp/.cfglockd.pid"
+#define CFG_SERVER_PORT 50121u
+#define CFG_LF_EOF -1
+#define CFG_LF_OKAY 1
+#define CFG_LF_AGAIN 0
+void cfg_lfinit();
+int cfg_filelock(int segment, int flag);
+int cfg_fileunlock(int segment);
+void cfg_readpid(int segment, pid_t *pidp);
+void cfg_writepid(int segment, pid_t pid);
+void cfg_enterpid();
+int cfg_lockd_init();
+cfglockd_t cfg_lockedby(pid_t *);
+void cfg_lockd_rdlock();
+void cfg_lockd_wrlock();
+void cfg_lockd_unlock();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFG_LOCKD_H */
diff --git a/usr/src/lib/libdscfg/common/cfg_lockdlck.c b/usr/src/lib/libdscfg/common/cfg_lockdlck.c
new file mode 100644
index 0000000000..14f7cb4b1d
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_lockdlck.c
@@ -0,0 +1,135 @@
+/*
+ * 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 <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "cfg_impl.h"
+#include "cfg_lockd.h"
+
+
+#define segment_off(s) ((off_t)(s) * sizeof (pid_t))
+
+static int local_lockfd;
+static int local_lockfda;
+
+void
+cfg_lfinit()
+{
+ local_lockfd = open(CFG_RDEV_LOCKFILE, O_RDWR|O_CREAT, 0644);
+ local_lockfda = open(CFG_RDEV_LOCKFILE, O_RDWR|O_APPEND, 0644);
+}
+
+int
+cfg_filelock(int segment, int flag)
+{
+ struct flock lk;
+ struct stat sb;
+ pid_t pid = 0;
+ off_t off = segment_off(segment);
+ int rc;
+
+ while (fstat(local_lockfd, &sb) == -1 && errno == EINTR)
+ ;
+ if (sb.st_size < off + sizeof (pid_t)) {
+ if ((flag&O_CREAT) == 0)
+ return (CFG_LF_EOF);
+ write(local_lockfda, &pid, sizeof (pid_t));
+ }
+ bzero(&lk, sizeof (lk));
+ lk.l_type = F_WRLCK;
+ lk.l_whence = SEEK_SET;
+ lk.l_start = off;
+ lk.l_len = (off_t)sizeof (pid_t);
+
+ while ((rc = fcntl(local_lockfd, F_SETLK, &lk)) < 0 && errno == EINTR)
+ ;
+ if (rc == -1 && errno == EAGAIN)
+ return (CFG_LF_AGAIN);
+
+ return (CFG_LF_OKAY);
+}
+
+
+int
+cfg_fileunlock(int segment)
+{
+ struct flock lk;
+ off_t off = segment_off(segment);
+
+ bzero(&lk, sizeof (lk));
+ lk.l_type = F_UNLCK;
+ lk.l_whence = SEEK_SET;
+ lk.l_start = off;
+ lk.l_len = (off_t)sizeof (pid_t);
+
+ while (fcntl(local_lockfd, F_SETLK, &lk) < 0 && errno == EINTR)
+ ;
+ return (1);
+}
+
+void
+cfg_readpid(int segment, pid_t *pidp)
+{
+ off_t off = segment_off(segment);
+ lseek(local_lockfd, off, SEEK_SET);
+ read(local_lockfd, pidp, sizeof (pid_t));
+}
+
+void
+cfg_writepid(int segment, pid_t pid)
+{
+ off_t off = segment_off(segment);
+ lseek(local_lockfd, off, SEEK_SET);
+ write(local_lockfd, &pid, sizeof (pid_t));
+}
+
+void
+cfg_enterpid()
+{
+ int i;
+ pid_t pid;
+
+ for (i = 0; ; i++) {
+ if (cfg_filelock(i, O_CREAT) == CFG_LF_OKAY) {
+ cfg_readpid(i, &pid);
+ if (pid != (pid_t)0) {
+ cfg_fileunlock(i);
+ continue;
+ }
+ pid = getpid();
+ cfg_writepid(i, pid);
+ break;
+ }
+ }
+}
diff --git a/usr/src/lib/libdscfg/common/cfg_lockdmsg.c b/usr/src/lib/libdscfg/common/cfg_lockdmsg.c
new file mode 100644
index 0000000000..238c75967e
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_lockdmsg.c
@@ -0,0 +1,324 @@
+/*
+ * 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 <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "cfg_lockd.h"
+
+static daemonaddr_t clientaddr;
+static daemonaddr_t server;
+
+static unsigned short server_port = CFG_SERVER_PORT;
+static int lock_soc = 0;
+static int pf_inet = AF_INET;
+static int locked;
+static int initdone;
+static int initresult;
+static pid_t socket_pid;
+
+static void cfg_lockd_reinit();
+
+static int last_cmd = -1;
+static uint8_t seq = 0;
+
+static void
+send_cmd(int cmd)
+{
+ struct lock_msg message_buf;
+ int rc;
+
+ if (last_cmd == cmd) {
+ message_buf.seq = seq;
+ } else {
+ message_buf.seq = ++seq;
+ last_cmd = cmd;
+ }
+ message_buf.message = cmd;
+ if ((message_buf.pid = getpid()) != socket_pid)
+ cfg_lockd_reinit();
+
+ do {
+ rc = sendto(lock_soc, &message_buf, sizeof (message_buf), 0,
+ (struct sockaddr *)&server, sizeof (server));
+ } while (rc == -1 && errno == EINTR);
+#ifdef CFG_LOCKD_DEBUG
+ if (rc < 0) {
+ perror("send");
+ }
+#endif
+}
+
+static void
+read_msg(struct lock_msg *mp)
+{
+ struct sockaddr from;
+ int rc, len;
+
+ /* wait for response */
+ do {
+ struct pollfd fds;
+
+ fds.fd = lock_soc;
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ rc = poll(&fds, 1, 500);
+ if (!rc) {
+#ifdef CFG_LOCKD_DEBUG
+ fprintf(stderr, "LOCKD: resending last command (%d)\n",
+ last_cmd);
+#endif
+ send_cmd(last_cmd);
+ }
+ } while (rc == 0 ||
+ (rc == -1 && errno == EINTR));
+
+ do {
+ len = sizeof (from);
+ rc = recvfrom(lock_soc, mp, sizeof (*mp), 0,
+ &from, &len);
+ } while (rc == -1 && errno == EINTR);
+#ifdef CFG_LOCKD_DEBUG
+ if (rc < 0) {
+ perror("revcfrom");
+ }
+#endif
+}
+
+static void
+read_reply()
+{
+ struct lock_msg message_buf;
+
+ do {
+ read_msg(&message_buf);
+ } while (message_buf.seq != seq || message_buf.message != LOCK_LOCKED);
+}
+
+static void
+read_ack()
+{
+ struct lock_msg message_buf;
+
+ do {
+ read_msg(&message_buf);
+ } while (message_buf.seq != seq || message_buf.message != LOCK_ACK);
+}
+
+void
+cfg_lockd_rdlock()
+{
+#ifdef CFG_LOCKD_DEBUG
+ FILE *fp;
+#endif
+
+ send_cmd(LOCK_READ);
+ locked = 1;
+ read_reply();
+
+#ifdef CFG_LOCKD_DEBUG
+ fp = fopen("/tmp/locktag", "a");
+ if (fp) {
+ time_t t = time(0);
+ fprintf(fp, "%19.19s read lock acquired\n", ctime(&t));
+ fclose(fp);
+ }
+ sleep(3);
+#endif
+}
+
+void
+cfg_lockd_wrlock()
+{
+#ifdef CFG_LOCKD_DEBUG
+ FILE *fp;
+#endif
+
+ send_cmd(LOCK_WRITE);
+ locked = 1;
+ read_reply();
+
+#ifdef CFG_LOCKD_DEBUG
+ fp = fopen("/tmp/locktag", "a");
+ if (fp) {
+ time_t t = time(0);
+ fprintf(fp, "%19.19s write lock acquired\n", ctime(&t));
+ fclose(fp);
+ }
+ sleep(3);
+#endif
+}
+
+void
+cfg_lockd_unlock()
+{
+#ifdef CFG_LOCKD_DEBUG
+ FILE *fp;
+#endif
+
+ send_cmd(LOCK_NOTLOCKED);
+ read_ack();
+ locked = 0;
+
+#ifdef CFG_LOCKD_DEBUG
+ fp = fopen("/tmp/locktag", "a");
+ if (fp) {
+ time_t t = time(0);
+ fprintf(fp, "%19.19s ----- lock released\n", ctime(&t));
+ fclose(fp);
+ }
+ sleep(3);
+#endif
+}
+
+void
+cfg_lockd_stat()
+{
+ send_cmd(LOCK_STAT);
+}
+
+cfglockd_t
+cfg_lockedby(pid_t *pidp)
+{
+ struct lock_msg message_buf;
+ send_cmd(LOCK_LOCKEDBY);
+ read_msg(&message_buf);
+ *pidp = message_buf.pid;
+ return ((cfglockd_t)message_buf.message);
+}
+
+static void
+cfg_atexit()
+{
+ if (locked)
+ cfg_lockd_unlock();
+}
+
+static int
+cfg_lockd_socket()
+{
+ if ((lock_soc = socket(pf_inet, SOCK_DGRAM, 0)) < 0) {
+#ifdef CFG_LOCKD_DEBUG
+ fprintf(stderr, "libcfg: failed to create socket\n");
+ perror("socket");
+#endif
+ return (-1);
+ }
+ clientaddr.sin_family = AF_INET;
+ clientaddr.sin_addr.s_addr = INADDR_ANY;
+ clientaddr.sin_port = htons(0);
+ if (bind(lock_soc, (struct sockaddr *)&clientaddr,
+ sizeof (clientaddr)) < 0) {
+#ifdef CFG_LOCKD_DEBUG
+ perror("bind");
+#endif
+ return (-1);
+ }
+ socket_pid = getpid();
+ return (0);
+}
+
+/*
+ * Re-initialise after a fork has been detected.
+ *
+ * Needs to create a new socket for new process to receive messages
+ * from the lock daemon and enter pid into lock file so that the daemon
+ * can detect new processes exit if it doesn't call unlock first.
+ */
+
+static void
+cfg_lockd_reinit()
+{
+ if (lock_soc)
+ close(lock_soc);
+ lock_soc = 0;
+ if (cfg_lockd_socket()) {
+ initresult = 0;
+ return;
+ }
+ cfg_enterpid();
+ initresult = 1;
+}
+
+int
+cfg_lockd_init()
+{
+ struct hostent *hp;
+ FILE *fp;
+ int pid = 0x12345678;
+
+ if (initdone) {
+ /* only perform reinit if init worked first time */
+ if (getpid() != socket_pid && initresult != 0)
+ cfg_lockd_reinit();
+ return (initresult);
+ }
+
+ initdone = 1;
+ initresult = 0;
+
+ /* check if there's a lock daemon out there */
+ if ((fp = fopen(CFG_PIDFILE, "r")) == NULL)
+ return (0);
+ if (fscanf(fp, "%d\n", &pid) != 1) {
+ fclose(fp);
+ return (0);
+ }
+ fclose(fp);
+ if (kill((pid_t)pid, 0) != 0)
+ return (0);
+
+ /* there is a lock daemon */
+ cfg_lfinit();
+ cfg_enterpid();
+ if (cfg_lockd_socket())
+ return (0);
+
+ if ((hp = gethostbyname("localhost")) == NULL) {
+#ifdef CFG_LOCKD_DEBUG
+ fprintf(stderr, "Can't find hostent for %s\n", "localhost");
+#endif
+ return (0);
+ }
+ (void) memcpy(&(server.sin_addr.s_addr), *(hp->h_addr_list),
+ sizeof (server.sin_addr));
+ server.sin_port = htons(server_port);
+ server.sin_family = hp->h_addrtype;
+ endhostent();
+ atexit(cfg_atexit);
+ initresult = 1;
+ return (1);
+}
diff --git a/usr/src/lib/libdscfg/common/cfg_vols.c b/usr/src/lib/libdscfg/common/cfg_vols.c
new file mode 100644
index 0000000000..6f0f8bb447
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/cfg_vols.c
@@ -0,0 +1,1286 @@
+/*
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <sys/nsctl/cfg.h>
+
+#include <sys/unistat/spcs_s.h>
+#include <sys/unistat/spcs_s_u.h>
+#include <sys/unistat/spcs_errors.h>
+#include <sys/unistat/spcs_s_impl.h>
+
+#include <sys/nsctl/sv.h>
+#include <sys/nsctl/nsc_hash.h>
+
+#define DEV_EXPAND 32
+
+#define DO_DISABLE 0
+#define DO_ENABLE 1
+
+/*
+ * Utility functions for iiadm and rdcadm/sndradm.
+ */
+
+typedef struct hash_data_s {
+ union {
+ char *users;
+ char *mode;
+ } u;
+ char *path;
+ char *node;
+ int setno;
+} hash_data_t;
+
+typedef struct {
+ dev_t rdev;
+ mode_t mode;
+ char *path;
+} device_t;
+
+static hash_data_t *make_svol_data(char *, char *, char *, int);
+static hash_data_t *make_dsvol_data(char *, char *, char *, int);
+static void delete_svol_data(void *);
+static void delete_dsvol_data(void *);
+static int sv_action(char *, CFGFILE *, char *, int);
+
+static int add_dev_entry(const char *);
+static int compare(const void *, const void *);
+static char *find_devid(const char *);
+static void free_dev_entries();
+static void rebuild_devhash();
+
+static hash_node_t **dsvol;
+static int dsvol_loaded = 0;
+
+static hash_node_t **svol;
+static int svol_loaded = 0;
+
+static hash_node_t **shadowvol;
+
+static hash_node_t **devhash;
+static device_t *devlist;
+static int devcount = 0;
+static int devalloc = 0;
+
+/*
+ * cfg_add_user
+ *
+ * Description:
+ * Adds the calling tool as a user of the volume.
+ *
+ * Inputs:
+ * char *path: The pathname of the volume to be enabled.
+ * char *cnode: The device group name, or NULL if -C local or not cluster
+ * CFGFILE *cfg: A pointer to the current config file, or NULL if this
+ * function is to open/write/commit/close the change itself.
+ *
+ * Return values:
+ * CFG_USER_FIRST: Indicates that this is the first user of this
+ * particular volume.
+ * CFG_USER_OK: Indicates that the volume has already been entered into
+ * the config file.
+ * CFG_USER_ERR: Indicates that some failure has occurred and no changes
+ * to the config file have been made.
+ * CFG_USER_REPEAT: Indicates that this user has already registered for
+ * the volume.
+ */
+int
+cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user)
+{
+ int self_open, self_loaded, change_made;
+ char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
+ int retval, rc;
+ hash_data_t *data;
+
+ self_open = (cfg == NULL);
+ self_loaded = 0;
+ change_made = 0;
+
+ if (self_open) {
+ cfg = cfg_open(NULL);
+ if (cfg == NULL) {
+ return (CFG_USER_ERR);
+ }
+
+ if (!cfg_lock(cfg, CFG_WRLOCK)) {
+ /* oops */
+ cfg_close(cfg);
+ return (CFG_USER_ERR);
+ }
+ }
+
+ /* Check cnode */
+ ctag = cfg_get_resource(cfg);
+ if (cnode) {
+ if (ctag) {
+ if (strcmp(cnode, ctag))
+ return (CFG_USER_ERR);
+ } else
+ cfg_resource(cfg, cnode);
+ } else
+ cnode = ctag;
+
+ if (!dsvol_loaded) {
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ self_loaded = 1;
+ }
+
+ /* find the volume */
+ (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
+ data = nsc_lookup(dsvol, search_key);
+
+ if (!data) {
+ /* whoops, not found. Add as new user */
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode,
+ user);
+ rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf));
+ if (rc < 0) {
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ /* reload hash, if we need to */
+ if (!self_loaded) {
+ cfg_unload_dsvols();
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ }
+ retval = CFG_USER_FIRST;
+ change_made = 1;
+ } else {
+ /* Check to ensure we're not already listed */
+ char *p = strdup(data->u.users);
+ char *q = strtok(p, ",");
+ while (q && (strcmp(q, user) != 0)) {
+ q = strtok(0, ",");
+ }
+ free(p); /* not using data; only testing 'q' ptr */
+
+ if (!q) {
+ /* not listed as a user */
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s",
+ data->path, data->node, data->u.users, user);
+ (void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d",
+ data->setno);
+ if (cfg_put_cstring(cfg, search_key, buf,
+ strlen(buf)) < 0) {
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+
+ /*
+ * Since we deleted an entry from the config
+ * file, we don't know what all the new
+ * set numbers are. We need to reload
+ * everything
+ */
+ if (!self_loaded) {
+ cfg_unload_dsvols();
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ }
+ change_made = 1;
+ retval = CFG_USER_OK;
+ } else {
+ retval = CFG_USER_REPEAT;
+ }
+ }
+
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+
+ if (self_open) {
+ if (change_made)
+ (void) cfg_commit(cfg);
+ cfg_close(cfg);
+ }
+
+ return (retval);
+}
+
+/*
+ * cfg_rem_user
+ *
+ * Description:
+ * Removes a user from the config file.
+ *
+ * Inputs:
+ * char *path: The pathname of the volume to be enabled.
+ * char *cnode: The device group name, or NULL if -C local or not cluster
+ * char *user: The subsystem that is adding this tag (sv, ii, sndr)
+ * CFGFILE *cfg: A pointer to the current config file, or NULL if this
+ * function is to open/write/commit/close the change itself.
+ * Return values:
+ * CFG_USER_ERR: An error occurred during the processing of this
+ * directive.
+ * CFG_USER_OK: User successfully removed; volume in use by other(s).
+ * CFG_USER_LAST: User successfuly removed; no other users registered
+ * CFG_USER_GONE: The volume is no longer listed in the dsvol section,
+ * indicating some sort of application-level error.
+ *
+ */
+int
+cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user)
+{
+ int self_open, self_loaded, change_made;
+ char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
+ char cfg_key[ CFG_MAX_KEY ];
+ hash_data_t *data;
+ int retval;
+ int force_remove;
+
+ self_open = (cfg == NULL);
+ self_loaded = 0;
+ change_made = 0;
+ force_remove = (strcmp(user, "sv") == 0);
+
+ if ('-' == *user) {
+ ++user;
+ }
+
+ /* Check cnode */
+ ctag = cfg_get_resource(cfg);
+ if (cnode) {
+ if (ctag) {
+ if (strcmp(cnode, ctag))
+ return (CFG_USER_ERR);
+ } else
+ cfg_resource(cfg, cnode);
+ } else
+ cnode = ctag;
+
+ if (self_open) {
+ cfg = cfg_open(NULL);
+ if (cfg == NULL) {
+ return (CFG_USER_ERR);
+ }
+
+ if (!cfg_lock(cfg, CFG_WRLOCK)) {
+ /* oops */
+ cfg_close(cfg);
+ return (CFG_USER_ERR);
+ }
+ }
+
+
+ change_made = 0;
+ if (!dsvol_loaded) {
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ self_loaded = 1;
+ }
+
+ /* find the volume */
+ (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
+ data = nsc_lookup(dsvol, search_key);
+
+ if (!data) {
+ /* yipes */
+ retval = CFG_USER_GONE;
+ } else if (force_remove) {
+ retval = CFG_USER_LAST;
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d",
+ data->setno);
+ if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) {
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ if (!self_loaded) {
+ cfg_unload_dsvols();
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ }
+ } else {
+ char *p = strdup(data->u.users);
+ char *q = strtok(p, ",");
+ int appended = 0;
+
+ (void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path,
+ data->node);
+ while (q && (strcmp(q, user) != 0)) {
+ if (appended) {
+ strcat(buf, ",");
+ strcat(buf, q);
+ } else {
+ strcat(buf, q);
+ appended = 1;
+ }
+ q = strtok(0, ",");
+ }
+
+ if (!q) {
+ /* uh-oh */
+ retval = CFG_USER_GONE;
+ } else {
+ /* old user skipped; add in remaining users */
+ while (q = strtok(0, ", ")) {
+ if (appended) {
+ strcat(buf, ",");
+ strcat(buf, q);
+ } else {
+ strcat(buf, q);
+ appended = 1;
+ }
+ }
+
+ if (appended) {
+ retval = CFG_USER_OK;
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(cfg_key, CFG_MAX_KEY,
+ "dsvol.set%d", data->setno);
+ if (cfg_put_cstring(cfg, cfg_key, buf,
+ strlen(buf)) < 0) {
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ if (!self_loaded) {
+ cfg_unload_dsvols();
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ }
+ } else {
+ retval = CFG_USER_LAST;
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(cfg_key, CFG_MAX_KEY,
+ "dsvol.set%d", data->setno);
+ if (cfg_put_cstring(cfg, cfg_key, NULL,
+ 0) < 0) {
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ /*
+ * Since we deleted an entry from the config
+ * file, we don't know what all the new
+ * set numbers are. We need to reload
+ * everything
+ */
+ if (!self_loaded) {
+ cfg_unload_dsvols();
+ if (cfg_load_dsvols(cfg) < 0) {
+ if (self_open) {
+ cfg_close(cfg);
+ }
+ return (CFG_USER_ERR);
+ }
+ }
+ }
+ change_made = 1;
+ }
+ }
+
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+
+ if (self_open) {
+ if (change_made)
+ (void) cfg_commit(cfg);
+ cfg_close(cfg);
+ }
+
+ return (retval);
+}
+
+/*
+ * Enable a volume under SV control (or add this char *user to the list
+ * of users of that volume).
+ *
+ * Parameters:
+ * cfg - The config file to use.
+ * path - The pathname of the volume
+ * ctag - The cluster tag for this volume (if any)
+ * user - The user (sv, ii, sndr) of the volume.
+ */
+int
+cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user)
+{
+ int rc;
+ int retval;
+
+ if (!ctag || *ctag == '\0') {
+ ctag = "-";
+ }
+
+ retval = -1;
+ rc = cfg_add_user(cfg, path, ctag, user);
+ switch (rc) {
+ case CFG_USER_ERR:
+ spcs_log("dsvol", NULL,
+ gettext("unable to set up dsvol section of config for %s"),
+ path);
+ break;
+ case CFG_USER_OK:
+ retval = 0;
+ break;
+ case CFG_USER_FIRST:
+ /* enable sv! */
+ retval = sv_action(path, cfg, ctag, DO_ENABLE);
+ if (retval < 0) {
+ (void) cfg_rem_user(cfg, path, ctag, user);
+ }
+ break;
+ default:
+ spcs_log("dsvol", NULL,
+ gettext("unexpected return from cfg_add_user(%d)"), rc);
+ break;
+ }
+
+ return (retval);
+}
+
+/*
+ * Disable a volume from SV control (or remove this char *user from the list
+ * of users of that volume).
+ *
+ * Parameters:
+ * cfg - The config file to use.
+ * path - The pathname of the volume
+ * ctag - The cluster tag for this volume (if any)
+ * user - The user (sv, ii, sndr) of the volume.
+ */
+int
+cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user)
+{
+ int rc;
+ int retval;
+
+ if (!ctag || *ctag == '\0') {
+ ctag = "-";
+ }
+
+ retval = -1;
+ rc = cfg_rem_user(cfg, path, ctag, user);
+ switch (rc) {
+ case CFG_USER_ERR:
+ spcs_log("dsvol", NULL,
+ gettext("unable to set up dsvol section of config for %s"),
+ path);
+ break;
+ case CFG_USER_OK:
+ retval = 0;
+ break;
+ case CFG_USER_GONE:
+ spcs_log("dsvol", NULL,
+ gettext("%s tried to remove non-existent tag for %s"),
+ user, path);
+ break;
+ case CFG_USER_LAST:
+ /* diable sv! */
+ retval = sv_action(path, cfg, ctag, DO_DISABLE);
+ break;
+ default:
+ spcs_log("dsvol", NULL,
+ gettext("unexpected return from cfg_rem_user(%d)"), rc);
+ break;
+ }
+
+ return (retval);
+}
+
+/*
+ * cfg_load_dsvols
+ *
+ * Description:
+ * Loads the dsvol section of the config file into a giant hash, to
+ * make searching faster. The important bit to remember is to not
+ * release the write lock between calling cfg_load_dsvols() and the
+ * cfg_*_user() functions.
+ *
+ * Assumptions:
+ * 1/ cfg file is open
+ * 2/ cfg file has been write-locked
+ * 3/ user of this routine may already be using hcreate/hsearch
+ *
+ * Return value:
+ * -1 if error, or total number of sets found
+ */
+int
+cfg_load_dsvols(CFGFILE *cfg)
+{
+ int set, rc, entries;
+ char search_key[ CFG_MAX_KEY ];
+ char *buf;
+ char **entry, *path, *cnode, *users;
+ hash_data_t *data;
+ int devs_added = 0;
+ int offset = 0;
+ char *ctag = cfg_get_resource(cfg);
+ if (!ctag || *ctag == '\0') {
+ ctag = "-";
+ }
+
+ dsvol = nsc_create_hash();
+ if (!dsvol) {
+ return (-1);
+ }
+
+ rc = 0;
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ entries = cfg_get_section(cfg, &entry, "dsvol");
+ for (set = 1; set <= entries; set++) {
+ buf = entry[set - 1];
+
+ /* split up the line */
+ if (!(path = strtok(buf, " "))) {
+ /* oops, now what? */
+ free(buf);
+ break;
+ }
+ if (!(cnode = strtok(0, " "))) {
+ free(buf);
+ break;
+ }
+ if (ctag && (strcmp(cnode, ctag) != 0)) {
+ ++offset;
+ free(buf);
+ continue;
+ }
+
+ if (!(users = strtok(0, " "))) {
+ free(buf);
+ break;
+ }
+
+ data = make_dsvol_data(path, cnode, users, set - offset);
+ if (!data) {
+ free(buf);
+ break;
+ }
+ (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
+ rc = nsc_insert_node(dsvol, data, search_key);
+ if (rc < 0) {
+ free(buf);
+ break;
+ }
+
+ /* we also need to keep track of node information */
+ rc = add_dev_entry(path);
+ if (rc < 0) {
+ free(buf);
+ break;
+ } else if (rc)
+ ++devs_added;
+
+ free(buf);
+ rc = 0;
+ }
+
+ while (set < entries)
+ free(entry[set++]);
+ if (entries)
+ free(entry);
+
+ if (devs_added) {
+ qsort(devlist, devcount, sizeof (device_t), compare);
+ rebuild_devhash();
+ }
+
+ dsvol_loaded = 1;
+ return (rc < 0? rc : entries);
+}
+
+/*
+ * cfg_unload_dsvols
+ *
+ * Description:
+ * Free all memory allocated with cfg_load_dsvols.
+ */
+void
+cfg_unload_dsvols()
+{
+ if (dsvol) {
+ nsc_remove_all(dsvol, delete_dsvol_data);
+ dsvol = 0;
+ dsvol_loaded = 0;
+ }
+}
+
+/*
+ * cfg_load_svols
+ *
+ * Description:
+ * Loads the sv section of the config file into a giant hash, to make
+ * searching faster. The important bit to remember is to not release
+ * the write lock between calling cfg_load_svols() and the cfg_*_user()
+ * functions.
+ *
+ * Assumptions:
+ * 1/ cfg file is open
+ * 2/ cfg file has been write-locked
+ * 3/ user of this routine may already be using builtin hcreate/hsearch
+ */
+int
+cfg_load_svols(CFGFILE *cfg)
+{
+ int set, entries, offset = 0;
+ char *buf, **entry;
+ char *path, *mode, *cnode;
+ hash_data_t *data;
+ char *ctag = cfg_get_resource(cfg);
+ if (!ctag || *ctag == '\0') {
+ ctag = "-";
+ }
+
+ svol = nsc_create_hash();
+ if (!svol) {
+ return (-1);
+ }
+
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ entries = cfg_get_section(cfg, &entry, "sv");
+ for (set = 1; set <= entries; set++) {
+ buf = entry[set - 1];
+
+ /* split up the line */
+ if (!(path = strtok(buf, " "))) {
+ free(buf);
+ break;
+ }
+ if (!(mode = strtok(0, " "))) {
+ free(buf);
+ break;
+ }
+ if (!(cnode = strtok(0, " "))) {
+ cnode = "";
+ }
+
+ if (ctag && (strcmp(cnode, ctag) != 0)) {
+ ++offset;
+ free(buf);
+ continue;
+ }
+
+ data = make_svol_data(path, mode, cnode, set - offset);
+ if (!data) {
+ free(buf);
+ break;
+ }
+ if (nsc_insert_node(svol, data, path) < 0) {
+ free(buf);
+ break;
+ }
+ free(buf);
+ }
+ while (set < entries)
+ free(entry[set++]);
+ if (entries)
+ free(entry);
+
+ svol_loaded = 1;
+ return (0);
+}
+
+/*
+ * cfg_unload_svols
+ *
+ * Description:
+ * Frees all memory allocated with cfg_load_dsvols
+ */
+void
+cfg_unload_svols()
+{
+ if (svol) {
+ nsc_remove_all(svol, delete_svol_data);
+ svol = 0;
+ svol_loaded = 0;
+ }
+}
+
+/*
+ * cfg_get_canonical_name
+ *
+ * Description:
+ * Find out whether a device is already known by another name in
+ * the config file.
+ *
+ * Parameters:
+ * cfg - The config file to use
+ * path - The pathname of the device
+ * result - (output) The name it is otherwise known as. This parameter
+ * must be freed by the caller.
+ *
+ * Return values:
+ * -1: error
+ * 0: name is as expected, or is not known
+ * 1: Name is known by different name (stored in 'result')
+ */
+int
+cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result)
+{
+ int self_loaded;
+ char *alt_path;
+ int retval;
+
+ if (devlist) {
+ self_loaded = 0;
+ } else {
+ if (cfg_load_shadows(cfg) < 0) {
+ return (-1);
+ }
+ self_loaded = 1;
+ }
+
+ /* see if it exists under a different name */
+ alt_path = find_devid(path);
+ if (!alt_path || strcmp(path, alt_path) == 0) {
+ *result = NULL;
+ retval = 0;
+ } else {
+ /* a-ha */
+ *result = strdup(alt_path);
+ retval = 1;
+ }
+
+ if (self_loaded) {
+ free_dev_entries();
+ }
+
+ return (retval);
+}
+
+/*
+ * cfg_load_shadows
+ *
+ * Description:
+ * Load in shadow and bitmap volumes from the II section of the
+ * config file. SNDR's volumes are handled already by cfg_load_dsvols.
+ * Not all shadow volumes are listed under dsvol: they can be exported.
+ *
+ * Parameters:
+ * cfg - The config file to use
+ *
+ * Return values:
+ * -1: error
+ * 0: success
+ */
+int
+cfg_load_shadows(CFGFILE *cfg)
+{
+ int set, self_loaded, rc, entries;
+ char *buf, **entry, *ptr;
+ int devs_added = 0;
+
+ if (dsvol_loaded) {
+ self_loaded = 0;
+ } else {
+ if (cfg_load_dsvols(cfg) < 0) {
+ return (-1);
+ }
+ self_loaded = 1;
+ }
+
+ shadowvol = nsc_create_hash();
+ if (!shadowvol) {
+ return (-1);
+ }
+
+ rc = 0;
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ entries = cfg_get_section(cfg, &entry, "ii");
+ for (set = 1; set <= entries; set++) {
+ buf = entry[set - 1];
+
+ /* skip the master vol */
+ ptr = strtok(buf, " ");
+
+ /* shadow is next */
+ ptr = strtok(NULL, " ");
+
+ rc = add_dev_entry(ptr);
+ if (rc < 0) {
+ free(buf);
+ break;
+ } else if (rc)
+ ++devs_added;
+
+ /* and next is bitmap */
+ ptr = strtok(NULL, " ");
+
+ rc = add_dev_entry(ptr);
+ if (rc < 0) {
+ free(buf);
+ break;
+ } else if (rc)
+ ++devs_added;
+ rc = 0;
+ free(buf);
+ }
+ while (set < entries)
+ free(entry[set++]);
+ if (entries)
+ free(entry);
+
+ if (self_loaded) {
+ cfg_unload_dsvols();
+ }
+
+ if (devs_added) {
+ /* sort it, in preparation for lookups */
+ qsort(devlist, devcount, sizeof (device_t), compare);
+ rebuild_devhash();
+ }
+
+ return (rc);
+}
+
+void
+cfg_unload_shadows()
+{
+ /* do nothing */
+}
+
+/* ---------------------------------------------------------------------- */
+
+static hash_data_t *
+make_dsvol_data(char *path, char *cnode, char *users, int set)
+{
+ hash_data_t *data;
+
+ data = (hash_data_t *)malloc(sizeof (hash_data_t));
+ if (!data) {
+ return (0);
+ }
+
+ data->u.users = strdup(users);
+ data->path = strdup(path);
+ data->node = strdup(cnode);
+ data->setno = set;
+
+ return (data);
+}
+
+static void
+delete_dsvol_data(void *data)
+{
+ hash_data_t *p = (hash_data_t *)data;
+
+ free(p->u.users);
+ free(p->path);
+ free(p->node);
+ free(p);
+}
+
+static hash_data_t *
+make_svol_data(char *path, char *mode, char *cnode, int set)
+{
+ hash_data_t *data;
+
+ data = (hash_data_t *)malloc(sizeof (hash_data_t));
+ if (!data) {
+ return (0);
+ }
+
+ data->u.mode = strdup(mode);
+ data->path = strdup(path);
+ data->node = strdup(cnode);
+ data->setno = set;
+
+ return (data);
+}
+
+
+static void
+delete_svol_data(void *data)
+{
+ hash_data_t *p = (hash_data_t *)data;
+
+ free(p->u.mode);
+ free(p->path);
+ free(p->node);
+ free(p);
+}
+
+static int
+sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable)
+{
+ struct stat stb;
+ sv_conf_t svc;
+ int fd = -1;
+ int cfg_changed = 0;
+ CFGFILE *cfg;
+ int print_log = 0;
+ int err = 0, rc;
+ int sv_ioctl, spcs_err, self_loaded;
+ char *log_str1, *log_str2;
+ char key[ CFG_MAX_KEY ];
+ char buf[ CFG_MAX_BUF ];
+ hash_data_t *node;
+ device_t *statinfo = 0;
+
+ if (caller_cfg == NULL) {
+ cfg = cfg_open(NULL);
+ if (cfg == NULL)
+ return (-1);
+
+ if (ctag)
+ cfg_resource(cfg, ctag);
+ } else
+ cfg = caller_cfg;
+
+
+ self_loaded = 0;
+ sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE);
+ log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s"));
+ log_str2 = (enable? gettext("unable to enable %s") :
+ gettext("unable to disable %s"));
+ spcs_err = (enable? SV_EENABLED : SV_EDISABLED);
+ bzero(&svc, sizeof (svc));
+
+ if (devhash)
+ statinfo = nsc_lookup(devhash, path);
+
+ if (statinfo) {
+ if (!S_ISCHR(statinfo->mode))
+ goto error;
+ svc.svc_major = major(statinfo->rdev);
+ svc.svc_minor = minor(statinfo->rdev);
+ } else {
+ if (stat(path, &stb) != 0)
+ goto error;
+
+ if (!S_ISCHR(stb.st_mode))
+ goto error;
+ svc.svc_major = major(stb.st_rdev);
+ svc.svc_minor = minor(stb.st_rdev);
+ }
+
+ strncpy(svc.svc_path, path, sizeof (svc.svc_path));
+
+ fd = open(SV_DEVICE, O_RDONLY);
+ if (fd < 0)
+ goto error;
+
+ svc.svc_flag = (NSC_DEVICE | NSC_CACHE);
+ svc.svc_error = spcs_s_ucreate();
+
+ do {
+ rc = ioctl(fd, sv_ioctl, &svc);
+ } while (rc < 0 && errno == EINTR);
+
+ if (rc < 0) {
+ if (errno != spcs_err) {
+ spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path);
+ if (enable)
+ goto error;
+ else
+ err = errno;
+ } else
+ err = spcs_err;
+ }
+
+ spcs_log("sv", NULL, log_str1, svc.svc_path);
+
+ /* SV enable succeeded */
+ if (caller_cfg == NULL) /* was not previously locked */
+ if (!cfg_lock(cfg, CFG_WRLOCK))
+ goto error;
+
+ if (err != spcs_err) { /* already enabled, already in config */
+ if (enable) {
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path,
+ ctag? ctag : "-");
+ if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) {
+ /* SV config not updated, so SV disable again */
+ (void) ioctl(fd, SVIOC_DISABLE, &svc);
+ print_log++;
+ } else
+ cfg_changed = 1;
+ } else {
+ /* pull it out of the config */
+ if (!svol_loaded) {
+ if (cfg_load_svols(cfg) < 0) {
+ if (NULL == caller_cfg) {
+ cfg_close(cfg);
+ }
+ return (-1);
+ }
+ self_loaded = 1;
+ }
+ node = nsc_lookup(svol, svc.svc_path);
+ if (node) {
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ (void) snprintf(key, CFG_MAX_KEY, "sv.set%d",
+ node->setno);
+ if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) {
+ spcs_log("sv", NULL,
+ gettext("failed to remove %s from "
+ "sv config"), svc.svc_path);
+ }
+ /*
+ * Since we deleted an entry from the config
+ * file, we don't know what all the new
+ * set numbers are. We need to reload
+ * everything
+ */
+ if (!self_loaded) {
+ cfg_unload_svols();
+ if (cfg_load_svols(cfg) < 0) {
+ if (NULL == caller_cfg) {
+ cfg_close(cfg);
+ }
+ return (-1);
+ }
+ }
+ cfg_changed = 1;
+ }
+ if (self_loaded) {
+ cfg_unload_svols();
+ self_loaded = 0;
+ }
+ }
+ }
+
+#ifdef lint
+ (void) printf("extra line to shut lint up %s\n", module_names[0]);
+#endif
+
+error:
+ if (fd >= 0)
+ (void) close(fd);
+
+ if (cfg == NULL)
+ return (-1);
+
+ if (cfg_changed)
+ if (caller_cfg == NULL) /* we opened config */
+ (void) cfg_commit(cfg);
+
+ if (caller_cfg == NULL)
+ cfg_close(cfg);
+ if ((cfg_changed) || (err == spcs_err))
+ return (1);
+ if (print_log)
+ spcs_log("sv", NULL,
+ gettext("unable to add to configuration, disabled %s"),
+ svc.svc_path);
+ spcs_s_ufree(&svc.svc_error);
+
+ return (-1);
+}
+
+/*
+ * add_dev_entry
+ *
+ * Add an entry into the devlist and the devhash for future lookups.
+ *
+ * Return values:
+ * -1 An error occurred.
+ * 0 Entry added
+ * 1 Entry already exists.
+ */
+static int
+add_dev_entry(const char *path)
+{
+ struct stat buf;
+ device_t *newmem;
+ hash_data_t *data;
+
+ if (!devhash) {
+ devhash = nsc_create_hash();
+ if (!devhash) {
+ return (-1);
+ }
+ } else {
+ data = nsc_lookup(devhash, path);
+ if (data) {
+ return (1);
+ }
+ }
+
+ if (stat(path, &buf) < 0) {
+ /* ignore error, we are most likely deleting entry anyway */
+ buf.st_rdev = 0;
+ }
+
+ if (devcount >= devalloc) {
+ /* make some room */
+ devalloc += DEV_EXPAND;
+ newmem = (device_t *)realloc(devlist, devalloc *
+ sizeof (device_t));
+ if (!newmem) {
+ free_dev_entries();
+ return (-1);
+ } else {
+ devlist = newmem;
+ }
+ }
+
+ devlist[ devcount ].path = strdup(path);
+ devlist[ devcount ].rdev = buf.st_rdev;
+ devlist[ devcount ].mode = buf.st_mode;
+
+ if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) {
+ return (-1);
+ }
+
+ ++devcount;
+ return (0);
+}
+
+static void
+rebuild_devhash()
+{
+ int i;
+
+ if (!devhash)
+ nsc_remove_all(devhash, 0);
+
+ devhash = nsc_create_hash();
+ if (!devhash)
+ return;
+
+ for (i = 0; i < devcount; i++) {
+ nsc_insert_node(devhash, &devlist[i], devlist[i].path);
+ }
+}
+
+static int
+compare(const void *va, const void *vb)
+{
+ device_t *a = (device_t *)va;
+ device_t *b = (device_t *)vb;
+
+ return (b->rdev - a->rdev);
+}
+
+static char *
+find_devid(const char *path)
+{
+ device_t key;
+ device_t *result;
+ struct stat buf;
+
+ if (!devlist || !devhash)
+ return (NULL);
+
+ /* See if we already know the device id by this name */
+ result = (device_t *)nsc_lookup(devhash, path);
+ if (result) {
+ return (NULL);
+ }
+
+ /* try to find it by another name */
+ if (stat(path, &buf) < 0)
+ return (NULL);
+
+ key.rdev = buf.st_rdev;
+
+ /* it's storted, so we use the binary-chop method to find it */
+ result = bsearch(&key, devlist, devcount, sizeof (device_t), compare);
+
+ if (result) {
+ return (result->path);
+ }
+
+ return (NULL);
+}
+
+static void
+free_dev_entries()
+{
+ int i;
+ device_t *p;
+
+ if (!devlist) {
+ return;
+ }
+ for (i = 0, p = devlist; i < devcount; i++, p++) {
+ free(p->path);
+ }
+ free(devlist);
+ devlist = NULL;
+ devcount = 0;
+ devalloc = 0;
+
+ if (devhash) {
+ nsc_remove_all(devhash, 0);
+ devhash = NULL;
+ }
+}
diff --git a/usr/src/lib/libdscfg/common/llib-ldscfg b/usr/src/lib/libdscfg/common/llib-ldscfg
new file mode 100644
index 0000000000..00f7f27ba9
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/llib-ldscfg
@@ -0,0 +1,72 @@
+/*
+ * 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
+ */
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/nsctl/cfg.h>
+
+int cfg_get_cstring(CFGFILE *fd, const char *key, void *value, int value_len);
+int cfg_put_cstring(CFGFILE *fd, const char *key, void *value, int value_len);
+int cfg_get_pstring(CFGFILE *fd, const char *key, void *value, int value_len);
+int cfg_put_pstring(CFGFILE *fd, const char *key, void *value, int value_len);
+int cfg_get_options(CFGFILE *fd, int section, const char *basekey,
+ char *tag, int tag_len, char *val, int val_len);
+int cfg_put_options(CFGFILE *fd, int section, const char *basekey,
+ char *tag, char *val);
+int cfg_get_single_option(CFGFILE *fd, int section, const char *basekey,
+ char *tag, char *val, int val_len);
+int cfg_del_option(CFGFILE *fd, int section, const char *basekey, char *tag);
+
+CFGFILE *cfg_open(char *filename);
+void cfg_rewind(CFGFILE *fd, int section);
+int cfg_update_parser_config(CFGFILE *, const char *key, int section);
+char *cfg_error(int *severity);
+char *cfg_location(char *location, int mode, char *altroot);
+void cfg_close(CFGFILE *fd);
+int cfg_lock(CFGFILE *fd, CFGLOCK type);
+void cfg_unlock(CFGFILE *fd);
+int cfg_get_lock(CFGFILE *fd, CFGLOCK *type, pid_t *pid);
+int cfg_commit(CFGFILE *fd);
+void cfg_resource(CFGFILE *fd, const char *resource);
+char *cfg_dgname(const char *path, char *dgname, size_t len);
+char *cfg_l_dgname(const char *path, char *dgname, size_t len);
+int cfg_dgname_islocal(char *dgname, char **node);
+int cfg_iscluster(void);
+int cfg_issuncluster(void);
+int cfg_add_user(CFGFILE *, char *, char *, char *);
+int cfg_rem_user(CFGFILE *, char *, char *, char *);
+int cfg_vol_enable(CFGFILE *, char *, char *, char *);
+int cfg_vol_disable(CFGFILE *, char *, char *, char *);
+int cfg_load_dsvols(CFGFILE *);
+void cfg_unload_dsvols();
+int cfg_load_svols(CFGFILE *);
+void cfg_unload_svols();
+int cfg_load_shadows(CFGFILE *);
+void cfg_unload_shadows();
+int cfg_get_canonical_name(CFGFILE *, const char *, char **);
+int cfg_get_tags(CFGFILE*, char ***);
+char *cfg_get_resource(CFGFILE *);
diff --git a/usr/src/lib/libdscfg/common/mapfile-vers b/usr/src/lib/libdscfg/common/mapfile-vers
new file mode 100644
index 0000000000..2f423e7c5c
--- /dev/null
+++ b/usr/src/lib/libdscfg/common/mapfile-vers
@@ -0,0 +1,93 @@
+# 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.
+#
+#
+#
+# Generic interface definition for usr/src/lib/libcfg.
+#
+# For information regarding the establishment of versioned definitions see:
+# The Linker and Libraries Manual (version 2.5 or greater)
+# This is part of the Developers Guide in the Answerbook. Specifically refer
+# to Chapter 2 under section "Defining Additional Symbols" through section
+# "Reducing Symbol Scope", and Chapter 5 "Versioning".
+#
+# For specific OSNET rules for the modification (evolution) of these version
+# definitions see:
+# Policy for Shared Library Version Names and Interface Definitions
+
+
+SUNWprivate_1.1 {
+ global:
+ cfg_get_cstring;
+ cfg_put_cstring;
+ cfg_find_cstring;
+ cfg_get_options;
+ cfg_put_options;
+ cfg_open;
+ cfg_close;
+ cfg_rewind;
+ cfg_update_parser_config;
+ cfg_lock;
+ cfg_unlock;
+ cfg_get_lock;
+ cfg_resource;
+ cfg_commit;
+ cfg_location;
+ cfg_error;
+ cfg_iscluster;
+ cfg_issuncluster;
+ cfg_dgname;
+ cfg_dgname_islocal;
+ cfg_lfinit;
+ cfg_filelock;
+ cfg_fileunlock;
+ cfg_readpid;
+ cfg_writepid;
+ cfg_lockd_stat;
+ cfg_invalidate_hsizes;
+ cfg_cfg_isempty;
+ cfg_get_section;
+ cfg_get_num_entries;
+ cfg_add_user;
+ cfg_rem_user;
+ cfg_vol_enable;
+ cfg_vol_disable;
+ cfg_load_dsvols;
+ cfg_unload_dsvols;
+ cfg_load_svols;
+ cfg_unload_svols;
+ cfg_load_shadows;
+ cfg_unload_shadows;
+ cfg_get_canonical_name;
+ cfg_get_tags;
+ cfg_is_cfg;
+ cfg_get_srtdsec;
+ cfg_free_section;
+ cfg_shldskip_vtoc;
+ cfg_get_single_option;
+ cfg_del_option;
+ cfg_get_resource;
+ cfg_l_dgname;
+ local:
+ *;
+};