/*
 * Copyright (C) 1994, 1995 Free Software Foundation
 *
 * This program 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.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can either send email to this
 * program's author (see below) or write to:
 *
 *              The Free Software Foundation, Inc.
 *              675 Mass Ave.
 *              Cambridge, MA 02139, USA. 
 *
 * Please send bug reports, etc. to zappo@gnu.ai.mit.edu.
 *
 * gtalkd.c
 *
 * Purpose:
 *  This is the main file for gtalkd, the GNU talk daemon, which is a
 * replacement, and advancement above the standard BSD talk daemon.
 *
 * $Log: gtalkd.c,v $
 * Revision 1.12  1995/07/15  15:42:49  zappo
 * Added new AUTOCONF variable NUM_OPENLOG_PARAMETERS which, if set to 3
 * lets us define the type of logging we wish.  Otherwise, assume only 2
 * parameters.
 *
 * Revision 1.11  1995/05/09  23:47:50  zappo
 * Added copyright information command line argument
 *
 * Revision 1.10  1995/05/09  23:04:33  zappo
 * Fixed the --help option, and made verbosity incremental (-vv makes
 * twice the verbosity.)
 *
 * Revision 1.9  1995/04/01  16:27:18  zappo
 * Statisized an assigned local constant variable
 *
 * Revision 1.8  1995/03/25  04:20:14  zappo
 * Updated copyright.
 *
 * Revision 1.7  1995/03/23  03:00:47  zappo
 * Fixed content of the --help message
 *
 * Revision 1.6  1995/03/04  14:46:44  zappo
 * Modified to use syslog to report errors when tty flag is not set
 *
 * Revision 1.5  1995/02/28  03:47:18  zappo
 * Fixed call to compile_check which never checked the return value to
 * see if we yad to exit or not.
 *
 * Revision 1.4  1995/02/25  20:54:36  zappo
 * Added extra help in when error encountered in options parse
 *
 * Revision 1.3  1995/02/12  14:20:10  zappo
 * Added "sun talk forwarding" support to create the forwarding socket
 *
 * Revision 1.2  1995/02/01  03:48:48  zappo
 * *** empty log message ***
 *
 */
#include "etalklib.h"
#include "gtalkd.h"

#include "getopt.h"

int verbose     = FALSE;	/* verbose requirement  */
int syslogdebug = FALSE;	/* syslogdebug variable */

struct DaemonContext Ctxt;

