// Network_Adapters_Test.cpp,v 1.1 2004/09/25 05:42:49 ossama Exp
//
// ============================================================================
//
// = LIBRARY
//    tests
//
// = FILENAME
//    Network_Adapters_Test.cpp
//
// = DESCRIPTION
//    Tests the ICMP-echo support in ACE.
//
// = AUTHOR
//    Robert S. Iakobashvili <roberti@go-WLAN.com> <coroberti@walla.co.il>
//    Gonzalo A. Diethelm <gonzalo.diethelm@aditiva.com> made aceing
//
// ============================================================================

// We need this to be able to check for ACE_HAS_ICMP_SUPPORT
#include "ace/config.h"
#include "test_config.h"

#if defined (ACE_HAS_ICMP_SUPPORT) && (ACE_HAS_ICMP_SUPPORT == 1)

#include "ace/ACE.h"
#include "ace/Get_Opt.h"
#include "ace/Signal.h"
#include "ace/High_Res_Timer.h"
#include "ace/Atomic_Op.h"
#include "ace/Sched_Params.h"
#include "ace/Reactor.h"
#include "ace/Timer_Queue.h"


#include "Network_Adapters_Test.h"


ACE_RCSID (tests,
           Network_Adapters_Test,
           "Network_Adapters_Test.cpp,v 1.1 2004/09/25 05:42:49 ossama Exp")


/**
 * There are two major uses of the functionality:
 *
 * 1. to check a local network adapter;
 * 2. to check, which of the remote CEs (computer elements) are alive.
 *
 * For the first purpose we are creating a raw socket, binding it to
 * the IP-address in question (adapter to be monitored), and are
 * sending via the adapter ICMP echo-checks to a list of 3-rd party
 * ping-points. If at least a single 3-rd party replies us within a
 * configurable timeout by an ICMP-reply, our adapter is OK, if not we
 * may wish to repeat ICMP-probing once or twice more. We may also
 * wish to make such tests regular with a certain timeout.
 *
 * For the second purpose we are creating a raw socket, and without
 * binding it are sending via any our CE's adapter ICMP echo-checks to
 * a list of CEs to be monitored. An array of chars (named ping_status
 * in main ()), corresponding to the array of addresses
 * (ping_points_addrs in main ()), contains status of each monitored
 * CE. When we get ICMP-reply from a ping_points_addrs[I] IP-address,
 * we are placing 0 to the ping_status[I]. The ICMP-probing may be
 * configured to test 2-3 times each pinged CE.  We may also wish to
 * make such tests regular with a certain timeout.
 *
 * Command line options:
 *
 * -b  IPv4 of the interface to bind to the socket (only for the
 *     purpose 1), e.g. -b 192.168.5.5;
 *
 * -p  IPv4 addresses of the remote CEs, which we are going to check
 *     (purpose 2), or they are 3-rd points for the purpose 1,
 *     e.g. “-p 192.168.5.120: 192.168.5.122: 192.168.5.125
 *
 * -w  milliseconds to wait for echo-reply, on lan 100-200 msec, on
 *     WAN may be 2000-5000 msec, for GPRS may reach 10000 - 20000
 *     mseconds;
 *
 * -t  as we are doing such checks regularly time in seconds between
 *     checks.
 *
 * In main we are activating by open () an instance of Echo_Handler
 * with parameters.
 *
 * Repeats_Handler serves to repeat the checks each
 * repeats_seconds_timer seconds.
 *
 * Stop_Handler contains a list of handlers to be stopped and is
 * supposed to close this business.

 * Attention: Running the test without parameters just with defaults
 * makes pinging to the loopback address. Therefore, the raw socket
 * sees both ICMP_ECHO and ICMP_ECHOREPLY with the first output in log
 * as not a ICMP_ECHOREPLY message and further ICMP_ECHOREPLY
 * received.
 */


Echo_Handler::Echo_Handler (void)
  : ping_socket_ (),
    reply_wait_ (),
    remote_addrs_ (0),
    number_remotes_ (0),
    success_status_ (0),
    delete_success_status_ (0),
    max_attempts_num_ (0),
    current_attempt_ (0),
    connect_to_remote_ (0)
{
}

Echo_Handler::~Echo_Handler (void)
{
  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Echo_Handler::~Echo_Handler - entered.\n"));

  this->ping_socket ().close ();
  if (this->remote_addrs_)
    {
      delete [] this->remote_addrs_;
      this->remote_addrs_ = 0;
    }
  if (this->success_status_ && this->delete_success_status_)
    {
      delete this->success_status_;
    }
  this->success_status_ = 0;

  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Echo_Handler::~Echo_Handler - completed.\n"));
}

