/*
 * psg.c
 *
 * Test scatter/gather ops for NASD drive
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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 <nasd/nasd_getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_mem.h>

#define BUFLEN 16384
#define LBUFLEN (BUFLEN/sizeof(long))

#define NELEMS 8

char *progname;
nasd_sec_keyring_t keys;
int          docreate = 0;

nasd_error_string_t error_text;

nasd_drive_handle_t        h;
nasd_identifier_t          nasdid;
char                      *server_name;
int                        partnum;
nasd_rpc_status_t          status;
nasd_cookie_t              cookie;
nasd_uint16                protection;
nasd_len_t                 out_len;
nasd_status_t              rc;
int                        binding_type;
int                        binding_args_len;
void                      *binding_args;
char                      *binding_port = NASD_PDRIVE_PORT;
nasd_drive_param_kpdev_t   kpdev_args;
int                        nondefault_binding = 0;

void
usage()
{
  int i;
  fprintf(stderr, "USAGE: %s [options] servername partnum master_password [nasdid]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -c create new object\n");
  fprintf(stderr, "  -k use kernel device\n");
  fprintf(stderr, "  -l use colocated drive\n");
  fprintf(stderr, "  -M use message queues\n");
  fprintf(stderr, "  -s security level\n");
  for(i = 0; i <= NASD_MAX_SECURITY_LEVEL; i++) {
    fprintf(stderr, "     %d %s\n", i, nasd_sec_level_string(i));
  }
  fflush(stderr);
  exit(1);
}

void
do_create()
{
  nasd_p_create_dr_args_t args;
  nasd_p_create_dr_res_t res;
  nasd_security_param_t sec_param;
  nasd_attribute_t *ap;

  printf("Create new object\n");
  bzero((char *)&args,sizeof(nasd_p_create_dr_args_t));

  args.in_partnum = partnum;
  args.in_fieldmask = NASD_ATTR_OBJECT_LEN;
  ap = &args.in_attribute;
  ap->object_len = 3000;
 
  sec_param.type = NASD_BLACK_KEY;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;
  
  nasd_cl_p_create_dr(h,keys.black_key, &sec_param, NULL, &args,
                      &res, &status);

  if (status) {
    fprintf(stderr, "ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      res.nasd_status, nasd_error_string(res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (res.nasd_status) {
    fprintf(stderr, "NASD ERROR: %s\n", nasd_error_string(res.nasd_status));
    fflush(stderr);
    exit(2);
  }
  nasdid = res.out_identifier;

  printf("New object identifier 0x%" NASD_ID_FMT "\n", nasdid);
}

void
dowrite(
  char  *buf,
  off_t  in_offset,
  int    len)
{
  nasd_security_param_t sec_param;
  nasd_p_smpl_op_dr_args_t wr_args;
  nasd_p_fastwrite_dr_res_t wr_res;
  nasd_rpc_status_t status;
  nasd_len_t out_len;

  sec_param.type = cookie.capability.type;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;

  wr_args.in_partnum = partnum;
  wr_args.in_identifier = nasdid;
  wr_args.in_offset = in_offset;
  wr_args.in_len = len;

  nasd_cl_p_write_simple_dr(h, cookie.key, &sec_param, &cookie.capability,
                            &wr_args, buf, &wr_res, &status);
  out_len = wr_res.out_datalen;

  if (status) {
    fprintf(stderr, "WRITE ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      wr_res.nasd_status, nasd_error_string(wr_res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (wr_res.nasd_status) {
    fprintf(stderr, "NASD WRITE ERROR: 0x%x (%s) id=0x%" NASD_ID_FMT "\n",
      wr_res.nasd_status, nasd_error_string(wr_res.nasd_status), nasdid);
    fflush(stderr);
    exit(2);
  }
}

void
doread(
  char  *buf,
  off_t  in_offset,
  int    len)
{
  nasd_security_param_t sec_param;
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_rpc_status_t status;
  nasd_len_t out_len;
  nasd_status_t rc;

  sec_param.type = cookie.capability.type;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;

  rd_args.in_partnum = partnum;
  rd_args.in_identifier = nasdid;
  rd_args.in_offset = in_offset;
  rd_args.in_len = len;

  nasd_cl_p_read_simple_dr(h, cookie.key, &sec_param, &cookie.capability,
                           &rd_args, buf, &rd_res, &status);
  out_len = rd_res.out_datalen;
  rc = rd_res.nasd_status;

  if (status) {
    fprintf(stderr, "READ ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(2);
  }

  if (rc) {
    fprintf(stderr, "NASD READ ERROR: 0x%x (%s)\n", rc, nasd_error_string(rc));
    fflush(stderr);
    exit(2);
  }

  if (out_len != len) {
    fprintf(stderr, "NASD READ ERROR: len=%d out_len=%d\n", len, out_len);
    fflush(stderr);
    exit(2);
  }
}

void
dorangeread(
  nasd_mem_list_t  *memlist,
  off_t             in_offset,
  int               len)
{
  nasd_security_param_t sec_param;
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_rpc_status_t status;

  sec_param.type = cookie.capability.type;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;

  rd_args.in_partnum = partnum;
  rd_args.in_identifier = nasdid;
  rd_args.in_offset = in_offset;
  rd_args.in_len = len;

  nasd_cl_p_range_read_dr(h, cookie.key, &sec_param, &cookie.capability,
                          &rd_args, memlist, &rd_res, &status);

  if (status) {
    fprintf(stderr, "RANGE READ ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      rd_res.nasd_status, nasd_error_string(rd_res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (rd_res.nasd_status) {
    fprintf(stderr, "NASD RANGE READ ERROR: 0x%x (%s)\n",
      rd_res.nasd_status, nasd_error_string(rd_res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (rd_res.out_datalen != len) {
    fprintf(stderr, "NASD RANGE READ ERROR: len=%d out_len=%d\n",
      len, rd_res.out_datalen);
    fflush(stderr);
    exit(2);
  }
}

void
dorangewrite(
  nasd_mem_list_t  *memlist,
  off_t             in_offset,
  int               len)
{
  nasd_security_param_t sec_param;
  nasd_p_smpl_op_dr_args_t wr_args;
  nasd_p_fastwrite_dr_res_t wr_res;
  nasd_rpc_status_t status;

  sec_param.type = cookie.capability.type;
  sec_param.partnum = partnum;
  sec_param.actual_protection = protection;

  wr_args.in_partnum = partnum;
  wr_args.in_identifier = nasdid;
  wr_args.in_offset = in_offset;
  wr_args.in_len = len;

  nasd_cl_p_range_write_dr(h, cookie.key, &sec_param, &cookie.capability,
                           &wr_args, memlist, &wr_res, &status);
  if (status) {
    fprintf(stderr, "RANGE WRITE ERROR: status=0x%x (%s) nasd_status=0x%x (%s)\n",
      status, nasd_cl_error_string(h, status, error_text),
      wr_res.nasd_status, nasd_error_string(wr_res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (wr_res.nasd_status) {
    fprintf(stderr, "NASD RANGE WRITE ERROR: 0x%x (%s)\n",
      wr_res.nasd_status, nasd_error_string(wr_res.nasd_status));
    fflush(stderr);
    exit(2);
  }

  if (wr_res.out_datalen != len) {
    fprintf(stderr, "NASD RANGE WRITE ERROR: len=%d out_len=%d\n",
      len, wr_res.out_datalen);
    fflush(stderr);
    exit(2);
  }
}

int
main(
  int     argc,
  char  **argv)
{
  long *lbuf, *lbuf1, *lbuf2, *lbuf3, *lbuf4;
  char c, *buf, *buf1, *buf2, *buf3, *buf4;
  nasd_mem_list_t elems[NELEMS], *l;

  int i;
  char *master_password;
  nasd_timespec_t tm;
  int sec_level=0;
  int  pass;

  progname = argv[0];

  binding_type = NASD_BIND_DEFAULT;
  binding_args = NULL;
  binding_args_len = 0;

  while (nasd_getopt(argc, argv, "cklMs:", &c)) {
    switch(c) {
      case 'k':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_KPDEV_DEFAULT;
        binding_args = &kpdev_args;
        binding_args_len = sizeof(kpdev_args);
        strcpy(kpdev_args.devname, "/dev/nasdkp0");
        break;
      case 'l':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_COLOCATE;
        binding_args = &kpdev_args;
        binding_args_len = sizeof(kpdev_args);
        strcpy(kpdev_args.devname, "/dev/nasdkp0");
        break;
      case 'M':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_MSGQ;
        break;
      case 's':
        if (sscanf(nasd_optarg, "%d", &sec_level) != 1) {
           usage();
          }
        break;
      case 'c':
        docreate = 1;
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", nasd_optopt);
        usage();
    }
  }

  if (nasd_optind >= argc)
    usage();

  server_name = argv[nasd_optind];

  nasd_optind++;
  if (nasd_optind >= argc)
    usage();

  if (sscanf(argv[nasd_optind], "%d", &partnum) != 1)
    usage();

  nasd_optind++;
  master_password=argv[nasd_optind];
  
  if (docreate == 0) {
    nasd_optind++;
    if (nasd_optind >= argc) {
      printf("If you do not use -c you must specify a nasdid to use\n");
      usage();
    }
    rc = nasd_str_to_nasd_id(argv[nasd_optind], &nasdid);
    if (rc)
      usage();
  }

  nasd_optind++;
  if (nasd_optind < argc)
    usage();

  rc = nasd_sec_seclevel_to_protection(sec_level, &protection);
  if(rc) {
    fprintf(stderr, "Unknown security level %d\n", sec_level);
    usage();
  }

  rc = nasd_cl_p_init();
  if (rc) {
    printf("ERROR (%s:%d): cannot init client library, rc=0x%x (%s)\n",
      __FILE__, __LINE__, rc, nasd_error_string(rc));
    return(rc);
  }

  NASD_Malloc(buf, BUFLEN, (char *));
  if (buf == NULL) {
    fprintf(stderr, "ERROR: no memory\n");
    exit(1);
  }
  NASD_Malloc(buf1, BUFLEN, (char *));
  if (buf1 == NULL) {
    fprintf(stderr, "ERROR: no memory\n");
    exit(1);
  }
  NASD_Malloc(buf2, BUFLEN, (char *));
  if (buf2 == NULL) {
    fprintf(stderr, "ERROR: no memory\n");
    exit(1);
  }
  NASD_Malloc(buf3, BUFLEN, (char *));
  if (buf3 == NULL) {
    fprintf(stderr, "ERROR: no memory\n");
    exit(1);
  }
  NASD_Malloc(buf4, BUFLEN, (char *));
  if (buf4 == NULL) {
    fprintf(stderr, "ERROR: no memory\n");
    exit(1);
  }
  lbuf = (long *)buf;
  lbuf1 = (long *)buf1;
  lbuf2 = (long *)buf2;
  lbuf3 = (long *)buf3;
  lbuf4 = (long *)buf4;

  rc = nasd_bind_to_drive(server_name, binding_port,
    binding_type, binding_args, binding_args_len, &h);
  if (rc) {
    fprintf(stderr, "ERROR: cannot bind to server %s\n", server_name);
    exit(1);
  }

  nasd_sec_password_to_keys(master_password, partnum, &keys);

  for(i=0;i<LBUFLEN;i++) {
    lbuf[i] = (long)i;
  }

  for(pass=0;pass<3;pass++) {
    printf("Begin pass %d\n", pass);
    if (docreate) {
      do_create();
    }
 
    nasd_drive_handle_get_time(h, &tm);
    tm.ts_sec+=(60*60);
    nasd_sec_build_capability(partnum, nasdid,
                              (NASD_ACCESS_RIGHTS_READ |
                               NASD_ACCESS_RIGHTS_WRITE ),
                              0, tm.ts_sec, protection, NASD_RED_CAPABILITY,
                              0, 8*BUFLEN, 0, keys.red_key, &cookie);
  
    for(i=0;i<LBUFLEN;i++) {
      lbuf[i] = (long)i;
    }

    l = NULL;
    for(i=NELEMS-1;i>=0;i--) {
      elems[i].next = l;
      l = &elems[i];
    }

    if (pass == 0) {
      dowrite(buf, 0, BUFLEN);
    }
    else if (pass == 1) {
      printf("Pass 1\n");
      elems[0].addr = buf;
      elems[0].len = BUFLEN;
      elems[0].stride = 0;
      elems[0].nelem = 1;
      elems[0].next = 0;
      dorangewrite(l, 0, BUFLEN);
    }
    else if (pass == 2) {
      printf("Pass 2\n"); 
      elems[0].addr = buf;
      elems[0].len = 8;
      elems[0].stride = 8;
      elems[0].nelem = BUFLEN/8;
      elems[0].next = 0;
      dorangewrite(l, 0, BUFLEN);
    }
    else {
      NASD_PANIC();
    }

    bzero(buf1, BUFLEN);
    doread(buf1, 0, BUFLEN);

    if (bcmp(buf, buf1, BUFLEN)) {
      fprintf(stderr, "Failed basic test\n");
      for(i=0;i<BUFLEN;i++) {
        if (buf[i] != buf1[i]) {
          printf("buf[%d] = %d\tbuf1[%d]=%d\n",i, buf[i], i, buf1[i]);
        }
      }
      exit(1);
    }

    printf("Passed basic test.\n");

    bzero(buf1, BUFLEN);
    bzero(buf2, BUFLEN);
    bzero(buf3, BUFLEN);
    bzero(buf4, BUFLEN);

    elems[0].addr = buf1;
    elems[0].len = BUFLEN;
    elems[0].stride = 0;
    elems[0].nelem = 1;
    elems[0].next = 0;

    dorangeread(l, 0, BUFLEN);

    if (bcmp(buf, buf1, BUFLEN)) {
      fprintf(stderr, "Failed single-vector test\n");
      exit(1);
    }

    printf("Passed single-vector test.\n");
    bzero(buf1, BUFLEN);
    bzero(buf2, BUFLEN);
    bzero(buf3, BUFLEN);
    bzero(buf4, BUFLEN);

    elems[0].addr = buf1;
    elems[0].len = 8;
    elems[0].stride = 8;
    elems[0].nelem = BUFLEN/8;
    elems[0].next = 0;

    dorangeread(l, 0, BUFLEN);

    if (bcmp(buf, buf1, BUFLEN)) {
      fprintf(stderr, "Failed single-strided-vector test\n");
      exit(1);
    }

    printf("Passed single-strided-vector test.\n");
    bzero(buf1, BUFLEN);
    bzero(buf2, BUFLEN);
    bzero(buf3, BUFLEN);
    bzero(buf4, BUFLEN);

    elems[0].addr = buf1;
    elems[0].len = 8;
    elems[0].stride = 8;
    elems[0].nelem = BUFLEN/16;
    elems[0].next = &elems[1];
    elems[1].addr = buf2;
    elems[1].len = 8;
    elems[1].stride = 8;
    elems[1].nelem = BUFLEN/16;
    elems[1].next = 0;

    dorangeread(l, 0, BUFLEN);

    if (bcmp(buf, buf1, BUFLEN/2)) {
      fprintf(stderr, "Failed double-strided-vector test (1)\n");
      exit(1);
    }
    if (bcmp(buf+(BUFLEN/2), buf2, BUFLEN/2)) {
      fprintf(stderr, "Failed double-strided-vector test (2)\n");
      exit(1);
    }

    printf("Passed double-strided-vector test.\n");
    
    bzero(buf1, BUFLEN);
    bzero(buf2, BUFLEN);
    bzero(buf3, BUFLEN);
    bzero(buf4, BUFLEN);
  }

  rc = nasd_unbind_drive(&h);
  if (rc) {
    fprintf(stderr, "ERROR: got 0x%x (%s) from nasd_unbind_drive()\n",
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(2);
  }

  NASD_Free(buf, BUFLEN);
  NASD_Free(buf1, BUFLEN);
  NASD_Free(buf2, BUFLEN);
  NASD_Free(buf3, BUFLEN);
  NASD_Free(buf4, BUFLEN);

  nasd_cl_p_shutdown();

  exit(0);
}

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