/*
 * nasd_cheops_cl_init.c
 *
 * initialization module for the CHEOPS client clerk
 *
 * 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 <sys/errno.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>

#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdev.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_client.h>
#include <nasd/nasd_cheops_client_internal.h>
#include <nasd/nasd_cheops_mgr.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_CHEOPS_MANAGER_RESOURCE_FILE "./cheops.mtab"
#define _NASD_CHEOPS_INIT_DEBUG 0

_nasd_cheops_mgr_handles_t _nasd_cheops_mgr_handles;
int _nasd_cheops_dr_handle_cache_size=16; /* jmb from 8 */
int _nasd_cheops_bs_handle_cache_size=60; /* jmb from 60 */
_nasd_cheops_cache_t *_nasd_cheops_bs_handle_cachep;
_nasd_cheops_cache_t *_nasd_cheops_dr_handle_cachep;
char manager_names[MAX_NUM_MANAGERS][NASD_CHEOPS_MAX_FNAME_LEN];
char manager_ports[MAX_NUM_MANAGERS][NASD_CHEOPS_MAX_FNAME_LEN];

/* prototypes */
int      _nasd_cheops_dr_handle_read(_nasd_cheops_cache_key_t, caddr_t);
int _nasd_cheops_dr_handle_writeback(_nasd_cheops_cache_ent_t *, int);
int      _nasd_cheops_bs_handle_read(_nasd_cheops_cache_key_t, caddr_t);
int _nasd_cheops_bs_handle_writeback(_nasd_cheops_cache_ent_t *, int);
int      _nasd_cheops_read_mtab_file(char *, int *, int, int);

int
_nasd_cheops_cl_init(
  char  *resource_arg,
  int    binding_type,
  int    override_defaults,
  int    quietude)
{
	char name[64];
	char port[64];
	int  num_managers;
	int  i, rc;
	nasd_status_t nrc;
	_nasd_cheops_mgr_handle_t *hp;
	char *resource_file;

	if (resource_arg) {
	  resource_file = resource_arg;
	} else {
	  resource_file = getenv("NASD_CHEOPSMGR_RESOURCE_FILE");
	  if(!resource_file) {
	    resource_file = _NASD_CHEOPS_MANAGER_RESOURCE_FILE;
	  }
	}

	nrc = nasd_cl_p_init();
	if (nrc)
	  return(-1);

  rc = nasd_cheops_client_init();
  if (rc)
    return rc;

	/* read mtab file, discover and bind to a few storage managers */
	rc = _nasd_cheops_read_mtab_file(resource_file, &num_managers,
                                   override_defaults, quietude);
	if (rc)
	  return rc;

	_nasd_cheops_mgr_handles.num_managers = num_managers;
  NASD_Malloc(hp, num_managers*sizeof(_nasd_cheops_mgr_handle_t),
              (_nasd_cheops_mgr_handle_t *));
	if (hp == NULL)
	  return ENOMEM;
	else
	  _nasd_cheops_mgr_handles.hl = hp;

	for (i=0; i< num_managers; i++) {
	  _nasd_cheops_mgr_handles.hl[i].mgr_name = manager_names[i];
	  _nasd_cheops_mgr_handles.hl[i].mgr_port = manager_ports[i];
    nrc = nasd_bind_to_cheops_mgr(manager_names[i],
                                  manager_ports[i],
                                  binding_type,
                                  NULL, 0,
                                  &_nasd_cheops_mgr_handles.hl[i].h);
	  if (nrc)
	    return EIO;
	}

	/* create a cache for drive handles */
	rc = _nasd_cheops_cache_create(_nasd_cheops_dr_handle_cache_size, 
                                 sizeof(_nasd_cheops_dr_handle_t),
                                 _NASD_CHEOPS_CACHE_TYPE_DMAP,
                                 &_nasd_cheops_dr_handle_cachep);
  if (rc)
	  return rc;

	_nasd_cheops_dr_handle_cachep->handle_read_fault =
    _nasd_cheops_dr_handle_read;
	_nasd_cheops_dr_handle_cachep->writeback =
    _nasd_cheops_dr_handle_writeback;

	/* create a cache for object stripe maps */
	rc = _nasd_cheops_cache_create(_nasd_cheops_bs_handle_cache_size, 
                                 sizeof(nasd_cheops_bs_handle_t),
                                 _NASD_CHEOPS_CACHE_TYPE_SMAP,
                                 &_nasd_cheops_bs_handle_cachep);
	if(rc)
	  return rc;

	_nasd_cheops_bs_handle_cachep->handle_read_fault =
    _nasd_cheops_bs_handle_read;
	_nasd_cheops_bs_handle_cachep->writeback =
    _nasd_cheops_bs_handle_writeback;

  _nasd_cheops_doio_init();

	return 0;
}