int
Echo_Handler::open (ACE_Reactor * const    reactor,
                    ACE_Time_Value const & reply_wait,
                    ACE_INET_Addr const &  remote_addr,
                    ACE_TCHAR *            success_status,
                    size_t                 max_attempts_num,
                    ACE_Addr const &       local_addr,
                    int                    connect_to_remote)
{
  if (this->reactor ())
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "reactor is already set. \n"),
                      -1);
  if (!reactor)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed : "
                       "NULL pointer to reactor provided. \n"),
                      -1);

  this->reactor (reactor);
  this->reply_wait_ = reply_wait;

  if (this->remote_addrs_)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "this->remote_addrs_ already initialized. \n"),
                      -1);

  if (! (this->remote_addrs_ = new ACE_INET_Addr))
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: to "
                       "allocate a single ACE_INET_Addr for "
                       "this->remote_addrs_. \n"),
                      -1);

  // now copy to keep it locally
  this->remote_addrs_[0] = remote_addr;
  this->number_remotes_ = 1;
  if (this->success_status_)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "this->success_status_ already initialized. \n"),
                      -1);

  if (! success_status)
    {
      if (! (this->success_status_ = new ACE_TCHAR))
        ACE_ERROR_RETURN ((LM_ERROR,
                           "(%P|%t) Echo_Handler::open - failed: "
                           "to allocate a single "
                           "ACE_TCHAR for this->success_status_. \n"),
                          -1);
      this->delete_success_status_ = 1;
    }
  else
    {
      this->success_status_ = success_status;
    }

  // place 'failed' to the array.
  this->success_status_[0] = 1;

  this->max_attempts_num_ = max_attempts_num;
  this->current_attempt_ = this->max_attempts_num_;

  if (this->ping_socket ().open (local_addr) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed to "
                       "initialize ping_socket_. \n"),
                      -1);

  this->connect_to_remote_ = connect_to_remote;

  // Register with the reactor for input.
  if (this->reactor ()->register_handler (this,
                                          ACE_Event_Handler::READ_MASK) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - can't register "
                       "with reactor for "
                       "handling input.\n"),
                      -1);
  return 0;
}

int
Echo_Handler::open (ACE_Reactor * const reactor,
                    ACE_Time_Value const & reply_wait,
                    ACE_INET_Addr const remote_addrs[],
                    size_t number_remotes,
                    ACE_TCHAR *success_status,
                    size_t max_attempts_num,
                    ACE_Addr const & local_addr)
{
  if (this->reactor ())
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "reactor is already set. \n"),
                      -1);

  if (!reactor)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: NULL "
                       "pointer to reactor provided. \n"),
                      -1);

  this->reactor (reactor);
  this->reply_wait_ = reply_wait;

  if (!remote_addrs)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "NULL remote_addr pointer provided. \n"),
                      -1);

  if (!number_remotes)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "size of remote_addrs array is 0.\n"),
                      -1);

  this->number_remotes_ = number_remotes;

  if (this->remote_addrs_)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "this->remote_addrs_ already initialized. \n"),
                      -1);

  if (! (this->remote_addrs_ = new ACE_INET_Addr [this->number_remotes_]))
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "to allocate an array of ACE_INET_Addr "
                       "objects for this->remote_addrs_. \n"),
                      -1);

  // now copy to keep them locally
  for (size_t i = 0; i < this->number_remotes_; ++i)
    {
      this->remote_addrs_[i] = remote_addrs[i];
    }

  if (this->success_status_)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed: "
                       "this->success_status_ already initialized. \n"),
                      -1);

  if (! success_status)
    {
      if (! (this->success_status_ = new ACE_TCHAR [this->number_remotes_]))
        ACE_ERROR_RETURN ((LM_ERROR,
                           "(%P|%t) Echo_Handler::open - failed: "
                           "to allocate an array of chars for "
                           "this->success_status_ . \n"),
                          -1);
      this->delete_success_status_ = 1;
    }
  else
    {
      this->success_status_ = success_status;
    }

  // place 'failed' to the this->success_status_ array.
  for (size_t j = 0; j < this->number_remotes_; ++j)
    {
      this->success_status_[j] = 1;
    }

  this->max_attempts_num_ = max_attempts_num;
  this->current_attempt_ = this->max_attempts_num_;

  if (this->ping_socket ().open (local_addr) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - failed to "
                       "initialize ping_socket_. \n"),
                      -1);

  // register with the reactor for input
  if (this->reactor ()->register_handler (this,
                                          ACE_Event_Handler::READ_MASK) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::open - "
                       "can't register with reactor for "
                       "handling input.\n"),
                      -1);
  return 0;
}

