OpenSolaris_b135/cmd/avs/dsw/iiboot.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/param.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <stdarg.h>
#include <ctype.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/nsctl/dsw.h>
#include <sys/nskernd.h>

#define	MAX_PROCESSES 64

int parseopts(int, char **, int *);
int read_resume_cfg();
int read_suspend_cfg();
void iiboot_usage(void);
extern char *basename(char *);

dsw_config_t *resume_list = 0;
dsw_ioctl_t *suspend_list = 0;
int	n_structs;
char *program;
char *cfg_cluster_tag = NULL;

volatile int fork_cnt;
volatile int fork_rc;

static void
iiboot_msg(char *prefix, spcs_s_info_t *status, char *string, va_list ap)
{
	if (status) {
		(void) fprintf(stderr, "II: %s\n", prefix);
		spcs_s_report(*status, stderr);
		spcs_s_ufree(status);
	} else {
		(void) fprintf(stderr, "%s: %s: ", program, prefix);
	}

	if (string && *string != '\0') {
		(void) vfprintf(stderr, string, ap);
	}

	(void) fprintf(stderr, "\n");
}

static void
iiboot_err(spcs_s_info_t *status, char *string, ...)
{
	va_list ap;
	va_start(ap, string);

	iiboot_msg(gettext("Error"), status, string, ap);

	va_end(ap);
	exit(1);
}

static void
iiboot_warn(spcs_s_info_t *status, char *string, ...)
{
	va_list ap;
	va_start(ap, string);

	iiboot_msg(gettext("warning"), status, string, ap);

	va_end(ap);
}

/* ARGSUSED */
static void
sigchld(int sig)
{
	int wait_loc = 0;

	(void) wait(&wait_loc);
	if (WIFEXITED(wait_loc) && (WEXITSTATUS(wait_loc) == 0)) {
		;
		/*EMPTY*/
	} else {
		fork_rc = WEXITSTATUS(wait_loc);
	}

	if (fork_cnt > 0)
		--fork_cnt;
}


