summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdscfg/common/cfg_local.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg_local.c')
-rw-r--r--usr/src/lib/libdscfg/common/cfg_local.c653
1 files changed, 653 insertions, 0 deletions
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);
+}