/*
 * ubench.c
 *
 * Perform a series of microbenchmarks on new drives
 *
 * Author: Jim Zelenka
 */
/*
 * 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 <nasd/nasd_getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.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_general.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_mem.h>

char *progname;

int                        niters       = 100;
int                        cr_delay     = 0;
int                        reported_yet = 0;
nasd_sec_keyring_t         keys;
char                      *server_name;
int                        partnum;
int                        kpfd;
nasd_drive_handle_t        h;
nasd_cookie_t              chocolate_chip;
nasd_uint16                protection;
nasd_identifier_t          basic_id;
nasd_identifier_t         *ids;
nasd_cookie_t             *cookies;
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;

int skip_noop     = 0;
int skip_getattr  = 0;
int skip_setattr0 = 0;
int skip_setattr1 = 0;
int skip_create   = 0;
int skip_remove   = 0;

#define DECLARE_TIME \
  double accum_time, d, avg; \
  nasd_timespec_t diff; \
  nasd_timer_t timer;
  int done, i;

#define INIT_TIME \
  accum_time = (double)0.0; \
  done = 0;

#define BEGIN_TIME \
  NASD_TM_START(&timer);

#define END_TIME \
  NASD_TM_STOP(&timer); \
  NASD_TM_ELAPSED_TS(&timer, &diff); \
  done++; \
  d = (double)diff.ts_nsec; \
  d /= (double)1000000000.0; \
  d += (double)diff.ts_sec; \
  accum_time += d;

#define REPORT_TIME(_op_) { \
  d = (double)done; \
  avg = accum_time / d; \
  if (reported_yet == 0) \
    reported_yet = 1; \
  else \
    printf("\n"); \
  printf("OPERATION: %s\n", _op_); \
  printf("  %f seconds for %d operations\n", accum_time, done); \
  printf("  %f seconds average\n", avg); \
  printf("  %f ops/sec\n", (d/accum_time)); \
}


void
do_cr_delay()
{
  struct timeval tv;
  int rc;

  tv.tv_sec = cr_delay / 1000;
  tv.tv_usec = (cr_delay % 1000) * 1000;
  rc = select(1, NULL, NULL, NULL, &tv);
  if (rc) {
    perror("select");
    NASD_PANIC();
  }
}

void
usage()
{
  int i;
  fprintf(stderr, "USAGE: %s [options] servername partnum master_password\n", progname);
  fprintf(stderr, "  -c create delay (milliseconds, default %d)\n", cr_delay);
  fprintf(stderr, "  -l use colocated drive\n");
  fprintf(stderr, "  -n set iterations (default %d)\n", niters);
  fprintf(stderr, "  -k use kernel device\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));
  }
  fprintf(stderr, "  -S skip test\n");
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
  fprintf(stderr, "  -T use DCE-TCP\n");
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
  fflush(stderr);
  exit(1);
}

void
report_err(
  nasd_drive_handle_t   dr_handle,
  nasd_status_t         nasd_status,
  nasd_rpc_status_t     status,
  char                 *op,
  int                   iter)
{
  nasd_error_string_t err_str;

  fprintf(stderr, "ERROR: nasd_status=0x%x (%s) status=0x%x (%s) performing %s\n",
    nasd_status, nasd_error_string(nasd_status), status,
    nasd_cl_error_string(dr_handle, status, err_str), op);
  if (dr_handle) {
    fprintf(stderr, "  dr_handle=0x%lx (type %d (%s))\n",
      (unsigned long)dr_handle, dr_handle->type,
      nasd_drive_handle_type_name(dr_handle));
  }
  else {
    fprintf(stderr, "  dr_handle is NULL!\n");
  }
  if (iter >= 0)
    fprintf(stderr, "  iteration %d\n", iter);
  fflush(stderr);
  exit(1);
}

/*
 * create basic
 */

void
p_create_basic()
{
  nasd_p_create_dr_args_t args;
  nasd_p_create_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_security_param_t sp;

  bzero((char *)&args, sizeof(nasd_p_create_dr_args_t));
  sp.type = NASD_BLACK_KEY;
  sp.partnum = partnum;
  sp.actual_protection = protection;
  
  args.in_partnum = partnum;

  nasd_cl_p_create_dr(h, keys.black_key, &sp, NULL, &args, &res, &status);
  if (status || res.nasd_status) {
    report_err(h, res.nasd_status, status, "nasd_cl_p_create_dr", -1);
  }
  basic_id = res.out_identifier;
}

/*
 * NULL
 */

void
p_noop_u()
{
  DECLARE_TIME
  nasd_status_t nasd_status;
  nasd_rpc_status_t status;


  INIT_TIME
  for(i=0;i<niters;i++) {
    BEGIN_TIME
    nasd_cl_p_null_dr(h, &nasd_status, &status);
    if (status || nasd_status) {
      report_err(h, nasd_status, status, "nasd_cl_p_null_dr", i);
    }
    END_TIME
  }
  REPORT_TIME("nasd_cl_p_null_dr")
}

