10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51945Sjeanm * Common Development and Distribution License (the "License").
61945Sjeanm * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*11053SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * Utility to import SVM disksets into an active SVM configuration.
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate #include <assert.h>
310Sstevel@tonic-gate #include <strings.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <meta.h>
340Sstevel@tonic-gate #include <sys/utsname.h>
350Sstevel@tonic-gate #include <sys/lvm/md_mddb.h>
360Sstevel@tonic-gate #include <sys/lvm/md_names.h>
370Sstevel@tonic-gate #include <sdssc.h>
380Sstevel@tonic-gate
391945Sjeanm static md_im_drive_info_t *overlap_disks;
400Sstevel@tonic-gate
410Sstevel@tonic-gate static void
usage(mdsetname_t * sp,char * string)420Sstevel@tonic-gate usage(mdsetname_t *sp, char *string)
430Sstevel@tonic-gate {
440Sstevel@tonic-gate if ((string != NULL) && (*string != '\0'))
450Sstevel@tonic-gate md_eprintf("%s\n", string);
460Sstevel@tonic-gate
470Sstevel@tonic-gate (void) fprintf(stderr,
480Sstevel@tonic-gate "%s:\t%s -s setname [-n] [-f] [-v] [%s...]\n",
490Sstevel@tonic-gate gettext("usage"), myname, gettext("disk"));
500Sstevel@tonic-gate (void) fprintf(stderr, " %s -r [%s...]\n",
510Sstevel@tonic-gate myname, gettext("disk"));
520Sstevel@tonic-gate (void) fprintf(stderr, " %s -?\n", myname);
530Sstevel@tonic-gate (void) fprintf(stderr, " %s -V\n", myname);
540Sstevel@tonic-gate
550Sstevel@tonic-gate md_exit(sp, (string == NULL) ? 0 : 1);
560Sstevel@tonic-gate }
570Sstevel@tonic-gate
580Sstevel@tonic-gate static void
print_version(mdsetname_t * sp)590Sstevel@tonic-gate print_version(mdsetname_t *sp)
600Sstevel@tonic-gate {
610Sstevel@tonic-gate struct utsname curname;
620Sstevel@tonic-gate
630Sstevel@tonic-gate if (uname(&curname) == -1) {
640Sstevel@tonic-gate md_eprintf("%s\n", strerror(errno));
650Sstevel@tonic-gate md_exit(sp, 1);
660Sstevel@tonic-gate }
670Sstevel@tonic-gate
680Sstevel@tonic-gate (void) fprintf(stderr, "%s %s\n", myname, curname.version);
690Sstevel@tonic-gate
700Sstevel@tonic-gate md_exit(sp, 0);
710Sstevel@tonic-gate }
720Sstevel@tonic-gate
730Sstevel@tonic-gate /*
740Sstevel@tonic-gate * Returns 0 if there is no overlap, 1 otherwise
750Sstevel@tonic-gate */
760Sstevel@tonic-gate static int
set_disk_overlap(md_im_set_desc_t * misp)770Sstevel@tonic-gate set_disk_overlap(md_im_set_desc_t *misp)
780Sstevel@tonic-gate {
791945Sjeanm md_im_set_desc_t *next, *isp = misp;
801945Sjeanm md_im_drive_info_t *set_dr, *next_set_dr, **chain;
811945Sjeanm int is_overlap = 0;
821945Sjeanm md_im_drive_info_t *good_disk = NULL;
831945Sjeanm md_im_drive_info_t *d;
841945Sjeanm md_timeval32_t gooddisktime;
851945Sjeanm int disk_not_available = 0;
861945Sjeanm /*
871945Sjeanm * There are 2 ways we could get an "overlap" disk.
881945Sjeanm * One is if the ctd's are the same. The other is if
891945Sjeanm * the setcreatetimestamp on the disk doesn't agree with the
901945Sjeanm * "good" disk in the set. However, if we have a disk that is
911945Sjeanm * unavailable and the other instance of the ctd is available we
921945Sjeanm * really don't have a conflict. It's just that the unavailable ctd
931945Sjeanm * is it's "old" location and the available instance is a current
941945Sjeanm * location.
951945Sjeanm */
960Sstevel@tonic-gate for (; isp != NULL; isp = isp->mis_next) {
970Sstevel@tonic-gate for (next = isp->mis_next; next != NULL; next = next->mis_next) {
980Sstevel@tonic-gate for (set_dr = isp->mis_drives; set_dr != NULL;
991945Sjeanm set_dr = set_dr->mid_next) {
1001945Sjeanm if (set_dr->mid_available == MD_IM_DISK_NOT_AVAILABLE)
1011945Sjeanm disk_not_available = 1;
1021945Sjeanm else
1031945Sjeanm disk_not_available = 0;
1041945Sjeanm for (next_set_dr = next->mis_drives; next_set_dr != NULL;
1051945Sjeanm next_set_dr = next_set_dr->mid_next) {
1061945Sjeanm if (disk_not_available &&
1071945Sjeanm (next_set_dr->mid_available
1081945Sjeanm == MD_IM_DISK_AVAILABLE))
1091945Sjeanm continue;
1101945Sjeanm else if (!disk_not_available &&
1111945Sjeanm (next_set_dr->mid_available ==
1121945Sjeanm MD_IM_DISK_NOT_AVAILABLE))
1131945Sjeanm continue;
1141945Sjeanm if (strcmp(set_dr->mid_dnp->cname,
1151945Sjeanm next_set_dr->mid_dnp->cname) == 0) {
1160Sstevel@tonic-gate /*
1171945Sjeanm * Chain it, skip if
1181945Sjeanm * already there
1190Sstevel@tonic-gate */
1200Sstevel@tonic-gate if (overlap_disks == NULL) {
1210Sstevel@tonic-gate set_dr->overlap = NULL;
1221945Sjeanm set_dr->overlapped_disk = 1;
1231945Sjeanm next_set_dr->overlapped_disk = 1;
1240Sstevel@tonic-gate overlap_disks = set_dr;
1250Sstevel@tonic-gate } else {
1260Sstevel@tonic-gate for (chain = &overlap_disks;
1270Sstevel@tonic-gate *chain != NULL;
1280Sstevel@tonic-gate chain = &(*chain)->overlap) {
1290Sstevel@tonic-gate if (strcmp(set_dr->mid_dnp->cname,
1301945Sjeanm (*chain)->mid_dnp->cname) == 0)
1310Sstevel@tonic-gate break;
1320Sstevel@tonic-gate }
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate if (*chain == NULL) {
1350Sstevel@tonic-gate *chain = set_dr;
1360Sstevel@tonic-gate set_dr->overlap = NULL;
1371945Sjeanm set_dr->overlapped_disk = 1;
1381945Sjeanm next_set_dr->overlapped_disk = 1;
1390Sstevel@tonic-gate }
1400Sstevel@tonic-gate }
1410Sstevel@tonic-gate if (!is_overlap)
1420Sstevel@tonic-gate is_overlap = 1;
1430Sstevel@tonic-gate }
1441945Sjeanm }
1450Sstevel@tonic-gate }
1460Sstevel@tonic-gate }
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate
1491945Sjeanm for (isp = misp; isp != NULL; isp = isp->mis_next) {
1501945Sjeanm good_disk = pick_good_disk(isp);
1511945Sjeanm if (good_disk == NULL) {
1521945Sjeanm /* didn't find a good disk */
1531945Sjeanm continue;
1541945Sjeanm }
1551945Sjeanm gooddisktime = good_disk->mid_setcreatetimestamp;
1561945Sjeanm for (d = isp->mis_drives; d != NULL; d = d->mid_next) {
1571945Sjeanm if (d->mid_available == MD_IM_DISK_NOT_AVAILABLE)
1581945Sjeanm continue;
1591945Sjeanm /*
1601945Sjeanm * If the disk doesn't have the same set creation
1611945Sjeanm * time as the designated "good disk" we have a
1621945Sjeanm * time conflict/overlap situation. Mark the disk
1631945Sjeanm * as such.
1641945Sjeanm */
1651945Sjeanm if ((gooddisktime.tv_usec !=
1661945Sjeanm d->mid_setcreatetimestamp.tv_usec) ||
1671945Sjeanm (gooddisktime.tv_sec !=
1681945Sjeanm d->mid_setcreatetimestamp.tv_sec)) {
1691945Sjeanm d->overlapped_disk = 1;
1701945Sjeanm if (overlap_disks == NULL) {
1711945Sjeanm d->overlap = NULL;
1721945Sjeanm d->overlapped_disk = 1;
1731945Sjeanm overlap_disks = d;
1741945Sjeanm } else {
1751945Sjeanm for (chain = &overlap_disks;
1761945Sjeanm *chain != NULL;
1771945Sjeanm chain = &(*chain)->overlap) {
1781945Sjeanm if (strcmp(d->mid_dnp->cname,
1791945Sjeanm (*chain)->mid_dnp->cname)
1801945Sjeanm == 0) {
1811945Sjeanm break;
1821945Sjeanm }
1831945Sjeanm }
1841945Sjeanm
1851945Sjeanm if (*chain == NULL) {
1861945Sjeanm *chain = d;
1871945Sjeanm d->overlap = NULL;
1881945Sjeanm d->overlapped_disk = 1;
1891945Sjeanm }
1901945Sjeanm }
1911945Sjeanm if (!is_overlap)
1921945Sjeanm is_overlap = 1;
1931945Sjeanm }
1941945Sjeanm }
1951945Sjeanm }
1960Sstevel@tonic-gate return (is_overlap);
1970Sstevel@tonic-gate }
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate static void
report_overlap_recommendation()2000Sstevel@tonic-gate report_overlap_recommendation()
2010Sstevel@tonic-gate {
2020Sstevel@tonic-gate mddb_mb_t *mbp;
2030Sstevel@tonic-gate md_error_t status = mdnullerror;
2040Sstevel@tonic-gate md_error_t *ep = &status;
2050Sstevel@tonic-gate md_im_drive_info_t *d;
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate (void) fprintf(stdout, "%s\n", gettext("Warning: The following disks "
2080Sstevel@tonic-gate "have been detected in more than one set.\n"
2090Sstevel@tonic-gate "Import recommendation based upon set creation time.\n"
2100Sstevel@tonic-gate "Proceed with the import with caution."));
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate /*
2130Sstevel@tonic-gate * Look at all overlapping disks. Determine which slice
2140Sstevel@tonic-gate * would have a replica on it. i.e. either slice 7 or 6.
2150Sstevel@tonic-gate * Then read the master block. If the disk doesn't have a
2160Sstevel@tonic-gate * metadb on it, the master block is a dummy master block.
2170Sstevel@tonic-gate * Both dummy or normal master block contain the timestamp
2180Sstevel@tonic-gate * which is what we are after. Use this timestamp to issue
2190Sstevel@tonic-gate * the appropriate recommendation.
2200Sstevel@tonic-gate */
2210Sstevel@tonic-gate mbp = Malloc(DEV_BSIZE);
2220Sstevel@tonic-gate for (d = overlap_disks; d != NULL; d = d->overlap) {
2230Sstevel@tonic-gate mdname_t *rsp;
2240Sstevel@tonic-gate uint_t sliceno;
2250Sstevel@tonic-gate int fd = -1;
2260Sstevel@tonic-gate
2271945Sjeanm /*
2281945Sjeanm * If the disk isn't available (i.e. powered off or dead)
2291945Sjeanm * we can't read the master block timestamp and thus
2301945Sjeanm * cannot make a recommendation as to which set it belongs to.
2311945Sjeanm */
2321945Sjeanm if (d->mid_available != MD_IM_DISK_AVAILABLE) {
2331945Sjeanm (void) fprintf(stdout, " %s ", d->mid_dnp->cname);
2341945Sjeanm (void) fprintf(stdout,
2351945Sjeanm gettext(" - no recommendation can "
2361945Sjeanm "be made because disk is unavailable\n"));
2371945Sjeanm continue;
2381945Sjeanm }
2391945Sjeanm
2400Sstevel@tonic-gate if (meta_replicaslice(d->mid_dnp, &sliceno, ep) != 0)
2410Sstevel@tonic-gate continue;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate if (d->mid_dnp->vtoc.parts[sliceno].size == 0)
2440Sstevel@tonic-gate continue;
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate if ((rsp = metaslicename(d->mid_dnp, sliceno, ep)) == NULL)
2470Sstevel@tonic-gate continue;
2480Sstevel@tonic-gate if ((fd = open(rsp->rname, O_RDONLY| O_NDELAY)) < 0)
2490Sstevel@tonic-gate continue;
2500Sstevel@tonic-gate if (read_master_block(ep, fd, mbp, DEV_BSIZE) <= 0) {
2510Sstevel@tonic-gate (void) close(fd);
2520Sstevel@tonic-gate mdclrerror(ep);
2530Sstevel@tonic-gate continue;
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate (void) close(fd);
256*11053SSurya.Prakki@Sun.COM (void) fprintf(stdout, " %s ", d->mid_dnp->cname);
257*11053SSurya.Prakki@Sun.COM (void) fprintf(stdout, "%s: %s\n",
2581945Sjeanm gettext(" - must import with set "
2590Sstevel@tonic-gate "created at "), meta_print_time((md_timeval32_t *)
2600Sstevel@tonic-gate (&(mbp->mb_setcreatetime))));
2610Sstevel@tonic-gate }
2620Sstevel@tonic-gate Free(mbp);
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate
2651945Sjeanm /*
2661945Sjeanm * is_first_disk is called to determine if the disk passed to it is
2671945Sjeanm * eligible to be used as the "first disk time" in the set. It checks to
2681945Sjeanm * see if the disk is available, on the skip list or not (thus already in
2691945Sjeanm * an importable set) or being used by the system already.
2701945Sjeanm * RETURN:
2711945Sjeanm * 1 The time can be used as the first disk time
2721945Sjeanm * 0 The time should not be used.
2731945Sjeanm */
2741945Sjeanm static int
is_first_disk(md_im_drive_info_t * d,mddrivenamelist_t ** skiph)2751945Sjeanm is_first_disk(
2761945Sjeanm md_im_drive_info_t *d,
2771945Sjeanm mddrivenamelist_t **skiph)
2781945Sjeanm {
2791945Sjeanm mddrivenamelist_t *slp;
2801945Sjeanm md_error_t status = mdnullerror;
2811945Sjeanm md_error_t *ep = &status;
2821945Sjeanm mdsetname_t *sp = metasetname(MD_LOCAL_NAME, ep);
2831945Sjeanm
2841945Sjeanm /*
2851945Sjeanm * If a disk is not available there is no
2861945Sjeanm * set creation timestamp available.
2871945Sjeanm */
2881945Sjeanm if (d->mid_available == MD_IM_DISK_AVAILABLE) {
2891945Sjeanm /*
2901945Sjeanm * We also need to make sure this disk isn't already on
2911945Sjeanm * the skip list.
2921945Sjeanm */
2931945Sjeanm for (slp = *skiph; slp != NULL; slp = slp->next) {
2941945Sjeanm if (d->mid_dnp == slp->drivenamep)
2951945Sjeanm return (0);
2961945Sjeanm }
2971945Sjeanm /*
2981945Sjeanm * And we need to make sure the drive isn't
2991945Sjeanm * currently being used for something else
3001945Sjeanm * like a mounted file system or a current
3011945Sjeanm * metadevice or in a set.
3021945Sjeanm */
3031945Sjeanm if (meta_imp_drvused(sp, d->mid_dnp, ep)) {
3041945Sjeanm return (0);
3051945Sjeanm }
3061945Sjeanm } else {
3071945Sjeanm return (0);
3081945Sjeanm }
3091945Sjeanm return (1);
3101945Sjeanm }
3111945Sjeanm
3121945Sjeanm /*
3131945Sjeanm * Input a list of disks (dnlp), find the sets that are importable, create
3141945Sjeanm * a list of these sets (mispp), and a list of the disks within each of these
3151945Sjeanm * sets (midp). These lists (mispp and midp) will be used by metaimport.
3161945Sjeanm */
process_disks(mddrivenamelist_t * dnlp,mddrivenamelist_t ** skipt,md_im_set_desc_t ** mispp,int flags,int * set_count,int overlap,md_error_t * ep)3171945Sjeanm static int process_disks(
3181945Sjeanm mddrivenamelist_t *dnlp,
3191945Sjeanm mddrivenamelist_t **skipt,
3201945Sjeanm md_im_set_desc_t **mispp,
3211945Sjeanm int flags,
3221945Sjeanm int *set_count,
3231945Sjeanm int overlap,
3241945Sjeanm md_error_t *ep
3251945Sjeanm )
3261945Sjeanm {
3271945Sjeanm mddrivenamelist_t *dp;
3281945Sjeanm int rscount = 0;
3291945Sjeanm int hasreplica;
3301945Sjeanm md_im_set_desc_t *p;
3311945Sjeanm md_im_drive_info_t *d;
3321945Sjeanm mddrivenamelist_t **skiph = skipt;
3331945Sjeanm
3341945Sjeanm /* Scan qualified disks */
3351945Sjeanm for (dp = dnlp; dp != NULL; dp = dp->next) {
3361945Sjeanm mddrivenamelist_t *slp;
3371945Sjeanm
3381945Sjeanm /* is the current drive on the skip list? */
3391945Sjeanm for (slp = *skiph; slp != NULL; slp = slp->next) {
340*11053SSurya.Prakki@Sun.COM if (dp->drivenamep == slp->drivenamep)
341*11053SSurya.Prakki@Sun.COM break;
3421945Sjeanm }
3431945Sjeanm /* drive on the skip list ? */
3441945Sjeanm if (slp != NULL)
3451945Sjeanm continue;
3461945Sjeanm
3471945Sjeanm /*
3481945Sjeanm * In addition to updating the misp list, either verbose or
3491945Sjeanm * standard output will be generated.
3501945Sjeanm *
3511945Sjeanm */
3521945Sjeanm hasreplica = meta_get_and_report_set_info(dp, mispp, 0,
3531945Sjeanm flags, set_count, overlap, overlap_disks, ep);
3541945Sjeanm
3551945Sjeanm if (hasreplica < 0) {
3561945Sjeanm mde_perror(ep, "");
3571945Sjeanm mdclrerror(ep);
3581945Sjeanm } else {
3591945Sjeanm
3601945Sjeanm rscount += hasreplica;
3611945Sjeanm
3621945Sjeanm /* Eliminate duplicate reporting */
3631945Sjeanm if (hasreplica > 0) {
3641945Sjeanm md_timeval32_t firstdisktime;
3651945Sjeanm
3661945Sjeanm /*
3671945Sjeanm * Go to the tail for the current set
3681945Sjeanm */
3691945Sjeanm for (p = *mispp; p->mis_next != NULL;
370*11053SSurya.Prakki@Sun.COM p = p->mis_next)
371*11053SSurya.Prakki@Sun.COM ;
3721945Sjeanm
3731945Sjeanm /*
3741945Sjeanm * Now look for the set creation timestamp.
3751945Sjeanm * If a disk is not available there is no
3761945Sjeanm * set creation timestamp available so look
3771945Sjeanm * for the first available disk to grab this
3781945Sjeanm * information from. We also need to make
3791945Sjeanm * sure this disk isn't already on the skip
3801945Sjeanm * list. If so go to the next available drive.
3811945Sjeanm * And we need to make sure the drive isn't
3821945Sjeanm * currently being used for something else
3831945Sjeanm * like a mounted file system or a current
3841945Sjeanm * metadevice or in a set.
3851945Sjeanm */
3861945Sjeanm for (d = p->mis_drives; d != NULL;
3871945Sjeanm d = d->mid_next) {
3881945Sjeanm if (is_first_disk(d, skiph)) {
3891945Sjeanm firstdisktime =
3901945Sjeanm d->mid_setcreatetimestamp;
3911945Sjeanm break;
3921945Sjeanm }
3931945Sjeanm }
3941945Sjeanm for (d = p->mis_drives; d != NULL;
3951945Sjeanm d = d->mid_next) {
3961945Sjeanm /*
3971945Sjeanm * if the mb_setcreatetime for a disk
3981945Sjeanm * is not the same as the first disk
3991945Sjeanm * in the set, don't put it on the
4001945Sjeanm * skip list. This disk probably
4011945Sjeanm * doesn't really belong in this set
4021945Sjeanm * and we'll want to look at it again
4031945Sjeanm * to figure out where it does belong.
4041945Sjeanm * If the disk isn't available, there's
4051945Sjeanm * really no point in looking at it
4061945Sjeanm * again so put it on the skip list.
4071945Sjeanm */
4081945Sjeanm if (d->mid_available ==
4091945Sjeanm MD_IM_DISK_AVAILABLE) {
4101945Sjeanm if ((d->mid_setcreatetimestamp.
4111945Sjeanm tv_sec != firstdisktime.
4121945Sjeanm tv_sec) ||
4131945Sjeanm (d->mid_setcreatetimestamp.
4141945Sjeanm tv_usec !=
4151945Sjeanm firstdisktime.tv_usec))
4161945Sjeanm continue;
4171945Sjeanm }
4181945Sjeanm skipt =
4191945Sjeanm meta_drivenamelist_append_wrapper(
420*11053SSurya.Prakki@Sun.COM skipt, d->mid_dnp);
4211945Sjeanm }
4221945Sjeanm }
4231945Sjeanm }
4241945Sjeanm }
4251945Sjeanm return (rscount);
4261945Sjeanm }
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate int
main(int argc,char * argv[])4290Sstevel@tonic-gate main(int argc, char *argv[])
4300Sstevel@tonic-gate {
4310Sstevel@tonic-gate char c;
4320Sstevel@tonic-gate md_error_t status = mdnullerror;
4330Sstevel@tonic-gate md_error_t *ep = &status;
4340Sstevel@tonic-gate mdsetname_t *sp = NULL;
4350Sstevel@tonic-gate char *setname_new = NULL;
4360Sstevel@tonic-gate int report_only = 0;
4370Sstevel@tonic-gate int version = 0;
4380Sstevel@tonic-gate bool_t dry_run = 0;
4390Sstevel@tonic-gate md_im_names_t cnames = { 0, NULL };
4400Sstevel@tonic-gate int err_on_prune = 0;
4410Sstevel@tonic-gate mddrivenamelist_t *dnlp = NULL;
4420Sstevel@tonic-gate mddrivenamelist_t *dp;
4430Sstevel@tonic-gate mddrivenamelist_t *skiph = NULL;
4440Sstevel@tonic-gate int rscount = 0;
4451945Sjeanm md_im_set_desc_t *pass1_misp = NULL;
4460Sstevel@tonic-gate md_im_set_desc_t *misp = NULL;
4471945Sjeanm md_im_set_desc_t **pass1_mispp = &pass1_misp;
4480Sstevel@tonic-gate md_im_set_desc_t **mispp = &misp;
4490Sstevel@tonic-gate mhd_mhiargs_t mhiargs = defmhiargs;
4500Sstevel@tonic-gate int have_multiple_sets = 0;
4510Sstevel@tonic-gate int force = 0;
4520Sstevel@tonic-gate int overlap = 0;
453734Smw145384 uint_t imp_flags = 0;
454734Smw145384 int set_count = 0;
4551945Sjeanm int no_quorum = 0;
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate /*
4580Sstevel@tonic-gate * Get the locale set up before calling any other routines
4590Sstevel@tonic-gate * with messages to output. Just in case we're not in a build
4600Sstevel@tonic-gate * environment, make sure that TEXT_DOMAIN gets set to
4610Sstevel@tonic-gate * something.
4620Sstevel@tonic-gate */
4630Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4640Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
4650Sstevel@tonic-gate #endif
4660Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
4670Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate /*
4700Sstevel@tonic-gate * Check to see if the libsds_sc.so is bound on the
4710Sstevel@tonic-gate * current system. If it is, it means the system is
4720Sstevel@tonic-gate * part of a cluster.
4730Sstevel@tonic-gate *
4740Sstevel@tonic-gate * The import operation is currently not supported
4750Sstevel@tonic-gate * in a SunCluster environment.
4760Sstevel@tonic-gate */
4770Sstevel@tonic-gate if (sdssc_bind_library() != SDSSC_NOT_BOUND) {
478*11053SSurya.Prakki@Sun.COM (void) printf(gettext(
4790Sstevel@tonic-gate "%s: Import operation not supported under SunCluster\n"),
4800Sstevel@tonic-gate argv[0]);
4810Sstevel@tonic-gate exit(0);
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate
4840Sstevel@tonic-gate /* initialize */
4850Sstevel@tonic-gate if (md_init(argc, argv, 0, 1, ep) != 0) {
4860Sstevel@tonic-gate mde_perror(ep, "");
4870Sstevel@tonic-gate md_exit(sp, 1);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate
4900Sstevel@tonic-gate optind = 1;
4910Sstevel@tonic-gate opterr = 1;
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate while ((c = getopt(argc, argv, "frns:vV?")) != -1) {
4940Sstevel@tonic-gate switch (c) {
4950Sstevel@tonic-gate
4960Sstevel@tonic-gate case 'f':
4970Sstevel@tonic-gate force = 1;
4980Sstevel@tonic-gate break;
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate case 'n':
5010Sstevel@tonic-gate dry_run = 1;
5020Sstevel@tonic-gate break;
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate case 'r':
5050Sstevel@tonic-gate report_only = 1;
506734Smw145384 imp_flags |= META_IMP_REPORT;
5070Sstevel@tonic-gate break;
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate case 's':
5100Sstevel@tonic-gate setname_new = optarg;
5110Sstevel@tonic-gate break;
5120Sstevel@tonic-gate
5130Sstevel@tonic-gate case 'v':
514734Smw145384 imp_flags |= META_IMP_VERBOSE;
5150Sstevel@tonic-gate break;
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate case 'V':
5180Sstevel@tonic-gate version = 1;
5190Sstevel@tonic-gate break;
5200Sstevel@tonic-gate
5210Sstevel@tonic-gate case '?':
5220Sstevel@tonic-gate default:
5230Sstevel@tonic-gate usage(sp, NULL);
5240Sstevel@tonic-gate break;
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate }
5270Sstevel@tonic-gate
5280Sstevel@tonic-gate if (version == 1)
5290Sstevel@tonic-gate print_version(sp);
5300Sstevel@tonic-gate
5310Sstevel@tonic-gate /* Detect conflicting options */
5320Sstevel@tonic-gate if ((dry_run != 0) && (report_only != 0))
5330Sstevel@tonic-gate usage(sp, gettext("The -n and -r options conflict."));
5340Sstevel@tonic-gate
5350Sstevel@tonic-gate if ((report_only != 0) && (setname_new != NULL))
5360Sstevel@tonic-gate usage(sp, gettext("The -r and -s options conflict."));
5370Sstevel@tonic-gate
5380Sstevel@tonic-gate if ((report_only == 0) && (setname_new == NULL))
5390Sstevel@tonic-gate usage(sp, gettext("You must specify either -r or -s."));
5400Sstevel@tonic-gate
5410Sstevel@tonic-gate /* Don't do any real work if we don't have root privilege */
5420Sstevel@tonic-gate if (meta_check_root(ep) != 0) {
5430Sstevel@tonic-gate mde_perror(ep, "");
5440Sstevel@tonic-gate md_exit(sp, 1);
5450Sstevel@tonic-gate }
5460Sstevel@tonic-gate
5470Sstevel@tonic-gate if (meta_setup_db_locations(ep) != 0) {
5480Sstevel@tonic-gate mde_perror(ep, "");
5490Sstevel@tonic-gate if (mdismddberror(ep, MDE_DB_STALE))
5500Sstevel@tonic-gate md_exit(sp, 66);
5510Sstevel@tonic-gate if (! mdiserror(ep, MDE_MDDB_CKSUM))
5520Sstevel@tonic-gate md_exit(sp, 1);
5530Sstevel@tonic-gate }
5540Sstevel@tonic-gate
5550Sstevel@tonic-gate /*
5560Sstevel@tonic-gate * Read remaining arguments into drive name list, otherwise
5570Sstevel@tonic-gate * call routine to list all drives in system.
5580Sstevel@tonic-gate */
5590Sstevel@tonic-gate if (argc > optind) {
5600Sstevel@tonic-gate int i;
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate /* For user specified disks, they MUST not be in use */
5630Sstevel@tonic-gate err_on_prune = 1;
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate /* All remaining args should be disks */
5660Sstevel@tonic-gate cnames.min_count = argc - optind;
5670Sstevel@tonic-gate cnames.min_names = Malloc(cnames.min_count * sizeof (char *));
5680Sstevel@tonic-gate
5690Sstevel@tonic-gate for (i = 0; i < cnames.min_count; i++, optind++) {
5700Sstevel@tonic-gate mddrivename_t *dnp;
5710Sstevel@tonic-gate dnp = metadrivename(&sp, argv[optind], ep);
5720Sstevel@tonic-gate if (dnp == NULL) {
5730Sstevel@tonic-gate mde_perror(ep, "");
5740Sstevel@tonic-gate md_exit(sp, 1);
5750Sstevel@tonic-gate } else {
5760Sstevel@tonic-gate cnames.min_names[i] = dnp->rname;
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate } else {
5800Sstevel@tonic-gate if (meta_list_disks(ep, &cnames) != 0) {
5810Sstevel@tonic-gate mde_perror(ep, "");
5820Sstevel@tonic-gate md_exit(sp, 1);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate /*
5870Sstevel@tonic-gate * If the user specified disks on the command line, min_count will be
5880Sstevel@tonic-gate * greater than zero. If they didn't, it should be safe to assume that
5890Sstevel@tonic-gate * the system in question has at least one drive detected by the
5900Sstevel@tonic-gate * snapshot code, or we would have barfed earlier initializing the
5910Sstevel@tonic-gate * metadb.
5920Sstevel@tonic-gate */
5930Sstevel@tonic-gate assert(cnames.min_count > 0);
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate /*
5960Sstevel@tonic-gate * Prune the list:
5970Sstevel@tonic-gate * - get rid of drives in current svm configuration
5980Sstevel@tonic-gate * - get rid of mounted drives
5990Sstevel@tonic-gate * - get rid of swap drives
6000Sstevel@tonic-gate * - get rid of drives in other sets
6010Sstevel@tonic-gate *
6020Sstevel@tonic-gate * If drives were specified on the command line, it should be
6030Sstevel@tonic-gate * an error to find in-use disks in the list. (err_on_prune)
6040Sstevel@tonic-gate *
6050Sstevel@tonic-gate * On return from meta_prune_cnames call, dnlp
6060Sstevel@tonic-gate * will have candidate for replica scan.
6070Sstevel@tonic-gate */
6080Sstevel@tonic-gate dnlp = meta_prune_cnames(ep, &cnames, err_on_prune);
6090Sstevel@tonic-gate
6100Sstevel@tonic-gate /*
6110Sstevel@tonic-gate * Doctor the drive string in the error structure to list all of the
6120Sstevel@tonic-gate * unused disks, rather than just one. The output will be done in the
6130Sstevel@tonic-gate * following !mdisok() block.
6140Sstevel@tonic-gate */
6150Sstevel@tonic-gate if (mdisdserror(ep, MDE_DS_DRIVEINUSE)) {
6160Sstevel@tonic-gate md_ds_error_t *ip =
6170Sstevel@tonic-gate &ep->info.md_error_info_t_u.ds_error;
6180Sstevel@tonic-gate char *dlist;
6190Sstevel@tonic-gate int sizecnt = 0;
6200Sstevel@tonic-gate
6211945Sjeanm /* add 1 for null terminator */
6221945Sjeanm sizecnt += strlen(ip->drive) + 1;
6230Sstevel@tonic-gate for (dp = dnlp->next; dp != NULL; dp = dp->next) {
6240Sstevel@tonic-gate sizecnt += 2; /* for the ", " */
6250Sstevel@tonic-gate sizecnt += strlen(dp->drivenamep->cname);
6260Sstevel@tonic-gate }
6270Sstevel@tonic-gate
6280Sstevel@tonic-gate dlist = Malloc(sizecnt);
6290Sstevel@tonic-gate
630*11053SSurya.Prakki@Sun.COM (void) strlcpy(dlist, ip->drive, sizecnt);
6311945Sjeanm
6320Sstevel@tonic-gate Free(ip->drive);
6330Sstevel@tonic-gate for (dp = dnlp->next; dp != NULL; dp = dp->next) {
634*11053SSurya.Prakki@Sun.COM (void) strlcat(dlist, ", ", sizecnt);
635*11053SSurya.Prakki@Sun.COM (void) strlcat(dlist, dp->drivenamep->cname, sizecnt);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate
6381945Sjeanm ip->drive = dlist;
6390Sstevel@tonic-gate }
6400Sstevel@tonic-gate
6410Sstevel@tonic-gate /* Don't continue if we're already hosed */
6420Sstevel@tonic-gate if (!mdisok(ep)) {
6430Sstevel@tonic-gate mde_perror(ep, "");
6440Sstevel@tonic-gate md_exit(sp, 1);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate
6470Sstevel@tonic-gate /* ...or if there's nothing to scan */
6480Sstevel@tonic-gate if (dnlp == NULL) {
6490Sstevel@tonic-gate md_eprintf("%s\n", gettext("no unused disks detected"));
6500Sstevel@tonic-gate md_exit(sp, 0);
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate
6531945Sjeanm /*
6541945Sjeanm * META_IMP_PASS1 means gather the info, but don't report.
6551945Sjeanm */
6561945Sjeanm (void) process_disks(dnlp, &skiph, pass1_mispp,
6571945Sjeanm imp_flags | META_IMP_PASS1, &set_count, overlap, ep);
6580Sstevel@tonic-gate
6591945Sjeanm overlap_disks = NULL;
6601945Sjeanm overlap = set_disk_overlap(pass1_misp);
6611945Sjeanm skiph = NULL;
6620Sstevel@tonic-gate
6631945Sjeanm /*
6641945Sjeanm * This time call without META_IMP_PASS1 set and we gather
6651945Sjeanm * and report the information.
6661945Sjeanm * We need to do this twice because of the overlap detection.
6671945Sjeanm * The first pass generates a list of disks to detect overlap on.
6681945Sjeanm * We then do a second pass using that overlap list to generate
6691945Sjeanm * the report.
6701945Sjeanm */
6711945Sjeanm rscount = process_disks(dnlp, &skiph, mispp, imp_flags, &set_count,
6721945Sjeanm overlap, ep);
6730Sstevel@tonic-gate
6740Sstevel@tonic-gate /*
6750Sstevel@tonic-gate * Now have entire list of disks associated with diskset including
6760Sstevel@tonic-gate * disks listed in mddb locator blocks and namespace. Before importing
6770Sstevel@tonic-gate * diskset need to recheck that none of these disks is already in use.
6780Sstevel@tonic-gate * If a disk is found that is already in use, print error and exit.
6790Sstevel@tonic-gate */
6800Sstevel@tonic-gate if (!report_only) {
6810Sstevel@tonic-gate md_im_set_desc_t *p;
6820Sstevel@tonic-gate md_im_drive_info_t *d;
6830Sstevel@tonic-gate mddrivename_t *dnp;
6840Sstevel@tonic-gate
6851945Sjeanm if (sp == NULL) {
6861945Sjeanm /* Get sp for local set */
6871945Sjeanm if ((sp = metasetname(MD_LOCAL_NAME, ep)) == NULL) {
6881945Sjeanm mde_perror(ep, "");
6891945Sjeanm meta_free_im_set_desc(misp);
6901945Sjeanm md_exit(sp, 1);
6911945Sjeanm }
6921945Sjeanm }
6931945Sjeanm
6940Sstevel@tonic-gate for (p = misp; p != NULL; p = p->mis_next) {
6950Sstevel@tonic-gate for (d = p->mis_drives; d != NULL; d = d->mid_next) {
6960Sstevel@tonic-gate dnp = d->mid_dnp;
6971945Sjeanm if (d->mid_available == MD_IM_DISK_AVAILABLE) {
6981945Sjeanm if (meta_imp_drvused(sp, dnp, ep)) {
6991945Sjeanm (void) mddserror(ep,
7001945Sjeanm MDE_DS_DRIVEINUSE, 0, NULL,
7011945Sjeanm dnp->cname, NULL);
7021945Sjeanm mde_perror(ep, "");
7031945Sjeanm meta_free_im_set_desc(misp);
7041945Sjeanm md_exit(sp, 1);
7051945Sjeanm }
7061945Sjeanm } else {
7071945Sjeanm /*
7081945Sjeanm * If drive is unavailable, then check
7091945Sjeanm * that this drive hasn't already been
7101945Sjeanm * imported as part of another partial
7111945Sjeanm * diskset. Check by devid instead of
7121945Sjeanm * cname since the unavailable drive
7131945Sjeanm * would have the cname from its
7141945Sjeanm * previous system and this may collide
7151945Sjeanm * with a valid cname on this system.
7161945Sjeanm * Fail if devid is found in another
7171945Sjeanm * set or if the routine fails.
7181945Sjeanm */
7191945Sjeanm mdsetname_t *tmp_sp = NULL;
7201945Sjeanm
7211945Sjeanm if ((meta_is_devid_in_anyset(
7221945Sjeanm d->mid_devid, &tmp_sp, ep) == -1) ||
7231945Sjeanm (tmp_sp != NULL)) {
7241945Sjeanm (void) mddserror(ep,
7251945Sjeanm MDE_DS_DRIVEINUSE, 0, NULL,
7261945Sjeanm dnp->cname, NULL);
7271945Sjeanm mde_perror(ep, "");
7281945Sjeanm meta_free_im_set_desc(misp);
7291945Sjeanm md_exit(sp, 1);
7301945Sjeanm }
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate }
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate }
7350Sstevel@tonic-gate
7360Sstevel@tonic-gate /*
7370Sstevel@tonic-gate * If there are no unconfigured sets, then our work here is done.
7380Sstevel@tonic-gate * Hopefully this is friendlier than just not printing anything at all.
7390Sstevel@tonic-gate */
7400Sstevel@tonic-gate if (rscount == 0) {
74157Sjeanm /*
74257Sjeanm * If we've found partial disksets but no complete disksets,
74357Sjeanm * we don't want this to print.
74457Sjeanm */
7451945Sjeanm if (!misp) {
74657Sjeanm md_eprintf("%s\n", gettext("no unconfigured sets "
74757Sjeanm "detected"));
7481945Sjeanm meta_free_im_set_desc(misp);
7491945Sjeanm md_exit(sp, 1);
75057Sjeanm }
7510Sstevel@tonic-gate md_exit(sp, 0);
7520Sstevel@tonic-gate }
7530Sstevel@tonic-gate
7540Sstevel@tonic-gate /*
7550Sstevel@tonic-gate * We'll need this info for both the report content and the import
7560Sstevel@tonic-gate * decision. By the time we're here, misp should NOT be NULL (or we
7570Sstevel@tonic-gate * would have exited in the rscount == 0 test above).
7580Sstevel@tonic-gate */
7590Sstevel@tonic-gate assert(misp != NULL);
7600Sstevel@tonic-gate if (misp->mis_next != NULL) {
7610Sstevel@tonic-gate have_multiple_sets = 1;
7620Sstevel@tonic-gate }
7630Sstevel@tonic-gate /*
7640Sstevel@tonic-gate * Generate the appropriate (verbose or not) report for all sets
7650Sstevel@tonic-gate * detected. If we're planning on importing later, only include the
7660Sstevel@tonic-gate * "suggested import" command if multiple sets were detected. (That
7670Sstevel@tonic-gate * way, when we error out later, we have still provided useful
7680Sstevel@tonic-gate * information.)
7690Sstevel@tonic-gate */
7700Sstevel@tonic-gate
7710Sstevel@tonic-gate /*
7720Sstevel@tonic-gate * Now we should have all the unconfigured sets detected
7730Sstevel@tonic-gate * check for the overlapping
7740Sstevel@tonic-gate */
7750Sstevel@tonic-gate if (have_multiple_sets) {
776734Smw145384 /* Printing out how many candidate disksets we found. */
777734Smw145384 if (imp_flags & META_IMP_REPORT) {
778734Smw145384 (void) printf("%s: %i\n\n",
779734Smw145384 gettext("Number of disksets eligible for import"),
780734Smw145384 set_count);
781734Smw145384 }
7821945Sjeanm }
7831945Sjeanm if (overlap) {
7841945Sjeanm report_overlap_recommendation();
7851945Sjeanm }
786734Smw145384
7871945Sjeanm if (have_multiple_sets && !report_only) {
7881945Sjeanm md_eprintf("%s\n\n", gettext("multiple unconfigured "
7891945Sjeanm "sets detected.\nRerun the command with the "
7901945Sjeanm "suggested options for the desired set."));
7910Sstevel@tonic-gate }
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate /*
7950Sstevel@tonic-gate * If it's a report-only request, we're done. If it's an import
7960Sstevel@tonic-gate * request, make sure that we only have one entry in the set list.
7970Sstevel@tonic-gate */
7980Sstevel@tonic-gate
7990Sstevel@tonic-gate if (report_only) {
8001945Sjeanm meta_free_im_set_desc(misp);
8010Sstevel@tonic-gate md_exit(sp, 0);
8020Sstevel@tonic-gate } else if (have_multiple_sets) {
8031945Sjeanm meta_free_im_set_desc(misp);
8041945Sjeanm md_exit(sp, 1);
8051945Sjeanm } else if (overlap) {
8061945Sjeanm md_im_drive_info_t *d;
8071945Sjeanm /*
8081945Sjeanm * The only way we can get here is if we're doing an import
8091945Sjeanm * request on a set that contains at least one disk with
8101945Sjeanm * a time conflict. We are prohibiting the importation of
8111945Sjeanm * this type of set until the offending disk(s) are turned
8121945Sjeanm * off to prevent data corruption.
8131945Sjeanm */
814*11053SSurya.Prakki@Sun.COM (void) printf(gettext("To import this set, "));
8151945Sjeanm for (d = pass1_misp->mis_drives;
8161945Sjeanm d != NULL;
8171945Sjeanm d = d->mid_next) {
8181945Sjeanm if (d->overlapped_disk)
819*11053SSurya.Prakki@Sun.COM (void) printf("%s ", d->mid_dnp->cname);
8201945Sjeanm }
821*11053SSurya.Prakki@Sun.COM (void) printf(gettext("must be removed from the system\n"));
8221945Sjeanm meta_free_im_set_desc(misp);
8230Sstevel@tonic-gate md_exit(sp, 1);
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate
8260Sstevel@tonic-gate if (setname_new == NULL) {
8270Sstevel@tonic-gate usage(sp, gettext("You must specify a new set name."));
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate
8301945Sjeanm /*
8311945Sjeanm * The user must specify the -f (force) flag if the following
8321945Sjeanm * conditions exist:
8331945Sjeanm * - partial diskset
8341945Sjeanm * - stale diskset
8351945Sjeanm */
8361945Sjeanm if (meta_replica_quorum(misp) != 0)
8371945Sjeanm no_quorum = 1;
8381945Sjeanm if (misp->mis_partial || no_quorum) {
8391945Sjeanm if (!force)
8401945Sjeanm usage(sp, gettext("You must specify the force flag"));
8411945Sjeanm }
8420Sstevel@tonic-gate (void) meta_imp_set(misp, setname_new, force, dry_run, ep);
8430Sstevel@tonic-gate if (dry_run) {
8441945Sjeanm meta_free_im_set_desc(misp);
8450Sstevel@tonic-gate md_exit(sp, 0);
8460Sstevel@tonic-gate }
8470Sstevel@tonic-gate
8480Sstevel@tonic-gate if (!mdisok(ep)) {
8491945Sjeanm meta_free_im_set_desc(misp);
8500Sstevel@tonic-gate mde_perror(ep, "");
8510Sstevel@tonic-gate md_exit(sp, 1);
8520Sstevel@tonic-gate }
8530Sstevel@tonic-gate
8540Sstevel@tonic-gate if ((sp = metasetname(setname_new, ep)) == NULL) {
8551945Sjeanm meta_free_im_set_desc(misp);
8560Sstevel@tonic-gate mde_perror(ep, "");
8570Sstevel@tonic-gate md_exit(sp, 1);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate
8600Sstevel@tonic-gate if (meta_lock_nowait(sp, ep) != 0) {
8611945Sjeanm meta_free_im_set_desc(misp);
8620Sstevel@tonic-gate mde_perror(ep, "");
8630Sstevel@tonic-gate md_exit(sp, 10); /* special errcode */
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate
8661945Sjeanm if (meta_set_take(sp, &mhiargs, (misp->mis_partial | TAKE_IMP),
8671945Sjeanm 0, &status)) {
8681945Sjeanm meta_free_im_set_desc(misp);
8690Sstevel@tonic-gate mde_perror(&status, "");
8700Sstevel@tonic-gate md_exit(sp, 1);
8710Sstevel@tonic-gate }
8720Sstevel@tonic-gate
8731945Sjeanm meta_free_im_set_desc(misp);
8740Sstevel@tonic-gate md_exit(sp, 0);
8750Sstevel@tonic-gate /*NOTREACHED*/
8760Sstevel@tonic-gate return (0);
8770Sstevel@tonic-gate }
878