ACE::Ping_Socket &
Echo_Handler::ping_socket (void)
{
  return this->ping_socket_;
}

int
Echo_Handler::dispatch_echo_checks (int first_call)
{
  // Set ones , if this is the first call (not from handle_timeout)
  if (first_call)
    {
      for (size_t j = 0; j < this->number_remotes_; ++j)
        {
          this->success_status_[j] = 1;
        }
      this->current_attempt_ = this->max_attempts_num_;
    }

  // Send echo-checks.
  for (size_t i = 0; i < this->number_remotes_; ++i)
    {
      if (this->success_status_[i] != 0)
        {
          if (this->ping_socket ().send_echo_check (
                this->remote_addrs_[i],
                this->connect_to_remote_) == -1)
            ACE_ERROR ((LM_ERROR,
                        "(%P|%t) Echo_Handler::dispatch_echo_checks - "
                        "failed for this->remote_addrs_[%d]. \n",
                        i));
        }
    }

  int rval_sched = -1;
  if ((rval_sched =
         this->reactor ()->schedule_timer (this,
                                           0,
                                           ACE_Time_Value (1),
                                           this->reply_wait_)) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Echo_Handler::dispatch_echo_checks - "
                       "schedule_timer() error.\n"),
                      -1);
  return 0;
}

int
Echo_Handler::handle_close (ACE_HANDLE handle, ACE_Reactor_Mask)
{
  ACE_UNUSED_ARG (handle);

  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Echo_Handler::handle_close - started.\n"));

#if 0
  this->ping_socket ().close ();
#endif

  this->reactor ()->cancel_timer (this);

#if 0
  this->reactor ()->remove_handler (this,
                                    ACE_Event_Handler::ALL_EVENTS_MASK |
                                    ACE_Event_Handler::DONT_CALL);
#endif

  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Echo_Handler::handle_close - completed.\n"));
  return 0;
}

ACE_HANDLE
Echo_Handler::get_handle (void) const
{
  return ((ACE::ICMP_Socket &) this->ping_socket_).get_handle ();
}

int
Echo_Handler::handle_input (ACE_HANDLE)
{
  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) Echo_Handler::handle_input - "
              "activity occurred on handle %d!\n",
              this->ping_socket ().get_handle ()));

  ACE_TCHAR buf[BUFSIZ];
  ACE_OS::memset (buf, 0, sizeof buf);

  ACE_INET_Addr addr;
  int rval_recv = -1;

  // Receive an <n> byte <buf> from the datagram socket
  // (uses<recvfrom(3)>).
  rval_recv =
    this->ping_socket ().recv (this->ping_socket ().icmp_recv_buff (),
                               ACE::Ping_Socket::PING_BUFFER_SIZE,
                               addr);
  switch (rval_recv)
    {
    case -1:
      // Complain and leave, but keep registered, returning 0.
      ACE_ERROR_RETURN ((LM_ERROR,
                         "(%P|%t) Echo_Handler::handle_input - "
                         "%p bad read\n", "client"),
                        0);
      // NOTREACHED

    case 0:
      // Complain and leave
      ACE_ERROR_RETURN ((LM_ERROR,
                         "(%P|%t) Echo_Handler::handle_input - "
                         "closing daemon (fd = %d)\n",
                         this->get_handle ()),
                        0);
      // NOTREACHED

    default:
      ACE_DEBUG ((LM_INFO,
                  "(%P|%t) Echo_Handler::handle_input - "
                  "message from %d bytes received.\n",
                  rval_recv));

      if (! this->ping_socket ().process_incoming_dgram (
             this->ping_socket ().icmp_recv_buff (),
             rval_recv))
        {
          for (size_t k = 0; k <this->number_remotes_; ++k)
            {
              if (addr.get_ip_address () ==
                  this->remote_addrs_[k].get_ip_address ())
                {
                  if (addr.addr_to_string (buf, sizeof buf) == -1)
                    {
                      ACE_ERROR ((LM_ERROR,
                                  "%p\n", "can't obtain peer's address"));
                    }
                  else
                    {
                      ACE_DEBUG ((LM_INFO,
                                  "(%P|%t) Echo_Handler::handle_input - "
                                  "ECHO_REPLY received "
                                  "from %s.\n"
                                  "\tMarking this peer, as alive.\n\n",
                                  buf));
                    }
                  // mark as successful
                  this->success_status_[k] = 0;
                  break;
                }
            }
        }
      break;
    }

  return 0;
}

