metaimport.c revision 80148899834a4078a2bd348504aa2d6de9752837
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Utility to import SVM disksets into an active SVM configuration.
*/
#include <assert.h>
#include <strings.h>
#include <string.h>
#include <meta.h>
#include <sdssc.h>
static md_im_drive_info_t *overlap_disks;
static void
{
"%s:\t%s -s setname [-n] [-f] [-v] [%s...]\n",
}
static void
{
}
}
/*
* Returns 0 if there is no overlap, 1 otherwise
*/
static int
{
int is_overlap = 0;
int disk_not_available = 0;
/*
* There are 2 ways we could get an "overlap" disk.
* One is if the ctd's are the same. The other is if
* the setcreatetimestamp on the disk doesn't agree with the
* "good" disk in the set. However, if we have a disk that is
* unavailable and the other instance of the ctd is available we
* really don't have a conflict. It's just that the unavailable ctd
* is it's "old" location and the available instance is a current
* location.
*/
disk_not_available = 1;
else
disk_not_available = 0;
if (disk_not_available &&
== MD_IM_DISK_AVAILABLE))
continue;
else if (!disk_not_available &&
(next_set_dr->mid_available ==
continue;
/*
* Chain it, skip if
* already there
*/
if (overlap_disks == NULL) {
} else {
for (chain = &overlap_disks;
break;
}
}
}
if (!is_overlap)
is_overlap = 1;
}
}
}
}
}
/* didn't find a good disk */
continue;
}
if (d->mid_available == MD_IM_DISK_NOT_AVAILABLE)
continue;
/*
* If the disk doesn't have the same set creation
* time as the designated "good disk" we have a
* as such.
*/
if ((gooddisktime.tv_usec !=
d->mid_setcreatetimestamp.tv_usec) ||
(gooddisktime.tv_sec !=
d->mid_setcreatetimestamp.tv_sec)) {
d->overlapped_disk = 1;
if (overlap_disks == NULL) {
d->overlapped_disk = 1;
overlap_disks = d;
} else {
for (chain = &overlap_disks;
== 0) {
break;
}
}
*chain = d;
d->overlapped_disk = 1;
}
}
if (!is_overlap)
is_overlap = 1;
}
}
}
return (is_overlap);
}
static void
{
"have been detected in more than one set.\n"
"Import recommendation based upon set creation time.\n"
"Proceed with the import with caution."));
/*
* Look at all overlapping disks. Determine which slice
* would have a replica on it. i.e. either slice 7 or 6.
* Then read the master block. If the disk doesn't have a
* metadb on it, the master block is a dummy master block.
* Both dummy or normal master block contain the timestamp
* which is what we are after. Use this timestamp to issue
* the appropriate recommendation.
*/
int fd = -1;
/*
* If the disk isn't available (i.e. powered off or dead)
* we can't read the master block timestamp and thus
* cannot make a recommendation as to which set it belongs to.
*/
if (d->mid_available != MD_IM_DISK_AVAILABLE) {
gettext(" - no recommendation can "
"be made because disk is unavailable\n"));
continue;
}
continue;
continue;
continue;
continue;
mdclrerror(ep);
continue;
}
gettext(" - must import with set "
(&(mbp->mb_setcreatetime))));
}
}
/*
* is_first_disk is called to determine if the disk passed to it is
* eligible to be used as the "first disk time" in the set. It checks to
* see if the disk is available, on the skip list or not (thus already in
* an importable set) or being used by the system already.
* RETURN:
* 1 The time can be used as the first disk time
* 0 The time should not be used.
*/
static int
{
/*
* If a disk is not available there is no
* set creation timestamp available.
*/
if (d->mid_available == MD_IM_DISK_AVAILABLE) {
/*
* We also need to make sure this disk isn't already on
* the skip list.
*/
return (0);
}
/*
* And we need to make sure the drive isn't
* currently being used for something else
* like a mounted file system or a current
* metadevice or in a set.
*/
return (0);
}
} else {
return (0);
}
return (1);
}
/*
* Input a list of disks (dnlp), find the sets that are importable, create
* a list of these sets (mispp), and a list of the disks within each of these
* sets (midp). These lists (mispp and midp) will be used by metaimport.
*/
static int process_disks(
int flags,
int *set_count,
int overlap,
)
{
int rscount = 0;
int hasreplica;
md_im_set_desc_t *p;
/* Scan qualified disks */
/* is the current drive on the skip list? */
break;
}
/* drive on the skip list ? */
continue;
/*
* In addition to updating the misp list, either verbose or
* standard output will be generated.
*
*/
if (hasreplica < 0) {
mdclrerror(ep);
} else {
rscount += hasreplica;
/* Eliminate duplicate reporting */
if (hasreplica > 0) {
/*
* Go to the tail for the current set
*/
p = p->mis_next)
;
/*
* Now look for the set creation timestamp.
* If a disk is not available there is no
* set creation timestamp available so look
* for the first available disk to grab this
* information from. We also need to make
* sure this disk isn't already on the skip
* list. If so go to the next available drive.
* And we need to make sure the drive isn't
* currently being used for something else
* like a mounted file system or a current
* metadevice or in a set.
*/
for (d = p->mis_drives; d != NULL;
d = d->mid_next) {
if (is_first_disk(d, skiph)) {
break;
}
}
for (d = p->mis_drives; d != NULL;
d = d->mid_next) {
/*
* if the mb_setcreatetime for a disk
* is not the same as the first disk
* in the set, don't put it on the
* skip list. This disk probably
* doesn't really belong in this set
* and we'll want to look at it again
* to figure out where it does belong.
* If the disk isn't available, there's
* really no point in looking at it
* again so put it on the skip list.
*/
if (d->mid_available ==
if ((d->mid_setcreatetimestamp.
tv_sec != firstdisktime.
tv_sec) ||
tv_usec !=
continue;
}
skipt =
}
}
}
}
return (rscount);
}
int
{
char c;
char *setname_new = NULL;
int report_only = 0;
int version = 0;
int err_on_prune = 0;
int rscount = 0;
int have_multiple_sets = 0;
int force = 0;
int overlap = 0;
int set_count = 0;
int no_quorum = 0;
/*
* Get the locale set up before calling any other routines
* with messages to output. Just in case we're not in a build
* environment, make sure that TEXT_DOMAIN gets set to
* something.
*/
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/*
* Check to see if the libsds_sc.so is bound on the
* current system. If it is, it means the system is
* part of a cluster.
*
* The import operation is currently not supported
* in a SunCluster environment.
*/
if (sdssc_bind_library() != SDSSC_NOT_BOUND) {
"%s: Import operation not supported under SunCluster\n"),
argv[0]);
exit(0);
}
/* initialize */
}
optind = 1;
opterr = 1;
switch (c) {
case 'f':
force = 1;
break;
case 'n':
dry_run = 1;
break;
case 'r':
report_only = 1;
break;
case 's':
break;
case 'v':
break;
case 'V':
version = 1;
break;
case '?':
default:
break;
}
}
if (version == 1)
/* Detect conflicting options */
if ((dry_run != 0) && (report_only != 0))
/* Don't do any real work if we don't have root privilege */
if (meta_check_root(ep) != 0) {
}
if (meta_setup_db_locations(ep) != 0) {
}
/*
* Read remaining arguments into drive name list, otherwise
* call routine to list all drives in system.
*/
int i;
/* For user specified disks, they MUST not be in use */
err_on_prune = 1;
/* All remaining args should be disks */
} else {
}
}
} else {
}
}
/*
* If the user specified disks on the command line, min_count will be
* greater than zero. If they didn't, it should be safe to assume that
* the system in question has at least one drive detected by the
* snapshot code, or we would have barfed earlier initializing the
* metadb.
*/
/*
* Prune the list:
* - get rid of drives in current svm configuration
* - get rid of mounted drives
* - get rid of swap drives
* - get rid of drives in other sets
*
* If drives were specified on the command line, it should be
* an error to find in-use disks in the list. (err_on_prune)
*
* On return from meta_prune_cnames call, dnlp
* will have candidate for replica scan.
*/
/*
* Doctor the drive string in the error structure to list all of the
* unused disks, rather than just one. The output will be done in the
* following !mdisok() block.
*/
md_ds_error_t *ip =
char *dlist;
int sizecnt = 0;
/* add 1 for null terminator */
}
}
}
/* Don't continue if we're already hosed */
}
/* ...or if there's nothing to scan */
}
/*
* META_IMP_PASS1 means gather the info, but don't report.
*/
/*
* This time call without META_IMP_PASS1 set and we gather
* and report the information.
* We need to do this twice because of the overlap detection.
* The first pass generates a list of disks to detect overlap on.
* We then do a second pass using that overlap list to generate
* the report.
*/
/*
* Now have entire list of disks associated with diskset including
* disks listed in mddb locator blocks and namespace. Before importing
* diskset need to recheck that none of these disks is already in use.
* If a disk is found that is already in use, print error and exit.
*/
if (!report_only) {
md_im_set_desc_t *p;
/* Get sp for local set */
}
}
if (d->mid_available == MD_IM_DISK_AVAILABLE) {
MDE_DS_DRIVEINUSE, 0, NULL,
}
} else {
/*
* If drive is unavailable, then check
* that this drive hasn't already been
* imported as part of another partial
* diskset. Check by devid instead of
* cname since the unavailable drive
* would have the cname from its
* previous system and this may collide
* with a valid cname on this system.
* Fail if devid is found in another
* set or if the routine fails.
*/
if ((meta_is_devid_in_anyset(
MDE_DS_DRIVEINUSE, 0, NULL,
}
}
}
}
}
/*
* If there are no unconfigured sets, then our work here is done.
* Hopefully this is friendlier than just not printing anything at all.
*/
if (rscount == 0) {
/*
* If we've found partial disksets but no complete disksets,
* we don't want this to print.
*/
if (!misp) {
"detected"));
}
}
/*
* We'll need this info for both the report content and the import
* decision. By the time we're here, misp should NOT be NULL (or we
* would have exited in the rscount == 0 test above).
*/
have_multiple_sets = 1;
}
/*
* Generate the appropriate (verbose or not) report for all sets
* detected. If we're planning on importing later, only include the
* "suggested import" command if multiple sets were detected. (That
* way, when we error out later, we have still provided useful
* information.)
*/
/*
* Now we should have all the unconfigured sets detected
* check for the overlapping
*/
if (have_multiple_sets) {
/* Printing out how many candidate disksets we found. */
if (imp_flags & META_IMP_REPORT) {
(void) printf("%s: %i\n\n",
gettext("Number of disksets eligible for import"),
}
}
if (overlap) {
}
if (have_multiple_sets && !report_only) {
"sets detected.\nRerun the command with the "
"suggested options for the desired set."));
}
/*
* If it's a report-only request, we're done. If it's an import
* request, make sure that we only have one entry in the set list.
*/
if (report_only) {
} else if (have_multiple_sets) {
} else if (overlap) {
/*
* The only way we can get here is if we're doing an import
* request on a set that contains at least one disk with
* a time conflict. We are prohibiting the importation of
* this type of set until the offending disk(s) are turned
* off to prevent data corruption.
*/
for (d = pass1_misp->mis_drives;
d != NULL;
d = d->mid_next) {
if (d->overlapped_disk)
}
}
if (setname_new == NULL) {
}
/*
* The user must specify the -f (force) flag if the following
* conditions exist:
* - partial diskset
* - stale diskset
*/
if (meta_replica_quorum(misp) != 0)
no_quorum = 1;
if (!force)
}
if (dry_run) {
}
}
}
}
0, &status)) {
}
/*NOTREACHED*/
return (0);
}