summaryrefslogtreecommitdiff
path: root/usr/src/cmd/avs/sv/svadm.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/avs/sv/svadm.c')
-rw-r--r--usr/src/cmd/avs/sv/svadm.c1515
1 files changed, 1515 insertions, 0 deletions
diff --git a/usr/src/cmd/avs/sv/svadm.c b/usr/src/cmd/avs/sv/svadm.c
new file mode 100644
index 0000000000..4fee03b277
--- /dev/null
+++ b/usr/src/cmd/avs/sv/svadm.c
@@ -0,0 +1,1515 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/mkdev.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <stdio.h>
+#include <locale.h>
+#include <unistd.h>
+#include <search.h>
+#include <libgen.h>
+#include <nsctl.h>
+
+#include <sys/unistat/spcs_s.h>
+#include <sys/unistat/spcs_s_u.h>
+#include <sys/unistat/spcs_errors.h>
+
+#include <sys/nsctl/sv.h>
+#include <sys/nsctl/sv_impl.h>
+
+#include <sys/nsctl/cfg.h>
+#include <sys/nsctl/nsc_hash.h>
+
+#include "../sv/svadm.h"
+
+
+static int sv_max_devices;
+
+
+/*
+ * support for the special cluster tag "local" to be used with -C in a
+ * cluster for local volumes.
+ */
+
+#define SV_LOCAL_TAG "local"
+
+static int sv_islocal;
+
+/*
+ * libcfg access.
+ */
+
+static CFGFILE *cfg; /* libcfg file pointer */
+static int cfg_changed; /* set to 1 if we need to commit changes */
+
+static char *cfg_cluster_tag; /* local cluster tag */
+
+static char *implicit_tag; /* implicit cluster tag */
+
+
+/*
+ * Print width for print_sv() output.
+ */
+
+#define STATWIDTH (SV_MAXPATH / 2)
+
+/*
+ * Pathnames.
+ */
+
+static const caddr_t sv_rpath = SV_DEVICE;
+
+/*
+ * Functions.
+ */
+
+static int read_config_file(const caddr_t, sv_name_t []);
+static int enable_dev(sv_name_t *);
+static int disable_dev(const caddr_t);
+static void error(spcs_s_info_t *, caddr_t, ...);
+static void create_cfg_hash();
+static int find_in_hash(char *path);
+static void destroy_hashtable();
+static void remove_from_cfgfile(char *path, int setnumber);
+
+static caddr_t program;
+
+static void
+sv_cfg_open(CFGLOCK mode)
+{
+ if (cfg != NULL)
+ return;
+
+ cfg = cfg_open(NULL);
+ if (cfg == NULL) {
+ error(NULL, gettext("unable to access the configuration"));
+ /* NOTREACHED */
+ }
+
+ if (cfg_cluster_tag && *cfg_cluster_tag) {
+ cfg_resource(cfg, cfg_cluster_tag);
+ } else {
+ cfg_resource(cfg, NULL);
+ }
+ if (!cfg_lock(cfg, mode)) {
+ error(NULL, gettext("unable to lock the configuration"));
+ /* NOTREACHED */
+ }
+}
+
+
+static void
+sv_cfg_close(void)
+{
+ if (cfg == NULL)
+ return;
+
+ if (cfg_changed) {
+ (void) cfg_commit(cfg);
+ cfg_changed = 0;
+ }
+
+ cfg_close(cfg);
+ cfg = NULL;
+}
+
+
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext("usage:\n"));
+
+ (void) fprintf(stderr, gettext(
+ "\t%s -h help\n"), program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] display status\n"),
+ program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] -i display "
+ "extended status\n"), program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] -v display "
+ "version number\n"), program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] -e { -f file | volume } enable\n"), program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] -d { -f file | volume } disable\n"), program);
+
+ (void) fprintf(stderr, gettext(
+ "\t%s [-C tag] -r { -f file | volume } reconfigure\n"), program);
+
+ sv_cfg_close();
+}
+
+static void
+message(caddr_t prefix, spcs_s_info_t *status, caddr_t string, va_list ap)
+{
+ (void) fprintf(stderr, "%s: %s: ", program, prefix);
+ (void) vfprintf(stderr, string, ap);
+ (void) fprintf(stderr, "\n");
+
+ if (status) {
+ spcs_s_report(*status, stderr);
+ spcs_s_ufree(status);
+ }
+}
+
+
+static void
+error(spcs_s_info_t *status, caddr_t string, ...)
+{
+ va_list ap;
+ va_start(ap, string);
+
+ message(gettext("error"), status, string, ap);
+
+ va_end(ap);
+
+ sv_cfg_close();
+ exit(1);
+}
+
+
+static void
+warn(spcs_s_info_t *status, caddr_t string, ...)
+{
+ va_list ap;
+ va_start(ap, string);
+
+ message(gettext("warning"), status, string, ap);
+
+ va_end(ap);
+}
+
+
+static void
+sv_get_maxdevs(void)
+{
+ sv_name_t svn[1];
+ sv_list_t svl;
+ int fd;
+
+ if (sv_max_devices > 0)
+ return;
+
+ fd = open(sv_rpath, O_RDONLY);
+ if (fd < 0)
+ error(NULL, gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+
+ bzero(&svl, sizeof (svl));
+ bzero(&svn[0], sizeof (svn));
+
+ svl.svl_names = &svn[0];
+ svl.svl_error = spcs_s_ucreate();
+
+ if (ioctl(fd, SVIOC_LIST, &svl) < 0) {
+ (void) close(fd);
+ error(&svl.svl_error, gettext("unable to get max devs"));
+ }
+
+ spcs_s_ufree(&svl.svl_error);
+ sv_max_devices = svl.svl_maxdevs;
+
+ (void) close(fd);
+}
+
+
+static sv_name_t *
+sv_alloc_svnames(void)
+{
+ sv_name_t *svn = NULL;
+
+ sv_get_maxdevs();
+
+ svn = calloc(sv_max_devices, sizeof (*svn));
+ if (svn == NULL) {
+ error(NULL, "unable to allocate %ld bytes of memory",
+ sv_max_devices * sizeof (*svn));
+ }
+
+ return (svn);
+}
+
+
+static void
+sv_check_dgislocal(char *dgname)
+{
+ char *othernode;
+ int rc;
+
+ /*
+ * check where this disk service is mastered
+ */
+
+ rc = cfg_dgname_islocal(dgname, &othernode);
+ if (rc < 0) {
+ error(NULL, gettext("unable to find "
+ "disk service, %s: %s"), dgname, strerror(errno));
+ }
+
+ if (rc == 0) {
+ error(NULL, gettext("disk service, %s, is "
+ "active on node \"%s\"\nPlease re-issue "
+ "the command on that node"), dgname, othernode);
+ }
+}
+
+
+/*
+ * Carry out cluster based checks for a specified volume, or just
+ * global options.
+ */
+static void
+sv_check_cluster(char *path)
+{
+ char dgname[CFG_MAX_BUF];
+ static int sv_iscluster = -1; /* set to 1 if running in a cluster */
+
+ /*
+ * Find out if we are running in a cluster
+ */
+ if (sv_iscluster == -1) {
+ if ((sv_iscluster = cfg_iscluster()) < 0) {
+ error(NULL, gettext("unable to ascertain environment"));
+ }
+ }
+
+ if (!sv_iscluster && cfg_cluster_tag != NULL) {
+ error(NULL, gettext("-C is not valid when not in a cluster"));
+ }
+
+ if (!sv_iscluster || sv_islocal || path == NULL) {
+ return;
+ }
+
+
+ /*
+ * Cluster-only checks on pathname
+ */
+ if (cfg_dgname(path, dgname, sizeof (dgname)) == NULL) {
+ error(NULL, gettext("unable to determine "
+ "disk group name for %s"), path);
+ return;
+ }
+
+ if (cfg_cluster_tag != NULL) {
+ /*
+ * Do dgislocal check now in case path did not contain
+ * a dgname.
+ *
+ * E.g. adding a /dev/did/ device to a disk service.
+ */
+
+ sv_check_dgislocal(cfg_cluster_tag);
+ }
+
+ if (strcmp(dgname, "") == 0)
+ return; /* NULL dgname is valid */
+
+ if (cfg_cluster_tag == NULL) {
+ /*
+ * Implicitly set the cluster tag to dgname
+ */
+
+ sv_check_dgislocal(dgname);
+
+ if (implicit_tag) {
+ free(implicit_tag);
+ implicit_tag = NULL;
+ }
+
+ implicit_tag = strdup(dgname);
+ if (implicit_tag == NULL) {
+ error(NULL,
+ gettext("unable to allocate memory "
+ "for cluster tag"));
+ }
+ } else {
+ /*
+ * Check dgname and cluster tag from -C are the same.
+ */
+
+ if (strcmp(dgname, cfg_cluster_tag) != 0) {
+ error(NULL,
+ gettext("-C (%s) does not match disk group "
+ "name (%s) for %s"), cfg_cluster_tag,
+ dgname, path);
+ }
+
+ /*
+ * sv_check_dgislocal(cfg_cluster_tag) was called above.
+ */
+ }
+}
+
+
+static void
+print_version(void)
+{
+ sv_version_t svv;
+ int fd;
+
+ bzero(&svv, sizeof (svv));
+ svv.svv_error = spcs_s_ucreate();
+
+ fd = open(sv_rpath, O_RDONLY);
+ if (fd < 0) {
+ warn(NULL, gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+ return;
+ }
+
+ if (ioctl(fd, SVIOC_VERSION, &svv) != 0) {
+ error(&svv.svv_error,
+ gettext("unable to read the version number"));
+ /* NOTREACHED */
+ }
+
+ spcs_s_ufree(&svv.svv_error);
+#ifdef DEBUG
+ (void) printf(gettext("Storage Volume version %d.%d.%d.%d\n"),
+ svv.svv_major_rev, svv.svv_minor_rev,
+ svv.svv_micro_rev, svv.svv_baseline_rev);
+#else
+ if (svv.svv_micro_rev) {
+ (void) printf(gettext("Storage Volume version %d.%d.%d\n"),
+ svv.svv_major_rev, svv.svv_minor_rev, svv.svv_micro_rev);
+ } else {
+ (void) printf(gettext("Storage Volume version %d.%d\n"),
+ svv.svv_major_rev, svv.svv_minor_rev);
+ }
+#endif
+
+ (void) close(fd);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern int optind;
+ extern char *optarg;
+ char *conf_file = NULL;
+ int enable, disable, compare, print, version;
+ int opt, Cflag, fflag, iflag;
+ int rc;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain("svadm");
+
+ program = strdup(basename(argv[0]));
+
+ Cflag = fflag = iflag = 0;
+ compare = enable = disable = version = 0;
+
+ print = 1;
+
+ while ((opt = getopt(argc, argv, "C:def:hirv")) != EOF) {
+ switch (opt) {
+
+ case 'C':
+ if (Cflag) {
+ warn(NULL,
+ gettext("-C specified multiple times"));
+ usage();
+ exit(2);
+ /* NOTREACHED */
+ }
+
+ Cflag++;
+ cfg_cluster_tag = optarg;
+ break;
+
+ case 'e':
+ print = 0;
+ enable++;
+ break;
+
+ case 'd':
+ print = 0;
+ disable++;
+ break;
+
+ case 'f':
+ fflag++;
+ conf_file = optarg;
+ break;
+
+ case 'i':
+ iflag++;
+ break;
+
+ case 'r':
+ /* Compare running system with sv.cf */
+ print = 0;
+ compare++;
+ break;
+
+ case 'v':
+ print = 0;
+ version++;
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ default:
+ usage();
+ exit(2);
+ /* NOTREACHED */
+ }
+ }
+
+
+ /*
+ * Usage checks
+ */
+
+ if ((enable + disable + compare) > 1) {
+ warn(NULL, gettext("-d, -e and -r are mutually exclusive"));
+ usage();
+ exit(2);
+ }
+
+ if (fflag && (print || version)) {
+ warn(NULL, gettext("-f is only valid with -d, -e or -r"));
+ usage();
+ exit(2);
+ }
+
+ if (fflag && optind != argc) {
+ usage();
+ exit(2);
+ }
+
+ if (print || version) {
+ /* check for no more args */
+
+ if (optind != argc) {
+ usage();
+ exit(2);
+ }
+ } else {
+ /* check for inline args */
+
+ if (!fflag && (argc - optind) != 1) {
+ usage();
+ exit(2);
+ }
+ }
+
+ if (!print && iflag) {
+ usage();
+ exit(2);
+ }
+
+
+ /*
+ * Check for the special cluster tag and convert into the
+ * internal representation.
+ */
+
+ if (cfg_cluster_tag != NULL &&
+ strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0) {
+ cfg_cluster_tag = "-";
+ sv_islocal = 1;
+ }
+
+
+ /*
+ * Process commands
+ */
+
+ if (optind != argc) {
+ /* deal with inline volume argument */
+
+ rc = 0;
+ if (enable)
+ rc = enable_one_sv(argv[optind]);
+ else if (disable)
+ rc = disable_one_sv(argv[optind]);
+ else /* if (compare) */
+ compare_one_sv(argv[optind]);
+
+ if (rc != 0)
+ return (1);
+
+ return (0);
+ }
+
+ rc = 0;
+ if (enable)
+ rc = enable_sv(conf_file);
+ else if (disable)
+ rc = disable_sv(conf_file);
+ else if (compare)
+ compare_sv(conf_file);
+ else if (print)
+ print_sv(iflag);
+ else /* if (version) */
+ print_version();
+
+ if (rc != 0)
+ return (1);
+
+ return (0);
+}
+
+
+
+/* LINT - not static as fwcadm uses it */
+static int
+enable_sv(char *conf_file)
+{
+ int index;
+ sv_name_t *svn;
+ int cnt;
+ int rc, ret;
+
+ svn = sv_alloc_svnames();
+
+ index = read_config_file(conf_file, svn);
+
+ rc = ret = 0;
+
+ for (cnt = 0; cnt < index; cnt++) {
+
+ /*
+ * Check for more data.
+ */
+ if (svn[cnt].svn_path[0] == '\0') {
+ /*
+ * This was set when reading sv.conf. After the last
+ * line svn_path was set to \0, so we are finished.
+ * We shouldn't get here, but put this in just in
+ * case.
+ */
+ break;
+ }
+ rc = enable_dev(&svn[cnt]);
+ if (rc && !ret)
+ ret = rc;
+ }
+
+ sv_cfg_close();
+
+ return (ret);
+}
+
+
+/* LINT - not static as fwcadm uses it */
+static int
+enable_one_sv(caddr_t path)
+{
+ sv_name_t svn;
+ int rc;
+
+ sv_get_maxdevs();
+
+ bzero(&svn, sizeof (svn));
+ (void) strncpy(svn.svn_path, path, sizeof (svn.svn_path));
+ svn.svn_mode = (NSC_DEVICE | NSC_CACHE);
+
+ /* force NULL termination */
+ svn.svn_path[sizeof (svn.svn_path) - 1] = '\0';
+
+ rc = enable_dev(&svn);
+ sv_cfg_close();
+
+ return (rc);
+}
+
+
+static int
+enable_dev(sv_name_t *svn)
+{
+ char buf[CFG_MAX_BUF];
+ struct stat stb;
+ sv_conf_t svc;
+ int fd;
+ int sev;
+ int rc;
+ char *lcltag;
+ char *altname;
+
+ sv_check_cluster(svn->svn_path);
+ sv_cfg_open(CFG_WRLOCK);
+
+ bzero(&svc, sizeof (svc));
+
+ if (stat(svn->svn_path, &stb) != 0) {
+ warn(NULL, gettext("unable to access %s: %s"),
+ svn->svn_path, strerror(errno));
+ return (1);
+ }
+
+ if (!S_ISCHR(stb.st_mode)) {
+ warn(NULL, gettext("%s is not a character device - ignored"),
+ svn->svn_path);
+ return (1);
+ }
+
+ svc.svc_major = major(stb.st_rdev);
+ svc.svc_minor = minor(stb.st_rdev);
+ (void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path));
+
+ fd = open(sv_rpath, O_RDONLY);
+ if (fd < 0) {
+ warn(NULL, gettext("unable to open %s: %s"),
+ svn->svn_path, strerror(errno));
+ return (1);
+ }
+
+ svc.svc_flag = svn->svn_mode;
+ svc.svc_error = spcs_s_ucreate();
+
+ /* first, check for duplicates */
+ rc = cfg_get_canonical_name(cfg, svn->svn_path, &altname);
+ if (rc < 0) {
+ spcs_log("sv", NULL, gettext("Unable to parse config file"));
+ warn(NULL, gettext("Unable to parse config file"));
+ (void) close(fd);
+ return (1);
+ }
+ if (rc) {
+ error(NULL, gettext("'%s' has already been configured as "
+ "'%s'. Re-enter command with the latter name."),
+ svn->svn_path, altname);
+ }
+
+ /* secondly, try to insert it into the dsvol config */
+ if (implicit_tag && *implicit_tag) {
+ lcltag = implicit_tag;
+ } else if (cfg_cluster_tag && *cfg_cluster_tag) {
+ lcltag = cfg_cluster_tag;
+ } else {
+ lcltag = "-";
+ }
+ rc = cfg_add_user(cfg, svn->svn_path, lcltag, "sv");
+ if (CFG_USER_ERR == rc) {
+ spcs_log("sv", NULL,
+ gettext("%s: unable to put %s into dsvol cfg"),
+ program, svn->svn_path);
+ warn(NULL, gettext("unable to put %s into dsvol cfg"),
+ svn->svn_path);
+ (void) close(fd);
+ return (1);
+ }
+ cfg_changed = 1;
+
+ if (CFG_USER_OK == rc) {
+ /* success */
+ (void) close(fd);
+ return (0);
+ }
+
+ if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) {
+ if ((CFG_USER_REPEAT == rc) && (SV_EENABLED == errno)) {
+ /* it's ok -- we were just double-checking */
+ (void) close(fd);
+ return (0);
+ }
+
+ spcs_log("sv", &svc.svc_error,
+ gettext("%s: unable to enable %s"),
+ program, svn->svn_path);
+
+ warn(&svc.svc_error, gettext("unable to enable %s"),
+ svn->svn_path);
+
+ /* remove it from dsvol, if we're the ones who put it in */
+ if (CFG_USER_FIRST == rc) {
+ (void) cfg_rem_user(cfg, svn->svn_path, lcltag, "sv");
+ }
+ (void) close(fd);
+ return (1);
+ }
+
+ spcs_log("sv", NULL, gettext("%s: enabled %s"),
+ program, svn->svn_path);
+
+ if (implicit_tag != NULL) {
+#ifdef DEBUG
+ if (cfg_cluster_tag != NULL) {
+ error(NULL,
+ gettext("enable_dev: -C %s AND implicit_tag %s!"),
+ cfg_cluster_tag, implicit_tag);
+ }
+#endif
+
+ (void) snprintf(buf, sizeof (buf), "%s - %s",
+ svc.svc_path, implicit_tag);
+ } else {
+ (void) strcpy(buf, svc.svc_path);
+ }
+
+ rc = 0;
+ if (cfg_put_cstring(cfg, "sv", buf, sizeof (buf)) < 0) {
+ warn(NULL,
+ gettext("unable to add %s to configuration storage: %s"),
+ svc.svc_path, cfg_error(&sev));
+ rc = 1;
+ }
+
+ cfg_changed = 1;
+ spcs_s_ufree(&svc.svc_error);
+ (void) close(fd);
+
+ return (rc);
+}
+
+
+/*
+ * This routine parses the config file passed in via conf_file and
+ * stores the data in the svn array. The return value is the number
+ * of entries read from conf_file. If an error occurs the error()
+ * routine is called (which exits the program).
+ */
+static int
+read_config_file(const caddr_t conf_file, sv_name_t svn[])
+{
+ char line[1024], rdev[1024], junk[1024];
+ struct stat stb;
+ int lineno;
+ int cnt, i;
+ int index = 0; /* Current location in svn array */
+ sv_name_t *cur_svn; /* Pointer to svn[index] */
+ FILE *fp;
+
+ if (access(conf_file, R_OK) != 0 ||
+ stat(conf_file, &stb) != 0 ||
+ !S_ISREG(stb.st_mode)) {
+ error(NULL, gettext("cannot read config file %s"), conf_file);
+ }
+
+ if ((fp = fopen(conf_file, "r")) == NULL) {
+ error(NULL, gettext("unable to open config file %s: %s"),
+ conf_file, strerror(errno));
+ }
+
+ lineno = 0;
+
+ while (fgets(line, sizeof (line), fp) != NULL) {
+ lineno++;
+
+ i = strlen(line);
+
+ if (i < 1)
+ continue;
+
+ if (line[i-1] == '\n')
+ line[i-1] = '\0';
+ else if (i == (sizeof (line) - 1)) {
+ warn(NULL, gettext(
+ "line %d: line too long -- should be less than %d characters"),
+ lineno, (sizeof (line) - 1));
+ warn(NULL, gettext("line %d: ignored"), lineno);
+ }
+
+ /*
+ * check for comment line.
+ */
+ if (line[0] == '#')
+ continue;
+
+ cnt = sscanf(line, "%s %s", rdev, junk);
+
+ if (cnt != 1 && cnt != 2) {
+ if (cnt > 0) {
+ warn(NULL, gettext("line %d: invalid format"),
+ lineno);
+ warn(NULL, gettext("line %d: ignored"), lineno);
+ }
+ continue;
+ }
+
+ rdev[sizeof (rdev) - 1] = '\0';
+
+ cur_svn = &svn[index]; /* For easier reading below */
+
+ if (strlen(rdev) >= sizeof (cur_svn->svn_path)) {
+ warn(NULL, gettext(
+ "line %d: raw device name (%s) longer than %d characters"),
+ lineno, rdev,
+ (sizeof (cur_svn->svn_path) - 1));
+ warn(NULL, gettext("line %d: ignored"), lineno);
+ continue;
+ }
+
+ (void) strcpy(cur_svn->svn_path, rdev);
+ cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE);
+
+ index++;
+ }
+
+ /* Set the last path to NULL */
+ svn[index].svn_path[0] = '\0';
+
+ (void) fclose(fp);
+
+ return (index);
+}
+
+
+/*
+ * Disable the device from the kernel configuration.
+ *
+ * RETURN:
+ * 0 on success
+ * non-zero on failure.
+ *
+ * Failures are reported to the user.
+ */
+static int
+disable_dev(const caddr_t path)
+{
+ struct stat stb;
+ sv_conf_t svc;
+ int fd;
+
+ sv_check_cluster(path);
+
+ if (stat(path, &stb) < 0) {
+ svc.svc_major = (major_t)-1;
+ svc.svc_minor = (minor_t)-1;
+ } else {
+ svc.svc_major = major(stb.st_rdev);
+ svc.svc_minor = minor(stb.st_rdev);
+ }
+
+ if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
+ warn(NULL, gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+ return (-1);
+ }
+
+ (void) strcpy(svc.svc_path, path);
+ svc.svc_error = spcs_s_ucreate();
+
+ /*
+ * Issue the ioctl to attempt to disable this device. Note that all
+ * the libdscfg details are handled elsewhere.
+ */
+ if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) {
+ if (errno != SV_EDISABLED) {
+ spcs_log("sv", &svc.svc_error,
+ gettext("%s: unable to disable %s"),
+ program, path);
+
+ warn(&svc.svc_error,
+ gettext("unable to disable %s"), path);
+ (void) close(fd);
+ return (-1);
+ }
+ }
+
+ spcs_log("sv", NULL, gettext("%s: disabled %s"), program, path);
+
+ spcs_s_ufree(&svc.svc_error);
+ (void) close(fd);
+
+ return (0);
+}
+
+
+static void
+print_cluster_tag(const int setnumber)
+{
+ char buf[CFG_MAX_BUF];
+ char key[CFG_MAX_KEY];
+
+ bzero(buf, sizeof (buf));
+ (void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
+
+ (void) cfg_get_cstring(cfg, key, buf, sizeof (buf));
+
+ if (*buf != '\0') {
+ if (strcmp(buf, "-") == 0) {
+ (void) printf(" [%s]", gettext("local to node"));
+ } else {
+ (void) printf(" [%s: %s]", gettext("cluster"), buf);
+ }
+ }
+}
+
+
+/* LINT - not static as fwcadm uses it */
+static void
+print_sv(int verbose)
+{
+ sv_name_t *svn, *svn_system; /* Devices in system */
+ sv_list_t svl_system;
+ int fd, i;
+ int setnumber;
+
+ sv_check_cluster(NULL);
+ sv_cfg_open(CFG_RDLOCK);
+
+ svn_system = sv_alloc_svnames();
+
+ if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
+ (void) printf(gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+ return;
+ }
+
+ /* Grab the system list from the driver */
+ svl_system.svl_count = sv_max_devices;
+ svl_system.svl_names = &svn_system[0];
+ svl_system.svl_error = spcs_s_ucreate();
+
+ if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
+ error(&svl_system.svl_error, gettext("unable to get list"));
+ }
+
+ spcs_s_ufree(&svl_system.svl_error);
+ (void) close(fd);
+
+ /*
+ * We build a hashmap out of the entries from the config file to make
+ * searching faster. We end up taking a performance hit when the # of
+ * volumes is small, but for larger configurations it's a
+ * HUGE improvement.
+ */
+
+ /* build the hashtable */
+ cfg_rewind(cfg, CFG_SEC_CONF);
+ create_cfg_hash();
+
+ /*
+ * For each volume found from the kernel, print out
+ * info about it from the kernel.
+ */
+ for (i = 0; i < svl_system.svl_count; i++) {
+ if (*svn_system[i].svn_path == '\0') {
+ break;
+ }
+
+ svn = &svn_system[i];
+ if (svn->svn_mode == 0) {
+#ifdef DEBUG
+ (void) printf(gettext("%s [kernel guard]\n"),
+ svn->svn_path);
+#endif
+ continue;
+ }
+ /* get sv entry from the hashtable */
+ if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
+ (void) printf("%-*s", STATWIDTH, svn->svn_path);
+
+ if (verbose) {
+ print_cluster_tag(setnumber);
+ }
+
+ (void) printf("\n");
+
+ } else {
+ /*
+ * We didn't find the entry in the hashtable. Let
+ * the user know that the persistent storage is
+ * inconsistent with the kernel configuration.
+ */
+ if (cfg_cluster_tag == NULL)
+ warn(NULL, gettext(
+ "%s is configured, but not in the "
+ "config storage"), svn->svn_path);
+ }
+ }
+
+ /* free up the hashtable */
+ destroy_hashtable();
+
+ sv_cfg_close();
+}
+
+
+/* LINT - not static as fwcadm uses it */
+static int
+disable_sv(char *conf_file)
+{
+ sv_name_t *svn, *svn_system; /* Devices in system */
+ sv_list_t svl_system;
+ int fd, i, setnumber;
+ int rc, ret;
+
+ svn_system = sv_alloc_svnames();
+
+ rc = ret = 0;
+
+ if (conf_file == NULL) {
+ if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
+ (void) printf(gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+ return (1);
+ }
+
+ /* Grab the system list from the driver */
+ svl_system.svl_count = sv_max_devices;
+ svl_system.svl_names = &svn_system[0];
+ svl_system.svl_error = spcs_s_ucreate();
+
+ if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
+ error(&(svl_system.svl_error),
+ gettext("unable to get list"));
+ }
+
+ spcs_s_ufree(&(svl_system.svl_error));
+ (void) close(fd);
+ } else {
+ svl_system.svl_count = read_config_file(conf_file, svn_system);
+ }
+
+
+ for (i = 0; i < svl_system.svl_count; i++) {
+ if (*svn_system[i].svn_path == '\0')
+ break;
+
+ svn = &svn_system[i];
+
+ sv_check_cluster(svn->svn_path);
+ sv_cfg_open(CFG_WRLOCK);
+ create_cfg_hash();
+ rc = 0;
+ if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
+ if ((rc = disable_dev(svn->svn_path)) != -1) {
+ remove_from_cfgfile(svn->svn_path, setnumber);
+ } else if (errno == SV_ENODEV) {
+ remove_from_cfgfile(svn->svn_path, setnumber);
+ }
+ } else {
+ /* warn the user that we didn't find it in cfg file */
+ warn(NULL, gettext(
+ "%s was not found in the config storage"),
+ svn->svn_path);
+ /* try to disable anyway */
+ (void) disable_dev(svn->svn_path);
+ rc = 1;
+ }
+
+ sv_cfg_close();
+ destroy_hashtable();
+
+ if (rc && !ret)
+ ret = rc;
+ }
+
+ return (ret);
+}
+
+
+/* LINT - not static as fwcadm uses it */
+static int
+disable_one_sv(char *path)
+{
+ int setnumber;
+ int rc;
+
+ sv_get_maxdevs();
+ sv_check_cluster(path);
+ sv_cfg_open(CFG_WRLOCK);
+
+ create_cfg_hash();
+ if ((setnumber = find_in_hash(path)) != -1) {
+ /* remove from kernel */
+ if ((rc = disable_dev(path)) == 0) {
+ /* remove the cfgline */
+ remove_from_cfgfile(path, setnumber);
+ } else if (errno == SV_ENODEV) {
+ remove_from_cfgfile(path, setnumber);
+ }
+ } else {
+ /* warn the user that we didn't find it in cfg file */
+ warn(NULL,
+ gettext("%s was not found in the config storage"), path);
+ /* still attempt to remove */
+ (void) disable_dev(path);
+ rc = 1;
+ }
+ destroy_hashtable();
+
+ sv_cfg_close();
+ return (rc);
+}
+
+
+static void
+compare_tag(char *path)
+{
+ char buf[CFG_MAX_BUF], vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
+ char key[CFG_MAX_KEY];
+ int found, setnumber, i;
+ char *tag;
+
+ sv_check_cluster(path);
+ cfg_resource(cfg, (char *)NULL); /* reset */
+ cfg_rewind(cfg, CFG_SEC_CONF);
+
+#ifdef DEBUG
+ if (cfg_cluster_tag != NULL && implicit_tag != NULL) {
+ error(NULL, gettext("compare_tag: -C %s AND implicit_tag %s!"),
+ cfg_cluster_tag, implicit_tag);
+ }
+#endif
+
+ if (cfg_cluster_tag != NULL)
+ tag = cfg_cluster_tag;
+ else if (implicit_tag != NULL)
+ tag = implicit_tag;
+ else
+ tag = "-";
+
+ found = 0;
+ for (i = 0; i < sv_max_devices; i++) {
+ setnumber = i + 1;
+ (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
+ if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
+ break;
+ }
+
+ if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
+ continue;
+ }
+
+ if (strcmp(path, vol) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ warn(NULL, gettext("unable to find %s in the configuration"),
+ path);
+ return;
+ }
+
+ /* have name match, compare cnode to new tag */
+
+ if (strcmp(tag, cnode) == 0) {
+ /* cluster tags match */
+ return;
+ }
+
+ /* need to change the cluster tag */
+
+ (void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
+ if (cfg_put_cstring(cfg, key, tag, strlen(tag)) < 0) {
+ warn(NULL,
+ gettext("unable to change cluster tag for %s"), path);
+ return;
+ }
+
+ cfg_changed = 1;
+
+ /* change "-" tags to "" for display purposes */
+
+ if (strcmp(tag, "-") == 0)
+ tag = "";
+
+ if (strcmp(cnode, "-") == 0)
+ (void) strcpy(cnode, "");
+
+ (void) printf(
+ gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\"\n"),
+ program, path, cnode, tag);
+
+ spcs_log("sv", NULL,
+ gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\""),
+ program, path, cnode, tag);
+}
+
+
+/* LINT - not static as fwcadm uses it */
+static void
+compare_sv(char *conf_file)
+{
+ sv_name_t *svn_config; /* Devices in config file */
+ sv_name_t *svn_system; /* Devices in system */
+ sv_name_t *enable; /* Devices that need enabled */
+ sv_list_t svl_system;
+ int config_cnt;
+ int sys_cnt = 0;
+ int setnumber, i, j;
+ int index = 0; /* Index in enable[] */
+ int found;
+ int fd0;
+
+ svn_config = sv_alloc_svnames();
+ svn_system = sv_alloc_svnames();
+ enable = sv_alloc_svnames();
+
+ bzero(svn_system, sizeof (svn_system));
+ bzero(&svl_system, sizeof (svl_system));
+ bzero(enable, sizeof (enable));
+
+ /*
+ * Read the configuration file
+ * The return value is the number of entries
+ */
+ config_cnt = read_config_file(conf_file, svn_config);
+
+ if ((fd0 = open(sv_rpath, O_RDONLY)) < 0)
+ error(NULL, gettext("unable to open %s: %s"),
+ sv_rpath, strerror(errno));
+
+ /* Grab the system list from the driver */
+ svl_system.svl_count = sv_max_devices;
+ svl_system.svl_names = &svn_system[0];
+ svl_system.svl_error = spcs_s_ucreate();
+
+ if (ioctl(fd0, SVIOC_LIST, &svl_system) < 0) {
+ error(&svl_system.svl_error, gettext("unable to get list"));
+ }
+
+ spcs_s_ufree(&svl_system.svl_error);
+ (void) close(fd0);
+
+ /*
+ * Count the number of devices in the system.
+ * The last entry in the array has '\0' for a path name.
+ */
+ for (j = 0; j < sv_max_devices; j++) {
+ if (svn_system[j].svn_path[0] != '\0') {
+ sys_cnt++;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Compare the configuration array with the system array.
+ * Mark any differences and disable conflicting devices.
+ */
+ for (i = 0; i < config_cnt; i++) {
+ found = 0;
+ for (j = 0; j < sys_cnt; j++) {
+ if (svn_system[j].svn_path[0] == '\0' ||
+ svn_system[j].svn_mode == 0)
+ continue;
+
+ /* Check to see if path matches */
+ if (strcmp(svn_system[j].svn_path,
+ svn_config[i].svn_path) == 0) {
+ /* Found a match */
+ svn_system[j].svn_path[0] = '\0';
+ found++;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Minor number not in system = > enable device */
+ enable[index].svn_mode = svn_config[i].svn_mode;
+ (void) strcpy(enable[index].svn_path,
+ svn_config[i].svn_path);
+ index++;
+ }
+ }
+
+ /* Disable any devices that weren't in the config file */
+ for (j = 0; j < sys_cnt; j++) {
+ sv_check_cluster(NULL);
+ sv_cfg_open(CFG_WRLOCK);
+ create_cfg_hash();
+ if (svn_system[j].svn_path[0] != '\0' &&
+ svn_system[j].svn_mode != 0) {
+ (void) printf(gettext("%s: disabling sv: %s\n"),
+ program, svn_system[j].svn_path);
+ if (disable_dev(svn_system[j].svn_path) == 0) {
+ setnumber =
+ find_in_hash(svn_system[j].svn_path);
+ if (setnumber != -1) {
+ /* the volume was found in cfg store */
+ remove_from_cfgfile(
+ svn_system[j].svn_path, setnumber);
+ }
+ }
+ }
+ sv_cfg_close();
+ destroy_hashtable();
+ }
+
+ while (index) {
+ /*
+ * Config file doesn't match system => enable the devices
+ * in enable[]
+ */
+ index--;
+ (void) printf(gettext("%s: enabling new sv: %s\n"),
+ program, enable[index].svn_path);
+ (void) enable_dev(&enable[index]);
+ }
+
+ /*
+ * Search for entries where the cluster tag has changed.
+ */
+ sv_check_cluster(NULL);
+ sv_cfg_open(CFG_WRLOCK);
+
+ for (i = 0; i < sv_max_devices; i++) {
+ if (svn_config[i].svn_path[0] == '\0')
+ break;
+
+ compare_tag(svn_config[i].svn_path);
+ }
+
+ sv_cfg_close();
+}
+
+
+/*
+ * We assume that the volume is already enabled and we can only
+ * be changing the cluster tag. Anything else is an error.
+ */
+/* LINT - not static as fwcadm uses it */
+static void
+compare_one_sv(char *path)
+{
+ sv_get_maxdevs();
+ sv_check_cluster(NULL);
+ sv_cfg_open(CFG_WRLOCK);
+
+ compare_tag(path);
+
+ sv_cfg_close();
+}
+
+/*
+ * Read all sets from the libdscfg configuration file, and store everything in
+ * the hashfile.
+ *
+ * We assume that the config file has been opened & rewound for us. We store
+ * the volume name as the key, and the setnumber where we found it as the data.
+ *
+ * The caller can pass in a pointer to the maximum number of volumes, or
+ * a pointer to NULL, specifying we want 'all' the volumes. The table is
+ * searched using find_in_hash.
+ */
+static void
+create_cfg_hash()
+{
+ char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
+ char vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
+ int setnumber;
+ ENTRY item;
+
+ if (hcreate((size_t)sv_max_devices) == 0)
+ error(NULL, gettext("unable to create hash table"));
+
+ for (setnumber = 1; /* CSTYLED */; setnumber++) {
+ (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
+ if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0)
+ break;
+
+ if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
+ continue;
+ }
+
+ item.key = strdup(vol);
+ item.data = (void *)setnumber;
+ if (hsearch(item, ENTER) == NULL) {
+ error(NULL,
+ gettext("unable to add entry to hash table"));
+ }
+ }
+}
+
+/*
+ * Function to search the hash for a specific volume. If it is found,
+ * we return the set number. If it isn't found, we return -1
+ */
+static int
+find_in_hash(char *path)
+{
+ ENTRY *found_entry, item;
+ int retval = -1;
+
+ item.key = path;
+
+ if ((found_entry = hsearch(item, FIND)) != NULL) {
+ retval = (int)found_entry->data;
+ }
+
+ return (retval);
+}
+
+/*
+ * Just a wrapper to destory the hashtable. At some point in the future we
+ * might want to do something more.... For instance, verify that the cfg
+ * database and the kernel configuration match (?) Just an idea.
+ */
+static void
+destroy_hashtable()
+{
+ hdestroy();
+}
+
+/*
+ * This function will remove a particular set from the config file.
+ *
+ * We make a whole host of assumptions:
+ * o the hashfile is up to date;
+ * o The config file has been opened with a WRLOCK for us.
+ */
+static void
+remove_from_cfgfile(char *path, int setnumber)
+{
+ char key[CFG_MAX_KEY];
+ int sev;
+ char *lcltag;
+
+ /* attempt to remove the volume from config storage */
+ (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
+ if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
+ warn(NULL, gettext("unable to remove %s from "
+ "config storage: %s"), path, cfg_error(&sev));
+ } else {
+ if (implicit_tag && *implicit_tag) {
+ lcltag = implicit_tag;
+ } else if (cfg_cluster_tag && *cfg_cluster_tag) {
+ lcltag = cfg_cluster_tag;
+ } else {
+ lcltag = "-";
+ }
+ if (cfg_rem_user(cfg, path, lcltag, "sv") != CFG_USER_LAST) {
+ warn(NULL, gettext("unable to remove %s from dsvol"),
+ path);
+ }
+ cfg_changed = 1;
+ }
+}