mdctl-0.5 mdctl-0.5
authorNeil Brown <neilb@suse.de>
Thu, 23 Aug 2001 02:33:20 +0000 (02:33 +0000)
committerNeil Brown <neilb@suse.de>
Thu, 23 Aug 2001 02:33:20 +0000 (02:33 +0000)
20 files changed:
Assemble.c
Create.c
Detail.c
Examine.c
Makefile
Manage.c
Monitor.c [new file with mode: 0644]
ReadMe.c
TAGS
TODO
config.c
makedist
md_p.h
mdctl.8 [new file with mode: 0644]
mdctl.c
mdctl.h
mdctl.spec [new file with mode: 0644]
raid5extend.c [new file with mode: 0644]
testconfig [deleted file]
testconfig2 [deleted file]

index e5210ce..272602e 100644 (file)
  */
 
 #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; i<MD_SB_DISKS; i++)
                best[i] = -1;
@@ -215,6 +164,8 @@ int Assemble(char *mddev, int mdfd,
                int dfd;
                struct stat stb;
                int inargv;
+               int havesuper=0;
+
                if (subdevs) {
                        devname = *subdev++;
                        subdevs--;
@@ -225,61 +176,69 @@ int Assemble(char *mddev, int mdfd,
                        inargv=0;
                }
 
+               if (ident->devices &&
+                   !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<first_super.raid_disks; i++) {
+                       int j = best[i];
+                       if (j>=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<MD_SB_DISKS; i++) {
+               int j = best[i];
+               int fd;
+               if (j<0)
+                       continue;
+               if (!devices[j].uptodate)
+                       continue;
+               chosen_drive = j;
+               if ((fd=open(devices[j].devname, O_RDONLY))< 0) {
+                       fprintf(stderr, Name ": Cannot open %s: %s\n",
+                               devices[j].devname, strerror(errno));
+                       return 1;
+               }
+               if (load_super(fd, &super)) {
+                       close(fd);
+                       fprintf(stderr, Name ": RAID superblock has disappeared from %s\n",
+                               devices[j].devname);
+                       return 1;
+               }
+               close(fd);
+       }
+
+       for (i=0; i<MD_SB_DISKS; i++) {
+               int j = best[i];
+               if (j<0)
+                       continue;
+               if (!devices[j].uptodate)
+                       continue;
+               if (devices[j].major != super.disks[j].major ||
+                   devices[j].minor != super.disks[j].minor) {
+                       change |= 1;
+                       super.disks[j].major = devices[j].major;
+                       super.disks[j].minor = devices[j].minor;
+               }
+               if (devices[j].uptodate &&
+                   (super.disks[i].state & (1 << MD_DISK_FAULTY))) {
+                       if (force) {
+                               fprintf(stderr, Name ": "
+                                       "clearing FAULT flag for device %d in %s for %s\n",
+                                       j, mddev, devices[j].devname);
+                               super.disks[i].state &= ~(1<<MD_DISK_FAULTY);
+                               change |= 2;
+                       } else {
+                               fprintf(stderr, Name ": "
+                                       "device %d in %s is marked faulty in superblock, but %s seems ok\n",
+                                       i, mddev, devices[j].devname);
+                       }
+               }
+               if (!devices[j].uptodate &&
+                   !(super.disks[i].state & (1 << MD_DISK_FAULTY))) {
+                       fprintf(stderr, Name ": devices %d of %s is not marked FAULTY in superblock, but cannot be found\n",
+                               i, mddev);
+               }
+       }
+
+       if ((force && (change & 2))
+           || (old_linux && (change & 1))) {
+               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);
+               change = 0;
+       }
+
        /* Almost ready to actually *do* something */
        if (!old_linux) {
                if (ioctl(mdfd, SET_ARRAY_INFO, NULL) != 0) {
@@ -338,9 +426,16 @@ int Assemble(char *mddev, int mdfd,
                                mddev, strerror(errno));
                        return 1;
                }
-               /* First, add the raid disks */
-               for (i=0; i<first_super.raid_disks; i++) {
-                       int j = best[i];
+               /* First, add the raid disks, but add the chosen one last */
+               for (i=0; i<=MD_SB_DISKS; i++) {
+                       int j;
+                       if (i < MD_SB_DISKS) {
+                               j = best[i];
+                               if (j == chosen_drive)
+                                       continue;
+                       } else
+                               j = chosen_drive;
+
                        if (j >= 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<first_super.nr_disks; i++) {
-                   if (!devices[i].uptodate)
-                       continue;
-                   if (chosen_drive == -1) {
-                           int fd;
-                           chosen_drive = i;
-                           if (open(devices[i].devname, O_RDONLY)>= 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)) {
index 0f477bc..c7d3597 100644 (file)
--- 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<subdevs; i++) {
                char *dname = subdev[i];
                int dsize, freesize;
-               int fd = open(dname, O_RDONLY, 0);
+               int fd;
+               if (strcasecmp(subdev[i], "missing")==0) {
+                       if (first_missing > 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<subdevs; i++) {
-           int fd = open(subdev[i], O_RDONLY, 0);
-           struct stat stb;
-           mdu_disk_info_t disk;
-           if (fd < 0) {
-               fprintf(stderr, Name ": failed to open %s after earlier success - aborting\n",
-                       subdev[i]);
-               return 1;
-           }
-           fstat(fd, &stb);
-           disk.number = i;
-           if ((level==4 || level==5) &&
-               disk.number >= 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;
index cb6dd66..026c3b3 100644 (file)
--- 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<array.nr_disks; d++) {
+       for (d= 0; d<array.raid_disks+array.spare_disks; d++) {
                mdu_disk_info_t disk;
                char *dv;
                disk.number = d;
index 4ed4f6b..bb4290d 100644 (file)
--- a/Examine.c
+++ b/Examine.c
@@ -147,7 +147,7 @@ int Examine(char *dev)
        }
        printf("\n");
        printf("      Number   Major   Minor   RaidDisk   State\n");
-       for (d= -1; d<(signed int)super.nr_disks; d++) {
+       for (d= -1; d<(signed int)(super.raid_disks+super.spare_disks); d++) {
                mdp_disk_t *dp;
                char *dv;
                char nb[5];
index a7a7c48..5d0b142 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@
 
 CFLAGS = -Wall,error,strict-prototypes -ggdb
 
-OBJS =  mdctl.o config.o  ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o dlink.o
+OBJS =  mdctl.o config.o  ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o Monitor.o dlink.o
 all : mdctl
 
 mdctl : $(OBJS)
@@ -38,7 +38,7 @@ mdctl : $(OBJS)
 $(OBJS) : mdctl.h
 
 clean : 
-       rm -f mdctl $(OBJS)
+       rm -f mdctl $(OBJS) core
 
 dist : clean
        ./makedist
index eda275a..d8cc136 100644 (file)
--- a/Manage.c
+++ b/Manage.c
@@ -107,7 +107,7 @@ int Manage_runstop(char *devname, int fd, int runstop)
                }
        } else if (runstop < 0){
                if (ioctl(fd, STOP_ARRAY, NULL)) {
-                       fprintf(stderr, Name ": fail to re writable for %s: %s\n",
+                       fprintf(stderr, Name ": fail to stop array %s: %s\n",
                                devname, strerror(errno));
                        return 1;
                }
diff --git a/Monitor.c b/Monitor.c
new file mode 100644 (file)
index 0000000..968e4b3
--- /dev/null
+++ b/Monitor.c
@@ -0,0 +1,211 @@
+/*
+ * mdctl - manage Linux "md" devices aka RAID arrays.
+ *
+ * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ *
+ *
+ *    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: <neilb@cse.unsw.edu.au>
+ *    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       <sys/signal.h>
+
+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 active<raid and spare==0
+        * we look at other arrays that have same spare-group
+        * If we find one with active==raid and spare>0,
+        *  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<array.raid_disks+array.spare_disks; i++) {
+                               mdu_disk_info_t disc;
+                               disc.number = i;
+                               if (ioctl(fd, GET_DISK_INFO, &disc)>= 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 <root>\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 */
+}
index ccfdaac..49830cd 100644 (file)
--- 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 bceb7ad..6c159c9 100644 (file)
--- a/TAGS
+++ b/TAGS
@@ -1,4 +1,12 @@
 \f
+dlink.h,193
+struct __dl_head\7f__dl_head\ 15,100
+#define        dl_alloc(\7fdl_alloc\ 111,187
+#define        dl_new(\7fdl_new\ 112,297
+#define        dl_newv(\7fdl_newv\ 113,341
+#define dl_next(\7fdl_next\ 115,391
+#define dl_prev(\7fdl_prev\ 116,461
+\f
 md_p.h,1316
 #define _MD_P_H\7f16,582
 #define MD_RESERVED_BYTES      \7f44,1414
@@ -69,72 +77,123 @@ typedef struct mdu_start_info_s \7fmdu_start_info_s\ 197,2713
 typedef struct mdu_param_s\7fmdu_param_s\ 1108,2878
 } mdu_param_t;\7fmdu_param_t\ 1113,3014
 \f
-mdctl.h,823
+mdctl.h,826
 #define        __USE_LARGEFILE64\7f30,1115
 #define        MD_MAJOR \7f47,1491
-extern char short_options[\7f52,1531
-extern struct option long_options[\7f53,1560
-extern char Version[\7f54,1597
-extern char Version[], Usage[\7f54,1597
-extern char Version[], Usage[], Help[\7f54,1597
-extern char Version[], Usage[], Help[], Help_create[\7f54,1597
-extern char Version[], Usage[], Help[], Help_create[], Help_build[\7f54,1597
-extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[\7f54,1597
-typedef struct mddev_uuid_s \7fmddev_uuid_s\ 158,1762
-} *mddev_uuid_t;\7fmddev_uuid_t\ 162,1852
-typedef struct mddev_dev_s \7fmddev_dev_s\ 165,1918
-} *mddev_dev_t;\7fmddev_dev_t\ 168,1990
-#define ALGORITHM_LEFT_ASYMMETRIC      \7f73,2044
-#define ALGORITHM_RIGHT_ASYMMETRIC     \7f74,2080
-#define ALGORITHM_LEFT_SYMMETRIC       \7f75,2117
-#define ALGORITHM_RIGHT_SYMMETRIC      \7f76,2152
+#define Name \7f52,1531
+extern char short_options[\7f54,1553
+extern struct option long_options[\7f55,1582
+extern char Version[\7f56,1619
+extern char Version[], Usage[\7f56,1619
+extern char Version[], Usage[], Help[\7f56,1619
+extern char Version[], Usage[], Help[], Help_create[\7f56,1619
+extern char Version[], Usage[], Help[], Help_create[], Help_build[\7f56,1619
+extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[\7f56,1619
+typedef struct mddev_ident_s \7fmddev_ident_s\ 168,2055
+} *mddev_ident_t;\7fmddev_ident_t\ 180,2292
+typedef struct mddev_dev_s \7fmddev_dev_s\ 183,2359
+} *mddev_dev_t;\7fmddev_dev_t\ 186,2431
+typedef struct mapping \7fmapping\ 188,2448
+} mapping_t;\7fmapping_t\ 191,2496
+extern mapping_t r5layout[\7f95,2606
+extern mapping_t r5layout[], pers[\7f95,2606
 \f
 Assemble.c,22
 int Assemble(\7f34,1171
 \f
-Build.c,19
-int Build(\7f32,1135
+Build.c,100
+#define REGISTER_DEV \7f32,1135
+#define START_MD \7f33,1176
+#define STOP_MD \7f34,1217
+int Build(\7f36,1259
 \f
 Create.c,20
-int Create(\7f32,1135
+int Create(\7f34,1171
 \f
 Detail.c,20
 int Detail(\7f34,1171
 \f
 Examine.c,21
-int Examine(\7f34,1171
-\f
-Manage.c,79
-int Manage_ro(\7f32,1135
-int Manage_runstop(\7f36,1191
-int Manage_subdevs(\7f40,1251
-\f
-ReadMe.c,231
-#define Name \7f32,1135
-char Version[\7f33,1156
-char short_options[\7f82,3241
-struct option long_options[\7f83,3297
-char Usage[\7f122,4441
-char Help[\7f127,4498
-char Help_create[\7f181,6989
-char Help_build[\7f203,7973
-char Help_assemble[\7f216,8513
-\f
-config.c,102
-char DefaultConfFile[\7f43,1371
-mddev_uuid_t conf_get_uuids(\7f45,1416
-mddev_dev_t conf_get_devs(\7f50,1482
-\f
-mdctl.c,40
-int main(\7f33,1153
-#define O(\7fO\ 1131,3313
-\f
-util.c,212
+int Examine(\7f37,1261
+\f
+Manage.c,161
+#define REGISTER_DEV \7f34,1171
+#define START_MD \7f35,1212
+#define STOP_MD \7f36,1253
+int Manage_ro(\7f38,1295
+int Manage_runstop(\7f75,2149
+int Manage_subdevs(\7f118,3161
+\f
+ReadMe.c,265
+char Version[\7f32,1135
+char short_options[\7f81,3222
+struct option long_options[\7f82,3280
+char Usage[\7f123,4484
+char Help[\7f128,4541
+char Help_create[\7f185,7233
+char Help_build[\7f212,8453
+char Help_assemble[\7f225,8993
+mapping_t r5layout[\7f284,11939
+mapping_t pers[\7f298,12155
+\f
+config.c,479
+char DefaultConfFile[\7f68,2396
+char *keywords[\7f70,2441
+int match_keyword(\7f77,2640
+char *conf_word(\7f97,3148
+char *conf_line(\7f163,4739
+void free_line(\7f184,5041
+struct conf_dev \7fconf_dev\ 1195,5183
+} *cdevlist \7f198,5244
+int devline(\7f202,5267
+mddev_ident_t mddevlist \7f220,5590
+mddev_ident_t *mddevlp \7f221,5622
+void arrayline(\7f223,5660
+int loaded \7f289,7453
+void load_conffile(\7f291,7470
+mddev_ident_t conf_get_ident(\7f324,7961
+mddev_dev_t conf_get_devs(\7f334,8163
+int match_oneof(\7f369,8791
+\f
+dlink.c,177
+void *dl_head(\7f11,180
+void dl_free(\7f20,289
+void dl_init(\7f26,363
+void dl_insert(\7f32,430
+void dl_add(\7f40,598
+void dl_del(\7f48,763
+char *dl_strndup(\7f57,969
+char *dl_strdup(\7f73,1176
+\f
+mdctl.c,64
+int open_mddev(\7f33,1153
+int main(\7f50,1472
+#define O(\7fO\ 1149,3610
+\f
+raid5extend.c,39
+int phys2log(\7f2,1
+raid5_extend(\7f46,902
+\f
+util.c,573
 int parse_uuid(\7f40,1354
-int md_get_version(\7f80,2091
-int get_linux_version(\7f99,2448
-int enough(\7f111,2639
-int same_uuid(\7f127,2889
-void uuid_from_super(\7f137,3018
-int compare_super(\7f151,3295
-int load_super(\7f185,4258
+int md_get_version(\7f82,2117
+int get_linux_version(\7f101,2476
+int enough(\7f113,2673
+int same_uuid(\7f129,2923
+void uuid_from_super(\7f139,3052
+int compare_super(\7f153,3329
+int load_super(\7f187,4292
+int store_super(\7f227,4950
+int check_ext2(\7f253,5321
+int check_reiser(\7f284,6084
+int check_raid(\7f308,6640
+int ask(\7f324,7003
+char *map_num(\7f344,7368
+int map_name(\7f354,7503
+struct devmap \7fdevmap\ 1369,7832
+} *devlist \7f373,7911
+int devlist_ready \7f374,7930
+#define  __USE_XOPEN_EXTENDED\7f376,7954
+int add_dev(\7f380,8003
+char *map_dev(\7f396,8370
+int calc_sb_csum(\7f412,8645
diff --git a/TODO b/TODO
index 017bcac..9bcf70f 100644 (file)
--- 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,
 
 
 - 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
index 2feaae3..39ef56b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -30,6 +30,8 @@
 #include       "mdctl.h"
 #include       "dlink.h"
 #include       <glob.h>
+#include       <fnmatch.h>
+
 /*
  * 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; i<globbuf.gl_pathc; i++) {
-       mddev_dev_t t = malloc(sizeof(*t));
-       t->devname = strdup(globbuf.gl_pathv[i]);
-       t->next = dlist;
-       dlist = t;
+       for (i=0; i<globbuf.gl_pathc; i++) {
+               mddev_dev_t t = malloc(sizeof(*t));
+               t->devname = 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;
+}
index 789a80a..773e7f0 100755 (executable)
--- 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 d6bb37a..99479b4 100644 (file)
--- 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 (file)
index 0000000..fe9c656
--- /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] <raiddevice> [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 70e8129..6387110 100644 (file)
--- a/mdctl.c
+++ b/mdctl.c
 #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; i<devs_found; i++) {
+                               mddev_ident_t array_ident = conf_get_ident(configfile, devs[i]);
+                               mdfd = open_mddev(devs[i]);
+                               if (mdfd < 0) {
+                                       rv |= 1;
+                                       continue;
+                               }
+                               if (array_ident == NULL) {
+                                       fprintf(stderr, Name ": %s not identified in config file.\n",
+                                               devs[i]);
+                                       rv |= 1;
+                                       continue;
+                               }
+                               rv |= Assemble(devs[i], mdfd, array_ident, configfile,
+                                              0, NULL,
+                                              readonly, runstop, verbose, force);
+                       }
+               else {
+                       mddev_ident_t array_list =  conf_get_ident(configfile, NULL);
+                       if (!array_list) {
+                               fprintf(stderr, Name ": No arrays found in config file\n");
+                               rv = 1;
+                       } else
+                               for (; array_list; array_list = array_list->next) {
+                                       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; i<devs_found; i++)
+                       rv |= Detail(devs[i]);
+               break;
+       case 'E': /* Examine */
+               for (i=0; i<devs_found; i++)
+                       rv |= Examine(devs[i]);
+               break;
+       case 'F': /* Follow */
+               rv= Monitor(devs_found, devs, mailaddr, program,
+                           delay?delay:60, configfile);
        }
-    }
-    
-
-    rv  =0;
-    switch(mode) {
-    case '@':/* Management */
-       /* readonly, add/remove, readwrite, runstop */
-       if (readonly>0)
-           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<subdevs; i++)
-           rv |= Detail(subdev[i]);
-       break;
-    case 'E': /* Examine */
-       for (i=0; i<subdevs; i++)
-           rv |= Examine(subdev[i]);
-       break;
-    }
-    exit(rv);
+       exit(rv);
 }
-
diff --git a/mdctl.h b/mdctl.h
index b038555..4308716 100644 (file)
--- a/mdctl.h
+++ b/mdctl.h
@@ -56,12 +56,30 @@ extern struct option long_options[];
 extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[];
 
 /* structures read from config file */
-/* List of mddevice names and uuids */
-typedef struct mddev_uuid_s {
+/* List of mddevice names and identifiers
+ * Identifiers can be:
+ *    uuid=128-hex-uuid
+ *    super-minor=decimal-minor-number-from-superblock
+ *    devices=comma,separated,list,of,device,names,with,wildcards
+ *
+ * If multiple fields are present, the intersection of all matching
+ * devices is considered
+ */
+typedef struct mddev_ident_s {
        char *devname;
+       
+       int uuid_set;
        __u32 uuid[4];
-       struct mddev_uuid_s *next;
-} *mddev_uuid_t;
+
+       int super_minor;        /* -1 if not set */
+
+       char *devices;          /* comma separated list of device
+                                * names with wild cards
+                                */
+
+       char *spare_group;
+       struct mddev_ident_s *next;
+} *mddev_ident_t;
 
 /* List of device names - wildcards expanded */
 typedef struct mddev_dev_s {
@@ -74,6 +92,10 @@ typedef struct mapping {
        int num;
 } mapping_t;
 
+#ifndef Sendmail
+#define Sendmail "/usr/lib/sendmail -t"
+#endif
+
 extern char *map_num(mapping_t *map, int num);
 extern int map_name(mapping_t *map, char *name);
 extern mapping_t r5layout[], pers[];
@@ -88,8 +110,8 @@ extern int Manage_subdevs(char *devname, int fd,
 
 
 extern 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);
@@ -102,10 +124,14 @@ extern int Build(char *mddev, int mdfd, int chunk, int level,
 extern 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);
 
 extern int Detail(char *dev);
 extern int Examine(char *dev);
+extern int Monitor(int num_devs, char *devlist[],
+                 char *mailaddr, char *alert_cmd,
+                 int period,
+                 char *config);
 
 extern int md_get_version(int fd);
 extern int get_linux_version();
@@ -114,5 +140,5 @@ extern int check_ext2(int fd, char *name);
 extern int check_reiser(int fd, char *name);
 extern int check_raid(int fd, char *name);
 
-extern mddev_uuid_t conf_get_uuids(char *);
+extern mddev_ident_t conf_get_ident(char *, char*);
 extern mddev_dev_t conf_get_devs(char *);
diff --git a/mdctl.spec b/mdctl.spec
new file mode 100644 (file)
index 0000000..a40a1a4
--- /dev/null
@@ -0,0 +1,47 @@
+Summary: mdctl is used for controlling Linux md devices (aka RAID arrays)
+Name: mdctl
+Version: 0.5
+Release: 1
+Source0: http://www.cse.unsw.edu.au/~neilb/source/mdctl/mdctl-%{version}.tgz
+URL: http://www.cse.unsw.edu.au/~neilb/source/mdctl/
+Copyright: GPL
+Group: Utilities/System
+BuildRoot: /var/tmp/%{name}-root
+Packager: Danilo Godec <danci@agenda.si>
+
+%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 <danci@agenda.si>
+- initial RPM build
diff --git a/raid5extend.c b/raid5extend.c
new file mode 100644 (file)
index 0000000..98615a6
--- /dev/null
@@ -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<blocks; b++) {
+       unsigned long stripe = b / blocksperchunk;
+       unsigned int offset = b - (stripe*blocksperchunk);
+       unsigned long chunk = stripe * (n-1);
+       int src;
+       for (src=0; src<n; src++) {
+           int dnum, snum;
+           if (read(rfds[src], buf, sizeof(buf)) != sizeof(buf)) {
+               error();
+               return 0;
+           }
+
+           snum = phys2log(src, stripe, n, layout);
+
+           if (snum == -1)
+               continue;
+           chunk = stripe*(n-1)+snum;
+
+           dstripe = chunk/(m-1);
+           dnum = log2phys(chunk-(stripe*(m-1)), dstripe, m, layout);
+           llseek(wfds[dnum], dstripe*chunksize+(offset*4096), 0);
+           write(wfds[dnum], buf, sizeof(buf));
+       }
+    }
+}
diff --git a/testconfig b/testconfig
deleted file mode 100644 (file)
index 0ed8b2c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Hello there, "this is a" conf file
-  which has several lines
-# there are comments
-  # losts of comments
- with comments # really truely
-Dev lines are needed
-
- and might have
- "blanks in them,
- they really could
-I think
- that empty "" strings are confusing
\ No newline at end of file
diff --git a/testconfig2 b/testconfig2
deleted file mode 100644 (file)
index 6cd7a78..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-
-DEV /dev/hda* /dev/sd?
-ARRAY /dev/md0 uuid=1234.5678.abcd.ef09:1234.5678.abcd.ef90