From: Neil Brown Date: Thu, 23 Aug 2001 02:33:20 +0000 (+0000) Subject: mdctl-0.5 X-Git-Tag: mdctl-0.5 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=commitdiff_plain;h=52826846282e9e224e05dde6d2e4cb38d1fefec7 mdctl-0.5 --- diff --git a/Assemble.c b/Assemble.c index e5210ce6..272602e9 100644 --- a/Assemble.c +++ b/Assemble.c @@ -28,18 +28,20 @@ */ #include "mdctl.h" -#include "md_p.h" #include "md_u.h" +#include "md_p.h" int Assemble(char *mddev, int mdfd, - int uuid[4], int uuidset, - char *conffile, int scan, + mddev_ident_t ident, char *conffile, int subdevs, char **subdev, int readonly, int runstop, int verbose, int force) { /* - * The task of Assemble is to submit a + * The task of Assemble is to find a collection of + * devices that should (according to their superblocks) + * form an array, and to give this collection to the MD driver. + * In Linux-2.4 and later, this involves submitting a * SET_ARRAY_INFO ioctl with no arg - to prepare * the array - and then submit a number of * ADD_NEW_DISK ioctls to add disks into @@ -100,52 +102,15 @@ int Assemble(char *mddev, int mdfd, long long events; time_t utime; int uptodate; + int raid_disk; } devices[MD_SB_DISKS]; int best[MD_SB_DISKS]; /* indexed by raid_disk */ - int devcnt = 0, okcnt; + int devcnt = 0, okcnt, sparecnt; int i; int most_recent = 0; + int chosen_drive = -1; + int change = 0; - if (!mddev && !scan) { - fputs(Name ": internal error - Assemble called with no device or --scan\n", stderr); - return 1; - } - if (!mddev) { - mddev_uuid_t device_list; - int found = 0; - device_list = conf_get_uuids(conffile); - if (!device_list) { - fprintf(stderr, Name ": No devices found in config file\n"); - return 1; - } - for (; device_list; device_list=device_list->next) { - if (!uuidset || same_uuid(device_list->uuid,uuid)) { - mdfd = open(device_list->devname, O_RDONLY, 0); - if (mdfd < 0) { - fprintf(stderr, - Name ": error opening %s: %s\n", - device_list->devname, - strerror(errno)); - continue; - } - if (Assemble(device_list->devname, mdfd, - device_list->uuid, 1, - conffile, 1, - subdevs, subdev, - readonly, runstop, verbose, force)==0) - found++; - close(mdfd); - } - } - if (found) - return 0; - fprintf(stderr,Name ": Did not successfully Assemble any devices\n"); - return 1; - } - - /* - * Ok, we have an mddev, check it out - */ vers = md_get_version(mdfd); if (vers <= 0) { fprintf(stderr, Name ": %s appears not to be an md device.\n"); @@ -153,7 +118,7 @@ int Assemble(char *mddev, int mdfd, } if (vers < 9000) { fprintf(stderr, Name ": Assemble requires driver version 0.90.0 or later.\n" - " Upgrade your kernel or try --Build\n"); + " Upgrade your kernel or try --build\n"); return 1; } if (get_linux_version() < 2004000) @@ -167,40 +132,24 @@ int Assemble(char *mddev, int mdfd, ioctl(mdfd, STOP_ARRAY, NULL); /* just incase it was started but has no content */ /* - * We have a valid mddev, check out uuid + * If any subdevs are listed, then any that don't + * match ident are discarded. Remainder must all match and + * become the array. + * If no subdevs, then we scan all devices in the config file, but + * there must be something in the identity */ - if (!uuidset && scan) { - /* device must be listed with uuid in conf file */ - mddev_uuid_t device_list; - device_list = conf_get_uuids(conffile); - while (device_list && - strcmp(device_list->devname, mddev) != 0) - device_list = device_list->next; - if (!device_list) { - fprintf(stderr, Name ": --scan set and no uuid found for %s in config file.\n", - mddev); - return 1; - } - /* the uuid is safe until next call to conf_get_uuids */ - uuid = device_list->uuid; - uuidset = 1; + if (subdevs == 0 && + ident->uuid_set == 0 && + ident->super_minor < 0 && + ident->devices == NULL) { + fprintf(stderr, Name ": No identity information available for %s - cannot assemble.\n", + mddev); + return 1; } - - /* Now to start looking at devices. - * If no devices were given, but a uuid is available and - * --scan was set, then we should scan all devices listed in the - * config file - * - */ - if (subdevs==0 && scan && uuidset) + if (subdevs==0) devlist = conf_get_devs(conffile); - if (subdevs == 0 && devlist == NULL) { - fprintf(stderr, Name ": no devices given for %s\n", mddev); - return 1; - } - /* now for each device */ first_super.md_magic = 0; for (i=0; idevices && + !match_oneof(ident->devices, devname)) + continue; + dfd = open(devname, O_RDONLY, 0); if (dfd < 0) { if (inargv || verbose) fprintf(stderr, Name ": cannot open device %s: %s\n", devname, strerror(errno)); - continue; - } - if (fstat(dfd, &stb)< 0) { - /* Impossible! */ - fprintf(stderr, Name ": fstat failed for %s: %s\n", - devname, strerror(errno)); - close(dfd); - continue; - } - if ((stb.st_mode & S_IFMT) != S_IFBLK) { - fprintf(stderr, Name ": %d is not a block device.\n", - devname); - close(dfd); - continue; - } - if (load_super(dfd, &super)) { + } else if (fstat(dfd, &stb)< 0) { + /* Impossible! */ + fprintf(stderr, Name ": fstat failed for %s: %s\n", + devname, strerror(errno)); + close(dfd); + } if ((stb.st_mode & S_IFMT) != S_IFBLK) { + fprintf(stderr, Name ": %d is not a block device.\n", + devname); + close(dfd); + } if (load_super(dfd, &super)) { if (inargv || verbose) fprintf( stderr, Name ": no RAID superblock on %s\n", devname); close(dfd); - continue; + } else { + havesuper =1; + uuid_from_super(this_uuid, &super); + close(dfd); } - close(dfd); - uuid_from_super(this_uuid, &super); - if (uuidset && !same_uuid(this_uuid, uuid)) { - if (inargv || verbose) - fprintf(stderr, Name ": %s has wrong uuid.\n", - devname); - continue; + + if (ident->uuid_set && + (!havesuper || same_uuid(this_uuid, ident->uuid)==0)) { + if (inargv || verbose) + fprintf(stderr, Name ": %s has wrong uuid.\n", + devname); + continue; } - if (compare_super(&first_super, &super)) { + if (ident->super_minor >= 0 && + (!havesuper || ident->super_minor != super.md_minor)) { if (inargv || verbose) - fprintf(stderr, Name ": superblock on %s doesn't match\n", + fprintf(stderr, Name ": %s has wrong super-minor.\n", devname); continue; } - if (uuidset) { - uuid_from_super(this_uuid, &first_super); - if (!same_uuid(this_uuid, uuid)) { - if (inargv || verbose) - fprintf(stderr, Name ": %s has wrong uuid.\n", - devname); - continue; - } - } else { - uuid_from_super(uuid, &first_super); - uuidset = 1; + + /* If we are this far, then we are commited to this device. + * If the super_block doesn't exist, or doesn't match others, + * then we cannot continue + */ + if (verbose) + fprintf(stderr, Name ": %s is identified as a member of %s.\n", + devname, mddev); + + if (!havesuper) { + fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", + devname); + return 1; + } + if (compare_super(&first_super, &super)) { + fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n", + devname); + return 1; } - /* Ok, this one is at least worth considering */ if (devcnt >= MD_SB_DISKS) { fprintf(stderr, Name ": ouch - too many devices appear to be in this array. Ignoring %s\n", devname); @@ -290,17 +249,19 @@ int Assemble(char *mddev, int mdfd, devices[devcnt].minor = MINOR(stb.st_rdev); devices[devcnt].events = md_event(&super); devices[devcnt].utime = super.utime; + devices[devcnt].raid_disk = super.this_disk.raid_disk; devices[devcnt].uptodate = 0; if (most_recent < devcnt) { if (devices[devcnt].events > devices[most_recent].events) most_recent = devcnt; } - i = super.this_disk.raid_disk; - if (best[i] == -1 - || devices[best[i]].events < devices[devcnt].events) { - best[i] = devcnt; - } + i = devices[devcnt].raid_disk; + if (i>=0 && i < MD_SB_DISKS) + if (best[i] == -1 + || devices[best[i]].events < devices[devcnt].events) + best[i] = devcnt; + devcnt++; } @@ -313,13 +274,17 @@ int Assemble(char *mddev, int mdfd, * I wonder how many */ okcnt = 0; - for (i=0; i< first_super.raid_disks;i++) { + sparecnt=0; + for (i=0; i< MD_SB_DISKS;i++) { int j = best[i]; if (j < 0) continue; if (devices[j].events+1 >= devices[most_recent].events) { devices[j].uptodate = 1; - okcnt++; + if (i < first_super.raid_disks) + okcnt++; + else + sparecnt++; } } while (force && !enough(first_super.level, first_super.raid_disks, okcnt)) { @@ -327,10 +292,133 @@ int Assemble(char *mddev, int mdfd, * not up-to-date, update the superblock * and add it. */ - fprintf(stderr,"NotImplementedYet\n"); - /* FIXME */ - exit(2); + int fd; + for (i=0; i=0 && + !devices[j].uptodate && + devices[j].events > 0 && + (chosen_drive < 0 || + devices[j].events > devices[chosen_drive].events)) + chosen_drive = j; + } + if (chosen_drive < 0) + break; + fprintf(stderr, Name ": forcing event count in %s(%d) from %d upto %d\n", + devices[chosen_drive].devname, devices[chosen_drive].raid_disk, + (int)(devices[chosen_drive].events), + (int)(devices[most_recent].events)); + fd = open(devices[chosen_drive].devname, O_RDWR); + if (fd < 0) { + fprintf(stderr, Name ": Couldn't open %s for write - not updating\n", + devices[chosen_drive].devname); + devices[chosen_drive].events = 0; + continue; + } + if (load_super(fd, &super)) { + close(fd); + fprintf(stderr, Name ": RAID superblock disappeared from %s - not updating.\n", + devices[chosen_drive].devname); + devices[chosen_drive].events = 0; + continue; + } + super.events_hi = (devices[most_recent].events>>32)&0xFFFFFFFF; + super.events_lo = (devices[most_recent].events)&0xFFFFFFFF; + super.sb_csum = calc_sb_csum(&super); +/*DRYRUN*/ if (store_super(fd, &super)) { + close(fd); + fprintf(stderr, Name ": Could not re-write superblock on %s\n", + devices[chosen_drive].devname); + devices[chosen_drive].events = 0; + continue; + } + close(fd); + devices[chosen_drive].events = devices[most_recent].events; + devices[chosen_drive].uptodate = 1; + okcnt++; } + + /* Now we want to look at the superblock which the kernel will base things on + * and compare the devices that we think are working with the devices that the + * superblock thinks are working. + * If there are differences and --force is given, then update this chosen + * superblock. + */ + for (i=0; chosen_drive < 0 && i= 0 && devices[j].uptodate) { mdu_disk_info_t disk; memset(&disk, 0, sizeof(disk)); @@ -351,18 +446,27 @@ int Assemble(char *mddev, int mdfd, devices[j].devname, mddev, strerror(errno)); - okcnt--; - } - } else if (verbose) + if (i < first_super.raid_disks) + okcnt--; + else + sparecnt--; + } else if (verbose) + fprintf(stderr, Name ": added %s to %s as %d\n", + devices[j].devname, mddev, devices[j].raid_disk); + } else if (verbose && i < first_super.raid_disks) fprintf(stderr, Name ": no uptodate device for slot %d of %s\n", i, mddev); } + if (runstop == 1 || (runstop == 0 && enough(first_super.level, first_super.raid_disks, okcnt))) { if (ioctl(mdfd, RUN_ARRAY, NULL)==0) { - fprintf(stderr, Name ": %s has been started with %d drives\n", - mddev, okcnt); + fprintf(stderr, Name ": %s has been started with %d drive%s", + mddev, okcnt, okcnt==1?"":"s"); + if (sparecnt) + fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); + fprintf(stderr, ".\n"); return 0; } fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n", @@ -370,68 +474,19 @@ int Assemble(char *mddev, int mdfd, return 1; } if (runstop == -1) { - fprintf(stderr, Name ": %s assembled from %d drives, but not started.\n", - mddev, okcnt); + fprintf(stderr, Name ": %s assembled from %d drive%s, but not started.\n", + mddev, okcnt, okcnt==1?"":"s"); return 0; } - fprintf(stderr, Name ": %s assembled from %d drives - not enough to start it.\n", - mddev, okcnt); + fprintf(stderr, Name ": %s assembled from %d drive%s - not enough to start it.\n", + mddev, okcnt, okcnt==1?"":"s"); return 1; } else { - /* It maybe just a case of calling START_ARRAY, but it may not.. - * We need to pick a working device, read it's super block, and make - * sure all the device numbers and the minor number are right. - * Then we might need to re-write the super block. - * THEN we call START_ARRAY - * If the md_minor is wrong, wejust give up for now. The alternate is to - * re-write ALL super blocks. + /* The "chosen_drive" is a good choice, and if necessary, the superblock has + * been updated to point to the current locations of devices. + * so we can just start the array */ - int chosen_drive = -1; - int change = 0; int dev; - for (i=0; i= 0) { - fprintf(stderr, Name ": Cannot open %s: %s\n", - devices[i].devname, strerror(errno)); - return 1; - } - if (load_super(fd, &super)) { - close(fd); - fprintf(stderr, Name ": RAID superblock has disappeared from %s\n", - devices[i].devname); - return 1; - } - close(fd); - } - if (devices[i].major != super.disks[i].major || - devices[i].minor != super.disks[i].minor) { - change = 1; - super.disks[i].major = devices[i].major; - super.disks[i].minor = devices[i].minor; - } - } - if (change) { - int fd; - super.sb_csum = calc_sb_csum(super); - fd = open(devices[chosen_drive].devname, O_RDWR); - if (fd < 0) { - fprintf(stderr, Name ": Could open %s for write - cannot Assemble array.\n", - devices[chosen_drive].devname); - return 1; - } - if (store_super(fd, &super)) { - close(fd); - fprintf(stderr, Name ": Could not re-write superblock on %s\n", - devices[chosen_drive].devname); - return 1; - } - close(fd); - } dev = MKDEV(devices[chosen_drive].major, devices[chosen_drive].minor); if (ioctl(mdfd, START_ARRAY, dev)) { diff --git a/Create.c b/Create.c index 0f477bce..c7d35979 100644 --- a/Create.c +++ b/Create.c @@ -34,7 +34,7 @@ int Create(char *mddev, int mdfd, int chunk, int level, int layout, int size, int raiddisks, int sparedisks, int subdevs, char *subdev[], - int runstop, int verbose) + int runstop, int verbose, int force) { /* * Create a new raid array. @@ -57,13 +57,16 @@ int Create(char *mddev, int mdfd, int i; int fail=0, warn=0; struct stat stb; + int first_missing = MD_SB_DISKS*2; + int missing_disks = 0; + int insert_point = MD_SB_DISKS*2; /* where to insert a missing drive */ mdu_array_info_t array; if (md_get_version(mdfd) < 9000) { - fprintf(stderr, Name ": Create requires md driver verison 0.90.0 or later\n"); - return 1; + fprintf(stderr, Name ": Create requires md driver verison 0.90.0 or later\n"); + return 1; } if (level == -10) { fprintf(stderr, @@ -75,6 +78,11 @@ int Create(char *mddev, int mdfd, Name ": a number of --raid-disks must be given to create an array\n"); return 1; } + if (raiddisks < 2 && level >= 4) { + fprintf(stderr, + Name ": atleast 2 raid-disks needed for level 4 or 5\n"); + return 1; + } if (raiddisks+sparedisks > MD_SB_DISKS) { fprintf(stderr, Name ": too many discs requested: %d+%d > %d\n", @@ -82,9 +90,14 @@ int Create(char *mddev, int mdfd, return 1; } if (subdevs > raiddisks+sparedisks) { - fprintf(stderr, Name ": You have listed more disks (%d) than are in the array(%d)!\n", subdevs, raiddisks+sparedisks); - return 1; + fprintf(stderr, Name ": You have listed more disks (%d) than are in the array(%d)!\n", subdevs, raiddisks+sparedisks); + return 1; } + if (subdevs < raiddisks) { + fprintf(stderr, Name ": You haven't given enough devices (real or missing) to create this array\n"); + return 1; + } + /* now set some defaults */ if (layout == -1) switch(level) { @@ -106,10 +119,22 @@ int Create(char *mddev, int mdfd, } /* now look at the subdevs */ + array.active_disks = 0; + array.working_disks = 0; for (i=0; i i) + first_missing = i; + missing_disks ++; + continue; + } + array.working_disks++; + if (i < raiddisks) + array.active_disks++; + fd = open(dname, O_RDONLY, 0); if (fd <0 ) { fprintf(stderr, Name ": Cannot open %s: %s\n", dname, strerror(errno)); @@ -124,29 +149,29 @@ int Create(char *mddev, int mdfd, continue; } if (dsize < MD_RESERVED_SECTORS*2) { - fprintf(stderr, Name ": %s is too small: %dK\n", - dname, dsize/2); - fail = 1; - close(fd); - continue; + fprintf(stderr, Name ": %s is too small: %dK\n", + dname, dsize/2); + fail = 1; + close(fd); + continue; } freesize = MD_NEW_SIZE_SECTORS(dsize); freesize /= 2; if (size && freesize < size) { - fprintf(stderr, Name ": %s is smaller that given size." - " %dK < %dK + superblock\n", dname, freesize, size); - fail = 1; - close(fd); - continue; + fprintf(stderr, Name ": %s is smaller that given size." + " %dK < %dK + superblock\n", dname, freesize, size); + fail = 1; + close(fd); + continue; } if (maxdisc< 0 || (maxdisc>=0 && freesize > maxsize)) { - maxdisc = i; - maxsize = freesize; + maxdisc = i; + maxsize = freesize; } if (mindisc < 0 || (mindisc >=0 && freesize < minsize)) { - mindisc = i; - minsize = freesize; + mindisc = i; + minsize = freesize; } warn |= check_ext2(fd, dname); warn |= check_reiser(fd, dname); @@ -154,41 +179,50 @@ int Create(char *mddev, int mdfd, close(fd); } if (fail) { - fprintf(stderr, Name ": create aborted\n"); - return 1; + fprintf(stderr, Name ": create aborted\n"); + return 1; } if (size == 0) { - if (mindisc == -1) { - fprintf(stderr, Name ": no size and no drives given - aborting create.\n"); - return 1; - } - size = minsize; - if (verbose) - fprintf(stderr, Name ": size set to %dK\n", size); + if (mindisc == -1) { + fprintf(stderr, Name ": no size and no drives given - aborting create.\n"); + return 1; + } + size = minsize; + if (verbose && level>0) + fprintf(stderr, Name ": size set to %dK\n", size); } if ((maxsize-size)*100 > maxsize) { - fprintf(stderr, Name ": largest drive (%s) exceed size (%dK) by more than 1%\n", - subdev[maxdisc], size); - warn = 1; + fprintf(stderr, Name ": largest drive (%s) exceed size (%dK) by more than 1%\n", + subdev[maxdisc], size); + warn = 1; } if (warn) { - if (runstop!= 1) { - if (!ask("Continue creating array? ")) { - fprintf(stderr, Name ": create aborted.\n"); - return 1; + if (runstop!= 1) { + if (!ask("Continue creating array? ")) { + fprintf(stderr, Name ": create aborted.\n"); + return 1; + } + } else { + if (verbose) + fprintf(stderr, Name ": creation continuing despite oddities due to --run\n"); } - } else { - if (verbose) - fprintf(stderr, Name ": creation continuing despite oddities due to --run\n"); - } } + /* If this is raid5, we want to configure the last active slot + * as missing, so that a reconstruct happens (faster than re-parity) + */ + if (force == 0 && level == 5 && first_missing >= raiddisks) { + insert_point = raiddisks-1; + sparedisks++; + array.active_disks--; + missing_disks++; + } + /* Ok, lets try some ioctls */ array.level = level; array.size = size; - array.nr_disks = raiddisks+sparedisks+(level==4||level==5); array.raid_disks = raiddisks; /* The kernel should *know* what md_minor we are dealing * with, but it chooses to trust me instead. Sigh @@ -197,10 +231,10 @@ int Create(char *mddev, int mdfd, if (fstat(mdfd, &stb)==0) array.md_minor = MINOR(stb.st_rdev); array.not_persistent = 0; - if (level == 4 || level == 5) - array.state = 1; /* clean, but one drive will be missing */ + if (level == 5 && (insert_point < raiddisks || first_missing < raiddisks)) + array.state = 1; /* clean, but one drive will be missing */ else - array.state = 0; /* not clean, but no errors */ + array.state = 0; /* not clean, but no errors */ /* There is lots of redundancy in these disk counts, * raid_disks is the most meaningful value @@ -221,52 +255,57 @@ int Create(char *mddev, int mdfd, * So for now, we assume that all raid and spare * devices will be given. */ - array.active_disks=raiddisks-(level==4||level==5); - array.working_disks=raiddisks+sparedisks; - array.spare_disks=sparedisks + (level==4||level==5); - array.failed_disks=0; + array.spare_disks=sparedisks; + array.failed_disks=missing_disks; + array.nr_disks = array.working_disks + array.failed_disks; array.layout = layout; array.chunk_size = chunk*1024; if (ioctl(mdfd, SET_ARRAY_INFO, &array)) { - fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n", - mddev, strerror(errno)); - return 1; + fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n", + mddev, strerror(errno)); + return 1; } for (i=0; i= raiddisks-1) - disk.number++; - disk.raid_disk = disk.number; - if (disk.raid_disk < raiddisks) - disk.state = 6; /* active and in sync */ - else - disk.state = 0; - disk.major = MAJOR(stb.st_rdev); - disk.minor = MINOR(stb.st_rdev); - close(fd); - if (ioctl(mdfd, ADD_NEW_DISK, &disk)) { - fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\n", - subdev[i], strerror(errno)); - return 1; - } + int fd; + struct stat stb; + mdu_disk_info_t disk; + + disk.number = i; + if (i >= insert_point) + disk.number++; + disk.raid_disk = disk.number; + if (disk.raid_disk < raiddisks) + disk.state = 6; /* active and in sync */ + else + disk.state = 0; + if (strcasecmp(subdev[i], "missing")==0) { + disk.major = 0; + disk.minor = 0; + disk.state = 1; /* faulty */ + } else { + fd = open(subdev[i], O_RDONLY, 0); + if (fd < 0) { + fprintf(stderr, Name ": failed to open %s after earlier success - aborting\n", + subdev[i]); + return 1; + } + fstat(fd, &stb); + disk.major = MAJOR(stb.st_rdev); + disk.minor = MINOR(stb.st_rdev); + close(fd); + } + if (ioctl(mdfd, ADD_NEW_DISK, &disk)) { + fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\n", + subdev[i], strerror(errno)); + return 1; + } } - /* hack */ - if (level==4 || level==5) { + if (insert_point < MD_SB_DISKS) { mdu_disk_info_t disk; - disk.number = raiddisks-1; + disk.number = insert_point; disk.raid_disk = disk.number; disk.state = 1; /* faulty */ disk.major = disk.minor = 0; diff --git a/Detail.c b/Detail.c index cb6dd66d..026c3b3d 100644 --- a/Detail.c +++ b/Detail.c @@ -119,7 +119,7 @@ int Detail(char *dev) printf("\n"); printf(" Number Major Minor RaidDisk State\n"); - for (d= 0; d + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +#include "mdctl.h" +#include "md_p.h" +#include "md_u.h" +#include + +static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd); + +int Monitor(int num_devs, char *devlist[], + char *mailaddr, char *alert_cmd, + int period, + char *config) +{ + /* + * Every few seconds, scan every md device looking for changes + * When a change is found, log it, possibly run the alert command, + * and possibly send Email + * + * For each array, we record: + * Update time + * active/working/failed/spare drives + * State of each device. + * + * If the update time changes, check out all the data again + * It is possible that we cannot get the state of each device + * due to bugs in the md kernel module. + * + * if active_drives decreases, generate a "Fail" event + * if active_drives increases, generate a "SpareActive" event + * + * if we detect an array with active0, + * and if we can get_disk_info and find a name + * Then we hot-remove and hot-add to the other array + * + */ + + struct state { + char *devname; + long utime; + int err; + int active, working, failed, spare; + int devstate[MD_SB_DISKS]; + struct state *next; + } *statelist = NULL; + int finished = 0; + while (! finished) { + mddev_ident_t mdlist = NULL; + int dnum=0; + if (num_devs == 0) + mdlist = conf_get_ident(config, NULL); + while (dnum < num_devs || mdlist) { + mddev_ident_t mdident; + struct state *st; + mdu_array_info_t array; + char *dev; + int fd; + char *event = NULL; + int i; + char *event_disc = NULL; + if (num_devs) { + dev = devlist[dnum++]; + mdident = conf_get_ident(config, dev); + } else { + mdident = mdlist; + dev = mdident->devname; + mdlist = mdlist->next; + } + for (st=statelist; st ; st=st->next) + if (strcmp(st->devname, dev)==0) + break; + if (!st) { + st =malloc(sizeof *st); + if (st == NULL) + continue; + st->devname = strdup(dev); + st->utime = 0; + st->next = statelist; + st->err = 0; + statelist = st; + } + fd = open(dev, O_RDONLY); + if (fd < 0) { + if (!st->err) + fprintf(stderr, Name ": cannot open %s: %s\n", + dev, strerror(errno)); + st->err=1; + continue; + } + if (ioctl(fd, GET_ARRAY_INFO, &array)<0) { + if (!st->err) + fprintf(stderr, Name ": cannot get array info for %s: %s\n", + dev, strerror(errno)); + st->err=1; + close(fd); + continue; + } + st->err = 0; + + if (st->utime == array.utime && + st->failed == array.failed_disks) { + close(fd); + continue; + } + event = NULL; + if (st->utime) { + int i; + if (st->active > array.active_disks) + event = "Fail"; + else if (st->working > array.working_disks) + event = "FailSpare"; + else if (st->active < array.active_disks) + event = "ActiveSpare"; + } + for (i=0; i= 0) { + if (event && event_disc == NULL && + st->devstate[i] != disc.state) { + char * dv = map_dev(disc.major, disc.minor); + if (dv) + event_disc = strdup(dv); + } + st->devstate[i] = disc.state; + } + } + close(fd); + st->active = array.active_disks; + st->working = array.working_disks; + st->spare = array.spare_disks; + st->failed = array.failed_disks; + st->utime = array.utime; + if (event) + alert(event, dev, event_disc, mailaddr, alert_cmd); + } + sleep(period); + } + return 0; +} + + +static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd) +{ + if (cmd) { + int pid = fork(); + switch(pid) { + default: + waitpid(pid, NULL, 0); + break; + case -1: + break; + case 0: + execl(cmd, cmd, event, dev, disc, NULL); + exit(2); + } + } + if (mailaddr && strncmp(event, "Fail", 4)==0) { + FILE *mp = popen(Sendmail, "w"); + if (mp) { + char hname[256]; + gethostname(hname, sizeof(hname)); + signal(SIGPIPE, SIG_IGN); + fprintf(mp, "From: " Name " monitoring \n"); + fprintf(mp, "To: %s\n", mailaddr); + fprintf(mp, "Subject: %s event on %s:%s\n\n", event, dev, hname); + + fprintf(mp, "This is an automatically generated mail message from " Name "\n"); + fprintf(mp, "running on %s\n\n", hname); + + fprintf(mp, "A %s event had been detected on md device %s.\n\n", event, dev); + + if (disc) + fprintf(mp, "It could be related to sub-device %s.\n\n", disc); + + fprintf(mp, "Faithfully yours, etc.\n"); + fclose(mp); + } + + } + /* FIXME log the event to syslog maybe */ +} diff --git a/ReadMe.c b/ReadMe.c index ccfdaac9..49830cdc 100644 --- a/ReadMe.c +++ b/ReadMe.c @@ -29,7 +29,7 @@ #include "mdctl.h" -char Version[] = Name " - v0.4.2 - 27 July 2001\n"; +char Version[] = Name " - v0.5 - 23 August 2001\n"; /* * File: ReadMe.c * @@ -78,7 +78,7 @@ char Version[] = Name " - v0.4.2 - 27 July 2001\n"; * command, subsequent Manage commands can finish the job. */ -char short_options[]="-ABCDEhVvc:l:p:n:x:u:c:z:sarfRSow"; +char short_options[]="-ABCDEFhVvc:l:p:m:n:x:u:c:d:z:sarfRSow"; struct option long_options[] = { {"manage", 0, 0, '@'}, {"assemble", 0, 0, 'A'}, @@ -86,6 +86,11 @@ struct option long_options[] = { {"create", 0, 0, 'C'}, {"detail", 0, 0, 'D'}, {"examine", 0, 0, 'E'}, + {"follow", 0, 0, 'F'}, + + /* synonyms */ + {"monitor", 0, 0, 'F'}, + /* after those will normally come the name of the md device */ {"help", 0, 0, 'h'}, {"version", 0, 0, 'V'}, @@ -103,6 +108,7 @@ struct option long_options[] = { /* For assemble */ {"uuid", 1, 0, 'u'}, + {"super-minor",1,0, 'm'}, {"config", 1, 0, 'c'}, {"scan", 0, 0, 's'}, {"force", 0, 0, 'f'}, @@ -115,6 +121,13 @@ struct option long_options[] = { {"stop", 0, 0, 'S'}, {"readonly", 0, 0, 'o'}, {"readwrite", 0, 0, 'w'}, + + /* For Follow/monitor */ + {"mail", 1, 0, 'm'}, + {"program", 1, 0, 'p'}, + {"alert", 1, 0, 'p'}, + {"delay", 1, 0, 'd'}, + {0, 0, 0, 0} }; @@ -130,6 +143,7 @@ char Help[] = " mdctl --build device options...\n" " mdctl --detail device\n" " mdctl --examine device\n" +" mdctl --follow options...\n" " mdctl device options...\n" " mdctl is used for controlling Linux md devices (aka RAID arrays)\n" " For detail help on major modes use, e.g.\n" @@ -145,6 +159,9 @@ char Help[] = " --build -B : Build a legacy array without superblock\n" " --detail -D : Print detail of a given md array\n" " --examine -E : Print content of md superblock on device\n" +" --follow -F : Follow (monitor) any changes to devices and respond to them\n" +" --monitor : same as --follow\n" +"\n" " --help -h : This help message or, after above option,\n" " mode specific help message\n" " --version -V : Print version information for mdctl\n" @@ -159,14 +176,24 @@ char Help[] = " --raid-disks= -n : number of active devices in array\n" " --spare-disks= -x : number of spares (eXtras) to allow space for\n" " --size= -z : Size (in K) of each drive in RAID1/4/5 - optional\n" +" --force -f : Honour devices as listed on command line. Don't\n" +" : insert a missing drive for RAID5.\n" "\n" " For assemble:\n" " --uuid= -u : uuid of array to assemble. Devices which don't\n" " have this uuid are excluded\n" +" --super-minor= -m : minor number to look for in super-block when\n" +" choosing devices to use.\n" " --config= -c : config file\n" " --scan -s : scan config file for missing information\n" " --force -f : Assemble the array even if some superblocks appear out-of-date\n" "\n" +" For follow/monitor:\n" +" --mail= -m : Address to mail alerts of failure to\n" +" --program= -p : Program to run when an event is detected\n" +" --alert= : same as --program\n" +" --delay= -d : seconds of delay between polling state. default=60\n" +"\n" " General management:\n" " --add -a : add, or hotadd subsequent devices\n" " --remove -r : remove subsequent devices\n" @@ -185,10 +212,10 @@ char Help_create[] = " This usage will initialise a new md array and possibly associate some\n" " devices with it. If enough devices are given to complete the array,\n" " the array will be activated. Otherwise it will be left inactive\n" -" to be competed and activated by subsequent management commands.\n" +" to be completed and activated by subsequent management commands.\n" "\n" " As devices are added, they are checked to see if they contain\n" -" raid superblock or filesystems. They are also check to see if\n" +" raid superblocks or filesystems. They are also check to see if\n" " the variance in device size exceeds 1%.\n" " If any discrepancy is found, the array will not automatically\n" " be run, though the presence of a '--run' can override this\n" @@ -225,30 +252,53 @@ char Help_assemble[] = "\n" "This usage assembles one or more raid arrays from pre-existing\n" "components.\n" -"For each array, mdctl needs to know the md device, the uuid, and\n" -"a number of sub devices. These can be found in a number of ways.\n" +"For each array, mdctl needs to know the md device, the identify of\n" +"the array, and a number of sub devices. These can be found in a number\n" +"of ways.\n" +"\n" +"The md device is either given on the command line or is found listed\n" +"in the config file. The array identity is determined either from the\n" +"--uuid or --super-minor commandline arguments, or from the config file,\n" +"or from the first component device on the command line.\n" "\n" -"The md device is either given before --scan or is found from the\n" -"config file. In the latter case, multiple md devices can be started\n" -"with a single mdctl command.\n" +"The different combinations of these are as follows:\n" +" If the --scan option is not given, then only devices and identities\n" +" listed on the command line are considered.\n" +" The first device will be the array devices, and the remainder will\n" +" examined when looking for components.\n" +" If an explicit identity is given with --uuid or --super-minor, then\n" +" Each device with a superblock which matches that identity is considered,\n" +" otherwise every device listed is considered.\n" "\n" -"The uuid can be given with the --uuid option, or can be found in\n" -"in the config file, or will be taken from the super block on the first\n" -"subdevice listed on the command line or in a subsequent --add command.\n" +" If the --scan option is given, and no devices are listed, then\n" +" every array listed in the config file is considered for assembly.\n" +" The identity can candidate devices are determined from the config file.\n" "\n" -"Devices can be given on the --assemble command line, on subsequent\n" -"'mdctl --add' command lines, or from the config file. Only devices\n" -"which have an md superblock which contains the right uuid will be\n" -"considered for any device.\n" +" If the --scan option is given as well as one or more devices, then\n" +" Those devices are md devices that are to be assembled. Their identity\n" +" and components are determined from the config file.\n" "\n" -"The config file is only used if explicitly named with --config or\n" -"requested with --scan. In the later case, '/etc/md.conf' is used.\n" +"The config file contains, apart from blank lines and comment lines that\n" +"start with a has, two sorts of configuration lines, array lines and\n" +"device lines.\n" +"Each configuration line is constructed of a number of space separated\n" +"words, and can be continued on subsequent physical lines by indenting\n" +"those lines.\n" "\n" -"If --scan is not given, then the config file will only be used\n" -"to find uuids for md arrays.\n" +"A device line starts with the word 'device' and then has a number of words\n" +"which identify devices. These words should be names of devices in the filesystem,\n" +"and can contain wildcards. There can be multiple words or each device line,\n" +"and multiple device lines. All devices so listed are checked for relevant\n" +"super blocks when assembling arrays.\n" "\n" -"The format of the config file is:\n" -" not yet documented\n" +"An array line start with the word 'array'. This is followed by the name of\n" +"the array device in the filesystem, e.g. '/dev/md2'. Subsequent words\n" +"describe the identity of the array, used to recognise devices to include in the\n" +"array. The identity can be given as a UUID with a word starting 'uuid=', or\n" +"as a minor-number stored in the superblock using 'super-minor=', or as a list\n" +"of devices. This is given as a comma separated list of names, possibly containing\n" +"wildcards, preceeded by 'devices='. If multiple critea are given, than a device\n" +"must match all of them to be considered.\n" "\n" ; diff --git a/TAGS b/TAGS index bceb7ad8..6c159c9b 100644 --- a/TAGS +++ b/TAGS @@ -1,4 +1,12 @@ +dlink.h,193 +struct __dl_head__dl_head5,100 +#define dl_alloc(dl_alloc11,187 +#define dl_new(dl_new12,297 +#define dl_newv(dl_newv13,341 +#define dl_next(dl_next15,391 +#define dl_prev(dl_prev16,461 + md_p.h,1316 #define _MD_P_H16,582 #define MD_RESERVED_BYTES 44,1414 @@ -69,72 +77,123 @@ typedef struct mdu_start_info_s mdu_start_info_s97,2713 typedef struct mdu_param_smdu_param_s108,2878 } mdu_param_t;mdu_param_t113,3014 -mdctl.h,823 +mdctl.h,826 #define __USE_LARGEFILE6430,1115 #define MD_MAJOR 47,1491 -extern char short_options[52,1531 -extern struct option long_options[53,1560 -extern char Version[54,1597 -extern char Version[], Usage[54,1597 -extern char Version[], Usage[], Help[54,1597 -extern char Version[], Usage[], Help[], Help_create[54,1597 -extern char Version[], Usage[], Help[], Help_create[], Help_build[54,1597 -extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[54,1597 -typedef struct mddev_uuid_s mddev_uuid_s58,1762 -} *mddev_uuid_t;mddev_uuid_t62,1852 -typedef struct mddev_dev_s mddev_dev_s65,1918 -} *mddev_dev_t;mddev_dev_t68,1990 -#define ALGORITHM_LEFT_ASYMMETRIC 73,2044 -#define ALGORITHM_RIGHT_ASYMMETRIC 74,2080 -#define ALGORITHM_LEFT_SYMMETRIC 75,2117 -#define ALGORITHM_RIGHT_SYMMETRIC 76,2152 +#define Name 52,1531 +extern char short_options[54,1553 +extern struct option long_options[55,1582 +extern char Version[56,1619 +extern char Version[], Usage[56,1619 +extern char Version[], Usage[], Help[56,1619 +extern char Version[], Usage[], Help[], Help_create[56,1619 +extern char Version[], Usage[], Help[], Help_create[], Help_build[56,1619 +extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[56,1619 +typedef struct mddev_ident_s mddev_ident_s68,2055 +} *mddev_ident_t;mddev_ident_t80,2292 +typedef struct mddev_dev_s mddev_dev_s83,2359 +} *mddev_dev_t;mddev_dev_t86,2431 +typedef struct mapping mapping88,2448 +} mapping_t;mapping_t91,2496 +extern mapping_t r5layout[95,2606 +extern mapping_t r5layout[], pers[95,2606 Assemble.c,22 int Assemble(34,1171 -Build.c,19 -int Build(32,1135 +Build.c,100 +#define REGISTER_DEV 32,1135 +#define START_MD 33,1176 +#define STOP_MD 34,1217 +int Build(36,1259 Create.c,20 -int Create(32,1135 +int Create(34,1171 Detail.c,20 int Detail(34,1171 Examine.c,21 -int Examine(34,1171 - -Manage.c,79 -int Manage_ro(32,1135 -int Manage_runstop(36,1191 -int Manage_subdevs(40,1251 - -ReadMe.c,231 -#define Name 32,1135 -char Version[33,1156 -char short_options[82,3241 -struct option long_options[83,3297 -char Usage[122,4441 -char Help[127,4498 -char Help_create[181,6989 -char Help_build[203,7973 -char Help_assemble[216,8513 - -config.c,102 -char DefaultConfFile[43,1371 -mddev_uuid_t conf_get_uuids(45,1416 -mddev_dev_t conf_get_devs(50,1482 - -mdctl.c,40 -int main(33,1153 -#define O(O131,3313 - -util.c,212 +int Examine(37,1261 + +Manage.c,161 +#define REGISTER_DEV 34,1171 +#define START_MD 35,1212 +#define STOP_MD 36,1253 +int Manage_ro(38,1295 +int Manage_runstop(75,2149 +int Manage_subdevs(118,3161 + +ReadMe.c,265 +char Version[32,1135 +char short_options[81,3222 +struct option long_options[82,3280 +char Usage[123,4484 +char Help[128,4541 +char Help_create[185,7233 +char Help_build[212,8453 +char Help_assemble[225,8993 +mapping_t r5layout[284,11939 +mapping_t pers[298,12155 + +config.c,479 +char DefaultConfFile[68,2396 +char *keywords[70,2441 +int match_keyword(77,2640 +char *conf_word(97,3148 +char *conf_line(163,4739 +void free_line(184,5041 +struct conf_dev conf_dev195,5183 +} *cdevlist 198,5244 +int devline(202,5267 +mddev_ident_t mddevlist 220,5590 +mddev_ident_t *mddevlp 221,5622 +void arrayline(223,5660 +int loaded 289,7453 +void load_conffile(291,7470 +mddev_ident_t conf_get_ident(324,7961 +mddev_dev_t conf_get_devs(334,8163 +int match_oneof(369,8791 + +dlink.c,177 +void *dl_head(11,180 +void dl_free(20,289 +void dl_init(26,363 +void dl_insert(32,430 +void dl_add(40,598 +void dl_del(48,763 +char *dl_strndup(57,969 +char *dl_strdup(73,1176 + +mdctl.c,64 +int open_mddev(33,1153 +int main(50,1472 +#define O(O149,3610 + +raid5extend.c,39 +int phys2log(2,1 +raid5_extend(46,902 + +util.c,573 int parse_uuid(40,1354 -int md_get_version(80,2091 -int get_linux_version(99,2448 -int enough(111,2639 -int same_uuid(127,2889 -void uuid_from_super(137,3018 -int compare_super(151,3295 -int load_super(185,4258 +int md_get_version(82,2117 +int get_linux_version(101,2476 +int enough(113,2673 +int same_uuid(129,2923 +void uuid_from_super(139,3052 +int compare_super(153,3329 +int load_super(187,4292 +int store_super(227,4950 +int check_ext2(253,5321 +int check_reiser(284,6084 +int check_raid(308,6640 +int ask(324,7003 +char *map_num(344,7368 +int map_name(354,7503 +struct devmap devmap369,7832 +} *devlist 373,7911 +int devlist_ready 374,7930 +#define __USE_XOPEN_EXTENDED376,7954 +int add_dev(380,8003 +char *map_dev(396,8370 +int calc_sb_csum(412,8645 diff --git a/TODO b/TODO index 017bcac8..9bcf70f3 100644 --- a/TODO +++ b/TODO @@ -3,13 +3,14 @@ - report "chunk" or "rounding" depending on raid level DONE - report "linear" instead of "-1" for raid level DONE - decode ayout depending on raid level DONE -- get Assemble to upgrade devices if force flag. - --verbose and --force flags. - set md_minor, *_disks for Create - DONE - for create raid5, how to choose between all working, but not insync one missing, one spare, insync +- and for raid1 - some failed drives... + - when RUN_ARRAY, make sure *_disks counts are right - get --detail to extract extra stuff from superblock, @@ -23,3 +24,54 @@ - mdctl -S /dev/md0 /dev/md1 gives internal error + +- mdctl --detail --scan print summary of what it can find? + + +--------- +Assemble doesn't add spares. - DONE +Create to allow "missing" name for devices. +Create to accept "--force" for do exactly what is requested +- get Assemble to upgrade devices if force flag. +ARRAY lines in config file to have super_minor=n +ARRAY lines in config file to have device=pattern, and only accept + those devices + If UUID given, insist on that + If not, but super_minor given, require all found with that minor + to have same uuid + If only device given, all valid supers on those devices must have + same uuid +allow /dev/mdX as first argument before any options +Possible --dry-run option for create and assemble--force + +Assemble to check that all devices mentioned in superblock + are present. + +New mode: --Monitor (or --Follow) + Periodically check status of all arrays (listed in config file). + Log every event and apparent cause - or differences + Email and alert - or run a program - for important events + Move spares around if necessary. + + An Array line can have a spare-group= field that indicates that + the array shares spares with other arrays with the same + spare-group name. + If an array has a failed and no spares, then check all other + arrays in the spare group. If one has no failures and a spare, + then consider that spare. + Choose the smallest considered spare that is large enough. + If there is one, then hot-remove it from it's home, and + hot-add it to the array in question. + + --mail-to address + --alert-handler program + + Will also extract information from /proc/mdstat if present, + and consider 20% marks in rebuild as events. + + Events are: + drive fails - causes mail to be sent + rebuild started + spare activated + spare removed + spare added \ No newline at end of file diff --git a/config.c b/config.c index 2feaae39..39ef56ba 100644 --- a/config.c +++ b/config.c @@ -30,6 +30,8 @@ #include "mdctl.h" #include "dlink.h" #include +#include + /* * Read the config file * @@ -74,15 +76,15 @@ char *keywords[] = { "device", "array", NULL }; int match_keyword(char *word) { - int len = strlen(word); - int n; + int len = strlen(word); + int n; - if (len < 3) return -1; - for (n=0; keywords[n]; n++) { - if (strncasecmp(word, keywords[n], len)==0) - return n; - } - return -1; + if (len < 3) return -1; + for (n=0; keywords[n]; n++) { + if (strncasecmp(word, keywords[n], len)==0) + return n; + } + return -1; } /* conf_word gets one word from the conf file. @@ -94,60 +96,60 @@ int match_keyword(char *word) char *conf_word(FILE *file, int allow_key) { - int wsize = 100; - int len = 0; - int c; - int quote; - int wordfound = 0; - char *word = malloc(wsize); - - if (!word) abort(); - - while (wordfound==0) { - /* at the end of a word.. */ - c = getc(file); - if (c == '#') - while (c != EOF && c != '\n') - c = getc(file); - if (c == EOF) break; - if (c == '\n') continue; + int wsize = 100; + int len = 0; + int c; + int quote; + int wordfound = 0; + char *word = malloc(wsize); - if (c != ' ' && c != '\t' && ! allow_key) { - ungetc(c, file); - break; - } - /* looks like it is safe to get a word here, if there is one */ - quote = 0; - /* first, skip any spaces */ - while (c == ' ' || c == '\t') - c = getc(file); - if (c != EOF && c != '\n' && c != '#') { - /* we really have a character of a word, so start saving it */ - while (c != EOF && c != '\n' && (quote || (c!=' ' && c != '\t'))) { - wordfound = 1; - if (quote && c == quote) quote = 0; - else if (quote == 0 && (c == '\'' || c == '"')) - quote = c; - else { - if (len == wsize-1) { - wsize += 100; - word = realloc(word, wsize); - if (!word) abort(); - } - word[len++] = c; - } + if (!word) abort(); + + while (wordfound==0) { + /* at the end of a word.. */ c = getc(file); - } + if (c == '#') + while (c != EOF && c != '\n') + c = getc(file); + if (c == EOF) break; + if (c == '\n') continue; + + if (c != ' ' && c != '\t' && ! allow_key) { + ungetc(c, file); + break; + } + /* looks like it is safe to get a word here, if there is one */ + quote = 0; + /* first, skip any spaces */ + while (c == ' ' || c == '\t') + c = getc(file); + if (c != EOF && c != '\n' && c != '#') { + /* we really have a character of a word, so start saving it */ + while (c != EOF && c != '\n' && (quote || (c!=' ' && c != '\t'))) { + wordfound = 1; + if (quote && c == quote) quote = 0; + else if (quote == 0 && (c == '\'' || c == '"')) + quote = c; + else { + if (len == wsize-1) { + wsize += 100; + word = realloc(word, wsize); + if (!word) abort(); + } + word[len++] = c; + } + c = getc(file); + } + } + if (c != EOF) ungetc(c, file); } - if (c != EOF) ungetc(c, file); - } - word[len] = 0; + word[len] = 0; /* printf("word is <%s>\n", word); */ - if (!wordfound) { - free(word); - word = NULL; - } - return word; + if (!wordfound) { + free(word); + word = NULL; + } + return word; } /* @@ -160,33 +162,33 @@ char *conf_word(FILE *file, int allow_key) char *conf_line(FILE *file) { - char *w; - char *list; + char *w; + char *list; - w = conf_word(file, 1); - if (w == NULL) return NULL; + w = conf_word(file, 1); + if (w == NULL) return NULL; - list = dl_strdup(w); - free(w); - dl_init(list); - - while ((w = conf_word(file,0))){ - char *w2 = dl_strdup(w); + list = dl_strdup(w); free(w); - dl_add(list, w2); - } + dl_init(list); + + while ((w = conf_word(file,0))){ + char *w2 = dl_strdup(w); + free(w); + dl_add(list, w2); + } /* printf("got a line\n");*/ - return list; + return list; } void free_line(char *line) { - char *w; - for (w=dl_next(line); w != line; w=dl_next(line)) { - dl_del(w); - dl_free(w); - } - dl_free(line); + char *w; + for (w=dl_next(line); w != line; w=dl_next(line)) { + dl_del(w); + dl_free(w); + } + dl_free(line); } @@ -199,141 +201,198 @@ struct conf_dev { int devline(char *line) { - char *w; - struct conf_dev *cd; - - for (w=dl_next(line); w != line; w=dl_next(w)) { - if (w[0] == '/') { - cd = malloc(sizeof(*cd)); - cd->name = strdup(w); - cd->next = cdevlist; - cdevlist = cd; - } else { - fprintf(stderr, Name ": unreconised word on DEVICE line: %s\n", - w); + char *w; + struct conf_dev *cd; + + for (w=dl_next(line); w != line; w=dl_next(w)) { + if (w[0] == '/') { + cd = malloc(sizeof(*cd)); + cd->name = strdup(w); + cd->next = cdevlist; + cdevlist = cd; + } else { + fprintf(stderr, Name ": unreconised word on DEVICE line: %s\n", + w); + } } - } } -mddev_uuid_t uuidlist = NULL; -mddev_uuid_t *uidlp = &uuidlist; +mddev_ident_t mddevlist = NULL; +mddev_ident_t *mddevlp = &mddevlist; void arrayline(char *line) { - char *w; - char *dev = NULL; - __u32 uuid[4]; - int uidset=0; - mddev_uuid_t mu; - - for (w=dl_next(line); w!=line; w=dl_next(w)) { - if (w[0] == '/') { - if (dev) - fprintf(stderr, Name ": only give one device per ARRAY line: %s and %s\n", - dev, w); - else dev = w; - } else if (strncasecmp(w, "uuid=", 5)==0 ) { - if (uidset) - fprintf(stderr, Name ": only specify uuid once, %s ignored.\n", - w); - else { - if (parse_uuid(w+5, uuid)) - uidset = 1; - else - fprintf(stderr, Name ": bad uuid: %s\n", w); - } - } else { - fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n", - w); + char *w; + + struct mddev_ident_s mis; + mddev_ident_t mi; + + mis.uuid_set = 0; + mis.super_minor = -1; + mis.devices = NULL; + mis.devname = NULL; + + for (w=dl_next(line); w!=line; w=dl_next(w)) { + if (w[0] == '/') { + if (mis.devname) + fprintf(stderr, Name ": only give one device per ARRAY line: %s and %s\n", + mis.devname, w); + else mis.devname = w; + } else if (strncasecmp(w, "uuid=", 5)==0 ) { + if (mis.uuid_set) + fprintf(stderr, Name ": only specify uuid once, %s ignored.\n", + w); + else { + if (parse_uuid(w+5, mis.uuid)) + mis.uuid_set = 1; + else + fprintf(stderr, Name ": bad uuid: %s\n", w); + } + } else if (strncasecmp(w, "super-minor=", 12)==0 ) { + if (mis.super_minor >= 0) + fprintf(stderr, Name ": only specify super-minor once, %s ignored.\n", + w); + else { + char *endptr; + mis.super_minor= strtol(w+12, &endptr, 10); + if (w[12]==0 || endptr[0]!=0 || mis.super_minor < 0) { + fprintf(stderr, Name ": invalid super-minor number: %s\n", + w); + mis.super_minor = -1; + } + } + } else if (strncasecmp(w, "devices=", 8 ) == 0 ) { + if (mis.devices) + fprintf(stderr, Name ": only specify devices once (use a comma separated list). %s ignored\n", + w); + else + mis.devices = strdup(w+8); + } else if (strncasecmp(w, "spare-group=", 12) == 0 ) { + if (mis.spare_group) + fprintf(stderr, Name ": only specify one spare group per array. %s ignored.\n", + w); + else + mis.spare_group = strdup(w+12); + } else { + fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n", + w); + } + } + if (mis.devname == NULL) + fprintf(stderr, Name ": ARRAY line with a device\n"); + else if (mis.uuid_set == 0 && mis.devices == NULL && mis.super_minor < 0) + fprintf(stderr, Name ": ARRAY line %s has no identity information.\n", mis.devname); + else { + mi = malloc(sizeof(*mi)); + *mi = mis; + mi->devname = strdup(mis.devname); + mi->next = NULL; + *mddevlp = mi; + mddevlp = &mi->next; } - } - if (dev == NULL) - fprintf(stderr, Name ": ARRAY line with a device\n"); - else if (uidset == 0) - fprintf(stderr, Name ": ARRAY line %s has no uuid\n", dev); - else { - mu = malloc(sizeof(*mu)); - mu->devname = strdup(dev); - memcpy(mu->uuid, uuid, sizeof(uuid)); - mu->next = NULL; - *uidlp = mu; - uidlp = &mu->next; - } } int loaded = 0; void load_conffile(char *conffile) { - FILE *f; - char *line; - - if (loaded) return; - if (conffile == NULL) - conffile = DefaultConfFile; - - f = fopen(conffile, "r"); - if (f ==NULL) - return; - - loaded = 1; - while ((line=conf_line(f))) { - switch(match_keyword(line)) { - case 0: /* DEVICE */ - devline(line); - break; - case 1: - arrayline(line); - break; - default: - fprintf(stderr, Name ": Unknown keyword %s\n", line); + FILE *f; + char *line; + + if (loaded) return; + if (conffile == NULL) + conffile = DefaultConfFile; + + f = fopen(conffile, "r"); + if (f ==NULL) + return; + + loaded = 1; + while ((line=conf_line(f))) { + switch(match_keyword(line)) { + case 0: /* DEVICE */ + devline(line); + break; + case 1: + arrayline(line); + break; + default: + fprintf(stderr, Name ": Unknown keyword %s\n", line); + } + free_line(line); } - free_line(line); - } /* printf("got file\n"); */ } -mddev_uuid_t conf_get_uuids(char *conffile) +mddev_ident_t conf_get_ident(char *conffile, char *dev) { - load_conffile(conffile); - return uuidlist; + mddev_ident_t rv; + load_conffile(conffile); + rv = mddevlist; + while (dev && rv && strcmp(dev, rv->devname)!=0) + rv = rv->next; + return rv; } mddev_dev_t conf_get_devs(char *conffile) { - glob_t globbuf; - struct conf_dev *cd; - int flags = 0; - static mddev_dev_t dlist = NULL; - int i; - - while (dlist) { - mddev_dev_t t = dlist; - dlist = dlist->next; - free(t->devname); - free(t); - } + glob_t globbuf; + struct conf_dev *cd; + int flags = 0; + static mddev_dev_t dlist = NULL; + int i; + + while (dlist) { + mddev_dev_t t = dlist; + dlist = dlist->next; + free(t->devname); + free(t); + } - load_conffile(conffile); + load_conffile(conffile); - for (cd=cdevlist; cd; cd=cd->next) { - glob(cd->name, flags, NULL, &globbuf); - flags |= GLOB_APPEND; - } + for (cd=cdevlist; cd; cd=cd->next) { + glob(cd->name, flags, NULL, &globbuf); + flags |= GLOB_APPEND; + } - for (i=0; idevname = strdup(globbuf.gl_pathv[i]); - t->next = dlist; - dlist = t; + for (i=0; idevname = strdup(globbuf.gl_pathv[i]); + t->next = dlist; + dlist = t; /* printf("one dev is %s\n", t->devname);*/ - } - globfree(&globbuf); + } + globfree(&globbuf); - return dlist; + return dlist; } +int match_oneof(char *devices, char *devname) +{ + /* check if one of the comma separated patterns in devices + * matches devname + */ + + + while (devices && *devices) { + char patn[1024]; + char *p = devices; + devices = strchr(devices, ','); + if (!devices) + devices = p + strlen(p); + if (devices-p < 1024) { + strncpy(patn, p, devices-p); + patn[devices-p] = 0; + if (fnmatch(patn, devname, FNM_PATHNAME)==0) + return 1; + } + if (*devices == ',') + devices++; + } + return 0; +} diff --git a/makedist b/makedist index 789a80a7..773e7f09 100755 --- a/makedist +++ b/makedist @@ -7,14 +7,15 @@ else echo $target is not a directory exit 2 fi set `grep '^char Version' ReadMe.c ` -echo version = $7 -base=mdctl-$7.tgz +version=`echo $7 | sed 's/v//'` +echo version = $version +base=mdctl-$version.tgz if [ -f $target/$base ] then echo $target/$base exists. exit 1 fi trap "rm $target/$base; exit" 1 2 3 -( cd .. ; tar czvf - mdctl ) > $target/$base +( cd .. ; ln -s mdctl mdctl-$version ; tar czhvf - --exclude='*,v' --exclude='*.o' --exclude=RCS mdctl-$version ; rm mdctl-$version ) > $target/$base chmod a+r $target/$base ls -l $target/$base diff --git a/md_p.h b/md_p.h index d6bb37a8..99479b43 100644 --- a/md_p.h +++ b/md_p.h @@ -128,7 +128,7 @@ typedef struct mdp_superblock_s { __u32 failed_disks; /* 4 Number of failed disks */ __u32 spare_disks; /* 5 Number of spare disks */ __u32 sb_csum; /* 6 checksum of the whole superblock */ -#ifdef __BIG_ENDIAN +#if __BYTE_ORDER == __BIG_ENDIAN __u32 events_hi; /* 7 high-order of superblock update count */ __u32 events_lo; /* 8 low-order of superblock update count */ #else diff --git a/mdctl.8 b/mdctl.8 new file mode 100644 index 00000000..fe9c6564 --- /dev/null +++ b/mdctl.8 @@ -0,0 +1,287 @@ +.\" -*- nroff -*- +.TH mdctl 8 +.SH NAME +mdctl \- a single program that can be used to control Linux md devices +.SH SYNOPSIS + +.BI mdctl +[mode] [options] + +.SH DESCRIPTION +RAID devices are virtual devices created from two or more +real block devices. This allows multiple disks to be combined into a single +filesystem, possibly with integrated redundancy to survive drive failure.. Linux RAID devices +are implemented through the md device driver. + +If you're using the +.B /proc +filesystem, +.B /proc/mdstat +gives you informations about md devices status. + +Currently, Linux supports linear md devices, RAID0 (striping), RAID1 +(mirrroring), RAID4 and RAID5. For information on the various levels of +RAID, check out: + + http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/ + +for new releases of the RAID driver check out: + + ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches + +.B mdctl +is a single program that can be used to control Linux md devices. It +is intended to provide all the functionality (and more) of the mdtools +and raidtools but with a very different interface. + +mdctl can perform all functions without a configuration file. There is the +option of using a configuration file, but not in the same way that raidtools +uses one. raidtools uses a configuration file to describe how to create a +RAID array, and also uses this file partially to start a previously created +RAID array. Further, raidtools requires the configuration file for such +things as stopping a raid array which needs to know nothing about the array. + +The configuration file that can be used by mdctl lists two different things: + +.IP "\fB\-\fP" +a list of md devices and information about how to identify each. The +identity can consist of a UUID, and minor-number as recorded on the +superblock, or a list of devices. + +.IP "\fB\-\fP" +a list of devices that should be scanned for md sub-devices. + +.SH MODES +mdctl has 4 major modes of operation: +.IP "\fBCreate\fP" +This mode is used to create a new array with a superblock. It can progress +in several step create-add-add-run or it can all happen with one command. + +.IP "\fBAssemble\fP" +This mode is used to assemble the parts of a previously created +array into an active array. Components can be explicitly given +or can be searched for. +.B mdctl +(optionally) checks that the components +do form a bonafide array, and can, on request, fiddle superblock +version numbers so as to assemble a faulty array. + +.IP "\fBBuild\fP" +This is for building legacy arrays without superblocks. + +.IP "\fBManage\fP" +This is for odd bits an pieces like hotadd, hotremove, setfaulty, stop, +readonly,readwrite If an array is only partially setup by the +Create/Assemble/Build command, subsequent Manage commands can finish the +job. + +.SH OPTIONS + +Available options are: + +.IP "\fB\-C\fP, \fB\-\-create\fP" +Create a new array + +.IP "\fB-A\fP, \fB\-\-assemble\fP" +Assemble an existing array + +.IP "\fB\-B\fP, \fB\-\-build\fP" +Build a legacy array without superblock + +.IP "\fB\-D\fP, \fB\-\-detail\fP" +Print detail of a given md array + +.IP "\fB\-E\fP, \fB\-\-examine\fP" +Print content of md superblock on device + +.IP "\fB\-h\fP, \fB\-\-help\fP" +This help message or, after above option, mode specific help message + +.IP "\fB\-V\fP, \fB\-\-version\fP" +Print version information for mdctl + +.IP "\fB\-v\fP, \fB\-\-verbose\fP" +Be more verbose about what is happening + +.SH For create or build: + +.IP "\fB\-c\fP, \fB\-\-chunk=\fP" +chunk size of kibibytes + +.IP "\fB\-\-rounding=\fP" +rounding factor for linear array (==chunk size) + +.IP "\fB\-l\fP, \fB\-\-level=\fP" +raid level: 0,1,4,5,linear. 0 or linear for build + +.IP "\fB\-p\fP, \fB\-\-parity=\fP" +raid5 parity algorithm: {left,right}-{,a}symmetric + +.IP "\fB\-\-layout=\fP" +same as --parity + +.IP "\fB\-n\fP, \fB\-\-raid-disks=\fP" +number of active devices in array + +.IP "\fB\-x\fP, \fB\-\-spare-disks=\fP" +number of spares (eXtras) to allow space for + +.IP "\fB\-z\fP, \fB\-\-size=\fP" +Size (in K) of each drive in RAID1/4/5 - optional + +.SH For assemble: + +.IP "\fB\-u\fP, \fB\-\-uuid=\fP" +uuid of array to assemble. Devices which don't have this uuid are excluded + +.IP "\fB\-c\fP, \fB\-\-config=\fP" +config file + +.IP "\fB\-s\fP, \fB\-\-scan\fP" +scan config file for missing information + +.IP "\fB\-f\fP, \fB\-\-force\fP" +Assemble the array even if some superblocks appear out-of-date + +.SH General management + +.IP "\fB\-a\fP, \fB\-\-add\fP" +add, or hotadd subsequent devices + +.IP "\fB\-r\fP, \fB\-\-remove\fP" +remove subsequent devices + +.IP "\fB\-f\fP, \fB\-\-fail\fP" +mark subsequent devices a faulty + +.IP "\fB\-\-set-faulty\fP" +same as --fail + +.IP "\fB\-R\fP, \fB\-\-run\fP" +start a partially built array + +.IP "\fB\-S\fP, \fB\-\-stop\fP" +deactivate array, releasing all resources + +.IP "\fB\-o\fP, \fB\-\-readonly\fP" +mark array as readonly + +.IP "\fB\-w\fP, \fB\-\-readwrite\fP" +mark array as readwrite + +.SH CREATE MODE + +Usage: + +.B mdctl +--create device --chunk=X --level=Y --raid-disks=Z devices + +This usage will initialise a new md array and possibly associate some +devices with it. If enough devices are given to complete the array, the +array will be activated. Otherwise it will be left inactive to be completed +and activated by subsequent management commands. + +As devices are added, they are checked to see if they contain raid +superblocks or filesystems. They are also check to see if the variance in +device size exceeds 1%. + +If any discrepancy is found, the array will not automatically be run, though +the presence of a +.B --run +can override this caution. + +If the +.B --size +option is given, it is not necessary to list any subdevices in this command. +They can be added later, before a +.B --run. +If no +.B --size +is given, the apparent size of the smallest drive given is used. + +The General management options that are valid with --create are: +.IP "\fB\-\-run\fP" +insist of running the array even if not all devices are present or some look +odd. + +.IP "\fB\-\-readonly\fP" +start the array readonly - not supported yet. + +.SH ASSEMBLY MODE + +Usage: + +.B mdctl +--assemble device options... + +.B mdctl +--assemble --scan options... + +This usage assembles one or more raid arrays from pre-existing components. +For each array, mdctl needs to know the md device, the uuid, and a number of +sub devices. These can be found in a number of ways. + +The md device is either given before +.B --scan +or is found from the config file. In the latter case, multiple md devices +can be started with a single mdctl command. + +The uuid can be given with the +.B --uuid +option, or can be found in in the config file, or will be taken from the +super block on the first subdevice listed on the command line or in a +subsequent +.B --add +command. + +Devices can be given on the +.B --assemble +command line, on subsequent +.B 'mdctl --add' +command lines, or from the config file. Only devices which have an md +superblock which contains the right uuid will be considered for any device. + +The config file is only used if explicitly named with +.B --config +or requested with +.B --scan. +In the later case, +.B /etc/md.conf +is used. + +If +.B --scan +is not given, then the config file will only be used to find uuids for md +arrays. + +The format of the config file is: + not yet documented + +.SH BUILD MDOE + +Usage: + +.B mdctl +--build device -chunk=X --level=Y --raid-disks=Z devices + +This usage is similar to +.B --create. +The difference is that it creates a legacy array without a superblock. With +these arrays there is no different between initially creating the array and +subsequently assembling the array, except that hopefully there is useful +data there in the second case. + +The level may only be 0 or linear. All devices must be listed and the array +will be started once complete. + +.SH BUGS +no known bugs. + +.SH TODO + + +.SH SEE ALSO +.IR raidtab (5), +.IR raid0run (8), +.IR raidstop (8), +.IR mkraid (8) diff --git a/mdctl.c b/mdctl.c index 70e81292..63871104 100644 --- a/mdctl.c +++ b/mdctl.c @@ -30,376 +30,466 @@ #include "mdctl.h" #include "md_p.h" -int main(int argc, char *argv[]) +int open_mddev(char *dev) { - char mode = '\0'; - int opt; - char *help_text; - char *c; - int rv; - int i; - - int chunk = 0; - int size = 0; - int level = -10; - int layout = -1; - int raiddisks = 0; - int sparedisks = 0; - int uuid[4]; - int uuidset = 0; - char *configfile = NULL; - int scan = 0; - char devmode = 0; - int runstop = 0; - int readonly = 0; - char *mddev = NULL; - char *subdev[MD_SB_DISKS]; - int devmodes[MD_SB_DISKS]; - int subdevs = 0; - int verbose = 0; - int force = 0; - - int mdfd = -1; - - while ((opt=getopt_long(argc, argv, - short_options, long_options, - NULL)) != -1) { - - switch(opt) { - case '@': /* just incase they say --manage */ - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - /* setting mode - only once */ - if (mode) { - fprintf(stderr, Name ": --%s/-%c not allowed, mode already set to %s\n", - long_options[opt-'A'+1].name, - long_options[opt-'A'+1].val, - long_options[mode-'A'+1].name); - exit(2); - } - mode = opt; - continue; - - case 'h': - help_text = Help; - switch (mode) { - case 'C': help_text = Help_create; break; - case 'B': help_text = Help_build; break; - case 'A': help_text = Help_assemble; break; - } - fputs(help_text,stderr); - exit(0); - - case 'V': - fputs(Version, stderr); - exit(0); - - case 'v': verbose = 1; - continue; - - case 1: /* an undecorated option - must be a device name. - * The first device is the "md" device unless scan - * has been set or mode is Examine or Detail - */ - if (mddev == NULL && !scan && mode != 'E' && mode != 'D') - mddev = optarg; - else { - if (subdevs +1 >= MD_SB_DISKS) { - fprintf(stderr, Name ": too many devices at %s - current limit -s %d\n", - optarg, MD_SB_DISKS); - exit(2); - } - subdev[subdevs] = optarg; - devmodes[subdevs] = devmode; - subdevs++; - } - continue; - - case ':': - case '?': - fputs(Usage, stderr); - exit(2); - default: - /* force mode setting - @==manage if nothing else */ - if (!mode) mode = '@'; + int mdfd = open(dev, O_RDWR, 0); + if (mdfd < 0) + fprintf(stderr,Name ": error opening %s: %s\n", + dev, strerror(errno)); + else if (md_get_version(mdfd) <= 0) { + fprintf(stderr, Name ": %s does not appear to be an md device\n", + dev); + close(mdfd); + mdfd = -1; } + return mdfd; +} - /* We've got a mode, and opt is now something else which - * could depend on the mode */ -#define O(a,b) ((a<<8)|b) - switch (O(mode,opt)) { - case O('C','c'): - case O('B','c'): /* chunk or rounding */ - if (chunk) { - fprintf(stderr, Name ": chunk/rounding may only be specified once. " - "Second value is %s.\n", optarg); - exit(2); - } - chunk = strtol(optarg, &c, 10); - if (!optarg[0] || *c || chunk<4 || ((chunk-1)&chunk)) { - fprintf(stderr, Name ": invalid chunk/rounding value: %s\n", - optarg); - exit(2); - } - continue; - case O('c','z'): /* size */ - if (size) { - fprintf(stderr, Name ": size may only be specified once. " - "Second value is %s.\n", optarg); - exit(2); - } - size = strtol(optarg, &c, 10); - if (!optarg[0] || *c || size < 4) { - fprintf(stderr, Name ": invalid size: %s\n", - optarg); + +int main(int argc, char *argv[]) +{ + char mode = '\0'; + int opt; + char *help_text; + char *c; + int rv; + int i; + + int chunk = 0; + int size = 0; + int level = -10; + int layout = -1; + int raiddisks = 0; + int sparedisks = 0; + struct mddev_ident_s ident; + char *configfile = NULL; + int scan = 0; + char devmode = 0; + int runstop = 0; + int readonly = 0; + char *devs[MD_SB_DISKS+1]; + int devmodes[MD_SB_DISKS+1]; + int devs_found = 0; + int verbose = 0; + int force = 0; + + char *mailaddr = NULL; + char *program = NULL; + int delay = 0; + + int mdfd = -1; + + ident.uuid_set=0; + ident.super_minor= -1; + ident.devices=0; + + while ((opt=getopt_long(argc, argv, + short_options, long_options, + NULL)) != -1) { + + switch(opt) { + case '@': /* just incase they say --manage */ + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + /* setting mode - only once */ + if (mode) { + fprintf(stderr, Name ": --%s/-%c not allowed, mode already set to %s\n", + long_options[opt-'A'+1].name, + long_options[opt-'A'+1].val, + long_options[mode-'A'+1].name); + exit(2); + } + mode = opt; + continue; + + case 'h': + help_text = Help; + switch (mode) { + case 'C': help_text = Help_create; break; + case 'B': help_text = Help_build; break; + case 'A': help_text = Help_assemble; break; + } + fputs(help_text,stderr); + exit(0); + + case 'V': + fputs(Version, stderr); + exit(0); + + case 'v': verbose = 1; + continue; + + case 1: /* an undecorated option - must be a device name. + * Depending on mode, it could be that: + * All devices listed are "md" devices : --Detail, -As + * No devices are "md" devices : --Examine + * First device is "md", others are component: -A,-B,-C + */ + if (devs_found >= MD_SB_DISKS+1) { + fprintf(stderr, Name ": too many devices at %s - current limit -s %d\n", + optarg, MD_SB_DISKS+1); + exit(2); + } + devs[devs_found] = optarg; + devmodes[devs_found] = devmode; + devs_found++; + continue; + + case ':': + case '?': + fputs(Usage, stderr); exit(2); + default: + /* force mode setting - @==manage if nothing else */ + if (!mode) mode = '@'; } - continue; - case O('C','l'): - case O('B','l'): /* set raid level*/ - if (level != -10) { - fprintf(stderr, Name ": raid level may only be set once. " - "Second value is %s.\n", optarg); - exit(2); - } - level = map_name(pers, optarg); - if (level == -10) { - fprintf(stderr, Name ": invalid raid level: %s\n", - optarg); - exit(2); - } - if (level > 0 && mode == 'B') { - fprintf(stderr, Name ": Raid level %s not permitted with --build.\n", - optarg); - exit(2); - } - if (sparedisks > 0 && level < 1) { - fprintf(stderr, Name ": raid level %s is incompatible with spare-disks setting.\n", - optarg); - exit(2); - } - continue; + /* We've got a mode, and opt is now something else which + * could depend on the mode */ +#define O(a,b) ((a<<8)|b) + switch (O(mode,opt)) { + case O('C','c'): + case O('B','c'): /* chunk or rounding */ + if (chunk) { + fprintf(stderr, Name ": chunk/rounding may only be specified once. " + "Second value is %s.\n", optarg); + exit(2); + } + chunk = strtol(optarg, &c, 10); + if (!optarg[0] || *c || chunk<4 || ((chunk-1)&chunk)) { + fprintf(stderr, Name ": invalid chunk/rounding value: %s\n", + optarg); + exit(2); + } + continue; - case O('C','p'): /* raid5 layout */ - if (layout >= 0) { - fprintf(stderr,Name ": layout may only be sent once. " - "Second value was %s\n", optarg); - exit(2); - } - switch(level) { - default: - fprintf(stderr, Name ": layout now meaningful for %s arrays.\n", - map_num(pers, level)); - exit(2); - case -10: - fprintf(stderr, Name ": raid level must be given before layout.\n"); - exit(2); - - case 5: - layout = map_name(r5layout, optarg); - if (layout==-10) { - fprintf(stderr, Name ": layout %s not understood for raid5.\n", - optarg); - exit(2); - } - break; - } - continue; - - case O('C','n'): - case O('B','n'): /* number of raid disks */ - if (raiddisks) { - fprintf(stderr, Name ": raid-disks set twice: %d and %s\n", - raiddisks, optarg); - exit(2); - } - raiddisks = strtol(optarg, &c, 10); - if (!optarg[0] || *c || raiddisks<=0 || raiddisks > MD_SB_DISKS) { - fprintf(stderr, Name ": invalid number of raid disks: %s\n", - optarg); - exit(2); - } - continue; + case O('C','z'): /* size */ + if (size) { + fprintf(stderr, Name ": size may only be specified once. " + "Second value is %s.\n", optarg); + exit(2); + } + size = strtol(optarg, &c, 10); + if (!optarg[0] || *c || size < 4) { + fprintf(stderr, Name ": invalid size: %s\n", + optarg); + exit(2); + } + continue; - case O('C','x'): /* number of spare (eXtra) discs */ - if (sparedisks) { - fprintf(stderr,Name ": spare-disks set twice: %d and %s\n", - sparedisks, optarg); - exit(2); - } - if (level > -10 && level < 1) { - fprintf(stderr, Name ": spare-disks setting is incompatible with raid level %d\n", - level); - exit(2); - } - sparedisks = strtol(optarg, &c, 10); - if (!optarg[0] || *c || sparedisks < 0 || sparedisks > MD_SB_DISKS - raiddisks) { - fprintf(stderr, Name ": invalid number of spare disks: %s\n", - optarg); - exit(2); - } - continue; - - /* now for the Assemble options */ - case O('A','f'): /* force assembly */ - force = 1; - continue; - case O('A','u'): /* uuid of array */ - if (uuidset) { - fprintf(stderr, Name ": uuid cannot bet set twice. " - "Second value %s.\n", optarg); - exit(2); - } - if (parse_uuid(optarg, uuid)) - uuidset = 1; - else { - fprintf(stderr,Name ": Bad uuid: %s\n", optarg); - exit(2); - } - continue; + case O('C','l'): + case O('B','l'): /* set raid level*/ + if (level != -10) { + fprintf(stderr, Name ": raid level may only be set once. " + "Second value is %s.\n", optarg); + exit(2); + } + level = map_name(pers, optarg); + if (level == -10) { + fprintf(stderr, Name ": invalid raid level: %s\n", + optarg); + exit(2); + } + if (level > 0 && mode == 'B') { + fprintf(stderr, Name ": Raid level %s not permitted with --build.\n", + optarg); + exit(2); + } + if (sparedisks > 0 && level < 1) { + fprintf(stderr, Name ": raid level %s is incompatible with spare-disks setting.\n", + optarg); + exit(2); + } + continue; - case O('A','c'): /* config file */ - if (configfile) { - fprintf(stderr, Name ": configfile cannot be set twice. " - "Second value is %s.\n", optarg); - exit(2); - } - configfile = optarg; - /* FIXME possibly check that config file exists. Even parse it */ - continue; - case O('A','s'): /* scan */ - scan = 1; - continue; - - /* now the general management options. Some are applicable - * to other modes. None have arguments. - */ - case O('@','a'): - case O('C','a'): - case O('B','a'): - case O('A','a'): /* add a drive */ - devmode = 'a'; - continue; - case O('@','r'): /* remove a drive */ - devmode = 'r'; - continue; - case O('@','f'): /* set faulty */ - case O('C','f'): - devmode = 'f'; - continue; - case O('@','R'): - case O('A','R'): - case O('B','R'): - case O('C','R'): /* Run the array */ - if (runstop < 0) { - fprintf(stderr, Name ": Cannot both Stop and Run an array\n"); - exit(2); - } - runstop = 1; - continue; - case O('@','S'): - if (runstop > 0) { - fprintf(stderr, Name ": Cannot both Run and Stop an array\n"); - exit(2); - } - runstop = -1; - continue; + case O('C','p'): /* raid5 layout */ + if (layout >= 0) { + fprintf(stderr,Name ": layout may only be sent once. " + "Second value was %s\n", optarg); + exit(2); + } + switch(level) { + default: + fprintf(stderr, Name ": layout now meaningful for %s arrays.\n", + map_num(pers, level)); + exit(2); + case -10: + fprintf(stderr, Name ": raid level must be given before layout.\n"); + exit(2); + + case 5: + layout = map_name(r5layout, optarg); + if (layout==-10) { + fprintf(stderr, Name ": layout %s not understood for raid5.\n", + optarg); + exit(2); + } + break; + } + continue; + + case O('C','n'): + case O('B','n'): /* number of raid disks */ + if (raiddisks) { + fprintf(stderr, Name ": raid-disks set twice: %d and %s\n", + raiddisks, optarg); + exit(2); + } + raiddisks = strtol(optarg, &c, 10); + if (!optarg[0] || *c || raiddisks<=0 || raiddisks > MD_SB_DISKS) { + fprintf(stderr, Name ": invalid number of raid disks: %s\n", + optarg); + exit(2); + } + continue; + + case O('C','x'): /* number of spare (eXtra) discs */ + if (sparedisks) { + fprintf(stderr,Name ": spare-disks set twice: %d and %s\n", + sparedisks, optarg); + exit(2); + } + if (level > -10 && level < 1) { + fprintf(stderr, Name ": spare-disks setting is incompatible with raid level %d\n", + level); + exit(2); + } + sparedisks = strtol(optarg, &c, 10); + if (!optarg[0] || *c || sparedisks < 0 || sparedisks > MD_SB_DISKS - raiddisks) { + fprintf(stderr, Name ": invalid number of spare disks: %s\n", + optarg); + exit(2); + } + continue; + case O('C','f'): /* force honouring of device list */ + force=1; + continue; + + /* now for the Assemble options */ + case O('A','f'): /* force assembly */ + force = 1; + continue; + case O('A','u'): /* uuid of array */ + if (ident.uuid_set) { + fprintf(stderr, Name ": uuid cannot bet set twice. " + "Second value %s.\n", optarg); + exit(2); + } + if (parse_uuid(optarg, ident.uuid)) + ident.uuid_set = 1; + else { + fprintf(stderr,Name ": Bad uuid: %s\n", optarg); + exit(2); + } + continue; + + case O('A','c'): /* config file */ + case O('F','c'): + if (configfile) { + fprintf(stderr, Name ": configfile cannot be set twice. " + "Second value is %s.\n", optarg); + exit(2); + } + configfile = optarg; + /* FIXME possibly check that config file exists. Even parse it */ + continue; + case O('A','s'): /* scan */ + scan = 1; + continue; + + case O('F','m'): /* mail address */ + if (mailaddr) + fprintf(stderr, Name ": only specify one mailaddress. %s ignored.\n", + optarg); + else + mailaddr = optarg; + continue; + + case O('F','p'): /* alert program */ + if (program) + fprintf(stderr, Name ": only specify one alter program. %s ignored.\n", + optarg); + else + program = optarg; + continue; - case O('@','o'): - if (readonly < 0) { - fprintf(stderr, Name ": Cannot have both readonly and readwrite\n"); + case O('F','d'): /* delay in seconds */ + if (delay) + fprintf(stderr, Name ": only specify delay once. %s ignored.\n", + optarg); + else { + delay = strtol(optarg, &c, 10); + if (!optarg[0] || *c || delay<1) { + fprintf(stderr, Name ": invalid delay: %s\n", + optarg); + exit(2); + } + } + continue; + + + /* now the general management options. Some are applicable + * to other modes. None have arguments. + */ + case O('@','a'): + case O('C','a'): + case O('B','a'): + case O('A','a'): /* add a drive */ + devmode = 'a'; + continue; + case O('@','r'): /* remove a drive */ + devmode = 'r'; + continue; + case O('@','f'): /* set faulty */ + devmode = 'f'; + continue; + case O('@','R'): + case O('A','R'): + case O('B','R'): + case O('C','R'): /* Run the array */ + if (runstop < 0) { + fprintf(stderr, Name ": Cannot both Stop and Run an array\n"); + exit(2); + } + runstop = 1; + continue; + case O('@','S'): + if (runstop > 0) { + fprintf(stderr, Name ": Cannot both Run and Stop an array\n"); + exit(2); + } + runstop = -1; + continue; + + case O('@','o'): + if (readonly < 0) { + fprintf(stderr, Name ": Cannot have both readonly and readwrite\n"); + exit(2); + } + readonly = 1; + continue; + case O('@','w'): + if (readonly > 0) { + fprintf(stderr, "mkdctl: Cannot have both readwrite and readonly.\n"); + exit(2); + } + readonly = -1; + continue; + } + /* We have now processed all the valid options. Anything else is + * an error + */ + fprintf(stderr, Name ": option %c not valid in mode %c\n", + opt, mode); exit(2); - } - readonly = 1; - continue; - case O('@','w'): - if (readonly > 0) { - fprintf(stderr, "mkdctl: Cannot have both readwrite and readonly.\n"); + + } + + if (!mode) { + fputs(Usage, stderr); exit(2); - } - readonly = -1; - continue; } - /* We have now processed all the valid options. Anything else is - * an error + /* Ok, got the option parsing out of the way + * hopefully it's mostly right but there might be some stuff + * missing + * + * That is mosty checked in ther per-mode stuff but... + * + * For @,B,C and A without -s, the first device listed must be an md device + * we check that here and open it. */ - fprintf(stderr, Name ": option %c not valid in mode %c\n", - opt, mode); - exit(2); - - } - - if (!mode) { - fputs(Usage, stderr); - exit(2); - } - /* Ok, got the option parsing out of the way - * hopefully it's mostly right but there might be some stuff - * missing - * - * That is mosty checked in ther per-mode stuff but... - * - * There must be an mddev unless D or E or (A and scan) - * If there is one, we open it. - */ - - if (mode !='D' && mode !='E' && ! (mode =='A' && scan)) { - if (!mddev) { - fprintf(stderr, Name ": an md device must be given in this mode\n"); - exit(2); - } - mdfd = open(mddev, O_RDWR, 0); - if (mdfd < 0) { - fprintf(stderr,Name ": error opening %s: %s\n", - mddev, strerror(errno)); - exit(1); + + if (mode=='@' || mode == 'B' || mode == 'C' || (mode == 'A' && ! scan)) { + if (devs_found < 1) { + fprintf(stderr, Name ": an md device must be given in this mode\n"); + exit(2); + } + mdfd = open_mddev(devs[0]); + if (mdfd < 0) + exit(1); } - if (md_get_version(mdfd) <= 0) { - fprintf(stderr, Name ": %s does not appear to be an md device\n", - mddev); - close(mdfd); - exit(1); + + + rv = 0; + switch(mode) { + case '@':/* Management */ + /* readonly, add/remove, readwrite, runstop */ + if (readonly>0) + rv = Manage_ro(devs[0], mdfd, readonly); + if (!rv && devs_found>1) + rv = Manage_subdevs(devs[0], mdfd, + devs_found-1, devs+1, devmodes+1); + if (!rv && readonly < 0) + rv = Manage_ro(devs[0], mdfd, readonly); + if (!rv && runstop) + rv = Manage_runstop(devs[0], mdfd, runstop); + break; + case 'A': /* Assemble */ + if (!scan) + rv = Assemble(devs[0], mdfd, &ident, configfile, + devs_found-1, devs+1, + readonly, runstop, verbose, force); + else if (devs_found>0) + for (i=0; inext) { + mdfd = open_mddev(array_list->devname); + if (mdfd < 0) { + rv |= 1; + continue; + } + rv |= Assemble(array_list->devname, mdfd, + array_list, configfile, + 0, NULL, + readonly, runstop, verbose, force); + } + } + break; + case 'B': /* Build */ + rv = Build(devs[0], mdfd, chunk, level, raiddisks, devs_found-1,devs+1); + break; + case 'C': /* Create */ + rv = Create(devs[0], mdfd, chunk, level, layout, size, + raiddisks, sparedisks, + devs_found-1,devs+1, runstop, verbose, force); + break; + case 'D': /* Detail */ + for (i=0; i0) - rv = Manage_ro(mddev, mdfd, readonly); - if (!rv && subdevs) - rv = Manage_subdevs(mddev, mdfd, subdevs, subdev, devmodes); - if (!rv && readonly < 0) - rv = Manage_ro(mddev, mdfd, readonly); - if (!rv && runstop) - rv = Manage_runstop(mddev, mdfd, runstop); - break; - case 'A': /* Assemble */ - rv = Assemble(mddev, mdfd, uuid, uuidset, configfile, scan, subdevs, subdev, readonly, runstop, verbose, force); - break; - case 'B': /* Build */ - rv = Build(mddev, mdfd, chunk, level, raiddisks, subdevs,subdev); - break; - case 'C': /* Create */ - rv = Create(mddev, mdfd, chunk, level, layout, size, raiddisks, sparedisks, - subdevs,subdev,runstop, verbose); - break; - case 'D': /* Detail */ - for (i=0; i + +%description +mdctl is a single program that can be used to control Linux md devices. It +is intended to provide all the functionality of the mdtools and raidtools +but with a very different interface. + +mdctl can perform all functions without a configuration file. There is the +option of using a configuration file, but not in the same way that raidtools +uses one. + +raidtools uses a configuration file to describe how to create a RAID array, +and also uses this file partially to start a previously created RAID array. +Further, raidtools requires the configuration file for such things as +stopping a raid array, which needs to know nothing about the array. + + +%prep +%setup -q -n mdctl + +%build +make + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/sbin +install -m755 mdctl $RPM_BUILD_ROOT/sbin/ + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%doc TODO testconfig testconfig2 +/sbin/* + +%changelog +* Tue Aug 07 2001 Danilo Godec +- initial RPM build diff --git a/raid5extend.c b/raid5extend.c new file mode 100644 index 00000000..98615a69 --- /dev/null +++ b/raid5extend.c @@ -0,0 +1,80 @@ + +int phys2log(int phys, int stripe, int n, int layout) +{ + /* In an 'n' disk array using 'layout', + * in stripe 'stripe', the physical disc 'phys' + * stores what logical chunk? + * -1 mean parity. + * + */ + switch(layout) { + case ALGORITHM_LEFT_ASYMMETRIC: + pd = (n-1) - (stripe % n); + if (phys < pd) + return phys; + else if (phys == pd) + return -1; + else return phys-1; + + case ALGORITHM_RIGHT_ASYMMETRIC: + pd = stripe % n; + if (phys < pd) + return phys; + else if (phys == pd) + return -1; + else return phys-1; + + case ALGORITHM_LEFT_SYMMETRIC: + pd = (n-1) - (stripe %n); + if (phys < pd) + return phys+ n-1-pd; + else if (phys == pd) + return -1; + else return phys-pd-1; + + case ALGORITHM_RIGHT_SYMMETRIC: + pd = stripe % n; + if (phys < pd) + return phys+ n-1-pd; + else if (phys == pd) + return -1; + else return phys-pd-1; + } + return -2; +} + +raid5_extend(unsigned long len, int chunksize, int layout, int n, int m, int rfds[], int wfds[]) +{ + + static char buf[4096]; + + unsigned long blocks = len/4; + unsigned int blocksperchunk= chunksize/4096; + + unsigned long b; + + for (b=0; b