/*
 * nasd_cheops_mgr_init.c
 *
 * Initialization and all for the Cheops storage manager
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 18 1997
 *          Sean Levy, CMU SCS, July 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/errno.h>
#include <stdio.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_mgr_common.h>
#include <nasd/nasd_cheops_mgr_structs.h>
#include <nasd/nasd_cheops_mgr_internal.h>
#include <nasd/nasd_od.h>

/*
 * XXX this should be fixed. It will not be correctly included
 * as a dependency on many platforms. Also, the relative path
 * thing makes it hard for alternatively-organized trees, and
 * just give up on including something like this in the kernel.
 */
#include "../cheops/shared/nasd_cheops_cl_drive.h"

#define NASD_DEBUG_CHEOPS_INIT_FILE_PARSE 0

/* local proto */
static int dmap_add_drive(
  int                    part_id,
  nasd_cheops_dmap_t    *dm,
  nasd_drive_handle_t    h,
  char                  *dr_name,
  char                  *dr_port,
  int                    di,
  int                    dr_psize,
  int                   *partnum);
static void dmap_print(
  nasd_cheops_dmap_t    *dm);
static int init_drives(
  char                  *resource_file,
  char                  *dmap_file,
  int                   *inout_partition_id,
  int                    partition_size,
  int                    quietude,
  int                    clear_old_state,
  int                    override_defaults);
static void io_batcher(
  nasd_threadarg_t       ignored);
static int read_resource_file(
  char                  *fname,
  int                   *num_drives,
  int                   *inout_part_id,
  int                    part_size,
  int                    quietude,
  int                    override_defaults);

/* nasty globals */
NASD_DECLARE_MUTEX(dr_thread_quit_lock);
NASD_DECLARE_COND(dr_thread_quit);
nasd_thread_t dr_thread; /* thread to prefetch */
int dr_thread_quit_flag = 0;
int Nasd_dmap_fd;
nasd_cheops_dmap_t *dmap;
int NumDrives;
int lastDrive;
int  dr_id[MAX_NUM_DRIVES];
int  dr_btype[MAX_NUM_DRIVES];
char dr_name[MAX_NUM_DRIVES][NASD_CHEOPS_MAX_FNAME_LEN];
char dr_port[MAX_NUM_DRIVES][NASD_CHEOPS_MAX_FNAME_LEN];
int  dr_psize[MAX_NUM_DRIVES];

/* Exported to other parts of Cheops, unfortunately */
int Default_Stripe_Unit_Size = 4;
nasd_cheops_raid_level_t Default_RAID_Level = RAID0;
int Default_Stripe_Width = 64 * 1024;