int
#ifdef lint
iiboot_lintmain(int argc, char *argv[])
#else
main(int argc, char *argv[])
#endif
{
	int pairs;
	pid_t pid = 0;
	int flag = 0;
	int i, j;
	int rc;
	int	ioctl_fd;
	void *ioarg;
	dsw_ioctl_t *ii_iop, ii_suspend;
	dsw_list_t args = {0};
	dsw_config_t *ii_cfgp, *lp = NULL;
	spcs_s_info_t ustatus;
	int max_processes = MAX_PROCESSES;

	(void) setlocale(LC_ALL, "");
	(void) textdomain("ii");

	program = strdup(basename(argv[0]));

	if ((ioctl_fd = open(DSWDEV, O_RDWR, 0)) == -1) {
		spcs_log("ii", NULL, "iiboot open %s failed, errno %d",
			DSWDEV, errno);
		iiboot_err(NULL,
		    gettext("Failed to open Point-in-Time Copy control "
			    "device"));
	}

	if (parseopts(argc, argv, &flag))
		return (1);

	if (flag == DSWIOC_RESUME)
		pairs = read_resume_cfg();
	else
		pairs = -1;

	if (pairs == 0) {
#ifdef DEBUG
		iiboot_err(NULL,
		    gettext("Config contains no Point-in-Time Copy sets"));
#endif
		return (0);
	}

	if (cfg_cluster_tag == NULL && flag != DSWIOC_RESUME) {
		if (ioctl(ioctl_fd, DSWIOC_SHUTDOWN, 0) < 0) {
			spcs_log("ii", &ustatus, "iiboot shutdown failed");
			iiboot_err(NULL, gettext("SHUTDOWN ioctl error"));
		}
		return (0);
	} else if (cfg_cluster_tag != NULL && flag == DSWIOC_SUSPEND) {
		bzero(&ii_suspend, sizeof (dsw_ioctl_t));
		ii_suspend.status = spcs_s_ucreate();
		ii_suspend.flags = CV_IS_CLUSTER;
		(void) strncpy(ii_suspend.shadow_vol, cfg_cluster_tag,
		    DSW_NAMELEN);
		rc = ioctl(ioctl_fd, flag, &ii_suspend);
		if ((rc) && (errno != DSW_ECNOTFOUND)) {
			spcs_log("ii", &ii_suspend.status,
			    "iiboot resume cluster %s failed", cfg_cluster_tag);
			iiboot_err(&ii_suspend.status, gettext("ioctl error"));
			spcs_s_ufree(&ii_suspend.status);
			return (-1);
		}
		spcs_s_ufree(&ii_suspend.status);
		return (0);

	} else if ((cfg_cluster_tag != NULL) && (flag == DSWIOC_RESUME)) {
		/*
		 * If we are running in a Sun Cluster, this is a resume
		 * operation, get a list of all shadow volumes, where the
		 * shadow volumes match the shadows of the sets being resumed
		 */
		rc = ioctl(ioctl_fd, DSWIOC_LISTLEN, &args);
		if (rc == -1) {
			spcs_log("ii", NULL,
				"iiboot get LIST failed, errno %d", errno);
			iiboot_err(NULL,
				gettext("Failed to get LIST of Point-in-Time "
				    "sets"));
			return (-1);
		}

		args.status = spcs_s_ucreate();
		args.list_used = 0;
		args.list_size = rc + 4;
		lp = args.list = (dsw_config_t *)
		    malloc(args.list_size * sizeof (dsw_config_t));
		if (args.list == NULL) {
			iiboot_err(NULL,
				gettext("Failed to allocate memory"));
		}
		if (ioctl(ioctl_fd, DSWIOC_LIST, &args)  == -1) {
			spcs_log("ii", &args.status, "Failed to get LIST");
			iiboot_err(&args.status, gettext("ioctl error"));
		}
		spcs_s_ufree(&args.status);

		/* Remove all elements that are not in the resume list */
		for (j = args.list_used; j; j--) {
			for (i = 0; i < pairs; i++) {
				if (strcmp(lp->shadow_vol,
				    resume_list[i].shadow_vol) == 0) {
					if (strlen(lp->cluster_tag) == 0) {
						lp++;
						break;
					}
				}
			}
			if (i != pairs)
				continue;
			(void) memmove(lp, lp + 1, j * sizeof (dsw_config_t));
			args.list_used--;
		}
	}

	(void) sigset(SIGCHLD, sigchld);
	fork_cnt = fork_rc = 0;
	for (i = 0; i < pairs; i++) {
		ustatus = spcs_s_ucreate();
		if (flag == DSWIOC_RESUME) {
			ioarg = (void *) (ii_cfgp = (resume_list + i));
			ii_cfgp->status = ustatus;
			pid = fork();
		} else {
			ioarg = (void *) (ii_iop = (suspend_list + i));
			ii_iop->status = ustatus;
		}
		while (pid == -1) {		/* error forking */
			perror("fork");

			/* back off on the max processes and try again */
			--max_processes;
			if (fork_cnt > 0) {
				(void) pause();
			}
			pid = fork();
		}

		if (pid > 0) {		/* this is parent process */
			++fork_cnt;
			while (fork_cnt > MAX_PROCESSES) {
				(void) pause();
			}
			continue;
		}

		rc = ioctl(ioctl_fd, flag, ioarg);
		if (rc == SPCS_S_ERROR) {
			if (flag == DSWIOC_RESUME)
				spcs_log("ii", &ustatus,
					"iiboot resume %s failed",
					ii_cfgp->shadow_vol);
			else
				spcs_log("ii", &ustatus,
					"iiboot suspend %s failed",
					ii_iop->shadow_vol);
			iiboot_err(&ustatus, gettext("ioctl error"));
		}
		/* Resuming child */
		spcs_s_ufree(&ustatus);
		if (flag == DSWIOC_RESUME)
			exit(0);
	}

	/*
	 * Allow all processes to finish up before exiting
	 * Set rc for success
	 */
	while (fork_cnt > 0) {
		(void) alarm(60);	/* wake up in 60 secs just in case */
		(void) pause();
	}
	(void) alarm(0);

	/* Disable duplicate shadows that were part of the implicit join */
	if ((j = args.list_used) != 0) {
		int setno;
		char key[CFG_MAX_KEY], buf[CFG_MAX_BUF], sn[CFG_MAX_BUF];
		CFGFILE *cfg;
		char *mst, *shd, *ctag;
		pid_t pid = fork();

		if (pid == -1) {
			iiboot_err(NULL, gettext("Failed to fork"));
			return (errno);
		} else if (pid > 0) {
			return (0);	/* Parent, OK exit */
		}

		for (j = args.list_used, lp = args.list; j; j--, lp++) {
		    setno = 0;
		    while (++setno) {

			/*
			 * Open the configuration database
			 */
			if (!(cfg = cfg_open(""))) {
			    iiboot_err(NULL, gettext("Failed to open dscfg"));
			    return (-1);
			}

			/* Sooner or later, this lock will be free */
			while (!cfg_lock(cfg, CFG_WRLOCK))
				(void) sleep(2);

			(void) snprintf(key, CFG_MAX_KEY, "ii.set%d", setno);
			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
				cfg_close(cfg);
				break;
			}

			/* For imported shadows, master must be special tag */
			mst = strtok(buf, " ");		/* master */
			shd = strtok(NULL, " ");	/* shadow */
			(void) strtok(NULL, " ");	/* bitmap */
			(void) strtok(NULL, " ");	/* mode */
			(void) strtok(NULL, " ");	/* overflow */
			ctag = strtok(NULL, " ");	/* cnode */

			/*
			 * For this record to be processed, the shadow volume
			 * name must match and the cluster tag must be blank
			 */
			if (strcmp(lp->shadow_vol, shd) || strcmp(ctag, "-")) {
				cfg_close(cfg);
				continue;
			}

			/* Derrive local cluster tag */
			if (cfg_l_dgname(lp->shadow_vol, sn, sizeof (sn)))
				ctag = sn;
			else
				iiboot_err(NULL, gettext(
					"Failed to device group for shadow %s"),
					lp->shadow_vol);

			/* disable master volume if not imported */
			if (strcmp(mst, II_IMPORTED_SHADOW))
			    if (cfg_vol_disable(cfg, mst, cfg_cluster_tag,
				"ii") < 0)
				iiboot_err(NULL, gettext(
				    "SV disable of master failed"));

			/*
			 * Delete the Imported Shadow set
			 */
			if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
				iiboot_err(NULL, gettext(
					"Failed to delete Imported shadow %s"),
					lp->shadow_vol);
			}

			/*
			 * SV disable shadow volume
			 */
			if (cfg_vol_disable(cfg, shd, NULL, "ii") < 0)
				iiboot_err(NULL, gettext(
					"SV disable of shadow failed"));

			/*
			 * Commit the delete
			 */
			(void) cfg_commit(cfg);
			cfg_close(cfg);

			/*
			 * Open the configuration database
			 */
			if (!(cfg = cfg_open(""))) {
			    iiboot_err(NULL, gettext("Failed to open dscfg"));
			    return (-1);
			}

			/* Sooner or later, this lock will be free */
			while (!cfg_lock(cfg, CFG_WRLOCK))
				(void) sleep(2);

			/* Set cluster tag for Shadow volume */
			(void) cfg_vol_enable(cfg, shd, ctag, "ii");


			/*
			 * Commit the delete
			 */
			(void) cfg_commit(cfg);
			cfg_close(cfg);
		    }
		}
	}
	return (fork_rc);
}