int
Echo_Handler::handle_timeout (ACE_Time_Value const &,
                              void const *)
{
  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) Echo_Handler::handle_timeout - "
              "timer for ping_socket_ with handle %d.\n",
              this->ping_socket ().get_handle ()));

  int need_to_proceed = 0;

  for (size_t i = 0; i < this->number_remotes_; ++i)
    {
      if (this->success_status_[i])
        {
          need_to_proceed = 1;

          ACE_DEBUG ((LM_DEBUG,
                      "(%P|%t) Echo_Handler::handle_timeout - "
                      "this->success_status_[%d] is not zero. "
                      "Need to proceed echo-checks . \n", i));
          break;
        }
    }

  if (!need_to_proceed)
    {
      ACE_DEBUG ((LM_DEBUG,
                  "(%P|%t) Echo_Handler::handle_timeout - "
                  "need_to_proceed == 0. "
                  "Completed echo-checks. \n"));
    }

  if (!this->current_attempt_ || !need_to_proceed)
    {
      ACE_DEBUG ((LM_DEBUG,
                  "(%P|%t) Echo_Handler::handle_timeout - "
                  "completed ECHO-checks for handle (%d).\n\n\n",
                  this->ping_socket ().get_handle ()));
      return -1; // to de-register from Reactor and make clean-up
                 // in handle-close
    }

  if (this->current_attempt_)
    {
      --this->current_attempt_;
    }

  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) Echo_Handler::handle_timeout - attempt %d.\n",
              this->current_attempt_));

  this->dispatch_echo_checks ();
  return 0;
}

int
Echo_Handler::does_echo_test_successful (void)
{
  for (size_t i = 0; i < this->number_remotes_; ++i)
    {
      if (!this->success_status_[i])
        {
          return 1;
        }
    }
  return 0;
}


Stop_Handler::Stop_Handler (ACE_Reactor * const reactor)
  : counter_ ((counter_sig) 1)
{
  this->reactor (reactor);
  ACE_OS::memset (this->handlers_to_stop_,
                  0,
                  sizeof this->handlers_to_stop_);
}

Stop_Handler::~Stop_Handler (void)
{
  ACE_DEBUG ((LM_INFO, "(%P|%t) Stop_Handler::~Stop_Handler.\n"));
}

int
Stop_Handler::open (void)
{
  // Register the signal handler object to catch the signals.
  if (this->reactor ()->register_handler (SIGINT, this) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Stop_Handler::open - "
                       "register_handler for SIGINT error.\n"),
                      -1);

  if (this->reactor ()->register_handler (SIGTERM, this) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Stop_Handler::open - "
                       "register_handler for SIGTERM error.\n"),
                      -1);

#if ! defined (ACE_WIN32)
  if (this->reactor ()->register_handler (SIGQUIT, this) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Stop_Handler::open - "
                       "register_handler for SIGQUIT error.\n"),
                      -1);
#endif /* #if ! defined (ACE_WIN32) */
  return 0;
}

int
Stop_Handler::handle_signal (int signum,
                             siginfo_t * ,
                             ucontext_t *)
{
  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Stop_Handler::handle_signal - started.\n"));
  if (! --this->counter_)
    {
      ACE_DEBUG ((LM_INFO, "\n-- Stop_Handler::handle_signal --- "
                  "SIGNAL %d RECEIVED -----------.\n",
                  signum));
      return reactor ()->notify (this, ACE_Event_Handler::READ_MASK);
    }
  ACE_DEBUG ((LM_DEBUG, "(%P|%t) Stop_Handler::handle_signal - "
              "finished.\n"));
  return 0;
}

