/* libmondo-raid.c                                                subroutines for handling RAID
   $Id: libmondo-raid.c 30 2005-09-28 23:32:28Z bcornec $
.


06/29
- added create_raidtab_from_mdstat()
- changed char[MAX_STR_LEN] to char*

10/21/2003
- get_next_raidtab_line() --- correctly handle multiple spaces
  between label and value

07/03
- line 447 - changed assert()

05/08
- cleaned up some FreeBSd-specific stuff

05/05
- added Joshua Oreman's FreeBSD patches

04/25
- added a bunch of RAID utilities from mondorestore/mondo-restore.c

04/24/2003
- added some assert()'s and log_OS_error()'s

10/19/2002
- added some comments

07/24
- created
*/


/**
 * @file
 * Functions for handling RAID (especially during restore).
 */

#include "my-stuff.h"
#include "mondostructures.h"
#include "libmondo-gui-EXT.h"
#include "libmondo-files-EXT.h"
#include "libmondo-tools-EXT.h"
#include "libmondo-string-EXT.h"
#include "lib-common-externs.h"
#include "libmondo-raid.h"

#ifdef __FreeBSD__
/* Nonstandard library functions: */
extern void errx (int exitval, const char *fmt, ...);
extern char *strsep (char **stringp, const char *delim);
#endif

/*@unused@*/
//static char cvsid[] = "$Id: libmondo-raid.c 30 2005-09-28 23:32:28Z bcornec $";


/**
 * @addtogroup raidGroup
 * @{
 */
/**
 * See if a particular RAID level is supported by the kernel.
 * @param raidno The RAID level (-1 through 5) to check. -1 means "linear" under Linux and
 * "concatenated" under FreeBSD. It's really the same thing, just different wording.
 * @return TRUE if it's supported, FALSE if not.
 */
bool
is_this_raid_personality_registered (int raidno)
{
#ifdef __FreeBSD__
  return ((raidno == -1) || (raidno == 0) || (raidno == 1) || (raidno == 5)) ? TRUE : FALSE;
#else
	/*@ buffer ***********************************************************/
  char *command;
  int res;
  
  command = malloc(MAX_STR_LEN*2);
  strcpy (command, "cat /proc/mdstat | grep \"");
  if (raidno == -1)
    {
      strcat (command, "linear");
    }
  else
    {
      sprintf (command + strlen (command), "raid%d", raidno);
    }
  strcat (command, "\" > /dev/null 2> /dev/null");
  log_it ("Is raid %d registered? Command = '%s'", raidno, command);
  res = system (command);
  paranoid_free(command);
  if (res)
    {
      return (FALSE);
    }
  else
    {
      return (TRUE);
    }
#endif
}






/**
 * Search for @p device in @p disklist.
 * @param disklist The disklist to search in.
 * @param device The device to search for.
 * @return The index number of @p device, or -1 if it does not exist.
 */
int
where_in_drivelist_is_drive (struct list_of_disks *disklist, char *device)
{

	/*@ int **************************************************************/
  int i = 0;

  assert(disklist!=NULL);
  assert_string_is_neither_NULL_nor_zerolength(device);

  for (i = 0; i < disklist->entries; i++)
    {
      if (!strcmp (disklist->el[i].device, device))
	{
	  break;
	}
    }
  if (i == disklist->entries)
    {
      return (-1);
    }
  else
    {
      return (i);
    }
}








/**
 * Determine which RAID device is using a particular partition.
 * @param raidlist The RAID information structure.
 * @param device The partition to find out about.
 * @return The index number of the RAID device using @p device, or -1 if there is none.
 */
int
which_raid_device_is_using_this_partition (struct raidlist_itself *raidlist,
					   char *device)
{
#ifdef __FreeBSD__
// FreeBSD-specific version of which_raid_device_is_using_this_partition()
	/*@ int **********************************************************/
  int i = 0;

  for (i = 0; i < raidlist->entries; i++)
    {
	bool thisone = FALSE;
	int j, k, l;
	
	for (j = 0; j < raidlist->el[i].plexes; ++j) {
	    for (k = 0; k < raidlist->el[i].plex[j].subdisks; ++k) {
		for (l = 0; l < raidlist->disks.entries; ++l) {
		    if (!strcmp (raidlist->disks.el[l].device,
				 device) &&
			!strcmp (raidlist->el[i].plex[j].sd[k].which_device,
				 raidlist->disks.el[l].name))
			thisone = TRUE;
		}
	    }
	}
	
	if (thisone)
	{
	  break;
	}
    }
  if (i == raidlist->entries)
    {
      return (-1);
    }
  else
    {
      return (i);
    }
}

