#include "includes.h"

pstring global_scope = "";
static char *sockops = "TCP_NODELAY SO_SNDBUF=131072 SO_RCVBUF=131072 SO_SNDLOWAT=16384";
static struct cli_state current_cli;
static char *client_txt = "client_oplocks.txt";
static int nprocs=1, numops=100;
static int procnum; /* records process count number when forking */
static fstring host = "", workgroup = "", share = "", password = "", \
        username = "", myname ="", host_addr;
char **host_address;
int host_addresses = 0;
fstring pre_path = "";  /* String to prepend to the path */
int line_count = 0;
int nbio_id;
static BOOL share_needs_server = False;
static BOOL use_oplocks;
static BOOL use_level_II_oplocks;
static BOOL use_kerberos;
static int max_protocol = PROTOCOL_NT1; /* ? */
static BOOL print_stats = True;
BOOL error_exit = 1;  /* Exit if an error occurs */
char *buf = NULL;

extern unsigned int tot_ent;
extern int read_entries[1000000];
#define ival(s) strtol(s, NULL, 0)

static struct timeval tp1,tp2;
static struct timeval ct1,ct2; /* For timing commands */

enum states bm_state;

int verbose = 0;
volatile pid_t *child_status;

volatile struct cmd_struct *proc_cmd_info;

static struct {
	char *name; /* name used in results */
	char *disable_name; /* name used in disable (-d) option */
	unsigned id; /* cmd id */
} cmd_names[] = {
	{"Close", "CLOSE", CMD_CLOSE},
	{"DelTree", "DELTREE", CMD_DELTREE},
	{"Find First", "FIND_FIRST", CMD_FIND_FIRST},
	{"Flush", "FLUSH", CMD_FLUSH},
	{"Locking & X", "LOCKINGX", CMD_LOCKINGX},
	{"NTCreate&X", "NTCREATEX", CMD_NTCREATEX},
	{"NTCreate&X Open", "NTCREATEX_OPEN", CMD_NTCREATEX_OPEN},
	{"Query File Info", "QUERY_FILE_INFO", CMD_QUERY_FILE_INFO},
	{"Query File System Info", "QUERY_FS_INFO", CMD_QUERY_FS_INFO},
	{"Query Path Info", "QUERY_PATH_INFO", CMD_QUERY_PATH_INFO},
	{"Read & X", "READX", CMD_READX},
	{"Rename", "RENAME", CMD_RENAME},
	{"Rmdir", "RMDIR", CMD_RMDIR},
	{"Unlink","UNLINK", CMD_UNLINK},
	{"Write & X", "WRITEX", CMD_WRITEX},
	{NULL, NULL, 0}
};

int cmd_enabled[CMD_MAX_CMD + 1];

volatile struct cmd_struct *cmd_info;

void show_stats(const struct cmd_struct *cmd_info)
{
  int i;

  for (i = 0; i <= CMD_MAX_CMD; i++) {
    fprintf(stdout, "%-22s %8d    %8d  %10d  %10lld   %5.1f%%\n",
	    cmd_names[i].name, cmd_info[i].count, 
	    cmd_info[i].min_micro_sec,
	    cmd_info[i].max_micro_sec,
	    (cmd_info[i].count ? 
	     cmd_info[i].total_micro_sec / cmd_info[i].count : 0),
	    100.0*cmd_info[i].total_micro_sec/cmd_info[i].count);
  }

}

/*
 * We should be smarter about these timers ...
 */
void start_cmd_timer(void)
{
  gettimeofday(&ct1, NULL);
}

/*
 * This means no more than bout 4096 seconds duration
 */

int end_cmd_timer(void)
{
	gettimeofday(&ct2,NULL);
	return((ct2.tv_sec - ct1.tv_sec)*1000000 + 
	       (ct2.tv_usec - ct1.tv_usec));
}

void start_timer(void)
{
	gettimeofday(&tp1,NULL);
}

