diff options
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg_vols.c')
| -rw-r--r-- | usr/src/lib/libdscfg/common/cfg_vols.c | 1286 |
1 files changed, 1286 insertions, 0 deletions
diff --git a/usr/src/lib/libdscfg/common/cfg_vols.c b/usr/src/lib/libdscfg/common/cfg_vols.c new file mode 100644 index 0000000000..6f0f8bb447 --- /dev/null +++ b/usr/src/lib/libdscfg/common/cfg_vols.c @@ -0,0 +1,1286 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/mkdev.h> +#include <strings.h> +#include <stdarg.h> +#include <stdlib.h> +#include <locale.h> +#include <errno.h> + +#include <sys/nsctl/cfg.h> + +#include <sys/unistat/spcs_s.h> +#include <sys/unistat/spcs_s_u.h> +#include <sys/unistat/spcs_errors.h> +#include <sys/unistat/spcs_s_impl.h> + +#include <sys/nsctl/sv.h> +#include <sys/nsctl/nsc_hash.h> + +#define DEV_EXPAND 32 + +#define DO_DISABLE 0 +#define DO_ENABLE 1 + +/* + * Utility functions for iiadm and rdcadm/sndradm. + */ + +typedef struct hash_data_s { + union { + char *users; + char *mode; + } u; + char *path; + char *node; + int setno; +} hash_data_t; + +typedef struct { + dev_t rdev; + mode_t mode; + char *path; +} device_t; + +static hash_data_t *make_svol_data(char *, char *, char *, int); +static hash_data_t *make_dsvol_data(char *, char *, char *, int); +static void delete_svol_data(void *); +static void delete_dsvol_data(void *); +static int sv_action(char *, CFGFILE *, char *, int); + +static int add_dev_entry(const char *); +static int compare(const void *, const void *); +static char *find_devid(const char *); +static void free_dev_entries(); +static void rebuild_devhash(); + +static hash_node_t **dsvol; +static int dsvol_loaded = 0; + +static hash_node_t **svol; +static int svol_loaded = 0; + +static hash_node_t **shadowvol; + +static hash_node_t **devhash; +static device_t *devlist; +static int devcount = 0; +static int devalloc = 0; + +/* + * cfg_add_user + * + * Description: + * Adds the calling tool as a user of the volume. + * + * Inputs: + * char *path: The pathname of the volume to be enabled. + * char *cnode: The device group name, or NULL if -C local or not cluster + * CFGFILE *cfg: A pointer to the current config file, or NULL if this + * function is to open/write/commit/close the change itself. + * + * Return values: + * CFG_USER_FIRST: Indicates that this is the first user of this + * particular volume. + * CFG_USER_OK: Indicates that the volume has already been entered into + * the config file. + * CFG_USER_ERR: Indicates that some failure has occurred and no changes + * to the config file have been made. + * CFG_USER_REPEAT: Indicates that this user has already registered for + * the volume. + */ +int +cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user) +{ + int self_open, self_loaded, change_made; + char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; + int retval, rc; + hash_data_t *data; + + self_open = (cfg == NULL); + self_loaded = 0; + change_made = 0; + + if (self_open) { + cfg = cfg_open(NULL); + if (cfg == NULL) { + return (CFG_USER_ERR); + } + + if (!cfg_lock(cfg, CFG_WRLOCK)) { + /* oops */ + cfg_close(cfg); + return (CFG_USER_ERR); + } + } + + /* Check cnode */ + ctag = cfg_get_resource(cfg); + if (cnode) { + if (ctag) { + if (strcmp(cnode, ctag)) + return (CFG_USER_ERR); + } else + cfg_resource(cfg, cnode); + } else + cnode = ctag; + + if (!dsvol_loaded) { + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + self_loaded = 1; + } + + /* find the volume */ + (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); + data = nsc_lookup(dsvol, search_key); + + if (!data) { + /* whoops, not found. Add as new user */ + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode, + user); + rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf)); + if (rc < 0) { + if (self_loaded) { + cfg_unload_dsvols(); + } + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + /* reload hash, if we need to */ + if (!self_loaded) { + cfg_unload_dsvols(); + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + } + retval = CFG_USER_FIRST; + change_made = 1; + } else { + /* Check to ensure we're not already listed */ + char *p = strdup(data->u.users); + char *q = strtok(p, ","); + while (q && (strcmp(q, user) != 0)) { + q = strtok(0, ","); + } + free(p); /* not using data; only testing 'q' ptr */ + + if (!q) { + /* not listed as a user */ + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s", + data->path, data->node, data->u.users, user); + (void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d", + data->setno); + if (cfg_put_cstring(cfg, search_key, buf, + strlen(buf)) < 0) { + if (self_loaded) { + cfg_unload_dsvols(); + } + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + + /* + * Since we deleted an entry from the config + * file, we don't know what all the new + * set numbers are. We need to reload + * everything + */ + if (!self_loaded) { + cfg_unload_dsvols(); + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + } + change_made = 1; + retval = CFG_USER_OK; + } else { + retval = CFG_USER_REPEAT; + } + } + + if (self_loaded) { + cfg_unload_dsvols(); + } + + if (self_open) { + if (change_made) + (void) cfg_commit(cfg); + cfg_close(cfg); + } + + return (retval); +} + +/* + * cfg_rem_user + * + * Description: + * Removes a user from the config file. + * + * Inputs: + * char *path: The pathname of the volume to be enabled. + * char *cnode: The device group name, or NULL if -C local or not cluster + * char *user: The subsystem that is adding this tag (sv, ii, sndr) + * CFGFILE *cfg: A pointer to the current config file, or NULL if this + * function is to open/write/commit/close the change itself. + * Return values: + * CFG_USER_ERR: An error occurred during the processing of this + * directive. + * CFG_USER_OK: User successfully removed; volume in use by other(s). + * CFG_USER_LAST: User successfuly removed; no other users registered + * CFG_USER_GONE: The volume is no longer listed in the dsvol section, + * indicating some sort of application-level error. + * + */ +int +cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user) +{ + int self_open, self_loaded, change_made; + char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; + char cfg_key[ CFG_MAX_KEY ]; + hash_data_t *data; + int retval; + int force_remove; + + self_open = (cfg == NULL); + self_loaded = 0; + change_made = 0; + force_remove = (strcmp(user, "sv") == 0); + + if ('-' == *user) { + ++user; + } + + /* Check cnode */ + ctag = cfg_get_resource(cfg); + if (cnode) { + if (ctag) { + if (strcmp(cnode, ctag)) + return (CFG_USER_ERR); + } else + cfg_resource(cfg, cnode); + } else + cnode = ctag; + + if (self_open) { + cfg = cfg_open(NULL); + if (cfg == NULL) { + return (CFG_USER_ERR); + } + + if (!cfg_lock(cfg, CFG_WRLOCK)) { + /* oops */ + cfg_close(cfg); + return (CFG_USER_ERR); + } + } + + + change_made = 0; + if (!dsvol_loaded) { + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + self_loaded = 1; + } + + /* find the volume */ + (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); + data = nsc_lookup(dsvol, search_key); + + if (!data) { + /* yipes */ + retval = CFG_USER_GONE; + } else if (force_remove) { + retval = CFG_USER_LAST; + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d", + data->setno); + if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) { + if (self_loaded) { + cfg_unload_dsvols(); + } + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + if (!self_loaded) { + cfg_unload_dsvols(); + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + } + } else { + char *p = strdup(data->u.users); + char *q = strtok(p, ","); + int appended = 0; + + (void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path, + data->node); + while (q && (strcmp(q, user) != 0)) { + if (appended) { + strcat(buf, ","); + strcat(buf, q); + } else { + strcat(buf, q); + appended = 1; + } + q = strtok(0, ","); + } + + if (!q) { + /* uh-oh */ + retval = CFG_USER_GONE; + } else { + /* old user skipped; add in remaining users */ + while (q = strtok(0, ", ")) { + if (appended) { + strcat(buf, ","); + strcat(buf, q); + } else { + strcat(buf, q); + appended = 1; + } + } + + if (appended) { + retval = CFG_USER_OK; + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(cfg_key, CFG_MAX_KEY, + "dsvol.set%d", data->setno); + if (cfg_put_cstring(cfg, cfg_key, buf, + strlen(buf)) < 0) { + if (self_loaded) { + cfg_unload_dsvols(); + } + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + if (!self_loaded) { + cfg_unload_dsvols(); + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + } + } else { + retval = CFG_USER_LAST; + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(cfg_key, CFG_MAX_KEY, + "dsvol.set%d", data->setno); + if (cfg_put_cstring(cfg, cfg_key, NULL, + 0) < 0) { + if (self_loaded) { + cfg_unload_dsvols(); + } + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + /* + * Since we deleted an entry from the config + * file, we don't know what all the new + * set numbers are. We need to reload + * everything + */ + if (!self_loaded) { + cfg_unload_dsvols(); + if (cfg_load_dsvols(cfg) < 0) { + if (self_open) { + cfg_close(cfg); + } + return (CFG_USER_ERR); + } + } + } + change_made = 1; + } + } + + if (self_loaded) { + cfg_unload_dsvols(); + } + + if (self_open) { + if (change_made) + (void) cfg_commit(cfg); + cfg_close(cfg); + } + + return (retval); +} + +/* + * Enable a volume under SV control (or add this char *user to the list + * of users of that volume). + * + * Parameters: + * cfg - The config file to use. + * path - The pathname of the volume + * ctag - The cluster tag for this volume (if any) + * user - The user (sv, ii, sndr) of the volume. + */ +int +cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user) +{ + int rc; + int retval; + + if (!ctag || *ctag == '\0') { + ctag = "-"; + } + + retval = -1; + rc = cfg_add_user(cfg, path, ctag, user); + switch (rc) { + case CFG_USER_ERR: + spcs_log("dsvol", NULL, + gettext("unable to set up dsvol section of config for %s"), + path); + break; + case CFG_USER_OK: + retval = 0; + break; + case CFG_USER_FIRST: + /* enable sv! */ + retval = sv_action(path, cfg, ctag, DO_ENABLE); + if (retval < 0) { + (void) cfg_rem_user(cfg, path, ctag, user); + } + break; + default: + spcs_log("dsvol", NULL, + gettext("unexpected return from cfg_add_user(%d)"), rc); + break; + } + + return (retval); +} + +/* + * Disable a volume from SV control (or remove this char *user from the list + * of users of that volume). + * + * Parameters: + * cfg - The config file to use. + * path - The pathname of the volume + * ctag - The cluster tag for this volume (if any) + * user - The user (sv, ii, sndr) of the volume. + */ +int +cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user) +{ + int rc; + int retval; + + if (!ctag || *ctag == '\0') { + ctag = "-"; + } + + retval = -1; + rc = cfg_rem_user(cfg, path, ctag, user); + switch (rc) { + case CFG_USER_ERR: + spcs_log("dsvol", NULL, + gettext("unable to set up dsvol section of config for %s"), + path); + break; + case CFG_USER_OK: + retval = 0; + break; + case CFG_USER_GONE: + spcs_log("dsvol", NULL, + gettext("%s tried to remove non-existent tag for %s"), + user, path); + break; + case CFG_USER_LAST: + /* diable sv! */ + retval = sv_action(path, cfg, ctag, DO_DISABLE); + break; + default: + spcs_log("dsvol", NULL, + gettext("unexpected return from cfg_rem_user(%d)"), rc); + break; + } + + return (retval); +} + +/* + * cfg_load_dsvols + * + * Description: + * Loads the dsvol section of the config file into a giant hash, to + * make searching faster. The important bit to remember is to not + * release the write lock between calling cfg_load_dsvols() and the + * cfg_*_user() functions. + * + * Assumptions: + * 1/ cfg file is open + * 2/ cfg file has been write-locked + * 3/ user of this routine may already be using hcreate/hsearch + * + * Return value: + * -1 if error, or total number of sets found + */ +int +cfg_load_dsvols(CFGFILE *cfg) +{ + int set, rc, entries; + char search_key[ CFG_MAX_KEY ]; + char *buf; + char **entry, *path, *cnode, *users; + hash_data_t *data; + int devs_added = 0; + int offset = 0; + char *ctag = cfg_get_resource(cfg); + if (!ctag || *ctag == '\0') { + ctag = "-"; + } + + dsvol = nsc_create_hash(); + if (!dsvol) { + return (-1); + } + + rc = 0; + cfg_rewind(cfg, CFG_SEC_CONF); + entries = cfg_get_section(cfg, &entry, "dsvol"); + for (set = 1; set <= entries; set++) { + buf = entry[set - 1]; + + /* split up the line */ + if (!(path = strtok(buf, " "))) { + /* oops, now what? */ + free(buf); + break; + } + if (!(cnode = strtok(0, " "))) { + free(buf); + break; + } + if (ctag && (strcmp(cnode, ctag) != 0)) { + ++offset; + free(buf); + continue; + } + + if (!(users = strtok(0, " "))) { + free(buf); + break; + } + + data = make_dsvol_data(path, cnode, users, set - offset); + if (!data) { + free(buf); + break; + } + (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); + rc = nsc_insert_node(dsvol, data, search_key); + if (rc < 0) { + free(buf); + break; + } + + /* we also need to keep track of node information */ + rc = add_dev_entry(path); + if (rc < 0) { + free(buf); + break; + } else if (rc) + ++devs_added; + + free(buf); + rc = 0; + } + + while (set < entries) + free(entry[set++]); + if (entries) + free(entry); + + if (devs_added) { + qsort(devlist, devcount, sizeof (device_t), compare); + rebuild_devhash(); + } + + dsvol_loaded = 1; + return (rc < 0? rc : entries); +} + +/* + * cfg_unload_dsvols + * + * Description: + * Free all memory allocated with cfg_load_dsvols. + */ +void +cfg_unload_dsvols() +{ + if (dsvol) { + nsc_remove_all(dsvol, delete_dsvol_data); + dsvol = 0; + dsvol_loaded = 0; + } +} + +/* + * cfg_load_svols + * + * Description: + * Loads the sv section of the config file into a giant hash, to make + * searching faster. The important bit to remember is to not release + * the write lock between calling cfg_load_svols() and the cfg_*_user() + * functions. + * + * Assumptions: + * 1/ cfg file is open + * 2/ cfg file has been write-locked + * 3/ user of this routine may already be using builtin hcreate/hsearch + */ +int +cfg_load_svols(CFGFILE *cfg) +{ + int set, entries, offset = 0; + char *buf, **entry; + char *path, *mode, *cnode; + hash_data_t *data; + char *ctag = cfg_get_resource(cfg); + if (!ctag || *ctag == '\0') { + ctag = "-"; + } + + svol = nsc_create_hash(); + if (!svol) { + return (-1); + } + + cfg_rewind(cfg, CFG_SEC_CONF); + entries = cfg_get_section(cfg, &entry, "sv"); + for (set = 1; set <= entries; set++) { + buf = entry[set - 1]; + + /* split up the line */ + if (!(path = strtok(buf, " "))) { + free(buf); + break; + } + if (!(mode = strtok(0, " "))) { + free(buf); + break; + } + if (!(cnode = strtok(0, " "))) { + cnode = ""; + } + + if (ctag && (strcmp(cnode, ctag) != 0)) { + ++offset; + free(buf); + continue; + } + + data = make_svol_data(path, mode, cnode, set - offset); + if (!data) { + free(buf); + break; + } + if (nsc_insert_node(svol, data, path) < 0) { + free(buf); + break; + } + free(buf); + } + while (set < entries) + free(entry[set++]); + if (entries) + free(entry); + + svol_loaded = 1; + return (0); +} + +/* + * cfg_unload_svols + * + * Description: + * Frees all memory allocated with cfg_load_dsvols + */ +void +cfg_unload_svols() +{ + if (svol) { + nsc_remove_all(svol, delete_svol_data); + svol = 0; + svol_loaded = 0; + } +} + +/* + * cfg_get_canonical_name + * + * Description: + * Find out whether a device is already known by another name in + * the config file. + * + * Parameters: + * cfg - The config file to use + * path - The pathname of the device + * result - (output) The name it is otherwise known as. This parameter + * must be freed by the caller. + * + * Return values: + * -1: error + * 0: name is as expected, or is not known + * 1: Name is known by different name (stored in 'result') + */ +int +cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result) +{ + int self_loaded; + char *alt_path; + int retval; + + if (devlist) { + self_loaded = 0; + } else { + if (cfg_load_shadows(cfg) < 0) { + return (-1); + } + self_loaded = 1; + } + + /* see if it exists under a different name */ + alt_path = find_devid(path); + if (!alt_path || strcmp(path, alt_path) == 0) { + *result = NULL; + retval = 0; + } else { + /* a-ha */ + *result = strdup(alt_path); + retval = 1; + } + + if (self_loaded) { + free_dev_entries(); + } + + return (retval); +} + +/* + * cfg_load_shadows + * + * Description: + * Load in shadow and bitmap volumes from the II section of the + * config file. SNDR's volumes are handled already by cfg_load_dsvols. + * Not all shadow volumes are listed under dsvol: they can be exported. + * + * Parameters: + * cfg - The config file to use + * + * Return values: + * -1: error + * 0: success + */ +int +cfg_load_shadows(CFGFILE *cfg) +{ + int set, self_loaded, rc, entries; + char *buf, **entry, *ptr; + int devs_added = 0; + + if (dsvol_loaded) { + self_loaded = 0; + } else { + if (cfg_load_dsvols(cfg) < 0) { + return (-1); + } + self_loaded = 1; + } + + shadowvol = nsc_create_hash(); + if (!shadowvol) { + return (-1); + } + + rc = 0; + cfg_rewind(cfg, CFG_SEC_CONF); + entries = cfg_get_section(cfg, &entry, "ii"); + for (set = 1; set <= entries; set++) { + buf = entry[set - 1]; + + /* skip the master vol */ + ptr = strtok(buf, " "); + + /* shadow is next */ + ptr = strtok(NULL, " "); + + rc = add_dev_entry(ptr); + if (rc < 0) { + free(buf); + break; + } else if (rc) + ++devs_added; + + /* and next is bitmap */ + ptr = strtok(NULL, " "); + + rc = add_dev_entry(ptr); + if (rc < 0) { + free(buf); + break; + } else if (rc) + ++devs_added; + rc = 0; + free(buf); + } + while (set < entries) + free(entry[set++]); + if (entries) + free(entry); + + if (self_loaded) { + cfg_unload_dsvols(); + } + + if (devs_added) { + /* sort it, in preparation for lookups */ + qsort(devlist, devcount, sizeof (device_t), compare); + rebuild_devhash(); + } + + return (rc); +} + +void +cfg_unload_shadows() +{ + /* do nothing */ +} + +/* ---------------------------------------------------------------------- */ + +static hash_data_t * +make_dsvol_data(char *path, char *cnode, char *users, int set) +{ + hash_data_t *data; + + data = (hash_data_t *)malloc(sizeof (hash_data_t)); + if (!data) { + return (0); + } + + data->u.users = strdup(users); + data->path = strdup(path); + data->node = strdup(cnode); + data->setno = set; + + return (data); +} + +static void +delete_dsvol_data(void *data) +{ + hash_data_t *p = (hash_data_t *)data; + + free(p->u.users); + free(p->path); + free(p->node); + free(p); +} + +static hash_data_t * +make_svol_data(char *path, char *mode, char *cnode, int set) +{ + hash_data_t *data; + + data = (hash_data_t *)malloc(sizeof (hash_data_t)); + if (!data) { + return (0); + } + + data->u.mode = strdup(mode); + data->path = strdup(path); + data->node = strdup(cnode); + data->setno = set; + + return (data); +} + + +static void +delete_svol_data(void *data) +{ + hash_data_t *p = (hash_data_t *)data; + + free(p->u.mode); + free(p->path); + free(p->node); + free(p); +} + +static int +sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable) +{ + struct stat stb; + sv_conf_t svc; + int fd = -1; + int cfg_changed = 0; + CFGFILE *cfg; + int print_log = 0; + int err = 0, rc; + int sv_ioctl, spcs_err, self_loaded; + char *log_str1, *log_str2; + char key[ CFG_MAX_KEY ]; + char buf[ CFG_MAX_BUF ]; + hash_data_t *node; + device_t *statinfo = 0; + + if (caller_cfg == NULL) { + cfg = cfg_open(NULL); + if (cfg == NULL) + return (-1); + + if (ctag) + cfg_resource(cfg, ctag); + } else + cfg = caller_cfg; + + + self_loaded = 0; + sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE); + log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s")); + log_str2 = (enable? gettext("unable to enable %s") : + gettext("unable to disable %s")); + spcs_err = (enable? SV_EENABLED : SV_EDISABLED); + bzero(&svc, sizeof (svc)); + + if (devhash) + statinfo = nsc_lookup(devhash, path); + + if (statinfo) { + if (!S_ISCHR(statinfo->mode)) + goto error; + svc.svc_major = major(statinfo->rdev); + svc.svc_minor = minor(statinfo->rdev); + } else { + if (stat(path, &stb) != 0) + goto error; + + if (!S_ISCHR(stb.st_mode)) + goto error; + svc.svc_major = major(stb.st_rdev); + svc.svc_minor = minor(stb.st_rdev); + } + + strncpy(svc.svc_path, path, sizeof (svc.svc_path)); + + fd = open(SV_DEVICE, O_RDONLY); + if (fd < 0) + goto error; + + svc.svc_flag = (NSC_DEVICE | NSC_CACHE); + svc.svc_error = spcs_s_ucreate(); + + do { + rc = ioctl(fd, sv_ioctl, &svc); + } while (rc < 0 && errno == EINTR); + + if (rc < 0) { + if (errno != spcs_err) { + spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path); + if (enable) + goto error; + else + err = errno; + } else + err = spcs_err; + } + + spcs_log("sv", NULL, log_str1, svc.svc_path); + + /* SV enable succeeded */ + if (caller_cfg == NULL) /* was not previously locked */ + if (!cfg_lock(cfg, CFG_WRLOCK)) + goto error; + + if (err != spcs_err) { /* already enabled, already in config */ + if (enable) { + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path, + ctag? ctag : "-"); + if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) { + /* SV config not updated, so SV disable again */ + (void) ioctl(fd, SVIOC_DISABLE, &svc); + print_log++; + } else + cfg_changed = 1; + } else { + /* pull it out of the config */ + if (!svol_loaded) { + if (cfg_load_svols(cfg) < 0) { + if (NULL == caller_cfg) { + cfg_close(cfg); + } + return (-1); + } + self_loaded = 1; + } + node = nsc_lookup(svol, svc.svc_path); + if (node) { + cfg_rewind(cfg, CFG_SEC_CONF); + (void) snprintf(key, CFG_MAX_KEY, "sv.set%d", + node->setno); + if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) { + spcs_log("sv", NULL, + gettext("failed to remove %s from " + "sv config"), svc.svc_path); + } + /* + * Since we deleted an entry from the config + * file, we don't know what all the new + * set numbers are. We need to reload + * everything + */ + if (!self_loaded) { + cfg_unload_svols(); + if (cfg_load_svols(cfg) < 0) { + if (NULL == caller_cfg) { + cfg_close(cfg); + } + return (-1); + } + } + cfg_changed = 1; + } + if (self_loaded) { + cfg_unload_svols(); + self_loaded = 0; + } + } + } + +#ifdef lint + (void) printf("extra line to shut lint up %s\n", module_names[0]); +#endif + +error: + if (fd >= 0) + (void) close(fd); + + if (cfg == NULL) + return (-1); + + if (cfg_changed) + if (caller_cfg == NULL) /* we opened config */ + (void) cfg_commit(cfg); + + if (caller_cfg == NULL) + cfg_close(cfg); + if ((cfg_changed) || (err == spcs_err)) + return (1); + if (print_log) + spcs_log("sv", NULL, + gettext("unable to add to configuration, disabled %s"), + svc.svc_path); + spcs_s_ufree(&svc.svc_error); + + return (-1); +} + +/* + * add_dev_entry + * + * Add an entry into the devlist and the devhash for future lookups. + * + * Return values: + * -1 An error occurred. + * 0 Entry added + * 1 Entry already exists. + */ +static int +add_dev_entry(const char *path) +{ + struct stat buf; + device_t *newmem; + hash_data_t *data; + + if (!devhash) { + devhash = nsc_create_hash(); + if (!devhash) { + return (-1); + } + } else { + data = nsc_lookup(devhash, path); + if (data) { + return (1); + } + } + + if (stat(path, &buf) < 0) { + /* ignore error, we are most likely deleting entry anyway */ + buf.st_rdev = 0; + } + + if (devcount >= devalloc) { + /* make some room */ + devalloc += DEV_EXPAND; + newmem = (device_t *)realloc(devlist, devalloc * + sizeof (device_t)); + if (!newmem) { + free_dev_entries(); + return (-1); + } else { + devlist = newmem; + } + } + + devlist[ devcount ].path = strdup(path); + devlist[ devcount ].rdev = buf.st_rdev; + devlist[ devcount ].mode = buf.st_mode; + + if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) { + return (-1); + } + + ++devcount; + return (0); +} + +static void +rebuild_devhash() +{ + int i; + + if (!devhash) + nsc_remove_all(devhash, 0); + + devhash = nsc_create_hash(); + if (!devhash) + return; + + for (i = 0; i < devcount; i++) { + nsc_insert_node(devhash, &devlist[i], devlist[i].path); + } +} + +static int +compare(const void *va, const void *vb) +{ + device_t *a = (device_t *)va; + device_t *b = (device_t *)vb; + + return (b->rdev - a->rdev); +} + +static char * +find_devid(const char *path) +{ + device_t key; + device_t *result; + struct stat buf; + + if (!devlist || !devhash) + return (NULL); + + /* See if we already know the device id by this name */ + result = (device_t *)nsc_lookup(devhash, path); + if (result) { + return (NULL); + } + + /* try to find it by another name */ + if (stat(path, &buf) < 0) + return (NULL); + + key.rdev = buf.st_rdev; + + /* it's storted, so we use the binary-chop method to find it */ + result = bsearch(&key, devlist, devcount, sizeof (device_t), compare); + + if (result) { + return (result->path); + } + + return (NULL); +} + +static void +free_dev_entries() +{ + int i; + device_t *p; + + if (!devlist) { + return; + } + for (i = 0, p = devlist; i < devcount; i++, p++) { + free(p->path); + } + free(devlist); + devlist = NULL; + devcount = 0; + devalloc = 0; + + if (devhash) { + nsc_remove_all(devhash, 0); + devhash = NULL; + } +} |