/*
 * GETATTR
 */

void
p_getattr_u()
{
  DECLARE_TIME
  nasd_p_getattr_dr_args_t args;
  nasd_p_getattr_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_security_param_t sp;

  INIT_TIME

  args.in_identifier = basic_id;
  args.in_partnum = partnum;
  sp.type = chocolate_chip.capability.type; 
  sp.partnum = args.in_partnum;
  sp.actual_protection = protection;

  for(i=0;i<niters;i++) {
    BEGIN_TIME
    nasd_cl_p_getattr_dr(h, chocolate_chip.key, &sp,
                         &chocolate_chip.capability, &args, &res, &status);
    if (status || res.nasd_status) {
      report_err(h, res.nasd_status, status, "nasd_cl_p_getattr_dr", i);
    }
    END_TIME
  }
  REPORT_TIME("nasd_cl_p_getattr_dr")
}

/*
 * SETATTR
 */

void
p_setattr_u(
  int  do_fs_specific)
{
  DECLARE_TIME
  nasd_p_setattr_dr_args_t args;
  nasd_p_setattr_dr_res_t res;
  nasd_security_param_t sp;
  nasd_rpc_status_t status;
  char *opstr;

  INIT_TIME
  opstr = do_fs_specific ? "nasd_cl_setattr_dr (w/fs_specific)"
    : "nasd_cl_setattr_dr (no fs_specific)";
  args.in_identifier = basic_id;
  args.in_partnum = partnum;
  args.in_fieldmask = NASD_ATTR_MODIFY_TIME;
  if (do_fs_specific)
    args.in_fieldmask |= NASD_ATTR_FS_SPECIFIC;
  bzero((char *)&args.in_attribute, sizeof(args.in_attribute));
  args.in_partnum = partnum;
  args.in_guard = 0;

  sp.type = chocolate_chip.capability.type; 
  sp.partnum = args.in_partnum;
  sp.actual_protection = protection;

  for(i=0;i<niters;i++) {
    BEGIN_TIME
    nasd_cl_p_setattr_dr(h, chocolate_chip.key, &sp,
                         &chocolate_chip.capability, &args, &res, &status);
    if (status || res.nasd_status) {
      report_err(h, res.nasd_status, status, opstr, i);
    }
    END_TIME
  }
  REPORT_TIME(opstr)
}

/*
 * CREATE
 */

void
p_create_u()
{
  DECLARE_TIME
  nasd_p_create_dr_args_t args;
  nasd_p_create_dr_res_t res;
  nasd_security_param_t sp;
  nasd_rpc_status_t status;


  INIT_TIME
  bzero((char *)&args, sizeof(nasd_p_create_dr_args_t));

  sp.type = NASD_BLACK_KEY;
  sp.partnum = partnum;
  sp.actual_protection = protection;
  
  args.in_fieldmask = 0;
  args.in_partnum = partnum;

  for(i=0;i<niters;i++) {
    BEGIN_TIME
    nasd_cl_p_create_dr(h, keys.black_key, &sp, NULL, &args, &res, &status);
    if (status || res.nasd_status) {
      report_err(h, res.nasd_status, status, "nasd_cl_p_create_dr", i);
    }
    END_TIME
    ids[i] = res.out_identifier;
    if (cr_delay)
      do_cr_delay();
  }
  REPORT_TIME("nasd_cl_p_create_dr")
}

/*
 * REMOVE
 */

void
p_remove_u()
{
  DECLARE_TIME
  nasd_p_remove_dr_args_t args;
  nasd_p_remove_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_timespec_t tm;
  nasd_security_param_t sp;


  INIT_TIME

  nasd_drive_handle_get_time(h, &tm);
  tm.ts_sec+=(60*60);
    
  args.in_partnum = partnum;

  /* Build an array of capabilities */
  for(i=0;i<niters;i++) {
    nasd_sec_build_capability(partnum, ids[i],
                              (NASD_ACCESS_RIGHTS_GETATTR |
                               NASD_ACCESS_RIGHTS_SETATTR |
                               NASD_ACCESS_RIGHTS_REMOVE),
                              0,
                              tm.ts_sec,
                              protection,
                              (i % 2 == 0 ? NASD_RED_CAPABILITY :
                               NASD_BLACK_CAPABILITY),
                              0,
                              666*666,
                              0,
                              (i % 2 == 0 ? keys.red_key : keys.black_key),
                              &cookies[i]); 
  }

  sp.partnum = args.in_partnum;
  sp.actual_protection = protection;
  
  for(i=0;i<niters;i++) {
    args.in_identifier = ids[i];
    sp.type = cookies[i].capability.type; 
  
    BEGIN_TIME
    nasd_cl_p_remove_dr(h, cookies[i].key, &sp, &cookies[i].capability,
                        &args, &res, &status);
    if (status || res.nasd_status) {
      fprintf(stderr, "i=%d ids[i]=0x%" NASD_ID_FMT "\n", i, ids[i]);
      report_err(h, res.nasd_status, status, "nasd_cl_p_remove_dr", i);
    }
    END_TIME
  }
  REPORT_TIME("nasd_cl_p_remove_dr")
}