#else
// Linux-specific version of which_raid_device_is_using_this_partition()
// and one other function which FreeBSD doesn't use

  int current_raiddev = 0;

  assert_string_is_neither_NULL_nor_zerolength(device);
  assert(raidlist!=NULL);

  for (current_raiddev = 0; current_raiddev < raidlist->entries;
       current_raiddev++)
    {
      if (where_in_drivelist_is_drive
	  (&raidlist->el[current_raiddev].data_disks, device) >= 0
	  || where_in_drivelist_is_drive (&raidlist->el[current_raiddev].
					  spare_disks, device) >= 0
	  || where_in_drivelist_is_drive (&raidlist->el[current_raiddev].
					  parity_disks, device) >= 0
	  || where_in_drivelist_is_drive (&raidlist->el[current_raiddev].
					  failed_disks, device) >= 0)
	{
	  break;
	}
    }
  if (current_raiddev == raidlist->entries)
    {
      return (-1);
    }
  else
    {
      return (current_raiddev);
    }
}

/**
 * Write an @c int variable to a list of RAID variables.
 * @param raidrec The RAID device record to write to.
 * @param lino The variable index number to modify/create.
 * @param label The label to write.
 * @param value The value to write.
 */
void
write_variableINT_to_raid_var_line (struct raid_device_record *raidrec,
				    int lino, char *label, int value)
{
	/*@ buffers ******************************************************/
  char *sz_value;

  malloc_string(sz_value);
  assert(raidrec!=NULL);
  assert(label!=NULL);

  sprintf (sz_value, "%d", value);
  strcpy (raidrec->additional_vars.el[lino].label, label);
  strcpy (raidrec->additional_vars.el[lino].value, sz_value);
  paranoid_free(sz_value);
}
#endif








#ifdef __FreeBSD__
/**
 * Add a disk to a RAID plex.
 * @param p The plex to add the device to.
 * @param device_to_add The device to add to @p p.
 */
void add_disk_to_raid_device(struct vinum_plex *p, char*device_to_add)
{
    strcpy (p->sd[p->subdisks].which_device, device_to_add);
    ++p->subdisks;

}
#else
/**
 * Add a disk to a RAID device.
 * @param disklist The disklist to add the device to.
 * @param device_to_add The device to add to @p disklist.
 * @param index The index number of the disklist entry we're creating.
 */
void add_disk_to_raid_device(struct list_of_disks *disklist, char*device_to_add, int index)
{
  int items;

  assert(disklist!=NULL);
  assert_string_is_neither_NULL_nor_zerolength(device_to_add);
  items = disklist->entries;
  strcpy( disklist->el[items].device, device_to_add );
  disklist->el[items].index = index;
  items++;
  disklist->entries = items;
}
#endif


/**
 * Save the additional RAID variables to a stream.
 * @param vars The RAID variable list to save.
 * @param fout The FILE pointer to save them to.
 */
void 
save_additional_vars_to_file(struct additional_raid_variables *vars, FILE *fout)
{
  int i;

  assert(vars!=NULL);
  assert(fout!=NULL);

  for(i = 0; i < vars->entries; i++ )
    {
      fprintf(fout,"    %-21s %s\n",vars->el[i].label, vars->el[i].value);
    }
}


/**
 * Save a raidlist structure to disk in raidtab format.
 * @param raidlist The raidlist to save.
 * @param fname The file to save it to.
 * @return 0, always.
 * @bug Return value is redundant.
 */
