diff options
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg_local.c')
-rw-r--r-- | usr/src/lib/libdscfg/common/cfg_local.c | 653 |
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); +} |