double end_timer(void)
{
	gettimeofday(&tp2,NULL);
	return((tp2.tv_sec - tp1.tv_sec) + 
	       (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
}


/* return a pointer to a anonymous shared memory segment of size "size"
   which will persist across fork() but will disappear when all processes
   exit 

   The memory is not zeroed 

   This function uses system5 shared memory. It takes advantage of a property
   that the memory is not destroyed if it is attached when the id is removed
   */
void *shm_setup(int size)
{
	int shmid;
	void *ret;

	shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
	if (shmid == -1) {
		printf("can't get %d bytes of shared memory\n", size);
		exit(1);
	}
	ret = (void *)shmat(shmid, 0, 0);
	if (!ret || ret == (void *)-1) {
		printf("can't attach to shared memory\n");
		return NULL;
	}
	/* the following releases the ipc, but note that this process
	   and all its children will still have access to the memory, its
	   just that the shmid is no longer valid for other shm calls. This
	   means we don't leave behind lots of shm segments after we exit 

	   See Stevens "advanced programming in unix env" for details
	   */
	shmctl(shmid, IPC_RMID, 0);
	
	return ret;
}

static BOOL open_nbt_connection(struct cli_state *c)
{
	struct nmb_name called, calling;
	struct in_addr ip;

	ZERO_STRUCTP(c);

	make_nmb_name(&calling, myname, 0x0);
	make_nmb_name(&called , host, 0x20);

        zero_ip(&ip);

	if (!cli_initialise(c) || 
	    !cli_connect(c, (host_addr[0]? host_addr : host), &ip)) {
		printf("Failed to connect with %s\n", host);
		return False;
	}

	c->force_dos_errors = False;
	c->capabilities |= CAP_STATUS32;

	c->use_kerberos = use_kerberos;

	c->timeout = 120000; /* set a really long timeout (2 minutes) */
	if (use_oplocks) c->use_oplocks = True;
	if (use_level_II_oplocks) c->use_level_II_oplocks = True;

	if (!cli_session_request(c, &calling, &called)) {
		printf("%s rejected the session\n",host);
		cli_shutdown(c);
		return False;
	}

	return True;
}

BOOL torture_open_connection(struct cli_state *c)
{
	ZERO_STRUCTP(c);

	if (!open_nbt_connection(c)) {
		return False;
	}

	if (!cli_negprot(c)) {
		printf("%s rejected the negprot (%s)\n",host, cli_errstr(c));
		cli_shutdown(c);
		return False;
	}

	if (!cli_session_setup(c, username, 
			       password, strlen(password),
			       password, strlen(password),
			       workgroup)) {
		printf("%s rejected the sessionsetup (%s)\n", host, cli_errstr(c));
		cli_shutdown(c);
		return False;
	}

	if (!cli_send_tconX(c, share, "?????",
			    password, strlen(password)+1)) {
		printf("%s refused tree connect (%s)\n", host, cli_errstr(c));
		cli_shutdown(c);
		return False;
	}

	return True;
}

BOOL torture_close_connection(struct cli_state *c)
{
	BOOL ret = True;
	if (!cli_tdis(c)) {
		printf("tdis failed (%s)\n", cli_errstr(c));
		ret = False;
	}

        cli_shutdown(c);

	return ret;
}

static double create_procs(BOOL (*fn)(int), BOOL *result)
{
	int i, status, exited, syncing;
	volatile BOOL *child_status_out;
	int synccount;
	int tries = 8;

	synccount = 0;

	child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*(nprocs+10));
	if (!child_status) {
		printf("Failed to setup shared memory\n");
		return -1;
	}

	child_status_out = (volatile BOOL *)shm_setup(sizeof(BOOL)*(nprocs+10));
	if (!child_status_out) {
		printf("Failed to setup result status shared memory\n");
		return -1;
	}

	for (i = 0; i < nprocs; i++) {
		child_status[i] = 0;
		child_status_out[i] = True;
	}

	proc_cmd_info = (volatile struct cmd_struct *)shm_setup(sizeof(struct cmd_struct) * (CMD_MAX_CMD + 1) * nprocs);

	if (!proc_cmd_info) {
	  printf("Failed to setup the latency shared memory: %s\n", strerror(errno));
	  return -1;
	}

	start_timer();

	for (i=0;i<nprocs;i++) {
		procnum = i;
		if (fork() == 0) {
			pid_t mypid = getpid();
			int j;

			/*
			 * Now, initialize the host addresses setup
			 * so we can round robin around them
			 */
			if (host_addresses)
				strncpy(host_addr, 
					host_address[i%host_addresses], 
					sizeof(fstring) - 1);

			if (verbose & host_addr[0])
			  fprintf(stderr, "Connecting to %s\n", host_addr);

			sys_srandom(((int)mypid) ^ ((int)time(NULL)));

			slprintf(myname,sizeof(myname),"CLIENT%d", i);

			/* 
			 * Some setup ...
			 */
  
			cmd_info = (struct cmd_struct *)(proc_cmd_info + (i * (CMD_MAX_CMD + 1)));
			fprintf(stderr, "child cmd_info: %0X\n", cmd_info);
			for (j = 0; j <= CMD_MAX_CMD; j++) {

			  cmd_info[j].count = 0;
			  cmd_info[j].min_micro_sec = 0;
			  cmd_info[j].max_micro_sec = 0;
			  cmd_info[j].total_micro_sec = 0;
			  cmd_info[j].total_sum_of_squares = 0;
      
			}

			while (1) {
				memset(&current_cli, 0, sizeof(current_cli));
				if (torture_open_connection(&current_cli)) break;
				if (tries-- == 0) {
					printf("pid %d failed to start\n", (int)getpid());
					_exit(1);
				}
				msleep(10);	
			}

			child_status[i] = getpid();

			while (child_status[i] && end_timer() < 15) msleep(2);

			child_status_out[i] = fn(i);
			if (verbose)
  				fprintf(stderr, "Tot_ent: %d\n", tot_ent);
  			if (verbose && tot_ent) {

 			    int i;
     				for (i=1; i < tot_ent; i++)
        			fprintf(stderr, "Read %d bytes\n", read_entries[i]);

  				}
			if (verbose)
			  show_stats((const struct cmd_struct *)cmd_info);
			child_status[i] = -1; /* We are gone ... */
			_exit(0);
		}
	}

	do {
		synccount = 0;
		for (i=0;i<nprocs;i++) {
			if (child_status[i]) synccount++;

		}

		if (synccount == nprocs) break;
		msleep(50);
	} while (end_timer() < 30);

	if (synccount != nprocs) {
		printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
		*result = False;
		return end_timer();
	}

	/* start the client load */
	start_timer();

	for (i=0;i<nprocs;i++) {
		child_status[i] = 0;
	}

	printf("%d clients started\n", nprocs);

	/*
	 * Now we should wait for sync events as well as exit of the
	 * processes ...
	 *
	 * A Sync will be signalled by a status of -1
	 */

	exited = 0;
	while (exited != nprocs) {

	  for (i = 0; i < nprocs; i++) {

	    if (child_status[i] < 0) 
	      exited++;
	    else if (child_status[i] > 0)
	      syncing++;

	  }

	  if (exited == nprocs) break;

	  if (syncing == nprocs) {

	    for (i = 0; i < nprocs; i++)
	      child_status[i] = 0;
	      
	  }

	  msleep(10);
	  syncing = 0;
	  exited = 0;
	}

	for (i=0;i<nprocs;i++) {
		while (waitpid(0, &status, 0) == -1 && errno == EINTR) /* noop */ ;
	}

	printf("\n");
	
	for (i=0;i<nprocs;i++) {
		if (!child_status_out[i]) {
			*result = False;
		}
	}
	return end_timer();
}

/* run a test that simulates an approximate netbench client load */
static BOOL run_netbench(int client)
{
	struct cli_state cli;
	int i;
	fstring fname;
	pstring line;
	char cname[20];
	FILE *f;
	char *params[20];
	BOOL correct = True;

	cli = current_cli;

	nbio_id = client;

	cli_sockopt(&cli, sockops);

	nb_setup(&cli);

	slprintf(cname, sizeof(cname), "client%d", client);

	f = fopen(client_txt, "r");

	if (!f) {
		perror(client_txt);
		return False;
	}

	while (fgets(line, sizeof(line)-1, f)) {
		line_count++;

		line[strlen(line)-1] = 0;

		/*printf("[%d] %s\n", line_count, line);*/

		all_string_sub(line,"client1", cname, sizeof(line));
		
		/* parse the command parameters */
		params[0] = strtok(line," ");
		i = 0;
		while (params[i]) params[++i] = strtok(NULL," ");

		params[i] = "";

		if (i < 1) continue;

		if (!strncmp(params[0],"SMB", 3)) {
			printf("ERROR: You are using a dbench 1 load file\n");
			exit(1);
		}

		if (!strcmp(params[0], "BM_SETUP")) {
		  bm_state = BM_SETUP;
		} else if (!strcmp(params[0], "BM_WARMUP")) {
		  bm_state = BM_WARMUP;
		} else if (!strcmp(params[0], "BM_MEASURE")) {
		  bm_state = BM_MEASURE;
		  if (verbose)
		    fprintf(stderr, "setting state to BM_MEASURE\n");
		} else if (!strcmp(params[0],"RECONNECT")) {
 		  if (verbose)
		    fprintf(stderr, "Reconnecting ...\n");
		  torture_close_connection(&cli);
		  memset(&current_cli, 0, sizeof(current_cli));
		  if (!torture_open_connection(&current_cli)) {
		    fprintf(stderr, "Failed to reconnect ...%s\n", strerror(errno));
		    exit(1);
		  }
		  cli = current_cli;
		  cli_sockopt(&cli, sockops);
		} else if (!strcmp(params[0], "SYNC")) {
		  int length = atoi(params[1]), st = 0;
		  /* Sync (wait) for params[1] seconds before continuing */
		  if (verbose)
		    fprintf(stderr, "Syncing for %d seconds\n", length);
		  while (child_status[i] && st < (length*1000)) {
		    msleep(2);
		  }
		} else if (!strcmp(params[0],"NTCreateX")) {
		  if (cmd_enabled[CMD_NTCREATEX])
		    nb_createx(params[1], ival(params[2]), ival(params[3]), 
			       ival(params[4]));
		} else if (!strcmp(params[0],"Close")) {
		  if (cmd_enabled[CMD_CLOSE])
		    nb_close(ival(params[1]));
		} else if (!strcmp(params[0],"Rename")) {
		  if (cmd_enabled[CMD_RENAME])
		    nb_rename(params[1], params[2]);
		} else if (!strcmp(params[0],"Unlink")) {
		  if (cmd_enabled[CMD_UNLINK])
		    nb_unlink(params[1]);
		} else if (!strcmp(params[0],"Deltree")) {
		  if (cmd_enabled[CMD_DELTREE])
		    nb_deltree(params[1]);
		} else if (!strcmp(params[0],"Rmdir")) {
		  if (cmd_enabled[CMD_RMDIR])
		    nb_rmdir(params[1]);
		} else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) {
		  if (cmd_enabled[CMD_QUERY_PATH_INFO])
		    nb_qpathinfo(params[1]);
		} else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) {
		  if (cmd_enabled[CMD_QUERY_FILE_INFO])
		    nb_qfileinfo(ival(params[1]));
		} else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) {
		  if (cmd_enabled[CMD_QUERY_FS_INFO])
			nb_qfsinfo(ival(params[1]));
		} else if (!strcmp(params[0],"FIND_FIRST")) {
		  if (cmd_enabled[CMD_FIND_FIRST])
		    nb_findfirst(params[1]);
		} else if (!strcmp(params[0],"WriteX")) {
		  if (cmd_enabled[CMD_WRITEX])
		    nb_writex(ival(params[1]), 
			      ival(params[2]), ival(params[3]), ival(params[4]));
		} else if (!strcmp(params[0],"ReadX")) {
		  if (cmd_enabled[CMD_READX])
		    nb_readx(ival(params[1]), 
			     ival(params[2]), ival(params[3]), ival(params[4]));
		} else if (!strcmp(params[0],"Flush")) {
		  if (cmd_enabled[CMD_FLUSH])
		    nb_flush(ival(params[1]));
		} else if (!strcmp(params[0],"LockingX")) {
		  if (cmd_enabled[CMD_LOCKINGX])
		    nb_lock(ival(params[1]), ival(params[2]),
			    ival(params[3]), ival(params[4]),
			    ival(params[5]), ival(params[6]));
		} else {
			printf("Unknown operation %s\n", params[0]);
			exit(1);
		}
	}
	fclose(f);

	nb_cleanup();

	if (!torture_close_connection(&cli)) {
		correct = False;
	}
	
	return correct;
}


