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