mdctl-0.6 mdctl-0.6
authorNeil Brown <neilb@suse.de>
Wed, 6 Mar 2002 23:17:40 +0000 (23:17 +0000)
committerNeil Brown <neilb@suse.de>
Wed, 6 Mar 2002 23:17:40 +0000 (23:17 +0000)
19 files changed:
Assemble.c
Build.c
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Create.c
Detail.c
Examine.c
Makefile
Manage.c
Monitor.c
ReadMe.c
TODO
config.c
makedist
mdctl.8
mdctl.c
mdctl.h
mdctl.man [new file with mode: 0644]
util.c

index 272602e..1a4cb7e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -33,7 +33,7 @@
 
 int Assemble(char *mddev, int mdfd,
             mddev_ident_t ident, char *conffile,
-            int subdevs, char **subdev,
+            mddev_dev_t devlist,
             int readonly, int runstop,
             int verbose, int force)
 {
@@ -67,7 +67,7 @@ int Assemble(char *mddev, int mdfd,
         *
         * If !uuidset and scan, look in conf-file for uuid
         *       If not found, give up
-        * If !subdevs and scan and uuidset, get list of devs from conf-file 
+        * If !devlist and scan and uuidset, get list of devs from conf-file 
         *
         * For each device:
         *   Check superblock - discard if bad
@@ -94,7 +94,6 @@ int Assemble(char *mddev, int mdfd,
        int old_linux = 0;
        int vers;
        mdu_array_info_t array;
-       mddev_dev_t devlist = NULL;
        mdp_super_t first_super, super;
        struct {
                char *devname;
@@ -108,8 +107,10 @@ int Assemble(char *mddev, int mdfd,
        int devcnt = 0, okcnt, sparecnt;
        int i;
        int most_recent = 0;
-       int chosen_drive = -1;
+       int chosen_drive;
        int change = 0;
+       int inargv = 0;
+       int start_partial_ok = force || devlist==NULL;
        
        vers = md_get_version(mdfd);
        if (vers <= 0) {
@@ -139,7 +140,7 @@ int Assemble(char *mddev, int mdfd,
         * there must be something in the identity
         */
 
-       if (subdevs == 0 &&
+       if (!devlist &&
            ident->uuid_set == 0 &&
            ident->super_minor < 0 &&
            ident->devices == NULL) {
@@ -147,8 +148,9 @@ int Assemble(char *mddev, int mdfd,
                        mddev);
                return 1;
        }
-       if (subdevs==0)
+       if (devlist == NULL)
                devlist = conf_get_devs(conffile);
+       else inargv = 1;
 
        first_super.md_magic = 0;
        for (i=0; i<MD_SB_DISKS; i++)
@@ -158,23 +160,15 @@ int Assemble(char *mddev, int mdfd,
            fprintf(stderr, Name ": looking for devices for %s\n",
                    mddev);
 
-       while (subdevs || devlist) {
+       while ( devlist) {
                char *devname;
                int this_uuid[4];
                int dfd;
                struct stat stb;
-               int inargv;
                int havesuper=0;
 
-               if (subdevs) {
-                       devname = *subdev++;
-                       subdevs--;
-                       inargv=1;
-               } else {
-                       devname = devlist->devname;
-                       devlist = devlist->next;
-                       inargv=0;
-               }
+               devname = devlist->devname;
+               devlist = devlist->next;
 
                if (ident->devices &&
                    !match_oneof(ident->devices, devname))
@@ -190,11 +184,11 @@ int Assemble(char *mddev, int mdfd,
                        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",
+               } else if ((stb.st_mode & S_IFMT) != S_IFBLK) {
+                       fprintf(stderr, Name ": %s is not a block device.\n",
                                devname);
                        close(dfd);
-               } if (load_super(dfd, &super)) {
+               } else if (load_super(dfd, &super)) {
                        if (inargv || verbose)
                                fprintf( stderr, Name ": no RAID superblock on %s\n",
                                         devname);
@@ -219,14 +213,25 @@ int Assemble(char *mddev, int mdfd,
                                        devname);
                        continue;
                }
+               if (ident->level != -10 &&
+                   (!havesuper|| ident->level != super.level)) {
+                       if (inargv || verbose)
+                               fprintf(stderr, Name ": %s has wrong raid level.\n",
+                                       devname);
+                       continue;
+               }
+               if (ident->raid_disks != -1 &&
+                   (!havesuper || ident->raid_disks!= super.raid_disks)) {
+                       if (inargv || verbose)
+                               fprintf(stderr, Name ": %s requires wrong number of drives.\n",
+                                       devname);
+                       continue;
+               }
 
                /* 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",
@@ -244,6 +249,9 @@ int Assemble(char *mddev, int mdfd,
                            devname);
                    continue;
                }
+               if (verbose)
+                       fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n",
+                               devname, mddev, super.this_disk.raid_disk);
                devices[devcnt].devname = devname;
                devices[devcnt].major = MAJOR(stb.st_rdev);
                devices[devcnt].minor = MINOR(stb.st_rdev);
@@ -277,8 +285,9 @@ int Assemble(char *mddev, int mdfd,
        sparecnt=0;
        for (i=0; i< MD_SB_DISKS;i++) {
                int j = best[i];
+               int event_margin = !force;
                if (j < 0) continue;
-               if (devices[j].events+1 >=
+               if (devices[j].events+event_margin >=
                    devices[most_recent].events) {
                        devices[j].uptodate = 1;
                        if (i < first_super.raid_disks)
@@ -293,6 +302,7 @@ int Assemble(char *mddev, int mdfd,
                 * and add it.
                 */
                int fd;
+               chosen_drive = -1;
                for (i=0; i<first_super.raid_disks; i++) {
                        int j = best[i];
                        if (j>=0 &&
@@ -344,6 +354,7 @@ int Assemble(char *mddev, int mdfd,
         * If there are differences and --force is given, then update this chosen
         * superblock.
         */
+       chosen_drive = -1;
        for (i=0; chosen_drive < 0 && i<MD_SB_DISKS; i++) {
                int j = best[i];
                int fd;
@@ -368,6 +379,7 @@ int Assemble(char *mddev, int mdfd,
 
        for (i=0; i<MD_SB_DISKS; i++) {
                int j = best[i];
+               int active_sync = (1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC);
                if (j<0)
                        continue;
                if (!devices[j].uptodate)
@@ -379,12 +391,12 @@ int Assemble(char *mddev, int mdfd,
                        super.disks[j].minor = devices[j].minor;
                }
                if (devices[j].uptodate &&
-                   (super.disks[i].state & (1 << MD_DISK_FAULTY))) {
+                   (super.disks[i].state != active_sync)) {
                        if (force) {
                                fprintf(stderr, Name ": "
-                                       "clearing FAULT flag for device %d in %s for %s\n",
+                                       "clearing FAULTY flag for device %d in %s for %s\n",
                                        j, mddev, devices[j].devname);
-                               super.disks[i].state &= ~(1<<MD_DISK_FAULTY);
+                               super.disks[i].state = active_sync;
                                change |= 2;
                        } else {
                                fprintf(stderr, Name ": "
@@ -460,7 +472,9 @@ int Assemble(char *mddev, int mdfd,
                
                if (runstop == 1 ||
                    (runstop == 0 && 
-                    enough(first_super.level, first_super.raid_disks, okcnt))) {
+                    ( first_super.raid_disks == okcnt
+                      || start_partial_ok && 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 drive%s",
                                        mddev, okcnt, okcnt==1?"":"s");
@@ -478,7 +492,7 @@ int Assemble(char *mddev, int mdfd,
                                mddev, okcnt, okcnt==1?"":"s");
                        return 0;
                }
-               fprintf(stderr, Name ": %s assembled from %d drive%s - not enough to start it.\n",
+               fprintf(stderr, Name ": %s assembled from %d drive%s - not enough to start it (use --run to insist).\n",
                        mddev, okcnt, okcnt==1?"":"s");
                return 1;
        } else {
@@ -486,7 +500,7 @@ int Assemble(char *mddev, int mdfd,
                 * been updated to point to the current locations of devices.
                 * so we can just start the array
                 */
-               int dev;
+               unsigned long dev;
                dev = MKDEV(devices[chosen_drive].major,
                            devices[chosen_drive].minor);
                if (ioctl(mdfd, START_ARRAY, dev)) {
diff --git a/Build.c b/Build.c
index 40b631f..51f2df0 100644 (file)
--- a/Build.c
+++ b/Build.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -35,7 +35,7 @@
 
 int Build(char *mddev, int mdfd, int chunk, int level,
          int raiddisks,
-         int subdevs, char *subdev[])
+         mddev_dev_t devlist)
 {
        /* Build a linear or raid0 arrays without superblocks
         * We cannot really do any checks, we just do it.
@@ -53,30 +53,34 @@ int Build(char *mddev, int mdfd, int chunk, int level,
        int i;
        int vers;
        struct stat stb;
-       if (raiddisks != subdevs) {
-               fprintf(stderr, Name ": requested %d devices in array but listed %d\n",
-                       raiddisks, subdevs);
-               return 1;
-       }
+       int subdevs = 0;
+       mddev_dev_t dv;
 
        /* scan all devices, make sure they really are block devices */
-       for (i=0; i<subdevs; i++) {
-               if (stat(subdev[i], &stb)) {
+       for (dv = devlist; dv; dv=dv->next) {
+               if (stat(dv->devname, &stb)) {
                        fprintf(stderr, Name ": Cannot find %s: %s\n",
-                               subdev[i], strerror(errno));
+                               dv->devname, strerror(errno));
                        return 1;
                }
                if ((stb.st_mode & S_IFMT) != S_IFBLK) {
                        fprintf(stderr, Name ": %s is not a block device.\n",
-                               subdev[i]);
+                               dv->devname);
                        return 1;
                }
+               subdevs++;
+       }
+
+       if (raiddisks != subdevs) {
+               fprintf(stderr, Name ": requested %d devices in array but listed %d\n",
+                       raiddisks, subdevs);
+               return 1;
        }
 
        vers = md_get_version(mdfd);
        
        /* looks Ok, go for it */
-       if (vers >= 90000) {
+       if (vers >= 9000) {
                mdu_array_info_t array;
                array.level = level;
                array.size = 0;
@@ -85,12 +89,14 @@ int Build(char *mddev, int mdfd, int chunk, int level,
                array.md_minor = 0;
                if (fstat(mdfd, &stb)==0)
                        array.md_minor = MINOR(stb.st_rdev);
-               array.not_persistent = 0;
+               array.not_persistent = 1;
                array.state = 0; /* not clean, but no errors */
                array.active_disks = raiddisks;
                array.working_disks = raiddisks;
                array.spare_disks = 0;
                array.failed_disks = 0;
+               if (chunk == 0)  
+                       chunk = 64;
                array.chunk_size = chunk*1024;
                if (ioctl(mdfd, SET_ARRAY_INFO, &array)) {
                        fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n",
@@ -99,18 +105,18 @@ int Build(char *mddev, int mdfd, int chunk, int level,
                }
        }
        /* now add the devices */
-       for (i=0; i<subdevs; i++) {
-               if (stat(subdev[i], &stb)) {
+       for ((i=0), (dv = devlist) ; dv ; i++, dv=dv->next) {
+               if (stat(dv->devname, &stb)) {
                        fprintf(stderr, Name ": Wierd: %s has disappeared.\n",
-                               subdev[i]);
+                               dv->devname);
                        goto abort;
                }
-               if ((stb.st_rdev & S_IFMT)!= S_IFBLK) {
+               if ((stb.st_mode & S_IFMT)!= S_IFBLK) {
                        fprintf(stderr, Name ": Wierd: %s is no longer a block device.\n",
-                               subdev[i]);
+                               dv->devname);
                        goto abort;
                }
-               if (vers> 90000) {
+               if (vers>= 9000) {
                        mdu_disk_info_t disk;
                        disk.number = i;
                        disk.raid_disk = i;
@@ -119,27 +125,27 @@ int Build(char *mddev, int mdfd, int chunk, int level,
                        disk.minor = MINOR(stb.st_rdev);
                        if (ioctl(mdfd, ADD_NEW_DISK, &disk)) {
                                fprintf(stderr, Name ": ADD_NEW_DISK failed for %s: %s\n",
-                                       subdev[i], strerror(errno));
+                                       dv->devname, strerror(errno));
                                goto abort;
                        }
                } else {
                        if (ioctl(mdfd, REGISTER_DEV, &stb.st_rdev)) {
                                fprintf(stderr, Name ": REGISTER_DEV failed for %s.\n",
-                                       subdev[i], strerror(errno));
+                                       dv->devname, strerror(errno));
                                goto abort;
                        }
                }
        }
        /* now to start it */
-       if (vers > 90000) {
+       if (vers >= 9000) {
                mdu_param_t param; /* not used by syscall */
-               if (ioctl(mdfd, RUN_ARRAY, param)) {
+               if (ioctl(mdfd, RUN_ARRAY, &param)) {
                        fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
                                strerror(errno));
                        goto abort;
                }
        } else {
-               int arg;
+               unsigned long arg;
                arg=0;
                while (chunk > 4096) {
                        arg++;
@@ -159,7 +165,7 @@ int Build(char *mddev, int mdfd, int chunk, int level,
        return 0;
 
  abort:
-       if (vers > 900000)
+       if (vers >= 9000)
            ioctl(mdfd, STOP_ARRAY, 0);
        else
            ioctl(mdfd, STOP_MD, 0);
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..b27e2d5
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,148 @@
+
+Changes Prior to 0.6 release
+
+       -   Remove the limit on the number of device names that can be
+       given on the command line.
+    -   Fix bug in --assemble --force where it would only update a 
+       single superblock.
+    -   Fix bogus printing of big numbers not being block devices
+       when given names of devices that don't exist.
+    -   When --assemble --force, consider superblocks with an event
+       count that is 1 behind as out-of-date.  Normally they are
+       considered up-to-date (as the kernel assumes this too).
+    -   When marking drives as not-failed in the superblock, 
+       we also mark them as ACTIVE and SYNC.
+    -   Don't start arrays for which not all drives are available unless:
+        --scan   which implies that all drives were found automatically
+        --run    which means the user knows what they want
+        --force  which means that we are fixing something broken
+    -   Make sure all device numbers passed as 3rd arg of ioctl
+       are passed as unsigned lock, so that it works on SPARC
+    -   If HOT_ADD_DISK failes for -a, then only try ADD_NEW_DISK
+       if we cannot read from the array, i.e. if the array is
+       not started yet.
+    -   man page update
+    -   Taught Examine to handle --scan. It examines all devices listed
+       on DEVICE lines in the config file.  
+    -   Added --brief (-b) flag for Examine and Detail to print out
+       and mdctl.conf compatible description with uuid=, level=,
+       disks= and  - for Examine - devices=
+       --examine --brief collects all devices the make the one array and 
+       list them as one entry.
+    -   Added level= and disks= options to ARRAY lines in config files
+       so --brief output could be used as-is.
+    -   Make parity style ({left,right}-{,a}symmetric) consistantly use -,
+       never _.
+    -   Add "Array Size" to --detail output
+    -   Change "Size" to "Device Size" and exclude from Detail of arrays
+       that do not have a consistent device size.
+    -   Add Human readable MiB or GiB value on size lines of Detail and Examine
+    -   --assemble --scan  doesn't complain about active drives
+    -   require number of spares given in -x to be listed.
+    -   Made --build actually work.
+Changes Prior to 0.5 release
+
+  --assemble:
+     spare drives are handled properly.
+
+     --force can be used to recover from 2-drive failures on RAID5
+     If you belive that /dev/hda1 /dev/hdb1 /dev/hdc1 /dev/hdd1 should
+     make a raid5 array, but it has experienced multiple failures and
+     wont start, then
+
+       mdctl --assemble --force /dev/md0 /dev/hd[abcd]1
+
+     Should update the superblock on the newest failed drive and
+     restart the array in degraded mode.  You should then remove the
+     remaining failed drive and re-add it (if you are happy that it
+     might work).
+
+     Ofcourse whenever you have a 2-drive failure, you have a risk
+     of corruption in data that hasn't be changed for a long time.  So
+     this doesn't give you your array back all nice and happy, but it
+     does allow you to recover data that might not be corrupt.
+
+     More flexibility in identifying a RAID array in the mdctl.conf
+     e.g.
+         array /dev/md4  super-minor=4
+
+      assembles /dev/md4 from all devices found that have a raid
+      superblock that says the minor number of the array is 4.
+      If the blocks with the right minor number do not all have the
+      same UUID, an error is flags and no assembly happens.
+        array /dev/md3  devices=/dev/hd[abc]2
+
+      Assembles /dev/md3 drom /dev/hda2 /dev/hdb2 and/dev/hdc2.  All
+      devices must exist and have raid superblock with the same uuid.
+
+      If two identity specifiers are used, only devices that match all
+      of them are considered, so
+
+        array /dev/md2 devices=/dev/hd?2 super-minor=2
+
+      will assemble /dev/md2 using all /dev/hd?2 devices which have a 
+      raid superblock with minor number 2.
+  --create:
+      When listing devices for --create, the word "missing" can be
+      used to indicate that the respective slot does not have a
+      working drive currently.  This is similar to the "failed-disk"
+      directive in mkraid/raidtab.
+      e.g.
+         mdctl --create --level=5 -raid-disks=4 --spare-disks=2
+                 /dev/md0 /dev/sda /dev/sdb missing /dev/sdc /dev/sdd  /dev/sde
+
+      will create a raid5 array with the third slot empty, and two
+      spares.
+
+      By default, raid5 arrays are created with the last slot empty
+      and drive listed for the last slot added as a spare.  If a
+      "missing" slot is given, or if --force is given, then --create
+      does exactly what you ask and doesn't try to be clever.
+   
+
+   --follow / --monitor:
+
+      This is a new mode.  I couldn't stop my self from picking a name
+      starting with F (as current modes start A,B,C,D,E) but I
+      relented and provided an alternate name that is somewhat more
+      meaningful. 
+      In this mode, mdctl does not exit, but runs continuously and
+      periodically polls all the md devices to see if they have had
+      any interested state change.
+      The changes that it currently notices are:
+           Fail      -  an active disc fails
+           FailSpare -  a spare, that was presumably being build, fails
+           ActiveSpare - a spare becomes active, presumably after a rebuild.
+
+      Options:
+         --mail mailaddress  - send Email on any Fail* event
+         --program program   - run the program on any event.  
+                  Args are: eventname mddevice subdevice(if-known)
+         --delay  seconds    - change from the default 60second pause
+                              between polls.
+
+      I plan to add functionality to this mode to allow sharing of
+      spare drives. If an array is marks "spare-group=fred", and it
+      has a failed drive and no spares, and if some other array is
+      also "spare-group=fred" and it has no failed drives, but does
+      have a spare drive that is big enough, the spare will be moved
+      to the first array.
+
+  I also have the idea of adding a --grow mode which will re-organise
+  the data on an N disk raid0/4/5 array to be on an N+M disk array.
+  I have no concrete plans for this though.
+
+  I got rid of the "v" in the archive file name, and include the
+  version number in the directory created by the archive.
+
+  There is now a man page and mdctl.spec (for rpm) thanks to
+  Danilo Godec <danci@agenda.si>.
+  
+  Ofcourse, the man page is now out of date and despite being based on
+  the --help output, is not wholy correct.  After I get --follow
+  working properly, I plan to revise the various documentation and/or
+  the code to make sure the two match.
+
index c7d3597..55a9eca 100644 (file)
--- a/Create.c
+++ b/Create.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -33,7 +33,7 @@
 
 int Create(char *mddev, int mdfd,
           int chunk, int level, int layout, int size, int raiddisks, int sparedisks,
-          int subdevs, char *subdev[],
+          int subdevs, mddev_dev_t devlist,
           int runstop, int verbose, int force)
 {
        /*
@@ -53,8 +53,10 @@ int Create(char *mddev, int mdfd,
         * RUN_ARRAY
         */
        int minsize, maxsize;
-       int maxdisc= -1, mindisc = -1;
+       char *mindisc = NULL;
+       char *maxdisc = NULL;
        int i;
+       mddev_dev_t dv;
        int fail=0, warn=0;
        struct stat stb;
        int first_missing = MD_SB_DISKS*2;
@@ -93,7 +95,7 @@ int Create(char *mddev, int mdfd,
                fprintf(stderr, Name ": You have listed more disks (%d) than are in the array(%d)!\n", subdevs, raiddisks+sparedisks);
                return 1;
        }
-       if (subdevs < raiddisks) {
+       if (subdevs < raiddisks+sparedisks) {
                fprintf(stderr, Name ": You haven't given enough devices (real or missing) to create this array\n");
                return 1;
        }
@@ -121,11 +123,11 @@ 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];
+       for (dv=devlist; dv; dv=dv->next) {
+               char *dname = dv->devname;
                int dsize, freesize;
                int fd;
-               if (strcasecmp(subdev[i], "missing")==0) {
+               if (strcasecmp(dname, "missing")==0) {
                        if (first_missing > i)
                                first_missing = i;
                        missing_disks ++;
@@ -165,12 +167,12 @@ int Create(char *mddev, int mdfd,
                        close(fd);
                        continue;
                }
-               if (maxdisc< 0 || (maxdisc>=0 && freesize > maxsize)) {
-                       maxdisc = i;
+               if (maxdisc == NULL || (maxdisc && freesize > maxsize)) {
+                       maxdisc = dname;
                        maxsize = freesize;
                }
-               if (mindisc < 0 || (mindisc >=0 && freesize < minsize)) {
-                       mindisc = i;
+               if (mindisc ==NULL || (mindisc && freesize < minsize)) {
+                       mindisc = dname;
                        minsize = freesize;
                }
                warn |= check_ext2(fd, dname);
@@ -183,7 +185,7 @@ int Create(char *mddev, int mdfd,
                return 1;
        }
        if (size == 0) {
-               if (mindisc == -1) {
+               if (mindisc == NULL) {
                        fprintf(stderr, Name ": no size and no drives given - aborting create.\n");
                        return 1;
                }
@@ -193,7 +195,7 @@ int Create(char *mddev, int mdfd,
        }
        if ((maxsize-size)*100 > maxsize) {
                fprintf(stderr, Name ": largest drive (%s) exceed size (%dK) by more than 1%\n",
-                       subdev[maxdisc], size);
+                       maxdisc, size);
                warn = 1;
        }
 
@@ -267,7 +269,7 @@ int Create(char *mddev, int mdfd,
                return 1;
        }
        
-       for (i=0; i<subdevs; i++) {
+       for (i=0, dv = devlist ; dv ; dv=dv->next, i++) {
                int fd;
                struct stat stb;
                mdu_disk_info_t disk;
@@ -280,15 +282,15 @@ int Create(char *mddev, int mdfd,
                        disk.state = 6; /* active and in sync */
                else
                        disk.state = 0;
-               if (strcasecmp(subdev[i], "missing")==0) {
+               if (strcasecmp(dv->devname, "missing")==0) {
                        disk.major = 0;
                        disk.minor = 0;
                        disk.state = 1; /* faulty */
                } else {
-                       fd = open(subdev[i], O_RDONLY, 0);
+                       fd = open(dv->devname, O_RDONLY, 0);
                        if (fd < 0) {
                                fprintf(stderr, Name ": failed to open %s after earlier success - aborting\n",
-                                       subdev[i]);
+                                       dv->devname);
                                return 1;
                        }
                        fstat(fd, &stb);
@@ -298,7 +300,7 @@ int Create(char *mddev, int mdfd,
                }
                if (ioctl(mdfd, ADD_NEW_DISK, &disk)) {
                        fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\n",
-                               subdev[i], strerror(errno));
+                               dv->devname, strerror(errno));
                        return 1;
                }
        }
index 026c3b3..7c2860d 100644 (file)
--- a/Detail.c
+++ b/Detail.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -31,7 +31,7 @@
 #include       "md_p.h"
 #include       "md_u.h"
 
-int Detail(char *dev)
+int Detail(char *dev, int brief)
 {
        /*
         * Print out details for an md array by using
@@ -77,48 +77,59 @@ int Detail(char *dev)
                return 1;
        }
        /* Ok, we have some info to print... */
-       printf("%s:\n", dev);
-       printf("        Version : %02d.%02d.%02d\n",
-              array.major_version, array.minor_version, array.patch_version);
-       atime = array.ctime;
-       printf("  Creation Time : %.24s\n", ctime(&atime));
        c = map_num(pers, array.level);
-       printf("     Raid Level : %s\n", c?c:"-unknown-");
-       printf("           Size : %d\n", array.size);
-       printf("     Raid Disks : %d\n", array.raid_disks);
-       printf("    Total Disks : %d\n", array.nr_disks);
-       printf("Preferred Minor : %d\n", array.md_minor);
-       printf("    Persistance : Superblock is %spersistant\n",
-              array.not_persistent?"not ":"");
-       printf("\n");
-       atime = array.utime;
-       printf("    Update Time : %.24s\n", ctime(&atime));
-       printf("          State : %s, %serrors\n",
-              (array.state&(1<<MD_SB_CLEAN))?"clean":"dirty",
-              (array.state&(1<<MD_SB_ERRORS))?"":"no-");
-       printf("  Active Drives : %d\n", array.active_disks);
-       printf(" Working Drives : %d\n", array.working_disks);
-       printf("  Failed Drives : %d\n", array.failed_disks);
-       printf("   Spare Drives : %d\n", array.spare_disks);
-       printf("\n");
-       if (array.level == 5) {
-               c = map_num(r5layout, array.layout);
-               printf("         Layout : %s\n", c?c:"-unknown-");
-       }
-       switch (array.level) {
-       case 0:
-       case 4:
-       case 5:
-               printf("     Chunk Size : %dK\n", array.chunk_size/1024);
-               break;
-       case -1:
-               printf("       Rounding : %dK\n", array.chunk_size/1024);
-               break;
-       default: break;
-       }
+       if (brief) 
+               printf("ARRAY %s level=%s disks=%d", dev, c?c:"-unknown-",array.raid_disks );
+       else {
+               int array_size;
+               if (ioctl(fd, BLKGETSIZE, &array_size))
+                       array_size = 0;
+               else array_size>>= 1;
+               printf("%s:\n", dev);
+               printf("        Version : %02d.%02d.%02d\n",
+                      array.major_version, array.minor_version, array.patch_version);
+               atime = array.ctime;
+               printf("  Creation Time : %.24s\n", ctime(&atime));
+               printf("     Raid Level : %s\n", c?c:"-unknown-");
+               if (array_size)
+               printf("     Array Size : %d%s\n", array_size, human_size(array_size));
+               if (array.level >= 1)
+                       printf("    Device Size : %d%s\n", array.size, human_size(array.size));
+               printf("     Raid Disks : %d\n", array.raid_disks);
+               printf("    Total Disks : %d\n", array.nr_disks);
+               printf("Preferred Minor : %d\n", array.md_minor);
+               printf("    Persistance : Superblock is %spersistant\n",
+                      array.not_persistent?"not ":"");
+               printf("\n");
+               atime = array.utime;
+               printf("    Update Time : %.24s\n", ctime(&atime));
+               printf("          State : %s, %serrors\n",
+                      (array.state&(1<<MD_SB_CLEAN))?"clean":"dirty",
+                      (array.state&(1<<MD_SB_ERRORS))?"":"no-");
+               printf("  Active Drives : %d\n", array.active_disks);
+               printf(" Working Drives : %d\n", array.working_disks);
+               printf("  Failed Drives : %d\n", array.failed_disks);
+               printf("   Spare Drives : %d\n", array.spare_disks);
+               printf("\n");
+               if (array.level == 5) {
+                       c = map_num(r5layout, array.layout);
+                       printf("         Layout : %s\n", c?c:"-unknown-");
+               }
+               switch (array.level) {
+               case 0:
+               case 4:
+               case 5:
+                       printf("     Chunk Size : %dK\n", array.chunk_size/1024);
+                       break;
+               case -1:
+                       printf("       Rounding : %dK\n", array.chunk_size/1024);
+                       break;
+               default: break;
+               }
        
-       printf("\n");
-       printf("    Number   Major   Minor   RaidDisk   State\n");
+               printf("\n");
+               printf("    Number   Major   Minor   RaidDisk   State\n");
+       }
        for (d= 0; d<array.raid_disks+array.spare_disks; d++) {
                mdu_disk_info_t disk;
                char *dv;
@@ -128,34 +139,40 @@ int Detail(char *dev)
                                d, strerror(errno));
                        continue;
                }
-               printf("   %5d   %5d    %5d    %5d     ", 
-                      disk.number, disk.major, disk.minor, disk.raid_disk);
-               if (disk.state & (1<<MD_DISK_FAULTY)) printf(" faulty");
-               if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active");
-               if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync");
-               if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed");
+               if (!brief) {
+                       printf("   %5d   %5d    %5d    %5d     ", 
+                              disk.number, disk.major, disk.minor, disk.raid_disk);
+                       if (disk.state & (1<<MD_DISK_FAULTY)) printf(" faulty");
+                       if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active");
+                       if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync");
+                       if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed");
+               }
                if ((dv=map_dev(disk.major, disk.minor))) {
-                   printf("   %s", dv);
-                   if (!have_super) {
-                       /* try to read the superblock from this device
-                        * to get more info
-                        */
-                       int fd = open(dv, O_RDONLY);
-                       if (fd >=0 &&
-                           load_super(fd, &super) ==0 &&
-                           super.ctime == array.ctime &&
-                           super.level == array.level)
-                           have_super = 1;
-                   }
+                       if (!brief) printf("   %s", dv);
+                       if (!have_super) {
+                               /* try to read the superblock from this device
+                                * to get more info
+                                */
+                               int fd = open(dv, O_RDONLY);
+                               if (fd >=0 &&
+                                   load_super(fd, &super) ==0 &&
+                                   super.ctime == array.ctime &&
+                                   super.level == array.level)
+                                       have_super = 1;
+                       }
                }
-               printf("\n");
+               if (!brief) printf("\n");
        }
        if (have_super) {
+               if (brief) printf(" UUID=");
+               else printf("           UUID : ");
                if (super.minor_version >= 90)
-               printf("           UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1,
-                      super.set_uuid2, super.set_uuid3);
-       else
-               printf("           UUID : %08x\n", super.set_uuid0);
+                       printf("%08x:%08x:%08x:%08x", super.set_uuid0, super.set_uuid1,
+                              super.set_uuid2, super.set_uuid3);
+               else
+                       printf("%08x", super.set_uuid0);
+               if (!brief) printf("\n");
        }
+       if (brief) printf("\n");
        return 0;
 }
index bb4290d..c8fbfbb 100644 (file)
--- a/Examine.c
+++ b/Examine.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
  */
 
 #include       "mdctl.h"
+#include       "dlink.h"
 
 #if ! defined(__BIG_ENDIAN) && ! defined(__LITTLE_ENDIAN)
 #error no endian defined
 #endif
 #include       "md_u.h"
 #include       "md_p.h"
-int Examine(char *dev)
+int Examine(mddev_dev_t devlist, int brief, char *conffile)
 {
 
        /* Read the raid superblock from a device and
@@ -49,120 +50,186 @@ int Examine(char *dev)
         *
         *   utime, state etc
         *
+        * If (brief) gather devices for same array and just print a mdctl.conf line including devices=
+        * if devlist==NULL, use conf_get_devs(
         */
-       int fd = open(dev, O_RDONLY);
+       int fd
        time_t atime;
        mdp_super_t super;
        int d;
        char *c;
-       int rv;
+       int rv = 0;
+       int err;
+       int scan= 0;
 
-       if (fd < 0) {
-               fprintf(stderr,Name ": cannot open %s: %s\n",
-                       dev, strerror(errno));
+       struct array {
+               mdp_super_t super;
+               void *devs;
+               struct array *next;
+       } *arrays = NULL;
+
+       if (devlist == NULL) {
+               devlist = conf_get_devs(conffile);
+               scan=1;
+       }
+       if (devlist == NULL) {
+               fprintf(stderr, Name ": No devices listed in %s\n", conffile);
                return 1;
        }
 
-       rv = load_super(fd, &super);
-       close(fd);
-       switch(rv) {
-       case 1:
-               fprintf(stderr, Name ": cannot find device size for %s: %s\n",
-                       dev, strerror(errno));
-               return 1;
-       case 2:
+       for (; devlist ; devlist=devlist->next) {
+               fd = open(devlist->devname, O_RDONLY);
+               if (fd < 0) {
+                       if (!scan)
+                               fprintf(stderr,Name ": cannot open %s: %s\n",
+                                       devlist->devname, strerror(errno));
+                       err = 1;
+               }
+               else {
+                       err = load_super(fd, &super);
+                       close(fd);
+               }
+               if (err && (brief||scan))
+                       continue;
+               if (err) rv =1;
+               switch(err) {
+               case 1:
+                       fprintf(stderr, Name ": cannot find device size for %s: %s\n",
+                               devlist->devname, strerror(errno));
+                       continue;
+               case 2:
 /*             fprintf(stderr, Name ": %s is too small for md: size is %ld sectors\n",
-                       dev, size);
+               devlist->devname, size);
 */
-               fprintf(stderr, Name ": %s is too small for md\n",
-                       dev);
-               return 1;
-       case 3:
-               fprintf(stderr, Name ": Cannot seek to superblock on %s: %s\n",
-                       dev, strerror(errno));
-               return 1;
-       case 4:
-               fprintf(stderr, Name ": Cannot read superblock on %s\n",
-                       dev);
-               return 1;
-       case 5:
-               fprintf(stderr, Name ": No super block found on %s (Expected magic %08x, got %08x)\n",
-                       dev, MD_SB_MAGIC, super.md_magic);
-               return 1;
-       case 6:
-               fprintf(stderr, Name ": Cannot interpret superblock on %s - version is %d\n",
-                       dev, super.major_version);
-               return 1;
-       }
+                       fprintf(stderr, Name ": %s is too small for md\n",
+                               devlist->devname);
+                       continue;
+               case 3:
+                       fprintf(stderr, Name ": Cannot seek to superblock on %s: %s\n",
+                               devlist->devname, strerror(errno));
+                       continue;
+               case 4:
+                       fprintf(stderr, Name ": Cannot read superblock on %s\n",
+                               devlist->devname);
+                       continue;
+               case 5:
+                       fprintf(stderr, Name ": No super block found on %s (Expected magic %08x, got %08x)\n",
+                               devlist->devname, MD_SB_MAGIC, super.md_magic);
+                       continue;
+               case 6:
+                       fprintf(stderr, Name ": Cannot interpret superblock on %s - version is %d\n",
+                               devlist->devname, super.major_version);
+                       continue;
+               }
     
-       /* Ok, its good enough to try, though the checksum could be wrong */
-       printf("%s:\n",dev);
-       printf("          Magic : %08x\n", super.md_magic);
-       printf("        Version : %02d.%02d.%02d\n", super.major_version, super.minor_version,
-              super.patch_version);
-       if (super.minor_version >= 90)
-               printf("           UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1,
-                      super.set_uuid2, super.set_uuid3);
-       else
-               printf("           UUID : %08x\n", super.set_uuid0);
+               /* Ok, its good enough to try, though the checksum could be wrong */
+               if (brief) {
+                       struct array *ap;
+                       char *d;
+                       for (ap=arrays; ap; ap=ap->next) {
+                               if (compare_super(&ap->super, &super)==0)
+                                       break;
+                       }
+                       if (!ap) {
+                               ap = malloc(sizeof(*ap));
+                               ap->super = super;
+                               ap->devs = dl_head();
+                               ap->next = arrays;
+                               arrays = ap;
+                       }
+                       d = dl_strdup(devlist->devname);
+                       dl_add(ap->devs, d);
+               } else {
+                       printf("%s:\n",devlist->devname);
+                       printf("          Magic : %08x\n", super.md_magic);
+                       printf("        Version : %02d.%02d.%02d\n", super.major_version, super.minor_version,
+                              super.patch_version);
+                       if (super.minor_version >= 90)
+                               printf("           UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1,
+                                      super.set_uuid2, super.set_uuid3);
+                       else
+                               printf("           UUID : %08x\n", super.set_uuid0);
 
-       atime = super.ctime;
-       printf("  Creation Time : %.24s\n", ctime(&atime));
-       c=map_num(pers, super.level);
-       printf("     Raid Level : %s\n", c?c:"-unknown-");
-       printf("           Size : %d\n", super.size);
-       printf("     Raid Disks : %d\n", super.raid_disks);
-       printf("    Total Disks : %d\n", super.nr_disks);
-       printf("Preferred Minor : %d\n", super.md_minor);
-       printf("\n");
-       atime = super.utime;
-       printf("    Update Time : %.24s\n", ctime(&atime));
-       printf("          State : %s, %serrors\n",
-              (super.state&(1<<MD_SB_CLEAN))?"clean":"dirty",
-              (super.state&(1<<MD_SB_ERRORS))?"":"no-");
-       printf("  Active Drives : %d\n", super.active_disks);
-       printf(" Working Drives : %d\n", super.working_disks);
-       printf("  Failed Drives : %d\n", super.failed_disks);
-       printf("   Spare Drives : %d\n", super.spare_disks);
-       if (calc_sb_csum(&super) == super.sb_csum)
-           printf("       Checksum : %x - correct\n", super.sb_csum);
-       else
-           printf("       Checksum : %x - expected %x\n", super.sb_csum, calc_sb_csum(&super));
-       printf("         Events : %d.%d\n", super.events_hi, super.events_lo);
-       printf("\n");
-       if (super.level == 5) {
-               c = map_num(r5layout, super.layout);
-               printf("         Layout : %s\n", c?c:"-unknown-");
-       }
-       switch(super.level) {
-       case 0:
-       case 4:
-       case 5:
-               printf("     Chunk Size : %dK\n", super.chunk_size/1024);
-               break;
-       case -1:
-               printf("       Rounding : %dK\n", super.chunk_size/1024);
-               break;
-       default: break;         
+                       atime = super.ctime;
+                       printf("  Creation Time : %.24s\n", ctime(&atime));
+                       c=map_num(pers, super.level);
+                       printf("     Raid Level : %s\n", c?c:"-unknown-");
+                       printf("    Device Size : %d%s\n", super.size, human_size(super.size));
+                       printf("     Raid Disks : %d\n", super.raid_disks);
+                       printf("    Total Disks : %d\n", super.nr_disks);
+                       printf("Preferred Minor : %d\n", super.md_minor);
+                       printf("\n");
+                       atime = super.utime;
+                       printf("    Update Time : %.24s\n", ctime(&atime));
+                       printf("          State : %s, %serrors\n",
+                              (super.state&(1<<MD_SB_CLEAN))?"clean":"dirty",
+                              (super.state&(1<<MD_SB_ERRORS))?"":"no-");
+                       printf("  Active Drives : %d\n", super.active_disks);
+                       printf(" Working Drives : %d\n", super.working_disks);
+                       printf("  Failed Drives : %d\n", super.failed_disks);
+                       printf("   Spare Drives : %d\n", super.spare_disks);
+                       if (calc_sb_csum(&super) == super.sb_csum)
+                               printf("       Checksum : %x - correct\n", super.sb_csum);
+                       else
+                               printf("       Checksum : %x - expected %x\n", super.sb_csum, calc_sb_csum(&super));
+                       printf("         Events : %d.%d\n", super.events_hi, super.events_lo);
+                       printf("\n");
+                       if (super.level == 5) {
+                               c = map_num(r5layout, super.layout);
+                               printf("         Layout : %s\n", c?c:"-unknown-");
+                       }
+                       switch(super.level) {
+                       case 0:
+                       case 4:
+                       case 5:
+                               printf("     Chunk Size : %dK\n", super.chunk_size/1024);
+                               break;
+                       case -1:
+                               printf("       Rounding : %dK\n", super.chunk_size/1024);
+                               break;
+                       default: break;         
+                       }
+                       printf("\n");
+                       printf("      Number   Major   Minor   RaidDisk   State\n");
+                       for (d= -1; d<(signed int)(super.raid_disks+super.spare_disks); d++) {
+                               mdp_disk_t *dp;
+                               char *dv;
+                               char nb[5];
+                               if (d>=0) dp = &super.disks[d];
+                               else dp = &super.this_disk;
+                               sprintf(nb, "%4d", d);
+                               printf("%4s %5d   %5d    %5d    %5d     ", d < 0 ? "this" :  nb,
+                                      dp->number, dp->major, dp->minor, dp->raid_disk);
+                               if (dp->state & (1<<MD_DISK_FAULTY)) printf(" faulty");
+                               if (dp->state & (1<<MD_DISK_ACTIVE)) printf(" active");
+                               if (dp->state & (1<<MD_DISK_SYNC)) printf(" sync");
+                               if (dp->state & (1<<MD_DISK_REMOVED)) printf(" removed");
+                               if ((dv=map_dev(dp->major, dp->minor)))
+                                       printf("   %s", dv);
+                               printf("\n");
+                       }
+               }
        }
-       printf("\n");
-       printf("      Number   Major   Minor   RaidDisk   State\n");
-       for (d= -1; d<(signed int)(super.raid_disks+super.spare_disks); d++) {
-               mdp_disk_t *dp;
-               char *dv;
-               char nb[5];
-               if (d>=0) dp = &super.disks[d];
-               else dp = &super.this_disk;
-               sprintf(nb, "%4d", d);
-               printf("%4s %5d   %5d    %5d    %5d     ", d < 0 ? "this" :  nb,
-                      dp->number, dp->major, dp->minor, dp->raid_disk);
-               if (dp->state & (1<<MD_DISK_FAULTY)) printf(" faulty");
-               if (dp->state & (1<<MD_DISK_ACTIVE)) printf(" active");
-               if (dp->state & (1<<MD_DISK_SYNC)) printf(" sync");
-               if (dp->state & (1<<MD_DISK_REMOVED)) printf(" removed");
-               if ((dv=map_dev(dp->major, dp->minor)))
-                   printf("   %s", dv);
-               printf("\n");
+       if (brief) {
+               struct array *ap;
+               for (ap=arrays; ap; ap=ap->next) {
+                       char sep='=';
+                       char *c=map_num(pers, ap->super.level);
+                       char *d;
+                       printf("ARRAY /dev/md%d level=%s disks=%d UUID=",
+                              ap->super.md_minor, c?c:"-unknown-", ap->super.raid_disks);
+                       if (ap->super.minor_version >= 90)
+                               printf("%08x:%08x:%08x:%08x", ap->super.set_uuid0, ap->super.set_uuid1,
+                                      ap->super.set_uuid2, ap->super.set_uuid3);
+                       else
+                               printf("%08x", ap->super.set_uuid0);
+                       printf("\n   devices");
+                       for (d=dl_next(ap->devs); d!= ap->devs; d=dl_next(d)) {
+                               printf("%c%s", sep, d);
+                               sep=',';
+                       }
+                       printf("\n");
+               }
        }
-       return 0;
+       return rv;
 }
index 5d0b142..5513451 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 #
 # mdctl - manage Linux "md" devices aka RAID arrays.
 #
-# Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+# Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
 #
 #
 #    This program is free software; you can redistribute it and/or modify
 #           Australia
 #
 
+CC = gcc
 CFLAGS = -Wall,error,strict-prototypes -ggdb
 
+INSTALL = /usr/bin/install
+DESTDIR = /sbin
+
 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
+
+all : mdctl mdctl.man
 
 mdctl : $(OBJS)
        $(CC) -o mdctl $^
 
+mdctl.man : mdctl.8
+       nroff -man mdctl.8 > mdctl.man
+
 $(OBJS) : mdctl.h
 
+install : mdctl
+       $(INSTALL) -m 755 mdctl $(DESTDIR)/sbin
+
 clean : 
-       rm -f mdctl $(OBJS) core
+       rm -f mdctl $(OBJS) core mdctl.man
 
 dist : clean
        ./makedist
index d8cc136..cab49bc 100644 (file)
--- a/Manage.c
+++ b/Manage.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -116,8 +116,8 @@ int Manage_runstop(char *devname, int fd, int runstop)
 }
 
 int Manage_subdevs(char *devname, int fd,
-                  int devcnt, char *devnames[], int devmodes[])
- {
+                  mddev_dev_t devlist)
+{
        /* do something to each dev.
         * devmode can be
         *  'a' - add the device
@@ -128,37 +128,49 @@ int Manage_subdevs(char *devname, int fd,
         */
        mdu_array_info_t array;
        mdu_disk_info_t disc;
+       mddev_dev_t dv;
        struct stat stb;
        int i,j;
+       int save_errno;
+       static buf[4096];
 
        if (ioctl(fd, GET_ARRAY_INFO, &array)) {
                fprintf(stderr, Name ": cannot get array info for %s\n",
                        devname);
                return 1;
        }
-       for (i=0 ; i<devcnt; i++) {
-               if (stat(devnames[i], &stb)) {
+       for (dv = devlist ; dv; dv=dv->next) {
+               if (stat(dv->devname, &stb)) {
                        fprintf(stderr, Name ": cannot find %s: %s\n",
-                               devnames[i], strerror(errno));
+                               dv->devname, strerror(errno));
                        return 1;
                }
                if ((stb.st_mode & S_IFMT) != S_IFBLK) {
                        fprintf(stderr, Name ": %s is not a block device.\n",
-                               devnames[i]);
+                               dv->devname);
                        return 1;
                }
-               switch(devmodes[i]){
+               switch(dv->disposition){
                default:
                        fprintf(stderr, Name ": internal error - devmode[%d]=%d\n",
-                               i, devmodes[i]);
+                               i, dv->disposition);
                        return 1;
                case 'a':
                        /* add the device - hot or cold */
-                       if (ioctl(fd, HOT_ADD_DISK, stb.st_rdev)==0) {
+                       if (ioctl(fd, HOT_ADD_DISK, (unsigned long)stb.st_rdev)==0) {
                                fprintf(stderr, Name ": hot added %s\n",
-                                       devnames[i]);
+                                       dv->devname);
                                continue;
                        }
+                       save_errno = errno;
+                       if (read(fd, buf, sizeof(buf)) > 0) {
+                               /* array is active, so don't try to add.
+                                * i.e. something is wrong 
+                                */
+                               fprintf(stderr, Name ": hot add failed for %s: %s\n",
+                                       dv->devname, strerror(save_errno));
+                               return 1;
+                       }
                        /* try ADD_NEW_DISK.
                         * we might be creating, we might be assembling,
                         * it is hard to tell.
@@ -180,32 +192,32 @@ int Manage_subdevs(char *devname, int fd,
                        disc.minor = MINOR(stb.st_rdev);
                        if (ioctl(fd,ADD_NEW_DISK, &disc)) {
                                fprintf(stderr, Name ": add new disk failed for %s: %s\n",
-                                       devnames[i], strerror(errno));
+                                       dv->devname, strerror(errno));
                                return 1;
                        }
-                       fprintf(stderr, Name ": added %s\n", devnames[i]);
+                       fprintf(stderr, Name ": added %s\n", dv->devname);
                        break;
 
                case 'r':
                        /* hot remove */
                        /* FIXME check that it is a current member */
-                       if (ioctl(fd, HOT_REMOVE_DISK, stb.st_rdev)) {
+                       if (ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev)) {
                                fprintf(stderr, Name ": hot remove failed for %s: %s\n",
-                                       devnames[i], strerror(errno));
+                                       dv->devname, strerror(errno));
                                return 1;
                        }
-                       fprintf(stderr, Name ": hot removed %s\n", devnames[i]);
+                       fprintf(stderr, Name ": hot removed %s\n", dv->devname);
                        break;
 
                case 'f': /* set faulty */
                        /* FIXME check current member */
-                       if (ioctl(fd, SET_DISK_FAULTY, stb.st_rdev)) {
+                       if (ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev)) {
                                fprintf(stderr, Name ": set disk faulty failed for %s:  %s\n",
-                                       devnames[i], strerror(errno));
+                                       dv->devname, strerror(errno));
                                return 1;
                        }
                        fprintf(stderr, Name ": set %s faulty in %s\n",
-                               devnames[i], devname);
+                               dv->devname, devname);
                        break;
                }
        }
index 968e4b3..4f4aa0e 100644 (file)
--- a/Monitor.c
+++ b/Monitor.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -34,7 +34,7 @@
 
 static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd);
 
-int Monitor(int num_devs, char *devlist[],
+int Monitor(mddev_dev_t devlist,
            char *mailaddr, char *alert_cmd,
            int period,
            char *config)
@@ -75,10 +75,12 @@ int Monitor(int num_devs, char *devlist[],
        int finished = 0;
        while (! finished) {
                mddev_ident_t mdlist = NULL;
+               mddev_dev_t dv;
                int dnum=0;
-               if (num_devs == 0)
+               if (devlist== NULL)
                        mdlist = conf_get_ident(config, NULL);
-               while (dnum < num_devs || mdlist) {
+               dv = devlist;
+               while (dv || mdlist) {
                        mddev_ident_t mdident;
                        struct state *st;
                        mdu_array_info_t array;
@@ -87,9 +89,10 @@ int Monitor(int num_devs, char *devlist[],
                        char *event = NULL;
                        int i;
                        char *event_disc = NULL;
-                       if (num_devs) {
-                               dev = devlist[dnum++];
+                       if (dv) {
+                               dev = dv->devname;
                                mdident = conf_get_ident(config, dev);
+                               dv = dv->next;
                        } else {
                                mdident = mdlist;
                                dev = mdident->devname;
@@ -171,6 +174,11 @@ int Monitor(int num_devs, char *devlist[],
 
 static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd)
 {
+       if (!cmd && !mailaddr) {
+               time_t now = time(0);
+              
+               printf("%0.15s: %s on %s %s\n", ctime(&now)+4, event, dev, disc?disc:"unknown device");
+       }
        if (cmd) {
                int pid = fork();
                switch(pid) {
index 49830cd..5128cbf 100644 (file)
--- a/ReadMe.c
+++ b/ReadMe.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -29,7 +29,7 @@
 
 #include "mdctl.h"
 
-char Version[] = Name " - v0.5 - 23 August 2001\n";
+char Version[] = Name " - v0.6 -  7 March 2002\n";
 /*
  * File: ReadMe.c
  *
@@ -78,7 +78,7 @@ char Version[] = Name " - v0.5 - 23 August 2001\n";
  *     command, subsequent Manage commands can finish the job.
  */
 
-char short_options[]="-ABCDEFhVvc:l:p:m:n:x:u:c:d:z:sarfRSow";
+char short_options[]="-ABCDEFhVvbc:l:p:m:n:x:u:c:d:z:sarfRSow";
 struct option long_options[] = {
     {"manage",    0, 0, '@'},
     {"assemble",  0, 0, 'A'},
@@ -122,6 +122,9 @@ struct option long_options[] = {
     {"readonly",  0, 0, 'o'},
     {"readwrite", 0, 0, 'w'},
 
+    /* For Detail/Examine */
+    {"brief",    0, 0, 'b'},
+
     /* For Follow/monitor */
     {"mail",      1, 0, 'm'},
     {"program",   1, 0, 'p'},
@@ -174,7 +177,7 @@ char Help[] =
 "  --paritiy=    -p   : raid5 parity algorith: {left,right}-{,a}symmetric\n"
 "  --layout=          : same as --parity\n"
 "  --raid-disks= -n   : number of active devices in array\n"
-"  --spare-disks= -x  : number of spares (eXtras) to allow space for\n"
+"  --spare-disks= -x  : number of spares (eXtras) devices in initial array\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"
@@ -188,6 +191,9 @@ char Help[] =
 "  --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 detail or examine:\n"
+"  --brief       -b   : Just print device name and UUID\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"
@@ -306,10 +312,10 @@ char Help_assemble[] =
 /* name/number mappings */
 
 mapping_t r5layout[] = {
-       { "left_asymmetric", 0},
-       { "right_asymmetric", 1},
-       { "left_symmetric", 2},
-       { "right_symmetric", 3},
+       { "left-asymmetric", 0},
+       { "right-asymmetric", 1},
+       { "left-symmetric", 2},
+       { "right-symmetric", 3},
 
        { "default", 2},
        { "la", 0},
diff --git a/TODO b/TODO
index 9bcf70f..a23e0fe 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,21 +1,47 @@
 
+
+?? Allow -S /dev/md? - current complains subsequent not a/d/r
+
+* write proc.c to parse /proc/mdstat file, and maybe /proc/partitions too.
+   Build list of arrays:  name, rebuild-percent
+
+* --detail --scan to read mdctl.conf, and then iterate over these,
+    but assume --brief.  --verbose can override
+    check each subdevice to see if it is in conf_get_devs.
+    Warn if not.
+
+* Support multipath ... maybe...
+
+* --follow to syslog 
+
+* --follow to move spares around
+
+* --follow to notice other events:
+     rebuild started
+     spare activated
+     spare removed
+     spare added
+
+------------------------------------
+- --examine --scan scans all drives and build an mdctl.conf file DONE
+
 - check superblock checksum in examine DONE
 - 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
-- --verbose and --force flags.
+- --verbose and --force flags. DONE
 
 - 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...
+   one missing, one spare, insync  DONE (--force)
+- and for raid1 - some failed drives...  (missing)
 
 - when RUN_ARRAY, make sure *_disks counts are right
 
 - get --detail to extract extra stuff from superblock,
    like uuid  DONE
-- --detail --brief to give a config file line
+- --detail --brief to give a config file line DONE
 - parse config file. DONE
 - test...
 
@@ -23,7 +49,7 @@
   then try to assemble that device first.
 
 
-- mdctl -S /dev/md0 /dev/md1 gives internal error
+- mdctl -S /dev/md0 /dev/md1 gives internal error FIXED
 
 - mdctl --detail --scan print summary of what it can find?
 
@@ -74,4 +100,4 @@ New mode: --Monitor (or --Follow)
      rebuild started
      spare activated
      spare removed
-     spare added
\ No newline at end of file
+     spare added
index 39ef56b..4fc381c 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -229,6 +229,8 @@ void arrayline(char *line)
 
        mis.uuid_set = 0;
        mis.super_minor = -1;
+       mis.level = -10;
+       mis.raid_disks = -1;
        mis.devices = NULL;
        mis.devname = NULL;
 
@@ -273,6 +275,12 @@ void arrayline(char *line)
                                        w);
                        else
                                mis.spare_group = strdup(w+12);
+               } else if (strncasecmp(w, "level=", 6) == 0 ) {
+                       /* this is mainly for compatability with --brief output */
+                       mis.level = map_name(pers, w+6);
+               } else if (strncasecmp(w, "disks=", 6) == 0 ) {
+                       /* again, for compat */
+                       mis.raid_disks = atoi(w+6);                        
                } else {
                        fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n",
                                w);
index 773e7f0..dae9949 100755 (executable)
--- a/makedist
+++ b/makedist
@@ -16,6 +16,6 @@ then
   exit 1
 fi
 trap "rm $target/$base; exit" 1 2 3
-( cd .. ; ln -s mdctl mdctl-$version ; tar czhvf - --exclude='*,v' --exclude='*.o' --exclude=RCS mdctl-$version ; rm mdctl-$version )  > $target/$base
+( cd .. ; ln -s mdctl mdctl-$version ; tar czhvf - --exclude='*,v' --exclude='*.o' --exclude mdctl --exclude=RCS mdctl-$version ; rm mdctl-$version )  > $target/$base
 chmod a+r $target/$base
 ls -l $target/$base
diff --git a/mdctl.8 b/mdctl.8
index fe9c656..153d7b5 100644 (file)
--- a/mdctl.8
+++ b/mdctl.8
 .\" -*- nroff -*-
 .TH mdctl 8
 .SH NAME
-mdctl \- a single program that can be used to control Linux md devices
+mdctl \- manage MD devices
+.I aka
+Linux Software Raid.
+
 .SH SYNOPSIS
 
-.BI mdctl 
-[mode] <raiddevice> [options]
+.BI mdctl " [mode] <raiddevice> [options] <subdevices>"
 
 .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.
+real block devices. This allows multiple devices (typically disk
+drives or partitions there-of) to be combined into a single device to
+hold (for example) a single filesystem.
+Some RAID levels included redundancy and so can survive some degree of
+device failure.
+
+Linux Software RAID devices are implemented through the md (Multiple Devices) device driver.
+
+Currently, Linux supports
+.B LINEAR
+md devices,
+.B RAID0
+(striping),
+.B RAID1
+(mirroring),
+.B RAID4
+and
+.B RAID5.
+
+Recent kernels (2002) also support a mode known as
+.BR MULTIPATH .
+.B mdctl
+does not support MULTIPATH as yet.
 
-.IP "\fB\-\fP"
-a list of devices that should be scanned for md sub-devices.
+.B mdctl
+is a program that can be used to create and manage MD devices.  As
+such it provides a similar set of functionality to the
+.B raidtools
+packages.
+The key differences between
+.B mdctl
+and
+.B raidtools
+are:
+.IP \(bu 4
+.B mdctl
+is a single program and not a collection of programs.
+.IP \(bu 4
+.B mdctl
+can perform (almost) all of its functions without having a
+configuration file.  Also mdctl helps with management of the configuration
+file.
+.IP \(bu 4
+.B mdctl
+can provide information about your arrays (through Detail and Examine)
+that
+.B  raidtools
+cannot.
+.IP \(bu 4
+.B raidtools
+can manage MULTIPATH devices which
+.B mdctl
+cannot yet manage.
 
 .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
+mdctl has 7 major modes of operation:
+.TP
+.B Assemble
+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"
+checks that the components
+do form a bona fide array, and can, on request, fiddle superblock
+information so as to assemble a faulty array.
+
+.TP
+.B Build
+Build a legacy array without per-device superblocks.
+
+.TP
+.B Create
+Create a new array with per-device superblocks.
+'''It can progress
+'''in several step create-add-add-run or it can all happen with one command.
+
+.TP
+.B Detail
+Display the details of a given md device.  Details include the RAID
+level, the number of devices, which ones are faulty (if any), and the
+array UUID.
+
+.TP
+.B Examine
+Examine a device to see if it is part of an md array, and print out
+the details of that array.
+This mode can also be used to examine a large number of devices and to
+print out a summary of the arrays found in a format suitable for the
+.B mdctl.conf
+configuration file.
+
+.TP
+.B "Follow or Monitor"
+Monitor one or more md devices and act on any state changes.
+
+.TP
+.B Manage
 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.
+readonly, readwrite.
+'''If an array is only partially setup by the
+'''Create or Assemble commands, subsequent Manage commands can finish the
+'''job.
 
 .SH OPTIONS
 
 Available options are:
 
-.IP "\fB\-C\fP, \fB\-\-create\fP"
-Create a new array
+.TP
+.BR -A ", " --assemble
+Assemble an existing array.
 
-.IP "\fB-A\fP, \fB\-\-assemble\fP"
-Assemble an existing array
+.TP
+.BR -B ", " --build
+Build a legacy array without superblocks.
 
-.IP "\fB\-B\fP, \fB\-\-build\fP"
-Build a legacy array without superblock
+.TP
+.BR -C ", " --create
+Create a new array.
 
-.IP "\fB\-D\fP, \fB\-\-detail\fP"
-Print detail of a given md array
+.TP
+.BR -D ", " --detail
+Print detail of one or more md devices.
 
-.IP "\fB\-E\fP, \fB\-\-examine\fP"
-Print content of md superblock on device
+.TP
+.BR -E ", " --examine
+Print content of md superblock on device(s).
 
-.IP "\fB\-h\fP, \fB\-\-help\fP"
-This help message or, after above option, mode specific help message
+.TP
+.BR -F ", " --follow ", " --monitor
+Select
+.B Monitor
+mode.
 
-.IP "\fB\-V\fP, \fB\-\-version\fP"
-Print version information for mdctl
+.TP
+.BR -h ", " --help
+Display help message or, after above option, mode specific help message.
 
-.IP "\fB\-v\fP, \fB\-\-verbose\fP"
-Be more verbose about what is happening
+.TP
+.BR -V ", " --version
+Print version information for mdctl.
 
-.SH For create or build:
+.TP
+.BR -v ", " --verbose
+Be more verbose about what is happening.
 
-.IP "\fB\-c\fP, \fB\-\-chunk=\fP"
-chunk size of kibibytes
+.TP
+.BR -b ", " --brief
+Be less verbose.  This is used with
+.B --detail
+and
+.BR --examine .
 
-.IP "\fB\-\-rounding=\fP"
-rounding factor for linear array (==chunk size)
+.SH For create or build:
 
-.IP "\fB\-l\fP, \fB\-\-level=\fP"
-raid level: 0,1,4,5,linear. 0 or linear for build
+.TP
+.BR -c ", " --chunk=
+Specify chunk size of kibibytes.  The default is 64.
 
-.IP "\fB\-p\fP, \fB\-\-parity=\fP"
-raid5 parity algorithm: {left,right}-{,a}symmetric
+.TP
+.BR --rounding=
+Specify rounding factor for linear array (==chunk size)
 
-.IP "\fB\-\-layout=\fP"
-same as --parity
+.TP
+.BR -l ", " --level=
+Set raid level.  Options are: linear, raid0, 0, stripe, raid1, 1, mirror, raid5, 4,
+raid5, 5.  Obviously some of these are synonymous.
+Only the first 4 are valid when Building.
 
-.IP "\fB\-n\fP, \fB\-\-raid-disks=\fP"
-number of active devices in array
+.TP
+.BR -p ", " --parity=
+Set raid5 parity algorithm. Options are:
+{left,right}-{,a}symmetric, la, ra, ls, rs.  The default is left-symmetric.
 
-.IP "\fB\-x\fP, \fB\-\-spare-disks=\fP"
-number of spares (eXtras) to allow space for
+.TP
+.BR --layout=
+same as --parity
 
-.IP "\fB\-z\fP, \fB\-\-size=\fP"
-Size (in K) of each drive in RAID1/4/5 - optional
+.TP
+.BR -n ", " --raid-disks=
+number of active devices in array.
+
+.TP
+.BR -x ", " --spare-disks=
+number of spare (eXtra) disks in initial array.  Spares can be added
+and removed later.
+
+.TP
+.BR -z ", " --size=
+Amount (in Kibibytes) of space to use from each drive in RAID1/4/5.
+This must be a multiple of the chunk size, and must leave about 128Kb
+of space at the end of the drive for the RAID superblock.
+If this is not specified
+(as it normally is not) the smallest drive (or partition) sets the
+size, though if there is a variance among the drives of greater than 1%, a warning is
+issued.
 
 .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"
+.TP
+.BR -u ", " --uuid=
+uuid of array to assemble. Devices which don't have this uuid are
+excluded
+
+.TP
+.BR -m ", " --super-minor=
+Minor number of device that array was created for.  Devices which
+don't have this minor number are excluded.  If you create an array as
+/dev/md1, then all superblock will contain the minor number 1, even if
+the array is later assembled as /dev/md2.
+
+.TP
+.BR -c ", " --config=
+config file.  Default is
+.BR /etc/mdctl.conf .
+
+.TP
+.BR -s ", " --scan
 scan config file for missing information
 
-.IP "\fB\-f\fP, \fB\-\-force\fP"
+.TP
+.BR -f ", " --force
 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
+.TP
+.BR -R ", " --run
+Attempt to start the array even if fewer drives were given than are
+needed for a full array. Normally if not all drives are found and
+.B --scan
+is not used, then the array will be assembled but not started.
+With
+.B --run
+an attempt will be made to start it anyway.
 
-.IP "\fB\-S\fP, \fB\-\-stop\fP"
-deactivate array, releasing all resources
+.SH General management
 
-.IP "\fB\-o\fP, \fB\-\-readonly\fP"
-mark array as readonly
+.TP
+.BR -a ", " --add
+'''add, or
+hotadd listed devices.
 
-.IP "\fB\-w\fP, \fB\-\-readwrite\fP"
-mark array as readwrite
+.TP
+.BR -r ", " --remove
+remove listed devices.  The must not be active.  i.e. they should
+be failed or spare devices.
 
-.SH CREATE MODE
+.TP
+.BR -f ", " --fail
+mark listed devices as faulty.
 
-Usage:
+.TP
+.BR --set-faulty
+same as --fail.
 
-.B mdctl
---create device --chunk=X --level=Y --raid-disks=Z devices
+.TP
+.BR -R ", " --run
+start a partially built array.
 
-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.
+.TP
+.BR -S ", " --stop
+deactivate array, releasing all resources.
 
-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%.
+.TP
+.BR -o ", " --readonly
+mark array as readonly.
 
-If any discrepancy is found, the array will not automatically be run, though
-the presence of a 
-.B --run
-can override this caution.
+.TP
+.BR -w ", " --readwrite
+mark array as readwrite.
 
-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...
+.HP 12
+Usage:
+.B mdctl --assemble
+.I device options...
+.HP 12
+Usage:
+.B mdctl --assemble --scan
+.I  options...
 
+.PP
 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.
+For each array, mdctl needs to know the md device, the identity of the
+array, 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 
+The identity 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.
+option, with the
+.B --super-minor
+option, 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.
 
 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.
+command line or from the config file. Only devices which have an md
+superblock which contains the right identity 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
+.B /etc/mdctl.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
+is not given, then the config file will only be used to find the
+identity of md arrays.
 
-.SH BUILD MDOE
+Normally the array will be started after it is assembled.  However is
+.B --scan
+is not given and insufficient drives were lists to start a complete
+(non-degraded) array, then the array is not started (to guard against
+usage errors).  To insist that the array be started in this case (as
+may work for RAID1 or RAID5), give the
+.B --run
+flag.
 
-Usage:
 
-.B mdctl 
---build device -chunk=X --level=Y --raid-disks=Z devices
+.SH BUILD MODE
 
+.HP 12
+Usage:
+.B mdctl --build
+.I device
+.BI --chunk= X
+.BI --level= Y
+.BI --raid-disks= Z
+.I devices
+
+.PP
 This usage is similar to 
-.B --create. 
+.BR --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
+these arrays there is no difference 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.
+The level may only be 0, raid0, or linear. All devices must be listed
+and the array will be started once complete.
+
+.SH CREATE MODE
+
+.HP 12
+Usage:
+.B mdctl --create
+.I device
+.BI --chunk= X
+.BI --level= Y
+.br
+.BI --raid-disks= Z
+.I  devices
+
+.PP
+This usage will initialise a new md array, associate some devices with
+it, and activate the array.
+
+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:
+.TP
+.B --run
+insist of running the array even if some devices look like they might
+be in use.
+
+.TP
+.B --readonly
+start the array readonly - not supported yet.
 
-.SH BUGS
-no known bugs.
+.SH DETAIL MODE
+.HP 12
+Usage:
+.B mdctl --detail
+.RB [ --brief ]
+.I device ...
+.PP
+
+This usage sill print out the details of the given array including a
+list of component devices.  To determine names for the devices,
+.B mdctl
+searches
+.B /dev
+for device files with the right major and minor numbers.
+
+With
+.B --brief
+.B mdctl
+prints a single line that identifies the level, number of disks, and
+UUID of the array.  This line is suitable for inclusion in
+.BR /etc/mdctl.conf .
+
+.SH EXAMINE MODE
+.HP 12
+Usage:
+.B mdctl --examine
+.RB [ --scan ]
+.RB [ --brief ]
+.I device ...
+.PP
+This usage will examine some block devices to see if that have a valid
+RAID superblock on them.  The information in each valid raid
+superblock will be printed.
+
+If
+.B --scan
+is used, the no devices should be listed, and the complete set of
+devices identified in the configuration file are checked.
+.B --scan
+implies
+.B --brief
+but this implication can be countered by specifying
+.BR --verbose .
+
+With
+.B --brief
+.B mdctl
+will output an config file entry of each distinct array that was
+found.  This entry will list the UUID, the raid level, and a list of
+the individual devices on which a superblock for that array was found.
+This output will by syntactically suitable for inclusion in the
+configuration file, but should
+.B NOT
+be used blindly.  Often the array description that you want in the
+configuration file is much less specific than that given by
+.BR "mdctl -Bs" .
+For example, you normally do not want to list the devices,
+particularly if they are SCSI devices.
+
+'''.SH BUGS
+'''no known bugs.
+
+.SH FILES
+
+.SS /proc/mdstat
+
+If you're using the 
+.B /proc 
+filesystem,
+.B /proc/mdstat
+gives you informations about md devices status.
+This file is not currently used by
+.BR mdctl .
+
+.SS /etc/mdctl.conf
+
+The config file is line oriented with, as usual, blank lines and lines
+beginning with a hash (or pound sign or sharp or number sign,
+whichever you like to call it) ignored.
+Lines that start with a blank are treated as continuations of the
+previous line (I don't like trailing slashes).
+
+Each line contains a sequence of space-separated words, the first of
+which identified the type of line. Keywords are case-insensitive, and
+the first work on a line can be abbreviated to 3 letters.
+
+There are two types of lines. ARRAY and DEVICE.
+
+The DEVICE lines usually come first. All remaining words on the line
+are treated as names of devices, possibly containing wild cards (see
+.IR glob (7)).
+These list all the devices that
+.B mdctl
+is allowed to scan
+when looking for devices with RAID superblocks.
+Each line can contain multiple device names, and there can be multiple
+DEVICE lines.  For example:
+.IP
+DEVICE /dev/hda* /dev/hdc*
+.br
+DEV    /dev/sd*
+.br
+DEVICE /dev/discs/disc*/disc
+.PP
+The ARRAY lines identify actual arrays.  The second word on the line
+should be the name of the device where the array is normally
+assembled, such as /dev/md1.
+Subsequent words identify the array. If multiple identities are given,
+then the array much match ALL identities to be considered a match.
+Each identity word has a tag, and equals sign, and some value.
+The options are:
+
+.TP
+.B uuid=
+The value should be a 128 bit uuid in hexadecimal, with punctuation
+interspersed if desired.  This must match the uuid stored in the
+superblock.
+.TP
+.B super-minor=
+The value is an integer which indicates the minor number that was
+stored in the superblock when the array was created. When an array is
+created as /dev/mdX, then the minor number X is stored.
+.TP
+.B devices=
+The value is a comma separated list of device names. Precisely these
+devices will be used to assemble the array.  Note that the devices
+listed there must also be listed on a DEVICE line.
+.TP
+.B level=
+The value is a raid level.  This is normally used to identify an
+array, but is supported so that the output of
+.B "mdctl --examine --scan"
+can be use directly in the configuration file.
+.TP
+.B disks=
+The value is the number of disks in a complete active array.  As with
+.B level=
+this is mainly for compatibility with the output of
+.BR "mdctl --examine --scan" .
 
 .SH TODO
 
+Finish and document Follow mode.
 
 .SH SEE ALSO
+For information on the various levels of
+RAID, check out:
+
+.IP
+.UR   http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/
+http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/
+.UE
+.PP
+for new releases of the RAID driver check out:
+
+.IP
+.UR  ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches
+ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches
+.UE
+.PP
+or
+.IP
+.UR http://www.cse.unsw.edu.au/~neilb/patches/linux-stable/
+http://www.cse.unsw.edu.au/~neilb/patches/linux-stable/
+.URk
+.PP
 .IR raidtab (5),
 .IR raid0run (8),
 .IR raidstop (8),
diff --git a/mdctl.c b/mdctl.c
index 6387110..054fbc4 100644 (file)
--- a/mdctl.c
+++ b/mdctl.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -64,14 +64,17 @@ int main(int argc, char *argv[])
        int sparedisks = 0;
        struct mddev_ident_s ident;
        char *configfile = NULL;
+       char *cp;
        int scan = 0;
        char devmode = 0;
        int runstop = 0;
        int readonly = 0;
-       char *devs[MD_SB_DISKS+1];
-       int devmodes[MD_SB_DISKS+1];
+       mddev_dev_t devlist = NULL;
+       mddev_dev_t *devlistend = & devlist;
+       mddev_dev_t dv;
        int devs_found = 0;
        int verbose = 0;
+       int brief = 0;
        int force = 0;
 
        char *mailaddr = NULL;
@@ -81,6 +84,8 @@ int main(int argc, char *argv[])
        int mdfd = -1;
 
        ident.uuid_set=0;
+       ident.level = -10;
+       ident.raid_disks = -1;
        ident.super_minor= -1;
        ident.devices=0;
 
@@ -124,19 +129,36 @@ int main(int argc, char *argv[])
                case 'v': verbose = 1;
                        continue;
 
+               case 'b': brief = 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
+                        *    All devices listed are "md" devices : --Detail, -As
+                        *    No devices are "md" devices : --Examine
+                        *    First device is "md", others are component: -A,-B,-C
+                        * Only accept on device before mode is determined.
+                        *  If mode is @, then require devmode for other devices.
                         */
-                       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);
+                       if (devs_found > 0 && !mode ) {
+                               fprintf(stderr, Name ": Must give mode flag before second device name at %s\n", optarg);
+                               exit(2);
+                       }
+                       if (devs_found > 0 && mode == '@' && !devmode) {
+                               fprintf(stderr, Name ": Must give on of -a/-r/-f for subsequent devices at %s\n", optarg);
                                exit(2);
                        }
-                       devs[devs_found] = optarg;
-                       devmodes[devs_found] = devmode;
+                       dv = malloc(sizeof(*dv));
+                       if (dv == NULL) {
+                               fprintf(stderr, Name ": malloc failed\n");
+                               exit(3);
+                       }
+                       dv->devname = optarg;
+                       dv->disposition = devmode;
+                       dv->next = NULL;
+                       *devlistend = dv;
+                       devlistend = &dv->next;
+                       
                        devs_found++;
                        continue;
 
@@ -205,6 +227,7 @@ int main(int argc, char *argv[])
                                        optarg);
                                exit(2);
                        }
+                       ident.level = level;
                        continue;
 
                case O('C','p'): /* raid5 layout */
@@ -246,6 +269,7 @@ int main(int argc, char *argv[])
                                        optarg);
                                exit(2);
                        }
+                       ident.raid_disks = raiddisks;
                        continue;
 
                case O('C','x'): /* number of spare (eXtra) discs */
@@ -276,7 +300,7 @@ int main(int argc, char *argv[])
                        continue;
                case O('A','u'): /* uuid of array */
                        if (ident.uuid_set) {
-                               fprintf(stderr, Name ": uuid cannot bet set twice.  "
+                               fprintf(stderr, Name ": uuid cannot be set twice.  "
                                        "Second value %s.\n", optarg);
                                exit(2);
                        }
@@ -288,6 +312,19 @@ int main(int argc, char *argv[])
                        }
                        continue;
 
+               case O('A','m'): /* super-minor for array */
+                       if (ident.super_minor >= 0) {
+                               fprintf(stderr, Name ": super-minor cannot be set twice.  "
+                                       "Second value: %s.\n", optarg);
+                               exit(2);
+                       }
+                       ident.super_minor = strtoul(optarg, &cp, 10);
+                       if (!optarg[0] || *cp) {
+                               fprintf(stderr, Name ": Bad super-minor number: %s.\n", optarg);
+                               exit(2);
+                       }
+                       continue;
+
                case O('A','c'): /* config file */
                case O('F','c'):
                        if (configfile) {
@@ -299,6 +336,7 @@ int main(int argc, char *argv[])
                        /* FIXME possibly check that config file exists.  Even parse it */
                        continue;
                case O('A','s'): /* scan */
+               case O('E','s'):
                        scan = 1;
                        continue;
 
@@ -409,7 +447,7 @@ int main(int argc, char *argv[])
                        fprintf(stderr, Name ": an md device must be given in this mode\n");
                        exit(2);
                }
-               mdfd = open_mddev(devs[0]);
+               mdfd = open_mddev(devlist->devname);
                if (mdfd < 0)
                        exit(1);
        }
@@ -420,36 +458,36 @@ int main(int argc, char *argv[])
        case '@':/* Management */
                /* readonly, add/remove, readwrite, runstop */
                if (readonly>0)
-                       rv = Manage_ro(devs[0], mdfd, readonly);
+                       rv = Manage_ro(devlist->devname, mdfd, readonly);
                if (!rv && devs_found>1)
-                       rv = Manage_subdevs(devs[0], mdfd,
-                                           devs_found-1, devs+1, devmodes+1);
+                       rv = Manage_subdevs(devlist->devname, mdfd,
+                                           devlist->next);
                if (!rv && readonly < 0)
-                       rv = Manage_ro(devs[0], mdfd, readonly);
+                       rv = Manage_ro(devlist->devname, mdfd, readonly);
                if (!rv && runstop)
-                       rv = Manage_runstop(devs[0], mdfd, runstop);
+                       rv = Manage_runstop(devlist->devname, mdfd, runstop);
                break;
        case 'A': /* Assemble */
                if (!scan)
-                       rv = Assemble(devs[0], mdfd, &ident, configfile,
-                                     devs_found-1, devs+1,
+                       rv = Assemble(devlist->devname, mdfd, &ident, configfile,
+                                     devlist->next,
                                      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]);
+                       for (dv = devlist ; dv ; dv=dv->next) {
+                               mddev_ident_t array_ident = conf_get_ident(configfile, dv->devname);
+                               mdfd = open_mddev(dv->devname);
                                if (mdfd < 0) {
                                        rv |= 1;
                                        continue;
                                }
                                if (array_ident == NULL) {
                                        fprintf(stderr, Name ": %s not identified in config file.\n",
-                                               devs[i]);
+                                               dv->devname);
                                        rv |= 1;
                                        continue;
                                }
-                               rv |= Assemble(devs[i], mdfd, array_ident, configfile,
-                                              0, NULL,
+                               rv |= Assemble(dv->devname, mdfd, array_ident, configfile,
+                                              NULL,
                                               readonly, runstop, verbose, force);
                        }
                else {
@@ -459,36 +497,43 @@ int main(int argc, char *argv[])
                                rv = 1;
                        } else
                                for (; array_list; array_list = array_list->next) {
+                                       mdu_array_info_t array;
                                        mdfd = open_mddev(array_list->devname);
                                        if (mdfd < 0) {
                                                rv |= 1;
                                                continue;
                                        }
+                                       if (ioctl(mdfd, GET_ARRAY_INFO, &array)>=0)
+                                               /* already assembled, skip */
+                                               continue;
                                        rv |= Assemble(array_list->devname, mdfd,
                                                       array_list, configfile,
-                                                      0, NULL,
+                                                      NULL,
                                                       readonly, runstop, verbose, force);
                                }
                }
                break;
        case 'B': /* Build */
-               rv = Build(devs[0], mdfd, chunk, level, raiddisks, devs_found-1,devs+1);
+               rv = Build(devlist->devname, mdfd, chunk, level, raiddisks, devlist->next);
                break;
        case 'C': /* Create */
-               rv = Create(devs[0], mdfd, chunk, level, layout, size,
+               rv = Create(devlist->devname, mdfd, chunk, level, layout, size,
                            raiddisks, sparedisks,
-                           devs_found-1,devs+1, runstop, verbose, force);
+                           devs_found-1, devlist->next, runstop, verbose, force);
                break;
        case 'D': /* Detail */
-               for (i=0; i<devs_found; i++)
-                       rv |= Detail(devs[i]);
+               for (dv=devlist ; dv; dv=dv->next)
+                       rv |= Detail(dv->devname, brief);
                break;
        case 'E': /* Examine */
-               for (i=0; i<devs_found; i++)
-                       rv |= Examine(devs[i]);
+               if (devlist == NULL && scan==0) {
+                       fprintf(stderr, Name ": No devices to examine\n");
+                       exit(2);
+               }
+               rv = Examine(devlist, devlist?brief:!verbose, configfile);
                break;
        case 'F': /* Follow */
-               rv= Monitor(devs_found, devs, mailaddr, program,
+               rv= Monitor(devlist, mailaddr, program,
                            delay?delay:60, configfile);
        }
        exit(rv);
diff --git a/mdctl.h b/mdctl.h
index 4308716..5aa61ce 100644 (file)
--- a/mdctl.h
+++ b/mdctl.h
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -76,7 +76,8 @@ typedef struct mddev_ident_s {
        char *devices;          /* comma separated list of device
                                 * names with wild cards
                                 */
-
+       int level;              /* -10 if not set */
+       int raid_disks;         /* -1 if not set */
        char *spare_group;
        struct mddev_ident_s *next;
 } *mddev_ident_t;
@@ -84,6 +85,9 @@ typedef struct mddev_ident_s {
 /* List of device names - wildcards expanded */
 typedef struct mddev_dev_s {
        char *devname;
+       char disposition;       /* 'a' for add, 'r' for remove, 'f' for fail.
+                                * Not set for names read from .config
+                                */
        struct mddev_dev_s *next;
 } *mddev_dev_t;
 
@@ -106,29 +110,29 @@ extern char *map_dev(int major, int minor);
 extern int Manage_ro(char *devname, int fd, int readonly);
 extern int Manage_runstop(char *devname, int fd, int runstop);
 extern int Manage_subdevs(char *devname, int fd,
-                         int devcnt, char *devnames[], int devmodes[]);
+                         mddev_dev_t devlist);
 
 
 extern int Assemble(char *mddev, int mdfd,
                    mddev_ident_t ident,
                    char *conffile,
-                   int subdevs, char *subdev[],
+                   mddev_dev_t devlist,
                    int readonly, int runstop,
                    int verbose, int force);
 
 extern int Build(char *mddev, int mdfd, int chunk, int level,
                 int raiddisks,
-                int subdevs, char *subdev[]);
+                mddev_dev_t devlist);
 
 
 extern int Create(char *mddev, int mdfd,
                  int chunk, int level, int layout, int size, int raiddisks, int sparedisks,
-                 int subdevs, char *subdev[],
+                 int subdevs, mddev_dev_t devlist,
                  int runstop, int verbose, int force);
 
-extern int Detail(char *dev);
-extern int Examine(char *dev);
-extern int Monitor(int num_devs, char *devlist[],
+extern int Detail(char *dev, int brief);
+extern int Examine(mddev_dev_t devlist, int brief, char *conffile);
+extern int Monitor(mddev_dev_t devlist,
                  char *mailaddr, char *alert_cmd,
                  int period,
                  char *config);
@@ -142,3 +146,5 @@ extern int check_raid(int fd, char *name);
 
 extern mddev_ident_t conf_get_ident(char *, char*);
 extern mddev_dev_t conf_get_devs(char *);
+
+extern char *human_size(long kbytes);
diff --git a/mdctl.man b/mdctl.man
new file mode 100644 (file)
index 0000000..682bc82
--- /dev/null
+++ b/mdctl.man
@@ -0,0 +1,476 @@
+mdctl(8)                                                 mdctl(8)
+
+
+
+N\bNA\bAM\bME\bE
+       mdctl - manage MD devices _\ba_\bk_\ba Linux Software Raid.
+
+
+S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS
+       m\bmd\bdc\bct\btl\bl _\b[_\bm_\bo_\bd_\be_\b] _\b<_\br_\ba_\bi_\bd_\bd_\be_\bv_\bi_\bc_\be_\b> _\b[_\bo_\bp_\bt_\bi_\bo_\bn_\bs_\b] _\b<_\bs_\bu_\bb_\bd_\be_\bv_\bi_\bc_\be_\bs_\b>
+
+
+D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
+       RAID  devices are virtual devices created from two or more
+       real block devices. This allows  multiple  devices  (typi-
+       cally  disk  drives or partitions there-of) to be combined
+       into a single  device  to  hold  (for  example)  a  single
+       filesystem.   Some  RAID levels included redundancy and so
+       can survive some degree of device failure.
+
+       Linux Software RAID devices are implemented through the md
+       (Multiple Devices) device driver.
+
+       Currently, Linux supports L\bLI\bIN\bNE\bEA\bAR\bR md devices, R\bRA\bAI\bID\bD0\b0 (strip-
+       ing), R\bRA\bAI\bID\bD1\b1 (mirroring), R\bRA\bAI\bID\bD4\b4 and R\bRA\bAI\bID\bD5\b5.\b.
+
+       Recent kernels (2002) also support a mode known as  M\bMU\bUL\bLT\bTI\bI-\b-
+       P\bPA\bAT\bTH\bH.  m\bmd\bdc\bct\btl\bl does not support MULTIPATH as yet.
+
+       m\bmd\bdc\bct\btl\bl  is  a program that can be used to create and manage
+       MD devices.  As such it provides a similar  set  of  func-
+       tionality  to the r\bra\bai\bid\bdt\bto\boo\bol\bls\bs packages.  The key differences
+       between m\bmd\bdc\bct\btl\bl and r\bra\bai\bid\bdt\bto\boo\bol\bls\bs are:
+
+       +\bo   m\bmd\bdc\bct\btl\bl is a single program and not a collection of pro-
+           grams.
+
+       +\bo   m\bmd\bdc\bct\btl\bl  can perform (almost) all of its functions with-
+           out having a configuration  file.   Also  mdctl  helps
+           with management of the configuration file.
+
+       +\bo   m\bmd\bdc\bct\btl\bl   can  provide  information  about  your  arrays
+           (through Detail and Examine) that r\bra\bai\bid\bdt\bto\boo\bol\bls\bs cannot.
+
+       +\bo   r\bra\bai\bid\bdt\bto\boo\bol\bls\bs can manage  MULTIPATH  devices  which  m\bmd\bdc\bct\btl\bl
+           cannot yet manage.
+
+
+M\bMO\bOD\bDE\bES\bS
+       mdctl has 7 major modes of operation:
+
+       A\bAs\bss\bse\bem\bmb\bbl\ble\be
+              Assemble  the  parts  of a previously created array
+              into an active array. Components can be  explicitly
+              given  or  can  be searched for.  m\bmd\bdc\bct\btl\bl checks that
+              the components do form a bona fide array, and  can,
+              on  request, fiddle superblock information so as to
+              assemble a faulty array.
+
+
+       B\bBu\bui\bil\bld\bd  Build   a   legacy   array    without    per-device
+              superblocks.
+
+
+       C\bCr\bre\bea\bat\bte\be Create a new array with per-device superblocks.
+
+
+       D\bDe\bet\bta\bai\bil\bl Display  the details of a given md device.  Details
+              include the RAID  level,  the  number  of  devices,
+              which ones are faulty (if any), and the array UUID.
+
+
+       E\bEx\bxa\bam\bmi\bin\bne\be
+              Examine a device to see if it  is  part  of  an  md
+              array,  and  print  out  the details of that array.
+              This mode can also be used to examine a large  num-
+              ber  of  devices  and to print out a summary of the
+              arrays  found  in  a  format   suitable   for   the
+              m\bmd\bdc\bct\btl\bl.\b.c\bco\bon\bnf\bf configuration file.
+
+
+       F\bFo\bol\bll\blo\bow\bw o\bor\br M\bMo\bon\bni\bit\bto\bor\br
+              Monitor one or more md devices and act on any state
+              changes.
+
+
+       M\bMa\ban\bna\bag\bge\be This  is  for  odd  bits  an  pieces  like  hotadd,
+              hotremove, setfaulty, stop, readonly, readwrite.
+
+
+O\bOP\bPT\bTI\bIO\bON\bNS\bS
+       Available options are:
+
+
+       -\b-A\bA, -\b--\b-a\bas\bss\bse\bem\bmb\bbl\ble\be
+              Assemble an existing array.
+
+
+       -\b-B\bB, -\b--\b-b\bbu\bui\bil\bld\bd
+              Build a legacy array without superblocks.
+
+
+       -\b-C\bC, -\b--\b-c\bcr\bre\bea\bat\bte\be
+              Create a new array.
+
+
+       -\b-D\bD, -\b--\b-d\bde\bet\bta\bai\bil\bl
+              Print detail of one or more md devices.
+
+
+       -\b-E\bE, -\b--\b-e\bex\bxa\bam\bmi\bin\bne\be
+              Print content of md superblock on device(s).
+
+
+       -\b-F\bF, -\b--\b-f\bfo\bol\bll\blo\bow\bw, -\b--\b-m\bmo\bon\bni\bit\bto\bor\br
+              Select M\bMo\bon\bni\bit\bto\bor\br mode.
+
+
+       -\b-h\bh, -\b--\b-h\bhe\bel\blp\bp
+              Display  help  message or, after above option, mode
+              specific help message.
+
+
+       -\b-V\bV, -\b--\b-v\bve\ber\brs\bsi\bio\bon\bn
+              Print version information for mdctl.
+
+
+       -\b-v\bv, -\b--\b-v\bve\ber\brb\bbo\bos\bse\be
+              Be more verbose about what is happening.
+
+
+       -\b-b\bb, -\b--\b-b\bbr\bri\bie\bef\bf
+              Be less verbose.  This is used  with  -\b--\b-d\bde\bet\bta\bai\bil\bl  and
+              -\b--\b-e\bex\bxa\bam\bmi\bin\bne\be.
+
+
+F\bFo\bor\br c\bcr\bre\bea\bat\bte\be o\bor\br b\bbu\bui\bil\bld\bd:\b:
+       -\b-c\bc, -\b--\b-c\bch\bhu\bun\bnk\bk=\b=
+              Specify  chunk  size  of kibibytes.  The default is
+              64.
+
+
+       -\b--\b-r\bro\bou\bun\bnd\bdi\bin\bng\bg=\b=
+              Specify rounding factor for linear  array  (==chunk
+              size)
+
+
+       -\b-l\bl, -\b--\b-l\ble\bev\bve\bel\bl=\b=
+              Set  raid  level.   Options  are: linear, raid0, 0,
+              stripe, raid1,  1,  mirror,  raid5,  4,  raid5,  5.
+              Obviously  some  of these are synonymous.  Only the
+              first 4 are valid when Building.
+
+
+       -\b-p\bp, -\b--\b-p\bpa\bar\bri\bit\bty\by=\b=
+              Set   raid5   parity   algorithm.   Options    are:
+              {left,right}-{,a}symmetric,  la,  ra,  ls, rs.  The
+              default is left-symmetric.
+
+
+       -\b--\b-l\bla\bay\byo\bou\but\bt=\b=
+              same as --parity
+
+
+       -\b-n\bn, -\b--\b-r\bra\bai\bid\bd-\b-d\bdi\bis\bsk\bks\bs=\b=
+              number of active devices in array.
+
+
+       -\b-x\bx, -\b--\b-s\bsp\bpa\bar\bre\be-\b-d\bdi\bis\bsk\bks\bs=\b=
+              number of spare (eXtra)  disks  in  initial  array.
+              Spares can be added and removed later.
+
+
+       -\b-z\bz, -\b--\b-s\bsi\biz\bze\be=\b=
+              Amount  (in  Kibibytes)  of  space to use from each
+              drive in RAID1/4/5.  This must be a multiple of the
+              chunk  size, and must leave about 128Kb of space at
+              the end of the drive for the RAID  superblock.   If
+              this  is  not specified (as it normally is not) the
+              smallest drive (or partition) sets the size, though
+              if  there is a variance among the drives of greater
+              than 1%, a warning is issued.
+
+
+F\bFo\bor\br a\bas\bss\bse\bem\bmb\bbl\ble\be:\b:
+       -\b-u\bu, -\b--\b-u\buu\bui\bid\bd=\b=
+              uuid of array to assemble. Devices which don't have
+              this uuid are excluded
+
+
+       -\b-m\bm, -\b--\b-s\bsu\bup\bpe\ber\br-\b-m\bmi\bin\bno\bor\br=\b=
+              Minor  number of device that array was created for.
+              Devices which don't  have  this  minor  number  are
+              excluded.  If you create an array as /dev/md1, then
+              all superblock will contain  the  minor  number  1,
+              even if the array is later assembled as /dev/md2.
+
+
+       -\b-c\bc, -\b--\b-c\bco\bon\bnf\bfi\big\bg=\b=
+              config file.  Default is /\b/e\bet\btc\bc/\b/m\bmd\bdc\bct\btl\bl.\b.c\bco\bon\bnf\bf.
+
+
+       -\b-s\bs, -\b--\b-s\bsc\bca\ban\bn
+              scan config file for missing information
+
+
+       -\b-f\bf, -\b--\b-f\bfo\bor\brc\bce\be
+              Assemble  the array even if some superblocks appear
+              out-of-date
+
+
+       -\b-R\bR, -\b--\b-r\bru\bun\bn
+              Attempt to start the array  even  if  fewer  drives
+              were  given  than are needed for a full array. Nor-
+              mally if not all drives are found and -\b--\b-s\bsc\bca\ban\bn is not
+              used,  then  the  array  will  be assembled but not
+              started.  With -\b--\b-r\bru\bun\bn an attempt  will  be  made  to
+              start it anyway.
+
+
+G\bGe\ben\bne\ber\bra\bal\bl m\bma\ban\bna\bag\bge\bem\bme\ben\bnt\bt
+       -\b-a\ba, -\b--\b-a\bad\bdd\bd
+              hotadd listed devices.
+
+
+       -\b-r\br, -\b--\b-r\bre\bem\bmo\bov\bve\be
+              remove  listed  devices.   The  must not be active.
+              i.e. they should be failed or spare devices.
+
+
+       -\b-f\bf, -\b--\b-f\bfa\bai\bil\bl
+              mark listed devices as faulty.
+
+
+       -\b--\b-s\bse\bet\bt-\b-f\bfa\bau\bul\blt\bty\by
+              same as --fail.
+
+
+       -\b-R\bR, -\b--\b-r\bru\bun\bn
+              start a partially built array.
+
+
+       -\b-S\bS, -\b--\b-s\bst\bto\bop\bp
+              deactivate array, releasing all resources.
+
+
+       -\b-o\bo, -\b--\b-r\bre\bea\bad\bdo\bon\bnl\bly\by
+              mark array as readonly.
+
+
+       -\b-w\bw, -\b--\b-r\bre\bea\bad\bdw\bwr\bri\bit\bte\be
+              mark array as readwrite.
+
+
+
+A\bAS\bSS\bSE\bEM\bMB\bBL\bLY\bY M\bMO\bOD\bDE\bE
+       Usage: m\bmd\bdc\bct\btl\bl -\b--\b-a\bas\bss\bse\bem\bmb\bbl\ble\be _\bd_\be_\bv_\bi_\bc_\be _\bo_\bp_\bt_\bi_\bo_\bn_\bs_\b._\b._\b.
+
+       Usage: m\bmd\bdc\bct\btl\bl -\b--\b-a\bas\bss\bse\bem\bmb\bbl\ble\be -\b--\b-s\bsc\bca\ban\bn _\bo_\bp_\bt_\bi_\bo_\bn_\bs_\b._\b._\b.
+
+
+       This usage assembles one or more  raid  arrays  from  pre-
+       existing  components.  For each array, mdctl needs to know
+       the md device, the identity of the array, and a number  of
+       sub devices. These can be found in a number of ways.
+
+       The  md  device  is either given before -\b--\b-s\bsc\bca\ban\bn or is found
+       from the config file. In  the  latter  case,  multiple  md
+       devices can be started with a single mdctl command.
+
+       The identity can be given with the -\b--\b-u\buu\bui\bid\bd option, with the
+       -\b--\b-s\bsu\bup\bpe\ber\br-\b-m\bmi\bin\bno\bor\br option, can be found in in the config  file,
+       or  will be taken from the super block on the first subde-
+       vice listed on the command line.
+
+       Devices can be given on the  -\b--\b-a\bas\bss\bse\bem\bmb\bbl\ble\be  command  line  or
+       from  the  config  file.  Only  devices  which  have an md
+       superblock which contains the right identity will be  con-
+       sidered for any device.
+
+       The  config  file  is  only  used if explicitly named with
+       -\b--\b-c\bco\bon\bnf\bfi\big\bg or requested with -\b--\b-s\bsc\bca\ban\bn.\b.   In  the  later  case,
+       /\b/e\bet\btc\bc/\b/m\bmd\bdc\bct\btl\bl.\b.c\bco\bon\bnf\bf is used.
+
+       If  -\b--\b-s\bsc\bca\ban\bn is not given, then the config file will only be
+       used to find the identity of md arrays.
+
+       Normally the array will be started after it is  assembled.
+       However  is  -\b--\b-s\bsc\bca\ban\bn  is  not given and insufficient drives
+       were lists to start a complete (non-degraded) array,  then
+       the  array is not started (to guard against usage errors).
+       To insist that the array be started in this case  (as  may
+       work for RAID1 or RAID5), give the -\b--\b-r\bru\bun\bn flag.
+
+
+
+B\bBU\bUI\bIL\bLD\bD M\bMO\bOD\bDE\bE
+       Usage:  m\bmd\bdc\bct\btl\bl  -\b--\b-b\bbu\bui\bil\bld\bd  _\bd_\be_\bv_\bi_\bc_\be -\b--\b-c\bch\bhu\bun\bnk\bk=\b=_\bX -\b--\b-l\ble\bev\bve\bel\bl=\b=_\bY -\b--\b-r\bra\bai\bid\bd-\b-
+                   d\bdi\bis\bsk\bks\bs=\b=_\bZ _\bd_\be_\bv_\bi_\bc_\be_\bs
+
+
+       This usage is similar to -\b--\b-c\bcr\bre\bea\bat\bte\be.  The difference is that
+       it creates a legacy array without a superblock. With these
+       arrays there is no difference 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, raid0, or linear. All devices
+       must be listed and the array will  be  started  once  com-
+       plete.
+
+
+C\bCR\bRE\bEA\bAT\bTE\bE M\bMO\bOD\bDE\bE
+       Usage: m\bmd\bdc\bct\btl\bl -\b--\b-c\bcr\bre\bea\bat\bte\be _\bd_\be_\bv_\bi_\bc_\be -\b--\b-c\bch\bhu\bun\bnk\bk=\b=_\bX -\b--\b-l\ble\bev\bve\bel\bl=\b=_\bY
+                   -\b--\b-r\bra\bai\bid\bd-\b-d\bdi\bis\bsk\bks\bs=\b=_\bZ _\bd_\be_\bv_\bi_\bc_\be_\bs
+
+
+       This  usage will initialise a new md array, associate some
+       devices with it, and activate the array.
+
+       As devices are added, they are checked to see if they con-
+       tain  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  automati-
+       cally  be run, though the presence of a -\b--\b-r\bru\bun\bn can override
+       this caution.
+
+
+       The General Management options that are valid with  --cre-
+       ate are:
+
+       -\b--\b-r\bru\bun\bn  insist  of  running  the array even if some devices
+              look like they might be in use.
+
+
+       -\b--\b-r\bre\bea\bad\bdo\bon\bnl\bly\by
+              start the array readonly - not supported yet.
+
+
+D\bDE\bET\bTA\bAI\bIL\bL M\bMO\bOD\bDE\bE
+       Usage: m\bmd\bdc\bct\btl\bl -\b--\b-d\bde\bet\bta\bai\bil\bl [-\b--\b-b\bbr\bri\bie\bef\bf] _\bd_\be_\bv_\bi_\bc_\be _\b._\b._\b.
+
+
+       This usage sill print out the details of the  given  array
+       including a list of component devices.  To determine names
+       for the devices, m\bmd\bdc\bct\btl\bl searches /\b/d\bde\bev\bv for device files with
+       the right major and minor numbers.
+
+       With  -\b--\b-b\bbr\bri\bie\bef\bf  m\bmd\bdc\bct\btl\bl  prints a single line that identifies
+       the level, number of disks, and UUID of the  array.   This
+       line is suitable for inclusion in /\b/e\bet\btc\bc/\b/m\bmd\bdc\bct\btl\bl.\b.c\bco\bon\bnf\bf.
+
+
+E\bEX\bXA\bAM\bMI\bIN\bNE\bE M\bMO\bOD\bDE\bE
+       Usage: m\bmd\bdc\bct\btl\bl -\b--\b-e\bex\bxa\bam\bmi\bin\bne\be [-\b--\b-s\bsc\bca\ban\bn] [-\b--\b-b\bbr\bri\bie\bef\bf] _\bd_\be_\bv_\bi_\bc_\be _\b._\b._\b.
+
+       This  usage will examine some block devices to see if that
+       have a valid RAID superblock on them.  The information  in
+       each valid raid superblock will be printed.
+
+       If  -\b--\b-s\bsc\bca\ban\bn  is  used, the no devices should be listed, and
+       the complete set of devices identified in  the  configura-
+       tion  file  are  checked.  -\b--\b-s\bsc\bca\ban\bn implies -\b--\b-b\bbr\bri\bie\bef\bf but this
+       implication can be countered by specifying -\b--\b-v\bve\ber\brb\bbo\bos\bse\be.
+
+       With -\b--\b-b\bbr\bri\bie\bef\bf m\bmd\bdc\bct\btl\bl will output an  config  file  entry  of
+       each  distinct array that was found.  This entry will list
+       the UUID, the raid level, and a  list  of  the  individual
+       devices  on  which  a superblock for that array was found.
+       This output will by syntactically suitable  for  inclusion
+       in the configuration file, but should N\bNO\bOT\bT be used blindly.
+       Often the array description that you want in the  configu-
+       ration file is much less specific than that given by m\bmd\bdc\bct\btl\bl
+       -\b-B\bBs\bs.  For example, you normally do not want  to  list  the
+       devices, particularly if they are SCSI devices.
+
+
+
+F\bFI\bIL\bLE\bES\bS
+   /\b/p\bpr\bro\boc\bc/\b/m\bmd\bds\bst\bta\bat\bt
+       If  you're  using the /\b/p\bpr\bro\boc\bc filesystem, /\b/p\bpr\bro\boc\bc/\b/m\bmd\bds\bst\bta\bat\bt gives
+       you informations about md devices status.   This  file  is
+       not currently used by m\bmd\bdc\bct\btl\bl.
+
+
+   /\b/e\bet\btc\bc/\b/m\bmd\bdc\bct\btl\bl.\b.c\bco\bon\bnf\bf
+       The  config  file  is  line oriented with, as usual, blank
+       lines and lines beginning with a hash (or  pound  sign  or
+       sharp  or  number  sign,  whichever  you  like to call it)
+       ignored.  Lines that start with a  blank  are  treated  as
+       continuations  of the previous line (I don't like trailing
+       slashes).
+
+       Each line contains a sequence  of  space-separated  words,
+       the  first  of which identified the type of line. Keywords
+       are case-insensitive, and the first work on a line can  be
+       abbreviated to 3 letters.
+
+       There are two types of lines. ARRAY and DEVICE.
+
+       The  DEVICE  lines usually come first. All remaining words
+       on the line are treated as names of devices, possibly con-
+       taining  wild  cards  (see  _\bg_\bl_\bo_\bb(7)).   These list all the
+       devices that m\bmd\bdc\bct\btl\bl is allowed to  scan  when  looking  for
+       devices with RAID superblocks.  Each line can contain mul-
+       tiple device names,  and  there  can  be  multiple  DEVICE
+       lines.  For example:
+
+              DEVICE /dev/hda* /dev/hdc*
+              DEV    /dev/sd*
+              DEVICE /dev/discs/disc*/disc
+
+       The  ARRAY  lines identify actual arrays.  The second word
+       on the line should be the name of  the  device  where  the
+       array is normally assembled, such as /dev/md1.  Subsequent
+       words identify  the  array.  If  multiple  identities  are
+       given, then the array much match ALL identities to be con-
+       sidered a match.  Each identity word has a tag, and equals
+       sign, and some value.  The options are:
+
+
+       u\buu\bui\bid\bd=\b=  The  value should be a 128 bit uuid in hexadecimal,
+              with punctuation  interspersed  if  desired.   This
+              must match the uuid stored in the superblock.
+
+       s\bsu\bup\bpe\ber\br-\b-m\bmi\bin\bno\bor\br=\b=
+              The  value  is an integer which indicates the minor
+              number that was stored in the superblock  when  the
+              array  was  created.  When  an  array is created as
+              /dev/mdX, then the minor number X is stored.
+
+       d\bde\bev\bvi\bic\bce\bes\bs=\b=
+              The value is  a  comma  separated  list  of  device
+              names.  Precisely  these  devices  will  be used to
+              assemble the array.  Note that the  devices  listed
+              there must also be listed on a DEVICE line.
+
+       l\ble\bev\bve\bel\bl=\b= The  value  is a raid level.  This is normally used
+              to identify an array, but is supported so that  the
+              output   of  m\bmd\bdc\bct\btl\bl  -\b--\b-e\bex\bxa\bam\bmi\bin\bne\be  -\b--\b-s\bsc\bca\ban\bn  can  be  use
+              directly in the configuration file.
+
+       d\bdi\bis\bsk\bks\bs=\b= The value is the number  of  disks  in  a  complete
+              active  array.   As  with l\ble\bev\bve\bel\bl=\b= this is mainly for
+              compatibility with the output  of  m\bmd\bdc\bct\btl\bl  -\b--\b-e\bex\bxa\bam\bmi\bin\bne\be
+              -\b--\b-s\bsc\bca\ban\bn.
+
+
+T\bTO\bOD\bDO\bO
+       Finish and document Follow mode.
+
+
+S\bSE\bEE\bE A\bAL\bLS\bSO\bO
+       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/peo-
+              ple/mingo/raid-patches
+
+       or
+
+              http://www.cse.unsw.edu.au/~neilb/patches/linux-
+              stable/
+
+       _\br_\ba_\bi_\bd_\bt_\ba_\bb(5), _\br_\ba_\bi_\bd_\b0_\br_\bu_\bn(8), _\br_\ba_\bi_\bd_\bs_\bt_\bo_\bp(8), _\bm_\bk_\br_\ba_\bi_\bd(8)
+
+
+
+                                                         mdctl(8)
diff --git a/util.c b/util.c
index ea30b31..17a7e87 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,7 +1,7 @@
 /*
  * mdctl - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -424,3 +424,16 @@ int calc_sb_csum(mdp_super_t *super)
        super->sb_csum = oldcsum;
        return csum;
 }
+
+char *human_size(long kbytes)
+{
+       static char buf[30];
+
+       if (kbytes < 2000)
+               buf[0]=0;
+       else if (kbytes < 2*1024*1024)
+               sprintf(buf, " (%d MiB)", kbytes>>10);
+       else
+               sprintf(buf, " (%d GiB)", kbytes>>20);
+       return buf;
+}