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