static int
set_is_offline(char *cflags)
{
	unsigned int flags;
	int conv;

	if (!cflags || !*cflags)
		return (0);

	/* convert flags to an int */
	conv = sscanf(cflags, "%x", &flags);
	return ((conv == 1) && ((flags & DSW_OFFLINE) != 0));
}

/*
 * read_resume_cfg()
 *
 * DESCRIPTION: Read the relevant config info via libcfg
 *
 * Outputs:
 *	int i			Number of Point-in-Time Copy sets
 *
 * Side Effects: The 0 to i-1 entries in the resume_list are filled.
 *
 */

int
read_resume_cfg()
{
	CFGFILE *cfg;
	int i;
	char *buf, **entry, *mst, *shd, *bmp, *ctag, *opt, *ptr;
	int valid_sets;
	dsw_config_t *p;
	static int offset = sizeof (NSKERN_II_BMP_OPTION);

	spcs_log("ii", NULL, "iiboot resume cluster tag %s",
			cfg_cluster_tag ? cfg_cluster_tag : "<none>");
	if ((cfg = cfg_open("")) == NULL) {
		spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
			errno);
		iiboot_err(NULL, gettext("Error opening config"));
	}

	cfg_resource(cfg, cfg_cluster_tag);
	if (!cfg_lock(cfg, CFG_RDLOCK)) {
		spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
			errno);
		iiboot_err(NULL, gettext("Error locking config"));
	}

	/* Determine number of set, if zero return 0 */
	if ((n_structs = cfg_get_section(cfg, &entry, "ii")) == 0)
		return (0);

	resume_list = calloc(n_structs, sizeof (*resume_list));
	if (resume_list == NULL) {
		spcs_log("ii", NULL, "iiboot resume realloc failed, errno %d",
		    errno);
		iiboot_err(NULL, gettext("Resume realloc failed"));
	}

	valid_sets = 0;
	p = resume_list;
	for (i = 0; i < n_structs; i++) {
		buf = entry[i];
		mst = strtok(buf, " ");
		shd = strtok(NULL, " ");
		bmp = strtok(NULL, " ");
		(void) strtok(NULL, " ");	/* mode */
		(void) strtok(NULL, " ");	/* overflow */
		ctag = strtok(NULL, " ");	/* ctag */
		if (ctag)
			ctag += strspn(ctag, "-");
		opt = strtok(NULL, " ");

		if (!mst || !shd || !bmp)
			break;

		/* If cluster tags don't match, skip record */
		if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) ||
		    (!cfg_cluster_tag && strlen(ctag))) {
			free(buf);
			continue;
		}

		ptr = strstr(opt, NSKERN_II_BMP_OPTION "=");
		if (ptr && set_is_offline(ptr + offset)) {
			free(buf);
			continue;
		}

		(void) strncpy(p->master_vol, mst, DSW_NAMELEN);
		(void) strncpy(p->shadow_vol, shd, DSW_NAMELEN);
		(void) strncpy(p->bitmap_vol, bmp, DSW_NAMELEN);
		if (ctag)
			(void) strncpy(p->cluster_tag, ctag, DSW_NAMELEN);
		free(buf);
		++p;
		++valid_sets;
	}

	while (i < n_structs)
		free(entry[i++]);
	if (entry)
		free(entry);

	cfg_close(cfg);
	return (valid_sets);
}