int 
nasd_cheops_mgr_init(
  char                          *resource_file,
  char                          *dmap_file,
  char                          *imap_file,
  int                            partition_id,
  int                            partition_size,
  int                            default_binding_type,
  int                            stripe_unit,
  int                            stripe_width,
  nasd_cheops_raid_level_t       raid_level,
  int                            quietude,
  int                            clear_old_state,
  int                            override_defaults)
{
  int i, rc;

  if (resource_file == NULL)
    resource_file = NASD_CHEOPS_DEFAULT_DRIVE_RESOURCE_FILE;
  if (dmap_file == NULL)
    dmap_file = NASD_CHEOPS_DEFAULT_DMAP_FILE;
  if (partition_size == 0)
    partition_size = NASD_CHEOPS_DEFAULT_PARTSIZE;

  if (!stripe_unit)
    stripe_unit = Default_Stripe_Unit_Size;
  else
    Default_Stripe_Unit_Size = stripe_unit;
  if (!stripe_width)
    stripe_width = Default_Stripe_Width;
  else
    Default_Stripe_Width = stripe_width;
  if (!raid_level)
    raid_level = Default_RAID_Level;
  else
    Default_RAID_Level = raid_level;

  for (i = 0; i < MAX_NUM_DRIVES; i++)
    dr_btype[i] = default_binding_type;

  rc = nasd_cl_p_init();
  if(rc) {
    fprintf(stderr, "CHEOPS MGR: client library initialization "
            "failed: %u (%s)\n",
     rc, nasd_error_string(rc));
    return rc;
  }
       
  rc = init_drives(resource_file, dmap_file, &partition_id, partition_size,
                   quietude, clear_old_state, override_defaults);
  if (rc) {
    fprintf(stderr, "CHEOPS MGR: cheops_mgr_init_drives() failed: %u (%s)\n",
     rc, nasd_error_string(rc));
    return rc;
  }

  if (!quietude) {
    fprintf(stderr, "--Cheops Manager Options--\n");
    if (Default_RAID_Level == RAID0)
      fprintf(stderr, "Running with RAID0.\n");
    else if (Default_RAID_Level == RAID1)
      fprintf(stderr, "Running with RAID1.\n");
    else if (Default_RAID_Level == RAID5)
      fprintf(stderr, "Running with RAID5.\n");
    else if (Default_RAID_Level == NORAID)
      fprintf(stderr, "Running with NORAID.\n");
    else
      fprintf(stderr, "Running with unknown RAID level %d.\n",
              Default_RAID_Level);
    fprintf(stderr, "Stripe size = %dKB.\n", Default_Stripe_Unit_Size/1024);
    fprintf(stderr, "Stripe width = %d.\n", Default_Stripe_Width);
    if (Default_Stripe_Width==1) {
      fprintf(stderr, "WARNING: Not striping over drives\n");
    }
    fprintf(stderr, "Partition = %d.\n", partition_id);
  }
       
  rc = nasd_cheops_bs_qos_init();
  if(rc) {
    fprintf(stderr, "CHEOPS MGR: cheops_bs_qos_init() failed: %u (%s)\n",
     rc, nasd_error_string(rc));
    return rc;
  }
       
  rc = nasd_cheops_bsdir_cache_init(imap_file);
  if (rc) 
    return rc;
       
  rc = nasd_cheops_sst_mgr_init();
  if (rc) {	    
    nasd_cheops_bsdir_cache_shutdown(); 
    return rc;
  }

  if (!quietude) {
    fprintf(stderr, "CHEOPS MGR: initialization done...\n");
    fprintf(stderr, "CHEOPS MGR: full dmap:\n");
    dmap_print(dmap);
  }
  rc = nasd_thread_create(&dr_thread, io_batcher, (void *)((unsigned long)i));
  if (rc) {
	  fprintf(stderr, "CHEOPS MGR: error spawning thread");
	}

  return 0; /* init success */
} 

int 
nasd_cheops_mgr_shutdown(void)
{
  int rc;

  NASD_LOCK_MUTEX(dr_thread_quit_lock);
  dr_thread_quit_flag = 1;
  NASD_UNLOCK_MUTEX(dr_thread_quit_lock);
  NASD_SIGNAL_COND(dr_thread_quit);

  return 0;
}

/* Note: if MAX_NUM_DRIVES is increased to a large size, stack allocation
   should be changed to mallocs */
