diff options
| author | John Forte <John.Forte@Sun.COM> | 2008-10-14 15:09:13 -0700 | 
|---|---|---|
| committer | John Forte <John.Forte@Sun.COM> | 2008-10-14 15:09:13 -0700 | 
| commit | fcf3ce441efd61da9bb2884968af01cb7c1452cc (patch) | |
| tree | 0e80d59ad41702571586195bf099ccc14222ce02 /usr/src/lib/libdscfg/common | |
| parent | 247b82a1f1cb5ebd2d163bd9afdb1a3065611962 (diff) | |
| download | illumos-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.c | 3581 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg.h | 180 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_cluster.c | 598 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_cluster.h | 344 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_impl.h | 247 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_local.c | 653 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_local.h | 42 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_lockd.h | 72 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_lockdlck.c | 135 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_lockdmsg.c | 324 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_vols.c | 1286 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/llib-ldscfg | 72 | ||||
| -rw-r--r-- | usr/src/lib/libdscfg/common/mapfile-vers | 93 | 
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: +		*; +}; | 
