/*
 * $Header: /home/gene/library/website/docsrc/sca/src/RCS/counter.c,v 395.1 2008/04/20 17:25:48 gene Exp $
 *
 *  Copyright (c) 2006 Gene Michael Stover.  All rights reserved.
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 * 
 *  This program 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 Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 *  USA
 */

/*
 * This is a simple service, sort of like "echo" except that it
 * returns a number, not the packet that you send it.
 */

#include "this.h"

/*
 * Port where we are looking for live hosts.
 */
static u_short S_port = 0;

/*
 */
static char *S_progname = "";

/*
 */
enum { ACTION_RUN, ACTION_HELP };
static int S_action = ACTION_RUN;

/*
 */
static int
S_CommandLine (int argc, char *argv[])
{
  int rc = 0;
  int optind = 1;

  S_progname = argv[0];
  if (optind < argc) {
    if (sscanf (argv[optind], "%hu", &S_port) == 1) {
      if (0 < S_port && S_port <= 65535) {
        ++optind;
        if (optind < argc) {
          fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
          fprintf (stderr, " Ignoring extra command line arguments.");
        }
      } else {
        fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
        fprintf (stderr, " %d is out of range for a UDP port.", S_port);
        S_action = ACTION_HELP;
      }
    } else {
      fprintf (stderr, "\n%s:%d:",  __FILE__, __LINE__);
      fprintf (stderr, " Cannot parse \"%s\" into a port", argv[optind]);
      fprintf (stderr, " number.");
      S_action = ACTION_HELP;
    }
  } else {
    fprintf (stderr, "\n%s:%d:",  __FILE__, __LINE__);
    fprintf (stderr, " Missing port number & name of message file");
    fprintf (stderr, " on the command line.");
    S_action = ACTION_HELP;
  }
  return rc;
}

/*
 */
static int
S_Help ()
{
  printf ("\nUsage: %s PORT", S_progname);
  printf ("\nPORT is the UDP port number");
  printf ("\n");
  return 0;
}

/*
 */
static SOCKET
S_CloseSocket (SOCKET s)
{
  if (s != INVALID_SOCKET) {
#if IS_UNIX
    close (s);
#endif
#if IS_WINDERS
    closesocket (s);
#endif
  }
  return INVALID_SOCKET;
}

/*
 */
static SOCKET
S_MakeSocket ()
{
  SOCKET s;
  struct sockaddr_in sin;
  struct sockaddr *sa;

  s = socket(PF_INET, SOCK_DGRAM, 0);
  if (s != INVALID_SOCKET) {
    bzero (&sin, sizeof sin);
    sin.sin_family = AF_INET;
    sin.sin_port = htons (S_port);
    sin.sin_addr.s_addr = INADDR_ANY;
    sa = (struct sockaddr *) &sin;
    if (bind (s, sa, sizeof sin) == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n");
      perror ("bind");
      fprintf (stderr, "%s:%d: bind failed", __FILE__, __LINE__);
      S_CloseSocket (s);
      s = INVALID_SOCKET;
    }
  } else {
    fprintf (stderr, "\n");
    perror ("socket");
    fprintf (stderr, "%s:%d: socket failed", __FILE__, __LINE__);
  }
  fprintf (stderr, "\n%s:%d: trace: s is %d", __FILE__, __LINE__, s);
  return s;
}

/*
 */
static int
S_DoRecv (struct sockaddr_in *sin, SOCKET s)
{
  int rc = 0, i;
  char buffer[8000];
  struct sockaddr *sa;
  int sin_len;

  fprintf (stderr, "\n%s:%d: trace: s is %d", __FILE__, __LINE__, s);
  bzero (sin, sizeof *sin);
  sin_len = sizeof *sin;
  sa = (struct sockaddr *) sin;
  i = recvfrom (s, buffer, sizeof buffer, 0, sa, &sin_len);
  if (i > 0) {
    /* good */
  } else {
    fprintf (stderr, "\n");
    perror ("recvfrom");
    fprintf (stderr, "%s:%d:", __FILE__, __LINE__);
    fprintf (stderr, " recvfrom failed");
    rc = 219;
  }
  return rc;
}

/*
 */
static int
S_DoSend (int count, struct sockaddr_in *sin, SOCKET s)
{
  int rc = 0, i;
  struct sockaddr *sa;
  char buffer[12];

  sprintf (buffer, "%d\n", count);
  sa = (struct sockaddr *) sin;
  i = sendto (s, buffer, strlen (buffer), 0, sa, sizeof *sin);
  if (i > 0) {
    /* good */
  } else {
    fprintf (stderr, "\n");
    perror ("sendto");
    fprintf (stderr, "%s:%d: sendto failed", __FILE__, __LINE__);
    rc = 219;
  }
  return rc;
}

/*
 */
static int
S_Loop (SOCKET s)
{
  int rc = 0;
  int count = 0;
  struct sockaddr_in addy;

  while (rc == 0) {
    if (S_DoRecv (&addy, s) == 0) {
      printf ("\n%s", inet_ntoa (addy.sin_addr)); fflush (stdout);
      if (S_DoSend (count, &addy, s) == 0) {
        /* good */
        ++count;
      } else {
        fprintf (stderr, "\n%s:%d: S_DoSend failed", __FILE__, __LINE__);
        rc = 3;
      }
    } else {
      fprintf (stderr, "\n%s:%d: S_DoRecv failed", __FILE__, __LINE__);
      rc = -172;
    }
  }
  return rc;
}

/*
 */
static int
S_Run ()
{
  int rc = 0;
  SOCKET s;

  s = S_MakeSocket ();
  if (s != INVALID_SOCKET) {
    if (S_Loop (s) == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: S_Loop failed", __FILE__, __LINE__);
      rc = 134;
    }
    S_CloseSocket (s);
  } else {
    fprintf (stderr, "\n%s:%d: S_MakeSocket failed", __FILE__, __LINE__);
    rc = -116;
  }
  return rc;
}

/*
 */
static int
S_Dispatch ()
{
  int rc = 0;

  switch (S_action) {
  case ACTION_RUN:
    rc = S_Run ();
    break;
  case ACTION_HELP:
    rc = S_Help ();
    break;
  default:
    fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
    fprintf (stderr, " S_action is %d.", S_action);
    fprintf (stderr, "  This should never happen!");
    rc = 102;
  }
  return rc;
}

int
main (int argc, char *argv[])
{
  int rc = 0;

  if (S_CommandLine (argc, argv) == 0) {
    if (S_Dispatch () == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: S_Dispatchfailed", __FILE__, __LINE__);
      rc = 234;
    }
  } else {
    fprintf (stderr, "\n%s:%d: S_CommandLine failed", __FILE__, __LINE__);
    rc = 234;
  }
  return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* --- end of file --- */
