/*
	
	PortTester.cpp : A program to ensure a MUD is running on a nominated port

	Author: Nick Gammon <nick@gammon.com.au>
	Web:		http://www.gammon.com.au

	Date: 	14th March 2000

	Version: 2.00

	Copyright 2000 Gammon Software Solutions.


	This program is intended to check a TCP/IP port to check if your MUD is running.
  If not, it will start up the nominated program with the nominated arguments.

	Usage:	porttester <port>  [<delay>  <command>  [ <args> ]]

	eg. 		porttester 4201 60 c:\pennmush\pennmush.exe /run

	If you normally run the MUD as a service you might replace /run with /start.

  This will check every 60 seconds to see if port 4201 is in use. If not, it runs this:

    c:\pennmush\pennmush.exe /run

	This program may be used for no fee. 

	Commercial copying by arrangement with the author.

	This program has been compiled and tested under Windows NT using Visual C++ 6.0.

	It should compile under Unix with only minor changes (eg. omit WSAStartup and
	WSACleanup, and changing the way a new process is spawned).

*/

#include <winsock2.h>
#include <stdio.h>
#include <process.h>
#include <time.h>

#define ERR_CANNOT_INITIALISE_SOCKETS 1
#define ERR_CANNOT_CREATE_A_SOCKET 2
#define ERR_NO_PORT_NUMBER 3
#define ERR_INVALID_ARGUMENTS 4
#define ERR_NOT_RUNNING 5

// Winsock is in the following library
#pragma comment(lib, "ws2_32.lib")

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

SOCKET s;
SOCKADDR_IN 	saServer; 	
WSADATA wsaData;
int nReturn = 0;
int nDelay = 0;
char * sCommand = "";
char * sArgs = "";

unsigned short nPort;

	printf ("[PortTester Version 2.00 - Gammon Software Solutions]\n");
	printf ("http://www.gammon.com.au\n");

	if (argc < 2)
		{
		printf ("Usage:  porttester <port>  [<delay>  <command>  [ <args> ]]\n");
		return ERR_NO_PORT_NUMBER;
		}

	nPort = atoi (argv [1]);
	if (nPort <= 0)
		{
		printf ("Port number of \"%s\" is invalid.\n", argv [1]);
		return ERR_INVALID_ARGUMENTS;
		}

  // get delay and program name
  if (argc > 2)
    {
	  if (argc < 4)
		  {
      printf ("** Program name must be specified\n");
		  printf ("Usage:  porttester <port>  [<delay>  <command>  [<args>]]\n");
		  return ERR_INVALID_ARGUMENTS;
		  }

    nDelay = atoi (argv [2]);
	  if (nDelay <= 0)
		  {
		  printf ("Delay of \"%s\" is invalid.\n", argv [2]);
		  return ERR_INVALID_ARGUMENTS;
		  }

    // get command to run
    sCommand = argv [3];

    // get args
    if (argc > 4)
      sArgs = argv [4];

    } // end of having a delay and program

	//
	// Initialize WinSock
	//
	if (WSAStartup(MAKEWORD (1,1), &wsaData))
		{
		printf ("Could not initialise Windows Sockets, error %i\n",
						WSAGetLastError ());
		return ERR_CANNOT_INITIALISE_SOCKETS;
		}

  // loop if we have a delay
  do
    {

	  //
	  // Create a TCP/IP stream socket
	  //
	  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	  if (s == INVALID_SOCKET)
		  {
		  printf ("Could not create a socket, error %i\n",
						  WSAGetLastError ());
		  return ERR_CANNOT_CREATE_A_SOCKET;
		  }


	  //
	  // Fill in the the address structure
	  //
	  saServer.sin_port = htons(nPort);
	  saServer.sin_family = AF_INET;
	  saServer.sin_addr.s_addr = INADDR_ANY;

	  //
	  // bind our name to the socket
	  //
	  if (bind(s, (LPSOCKADDR) &saServer, sizeof saServer) != SOCKET_ERROR)
		  {
      struct tm *thetime;
      time_t now;
      char sTimeBuff [40];

      now = time (NULL);
      thetime = localtime (&now);
      strftime (sTimeBuff, sizeof sTimeBuff, "%d-%b-%Y %H:%M:%S", thetime);

		  printf ("NO server running on port %i at %s\n", nPort, sTimeBuff);
		  nReturn = ERR_NOT_RUNNING;
      if (strlen (sCommand) != 0)
        {
        if (strlen (sArgs) != 0)
          _spawnl (_P_DETACH, sCommand, sCommand, sArgs, NULL);
        else    // no arguments
          _spawnl (_P_DETACH, sCommand, sCommand, NULL);
  		  printf ("Started %s %s\n", sCommand, sArgs);
        }
		  }   // end of server not running

	  //
	  // Close the socket
	  //
	  closesocket (s);

    // wait the specified number of seconds
    Sleep (nDelay * 1000L);
    } while (nDelay != 0);

  // wrap up

	WSACleanup ();

	return nReturn;
}