int 
save_raidlist_to_raidtab(struct raidlist_itself *raidlist, char *fname)
{
  FILE *fout;
  int current_raid_device;
#ifdef __FreeBSD__
  int i;
#else
// Linux
#endif

  assert(raidlist!=NULL);
  assert_string_is_neither_NULL_nor_zerolength(fname);

  if (raidlist->entries <= 0)
    {
      unlink(fname);
      log_it("Deleting raidtab (no RAID devs anyway)");
      return(0);
    }
  if (!(fout=fopen(fname,"w"))) 
    {
      log_OS_error("Failed to save raidlist");return(1);
    }
    fprintf (fout, "# Generated by Mondo Rescue\n");

#ifdef __FreeBSD__
    for (i = 0; i < raidlist->disks.entries; ++i) {
	fprintf (fout, "drive %s device %s\n", raidlist->disks.el[i].name, raidlist->disks.el[i].device);
    }
    for (i = 0; i < (raidlist->spares.entries); ++i) {
	fprintf (fout, "drive %s device %s hotspare\n", raidlist->spares.el[i].name, raidlist->spares.el[i].device);
    }
#endif

  for(current_raid_device = 0; current_raid_device < raidlist->entries; current_raid_device++ )
    {
      save_raidrec_to_file(&raidlist->el[current_raid_device],fout);
    }
  paranoid_fclose(fout);
  return(0);
}


/**
 * Save an individual RAID device record to a stream.
 * @param raidrec The RAID device record to save.
 * @param fout The stream to save it to.
 */
void 
save_raidrec_to_file( struct
#ifdef __FreeBSD__
vinum_volume
#else
raid_device_record
#endif
*raidrec, FILE *fout )
{
#ifdef __FreeBSD__
    int i, j;

    fprintf (fout, "\nvolume %s\n", raidrec->volname);
    for (i = 0; i < raidrec->plexes; ++i) {
	char org[24];
	switch (raidrec->plex[i].raidlevel) {
	case -1: strcpy (org, "concat"); break;
	case 0:  strcpy (org, "striped"); break;
	case 5:  strcpy (org, "raid5"); break;
	}
	fprintf (fout, "  plex org %s", org);
	if (raidrec->plex[i].raidlevel != -1) {
	    fprintf (fout, " %ik", raidrec->plex[i].stripesize);
	}
	fprintf (fout, "\n");
	
	for (j = 0; j < raidrec->plex[i].subdisks; ++j) {
	    fprintf (fout, "    sd drive %s size 0\n", raidrec->plex[i].sd[j].which_device);
	}
    }
#else
  assert(raidrec!=NULL);
  assert(fout!=NULL);

  fprintf(fout,"raiddev %s\n",raidrec->raid_device);
  if (raidrec->raid_level == -1 )
    { 
      fprintf(fout,"    raid-level            linear\n"); 
    }
  else
    { 
      fprintf(fout,"    raid-level            %d\n",raidrec->raid_level); 
    }
  fprintf(fout,"    chunk-size            %d\n",raidrec->chunk_size);
  fprintf(fout,"    nr-raid-disks         %d\n",raidrec->data_disks.entries); 
  fprintf(fout,"    nr-spare-disks        %d\n",raidrec->spare_disks.entries);
  if (raidrec->parity_disks.entries > 0)
    {
      fprintf(fout,"    nr-parity-disks       %d\n",raidrec->parity_disks.entries);
    }

  fprintf(fout,"    persistent-superblock %d\n",raidrec->persistent_superblock);
  save_additional_vars_to_file(&raidrec->additional_vars,fout);
  fprintf(fout,"\n");
  save_disklist_to_file("raid-disk", &raidrec->data_disks, fout);
  save_disklist_to_file("spare-disk", &raidrec->spare_disks, fout);
  save_disklist_to_file("parity-disk", &raidrec->parity_disks, fout);
  save_disklist_to_file("failed-disk", &raidrec->failed_disks, fout);
  fprintf(fout,"\n");
#endif
}

/**
 * Retrieve the next line from a raidtab stream.
 * @param fin The file to read the input from.
 * @param label Where to put the line's label.
 * @param value Where to put the line's value.
 * @return 0 if the line was read and stored successfully, 1 if we're at end of file.
 */
