mdctl-v0.3

This commit is contained in:
Neil Brown 2001-06-14 05:33:53 +00:00
parent 64c4757e27
commit 682c705194
13 changed files with 808 additions and 120 deletions

View File

@ -107,7 +107,7 @@ int Assemble(char *mddev, int mdfd,
int most_recent = 0;
if (!mddev && !scan) {
fputs("mdctl: internal error - Assemble called with no devie or scan\n", stderr);
fputs(Name ": internal error - Assemble called with no devie or scan\n", stderr);
return 1;
}
if (!mddev) {
@ -115,7 +115,7 @@ int Assemble(char *mddev, int mdfd,
int found = 0;
device_list = conf_get_uuids(conffile);
if (!device_list) {
fprintf(stderr, "mdctl: No devices found in config file\n");
fprintf(stderr, Name ": No devices found in config file\n");
return 1;
}
while (device_list) {
@ -123,7 +123,7 @@ int Assemble(char *mddev, int mdfd,
mdfd = open(device_list->devname, O_RDONLY, 0);
if (mdfd < 0) {
fprintf(stderr,
"mdctl: error opening %s: %s\n",
Name ": error opening %s: %s\n",
device_list->devname,
strerror(errno));
continue;
@ -140,7 +140,7 @@ int Assemble(char *mddev, int mdfd,
}
if (found)
return 0;
fprintf(stderr,"mdctl: Did not successful Assemble any devices\n");
fprintf(stderr,Name ": Did not successful Assemble any devices\n");
return 1;
}
@ -149,19 +149,19 @@ int Assemble(char *mddev, int mdfd,
*/
vers = md_get_version(mdfd);
if (vers <= 0) {
fprintf(stderr, "mdctl: %s appears not to be an md device.\n");
fprintf(stderr, Name ": %s appears not to be an md device.\n");
return 1;
}
if (vers < (90<<8)) {
fprintf(stderr, "mdctl: Assemble requires driver version 0.90.0 or later.\n"
if (vers < 9000) {
fprintf(stderr, Name ": Assemble requires driver version 0.90.0 or later.\n"
" Upgrade your kernel or try --Build\n");
return 1;
}
if (get_linux_version() < 0x020400)
if (get_linux_version() < 2004000)
old_linux = 1;
if (ioctl(mdfd, GET_ARRAY_INFO, &array)>=0) {
fprintf(stderr, "mdctl: device %s already active - cannot assemble it\n",
fprintf(stderr, Name ": device %s already active - cannot assemble it\n",
mddev);
return 1;
}
@ -179,7 +179,7 @@ int Assemble(char *mddev, int mdfd,
device_list = device_list->next;
if (!device_list) {
fprintf(stderr, "mdctl: --scan set and no uuid found for %s in config file.\n",
fprintf(stderr, Name ": --scan set and no uuid found for %s in config file.\n",
mddev);
return 1;
}
@ -198,7 +198,7 @@ int Assemble(char *mddev, int mdfd,
devlist = conf_get_devs(conffile);
if (subdevs == 0 && devlist == NULL) {
fprintf(stderr, "mdctl: no devices given for %s\n", mddev);
fprintf(stderr, Name ": no devices given for %s\n", mddev);
return 1;
}
/* now for each device */
@ -225,26 +225,26 @@ int Assemble(char *mddev, int mdfd,
dfd = open(devname, O_RDONLY, 0);
if (dfd < 0) {
if (inargv || verbose)
fprintf(stderr, "mdctl: cannot open device %s: %s\n",
fprintf(stderr, Name ": cannot open device %s: %s\n",
devname, strerror(errno));
continue;
}
if (fstat(dfd, &stb)< 0) {
/* Impossible! */
fprintf(stderr, "mdctl: fstat failed for %s: %s\n",
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, "mdctl: %d is not a block device.\n",
fprintf(stderr, Name ": %d is not a block device.\n",
devname);
close(dfd);
continue;
}
if (load_super(dfd, &super)) {
if (inargv || verbose)
fprintf( stderr, "mdctl: no RAID superblock on %s\n",
fprintf( stderr, Name ": no RAID superblock on %s\n",
devname);
close(dfd);
continue;
@ -252,7 +252,7 @@ int Assemble(char *mddev, int mdfd,
close(dfd);
if (compare_super(&first_super, &super)) {
if (inargv || verbose)
fprintf(stderr, "mdctl: superblock on %s doesn't match\n",
fprintf(stderr, Name ": superblock on %s doesn't match\n",
devname);
continue;
}
@ -260,7 +260,7 @@ int Assemble(char *mddev, int mdfd,
uuid_from_super(this_uuid, &first_super);
if (!same_uuid(this_uuid, uuid)) {
if (inargv || verbose)
fprintf(stderr, "mdctl: %s has wrong uuid.\n",
fprintf(stderr, Name ": %s has wrong uuid.\n",
devname);
continue;
}
@ -271,7 +271,7 @@ int Assemble(char *mddev, int mdfd,
/* Ok, this one is at least worth considering */
if (devcnt >= MD_SB_DISKS) {
fprintf(stderr, "mdctl: ouch - too many devices appear to be in this array. Ignoring %s\n",
fprintf(stderr, Name ": ouch - too many devices appear to be in this array. Ignoring %s\n",
devname);
continue;
}
@ -295,7 +295,7 @@ int Assemble(char *mddev, int mdfd,
}
if (devcnt == 0) {
fprintf(stderr, "mdctl: no devices found for %s\n",
fprintf(stderr, Name ": no devices found for %s\n",
mddev);
return 1;
}
@ -317,14 +317,14 @@ int Assemble(char *mddev, int mdfd,
* not up-to-date, update the superblock
* and add it.
*/
fprintf(stderr,"NoImplementedYet\n");
fprintf(stderr,"NotImplementedYet\n");
/* FIXME */
exit(2);
}
/* Almost ready to actually *do* something */
if (!old_linux) {
if (ioctl(mdfd, SET_ARRAY_INFO, NULL) != 0) {
fprintf(stderr, "mdctl: SET_ARRAY_INFO failed for %s: %s\n",
fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n",
mddev, strerror(errno));
return 1;
}
@ -337,14 +337,14 @@ int Assemble(char *mddev, int mdfd,
disk.major = devices[j].major;
disk.minor = devices[j].minor;
if (ioctl(mdfd, ADD_NEW_DISK, &disk)!=0) {
fprintf(stderr, "mdctl: failed to add %s to %s: %s\n",
fprintf(stderr, Name ": failed to add %s to %s: %s\n",
devices[j].devname,
mddev,
strerror(errno));
} else
okcnt--;
} else if (verbose)
fprintf(stderr, "mdctl: no uptodate device for slot %d of %s\n",
fprintf(stderr, Name ": no uptodate device for slot %d of %s\n",
i, mddev);
}
if (runstop == 1 ||
@ -352,7 +352,7 @@ int Assemble(char *mddev, int mdfd,
enough(first_super.level, first_super.raid_disks, okcnt))) {
if (ioctl(mdfd, RUN_ARRAY, NULL)==0)
return 0;
fprintf(stderr, "mdctl: failed to RUN_ARRAY %s: %s\n",
fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n",
mddev, strerror(errno));
return 1;
}

211
Create.c
View File

@ -28,10 +28,217 @@
*/
#include "mdctl.h"
#include "md_u.h"
#include "md_p.h"
int Create(char *mddev, int mdfd,
int chunk, int level, int layout, int raiddisks, int sparedisks,
int chunk, int level, int layout, int size, int raiddisks, int sparedisks,
int subdevs, char *subdev[],
int runstop)
int runstop, int verbose)
{
/*
* Create a new raid array.
*
* First check that necessary details are available
* (i.e. level, raid-disks)
*
* Then check each disk to see what might be on it
* and report anything interesting.
*
* If anything looks odd, and runstop not set,
* abort.
*
* SET_ARRAY_INFO and ADD_NEW_DISK, and
* if runstop==run, or raiddisks diskswere used,
* RUN_ARRAY
*/
int minsize, maxsize;
int maxdisc= -1, mindisc = -1;
int i;
int fail=0, warn=0;
mdu_array_info_t array;
mdu_param_t param;
if (md_get_version(mdfd) < 9000) {
fprintf(stderr, Name ": Create requires md driver verison 0.90.0 or later\n");
return 1;
}
if (level == -10) {
fprintf(stderr,
Name ": a RAID level is needed to create an array.\n");
return 1;
}
if (raiddisks < 1) {
fprintf(stderr,
Name ": a number of --raid-disks must be given to create an array\n");
return 1;
}
if (raiddisks+sparedisks > MD_SB_DISKS) {
fprintf(stderr,
Name ": too many discs requested: %d+%d > %d\n",
raiddisks, sparedisks, MD_SB_DISKS);
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;
}
/* now set some defaults */
if (layout == -1)
switch(level) {
default: /* no layout */
layout = 0;
break;
case 5:
layout = map_name(r5layout, "default");
if (verbose)
fprintf(stderr,
Name ": layout defaults to %s\n", map_num(r5layout, layout));
break;
}
if (chunk == 0) {
chunk = 64;
if (verbose)
fprintf(stderr, Name ": chunk size defaults to 64K\n");
}
/* now look at the subdevs */
for (i=0; i<subdevs; i++) {
char *dname = subdev[i];
int dsize, freesize;
int fd = open(dname, O_RDONLY, 0);
if (fd <0 ) {
fprintf(stderr, Name ": Cannot open %s: %s\n",
dname, strerror(errno));
fail=1;
continue;
}
if (ioctl(fd, BLKGETSIZE, &dsize)) {
fprintf(stderr, Name ": Cannot get size of %s: %s\n",
dname, strerror(errno));
fail = 1;
close(fd);
continue;
}
if (dsize < MD_RESERVED_SECTORS*2) {
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;
}
if (maxdisc< 0 || (maxdisc>=0 && freesize > maxsize)) {
maxdisc = i;
maxsize = freesize;
}
if (mindisc < 0 || (mindisc >=0 && freesize < minsize)) {
mindisc = i;
minsize = freesize;
}
warn |= check_ext2(fd, dname);
warn |= check_reiser(fd, dname);
warn |= check_raid(fd, dname);
close(fd);
}
if (fail) {
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 ((maxsize-size)*100 > maxsize) {
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;
}
} else {
if (verbose)
fprintf(stderr, Name ": creation continuing despite oddities due to --run\n");
}
}
/* Ok, lets try some ioctls */
array.level = level;
array.size = size;
array.nr_disks = raiddisks+sparedisks;
array.raid_disks = raiddisks;
array.md_minor = 0;
array.not_persistent = 0;
array.state = 0; /* not clean, but no errors */
array.active_disks=0;
array.working_disks=0;
array.spare_disks=0;
array.failed_disks=0;
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;
}
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;
disk.raid_disk = i;
disk.state = 6; /* active and in sync */
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\b",
subdev[i], strerror(errno));
return 1;
}
}
/* param is not actually used */
if (runstop == 1 || subdevs >= raiddisks) {
if (ioctl(mdfd, RUN_ARRAY, &param)) {
fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
strerror(errno));
return 1;
}
fprintf(stderr, Name ": array %s started.\n", mddev);
} else {
fprintf(stderr, Name ": not starting array - not enough discs.\n");
}
return 0;
}

View File

@ -43,31 +43,32 @@ int Detail(char *dev)
mdu_array_info_t array;
int d;
time_t atime;
char *c;
if (fd < 0) {
fprintf(stderr, "mdctl: cannot open %s: %s\n",
fprintf(stderr, Name ": cannot open %s: %s\n",
dev, strerror(errno));
return 1;
}
vers = md_get_version(fd);
if (vers < 0) {
fprintf(stderr, "mdctl: %s does not appear to be an md device\n",
fprintf(stderr, Name ": %s does not appear to be an md device\n",
dev);
close(fd);
return 1;
}
if (vers < (90<<8)) {
fprintf(stderr, "mdctl: cannot get detail for md device %s: driver version too old.\n",
if (vers < 9000) {
fprintf(stderr, Name ": cannot get detail for md device %s: driver version too old.\n",
dev);
close(fd);
return 1;
}
if (ioctl(fd, GET_ARRAY_INFO, &array)<0) {
if (errno == ENODEV)
fprintf(stderr, "mdctl: md device %s does not appear to be active.\n",
fprintf(stderr, Name ": md device %s does not appear to be active.\n",
dev);
else
fprintf(stderr, "mdctl: cannot get array detail for %s: %s\n",
fprintf(stderr, Name ": cannot get array detail for %s: %s\n",
dev, strerror(errno));
close(fd);
return 1;
@ -78,7 +79,8 @@ int Detail(char *dev)
array.major_version, array.minor_version, array.patch_version);
atime = array.ctime;
printf(" Creation Time : %.24s\n", ctime(&atime));
printf(" Raid Level : %d\n", array.level);
c = map_num(pers, array.level);
printf(" Raid Level : %s\n", c?c:"-unknown-");
printf(" Size : %d\n", array.size);
printf(" Raid Disks : %d\n", array.raid_disks);
printf(" Total Disks : %d\n", array.nr_disks);
@ -96,7 +98,10 @@ int Detail(char *dev)
printf(" Failed Drives : %d\n", array.failed_disks);
printf(" Spare Drives : %d\n", array.spare_disks);
printf("\n");
printf(" Layout : %d\n", array.layout);
if (array.level == 5) {
c = map_num(r5layout, array.layout);
printf(" Layout : %s\n", c?c:"-unknown-");
}
printf(" Chunk Size : %dK\n", array.chunk_size/1024);
printf("\n");
printf(" Number Major Minor RaidDisk State\n");
@ -104,7 +109,7 @@ int Detail(char *dev)
mdu_disk_info_t disk;
disk.number = d;
if (ioctl(fd, GET_DISK_INFO, &disk) < 0) {
fprintf(stderr, "mdctl: cannot get disk detail for disk %d: %s\n",
fprintf(stderr, Name ": cannot get disk detail for disk %d: %s\n",
d, strerror(errno));
continue;
}

View File

@ -51,10 +51,11 @@ int Examine(char *dev)
time_t atime;
mdp_super_t super;
int d;
char *c;
int rv;
if (fd < 0) {
fprintf(stderr,"mdctl: cannot open %s: %s\n",
fprintf(stderr,Name ": cannot open %s: %s\n",
dev, strerror(errno));
return 1;
}
@ -63,30 +64,30 @@ int Examine(char *dev)
close(fd);
switch(rv) {
case 1:
fprintf(stderr, "mdctl: cannot find device size for %s: %s\n",
fprintf(stderr, Name ": cannot find device size for %s: %s\n",
dev, strerror(errno));
return 1;
case 2:
/* fprintf(stderr, "mdctl: %s is too small for md: size is %ld sectors\n",
/* fprintf(stderr, Name ": %s is too small for md: size is %ld sectors\n",
dev, size);
*/
fprintf(stderr, "mdctl: %s is too small for md\n",
fprintf(stderr, Name ": %s is too small for md\n",
dev);
return 1;
case 3:
fprintf(stderr, "mdctl: Cannot seek to superblock on %s: %s\n",
fprintf(stderr, Name ": Cannot seek to superblock on %s: %s\n",
dev, strerror(errno));
return 1;
case 4:
fprintf(stderr, "mdctl: Cannot read superblock on %s\n",
fprintf(stderr, Name ": Cannot read superblock on %s\n",
dev);
return 1;
case 5:
fprintf(stderr, "mdctl: No super block found on %s (Expected magic %08x, got %08x)\n",
fprintf(stderr, Name ": No super block found on %s (Expected magic %08x, got %08x)\n",
dev, MD_SB_MAGIC, super.md_magic);
return 1;
case 6:
fprintf(stderr, "mdctl: Cannot interpret superblock on %s - version is %d\n",
fprintf(stderr, Name ": Cannot interpret superblock on %s - version is %d\n",
dev, super.major_version);
return 1;
}
@ -104,7 +105,8 @@ int Examine(char *dev)
atime = super.ctime;
printf(" Creation Time : %.24s\n", ctime(&atime));
printf(" Raid Level : %d\n", super.level);
c=map_num(pers, super.level);
printf(" Raid Level : %s\n", c?c:"-unknown-");
printf(" Size : %d\n", super.size);
printf(" Raid Disks : %d\n", super.raid_disks);
printf(" Total Disks : %d\n", super.nr_disks);
@ -122,7 +124,10 @@ int Examine(char *dev)
printf(" - checksum not checked yet - \n");
printf(" Events : %d.%d\n", super.events_hi, super.events_lo);
printf("\n");
printf(" Layout : %d\n", super.layout);
if (super.level == 5) {
c = map_num(r5layout, super.layout);
printf(" Layout : %s\n", c?c:"-unknown-");
}
printf(" Chunk Size : %dK\n", super.chunk_size/1024);
printf("\n");
printf(" Number Major Minor RaidDisk State\n");

View File

@ -42,3 +42,6 @@ clean :
dist : clean
./makedist
TAGS :
etags *.h *.c

160
Manage.c
View File

@ -28,16 +28,174 @@
*/
#include "mdctl.h"
#include "md_u.h"
#include "md_p.h"
int Manage_ro(char *devname, int fd, int readonly)
{
/* switch to readonly or rw
*
* requires >= 0.90.0
* first check that array is runing
* use RESTART_ARRAY_RW or STOP_ARRAY_RO
*
*/
mdu_array_info_t array;
if (md_get_version(fd) < 9000) {
fprintf(stderr, Name ": need md driver version 0.90.0 or later\n");
return 1;
}
if (ioctl(fd, GET_ARRAY_INFO, &array)) {
fprintf(stderr, Name ": %s does not appear to be active.\n",
devname);
return 1;
}
if (readonly>0) {
if (ioctl(fd, STOP_ARRAY_RO, NULL)) {
fprintf(stderr, Name ": failed to set readonly for %s: %s\n",
devname, strerror(errno));
return 1;
}
} else if (readonly < 0) {
if (ioctl(fd, RESTART_ARRAY_RW, NULL)) {
fprintf(stderr, Name ": fail to re writable for %s: %s\n",
devname, strerror(errno));
return 1;
}
}
return 0;
}
int Manage_runstop(char *devname, int fd, int runstop)
{
/* Run or stop the array. array must already be configured
* required >= 0.90.0
*/
mdu_array_info_t array;
mdu_param_t param; /* unused */
if (md_get_version(fd) < 9000) {
fprintf(stderr, Name ": need md driver version 0.90.0 or later\n");
return 1;
}
if (ioctl(fd, GET_ARRAY_INFO, &array)) {
fprintf(stderr, Name ": %s does not appear to be active.\n",
devname);
return 1;
}
if (runstop>0) {
if (ioctl(fd, RUN_ARRAY, &param)) {
fprintf(stderr, Name ": failed to run array %s: %s\n",
devname, strerror(errno));
return 1;
}
} else if (runstop < 0){
if (ioctl(fd, STOP_ARRAY, NULL)) {
fprintf(stderr, Name ": fail to re writable for %s: %s\n",
devname, strerror(errno));
return 1;
}
}
return 0;
}
int Manage_subdevs(char *devname, int fd,
int devcnt, char *devnames[], int devmodes[])
{
{
/* do something to each dev.
* devmode can be
* 'a' - add the device
* try HOT_ADD_DISK
* If that fails EINVAL, try ADD_NEW_DISK
* 'r' - remove the device HOT_REMOVE_DISK
* 'f' - set the device faulty SET_DISK_FAULTY
*/
mdu_array_info_t array;
mdu_disk_info_t disc;
struct stat stb;
int i,j;
if (ioctl(fd, GET_ARRAY_INFO, &array)) {
fprintf(stderr, Name ": cannot get array info for %s\n",
devname);
return 1;
}
for (i=0 ; i<devcnt; i++) {
if (stat(devnames[i], &stb)) {
fprintf(stderr, Name ": cannot find %s: %s\n",
devnames[i], strerror(errno));
return 1;
}
if ((stb.st_mode & S_IFMT) != S_IFBLK) {
fprintf(stderr, Name ": %s is not a block device.\n",
devnames[i]);
return 1;
}
switch(devmodes[i]){
default:
fprintf(stderr, Name ": internal error - devmode[%d]=%d\n",
i, devmodes[i]);
return 1;
case 'a':
/* add the device - hot or cold */
if (ioctl(fd, HOT_ADD_DISK, stb.st_rdev)==0) {
fprintf(stderr, Name ": hot added %s\n",
devnames[i]);
continue;
}
/* try ADD_NEW_DISK.
* we might be creating, we might be assembling,
* it is hard to tell.
* set up number/raid_disk/state just
* in case
*/
for (j=0; j<array.nr_disks; j++) {
if (ioctl(fd, GET_DISK_INFO, &disc))
break;
if (disc.major==0 && disc.minor==0)
break;
if (disc.state & 8) /* removed */
break;
}
disc.number =j;
disc.raid_disk = j;
disc.state = 0;
disc.major = MAJOR(stb.st_rdev);
disc.minor = MINOR(stb.st_rdev);
if (ioctl(fd,ADD_NEW_DISK, &disc)) {
fprintf(stderr, Name ": add new disk failed for %s: %s\n",
devnames[i], strerror(errno));
return 1;
}
fprintf(stderr, Name ": added %s\n", devnames[i]);
break;
case 'r':
/* hot remove */
/* FIXME check that is is a current member */
if (ioctl(fd, HOT_REMOVE_DISK, stb.st_rdev)) {
fprintf(stderr, Name ": hot remove failed for %s: %s\n",
devnames[i], strerror(errno));
return 1;
}
fprintf(stderr, Name ": hot removed %s\n", devnames[i]);
break;
case 'f': /* set faulty */
/* FIXME check current member */
if (ioctl(fd, SET_DISK_FAULTY, stb.st_rdev)) {
fprintf(stderr, Name ": set disk faulty failed for %s: %s\n",
devnames[i], strerror(errno));
return 1;
}
fprintf(stderr, Name ": set %s faulty in %s\n",
devnames[i], devname);
break;
}
}
return 0;
}

View File

@ -29,7 +29,7 @@
#include "mdctl.h"
char Version[] = "mdctl - v0.2 - 06 June 2001\n";
char Version[] = Name " - v0.3 - 14 June 2001\n";
/*
* File: ReadMe.c
*
@ -78,7 +78,7 @@ char Version[] = "mdctl - v0.2 - 06 June 2001\n";
* command, subsequent Manage commands can finish the job.
*/
char short_options[]="-ABCDEhVvc:l:p:n:x:u:c:sarfRSow";
char short_options[]="-ABCDEhVvc:l:p:n:x:u:c:z:sarfRSow";
struct option long_options[] = {
{"manage", 0, 0, '@'},
{"assemble", 0, 0, 'A'},
@ -99,6 +99,7 @@ struct option long_options[] = {
{"layout", 1, 0, 'p'},
{"raid-disks",1, 0, 'n'},
{"spare-disks",1,0, 'x'},
{"size" ,1, 0, 'z'},
/* For assemble */
{"uuid", 1, 0, 'u'},
@ -157,6 +158,7 @@ char Help[] =
" --layout= : same as --parity\n"
" --raid-disks= -n : number of active devices in array\n"
" --spare-disks= -x : number of spares (eXtras) to allow space for\n"
" --size= -z : Size (in K) of each drive in RAID1/4/5 - optional\n"
"\n"
" For assemble:\n"
" --uuid= -u : uuid of array to assemble. Devices which don't\n"
@ -192,6 +194,11 @@ char Help_create[] =
" be run, though the presence of a '--run' can override this\n"
" caution.\n"
"\n"
" If the --size option is given, it is not necessary to list any subdevices\n"
" in this command. They can be added later, before a --run.\n"
" If no --size is given, the apparent size of the smallest drive given\n"
" is used.\n"
"\n"
" The General management options that are valid with --create are:\n"
" --run : insist of running the array even if not all devices\n"
" are present or some look odd.\n"
@ -244,3 +251,35 @@ char Help_assemble[] =
" not yet documented\n"
"\n"
;
/* name/number mappings */
mapping_t r5layout[] = {
{ "left_asymmetric", 0},
{ "right_asymmetric", 1},
{ "left_symmetric", 2},
{ "right_symmetric", 3},
{ "default", 2},
{ "la", 0},
{ "ra", 1},
{ "ls", 2},
{ "rs", 3},
{ NULL, 0}
};
mapping_t pers[] = {
{ "linear", -1},
{ "raid0", 0},
{ "0", 0},
{ "stripe", 0},
{ "raid1", 1},
{ "1", 1},
{ "mirror", 1},
{ "raid4", 4},
{ "4", 4},
{ "raid5", 5},
{ "5", 5},
{ NULL, 0}
};

140
TAGS Normal file
View File

@ -0,0 +1,140 @@
md_p.h,1316
#define _MD_P_H16,582
#define MD_RESERVED_BYTES 44,1414
#define MD_RESERVED_SECTORS 45,1453
#define MD_RESERVED_BLOCKS 46,1508
#define MD_NEW_SIZE_SECTORS(MD_NEW_SIZE_SECTORS48,1570
#define MD_NEW_SIZE_BLOCKS(MD_NEW_SIZE_BLOCKS49,1659
#define MD_SB_BYTES 51,1746
#define MD_SB_WORDS 52,1773
#define MD_SB_BLOCKS 53,1813
#define MD_SB_SECTORS 54,1863
#define MD_SB_GENERIC_OFFSET 59,1960
#define MD_SB_PERSONALITY_OFFSET 60,1992
#define MD_SB_DISKS_OFFSET 61,2028
#define MD_SB_DESCRIPTOR_OFFSET 62,2060
#define MD_SB_GENERIC_CONSTANT_WORDS 64,2098
#define MD_SB_GENERIC_STATE_WORDS 65,2138
#define MD_SB_GENERIC_WORDS 66,2175
#define MD_SB_PERSONALITY_WORDS 67,2263
#define MD_SB_DESCRIPTOR_WORDS 68,2299
#define MD_SB_DISKS 69,2334
#define MD_SB_DISKS_WORDS 70,2359
#define MD_SB_RESERVED_WORDS 71,2423
#define MD_SB_EQUAL_WORDS 72,2553
#define MD_DISK_FAULTY 77,2691
#define MD_DISK_ACTIVE 78,2752
#define MD_DISK_SYNC 79,2814
#define MD_DISK_REMOVED 80,2878
typedef struct mdp_device_descriptor_s mdp_device_descriptor_s82,2946
} mdp_disk_t;mdp_disk_t89,3310
#define MD_SB_MAGIC 91,3325
#define MD_SB_CLEAN 96,3390
#define MD_SB_ERRORS 97,3413
typedef struct mdp_superblock_s mdp_superblock_s99,3438
} mdp_super_t;mdp_super_t164,5820
static inline __u64 md_event(166,5836
md_u.h,1118
#define _MD_U_H16,590
#define RAID_VERSION 21,634
#define GET_ARRAY_INFO 22,693
#define GET_DISK_INFO 23,757
#define PRINT_RAID_DEBUG 24,819
#define RAID_AUTORUN 25,865
#define CLEAR_ARRAY 28,929
#define ADD_NEW_DISK 29,971
#define HOT_REMOVE_DISK 30,1032
#define SET_ARRAY_INFO 31,1078
#define SET_DISK_INFO 32,1142
#define WRITE_RAID_INFO 33,1186
#define UNPROTECT_ARRAY 34,1232
#define PROTECT_ARRAY 35,1278
#define HOT_ADD_DISK 36,1322
#define SET_DISK_FAULTY 37,1365
#define RUN_ARRAY 40,1424
#define START_ARRAY 41,1478
#define STOP_ARRAY 42,1520
#define STOP_ARRAY_RO 43,1561
#define RESTART_ARRAY_RW 44,1605
typedef struct mdu_version_s mdu_version_s46,1652
} mdu_version_t;mdu_version_t50,1724
typedef struct mdu_array_info_s mdu_array_info_s52,1742
} mdu_array_info_t;mdu_array_info_t83,2516
typedef struct mdu_disk_info_s mdu_disk_info_s85,2537
} mdu_disk_info_t;mdu_disk_info_t95,2693
typedef struct mdu_start_info_s mdu_start_info_s97,2713
} mdu_start_info_t;mdu_start_info_t106,2857
typedef struct mdu_param_smdu_param_s108,2878
} mdu_param_t;mdu_param_t113,3014
mdctl.h,823
#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
Assemble.c,22
int Assemble(34,1171
Build.c,19
int Build(32,1135
Create.c,20
int Create(32,1135
Detail.c,20
int Detail(34,1171
Examine.c,21
int Examine(34,1171
Manage.c,79
int Manage_ro(32,1135
int Manage_runstop(36,1191
int Manage_subdevs(40,1251
ReadMe.c,231
#define Name 32,1135
char Version[33,1156
char short_options[82,3241
struct option long_options[83,3297
char Usage[122,4441
char Help[127,4498
char Help_create[181,6989
char Help_build[203,7973
char Help_assemble[216,8513
config.c,102
char DefaultConfFile[43,1371
mddev_uuid_t conf_get_uuids(45,1416
mddev_dev_t conf_get_devs(50,1482
mdctl.c,40
int main(33,1153
#define O(O131,3313
util.c,212
int 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

11
TODO
View File

@ -1,7 +1,12 @@
- check superblock checksum in examine
- report "chunk" or "rounding" depending on raid level
- report "linear" instead of "-1" for raid level
- decode ayout depending on raid level
- 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.
- --verbose and --force flags.
- set md_minor, *_disks for Create
- for create raid5, how to choose between
all working, but not insync
one missing, one spare, insync

View File

@ -7,8 +7,8 @@ else echo $target is not a directory
exit 2
fi
set `grep '^char Version' ReadMe.c `
echo version = $6
base=mdctl-$6.tgz
echo version = $7
base=mdctl-$7.tgz
if [ -f $target/$base ]
then
echo $target/$base exists.
@ -17,4 +17,4 @@ fi
trap "rm $target/$base; exit" 1 2 3
( cd .. ; tar czvf - mdctl ) > $target/$base
chmod a+r $target/$base
ls -l $target/$base
ls -l $target/$base

114
mdctl.c
View File

@ -40,6 +40,7 @@ int main(int argc, char *argv[])
int i;
int chunk = 0;
int size = 0;
int level = -10;
int layout = -1;
int raiddisks = 0;
@ -73,7 +74,7 @@ int main(int argc, char *argv[])
case 'E':
/* setting mode - only once */
if (mode) {
fprintf(stderr, "mdctl: --%s/-%c not allowed, mode already set to %s\n",
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);
@ -107,7 +108,7 @@ int main(int argc, char *argv[])
mddev = optarg;
else {
if (subdevs +1 >= MD_SB_DISKS) {
fprintf(stderr, "mdctl: too many devices at %s - current limit -s %d\n",
fprintf(stderr, Name ": too many devices at %s - current limit -s %d\n",
optarg, MD_SB_DISKS);
exit(2);
}
@ -133,46 +134,52 @@ int main(int argc, char *argv[])
case O('C','c'):
case O('B','c'): /* chunk or rounding */
if (chunk) {
fprintf(stderr, "mdctl: chunk/rounding may only be specified once. "
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) {
fprintf(stderr, "mdctl: invalid chunk/rounding value: %s\n",
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, "mdctl: raid level may only be set once. "
fprintf(stderr, Name ": raid level may only be set once. "
"Second value is %s.\n", optarg);
exit(2);
}
if (strcmp(optarg,"linear")==0)
level = -1;
else if (strlen(optarg)==1 && strchr("01245", optarg[0]))
level = optarg[0]-'0';
else {
fprintf(stderr, "mdctl: invalid raid level: %s\n",
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, "mdctl: Raid level %s not permitted with --build.\n",
optarg);
exit(2);
}
if (layout >=0 && level < 4) {
fprintf(stderr, "mdctl: raid level %s is incompatible with layout setting\n",
fprintf(stderr, Name ": Raid level %s not permitted with --build.\n",
optarg);
exit(2);
}
if (sparedisks > 0 && level < 1) {
fprintf(stderr, "mdctl: raid level %s is incompatible with spare-disks setting.\n",
fprintf(stderr, Name ": raid level %s is incompatible with spare-disks setting.\n",
optarg);
exit(2);
}
@ -180,39 +187,40 @@ int main(int argc, char *argv[])
case O('C','p'): /* raid5 layout */
if (layout >= 0) {
fprintf(stderr,"mdctl: layout may only be sent once. "
fprintf(stderr,Name ": layout may only be sent once. "
"Second value was %s\n", optarg);
exit(2);
}
if (level > -10 && level < 4) {
fprintf(stderr,"mdctl: layout is incompatible with raid levels below 4.\n");
exit(2);
}
if (strcmp(optarg, "left-symmetric")==0 || strcmp(optarg,"ls")==0)
layout = ALGORITHM_LEFT_SYMMETRIC;
else if (strcmp(optarg, "left-asymmetric")==0 || strcmp(optarg,"la")==0)
layout = ALGORITHM_LEFT_ASYMMETRIC;
else if (strcmp(optarg, "right-symmetric")==0 || strcmp(optarg,"rs")==0)
layout = ALGORITHM_RIGHT_SYMMETRIC;
else if (strcmp(optarg, "right-asymmetric")==0 || strcmp(optarg,"ra")==0)
layout = ALGORITHM_RIGHT_ASYMMETRIC;
else {
fprintf(stderr,"mdctl: %s is not a valid layout value\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, "mdctl: raid-disks set twice: %d and %s\n",
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, "mdctl: invalid number of raid disks: %s\n",
fprintf(stderr, Name ": invalid number of raid disks: %s\n",
optarg);
exit(2);
}
@ -220,18 +228,18 @@ int main(int argc, char *argv[])
case O('C','x'): /* number of spare (eXtra) discs */
if (sparedisks) {
fprintf(stderr,"mdctl: spare-disks set twice: %d and %s\n",
fprintf(stderr,Name ": spare-disks set twice: %d and %s\n",
sparedisks, optarg);
exit(2);
}
if (level > -10 && level < 1) {
fprintf(stderr, "mdctl: spare-disks setting is incompatible with raid level %d\n",
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, "mdctl: invalid number of spare disks: %s\n",
fprintf(stderr, Name ": invalid number of spare disks: %s\n",
optarg);
exit(2);
}
@ -243,21 +251,21 @@ int main(int argc, char *argv[])
continue;
case O('A','u'): /* uuid of array */
if (uuidset) {
fprintf(stderr, "mdctl: uuid cannot bet set twice. "
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,"mdctl: Bad uuid: %s\n", optarg);
fprintf(stderr,Name ": Bad uuid: %s\n", optarg);
exit(2);
}
continue;
case O('A','c'): /* config file */
if (configfile) {
fprintf(stderr, "mdctl: configfile cannot be set twice. "
fprintf(stderr, Name ": configfile cannot be set twice. "
"Second value is %s.\n", optarg);
exit(2);
}
@ -289,14 +297,14 @@ int main(int argc, char *argv[])
case O('B','R'):
case O('C','R'): /* Run the array */
if (runstop < 0) {
fprintf(stderr, "mdctl: Cannot both Stop and Run an array\n");
fprintf(stderr, Name ": Cannot both Stop and Run an array\n");
exit(2);
}
runstop = 1;
continue;
case O('@','S'):
if (runstop > 0) {
fprintf(stderr, "mdctl: Cannot both Run and Stop an array\n");
fprintf(stderr, Name ": Cannot both Run and Stop an array\n");
exit(2);
}
runstop = -1;
@ -304,7 +312,7 @@ int main(int argc, char *argv[])
case O('@','o'):
if (readonly < 0) {
fprintf(stderr, "mdctl: Cannot have both readonly and readwrite\n");
fprintf(stderr, Name ": Cannot have both readonly and readwrite\n");
exit(2);
}
readonly = 1;
@ -320,7 +328,7 @@ int main(int argc, char *argv[])
/* We have now processed all the valid options. Anything else is
* an error
*/
fprintf(stderr, "mdctl: option %c not valid in mode %c\n",
fprintf(stderr, Name ": option %c not valid in mode %c\n",
opt, mode);
exit(2);
@ -341,17 +349,17 @@ int main(int argc, char *argv[])
*/
if (mode !='D' && mode !='E' && ! (mode =='A' && scan)) {
if (!mddev) {
fprintf(stderr, "mdctl: an md device must be given in this mode\n");
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,"mdctl: error opening %s: %s\n",
fprintf(stderr,Name ": error opening %s: %s\n",
mddev, strerror(errno));
exit(1);
}
if (md_get_version(mdfd) <= 0) {
fprintf(stderr, "mdctl: %s does not appear to be an md device\n",
fprintf(stderr, Name ": %s does not appear to be an md device\n",
mddev);
close(mdfd);
exit(1);
@ -378,8 +386,8 @@ int main(int argc, char *argv[])
rv = Build(mddev, mdfd, chunk, level, raiddisks, subdevs,subdev);
break;
case 'C': /* Create */
rv = Create(mddev, mdfd, chunk, level, layout, raiddisks, sparedisks,
subdevs,subdev,runstop);
rv = Create(mddev, mdfd, chunk, level, layout, size, raiddisks, sparedisks,
subdevs,subdev,runstop, verbose);
break;
case 'D': /* Detail */
for (i=0; i<subdevs; i++)

25
mdctl.h
View File

@ -49,6 +49,8 @@ extern __off64_t lseek64 __P ((int __fd, __off64_t __offset, int __whence));
#include "md_u.h"
#define Name "mdctl"
extern char short_options[];
extern struct option long_options[];
extern char Version[], Usage[], Help[], Help_create[], Help_build[], Help_assemble[];
@ -67,13 +69,15 @@ typedef struct mddev_dev_s {
struct mddev_dev_s *next;
} *mddev_dev_t;
/*
* RAID5 supported algorithms
*/
#define ALGORITHM_LEFT_ASYMMETRIC 0
#define ALGORITHM_RIGHT_ASYMMETRIC 1
#define ALGORITHM_LEFT_SYMMETRIC 2
#define ALGORITHM_RIGHT_SYMMETRIC 3
typedef struct mapping {
char *name;
int num;
} mapping_t;
extern char *map_num(mapping_t *map, int num);
extern int map_name(mapping_t *map, char *name);
extern mapping_t r5layout[], pers[];
extern int Manage_ro(char *devname, int fd, int readonly);
@ -95,9 +99,9 @@ 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 raiddisks, int sparedisks,
int chunk, int level, int layout, int size, int raiddisks, int sparedisks,
int subdevs, char *subdev[],
int runstop);
int runstop, int verbose);
extern int Detail(char *dev);
extern int Examine(char *dev);
@ -105,6 +109,9 @@ extern int Examine(char *dev);
extern int md_get_version(int fd);
extern int get_linux_version();
extern int parse_uuid(char *str, int uuid[4]);
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_dev_t conf_get_devs(char *);

119
util.c
View File

@ -88,10 +88,10 @@ int md_get_version(int fd)
return -1;
if (ioctl(fd, RAID_VERSION, &vers) == 0)
return (vers.major<<16) | (vers.minor<<8) | vers.patchlevel;
return (vers.major*10000) + (vers.minor*100) + vers.patchlevel;
if (MAJOR(stb.st_rdev) == MD_MAJOR)
return (36<<8);
return (3600);
return -1;
}
@ -105,7 +105,7 @@ int get_linux_version()
if (sscanf(name.release, "%d.%d.%d", &a,&b,&c)!= 3)
return -1;
return (a<<16)+(b<<8)+c;
return (a*1000000)+(b*1000)+c;
}
int enough(int level, int raid_disks, int avail_disks)
@ -211,7 +211,7 @@ int load_super(int fd, mdp_super_t *super)
if (lseek64(fd, offset, 0)< 0LL)
return 3;
if (read(fd, &super, sizeof(super)) != sizeof(super))
if (read(fd, super, sizeof(*super)) != sizeof(*super))
return 4;
if (super->md_magic != MD_SB_MAGIC)
@ -222,3 +222,114 @@ int load_super(int fd, mdp_super_t *super)
return 0;
}
int check_ext2(int fd, char *name)
{
/*
* Check for an ext2fs file system.
* Superblock is always 1K at 1K offset
*
* s_magic is le16 at 56 == 0xEF53
* report mtime - le32 at 44
* blocks - le32 at 4
* logblksize - le32 at 24
*/
unsigned char sb[1024];
time_t mtime;
int size, bsize;
if (lseek(fd, 1024,0)!= 1024)
return 0;
if (read(fd, sb, 1024)!= 1024)
return 0;
if (sb[56] != 0x53 || sb[57] != 0xef)
return 0;
mtime = sb[44]|(sb[45]|(sb[46]|sb[47]<<8)<<8)<<8;
bsize = sb[24]|(sb[25]|(sb[26]|sb[27]<<8)<<8)<<8;
size = sb[4]|(sb[5]|(sb[6]|sb[7]<<8)<<8)<<8;
fprintf(stderr, Name ": %s appears to contain an ext2fs file system\n",
name);
fprintf(stderr," size=%dK mtime=%s",
size*(1<<bsize), ctime(&mtime));
return 1;
}
int check_reiser(int fd, char *name)
{
/*
* superblock is at 64K
* size is 1024;
* Magic string "ReIsErFs" or "ReIsEr2Fs" at 52
*
*/
unsigned char sb[1024];
int size;
if (lseek(fd, 64*1024, 0) != 64*1024)
return 0;
if (read(fd, sb, 1024) != 1024)
return 0;
if (strncmp(sb+52, "ReIsErFs",8)!=0 &&
strncmp(sb+52, "ReIsEr2Fs",9)!=0)
return 0;
fprintf(stderr, Name ": %s appears to contain a reiserfs file system\n",name);
size = sb[0]|(sb[1]|(sb[2]|sb[3]<<8)<<8)<<8;
fprintf(stderr, " size = %dK\n", size*4);
return 1;
}
int check_raid(int fd, char *name)
{
mdp_super_t super;
time_t crtime;
if (load_super(fd, &super))
return 0;
/* Looks like a raid array .. */
fprintf(stderr, Name ": %s appear to be part of a raid array:\n",
name);
crtime = super.ctime;
fprintf(stderr, " level=%d disks=%d ctime=%s",
super.level, super.raid_disks, ctime(&crtime));
return 1;
}
int ask(char *mesg)
{
char *add = "";
int i;
for (i=0; i<5; i++) {
char buf[100];
fprintf(stderr, "%s%s", mesg, add);
fflush(stderr);
if (fgets(buf, 100, stdin)==NULL)
return 0;
if (buf[0]=='y' || buf[0]=='Y')
return 1;
if (buf[0]=='n' || buf[0]=='N')
return 0;
add = "(y/n) ";
}
fprintf(stderr, Name ": assuming 'no'\n");
return 0;
}
char *map_num(mapping_t *map, int num)
{
while (map->name) {
if (map->num == num)
return map->name;
map++;
}
return NULL;
}
int map_name(mapping_t *map, char *name)
{
while (map->name) {
if (strcmp(map->name, name)==0)
return map->num;
map++;
}
return -10;
}