int
_nasd_cheops_read_mtab_file(
  char          *fname,
  int           *num_managers,
  int            override_defaults,
  int            quietude)
{
  char line[1024];
	FILE *fp;
	int i;
	int j;
	int len;
  int line_no;
  int eof;
  int fatal_err;
  char *errmsg;
  char *ptr;

	if (fname == NULL) {
    fprintf(stderr, "CHEOPS: empty mtab filename\n");
    return EINVAL;
	}
	
	if ((fp = fopen(fname, "r")) == NULL) {
    fprintf(stderr, "CHEOPS: "
            "_nasd_cheops_read_mtab_file() unable to open file %s\n", fname);
	  return ENOENT;
	}
	i = 0;
  line_no = 0;
  eof = 0;
  fatal_err = 0;
	while (fgets(line, sizeof(line), fp) != NULL) {
    int line_too_long;
    int complain;

    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: %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: %s:%d eof reached on incomplete line\n",
                fname, line_no);
      continue;
    }
	  if (line[0] == '#')
	    continue;
	  line[len-1]='\0';
	  if (!strncasecmp(line, "manager", 7)) {
      char *name_ptr = NULL;
      char *port_ptr = NULL;
      int temp;

      errmsg = NULL;
      ptr = line;
      while (*ptr) { if ((*ptr == ' ') || (*ptr == '\t')) break; else ptr++; }
      if ((name_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
        errmsg = "manager name";
      else if ((port_ptr = nasd_cheops_walk_str(&ptr)) == NULL)
        errmsg = "manager port";
      else if (sscanf(port_ptr, "%d", &temp) != 1)
        errmsg = "manager port (does not scan as integer)";
      else if (strlen(name_ptr) >= NASD_CHEOPS_MAX_FNAME_LEN)
        errmsg = "manager name (too long)";
      else if (strlen(port_ptr) >= NASD_CHEOPS_MAX_FNAME_LEN)
        errmsg = "manager port (too long)";
      else if (i >= MAX_NUM_MANAGERS) {
        errmsg = "too many manager definitions";
        fatal_err = 1;
      }
      if (errmsg != NULL) {
        fprintf(stderr, "CHEOPS: %s:%d bad %s -- skipped\n", fname, line_no,
                errmsg);
        continue;
      }
      strcpy(manager_names[i], name_ptr);
      strcpy(manager_ports[i], port_ptr);
	    i++;
	  } else if (!strncasecmp(line, "default", 7)) {
      char *what_ptr;
      char *val_ptr;

      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: %s:%d bad %s -- skipped\n",
                fname, line_no, errmsg);
        continue;
      } else if (nasd_cheops_walk_str(&ptr) != NULL) {
        fprintf(stderr, "CHEOPS: %s:%d garbage at end of line "
                "-- skipped\n", fname, line_no);
        continue;
      }
      errmsg = NULL;
      if (!strcasecmp(what_ptr, "io_threads")) {
        int temp = -2;

        if (sscanf(val_ptr, "%d", &temp) != 1)
          errmsg = "io_thread count (does not scan as int)";
        else if (temp < 1)
          errmsg = "io_thread count not sane";
        else
          _nasd_cheops_doio_n_threads = temp;
      } else
        errmsg = "unrecognized parameter name";
      if (errmsg)
        fprintf(stderr, "CHEOPS: %s:%d %s -- skipped\n", fname, line_no,
                errmsg);
      else if (!quietude)
        fprintf(stderr, "CHEOPS: %s;%d %s = %s\n", fname, line_no,
                what_ptr, val_ptr);
    } else
      fprintf(stderr, "CHEOPS: %s:%d unrecognized directive -- skipped\n",
              fname, line_no);
    if (fatal_err)
      break;
	}
  fclose(fp);
	*num_managers = i;
	return 0;
}

void
_nasd_cheops_cl_show_mgrs(void)
{
  int i;
	_nasd_cheops_mgr_handles_t *handles;

	handles = &_nasd_cheops_mgr_handles;
	fprintf(stderr, "CHEOPS: num managers: %d\n", handles->num_managers);
	for (i=0; i< handles->num_managers; i++)
	  fprintf(stderr, "CHEOPS: %s: %s\n",
            handles->hl[i].mgr_name, handles->hl[i].mgr_port);
}

int
_nasd_cheops_dr_handle_writeback(
  _nasd_cheops_cache_ent_t      *e,
  int                            bsize)
{
  return 0;
}