/* run a test that simulates an approximate netbench client load */
static BOOL run_nbench(int dummy)
{
	volatile double t, totb;
	BOOL correct = True;

	nbio_shmem(nprocs);

	nbio_id = -1;

	signal(SIGALRM, nb_alarm);
	alarm(1);
	t = create_procs(run_netbench, &correct);
	alarm(0);

	printf("\nThroughput %g MB/sec\n", 
	       1.0e-6 * nbio_total() / t);
	printf("Total bytes read   : %10.0f\n", nbio_bytes_in());
	printf("Total bytes written: %10.0f\n", nbio_bytes_out());
	print_nbio_stats();
	return correct;
}

static void usage(void)
{
	int i;
	fprintf(stderr, "usage: cifs_bm [options] //server/share\n");
	fprintf(stderr, "where options can be:\n");
	fprintf(stderr, "\t-c <client-file>\tspecifies the client file to use\n");
	fprintf(stderr, "\t-b <size>\t\tspecifies the buffer space used for xmit/recv\n");
	fprintf(stderr, "\t-I <IP>\t\tspecifies the IP to connect to\n");
	fprintf(stderr, "\t-N <num>\t\tThe number of copies of cifs_bm to run\n");
	fprintf(stderr, "\t-o <num>\t\tThe number of operations to perform\n");
	fprintf(stderr, "\t-O\t\tUse oplocks when opening/creating files\n");
	fprintf(stderr, "\t-m\t\tThe maximum protocol to use\n");
	fprintf(stderr, "\t-W<wg>\t\tThe workgroup that we are in\n");
	fprintf(stderr, "\t-U\t\tThe username and (%)passwd to use\n");
	fprintf(stderr, "\t-v\t\tSets verbose mode, where more errors are reported\n");
	fprintf(stderr, "\t-p<path>\t\tPath to prepend to all given in the script file\n");
	fprintf(stderr, "\t-d<command+>\t\tDisable commands ...\n");
	fprintf(stderr, "\t-s\t\tShare name needs servername to server\n");
	fprintf(stderr, "\t-n\t\tNo stats, just throughput\n");
	
	fprintf(stderr,"\nCommand names:\n");
	for (i=0;cmd_names[i].name;i++) {
		fprintf(stderr,"%-17s: %s\n", 
			cmd_names[i].disable_name, cmd_names[i].name);
	}
}

