/*
* 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);
}