summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdscfg/common/cfg_vols.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdscfg/common/cfg_vols.c')
-rw-r--r--usr/src/lib/libdscfg/common/cfg_vols.c1286
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;
+ }
+}