void check_disabled(char *cmds)
{
	char *tok;
	int i;

	for (tok=strtok(cmds, ","); tok; tok=strtok(NULL, ",")) {
		for (i=0;cmd_names[i].name; i++) {
			if (strcmp(tok, cmd_names[i].disable_name) == 0) {
				cmd_enabled[cmd_names[i].id] = 0;
				break;
			}
		}
		if (!cmd_names[i].name) {
			printf("ERROR: Unknown command '%s' in disable\n", tok);
			exit(1);
		}
	}
	
	for (i = 0; i<=CMD_MAX_CMD; i++)
		printf("%d ", cmd_enabled[i]);
	printf("\n");
}

/*
 * We need to find out how many hosts in the list, separated by comma
 * and then allocate space for pointers to each of them
 */
void process_hosts(char *list)
{
  char *saved = strdup(list);
  char *tok;
  int count = 0;

  tok = strtok(list, ",");

  while (tok) {

    count = count + 1;
    tok = strtok(NULL, ",");

  }

  /* Now allocate some space for the addresses */

  if (!(host_address = malloc(count * sizeof(char *)))) {

    fprintf(stderr, "Unable to allocate space for address table for %d host addresses", count);
    exit(1);

  }

  host_addresses = count;

  for (count = 0; count < host_addresses; count++) {
    host_address[count] = strtok((count?NULL:saved), ",");
  }
}

