/*    bcnu.c,v 1.18 2000/03/30 09:22:37 jap Exp

 *    Copyright (c) 1998 John Phillips
 *
 *    You may distribute under the terms of the Artistic License
 *
 */

#include "bcnu.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

char *id = "bcnu";
char *subver = "1.18";

int s;				/* connected socket descriptor */

int dosyslog = 0;		/* don't log to syslog */

int port = 0;
char progname[80];		/* our name */
char master[80];		/* hostname of master */
char myhostname[80], *hostname;

struct hostent *hp;		/* pointer to host info for remote host */
struct servent *sp;		/* pointer to service information */

long timevar;			/* contains time returned by time() */
char *ctime ();			/* declare time formatting routine */
int xclient, ping, agent, file, fd, mess, asend, severity, htype, waitclose;

char buf[BCNU_MESS + 1];
char infile[80], cmd[80], msg[80], filename[80];
char hostfor[80];

char usage[] = "\n\
        -s port		the tcp service port no to use\n\
	-f name		file name when getting or sending\n\
        -h host		host we are logging for when sending message\n\
	-m message	message to send to the master\n\
        -e severity	0 ok, 5 warning, 9 error, 1 blackout\n\
	-n name		output file name when sending file\n\
	-a command	action command e.g get, send, show, net, reload\n\
	-t type		type of host\n\
        -x 		extended client off\n\
        -p 		ping - request status\n\
        -d		Switch on debug flag\n\
	-v		show version info\n\
        master	 	the name of the master";


struct sockaddr_in myaddr_in;	/* for local socket address */
struct sockaddr_in peeraddr_in;	/* for peer socket address */

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


  int c;
  extern char *optarg;
  extern int optind;
  int errflg = 0;
  int bytes;
  int returned_from_longjump;
  char *ofile = NULL;
  char aport[8];
  int f;

  char *bcnuhome, *bcnuagent;

  debug = 0;
  ping = 0;
  agent = 0;
  file = 0;
  mess = 0;
  asend = 0;
  severity = 0;
  port = 0;
  xclient = 1;
  htype = HOST_ANY;
  waitclose=0;

  strcpy (master, argv[argc - 1]);	/* get host name of master */
  strcpy (infile, "/");
  *hostfor = 0;

  while ((c = getopt (argc, argv, "xvds:f:pa:e:m:n:h:t:")) != EOF)
    switch (c)
      {
      case 's':		/* socket no */
	port++;
	strcpy (aport, optarg);
	break;
      case 'f':		/* File name of input */
	strcpy (infile, optarg);
	file++;
	break;
      case 'h':		/* host we are logging for */
	strcpy (hostfor, optarg);
	break;
      case 'm':		/* Message */
	strcpy (msg, optarg);
	mess++;
	break;
      case 't':		/* type of host */
	htype = atoi (optarg);
	break;
      case 'e':		/* Severity e.g. error */
	severity = atoi (optarg);
	break;
      case 'n':		/* Name of output file */
	strcpy (filename, optarg);
	break;
      case 'd':		/* debug flag */
	debug++;
	break;
      case 'p':		/* ping */
	ping++;
	strcpy (cmd, BCNU_MSG_PING);
	break;
      case 'x':		/* extended client */
	xclient=0;
	break;
      case 'a':		/* Agent type message */
	strcpy (cmd, optarg);
	if (strcasecmp (cmd, BCNU_MSG_AGT_SEND) == 0)
	{
	  asend++;
	  file++;
	  }
	else
	  agent++;
	break;
      case 'v':		/* Version info */
	printf ("%s version %s.%s\n", id, BCNU_VER, subver);
	errflg++;
      case '?':
	errflg++;
      }
  if (errflg)
    {
      fprintf (stderr, "usage: %s -s port -f file -a command -m message -e severity master %s\n", argv[0], usage);
      exit (2);
    }
/*
   get the name of the agent from environment
 */
  if ((bcnuagent = getenv ("BCNUAGENT")) == NULL)
    bcnuagent = "none";
/*
   set up program name
 */

  strcpy (progname, argv[0]);

/* clear out address structures */
  memset ((char *) &myaddr_in, 0, sizeof (struct sockaddr_in));
  memset ((char *) &peeraddr_in, 0, sizeof (struct sockaddr_in));

/* Set up the peer address to which we will connect. */
  peeraddr_in.sin_family = AF_INET;

/* Get the host information for the hostname that the
   * user passed in.
 */
  hp = gethostbyname (master);
  if (hp == NULL)
    {
      sprintf (buf, "%s not found by nameservice\n", master);
      bcnu_error (buf);
    }
  peeraddr_in.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;

/*
   Get the port info
 */
  peeraddr_in.sin_port = htons (BCNU_PORT);
  if (port)
    {
      if (isdigit (*aport))
	peeraddr_in.sin_port = htons (atoi (aport));
      else
	{
	  if ((peeraddr_in.sin_port = htons (bcnu_port (aport))) == 0)
	    {
	      sp = getservbyname (aport, "tcp");
	      if (sp == NULL)
		{
		  bcnu_error ("Invalid service port");
		}
	      else
		peeraddr_in.sin_port = sp->s_port;
	    }
	}
    }

  /* Create the socket. */
  s = socket (AF_INET, SOCK_STREAM, 0);
  if (s == -1)
    {
      bcnu_error ("Unable to create socket");
    }

  if ((returned_from_longjump = sigsetjmp (env,savemask)) != 0)
    switch (returned_from_longjump)
      {
      case SIGALRM:
	sprintf (buf, "Connect to %s timed out", master);
	bcnu_error (buf);
	break;
      }
  signal (SIGALRM, signal_handler);
  alarm (30);

  /* Try to connect to the remote server at the address
   * which was just built into peeraddr.
   */
  if (connect (s, (struct sockaddr *) &peeraddr_in, sizeof (struct sockaddr_in)) == -1)
    {
      sprintf (buf, "Unable to connect to %s:%u", master, ntohs (peeraddr_in.sin_port));
      bcnu_error (buf);
    }