int
main(
  int     argc,
  char  **argv)
{
  nasd_status_t rc;
  char c;
  int sec_level=0;

  char *master_password;
  nasd_timespec_t tm;
  progname = argv[0];

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

  while (nasd_getopt(argc, argv, "s:c:klMn:oS:T", &c)) {
    switch(c) {
      case 'c':
        if (sscanf(nasd_optarg, "%d", &cr_delay) != 1)
          usage();
        if (cr_delay < 0)
          usage();
        break;
      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 'n':
        if (sscanf(nasd_optarg, "%d", &niters) != 1)
          usage();
        if (niters <= 0)
          usage();
        break;
      case 's':
        if (sscanf(nasd_optarg, "%d", &sec_level) != 1) {
          usage();
        }
        break;
      case 'S':
        if (!strcasecmp(nasd_optarg, "noop"))
          skip_noop = 1;
        else if (!strcasecmp(nasd_optarg, "getattr"))
          skip_getattr = 1;
        else if (!strcasecmp(nasd_optarg, "setattr0"))
          skip_setattr0 = 1;
        else if (!strcasecmp(nasd_optarg, "setattr1"))
          skip_setattr1 = 1;
        else if (!strcasecmp(nasd_optarg, "create")) {
          skip_create = 1;
          skip_remove = 1;
        }
        else if (!strcasecmp(nasd_optarg, "remove"))
          skip_remove = 1;
        else if (!strcasecmp(nasd_optarg, "all")) {
          skip_noop = 1;
          skip_getattr = 1;
          skip_setattr0 = 1;
          skip_setattr1 = 1;
          skip_create = 1;
          skip_remove = 1;
        }
        break;
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
      case 'T':
        if (nondefault_binding)
          usage();
        nondefault_binding = 1;
        binding_type = NASD_BIND_DCE_DIRECT_TCP;
        break;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
      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++;

  if (nasd_optind >= argc)
    usage();
  master_password=argv[nasd_optind];
  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();
  }

 printf("Security Level %d Protection Options 0x%x\n",sec_level,protection);
  rc = nasd_cl_p_init();
  if (rc) {
    printf("ERROR (%s:%d): cannot init client library, rc=%ld\n",
      __FILE__, __LINE__, (long)rc);
    return(rc);
  }

  NASD_Malloc(ids, niters * sizeof(nasd_identifier_t), (nasd_identifier_t *));
  if (ids == NULL) {
    fprintf(stderr, "ERROR: cannot allocate memory for %d NASD ids\n",
      niters);
    fflush(stderr);
    exit(1);
  }
  bzero((char *)ids, niters * sizeof(nasd_identifier_t));

  NASD_Malloc(cookies, niters * sizeof(nasd_cookie_t), (nasd_cookie_t *));
  if (cookies == NULL) {
    fprintf(stderr, "ERROR: cannot allocate memory for %d NASD cookies\n",
      niters);
    fflush(stderr);
    exit(1);
  }
  bzero((char *)cookies, niters * sizeof(nasd_identifier_t));

  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);
    fflush(stderr);
    exit(1);
  }

  bzero((char *)&chocolate_chip, sizeof(nasd_cookie_t));

  nasd_sec_password_to_keys(master_password, partnum, &keys);

  p_create_basic();

  /* Fill in capability and security param */
  nasd_drive_handle_get_time(h, &tm);
  tm.ts_sec+=(60*60);
    
  nasd_sec_build_capability(partnum, basic_id,
                            (NASD_ACCESS_RIGHTS_READ |
                             NASD_ACCESS_RIGHTS_WRITE | 
                             NASD_ACCESS_RIGHTS_GETATTR |
                             NASD_ACCESS_RIGHTS_SETATTR |
                             NASD_ACCESS_RIGHTS_REMOVE),
                            0,
                            tm.ts_sec,
                            protection,
                            NASD_RED_CAPABILITY,
                            0,
                            666*666,
                            0, keys.red_key,
                            &chocolate_chip);  

  if (skip_noop == 0)
    p_noop_u();
  if (skip_getattr == 0)
    p_getattr_u();
  if (skip_setattr0 == 0)
    p_setattr_u(0);
  if (skip_setattr1 == 0)
    p_setattr_u(1);
  if (skip_create == 0)
    p_create_u();
  if (skip_remove == 0)
    p_remove_u();

  NASD_Free(ids, niters * sizeof(nasd_identifier_t));
  NASD_Free(cookies, niters * sizeof(nasd_cookie_t));

  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_cl_p_shutdown();

  exit(0);
}

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