int
Stop_Handler::handle_input (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);

  ACE_DEBUG ((LM_INFO, "(%P|%t) Stop_Handler::handle_input - entered\n"));

  for (size_t i = 0; i < HANDLERS_TO_STOP_TABLE_SIZE; ++i)
    {
      // remove from the reactor's tables all non-null entries
      if (this->handlers_to_stop_[i])
        {
#if defined ACE_HAS_EXCEPTIONS

          // protect from deleted pointer
          try
            {
#endif // ACE_HAS_EXCEPTIONS

              this->reactor ()->cancel_timer (this->handlers_to_stop_[i]);
              this->reactor ()->remove_handler (
                this->handlers_to_stop_[i],
                ACE_Event_Handler::ALL_EVENTS_MASK
                | ACE_Event_Handler::DONT_CALL);
#if defined ACE_HAS_EXCEPTIONS
            }
          catch (...)
            {
              ACE_ERROR ((LM_ERROR,
                          "(%P|%t) Stop_Handler::handle_input - "
                          "EXCEPTION CATCHED. Most probably "
                          "handler's pointer has been deleted.\n"));
            }
#endif // ACE_HAS_EXCEPTIONS
          this->handlers_to_stop_[i] = 0;
        }
    }

  this->reactor ()->remove_handler (this,
                                    ACE_Event_Handler::SIGNAL_MASK |
                                    ACE_Event_Handler::DONT_CALL);

  if (reactor ()->end_reactor_event_loop () ==-1)
    {
      ACE_ERROR_RETURN ((LM_DEBUG,
                         "(%P|%t) Stop_Handler::handle_signal - "
                         "reactor_->end_reactor_event_loop().\n"),
                        -1);
    }

  ACE_DEBUG ((LM_INFO,
              "(%P|%t) Stop_Handler::handle_input - completed.\n"));
  return 0;
}

int
Stop_Handler::handle_close (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);

  ACE_DEBUG ((LM_INFO, "(%P|%t) Stop_Handler::handle_close - "
              "entered.\n"));
  this->reactor ()->remove_handler (this,
                                    ACE_Event_Handler::SIGNAL_MASK |
                                    ACE_Event_Handler::DONT_CALL);

  if (reactor ()->end_reactor_event_loop () ==-1)
    ACE_ERROR_RETURN ((LM_DEBUG,
                       "Stop_Handler::handle_close - "
                       "reactor_->end_reactor_event_loop().\n"),
                      -1);
  return 0;
}

int
Stop_Handler::handle_timeout (ACE_Time_Value const & current_time,
                              void const * act)
{
  ACE_UNUSED_ARG (current_time);
  ACE_UNUSED_ARG (act);

  return 0;
}

// Register handler with us for stopping.
int
Stop_Handler::register_handler (ACE_Event_Handler *handler)
{
  if (!handler)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Stop_Handler::register_handler - "
                       "error, handler is a null pointer.\n"),
                      -1);

  size_t index = 0;

  for (index = 0;
       (index < HANDLERS_TO_STOP_TABLE_SIZE &&
        this->handlers_to_stop_[index]);
       ++index)
    ;

  if (index == HANDLERS_TO_STOP_TABLE_SIZE)
    {
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("(%P|%t) Stop_Handler::register_handler "
                                   "- error, no space in "
                                   "handlers_to_stop_table.\nIncrease"
                                   "HANDLERS_TO_STOP_TABLE_SIZE.\n")),
                        -1);
    }

  this->handlers_to_stop_[index] = handler;
  return 0;
}

// Unregister handler, registered before with us for stopping.
int
Stop_Handler::unregister_handler (ACE_Event_Handler *handler)
{
  if (!handler)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Stop_Handler::unregister_handler - "
                       "error, handler is a null pointer.\n"),
                      -1);

  size_t index = 0;

  for (index = 0;
       (index < HANDLERS_TO_STOP_TABLE_SIZE &&
        handler != this->handlers_to_stop_[index]);
       ++index)
    ;

  size_t entrance = 0;
  if (index == HANDLERS_TO_STOP_TABLE_SIZE)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("(%P|%t) Stop_Handler::unregister_"
                                 "handler - error, the handler was not "
                                 "found amoung registered handlers.\n")),
                      -1);

  entrance = index;
  // null the entrance. Nulled entrances cannot be destroyed
  this->handlers_to_stop_[entrance] = 0;

  return 0;
}


Repeats_Handler::Repeats_Handler (void)
  : check_handler_ (0),
    seconds_timer_ (60),
    counter_ (0)
{
}

Repeats_Handler::~Repeats_Handler (void)
{
  ACE_DEBUG ((LM_INFO, "(%P|%t) Repeats_Handler::~Repeats_Handler.\n"));
}

