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