int
get_next_raidtab_line( FILE *fin, char *label, char *value )
{
  char *incoming;
  char *p;

  malloc_string(incoming);
  assert(fin!=NULL);
  assert(label!=NULL);
  assert(value!=NULL);

  label[0] = value[0]= '\0';
  if ( feof(fin) )
    {
      paranoid_free(incoming);
      return( 1 );
    }
  for( fgets(incoming, MAX_STR_LEN - 1, fin ); !feof( fin ); fgets(incoming, MAX_STR_LEN - 1, fin ) )
    {
      strip_spaces( incoming );
      p = strchr( incoming,' ' );
      if ( strlen( incoming ) < 3 || incoming[0] == '#' || !p)
	{
	  continue;
	}
      *(p++) = '\0';
      while(*p==' ') { p++; }
      strcpy( label, incoming );
      strcpy( value, p );
      paranoid_free(incoming);
      return( 0 );
    }
  return( 1 );
}



/**
 * Load a raidtab file into a raidlist structure.
 * @param raidlist The raidlist to fill.
 * @param fname The file to read from.
 * @return 0 for success, 1 for failure.
 */
#ifdef __FreeBSD__
int load_raidtab_into_raidlist( struct raidlist_itself *raidlist, char *fname)
{
  FILE*fin;
  char *tmp;
  int items;
  
  malloc_string(tmp);
  raidlist->spares.entries = 0;
  raidlist->disks.entries  = 0;
  if (length_of_file(fname)<5)
    {
      log_it("Raidtab is very small or non-existent. Ignoring it.");
      raidlist->entries=0;
      paranoid_free(tmp);
      return(0);
    }
  if (!(fin=fopen(fname,"r")))
    {
      log_it("Cannot open raidtab");
      paranoid_free(tmp);
      return(1);
    }
  items=0;
  log_it("Loading raidtab...");
  while(!feof(fin))
    {
	int argc;
	char **argv = get_next_vinum_conf_line (fin, &argc);
	if (!argv) break;
	if (!strcmp (argv[0], "drive")) {
	    char *drivename, *devname;
	    if (argc < 4) continue;
	    drivename   = argv[1];
	    devname     = get_option_val (argc, argv, "device");
	    if (!devname) continue;

	    if (get_option_state (argc, argv, "hotspare")) {
		strcpy (raidlist->spares.el [raidlist->spares.entries].name, drivename);
		strcpy (raidlist->spares.el [raidlist->spares.entries].device, devname);
		raidlist->spares.el [raidlist->spares.entries].index  = raidlist->disks.entries;
		raidlist->spares.entries++;
	    } else {
		strcpy (raidlist->disks.el [raidlist->disks.entries].name, drivename);
		strcpy (raidlist->disks.el [raidlist->disks.entries].device, devname);
		raidlist->disks.el [raidlist->disks.entries].index   = raidlist->disks.entries;
		raidlist->disks.entries++;
	    }
	}
	else if (!strcmp (argv[0], "volume")) {
	    char *volname;
	    if (argc < 2) continue;
	    volname = argv[1];
	    strcpy (raidlist->el [raidlist->entries].volname, volname);
	    raidlist->el [raidlist->entries].plexes = 0;
	    raidlist->entries++;
	}
	else if (!strcmp (argv[0], "plex")) {
	    int raidlevel, stripesize;
	    char *org   = 0;
	    char ** tmp = 0;
	    if (argc < 3) continue;
	    org = get_option_val (argc, argv, "org");
	    if (!org) continue;
	    if (strcmp (org, "concat")) {
		tmp = get_option_vals (argc, argv, "org", 2);
		if (tmp && tmp[1]) {
		    stripesize = (int) (size_spec (tmp[1]) / 1024);
		}
		else stripesize = 279;
	    }
	    else stripesize = 0;

	    if (!strcmp (org, "concat")) {
		raidlevel = -1;
	    }
	    else if (!strcmp (org, "striped")) {
		raidlevel = 0;
	    }
	    else if (!strcmp (org, "raid5")) {
		raidlevel = 5;
	    }
	    else continue;

	    raidlist->el[raidlist->entries - 1].plex
		[raidlist->el [raidlist->entries - 1].plexes].raidlevel = raidlevel;
	    raidlist->el[raidlist->entries - 1].plex
		[raidlist->el [raidlist->entries - 1].plexes].stripesize = stripesize;
	    raidlist->el[raidlist->entries - 1].plex
		[raidlist->el [raidlist->entries - 1].plexes].subdisks = 0;
	    raidlist->el[raidlist->entries - 1].plexes++;
	}
	else if ((!strcmp (argv[0], "sd")) || (!strcmp (argv[0], "subdisk"))) {
	    char *drive = 0;
	    if (argc < 3) continue;
	    drive = get_option_val (argc, argv, "drive");
	    if (!drive) continue;
	    
	    strcpy (raidlist->el [raidlist->entries - 1].plex
		    [raidlist->el [raidlist->entries - 1].plexes - 1].sd
		    [raidlist->el [raidlist->entries - 1].plex
		     [raidlist->el [raidlist->entries - 1].plexes - 1].subdisks].which_device, drive);
	    raidlist->el [raidlist->entries - 1].plex
		[raidlist->el [raidlist->entries - 1].plexes - 1].subdisks++;
	}
    }
  fclose(fin);
  log_it("Raidtab loaded successfully.");
  sprintf(tmp,"%d RAID devices in raidtab", raidlist->entries);
  log_it(tmp);
  paranoid_free(tmp);
  return(0);
}


