/*
 * client program to read data from server
 *
 * based on proxy.c 
 * $Id: client.c,v 1.5 2003/11/28 20:48:23 ca Exp $
 */

/*
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met: 
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "sm/generic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "st.h"

#if !HAVE_SNPRINTF
# define snprintf sm_snprintf
# include "sm/string.h"
#endif /* !HAVE_SNPRINTF */

#define IOBUFSIZE (16*1024)

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

#define MAXTC	512L		/* max. number of total connections */
#define REQUEST_TIMEOUT 3
#define SEC2USEC(s) ((s)*1000000LL)

static char *prog;                     /* Program name   */
static struct sockaddr_in rmt_addr;    /* Remote address */
static int conns;
static int debug = 0;
static int busy = 0;
static long int total = 0;

static void read_address(const char *str, struct sockaddr_in *sin);
static void *handle_request(void *arg);
static void print_sys_error(const char *msg);


/*
 * This program acts as client for the server program.
 * It connects to the specified remote address ('-r' option) and
 * asks for data.
 */
int main(int argc, char *argv[])
{
  extern char *optarg;
  int opt, n, threads;
  int raddr;
  long int tc;

  prog = argv[0];
  raddr = 0;
  conns = threads = 1;

  /* Parse arguments */
  while((opt = getopt(argc, argv, "c:d:hr:t:S")) != -1) {
    switch (opt) {
    case 'c':
      conns = atoi(optarg);
      if (conns < 1 || conns > 100) {
	fprintf(stderr, "%s: invalid number of connections: %s\n", prog, optarg);
	exit(1);
      }
      break;
    case 'd':
      debug = atoi(optarg);
      if (debug < 0) {
	fprintf(stderr, "%s: invalid number for debug: %s\n", prog, optarg);
	exit(1);
      }
      break;
    case 'r':
      read_address(optarg, &rmt_addr);
      if (rmt_addr.sin_addr.s_addr == INADDR_ANY) {
	fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);
	exit(1);
      }
      raddr = 1;
      break;
    case 't':
      threads = atoi(optarg);
      if (threads < 1 || threads > 100) {
	fprintf(stderr, "%s: invalid number of threads: %s\n", prog, optarg);
	exit(1);
      }
      break;
    case 'h':
    case '?':
      fprintf(stderr, "Usage: %s -l <[host]:port> -r <host:port> "
	      "[-p <num_processes>] [-S]\n", prog);
      exit(1);
    }
  }
  if (!raddr) {
    fprintf(stderr, "%s: remote address required\n", prog);
    exit(1);
  }

  tc = (long) threads * (long) conns;
  if (tc > MAXTC) {
    fprintf(stderr, "%s: too many total connections  (%ld > %ld)\n", prog,
	tc, MAXTC);
    exit(1);
  }

  if (debug)
    fprintf(stderr, "%s: starting client [%d]\n", prog, threads);

  /* Initialize the ST library */
  if (st_init() < 0) {
    print_sys_error("st_init");
    exit(1);
  }

  for (n = 0; n < threads; n++)
  {
    if (debug)
      fprintf(stderr, "%s: starting client %d/%d\n", prog, n, threads);
    if (st_thread_create(handle_request, (void *) n, 0, 0) == NULL) {
      print_sys_error("st_thread_create");
      exit(1);
    }
  }

  /* wait for them... */
  st_sleep(1);
  while (busy > 0)
  	st_sleep(1);
  /* XXX how? */

  fprintf(stderr, "%s: total=%ld (should be %ld)\n", prog, total,
	(long) threads * (long) conns);

  return 0;
}


static void read_address(const char *str, struct sockaddr_in *sin)
{
  char host[128], *p;
  struct hostent *hp;
  short port;

  strlcpy(host, str, sizeof(host));
  if ((p = strchr(host, ':')) == NULL) {
    fprintf(stderr, "%s: invalid address: %s\n", prog, host);
    exit(1);
  }
  *p++ = '\0';
  port = (short) atoi(p);
  if (port < 1) {
    fprintf(stderr, "%s: invalid port: %s\n", prog, p);
    exit(1);
  }

  memset(sin, 0, sizeof(struct sockaddr_in));
  sin->sin_family = AF_INET;
  sin->sin_port = htons(port);
  if (host[0] == '\0') {
    sin->sin_addr.s_addr = INADDR_ANY;
    return;
  }
  sin->sin_addr.s_addr = inet_addr(host);
  if (sin->sin_addr.s_addr == INADDR_NONE) {
    /* not dotted-decimal */
    if ((hp = gethostbyname(host)) == NULL) {
      fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);
      exit(1);
    }
    memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
  }
}

static void *handle_request(void *arg)
{
  struct pollfd pds[1];
  st_netfd_t rmt_nfd;
  int sock, n, i, tid, r;
  char buf[IOBUFSIZE];

  ++busy;
  tid = (int) arg;
  if (debug)
    fprintf(stderr, "client[%d]: conns=%d\n", tid, conns);


  /*
   * currently the server only answers one request...
   *
   * hence this breaks for too many connections since too many sckets
   *  are opened
   */

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

    /* Connect to remote host */
    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
      snprintf(buf, sizeof(buf), "[%d] socket i=%d", tid, i);
      print_sys_error(buf);
      goto done;
    }
    if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {
      snprintf(buf, sizeof(buf), "[%d] st_netfd_open_socket i=%d", tid, i);
      print_sys_error(buf);
      close(sock);
      goto done;
    }
    if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
		 sizeof(rmt_addr), SEC2USEC(REQUEST_TIMEOUT)) < 0) {
      snprintf(buf, sizeof(buf), "[%d] connect i=%d", tid, i);
      print_sys_error(buf);
      st_netfd_close(rmt_nfd);
      goto done;
    }
    pds[0].fd = sock;
    pds[0].events = POLLIN;

    if (debug > 2)
      fprintf(stderr, "client[%d]: connected (%d/%d)\n", tid, i, conns);

    n = strlcpy(buf, "gimme\n\r", sizeof(buf));
    if ((r = st_write(rmt_nfd, buf, n, SEC2USEC(REQUEST_TIMEOUT))) != n)
    {
      fprintf(stderr, "[%d] server went away? i=%d, n=%d, r=%d, errno=%d\n",
		tid, i, n, r, errno);
      break;
    }
    for ( ; ; ) {
      if (st_poll(pds, 1, -1) <= 0) {
        print_sys_error("st_poll");
        break;
      }

      if (pds[0].revents & POLLIN) {
        if ((n = (int) st_read(rmt_nfd, buf, IOBUFSIZE, -1)) <= 0)
          break;
      }
    }
    ++total;
    st_netfd_close(rmt_nfd);
  }

done:
  --busy;
  return NULL;
}

static void print_sys_error(const char *msg)
{
  fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
}


syntax highlighted by Code2HTML, v. 0.9.1