/*-GNU-GPL-BEGIN-*
nepim - network pipemeter
Copyright (C) 2005 Everton da Silva Marques

nepim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

nepim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with nepim; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*-GNU-GPL-END-*/

/*
  nepim - network pipemeter

  $Id: main.c,v 1.31 2005/10/13 19:02:20 evertonm Exp $
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <oop.h>
#include <assert.h>

#ifdef HAVE_SIGHANDLER_T
# define __USE_GNU
# include <signal.h>
#else
# include <signal.h>
typedef void (*sighandler_t)(int);
#endif

#include "conf.h"
#include "common.h"
#include "str.h"
#include "int.h"
#include "udp_header.h"

extern void nepim_server_run();
extern void nepim_client_run();


static void usage(FILE *out, const char *prog_name)
{
  char buf[100];
  char buf2[100];
  char *bit_rate;
  char *pkt_rate;

  if (nepim_global.bit_rate < 0) {
    bit_rate = "unlimited";
  } else {
    int wr = snprintf(buf, sizeof(buf), "%lld", nepim_global.bit_rate);
    assert(wr > 0);
    assert(wr < sizeof(buf));
    bit_rate = buf;
  }

  if (nepim_global.pkt_rate < 0) {
    pkt_rate = "unlimited";
  } else {
    int wr = snprintf(buf2, sizeof(buf2), "%d", nepim_global.pkt_rate);
    assert(wr > 0);
    assert(wr < sizeof(buf2));
    pkt_rate = buf2;
  }

  fprintf(out, 
	  "usage: %s [options]\n"
	  "\n"
	  "common (client/server) options:\n"
	  "  -h             help\n"
	  "  -v             show program version\n"
	  "  -p port        server port number (defaults to %s)\n"
	  "  -m mode        path MTU discovery mode (defaults to %d)\n"
	  "                 modes: -1 = use system-wide settings\n"
	  "                         0 = DONT (never do PMTU discovery, DF=0)\n"
	  "                         1 = WANT (use per-route hints)\n"
	  "                         2 = DO   (always do PMTU discovery, DF=1)\n"
	  "  -t ttl         TTL for sending packets (defaults to system)\n"
	  "  -w size        TCP write size (defaults to %d bytes)\n"
	  "  -z size        TCP read size (defaults to %d bytes)\n"
	  "  -W size        UDP write size (defaults to %d bytes)\n"
	  "  -Z size        UDP read size (defaults to %d bytes)\n"
	  "  -O size        maximum socket send buffer in bytes\n"
	  "  -I size        maximum socket receive buffer in bytes\n"
	  "  -k password    server authentication secret\n"
	  "  -6             disable IPv6\n"
	  "  -4             disable IPv4\n"
	  "  -x [win]       experimental UDP reception statistics (win=%d)\n"
	  "  -L loss        experimental UDP sending loss\n"
	  "  -D dup         experimental UDP sending duplication\n"
	  "client-only options:\n"
	  "  -u             UDP mode (defaults to TCP)\n"
	  "  -s             client-send simplex mode (defaults to server-send)\n"
	  "  -d             duplex traffic (defaults to simplex)\n"
	  "  -M             enable multicast-compatible options\n"
	  "  -c host-list   client mode (defaults to server mode)\n"
	  "  -n pipes       number of parallel pipes (defaults to %d)\n"
	  "  -r bit-rate    upper bit rate limit (defaults to %s bps)\n"
	  "  -R pkt-rate    upper packet rate limit (defaults to %s pps)\n"
	  "  -i interval    statistics report interval (defaults to %d seconds)\n"
	  "  -a age         test duration (defaults to %d seconds)\n"
	  "server-only options:\n"
	  "  -f [lib-name]  enable TCP wrapper filtering\n"
	  "  -b addr-list   bind to specific addresses (defaults to any)\n"
	  "  -j addr-list   join multicast groups (defaults to empty)\n"
	  "                 addr-list: addr_1[,port_1][/.../addr_n[,port_n]]\n"
	  "                 example:   10.0.0.1/192.168.0.1,5000/::1\n"
	  "  -l backlog     TCP listen(2) backlog (defaults to %d)\n"
	  "  -T mcast-ttl   TTL for multicast packets (defaults to system)\n"
	  ,
	  nepim_global.prog_name, 
	  nepim_global.portname,
	  nepim_global.pmtu_mode,
	  nepim_global.tcp_write_size,
	  nepim_global.tcp_read_size,
	  nepim_global.udp_write_size,
	  nepim_global.udp_read_size,
	  nepim_global.udp_win_max,
	  nepim_global.pipes,
	  bit_rate,
	  pkt_rate,
	  nepim_global.stat_interval,
	  nepim_global.test_duration,
	  nepim_global.listen_backlog
	  );
}

static void init_event_loop()
{
  sighandler_t handler;
  handler = signal(SIGPIPE, SIG_IGN);
  assert(handler != SIG_ERR);

  nepim_global.oop_sys = oop_sys_new();
  assert(nepim_global.oop_sys);
  nepim_global.oop_src = oop_sys_source(nepim_global.oop_sys);
  assert(nepim_global.oop_src);
}

static void show_version_brief() 
{
  printf("nepim - network pipemeter - version %s\n", nepim_version());
}

static void show_version() 
{
  show_version_brief();

  printf(
	 "Copyright (C) 2005 Everton da Silva marques\n"
"\n"
"This program is free software; you can redistribute it and/or\n"
"modify it under the terms of the GNU General Public License\n"
"as published by the Free Software Foundation; either version 2\n"
"of the License, or (at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
"GNU General Public License for more details.\n"
	 );
}


int main(int argc, const char *argv[])
{
  int i;

  nepim_int_sanity();

  for (i = 1; i < argc; ++i) {
    const char *arg = argv[i];

    if (!strcmp(arg, "-f")) {
      int j = i + 1;

      nepim_global.tcpwrap = "libwrap.so";
      
      if (j < argc)
	if (*argv[j] != '-') {
	  ++i;
	  nepim_global.tcpwrap = argv[i];
	}

      continue;
    }

    if (!strcmp(arg, "-L")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing UDP loss\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.udp_exp_loss = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-D")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing UDP duplication\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.udp_exp_dup = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-x")) {
      int j = i + 1;
      nepim_global.udp_exp_stats = 1;

      if (j < argc)
	if (*argv[j] != '-') {
	  ++i;
	  nepim_global.udp_win_max = atoi(argv[i]);
	}

      continue;
    }

    if (!strcmp(arg, "-4")) {
      nepim_global.no_inet4 = 1;
      continue;
    }

    if (!strcmp(arg, "-6")) {
      nepim_global.no_inet6 = 1;
      continue;
    }

    if (!strcmp(arg, "-k")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing password\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.password = argv[i];
      continue;
    }

    if (!strcmp(arg, "-a")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing test duration\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.test_duration = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-b")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing bind address list\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.bind_list = addr_list_append(nepim_global.bind_list, argv[i]);
      continue;
    }

    if (!strcmp(arg, "-c")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing hostname list\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.client_mode = 1;
      nepim_global.hostname = addr_list_append(nepim_global.hostname, argv[i]);
      continue;
    }

    if (!strcmp(arg, "-d")) {
      nepim_global.duplex_mode = 1;
      continue;
    }

    if (!strcmp(arg, "-h")) {
      usage(stdout, nepim_global.prog_name);
      exit(0);
    }

    if (!strcmp(arg, "-i")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing report interval\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.stat_interval = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-j")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing multicast join list\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.join_list = addr_list_append(nepim_global.join_list, argv[i]);
      continue;
    }

    if (!strcmp(arg, "-n")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing number of pipes\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.pipes = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-p")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing port number\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.portname = argv[i];
      continue;
    }

    if (!strcmp(arg, "-m")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing PMTU discovery mode\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.pmtu_mode = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-M")) {
      nepim_global.udp_mode                = 1; /* boolean */
      nepim_global.simplex_client_send     = 1; /* boolean */
      nepim_global.udp_keepalive_require   = 0; /* boolean */
      nepim_global.udp_require_greet_reply = 0; /* boolean */
      nepim_global.max_greetings           = 1; /* counter */
      continue;
    }

    if (!strcmp(arg, "-r")) {
      int bytes;
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing bit-rate\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.bit_rate = strtoll(argv[i], 0, 10);
      bytes = nepim_bps2bytes(nepim_global.bit_rate,
			      nepim_global.write_delay);
      if (bytes < 1) {
	long long min_bps = nepim_min_bps(nepim_global.write_delay);
	fprintf(stderr,
		"%s: bit-rate=%lld (bytes=%d) lower than minimum=%lld\n",
		nepim_global.prog_name,
		nepim_global.bit_rate, bytes, min_bps);
	exit(1);
      }
      assert(nepim_global.bit_rate >= nepim_min_bps(nepim_global.write_delay));
      continue;
    }

    if (!strcmp(arg, "-R")) {
      int pkts;
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing pkt-rate\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.pkt_rate = atoi(argv[i]);
      pkts = nepim_pps2packets(nepim_global.pkt_rate,
			       nepim_global.write_delay);
      if (pkts < 1) {
	int min_pps = nepim_min_pps(nepim_global.write_delay);
	fprintf(stderr,
		"%s: pkt-rate=%d (pkts=%d) lower than minimum=%d\n",
		nepim_global.prog_name, 
		nepim_global.pkt_rate, pkts, min_pps);
	exit(1);
      }
      assert(nepim_global.pkt_rate >= nepim_min_pps(nepim_global.write_delay));
      continue;
    }

    if (!strcmp(arg, "-s")) {
      nepim_global.simplex_client_send = 1;
      continue;
    }

    if (!strcmp(arg, "-t")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing ttl\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.ttl = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-T")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing multicast-ttl\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.mcast_ttl = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-u")) {
      nepim_global.udp_mode = 1;
      continue;
    }

    if (!strcmp(arg, "-v")) {
      show_version();
      exit(0);
    }

    if (!strcmp(arg, "-w")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing TCP write size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.tcp_write_size = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-z")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing TCP read size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.tcp_read_size = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-W")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing UDP write size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.udp_write_size = atoi(argv[i]);
      if (nepim_global.udp_write_size < UDP_HEADER_LEN) {
	fprintf(stderr,
		"%s: %d is less than minimum UDP write size (%d)\n",
		nepim_global.prog_name, nepim_global.udp_write_size,
		UDP_HEADER_LEN);
	exit(1);
      }
      continue;
    }

    if (!strcmp(arg, "-Z")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing UDP read size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.udp_read_size = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-l")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing listen backlog\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.listen_backlog = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-O")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing maximum socket send buffer size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.win_send = atoi(argv[i]);
      continue;
    }

    if (!strcmp(arg, "-I")) {
      ++i;
      if (i >= argc) {
	fprintf(stderr, "%s: missing maximum socket read buffer size\n",
		nepim_global.prog_name);
	exit(1);
      }
      nepim_global.win_recv = atoi(argv[i]);
      continue;
    }

    fprintf(stderr, "%s: unknown option: %s\n",
	    nepim_global.prog_name, arg);
    usage(stderr, nepim_global.prog_name);
    exit(1);
  }

  show_version_brief();

  init_event_loop();

  if (nepim_global.client_mode) {

    if (nepim_global.udp_mode)
      fprintf(stderr, 
	      "client: udp_read=%d udp_write=%d\n",
	      nepim_global.udp_read_size,
	      nepim_global.udp_write_size);
    else
      fprintf(stderr, 
	      "client: tcp_read=%d tcp_write=%d\n",
	      nepim_global.tcp_read_size,
	      nepim_global.tcp_write_size);

    nepim_client_run();

    fprintf(stderr, "%s: done\n", nepim_global.prog_name);
    exit(0);
  }

  fprintf(stderr, 
	  "server: tcp_read=%d tcp_write=%d udp_read=%d udp_write=%d\n",
	  nepim_global.tcp_read_size,
	  nepim_global.tcp_write_size,
	  nepim_global.udp_read_size,
	  nepim_global.udp_write_size);

  nepim_server_run();

  fprintf(stderr, "%s: done\n", nepim_global.prog_name);
  exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1