#else

int load_raidtab_into_raidlist( struct raidlist_itself *raidlist, char *fname)

{
  FILE *fin;
  char *tmp;
  char *label;
  char *value;
  int items;
  int v;

  malloc_string(tmp);
  malloc_string(label);
  malloc_string(value);
  assert(raidlist!=NULL);
  assert_string_is_neither_NULL_nor_zerolength(fname);

  if ( length_of_file( fname ) < 5 )
    {
      log_it( "Raidtab is very small or non-existent. Ignoring it." );
      raidlist->entries = 0;
      paranoid_free(tmp);
      paranoid_free(label);
      paranoid_free(value);
      return( 0 );
    }
  if ( !( fin = fopen( fname, "r" ) ) )
    {
      log_it( "Cannot open raidtab" );
      paranoid_free(tmp);
      paranoid_free(label);
      paranoid_free(value);
      return( 1 );
    }
  items = 0;
  log_it( "Loading raidtab..." );
  get_next_raidtab_line( fin, label, value );
  while( !feof( fin ) )
    {
      log_msg(1, "Looking for raid record #%d", items);
      initialize_raidrec( &raidlist->el[items] );
      v = 0;
      /* find the 'raiddev' entry, indicating the start of the block of info */
      while( !feof( fin ) && strcmp( label, "raiddev") )
	{
	  strcpy( raidlist->el[items].additional_vars.el[v].label, label );
	  strcpy( raidlist->el[items].additional_vars.el[v].value, value );
	  v++;
	  get_next_raidtab_line( fin, label, value );
	  log_it( tmp );
	}
      raidlist->el[items].additional_vars.entries = v;
      if ( feof( fin ) )
	{
	  log_msg(1, "No more records.");
	  continue;
	}
      log_msg(2, "Record #%d (%s) found", items, value);
      strcpy( raidlist->el[items].raid_device, value );
      for( get_next_raidtab_line( fin, label, value);
	   !feof( fin ) && strcmp( label, "raiddev" );
	   get_next_raidtab_line( fin, label, value ) )
	{
	  process_raidtab_line( fin, &raidlist->el[items], label, value );
	}
      items++;
    }
  paranoid_fclose( fin );
  raidlist->entries = items;
  log_msg(1,  "Raidtab loaded successfully." );
  log_msg(1, "%d RAID devices in raidtab", items );
  paranoid_free(tmp);
  paranoid_free(label);
  paranoid_free(value);
  return( 0 );
}
#endif








#ifndef __FreeBSD__
/**
 * Process a single line from the raidtab and store the results into @p raidrec.
 * @param fin The stream to read the line from.
 * @param raidrec The RAID device record to update.
 * @param label Where to put the label processed.
 * @param value Where to put the value processed.
 */