int
Repeats_Handler::open (Echo_Handler * check_handler,
                       ACE_Reactor * const reactor,
                       unsigned int seconds_timer)
{
  if (!check_handler)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Repeats_Handler::open - error: "
                       "NULL check_handler.\n"),
                      -1);

  this->check_handler_ = check_handler;

  if (!reactor)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("(%P|%t) Stop_Handler::open - error: "
                                 "NULL reactor.\n")),
                      -1);

  this->reactor (reactor);
  this->seconds_timer_ = seconds_timer;

  if (this->reactor ()->schedule_timer (
        this,
        0,
        ACE_Time_Value (1),
        ACE_Time_Value (this->seconds_timer_)) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "(%P|%t) Repeats_Handler::open - "
                       "schedule_timer () error.\n"),
                      -1);
  return 0;
}

int
Repeats_Handler::handle_close (ACE_HANDLE handle)
{
  ACE_UNUSED_ARG (handle);

  ACE_DEBUG ((LM_INFO, "(%P|%t) Repeats_Handler::handle_close - entered.\n"));

  this->reactor ()->remove_handler (this,
                                    ACE_Event_Handler::ALL_EVENTS_MASK |
                                    ACE_Event_Handler::DONT_CALL);
  return 0;
}

static int one_button_test = 0;

int
Repeats_Handler::handle_timeout (ACE_Time_Value const & current_time,
                                 void const * act)
{
  ACE_UNUSED_ARG (current_time);
  ACE_UNUSED_ARG (act);

  this->counter_++ ;
  if (one_button_test && this->counter_ > 3)
    {
      ::raise (SIGINT);
    }
  if (this->check_handler_)
    {
      return this->check_handler_->dispatch_echo_checks (true);
    }

  return -1;
}

// to create core on some UNIX platforms
#if defined (ACE_HAS_SIG_C_FUNC)
extern "C"
{
#endif /* #if defined (ACE_HAS_SIG_C_FUNC) */

#if ! defined (ACE_WIN32)
  static void sigsegv_handler (int)
  {
    ACE_OS::abort ();
  }
#endif /* #if ! defined (ACE_WIN32) */

#if defined (ACE_HAS_SIG_C_FUNC)
}
#endif /* #if defined (ACE_HAS_SIG_C_FUNC) */

#if defined (ACE_WIN32)
BOOL CtrlHandler(DWORD fdwCtrlType)
{
  switch (fdwCtrlType)
    {
    case CTRL_C_EVENT:
    case CTRL_BREAK_EVENT:
    case CTRL_SHUTDOWN_EVENT:
    case CTRL_CLOSE_EVENT:
    case CTRL_LOGOFF_EVENT:
      ::raise (SIGINT);
      return TRUE;

      // Pass other signals to the next handler.
    default:
      return FALSE;
    }
}
#endif /* #if defined (ACE_WIN32) */


Fini_Guard::Fini_Guard (void)
{
}

Fini_Guard::~Fini_Guard (void)
{
  ACE::fini ();
}


#define MAX_NUMBER_OF_PING_POINTS   16

static int number_of_ping_points  = 0;
static char ping_points_ips [MAX_NUMBER_OF_PING_POINTS][16];
static ACE_INET_Addr ping_points_addrs [MAX_NUMBER_OF_PING_POINTS];
static char local_ip_to_bind [16];

static int wait_echo_reply_timer = 500; // 500 ms to wait is the default
static int repeats_seconds_timer = 60; // 60 seconds between repeats

static int
is_ip_address_local (char const * const ip_to_bind)
{
  ACE_INET_Addr *the_addr_array;
  size_t how_many = 0;
  int rc = ACE::get_ip_interfaces (how_many, the_addr_array);

  if (rc != 0)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "%p\n", "is_ip_address_local () - error: "
                       "ACE::get_ip_interfaces failed"),
                      -1);

  if (how_many == 0)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "is_ip_address_local () - error: "
                       "No interfaces presently configured "
                       "in the kernel\n"),
                      -1);

  // debugging messages
  ACE_DEBUG ((LM_DEBUG,
              "is_ip_address_local () - there are %d interfaces\n",
              how_many));

  for (size_t i = 0; i < how_many; ++i)
    {
      ACE_DEBUG ((LM_DEBUG, "\t%s\n", the_addr_array[i].get_host_addr ()));
    }

  for (size_t j = 0; j < how_many; ++j)
    {
      if (!ACE_OS::strcmp (the_addr_array[j].get_host_addr (), ip_to_bind))
        {
          return 0;
        }
    }
  return -1;
}