/*
  show results in main process
*/
static void show_results(void)
{
	struct cmd_struct tot_cmd_info[CMD_MAX_CMD + 1];
	int i, j;
	unsigned grand_total = 0;

	for (j = 0; j <= CMD_MAX_CMD; j++) {
		tot_cmd_info[j].count = 0;
		tot_cmd_info[j].min_micro_sec = 0;
		tot_cmd_info[j].max_micro_sec = 0;
		tot_cmd_info[j].total_micro_sec = 0;
	}

	for (j = 0; j < nprocs; j++) { 
		cmd_info = proc_cmd_info + (j * (CMD_MAX_CMD + 1));
		fprintf(stderr, "cmd_info: %0X\n", cmd_info);
		for (i = 0; i <= CMD_MAX_CMD; i++) {
			tot_cmd_info[i].count += cmd_info[i].count;
			/*
			 * Because we run out of bits for the total
			 * latency in large runs, We keep the average
			 * in total_micro_sec and recompute things
			 */
			tot_cmd_info[i].total_micro_sec += cmd_info[i].total_micro_sec; 
			grand_total += cmd_info[i].total_micro_sec;
			if (!tot_cmd_info[i].min_micro_sec || 
			    tot_cmd_info[i].min_micro_sec > cmd_info[i].min_micro_sec)
				tot_cmd_info[i].min_micro_sec = cmd_info[i].min_micro_sec;
			if (tot_cmd_info[i].max_micro_sec < cmd_info[i].max_micro_sec)
				tot_cmd_info[i].max_micro_sec = cmd_info[i].max_micro_sec;
			
		}
	}
	
	fprintf(stdout, "  Command:                Count Min Latency Max Latency Ave Latncy      Cost\n");
	fprintf(stdout, "                                (micro sec) (micro sec) (micro sec)\n");
	
	for (i = 0; i <= CMD_MAX_CMD; i++) {
		fprintf(stdout, "%-22s %8d    %8d  %10d  %10lld   %5.1f%%\n",
			cmd_names[i].name, tot_cmd_info[i].count, 
			tot_cmd_info[i].min_micro_sec,
			tot_cmd_info[i].max_micro_sec,
			(tot_cmd_info[i].count ? 
			 tot_cmd_info[i].total_micro_sec / tot_cmd_info[i].count : 0),
			100.0*tot_cmd_info[i].total_micro_sec/grand_total);
	}
}