void
process_raidtab_line( FILE *fin,
		      struct raid_device_record *raidrec,
		      char *label,
		      char *value)
{

  /*@ add mallocs **/
  char *tmp;
  char *labelB;
  char *valueB;

  struct list_of_disks *disklist;
  int index;
  int  v;

  malloc_string(tmp);
  malloc_string(labelB);
  malloc_string(valueB);
  assert(fin!=NULL);
  assert(raidrec!=NULL);
  assert_string_is_neither_NULL_nor_zerolength(label);
  assert(value!=NULL);

  if (!strcmp( label, "raid-level" ) )
    {
      if (!strcmp( value, "linear" ) )
	{ raidrec->raid_level = -1;
	}
      else
	{
	  raidrec->raid_level = atoi(value);
	}
    }
  else if (!strcmp(label,"nr-raid-disks") )
    { /* ignore it */
    }
  else if (!strcmp(label,"nr-spare-disks"))
    { /* ignore it */
    }
  else if (!strcmp(label,"nr-parity-disks"))
    { /* ignore it */
    }
  else if (!strcmp(label,"nr-failed-disks"))
    { /* ignore it */
    }
  else if (!strcmp(label,"persistent-superblock"))
    {
      raidrec->persistent_superblock = atoi(value);
    }
  else if (!strcmp(label,"chunk-size"))
    {
      raidrec->chunk_size = atoi(value);
    }
  else if (!strcmp(label,"device"))
    {
      get_next_raidtab_line(fin,labelB,valueB);
      if (!strcmp(labelB,"raid-disk"))
	{
	  disklist=&raidrec->data_disks;
	}
      else if (!strcmp(labelB,"spare-disk"))
	{
	  disklist=&raidrec->spare_disks;
	}
      else if (!strcmp(labelB,"parity-disk"))
	{
	  disklist=&raidrec->parity_disks;
	}
      else if (!strcmp(labelB,"failed-disk"))
	{
	  disklist=&raidrec->failed_disks;
	}
      else
	{ 
	  disklist=NULL; 
	}
      if (!disklist)
	{ 
	  sprintf( tmp,
		   "Ignoring '%s %s' pair of disk %s",labelB,valueB,label); 
	  log_it(tmp); 
	}
      else
	{ 
	  index = atoi(valueB); 
	  add_disk_to_raid_device(disklist,value,index); 
	}
    }
  else
    {
      v = raidrec->additional_vars.entries;
      strcpy(raidrec->additional_vars.el[v].label, label);
      strcpy(raidrec->additional_vars.el[v].value, value);
      raidrec->additional_vars.entries = ++v;
    }
  paranoid_free(tmp);
  paranoid_free(labelB);
  paranoid_free(valueB);
}
#endif


/**
 * Save a disklist to a stream in raidtab format.
 * @param listname One of "raid-disk", "spare-disk", "parity-disk", or "failed-disk".
 * @param disklist The disklist to save to @p fout.
 * @param fout The stream to write to.
 */
void 
save_disklist_to_file(char *listname, 
		      struct list_of_disks *disklist, 
		      FILE *fout)
{
  int i;

  assert_string_is_neither_NULL_nor_zerolength(listname);
  assert(disklist!=NULL);
  assert(fout!=NULL);

  for(i = 0; i < disklist->entries; i++)
    { 
      fprintf(fout,"    device                %s\n",disklist->el[i].device);
      fprintf(fout,"    %-21s %d\n",listname,disklist->el[i].index);
    }
}





#ifdef __FreeBSD__
/**
 * Add a new plex to a volume. The index of the plex will be <tt>v-\>plexes - 1</tt>.
 * @param v The volume to operate on.
 * @param raidlevel The RAID level of the new plex.
 * @param stripesize The stripe size (chunk size) of the new plex.
 */
void add_plex_to_volume(struct vinum_volume *v, int raidlevel, int stripesize)
{
    v->plex[v->plexes].raidlevel = raidlevel;
    v->plex[v->plexes].stripesize = stripesize;
    v->plex[v->plexes].subdisks = 0;
    ++v->plexes;
}

/**
 * For internal use only.
 */
char ** get_next_vinum_conf_line (FILE *f, int *argc)
{
    int cnt = 0;
    static char *argv[64];
    char **ap;
    char *line = (char *) malloc (MAX_STR_LEN);
    if (!line) errx (1, "unable to allocate %i bytes of memory for `char *line' at %s:%i",
		     MAX_STR_LEN, __FILE__, __LINE__);
    (void) fgets (line, MAX_STR_LEN, f);
    if (feof (f)) {
	log_it ("[GNVCL] Uh... I reached the EOF.");
	return 0;
    }
    
    for (ap = argv; (*ap = strsep (&line, " \t")) != NULL;)
	if (**ap != '\0') {
	    if (++ap >= &argv[64])
		break;
	    cnt++;
	}

    if (strchr (argv[cnt-1], '\n')) {
	*(strchr (argv[cnt-1], '\n')) = '\0';
    }

    if (argc) *argc = cnt;
    return argv;
}