/*
 * read_suspend_cfg()
 *
 * DESCRIPTION: Read the relevant config info via libcfg
 *
 * Outputs:
 *	int i			Number of Point-in-Time Copy sets
 *
 * Side Effects: The 0 to i-1 entries in the suspend_list are filled.
 *
 */

int
read_suspend_cfg()
{
	int rc;
	CFGFILE *cfg;
	int i;
	char buf[CFG_MAX_BUF];
	char key[CFG_MAX_KEY];
	int setnumber;
	dsw_ioctl_t *p;

	spcs_log("ii", NULL, "iiboot suspend cluster tag %s",
			cfg_cluster_tag ? cfg_cluster_tag : "<none>");

	if (cfg_cluster_tag == NULL) {
		return (1);
	}

	if ((cfg = cfg_open("")) == NULL) {
		spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
			errno);
		iiboot_err(NULL, gettext("Error opening config"));
	}

	cfg_resource(cfg, cfg_cluster_tag);
	if (!cfg_lock(cfg, CFG_RDLOCK)) {
		spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
			errno);
		iiboot_err(NULL, gettext("Error locking config"));
	}


	/*CSTYLED*/
	for (i = 0; ; i++) {
		setnumber = i + 1;

		bzero(buf, CFG_MAX_BUF);
		(void) snprintf(key, sizeof (key), "ii.set%d", setnumber);
		rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
		if (rc < 0)
			break;
		if (n_structs < setnumber) {
			n_structs += 2;
			suspend_list = realloc(suspend_list,
					sizeof (*suspend_list) * n_structs);
			if (suspend_list == NULL) {
			    spcs_log("ii", NULL,
			    "iiboot suspend realloc failed, errno %d",
			    errno);
			    iiboot_err(NULL, gettext("Suspend realloc failed"));
			}
		}
		p = suspend_list + i;

		(void) snprintf(key, sizeof (key), "ii.set%d.shadow",
		    setnumber);
		(void) cfg_get_cstring(cfg, key, p->shadow_vol, DSW_NAMELEN);

	}

	cfg_close(cfg);
	return (i);
}


int
parseopts(argc, argv, flag)
int argc;
char **argv;
int *flag;
{
	int  errflag = 0;
	int  Cflag = 0;
	char c;
	char inval = 0;

	while ((c = getopt(argc, argv, "hrsC:")) != -1) {
		switch (c) {
		case 'C':
			if (Cflag) {
				iiboot_warn(NULL,
				    gettext("-C specified multiple times"));
				iiboot_usage();
				return (-1);
			}

			Cflag++;
			cfg_cluster_tag = (optarg[0] == '-') ? NULL : optarg;
			break;

		case 'h':
			iiboot_usage();
			exit(0);
			/* NOTREACHED */

		case 'r':
			if (*flag)
				inval = 1;
			*flag = DSWIOC_RESUME;
			break;
		case 's':
			if (*flag)
				inval = 1;
			*flag = DSWIOC_SUSPEND;
			break;
		case '?':
			errflag++;
		}
	}

	if (inval) {
		iiboot_warn(NULL, gettext("Invalid argument combination"));
		errflag = 1;
	}

	if (!*flag || errflag) {
		iiboot_usage();
		return (-1);
	}

	return (0);
}

void
iiboot_usage()
{
	(void) fprintf(stderr, gettext("usage:\n"));
	(void) fprintf(stderr,
		gettext("\t%s -r [-C tag]\t\tresume\n"), program);
	(void) fprintf(stderr,
		gettext("\t%s -s [-C tag]\t\tsuspend\n"), program);
	(void) fprintf(stderr, gettext("\t%s -h\t\t\tthis help message\n"),
	    program);
}