int main(int argc, char *argv[])
{
  int dummy, i, j, tot_cmds;
  extern char *optarg;
  extern int optind;
  char *p;
  int opt;
  int buf_size = 256*1024;
 
  for (i = 0; i <= CMD_MAX_CMD; i++)
	cmd_enabled[i] = 1;
 
  while ((opt = getopt(argc, argv, "Eeb:d:vI:N:Oo:m:nLc:U:W:p:st:")) != EOF) {
    switch (opt) {
    case 'b':
	buf_size = atoi(optarg) * 1024;
	if (!buf_size) {
		usage();
		exit(1);
	}
	break;

    case 'd':
      check_disabled(optarg);
      break;

    case 'e':
      error_exit = 1;
      break;

    case 'E':
      error_exit = 0;
      break;

    case 'v':
        verbose += 1;
        break;

    case 'I':
	process_hosts(optarg);
	break;

    case 'W':
        strncpy(workgroup, optarg, sizeof(workgroup) - 1);
        break;

    case 'N':
        nprocs = atoi(optarg);
        break;

    case 'n':
	print_stats = False;
	break;

    case 'o':
        numops = atoi(optarg);
        break;

    case 'O':
	use_oplocks = True;
	break;

    case 'm':
        max_protocol = interpret_protocol(optarg, max_protocol);
        break;

    case 'p':
	strncpy(pre_path, optarg, sizeof(pre_path) - 1);
	all_string_sub(pre_path, "/", "\\", 0);
	break;

    case 's':
	share_needs_server = True;
	break;

    case 't':
      sockops = optarg;
      /*strncpy(sockops, optarg, sizeof(sockops) - 1);*/
      break;

    case 'U':
    {
        char *lp;
        strncpy(username, optarg, sizeof(username) - 1);  /* Have to split this looking for passwd if there */
        if ((lp = strchr(username, '%'))) {
            *lp = 0;  /* Split the string */
            strncpy(password, lp+1, sizeof(password) - 1);
            memset(strchr(optarg, '%')+1, 'X', strlen(password));
        }
    }

      break;

    case 'c':
      client_txt = optarg;
      break;

    default:
        usage();
        exit(1);
      break;
    }
  }

  argc -= optind;
  argv += optind;

  if (argc < 1) {
      usage();
      exit(1);
  }

  for(p = argv[0]; *p; p++)
      if(*p == '\\')
          *p = '/';
 
  if (strncmp(argv[0], "//", 2)) {
      usage();
      exit(1);
  }

  strncpy(host, &argv[0][2], sizeof(host) - 1);
  p = strchr(host, '/');
  
  if (!p) {
      usage();
      exit(1);
  }
  *p = 0;

  /*
   * Some servers want the share to be of the form \\server\share
   * so we reconstruct the share the way it should be.
   * Eventually, we should perhaps do more
   */

  if (share_needs_server) {
    strncpy(share, "\\\\", sizeof(share) - 1);
    strncat(share, host, sizeof(share) - 2 - 1);
    strncat(share, "\\", sizeof(share) - 2 - strlen(host) - 1);
    strncat(share, p+1, sizeof(share) -2 - strlen(host) - 1 - 1);
  }
  else {
    strncpy(share, p+1, sizeof(share) - 1);
  }

  if (!workgroup[0]) {
      fprintf(stderr, "Workgroup must be specified!\n");
      usage();
      exit(1);
  }

  buf = malloc(buf_size);
  if (!buf) {
	fprintf(stderr, "Unable to allocate %d bytes for send/recv buffer: %s\n", buf_size, strerror(errno));
	exit(1);
  }

  /* 
   * This next statment will have to change when we add the code to
   * handle a warm-up and setup phase.
   */ 

  bm_state = BM_SETUP;

  run_nbench(dummy);

  /* Now, print stats ... */
  /* 
   * We have to go through the big array that was allocated 
   * and roll up and print out the values 
   */

  if (!print_stats) exit(0); /* For now ... */

  show_results();

  return 0;
}