static int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_OS::memset (ping_points_ips, 0, sizeof ping_points_ips);
  ACE_OS::memset (local_ip_to_bind, 0, sizeof local_ip_to_bind);

  if (argc == 1) // one button test
    {
      one_button_test = 1;
      repeats_seconds_timer = 2;
      number_of_ping_points = 1;

      ACE_OS::strncpy (ping_points_ips [0],
                       "127.0.0.1",
                       sizeof ping_points_ips [0]);

      ping_points_addrs[0].set ((u_short) 0, ping_points_ips[0]);
      return 0;
    }

  ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("b:p:t:w:"));
  int c, counter = 0;
  ACE_INET_Addr b_temp_addr;
  char *token = 0;
  while ((c = get_opt ()) != EOF)
    {
      switch (c)
        {
        case 'b':     // ip-address of the interface to bind to
          ACE_OS::strncpy (local_ip_to_bind,
                           get_opt.optarg,
                           sizeof local_ip_to_bind);

          if (!ACE_OS::strlen (local_ip_to_bind) ||
              b_temp_addr.set ((u_short)0, local_ip_to_bind) != 0)
            {
              ACE_ERROR ((LM_ERROR, "%s, -b  should be followed by a valid "
                          "IPv4 address.\n",
                          "Network_Adapters_Test"));
              // print_usage ();
              return -1;
            }
          if (is_ip_address_local (local_ip_to_bind) == -1)
            {
              ACE_ERROR_RETURN ((LM_ERROR,
                                 "%s, the IPv4 address of the -b option (%s) "
                                 "is not a local "
                                 "address of your computer.\n"
                                 "\tPlease correct it.\n",
                                 "Network_Adapters_Test",
                                 local_ip_to_bind),
                                -1);
            }
          break;

        case 'p':  // ping-point (target) ip-addresses, separated by ":'"

          // tokenizing the string
          for (token = ACE_OS::strtok (get_opt.optarg, ACE_TEXT (":"));
               token != 0 && counter < MAX_NUMBER_OF_PING_POINTS;
               token = ACE_OS::strtok (0, ACE_TEXT (":")))
            {
              if (ping_points_addrs[counter].set ((u_short)0, token) != 0)
                ACE_ERROR_RETURN ((LM_ERROR,
                                   "%s - error: the address \"%s\" is not "
                                   "a valid IPv4 "
                                   "address. \n", "Network_Adapters_Test",
                                   token),
                                  -1);
              ++number_of_ping_points;
              ++counter;
            }
          break;

        case 't':
          repeats_seconds_timer = ACE_OS::atoi (get_opt.optarg);
          break;

        case 'w':
          wait_echo_reply_timer = ACE_OS::atoi (get_opt.optarg);
          break;

        default:
          // return print_usage (argc,argv);
          break;

        }
    }

  if (!number_of_ping_points)
    {
      ACE_ERROR_RETURN ((LM_ERROR,
                         "%s - error: no valid IPv4 addresses "
                         "were provided, using -p option. \n",
                         "Network_Adapters_Test"),
                        -1);
    }

  return 0;
}