/**
 * For internal use only.
 */
char * get_option_val (int argc, char ** argv, char * option)
{
    int i;
    for (i = 0; i < (argc - 1); ++i) {
	if (!strcmp (argv[i], option)) {
	    return argv[i + 1];
	}
    }
    return 0;
}

/**
 * For internal use only.
 */
char ** get_option_vals (int argc, char ** argv, char * option, int nval)
{
    int i, j;
    static char **ret;
    ret = (char **) malloc (nval * sizeof (char *));
    for (i = 0; i < (argc - nval); ++i) {
	if (!strcmp (argv[i], option)) {
	    for (j = 0; j < nval; ++j) {
		ret[j] = (char *) malloc (strlen (argv[i + j + 1]) + 1);
		strcpy (ret[j], argv[i + j + 1]);
	    }
	    return ret;
	}
    }
    return 0;
}

/**
 * For internal use only.
 */
bool get_option_state (int argc, char ** argv, char * option)
{
    int i;
    for (i = 0; i < argc; ++i)
	if (!strcmp (argv[i], option))
	    return TRUE;

    return FALSE;
}

/**
 * Taken from Vinum source -- for internal use only.
 */
long long size_spec(char *spec)
{
    u_int64_t size;
    char *s;
    int sign = 1;					    /* -1 if negative */

    size = 0;
    if (spec != NULL) {					    /* we have a parameter */
	s = spec;
	if (*s == '-') {				    /* negative, */
	    sign = -1;
	    s++;					    /* skip */
	}
	if ((*s >= '0') && (*s <= '9')) {		    /* it's numeric */
	    while ((*s >= '0') && (*s <= '9'))		    /* it's numeric */
		size = size * 10 + *s++ - '0';		    /* convert it */
	    switch (*s) {
	    case '\0':
		return size * sign;

	    case 'B':
	    case 'b':
	    case 'S':
	    case 's':
		return size * sign * 512;

	    case 'K':
	    case 'k':
		return size * sign * 1024;

	    case 'M':
	    case 'm':
		return size * sign * 1024 * 1024;

	    case 'G':
	    case 'g':
		return size * sign * 1024 * 1024 * 1024;

	    case 'T':
	    case 't':
		log_it ("Ok, I'm scared... Someone did a TERABYTE+ size-spec");
		return size * sign * 1024 * 1024 * 1024 * 1024;
		
	    case 'P':
	    case 'p':
		log_it ("If I was scared last time, I'm freaked out now. Someone actually has a PETABYTE?!?!?!?!");
		return size * sign * 1024 * 1024 * 1024 * 1024 * 1024;

	    case 'E':
	    case 'e':
		log_it ("Okay, I'm REALLY freaked out. Who could devote a whole EXABYTE to their data?!?!");
		return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;

	    case 'Z':
	    case 'z':
		log_it ("WHAT!?!? A ZETABYTE!?!? You've GOT to be kidding me!!!");
		return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;

	    case 'Y':
	    case 'y':
		log_it ("Oh my gosh. You actually think a YOTTABYTE will get you anywhere? What're you going to do with 1,208,925,819,614,629,174,706,176 bytes?!?!");
		popup_and_OK ("That sizespec is more than 1,208,925,819,614,629,174,706,176 bytes. You have a shocking amount of data. Please send a screenshot to the list :-)");
		return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
	    }
	}
    }
    return size * sign;
}

#endif