int main(argc, argv)
     int argc;
     char *argv[];
{
  if(DMN_check_compile() == Fail)
    {
      fprintf(stderr, "Message size check error!\n");
      exit(1);
    }

  Ctxt.tty = FALSE;
  Ctxt.type = GTALKD;
  Ctxt.forever = FALSE;
  Ctxt.sunf = FALSE;

  /* Here is a chunk from the GNU getopt manual page with the correct
   * extensions for etalk.
   */
  while(1)
    {
      int option_index = 0;	/* options returned index */
      int c;			/* return value           */
      static char *short_options = "?ChvsStbf";
      static struct option long_options[] =
	{
	  { "bsd", FALSE, NULL, 'b' },
	  { "copyright", FALSE, NULL, 'C' },
	  { "forever", FALSE, NULL, 'f' },
	  { "gnu", FALSE, NULL, 'g' },
	  { "help", FALSE, NULL, 'h' },
	  { "sun", FALSE, NULL, 's' },
	  { "sunforward", FALSE, NULL, 'S' },
	  { "tty", FALSE, NULL, 't' },
	  { "user", FALSE, NULL, 'u' },
	  { "verbose", FALSE, NULL, 'v' },
	  { "version", FALSE, NULL, 0 },
	  { NULL, FALSE, NULL, 0 }
	};

      c = getopt_long(argc, argv, short_options,
		      long_options, &option_index);

      if(c == -1) break;
            
      switch(c) {
      case 0:
	printf("%s\n", GTALKD_);
	exit(0);
      case 'b':
	Ctxt.type = NTALKD;
	if(verbose)
	  printf("Setting type to BSD post 4.2 NTALK protocol only.\n");
	break;
      case 'C':
	CW_display_copywrite();
	exit(0);
      case 'f':
	Ctxt.forever = TRUE;
	break;
      case 'g':
	Ctxt.type = GTALKD;
	break;
      case 'h':
      case '?':
	printf("%s\n", GTALKD_);
	printf("gtalkd command line options:\n\n");
	printf(" --copyright\t-C\tDisplay copyright information.\n");
	printf(" --bsd\t\t-b\tEmulate BSD post 4.2 talk protocol, by not accepting\n");
	printf("\t\t\tversion numbers > 1, or any GNU extionsions.\n");
	printf(" --forever\t-f\tOpen own socket, and loop forever.  (Used with --tty\n");
	printf("\t\t\tfor debugging.)\n");
	printf(" --gnu\t\t-g\tRun as GNU talk daemon with all the options.\n");
	printf(" --help\t\t-h,-?\tPrint this help.\n");
	printf(" --sun\t\t-s\tUse Sun OTALK protocol.\n");
	printf(" --sunforward\t-S\tSun requests are converted, and forwarded to the\n");
	printf("\t\t\tNTALK protocol port.\n");
	printf(" --tty\t\t-t\tUse for testing on command line.\n");
	printf(" --user\t\t-u\tExecute from USER space with non-special socket id\n");
	printf("\t\t\t[not yet available].\n");
	printf(" --verbose\t-v\tPrint debugging information as we go to stdout.\n");
	printf("\t\t\tWhen used in terminal mode, it prints output to STDOUT,\n");
	printf("\t\t\twhen in daemon mode, errors and other verbose\n");
	printf("\t\t\tinformation is logged to syslog\n");
	printf(" --version\t\tPrint version and exit.\n\n");
	exit(0);
      case 's':
	Ctxt.type = OTALKD;
	break;
      case 'S':
	Ctxt.sunf = TRUE;
	break;
      case 't':
	printf("%s\n", GTALKD_);
	Ctxt.tty = TRUE;
	break;
      case 'u':
	printf("%s\n", GTALKD_);
	printf("User space option not yet available.\n");
	exit(0);
	break;
      case 'v':
	verbose++;
	break;
      default:
	printf("Illegal option %c, try: %s --help\n", c, argv[0]);
	exit(0);
      }
    }

  if(verbose && !Ctxt.tty)
    {
      /* Set debug level instead of using verbosity flag? */
      syslogdebug = verbose;
      verbose = FALSE;
    }
  else if(Ctxt.tty)
    {
      syslogdebug = FALSE;
    }

  /* Print out a status based on these values. */
  if(verbose)
    {
      if(Ctxt.type == GTALKD)
	printf("GTALK deamon protocol set to GTALK.\n");
      else if(Ctxt.type == NTALKD)
	printf("GTALK deamon protocol set to NTALK.\n");
      else if(Ctxt.type == OTALKD)
	printf("GTALK deamon protocol set to OTALK.\n");
    }
  
  /* do that log thing.  Check number or parameters it needs dependant
   * on the operating system
   */
  openlog("gtalkd", LOG_PID
#if NUM_OPENLOG_PARAMETERS == 3
	  , LOG_DAEMON
#endif
	  );

  if(syslogdebug)
    syslog(LOG_DEBUG, "Syslog debug flag is set");

  if(Ctxt.sunf)
    {
      if(Ctxt.type != OTALKD)
	{
	  if(verbose)
	    {
	      printf("Sun forwarding flag invoked without SUN flag set.\n");
	    }
	  else
	    {
	      syslog(LOG_ERR, "Sun forwarding flag invoked without SUN flag set.");
	    }
	  exit(0);
	}
      if(verbose)
	printf("Setting forwarding to ntalk protocol port.\n");
    }

  Ctxt.pid       = getpid();

  Ctxt.me        = HOST_gen_local_host();
  Ctxt.checkrate = 5;

  /* Set up ringer port.  Unlike etalk, we don't need to reset the
   * port because the servent doesn't need that type of info.
   * If we are in forward mode, then define this as being the
   * forwarding port. 
   */
  UDP_setup_localport();
  Ctxt.udp_ring  = UDP_host(Ctxt.me->name);
  if(Ctxt.sunf)
    {
      struct servent     *sp;
      Ctxt.udp_ring->name = "forward_port";

      /* Determine the port to use for ntalk forwarding */
      if((sp = getservbyname("ntalk", "udp")) == NULL) 
	{
	  if(verbose)
	    {
	      printf("getservbyname - Error finding service\n");
	      printf("Cannot forward without knowledge of ntalk.\n");
	    }
	  else
	    {
	      syslog(LOG_ERR, "getservbyname: error finding service.");
	    }
	  return 1;
	}
      Ctxt.udp_ring->raddr.sin_port = sp->s_port;
      if(verbose)
	{
	  printf("Forwarding socket allocated at: ");
	  print_sockaddr((struct sockaddr *)&Ctxt.udp_ring->laddr);
	  printf("\n");
	}
    }
  else
    {
      /* Setup for ringer type actions */
      Ctxt.udp_ring->name     = "ring_port";
    }

  /* Load in the servent port numbers.
   * If we are not root, this just won't work.  Mabee to one of those
   * posix things, but the errors will auto-exit is anyway. FOO!
   */
  if(Ctxt.tty)
    {
      static char *services[] = { "talk", "ntalk", "ntalk" };

      if(verbose)
	printf("Loading udp socket servent for %s\n", services[Ctxt.type]);
      else if(syslogdebug > 1)
	syslog(LOG_DEBUG, "Loading udp socket servent for %s\n", services[Ctxt.type]);
      Ctxt.talk = UDP_servent(services[Ctxt.type]);
      if(Ctxt.talk == (struct InputDevice *)-1)
	{
	  /* Use printf because we know we are on a TTY in this case. */
	  printf("bind failed: Ignoring service %s.\n", services[Ctxt.type]);
	  Ctxt.talk = NULL;
	}
    }
  else
    {
      /* Here, turn TTY into a UDP socket input object.
       */
      Ctxt.talk = UDP_stdin();
    }

  if(!Ctxt.talk)
    {
      if(Ctxt.tty)
	printf("Error binding sockets.\n");
      else
	syslog(LOG_ERR, "Error finding UDP information.\n");

      exit(1);
    }

  Ctxt.talk->readme  = GTR_read_request;
  Ctxt.talk->timefn  = GCM_timeout_messages;
  Ctxt.talk->timeout = Ctxt.checkrate;

  /* Select loop.  This should be the only occurance with NULL as the
   * reton parameter...
   */
  ET_select_all(&Ctxt, NULL);

  /* Close the system log. */
  closelog();

  return 0;
}