int
run_main (int argc, ACE_TCHAR *argv[])
{
  ACE_START_TEST (ACE_TEXT ("Network_Adapters_Test"));

  ACE::init ();

  // to call for ACE::fini () in its destructor
  Fini_Guard fg;

#if defined (ACE_WIN32)
  SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
#else /* #if defined (ACE_WIN32) */
  // Set a handler for SIGSEGV signal to call for abort.
  ACE_Sig_Action sa1 ((ACE_SignalHandler) sigsegv_handler, SIGSEGV);
#endif /* #if defined (ACE_WIN32) */

  // Just to ensure that user has root/administrative permissions to
  // open raw sockets.
  if (ACE_OS::sched_params (
        ACE_Sched_Params (ACE_SCHED_FIFO,
                          ACE_Sched_Params::priority_min (ACE_SCHED_FIFO),
                          ACE_SCOPE_PROCESS)) != 0)
    {
      if (ACE_OS::last_error () == EPERM)
        {
          ACE_DEBUG ((LM_MAX,
                      ACE_TEXT ("user is not superuser, ")
                      ACE_TEXT ("unable to run this test\n"))) ;
          return -1;
        }
    }

  if (::parse_args (argc, argv) == -1)
    {
      return -1;
    }

  ACE_Reactor * main_reactor = 0;
  ACE_NEW_RETURN (main_reactor, ACE_Reactor, -1);

  (void) ACE_High_Res_Timer::global_scale_factor ();
  main_reactor->timer_queue ()->gettimeofday (
    &ACE_High_Res_Timer::gettimeofday_hr);

  /**
   * Stop_Handler's is supposed to stop the activity of all
   * handlers by a SIGINT signal.  We create and activate here an object of
   * Stop_Handler and pass an instance of reactor (main_reactor),
   * running demultiplexing event loop in the "main thread".
   */
  Stop_Handler* stop_handler = 0;
  ACE_NEW_RETURN (stop_handler, Stop_Handler (main_reactor), -1);
  if (stop_handler->open () == -1)
    {
      ACE_ERROR ((LM_ERROR,
                  "(%P|%t) %p\n", "\"Network_Adapters_Test\" main() - "
                  "stop_handler->open () failed.\nExiting ...\n"));
      ACE_OS::exit(-2);
    }

  ACE_TCHAR *ping_status = 0;
  ACE_NEW_RETURN (ping_status, ACE_TCHAR[number_of_ping_points], -1);

  // wait_echo_reply_timer is in msec
  int seconds = 0;
  int milliseconds = 0;
  seconds = wait_echo_reply_timer / 1000;
  milliseconds =  wait_echo_reply_timer % 1000;
  ACE_Time_Value const wait_timer (seconds, milliseconds);

  Echo_Handler *ping_handler;
  ACE_NEW_RETURN (ping_handler, Echo_Handler, -1);

  if (ACE_OS::strlen (local_ip_to_bind))
    {
      // We are willing to bind the raw-socket to a certain adapter,
      // probably because we are willing to check connectivity/etc
      // of the local adapter.
      ACE_INET_Addr local_adapter;
      local_adapter.set ((u_short) 0, local_ip_to_bind);
      if (ping_handler->open (main_reactor,
                              wait_timer,
                              ping_points_addrs,
                              number_of_ping_points,
                              ping_status,
                              2,  // max_attempts_number
                              local_adapter) == -1)
        {
          ACE_ERROR ((LM_ERROR,
                      "(%P|%t) %p\n", "\"Network_Adapters_Test\" main() - "
                      "ping_handler->open () failed.\nExiting ...\n"));
          ACE_OS::exit (-4);
        }
    }
  else
    {
      // Binding to a local adapter is not of our interest. We just
      // are willing to check all these remote IPs, to monitor, that
      // they are alive.
      if (ping_handler->open (main_reactor,
                              wait_timer,
                              ping_points_addrs,
                              number_of_ping_points,
                              ping_status,
                              2) == -1)   // max_attempts_number
        {
          ACE_ERROR ((LM_ERROR,
                      "(%P|%t) %p\n",
                      "\"Network_Adapters_Test\" main() - "
                      "ping_handler->open () failed.\nExiting ...\n"));
          ACE_OS::exit (-4);
        }
    }

  Repeats_Handler *repeats_handler;
  ACE_NEW_RETURN (repeats_handler, Repeats_Handler, -1);
  if (repeats_handler->open (ping_handler,
                             main_reactor,
                             repeats_seconds_timer) == -1)
    {
      ACE_ERROR ((LM_ERROR,
                  "(%P|%t) %p\n",
                  "\"Network_Adapters_Test\" main() - "
                  "repeats_handler->open failed.\nExiting ...\n"));
      ACE_OS::exit (-4);
    }

  stop_handler->register_handler (repeats_handler);
  stop_handler->register_handler (ping_handler);

  // Demultiplexing event loop of the main_reactor.
  while (main_reactor->reactor_event_loop_done () == 0)
    {
      main_reactor->run_reactor_event_loop ();
    }

  ACE_DEBUG ((LM_INFO,
              "(%P|%t|%T) \"Network_Adapters_Test\" main() - "
              "out of reactor's loop.\n"));

  delete repeats_handler;
  delete ping_handler;
  delete [] ping_status;
  delete stop_handler;
  delete main_reactor;

  ACE_END_TEST;
  return 0;
}

#else

int
run_main (int argc, ACE_TCHAR *argv[])
{
  ACE_UNUSED_ARG (argc);
  ACE_UNUSED_ARG (argv);

  ACE_START_TEST (ACE_TEXT ("Network_Adapters_Test"));

  ACE_DEBUG ((LM_INFO,
              "(%P|%t|%T) \"Network_Adapters_Test\" main() - "
              "ICMP support not configured.\n"
              "Define ACE_HAS_ICMP_SUPPORT = 1 in your config.h "
              "file to enable.\n"));

  ACE_END_TEST;

  return 0;
}

#endif  /* ACE_HAS_ICMP_SUPPORT == 1 */