int
_nasd_cheops_dr_handle_read(
  _nasd_cheops_cache_key_t       key,
  caddr_t                        bp)
{
  int rc=0;
  int mgr_id;
  int fd=0;
  nasd_disk_ident_t dr_id;
  nasd_cheops_handle_t mh;
  nasd_drive_handle_t out_h;
  _nasd_cheops_dr_handle_t dh;
  nasd_cheops_cl_dinfo_t  dinfo;
  nasd_cookie_t in_cookie;
  nasd_rpc_status_t op_status;
  nasd_cheops_dr_lu_args_t args;
  nasd_cheops_dr_lu_res_t res;

  dr_id = key.record_id;
  mgr_id = 0;                           /* XXX fix this */
  mh = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  args.in_drive_id = dr_id;
#if _NASD_CHEOPS_INIT_DEBUG > 0
  fprintf(stderr, "CHEOPS: clerk issuing dr_lookup due to cache miss "
          "(disk ident %hd, bp=%08lx)\n", dr_id, bp);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
  nasd_cheops_client_dr_lookup(mh, &args, &res, &op_status);
#if _NASD_CHEOPS_INIT_DEBUG > 0
  fprintf(stderr, "CHEOPS: dr_lookup(%hd) op_status=%d nasd_status=%d\n",
          dr_id, op_status, res.out_nasd_status);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
  if (op_status) 
    rc = op_status;
  else if (res.out_nasd_status)
    rc = res.out_nasd_status;
  else {
    bcopy((void *)&res.out_dr_info, (void *)&dinfo, sizeof(dinfo));
#if _NASD_CHEOPS_INIT_DEBUG > 0
    fprintf(stderr, "CHEOPS: dr_lookup(%hd) => name:%s port:%s\n", dr_id,
            dinfo.dr_name, dinfo.dr_port);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
    rc = nasd_bind_to_drive((char *)dinfo.dr_name, (char *)dinfo.dr_port,
                            NASD_BIND_DEFAULT, NULL, 0, &out_h);
    if (rc) {
      fprintf(stderr,
              "CHEOPS: error: failed to bind to drive %s at port %s\n", 
              dinfo.dr_name, dinfo.dr_port);
    } else {
#if _NASD_CHEOPS_INIT_DEBUG > 0
      fprintf(stderr, "CHEOPS: bound to drive %s:%s\n", dinfo.dr_name,
              dinfo.dr_port);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
      dh.h = out_h;
      dh.fd = fd;
      dh.partnum = dinfo.partnum;
      bcopy(dinfo.dr_port, dh.dr_port, NASD_CHEOPS_DR_NAMELEN);
      bcopy(dinfo.dr_name, dh.dr_name, NASD_CHEOPS_DR_NAMELEN);
      bcopy((char *)&dh, bp, sizeof(_nasd_cheops_dr_handle_t));
#if _NASD_CHEOPS_INIT_DEBUG > 0
      fprintf(stderr, "CHEOPS: dr_lookup faulted in %s:%s\n",
              dinfo.dr_name, dinfo.dr_port);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
    }
  }
  return rc;
}

int
_nasd_cheops_bs_handle_writeback(
  _nasd_cheops_cache_ent_t      *e,
  int                            bsize)
{
  return 0;
}

int
_nasd_cheops_bs_handle_read(
  _nasd_cheops_cache_key_t       key,
  caddr_t                        bp)
{
  int rc;
  int mgr_id;
  nasd_cheops_handle_t h;
  nasd_cookie_t in_cookie;
  nasd_rpc_status_t op_status;
  nasd_identifier_t bs_id;
  nasd_cheops_bs_handle_t out_bs_handle;
  nasd_cheops_bs_lu_args_t args;
  nasd_cheops_bs_lu_res_t res;

  bs_id = key.record_id;
  mgr_id = 0;
  h = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  args.in_bsid = bs_id;
  nasd_cheops_client_bs_lookup(h, &args, &res, &op_status);
#if _NASD_CHEOPS_INIT_DEBUG > 0
  fprintf(stderr, "CHEOPS: "
          "clerk: issued bs_lookup due to cache miss (object=%"
          NASD_64u_FMT ") op_status=%d nasd_status=%d\n", bs_id,
          op_status, res.out_nasd_status);
#endif /* _NASD_CHEOPS_INIT_DEBUG > 0 */
  if (op_status)
    rc = op_status;
  else if (res.out_nasd_status)
    rc = res.out_nasd_status;
  else {
    bcopy((char *)&res.out_bs_handle, bp, sizeof(nasd_cheops_bs_handle_t));
    rc = 0;
  }
  return rc;
}

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