/* * 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 static int sv_max_devices; /* * Pathnames. */ static const caddr_t sv_rpath = SV_DEVICE; /* * Functions. */ static void resume_dev(int, sv_name_t *); static void suspend_dev(int, const caddr_t); static int read_libcfg(sv_name_t svn[]); static void resume_sv(); static void suspend_sv(); static void prepare_unload_sv(); /* * support for the special cluster tag "local" to be used with -C in a * cluster for local volumes. */ #define SV_LOCAL_TAG "local" static caddr_t program; static caddr_t cfg_cluster_tag; 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] -r resume all sv devices\n"), program); (void) fprintf(stderr, gettext( "\t%s [-C tag] -s suspend all sv devices\n"), program); (void) fprintf(stderr, gettext( "\t%s -u prepare for sv unload\n"), program); } 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); 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) 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); } int main(int argc, char *argv[]) { extern int optind; extern char *optarg; int Cflag, resume, suspend, unload; int opt; (void) setlocale(LC_ALL, ""); (void) textdomain("svboot"); program = strdup(basename(argv[0])); Cflag = unload = resume = suspend = 0; while ((opt = getopt(argc, argv, "C:hrsu")) != 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 'r': resume++; break; case 's': suspend++; break; case 'u': unload++; break; case 'h': usage(); exit(0); case '?': /* FALLTHRU */ default: usage(); exit(2); /* NOTREACHED */ } } /* * Usage checks */ if ((resume + suspend + unload) > 1) { warn(NULL, gettext("-r , -s and -u are mutually exclusive")); usage(); exit(2); } if (!resume && !suspend && !unload) { warn(NULL, gettext("option required")); usage(); exit(2); } if (optind != argc) { usage(); exit(2); } /* * Check for the special (local) cluster tag */ if (cfg_cluster_tag != NULL && strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0) cfg_cluster_tag = "-"; /* * Process commands */ if (resume) resume_sv(); else if (suspend) suspend_sv(); else if (unload) prepare_unload_sv(); return (0); } static void resume_sv() { int index; sv_name_t *svn; int cnt; int fd; svn = sv_alloc_svnames(); index = read_libcfg(svn); fd = open(sv_rpath, O_RDONLY); if (fd < 0) { warn(NULL, gettext("unable to open %s: %s"), svn->svn_path, strerror(errno)); return; } 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; } resume_dev(fd, &svn[cnt]); } (void) close(fd); } static void resume_dev(int fd, sv_name_t *svn) { struct stat stb; sv_conf_t svc; 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; } 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)); svc.svc_flag = svn->svn_mode; svc.svc_error = spcs_s_ucreate(); if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) { spcs_log("sv", &svc.svc_error, gettext("%s: unable to resume %s"), program, svn->svn_path); warn(&svc.svc_error, gettext("unable to resume %s"), svn->svn_path); return; } spcs_log("sv", NULL, gettext("%s: resume %s"), program, svn->svn_path); spcs_s_ufree(&svc.svc_error); } /* * This routine parses the config 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_libcfg(sv_name_t svn[]) { char rdev[CFG_MAX_BUF]; char key[CFG_MAX_KEY]; struct stat stb; int i; int setnumber; int index = 0; /* Current location in svn array */ sv_name_t *cur_svn; /* Pointer to svn[index] */ CFGFILE *cfg; if ((cfg = cfg_open("")) == NULL) { error(NULL, gettext("Error opening config: %s"), strerror(errno)); } cfg_resource(cfg, cfg_cluster_tag); if (!cfg_lock(cfg, CFG_RDLOCK)) { error(NULL, gettext("Error locking config: %s"), strerror(errno)); } for (i = 0; /*CSTYLED*/; i++) { setnumber = i + 1; bzero(rdev, CFG_MAX_BUF); (void) snprintf(key, sizeof (key), "sv.set%d.vol", setnumber); if (cfg_get_cstring(cfg, key, rdev, sizeof (rdev)) < 0) break; /* Check to see if the raw device is present */ if (stat(rdev, &stb) != 0) { warn(NULL, gettext("unable to access %s: %s"), rdev, strerror(errno)); continue; } if (!S_ISCHR(stb.st_mode)) { warn(NULL, gettext("%s is not a character device"), rdev); continue; } cur_svn = &svn[index]; /* For easier reading below */ if (strlen(rdev) >= sizeof (cur_svn->svn_path)) { warn(NULL, gettext( "raw device name (%s) longer than %d characters"), rdev, (sizeof (cur_svn->svn_path) - 1)); continue; } (void) strcpy(cur_svn->svn_path, rdev); cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE); index++; } cfg_close(cfg); /* Set the last path to NULL */ svn[index].svn_path[0] = '\0'; return (index); } static void suspend_dev(int fd, const caddr_t path) { struct stat stb; sv_conf_t svc; 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); } (void) strcpy(svc.svc_path, path); svc.svc_error = spcs_s_ucreate(); if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) { if (errno != SV_EDISABLED) { spcs_log("sv", &svc.svc_error, gettext("%s: unable to suspend %s"), program, path); warn(&svc.svc_error, gettext("unable to suspend %s"), path); return; } } spcs_log("sv", NULL, gettext("%s: suspend %s"), program, path); spcs_s_ufree(&svc.svc_error); } static void suspend_sv(void) { sv_name_t *svn, *svn_system; /* Devices in system */ sv_list_t svl_system; int i; int fd; svn_system = sv_alloc_svnames(); svl_system.svl_count = read_libcfg(svn_system); if ((fd = open(sv_rpath, O_RDONLY)) < 0) { warn(NULL, gettext("unable to open %s: %s"), sv_rpath, strerror(errno)); return; } for (i = 0; i < svl_system.svl_count; i++) { if (*svn_system[i].svn_path == '\0') break; svn = &svn_system[i]; suspend_dev(fd, svn->svn_path); } (void) close(fd); } /* * Check kernel's sv_ndevices and thread sets, * if empty then change kernel state to allow unload, * and sleep SV_WAIT_UNLAOD (10 seconds). * * Only called in pkgrm time. */ static void prepare_unload_sv(void) { int fd; int rc = 0; if ((fd = open(sv_rpath, O_RDONLY)) < 0) { warn(NULL, gettext("unable to open %s: %s"), sv_rpath, strerror(errno)); return; } if (ioctl(fd, SVIOC_UNLOAD, &rc) < 0) error(NULL, gettext("unable to unload")); if (rc != 0) error(NULL, gettext("still has active devices or threads")); (void) close(fd); }