/*
   get socket number
 */
  addrlen = sizeof (struct sockaddr_in);
  if (getsockname (s, (struct sockaddr *) &myaddr_in, &addrlen) == -1)
    {
      bcnu_error ("unable to read socket address");
    }


  time (&timevar);

  if (debug)
    printf ("Connected to %s on port %u at %s",
	    master, ntohs (myaddr_in.sin_port), ctime (&timevar));

/*
   get ip address
 */
  if (gethostname (myhostname, 80) == 0)
    hostname = myhostname;
  else
    hostname = inet_ntoa (myaddr_in.sin_addr);

  if (!strlen (hostfor))
    strcpy (hostfor, hostname);

  if (debug)
    printf ("Hostname of this box is %s\n", hostname);

/*
   is it a ping
 */
  if (ping || (strcasecmp (cmd, BCNU_MSG_AGT_RELOAD) == 0))
    {

      if (xclient)
	sprintf (buf, "V%d:%s:", BCNU_PROTO, cmd);
      else
	sprintf (buf, "%s:", cmd);
      pump_data (strlen (buf));	/* send the message out  */
      puts (buf);		/* display the result */
    }
/* network info
 */
  else if (agent && (strcasecmp (cmd, BCNU_MSG_AGT_NET) == 0))
    {
      switch (htons(peeraddr_in.sin_port))
	{
	case 21:	/* ftp */
	  waitclose++;
	case 25:	/* smtp */
	  waitclose++;
	case 80:	/* http */
	  waitclose++;
	  sprintf (buf, "HEAD %s HTTP/1.0\n\n", infile);
	  break;		/* get file if http */
	case 3128:	/* squid */
	  waitclose++;
	  sprintf (buf, "GET %s HTTP/1.0\n\n", infile);
	  break;		/* get file if http */
	case 515:	/* lpd */
	  if (file)
	    {
	      sprintf (buf, "%c%s\n", 04, infile);
	      *portmsg=0;
	      break;		/* lpd status */
	    }
	default:
	  sprintf (buf, "QUIT\r\n\n");	/* quit */
	}
      if (strlen (portmsg))	/* specific mesage */
	sprintf (buf, "%s\n", portmsg);
      else
	pump_data (strlen (buf));	/* send the message out  */
      fputs (buf, stdout);	/* display the result */
    }
  else if (agent)
    {
      /*
         I want agent information format the message
       */
      if (xclient)
	sprintf (buf, "V%d:%s:%d:%s:%d:%s:%s:%d::%s", BCNU_PROTO, cmd, severity, hostfor, htype, hostname, bcnuagent, time (&timevar), infile);
      else
	sprintf (buf, "%s:%d:%s:%s:%s:%d:%s", cmd, severity, hostfor, hostname, bcnuagent, time (&timevar), infile);
      while (bytes = pump_data (strlen (buf)))
	{
	  fwrite (buf, sizeof (char), bytes, stdout);
	  strcpy (buf, "OK");
	}
    }

  else if (file)
    {
      if (mess)
	{
	  /*
	     I want to send a file with my message
	   */
	  if (xclient)
	    sprintf (buf, "V%d:%s:%d:%s:%d:%s:%s:%d:%s:%s", BCNU_PROTO, BCNU_MSG_STATUS_FILE, severity, hostfor, htype, hostname, bcnuagent, time (&timevar), msg, filename);
	  else
	    sprintf (buf, "%s:%d:%s:%s:%s:%d:%s:%s", BCNU_MSG_STATUS_FILE, severity, hostfor, hostname, bcnuagent, time (&timevar), msg, filename);
	}
      else if (asend)
	{
	  /*
	     I want to send a new agent file
	   */
	  if (xclient)
	    sprintf (buf, "V%d:%s:%d:%s:%d:%s:%s:%d:%s:%s", BCNU_PROTO, BCNU_MSG_AGT_SEND, severity, hostfor, htype, hostname, bcnuagent, time (&timevar), "", filename);
	  else
	    sprintf (buf, "%s:%d:%s:%s:%s:%d:%s:%s", BCNU_MSG_AGT_SEND, severity, hostfor, hostname, bcnuagent, time (&timevar), "", filename);
	}
      pump_data (strlen (buf));	/* send messages */
      if (debug)
	printf ("opening file %s\n", infile);

      /*
         Open the file to be sent 
       */
      if ((fd = open (infile, O_RDONLY)) <= 0 )
	{
	  bcnu_error ("error on opening input file");
	}
      /*
         send the file
       */
      while (bytes = read (fd, buf, BCNU_MESS))
	{
	  if (debug)
	    printf ("sending:%d bytes\n", bytes);
	  pump_data (bytes);
	}
      close (fd);
    }
  /*
     I only want to send a message
   */
  else if (mess)
    {
      if (xclient)
	sprintf (buf, "V%d:%s:%d:%s:%d:%s:%s:%d:%s:", BCNU_PROTO, BCNU_MSG_STATUS_LOG, severity, hostfor, htype, hostname, bcnuagent, time (&timevar), msg);
      else
	sprintf (buf, "%s:%d:%s:%s:%s:%d:%s:", BCNU_MSG_STATUS_LOG, severity, hostfor, hostname, bcnuagent, time (&timevar), msg);
      pump_data (strlen (buf));
    }

/*
   close the socket
 */
  if (waitclose)
  	sleep(3);
  shutdown (s, 2);
  exit (0);
}