static int
init_drives(
  char          *resource_file,
  char          *dmap_file,
  int           *inout_partition_id,
  int            partition_size,
  int            quietude,
  int            clear_old_state,
  int            override_defaults)
{
	int  i, nb, rc, num_drives;
  int  partition_id;
	int  partnum;
	nasd_cheops_dmap_t *dm;
	nasd_drive_handle_t hlist[MAX_NUM_DRIVES];
	nasd_drive_handle_t h;
	char *bp;

	/* read drive resources (export) */
	rc = read_resource_file(resource_file, &num_drives, inout_partition_id,
                          partition_size, quietude, override_defaults);
	if (rc) {
	  fprintf(stderr, "CHEOPS MGR: drive initization failed ..\n");
	  return _NASD_CHEOPS_ERR_INIT_FAILED;
	}
  partition_id = *inout_partition_id;
  if (partition_id < 0) {
    fprintf(stderr, "CHEOPS MGR: no partition id specified and no default\n");
    return _NASD_CHEOPS_ERR_INIT_FAILED;
  }

	if (!quietude)
    fprintf(stderr, "CHEOPS MGR: initializing %d drives...\n", num_drives);

	/* bind to drives */
	for (i = 0; i < num_drives; i++) {
    nasd_drive_param_kpdev_t kpdev_args;
    void *binding_args;
    int binding_args_len;

    if (!quietude)
      fprintf(stderr, "CHEOPS MGR: initializing drive %d: #%d %s @ %s\n", i,
       dr_id[i], dr_name[i], dr_port[i]);

    if ((dr_btype[i] == NASD_BIND_COLOCATE) ||
        (dr_btype[i] == NASD_BIND_KPDEV_DEFAULT)) {
      strcpy(kpdev_args.devname, "/dev/nasdkp0");
      binding_args_len = sizeof(kpdev_args);
      binding_args = (void *)&kpdev_args;
    } else {
      binding_args_len = 0;
      binding_args = NULL;
    }
    rc = nasd_bind_to_drive(dr_name[i], dr_port[i], dr_btype[i],
                            binding_args, binding_args_len, &h);
	  if (rc != NASD_SUCCESS) {
	    fprintf(stderr, "CHEOPS MGR: could not bind to drive [%s:%s]: status=%u (%s)\n",
	     dr_name[i], dr_port[i], rc, nasd_error_string(rc));
	    return _NASD_CHEOPS_ERR_INIT_FAILED;
	  }
	  hlist[i] = h;
  }

	/* update drive map and write to disk*/
	NASD_Malloc(dm, sizeof(nasd_cheops_dmap_t), (nasd_cheops_dmap_t *) );
	bzero((char *)dm, sizeof(nasd_cheops_dmap_t));
	bp = (char *) dm;

	if (!clear_old_state &&
      (Nasd_dmap_fd = open(dmap_file, O_RDWR, 0666)) > 0) {
    lseek(Nasd_dmap_fd, 0, SEEK_SET);
    nb = read(Nasd_dmap_fd, bp, sizeof(nasd_cheops_dmap_t));
    if (nb != sizeof(nasd_cheops_dmap_t)) {
      (cheops_Msg
       "found old dmap file, could not read drive map!..exiting...\n");
      nasd_cheops_mgr_shutdown();
      exit(1);
    } else
      fprintf(stderr, "CHEOPS MGR: found old dmap file,"
              " read drive map successfully!...\n");
	} else { 
	  if ((Nasd_dmap_fd = open(dmap_file, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) {
	    return _NASD_CHEOPS_ERR_CANT_CREATE_FILE;
	  } else if (!quietude) {
      fprintf(stderr, "CHEOPS MGR: new dmap file created (%s)\n", dmap_file);
    }
	}

	/* add new drives to dmap (if not already there) */
	for (i = 0; i < num_drives; i++)
	  dmap_add_drive(partition_id, dm, hlist[i], dr_name[i], dr_port[i],
                   dr_id[i], dr_psize[i], &partnum);
	
	/* write dmap to file */
	nb = write(Nasd_dmap_fd, (caddr_t )dm, sizeof(nasd_cheops_dmap_t));
	if (nb != sizeof(nasd_cheops_dmap_t)) {
	  fprintf(stderr, "CHEOPS MGR: found old imap file, could not write drive map!\n");
	  nasd_cheops_mgr_shutdown();
	  exit(1);
	}

	dmap = dm;
	lastDrive = 0;
	NumDrives = num_drives;

	return 0;
}

static void
io_batcher(
  nasd_threadarg_t  ignored)
{
  NASD_LOCK_MUTEX(dr_thread_quit_lock);
  do {
    NASD_WAIT_COND(dr_thread_quit, dr_thread_quit_lock);
  } while (dr_thread_quit_flag == 0);
  NASD_UNLOCK_MUTEX(dr_thread_quit_lock);
}

/* read_resource_file - read cheops manager resource file

   Format of resource lines:

     # comments start with a hash
     #
     default raid_level <raid_level>
     default stripe_unit <stripe_unit>
     default stripe_width <stripe_width>
     default partition_id <partition_id>
     drive <num> <name> <port> [<part_size> [<binding_type>]]

   The default directives only do something if override_defaults is
   zero; if cheops_mgr is started with -O, then whatever is on the
   command line overrides anything in the resource file.

   If part_size is zero, then the default is used.  The unit
   is blocks, as it is for "default stripe_unit".

   If no binding type is specified, the default is used, which
   may not be NASD_BIND_DEFAULT (it can be specified on the
   command line).  This means you could theoretically have
   one drive colocated and one drive remote, i.e.

     drive 10 localhost 6661 0 colocate
     drive 20 cador     6661 0 default 

   Obviously, mixing srpc and dce binding types won't work, but
   you can force dce tcp or udp bindings per drive. */

static int
read_resource_file(
  char  *fname,
  int   *num_drives,
  int   *inout_part_id,
  int    part_size,
  int    quietude,
  int    override_defaults)
{
  char line[1024];
  FILE *fp;
  int i;
  int len;
  int line_no;
  int eof;
  int complain;
  int line_too_long;
  char *errmsg;
  char *ptr;

	fprintf(stderr, "CHEOPS MGR: nasd_cheops_read_exports_file %s\n", fname);

  if (fname==NULL) {
    fprintf(stderr, "CHEOPS MGR: "
     "nasd_cheops_read_resource_file received empty filename\n");
    return EINVAL;
  }

  if ((fp = fopen(fname, "r")) == NULL) {
    fprintf(stderr, "CHEOPS MGR: nasd_cheops_read_resource_file() "
            "unable to open %s\n", fname);
    return ENOENT;
  }
  i = 0;
  line_no = 0;
  eof = 0;
  while (fgets(line, sizeof(line), fp) != NULL) {
    line_no++;
    len = strlen(line);
    line_too_long = 0;
    complain = 1;
    /* fgets() can screw us over in many ways, the main one being returning
       an incomplete line.  if someone screws up and hands us a resource
       file with very long lines, they should be informed of it. */
    while (line[len - 1] != '\n') {
      line_too_long++;
      if (complain) {
        fprintf(stderr, "CHEOPS MGR: %s:%d line too long -- skipped\n", fname,
                line_no);
        complain = 0;
      }
      if (fgets(line, sizeof(line), fp) == NULL) {
        eof = 1;
        break;
      }
      len = strlen(line);
    }
    if (line_too_long) {
      if (eof)
        fprintf(stderr, "CHEOPS MGR: %s:%d eof reached on incomplete line\n",
                fname, line_no);
      continue;
    }
    if (line[0] == '#')
      continue;
    line[len-1] = '\0';
    if (!strncasecmp(line, "default", 7)) {
      char *what_ptr;
      char *val_ptr;

#if NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0
      if (!quietude)
        fprintf(stderr, "CHEOPS MGR: %s:%d parsing default line ...\n",
                fname, line_no);
#endif /* NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0 */
      ptr = line;
      if (override_defaults)
        errmsg = "defaults overridden on command line";
      else {
        errmsg = NULL;
        while (*ptr) { if ((*ptr == ' ')||(*ptr == '\t')) break; else ptr++; }
        if ((what_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
          errmsg = "parameter";
        else if ((val_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
          errmsg = "value";
      }
      if (errmsg != NULL) {
        fprintf(stderr, "CHEOPS MGR: %s:%d bad %s -- skipped\n",
                fname, line_no, errmsg);
        continue;
      } else if (nasd_cheops_walk_str(&ptr) != NULL) {
        fprintf(stderr, "CHEOPS MGR: %s:%d garbage at end of line "
                "-- skipped\n", fname, line_no);
        continue;
      }
      errmsg = NULL;
      if (!strcasecmp(what_ptr, "raid_level")) {
        int temp = -2;

        if (sscanf(val_ptr, "%d", &temp) != 1) {
          if (!strcasecmp(val_ptr, "raid0"))
            temp = 0;
          else if (!strcasecmp(val_ptr, "raid1"))
            temp = 1;
          else if (!strcasecmp(val_ptr, "raid5"))
            temp = 5;
          else if (!strcasecmp(val_ptr, "noraid"))
            temp = -1;
          else
            temp = -2;
        }
        switch (temp) {
        case 0:
          Default_RAID_Level = RAID0;
          break;
        case 1:
          Default_RAID_Level = RAID1;
          break;
        case 5:
          Default_RAID_Level = RAID5;
          break;
        case -1:
          Default_RAID_Level = NORAID;
          break;
        case -2:
          errmsg = "invalid RAID level";
          break;
        }
      } else if (!strcasecmp(what_ptr, "stripe_width")) {
        int temp;

        if (sscanf(val_ptr, "%d", &temp) == 1)
          Default_Stripe_Width = temp;
        else
          errmsg = "default stripe_width did not scan as integer";
      } else if (!strcasecmp(what_ptr, "stripe_unit")) {
        int temp;

        if (sscanf(val_ptr, "%d", &temp) == 1) {
          if (temp < 1024)
            temp *= 1024;
          Default_Stripe_Unit_Size = temp;
        } else
          errmsg = "default stripe_unit did not scan as integer";
      } else if (!strcasecmp(what_ptr, "partition_id")) {
        int temp;

        if (sscanf(val_ptr, "%d", &temp) == 1) {
          if ((temp < 0) || (temp >= NASD_OD_MAXPARTS))
            errmsg = "default partition_id out of range";
          else
            *inout_part_id = temp;
        } else
          errmsg = "default partition_id did not scan as integer";
      } else
        errmsg = "unrecognized parameter name";
      if (errmsg)
        fprintf(stderr, "CHEOPS MGR: %s:%d %s -- skipped\n", fname, line_no,
                errmsg);
#if NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0
      else if (!quietude)
        fprintf(stderr, "CHEOPS MGR: %s;%d %s = %s\n", fname, line_no,
                what_ptr, val_ptr);
#endif /* NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0 */
    } else if (!strncasecmp(line, "drive", 5)) {
      char *name_ptr;
      char *num_ptr;
      char *port_ptr;
      char *psize_ptr;
      char *btype_ptr;
      int drive_id;
      int btype;
      int port_no;
      int psize;

#if NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0
      if (!quietude)
        fprintf(stderr, "CHEOPS MGR: %s:%d parsing drive line ...\n",
                fname, line_no);
#endif /* NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0 */
      ptr = line;
      errmsg = NULL;
      while (*ptr) { if ((*ptr == ' ') || (*ptr == '\t')) break; else ptr++; }
      if ((num_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
        errmsg = "num_ptr";
      else if ((name_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
        errmsg = "name_ptr";
      else if ((port_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
        errmsg = "port_ptr";
      else if ((psize_ptr = nasd_cheops_walk_str(&ptr)) != NULL) {
        btype_ptr = nasd_cheops_walk_str(&ptr);
      } else {
        psize_ptr = "0";
        btype_ptr = "default";
      }
      if (errmsg != NULL) {
        fprintf(stderr, "CHEOPS MGR: %s:%d bad %s -- skipped\n", fname,
                line_no, errmsg);
        continue;
      }
#if NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0
      if (!quietude)
        fprintf(stderr, "CHEOPS MGR: %s:%d "
                "num_ptr=|%s| name_ptr=|%s| port_ptr=|%s| "
                "psize_ptr=|%s| btype_ptr=|%s|\n", fname, line_no, num_ptr,
                name_ptr, port_ptr, psize_ptr, btype_ptr);
#endif /* NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0 */
      if (nasd_cheops_walk_str(&ptr) != NULL)
        errmsg = "garbage at end of line";
      else
        errmsg = NULL;
      if (sscanf(num_ptr, "%d", &drive_id) != 1)
        errmsg = "drive number did not scan as int";
      else if (strlen(name_ptr) >= NASD_CHEOPS_MAX_FNAME_LEN)
        errmsg = "drive name too long";
      else if (sscanf(port_ptr, "%d", &port_no) != 1)
        errmsg = "port number did not scan as int";
      else if (sscanf(psize_ptr, "%d", &psize) != 1)
        errmsg = "partition size did not scan as int";
      else if (!strncasecmp(btype_ptr, "default", 7))
        btype = NASD_BIND_DEFAULT;
      else if (!strncasecmp(btype_ptr, "colocate", 8))
        btype = NASD_BIND_COLOCATE;
      else if (!strncasecmp(btype_ptr, "msgq", 4))
        btype = NASD_BIND_MSGQ;
#if 0 /* XXX need to figure out how to get the arg */
      else if (!strncasecmp(btype_ptr, "kpdev", 5))
        btype = NASD_BIND_KPDEV;
#endif /* 0 */
      else if (!strncasecmp(btype_ptr, "kpdev_default", 13))
        btype = NASD_BIND_KPDEV_DEFAULT;
      else if (!strncasecmp(btype_ptr, "dce_udp", 7))
        btype = NASD_BIND_DCE_DIRECT_UDP;
      else if (!strncasecmp(btype_ptr, "dce_tcp", 7))
        btype = NASD_BIND_DCE_DIRECT_TCP;
      else if (!strncasecmp(btype_ptr, "srpc", 4))
        btype = NASD_BIND_SRPC;
      if (errmsg == NULL) {
        int k;

        for (k = 0; k < i; k++) {
          if (dr_id[k] == drive_id) {
            errmsg = "duplicate drive id";
            break;
          }
          if (!strcasecmp(dr_name[k], name_ptr) &&
              !strcasecmp(dr_port[k], port_ptr)) {
            errmsg = "duplicate drive name and port";
            break;
          }
        }
      }
      if (errmsg != NULL)
        fprintf(stderr, "CHEOPS MGR: %s:%d error: %s\n", fname, line_no,
                errmsg);
      else {
#if NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0
        if (!quietude)
          fprintf(stderr, "CHEOPS MGR: %s:%d defines drive # %d (%s @ %s)\n",
                  fname, line_no, drive_id, name_ptr, port_ptr);
#endif /* NASD_DEBUG_CHEOPS_INIT_FILE_PARSE > 0 */
        dr_id[i] = drive_id;
        strcpy(dr_name[i], name_ptr);
        strcpy(dr_port[i], port_ptr);
        if (psize == 0)
          dr_psize[i] = part_size;
        else
          dr_psize[i] = psize;
        dr_btype[i] = btype;
        i++;
      }
    } else {
      if (!quietude)
        fprintf(stderr, "CHEOPS MGR: %s:%d does not start with known keyword -- ignored\n",
         fname, line_no);
      continue;
    }
    if (i >= MAX_NUM_DRIVES) {
      fprintf(stderr, "CHEOPS MGR: warning %s:%d too many drives\n",
              fname, line_no);
      break;
    }
  }
  fclose(fp);
  *num_drives = i;
  return 0;
}

static int
dmap_add_drive(
  int                    part_id,
  nasd_cheops_dmap_t    *dm,
  nasd_drive_handle_t    h,
  char                  *dr_name,
  char                  *dr_port,
  int                    dr_id,
  int                    dr_psize,
  int                   *partnum)
{
  int i;
  int rc;
  int pn;
  nasd_boolean_t found;
  nasd_cookie_t in_cookie;
  nasd_status_t nasd_status;
  nasd_rpc_status_t status;
  nasd_key_t in_key;

  i=0;
  found = NASD_FALSE;
  while ((found==NASD_FALSE) && (dm->dinfo[i].dr_name[0] != '\0')) {
    if (!strcmp(dm->dinfo[i].dr_name,dr_name) || (dm->dinfo[i].dr_id==dr_id)) {
      found = NASD_TRUE;
      if (!(!strcmp(dm->dinfo[i].dr_name, dr_name) &&
            (dm->dinfo[i].dr_id == dr_id))) {
        fprintf(stderr, "CHEOPS MGR: drive %s has changed name/id....\n",
                dr_name);
        fprintf(stderr, "CHEOPS MGR: old id %d, new id %d \n",
                dm->dinfo[i].dr_id,dr_id);
        fprintf(stderr, "CHEOPS MGR: old name %s, new name %s \n",
                dm->dinfo[i].dr_name, dr_name);
        exit(1);
      }
    }
    if (found==NASD_TRUE) 
      break;
    else
      i++;
  }
     
  *partnum = dm->dinfo[i].partnum;

  if (found ==NASD_FALSE) {
    dm->dinfo[i].dr_id = dr_id;
    dm->dinfo[i].h = h;
    strcpy(dm->dinfo[i].dr_name, dr_name);
    strcpy(dm->dinfo[i].dr_port, dr_port);
       
    fprintf(stderr, "CHEOPS MGR: Creating %d block partition on "
            "drive %d: %s \n", dr_psize, i, dr_name);

    rc = _nasd_cheops_part_create_h_dr(h, part_id, in_key,
                                       dr_psize, &nasd_status, &status);

    if (rc) {
      fprintf(stderr, "CHEOPS MGR: could not create partition! \n");
      return rc; /* change to handle errors */
    } else
      fprintf(stderr, "CHEOPS MGR: success...\n");

    dm->dinfo[i].partnum = part_id;
    dm->dinfo[i].max_capacity = dr_psize;
    *partnum = part_id;
  }    
  
  return 0;
}

void
static dmap_print(
  nasd_cheops_dmap_t    *dm) 
{
  int i=0;
     
  while ( dm->dinfo[i].dr_name[0] != '\0') {
    fprintf(stderr, "CHEOPS MGR: drive name: %s, id: %d, part num: %d\n",
            dm->dinfo[i].dr_name,
            dm->dinfo[i].dr_id,
            dm->dinfo[i].partnum);
    i++;
  }
}

/* XXX This is called from bsdir.c  Gross.  SNL */

/* extern */ int
dmap_alloc_chunk(
  nasd_cheops_dmap_t    *dm,
  int                    g,
  nasd_disk_ident_t     *dr_id,
  int                   *dr_index)
{
  int i, su;

  i = lastDrive;
  for (su=0; su<g; su++) {
    dr_id[su] = dm->dinfo[i].dr_id;
    dr_index[su] = i;
    i = (i+1) % NumDrives;
  }

  /* lastDrive = (lastDrive+1)%NumDrives;  */
  return 0;
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
