mdctl-0.5
This commit is contained in:
parent
0db17fcbde
commit
5282684628
433
Assemble.c
433
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
devlist = conf_get_devs(conffile);
|
||||
|
||||
if (subdevs == 0 && devlist == NULL) {
|
||||
fprintf(stderr, Name ": no devices given for %s\n", mddev);
|
||||
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 for each device */
|
||||
if (subdevs==0)
|
||||
devlist = conf_get_devs(conffile);
|
||||
|
||||
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 (compare_super(&first_super, &super)) {
|
||||
|
||||
if (ident->uuid_set &&
|
||||
(!havesuper || same_uuid(this_uuid, ident->uuid)==0)) {
|
||||
if (inargv || verbose)
|
||||
fprintf(stderr, Name ": superblock on %s doesn't match\n",
|
||||
fprintf(stderr, Name ": %s has wrong uuid.\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 (ident->super_minor >= 0 &&
|
||||
(!havesuper || ident->super_minor != super.md_minor)) {
|
||||
if (inargv || verbose)
|
||||
fprintf(stderr, Name ": %s has wrong super-minor.\n",
|
||||
devname);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we are this far, then we are commited to this device.
|
||||
* If the super_block doesn't exist, or doesn't match others,
|
||||
* then we cannot continue
|
||||
*/
|
||||
if (verbose)
|
||||
fprintf(stderr, Name ": %s is identified as a member of %s.\n",
|
||||
devname, mddev);
|
||||
|
||||
if (!havesuper) {
|
||||
fprintf(stderr, Name ": %s has no superblock - assembly aborted\n",
|
||||
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)) {
|
||||
|
|
199
Create.c
199
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;
|
||||
|
|
2
Detail.c
2
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;
|
||||
|
|
|
@ -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];
|
||||
|
|
4
Makefile
4
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
|
||||
|
|
2
Manage.c
2
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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
}
|
94
ReadMe.c
94
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 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 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 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"
|
||||
"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"
|
||||
"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, 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"
|
||||
"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"
|
||||
" 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"
|
||||
"If --scan is not given, then the config file will only be used\n"
|
||||
"to find uuids for md arrays.\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"
|
||||
"The format of the config file is:\n"
|
||||
" not yet documented\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"
|
||||
"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"
|
||||
;
|
||||
|
||||
|
|
159
TAGS
159
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
|
||||
int Examine(37,1261
|
||||
|
||||
Manage.c,79
|
||||
int Manage_ro(32,1135
|
||||
int Manage_runstop(36,1191
|
||||
int Manage_subdevs(40,1251
|
||||
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,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
|
||||
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,102
|
||||
char DefaultConfFile[43,1371
|
||||
mddev_uuid_t conf_get_uuids(45,1416
|
||||
mddev_dev_t conf_get_devs(50,1482
|
||||
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
|
||||
|
||||
mdctl.c,40
|
||||
int main(33,1153
|
||||
#define O(O131,3313
|
||||
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
|
||||
|
||||
util.c,212
|
||||
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
|
||||
|
|
54
TODO
54
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
|
405
config.c
405
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);
|
||||
int wsize = 100;
|
||||
int len = 0;
|
||||
int c;
|
||||
int quote;
|
||||
int wordfound = 0;
|
||||
char *word = malloc(wsize);
|
||||
|
||||
if (!word) abort();
|
||||
if (!word) abort();
|
||||
|
||||
while (wordfound==0) {
|
||||
/* at the end of a word.. */
|
||||
c = getc(file);
|
||||
if (c == '#')
|
||||
while (c != EOF && c != '\n')
|
||||
while (wordfound==0) {
|
||||
/* at the end of a word.. */
|
||||
c = getc(file);
|
||||
if (c == EOF) break;
|
||||
if (c == '\n') continue;
|
||||
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;
|
||||
if (c != ' ' && c != '\t' && ! allow_key) {
|
||||
ungetc(c, file);
|
||||
break;
|
||||
}
|
||||
c = getc(file);
|
||||
}
|
||||
/* 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;
|
||||
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);
|
||||
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;
|
||||
char *w;
|
||||
|
||||
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);
|
||||
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;
|
||||
FILE *f;
|
||||
char *line;
|
||||
|
||||
if (loaded) return;
|
||||
if (conffile == NULL)
|
||||
conffile = DefaultConfFile;
|
||||
if (loaded) return;
|
||||
if (conffile == NULL)
|
||||
conffile = DefaultConfFile;
|
||||
|
||||
f = fopen(conffile, "r");
|
||||
if (f ==NULL)
|
||||
return;
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
7
makedist
7
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
|
||||
|
|
2
md_p.h
2
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
|
||||
|
|
|
@ -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)
|
828
mdctl.c
828
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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('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;
|
||||
|
||||
/* 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('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('@','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);
|
||||
|
||||
}
|
||||
|
||||
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 (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(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);
|
||||
return mdfd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 = '@';
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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('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('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);
|
||||
|
||||
}
|
||||
|
||||
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...
|
||||
*
|
||||
* For @,B,C and A without -s, the first device listed must be an md device
|
||||
* we check that here and open it.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
exit(rv);
|
||||
}
|
||||
|
|
42
mdctl.h
42
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 *);
|
||||
|
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
12
testconfig
12
testconfig
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
DEV /dev/hda* /dev/sd?
|
||||
ARRAY /dev/md0 uuid=1234.5678.abcd.ef09:1234.5678.abcd.ef90
|
Loading…
Reference in New Issue