int read_mdstat(struct s_mdstat *mdstat, char*mdstat_file)
{
    FILE*fin;
    char*tmp;
    char*stub;
    char*incoming;
    char*raid_devname;
    char*p, *q, *r;
    int  diskno;
    
    malloc_string(tmp);
    malloc_string(stub);
    malloc_string(incoming);
    malloc_string(raid_devname);
    if (!(fin = fopen(mdstat_file, "r")))
      {
        log_msg(1, "%s not found", mdstat_file);
	return(1);
      }
    mdstat->entries = 0;
    for (fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin); fgets(incoming, MAX_STR_LEN - 1, fin))
      {
        p = incoming;
        if (*p != 'm' && *(p+1) == 'm') { p++; }
	if (strncmp(p, "md", 2)) { continue; }
// read first line --- mdN : active raidX ............
	mdstat->el[mdstat->entries].md = atoi(p+2);
	log_msg(8, "Storing /dev/md%d's info", atoi(p+2));
	while(*p != ':' && *p) { p++; }
	while((*p!='r' || *(p+1)!='a') && *p) { p++; }
        if (!strncmp(p, "raid", 4))
	  {
	    mdstat->el[mdstat->entries].raidlevel = *(p+4) - '0';
	  }
	p += 4;
        while(*p!=' '&& *p) { p++; }
	while(*p==' '&& *p) { p++; }
	for(diskno=0; *p; diskno++)
	  {
	    strcpy(stub, p);
//	    log_msg(1, "diskno=%d; tmp=%s", diskno, tmp);
	    q = strchr(stub, '[');
	    if (q)
	      {
	        *q = '\0';
	        q++;
		r = strchr(q, ']');
		if (r) { *r='\0'; }
		mdstat->el[mdstat->entries].disks.el[diskno].index = atoi(q);
	      }
	    else
	      {
	        mdstat->el[mdstat->entries].disks.el[diskno].index = -1;
		q = strchr(stub, ' ');
		if (q) { *q = '\0'; }
	      }
	    sprintf(tmp, "/dev/%s", stub);
	    log_msg(8, "/dev/md%d : disk#%d : %s (%d)", mdstat->el[mdstat->entries].md, diskno, tmp, mdstat->el[mdstat->entries].disks.el[diskno].index);
	    strcpy(mdstat->el[mdstat->entries].disks.el[diskno].device, tmp);
	    while(*p!=' '&& *p) { p++; }
            while(*p==' '&& *p) { p++; }
	  }
	mdstat->el[mdstat->entries].disks.entries = diskno;
// next line --- skip it
	if (!feof(fin)) { fgets(incoming, MAX_STR_LEN - 1, fin); } else {continue; }
// next line --- the 'progress' line
        if (!feof(fin)) { fgets(incoming, MAX_STR_LEN - 1, fin); } else {continue; }
//	log_msg(1, "Percentage line = '%s'", incoming);
	if (!(p=strchr(incoming, '\%')))
	  {
	    mdstat->el[mdstat->entries].progress = 999; // not found
	  }
	else if (strstr(incoming, "DELAYED"))
	  { 
	    mdstat->el[mdstat->entries].progress = -1; // delayed (therefore, stuck at 0%)
	  }
	else
	  {
	    for(*p='\0'; *p!=' '; p--);
	    mdstat->el[mdstat->entries].progress = atoi(p);
	  }
	log_msg(8, "progress =%d", mdstat->el[mdstat->entries].progress);
	mdstat->entries ++;
      }
    fclose(fin);
    paranoid_free(tmp);
    paranoid_free(stub);
    paranoid_free(incoming);
    paranoid_free(raid_devname);
    return(0);
}



int create_raidtab_from_mdstat(char*raidtab_fname, char *mdstat_fname)
{
    struct raidlist_itself *raidlist;
    struct s_mdstat *mdstat;
    int retval=0;
    int i;
    
    raidlist = malloc(sizeof(struct raidlist_itself));
    mdstat = malloc(sizeof(struct s_mdstat));

    if (read_mdstat(mdstat, mdstat_fname))
        { log_to_screen("Sorry, cannot read %s", mdstat_fname); return(1); }

    for(i=0; i<mdstat->entries; i++)
      {
        sprintf(raidlist->el[i].raid_device, "/dev/md%d", mdstat->el[i].md);
	raidlist->el[i].raid_level = mdstat->el[i].raidlevel;
	raidlist->el[i].persistent_superblock = 1;
	raidlist->el[i].chunk_size = 4;
	memcpy((void*)&raidlist->el[i].data_disks, (void*)&mdstat->el[i].disks, sizeof(struct list_of_disks));
	// FIXME --- the above line does not allow for spare disks
	log_to_screen("FIXME - create_raidtab_from_mdstat does not allow for spare disks");
      }
    raidlist->entries = i;
    retval += save_raidlist_to_raidtab(raidlist, raidtab_fname);
    return(retval);
}



/* @} - end of raidGroup */

