summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Assemble.c76
-rw-r--r--Build.c56
-rw-r--r--COPYING340
-rw-r--r--ChangeLog148
-rw-r--r--Create.c38
-rw-r--r--Detail.c147
-rw-r--r--Examine.c273
-rw-r--r--Makefile17
-rw-r--r--Manage.c50
-rw-r--r--Monitor.c20
-rw-r--r--ReadMe.c22
-rw-r--r--TODO38
-rw-r--r--config.c10
-rwxr-xr-xmakedist2
-rw-r--r--mdctl.8668
-rw-r--r--mdctl.c115
-rw-r--r--mdctl.h24
-rw-r--r--mdctl.man476
-rw-r--r--util.c15
19 files changed, 2014 insertions, 521 deletions
diff --git a/Assemble.c b/Assemble.c
index 272602e..1a4cb7e 100644
--- a/Assemble.c
+++ b/Assemble.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 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
--- 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
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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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
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.
+
diff --git a/Create.c b/Create.c
index c7d3597..55a9eca 100644
--- 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;
}
}
diff --git a/Detail.c b/Detail.c
index 026c3b3..7c2860d 100644
--- 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;
}
diff --git a/Examine.c b/Examine.c
index bb4290d..c8fbfbb 100644
--- 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
@@ -28,13 +28,14 @@
*/
#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;
}
diff --git a/Makefile b/Makefile
index 5d0b142..5513451 100644
--- 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
@@ -27,18 +27,29 @@
# 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
diff --git a/Manage.c b/Manage.c
index d8cc136..cab49bc 100644
--- 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;
}
}
diff --git a/Monitor.c b/Monitor.c
index 968e4b3..4f4aa0e 100644
--- 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) {
diff --git a/ReadMe.c b/ReadMe.c
index 49830cd..5128cbf 100644
--- 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
--- 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
diff --git a/config.c b/config.c
index 39ef56b..4fc381c 100644
--- 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);
diff --git a/makedist b/makedist
index 773e7f0..dae9949 100755
--- 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
--- a/mdctl.8
+++ b/mdctl.8
@@ -1,286 +1,574 @@
.\" -*- 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
--- 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
--- 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
index 0000000..682bc82
--- /dev/null
+++ b/mdctl.man
@@ -0,0 +1,476 @@
+mdctl(8) mdctl(8)
+
+
+
+NNAAMMEE
+ mdctl - manage MD devices _a_k_a Linux Software Raid.
+
+
+SSYYNNOOPPSSIISS
+ mmddccttll _[_m_o_d_e_] _<_r_a_i_d_d_e_v_i_c_e_> _[_o_p_t_i_o_n_s_] _<_s_u_b_d_e_v_i_c_e_s_>
+
+
+DDEESSCCRRIIPPTTIIOONN
+ 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 LLIINNEEAARR md devices, RRAAIIDD00 (strip-
+ ing), RRAAIIDD11 (mirroring), RRAAIIDD44 and RRAAIIDD55..
+
+ Recent kernels (2002) also support a mode known as MMUULLTTII--
+ PPAATTHH. mmddccttll does not support MULTIPATH as yet.
+
+ mmddccttll 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 rraaiiddttoooollss packages. The key differences
+ between mmddccttll and rraaiiddttoooollss are:
+
+ +o mmddccttll is a single program and not a collection of pro-
+ grams.
+
+ +o mmddccttll can perform (almost) all of its functions with-
+ out having a configuration file. Also mdctl helps
+ with management of the configuration file.
+
+ +o mmddccttll can provide information about your arrays
+ (through Detail and Examine) that rraaiiddttoooollss cannot.
+
+ +o rraaiiddttoooollss can manage MULTIPATH devices which mmddccttll
+ cannot yet manage.
+
+
+MMOODDEESS
+ mdctl has 7 major modes of operation:
+
+ AAsssseemmbbllee
+ Assemble the parts of a previously created array
+ into an active array. Components can be explicitly
+ given or can be searched for. mmddccttll checks that
+ the components do form a bona fide array, and can,
+ on request, fiddle superblock information so as to
+ assemble a faulty array.
+
+
+ BBuuiilldd Build a legacy array without per-device
+ superblocks.
+
+
+ CCrreeaattee Create a new array with per-device superblocks.
+
+
+ DDeettaaiill 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.
+
+
+ EExxaammiinnee
+ 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
+ mmddccttll..ccoonnff configuration file.
+
+
+ FFoollllooww oorr MMoonniittoorr
+ Monitor one or more md devices and act on any state
+ changes.
+
+
+ MMaannaaggee This is for odd bits an pieces like hotadd,
+ hotremove, setfaulty, stop, readonly, readwrite.
+
+
+OOPPTTIIOONNSS
+ Available options are:
+
+
+ --AA, ----aasssseemmbbllee
+ Assemble an existing array.
+
+
+ --BB, ----bbuuiilldd
+ Build a legacy array without superblocks.
+
+
+ --CC, ----ccrreeaattee
+ Create a new array.
+
+
+ --DD, ----ddeettaaiill
+ Print detail of one or more md devices.
+
+
+ --EE, ----eexxaammiinnee
+ Print content of md superblock on device(s).
+
+
+ --FF, ----ffoollllooww, ----mmoonniittoorr
+ Select MMoonniittoorr mode.
+
+
+ --hh, ----hheellpp
+ Display help message or, after above option, mode
+ specific help message.
+
+
+ --VV, ----vveerrssiioonn
+ Print version information for mdctl.
+
+
+ --vv, ----vveerrbboossee
+ Be more verbose about what is happening.
+
+
+ --bb, ----bbrriieeff
+ Be less verbose. This is used with ----ddeettaaiill and
+ ----eexxaammiinnee.
+
+
+FFoorr ccrreeaattee oorr bbuuiilldd::
+ --cc, ----cchhuunnkk==
+ Specify chunk size of kibibytes. The default is
+ 64.
+
+
+ ----rroouunnddiinngg==
+ Specify rounding factor for linear array (==chunk
+ size)
+
+
+ --ll, ----lleevveell==
+ 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.
+
+
+ --pp, ----ppaarriittyy==
+ Set raid5 parity algorithm. Options are:
+ {left,right}-{,a}symmetric, la, ra, ls, rs. The
+ default is left-symmetric.
+
+
+ ----llaayyoouutt==
+ same as --parity
+
+
+ --nn, ----rraaiidd--ddiisskkss==
+ number of active devices in array.
+
+
+ --xx, ----ssppaarree--ddiisskkss==
+ number of spare (eXtra) disks in initial array.
+ Spares can be added and removed later.
+
+
+ --zz, ----ssiizzee==
+ 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.
+
+
+FFoorr aasssseemmbbllee::
+ --uu, ----uuuuiidd==
+ uuid of array to assemble. Devices which don't have
+ this uuid are excluded
+
+
+ --mm, ----ssuuppeerr--mmiinnoorr==
+ 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.
+
+
+ --cc, ----ccoonnffiigg==
+ config file. Default is //eettcc//mmddccttll..ccoonnff.
+
+
+ --ss, ----ssccaann
+ scan config file for missing information
+
+
+ --ff, ----ffoorrccee
+ Assemble the array even if some superblocks appear
+ out-of-date
+
+
+ --RR, ----rruunn
+ 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 ----ssccaann is not
+ used, then the array will be assembled but not
+ started. With ----rruunn an attempt will be made to
+ start it anyway.
+
+
+GGeenneerraall mmaannaaggeemmeenntt
+ --aa, ----aadddd
+ hotadd listed devices.
+
+
+ --rr, ----rreemmoovvee
+ remove listed devices. The must not be active.
+ i.e. they should be failed or spare devices.
+
+
+ --ff, ----ffaaiill
+ mark listed devices as faulty.
+
+
+ ----sseett--ffaauullttyy
+ same as --fail.
+
+
+ --RR, ----rruunn
+ start a partially built array.
+
+
+ --SS, ----ssttoopp
+ deactivate array, releasing all resources.
+
+
+ --oo, ----rreeaaddoonnllyy
+ mark array as readonly.
+
+
+ --ww, ----rreeaaddwwrriittee
+ mark array as readwrite.
+
+
+
+AASSSSEEMMBBLLYY MMOODDEE
+ Usage: mmddccttll ----aasssseemmbbllee _d_e_v_i_c_e _o_p_t_i_o_n_s_._._.
+
+ Usage: mmddccttll ----aasssseemmbbllee ----ssccaann _o_p_t_i_o_n_s_._._.
+
+
+ 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 ----ssccaann 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 ----uuuuiidd option, with the
+ ----ssuuppeerr--mmiinnoorr 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 ----aasssseemmbbllee 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
+ ----ccoonnffiigg or requested with ----ssccaann.. In the later case,
+ //eettcc//mmddccttll..ccoonnff is used.
+
+ If ----ssccaann 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 ----ssccaann 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 ----rruunn flag.
+
+
+
+BBUUIILLDD MMOODDEE
+ Usage: mmddccttll ----bbuuiilldd _d_e_v_i_c_e ----cchhuunnkk==_X ----lleevveell==_Y ----rraaiidd--
+ ddiisskkss==_Z _d_e_v_i_c_e_s
+
+
+ This usage is similar to ----ccrreeaattee. 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.
+
+
+CCRREEAATTEE MMOODDEE
+ Usage: mmddccttll ----ccrreeaattee _d_e_v_i_c_e ----cchhuunnkk==_X ----lleevveell==_Y
+ ----rraaiidd--ddiisskkss==_Z _d_e_v_i_c_e_s
+
+
+ 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 ----rruunn can override
+ this caution.
+
+
+ The General Management options that are valid with --cre-
+ ate are:
+
+ ----rruunn insist of running the array even if some devices
+ look like they might be in use.
+
+
+ ----rreeaaddoonnllyy
+ start the array readonly - not supported yet.
+
+
+DDEETTAAIILL MMOODDEE
+ Usage: mmddccttll ----ddeettaaiill [----bbrriieeff] _d_e_v_i_c_e _._._.
+
+
+ This usage sill print out the details of the given array
+ including a list of component devices. To determine names
+ for the devices, mmddccttll searches //ddeevv for device files with
+ the right major and minor numbers.
+
+ With ----bbrriieeff mmddccttll prints a single line that identifies
+ the level, number of disks, and UUID of the array. This
+ line is suitable for inclusion in //eettcc//mmddccttll..ccoonnff.
+
+
+EEXXAAMMIINNEE MMOODDEE
+ Usage: mmddccttll ----eexxaammiinnee [----ssccaann] [----bbrriieeff] _d_e_v_i_c_e _._._.
+
+ 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 ----ssccaann is used, the no devices should be listed, and
+ the complete set of devices identified in the configura-
+ tion file are checked. ----ssccaann implies ----bbrriieeff but this
+ implication can be countered by specifying ----vveerrbboossee.
+
+ With ----bbrriieeff mmddccttll 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 NNOOTT be used blindly.
+ Often the array description that you want in the configu-
+ ration file is much less specific than that given by mmddccttll
+ --BBss. For example, you normally do not want to list the
+ devices, particularly if they are SCSI devices.
+
+
+
+FFIILLEESS
+ //pprroocc//mmddssttaatt
+ If you're using the //pprroocc filesystem, //pprroocc//mmddssttaatt gives
+ you informations about md devices status. This file is
+ not currently used by mmddccttll.
+
+
+ //eettcc//mmddccttll..ccoonnff
+ 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 _g_l_o_b(7)). These list all the
+ devices that mmddccttll 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:
+
+
+ uuuuiidd== The value should be a 128 bit uuid in hexadecimal,
+ with punctuation interspersed if desired. This
+ must match the uuid stored in the superblock.
+
+ ssuuppeerr--mmiinnoorr==
+ 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.
+
+ ddeevviicceess==
+ 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.
+
+ lleevveell== The value is a raid level. This is normally used
+ to identify an array, but is supported so that the
+ output of mmddccttll ----eexxaammiinnee ----ssccaann can be use
+ directly in the configuration file.
+
+ ddiisskkss== The value is the number of disks in a complete
+ active array. As with lleevveell== this is mainly for
+ compatibility with the output of mmddccttll ----eexxaammiinnee
+ ----ssccaann.
+
+
+TTOODDOO
+ Finish and document Follow mode.
+
+
+SSEEEE AALLSSOO
+ 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/
+
+ _r_a_i_d_t_a_b(5), _r_a_i_d_0_r_u_n(8), _r_a_i_d_s_t_o_p(8), _m_k_r_a_i_d(8)
+
+
+
+ mdctl(8)
diff --git a/util.c b/util.c
index ea30b31..17a7e87 100644
--- 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;
+}