/* driver.c -- driver for srouted daemon */

/*
 *  srouted -- silent routing daemon
 *  Copyright (C) 1995 Kevin Buhr
 *
 *  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 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef lint
static char rcsid[] = "$Id: driver.c,v 1.5 1995/02/17 17:43:33 buhr Exp $";
#endif /* not lint */

#define DEFEXTS
#include "defs.h"
#include "error.h"
#include "input.h"
#include "kernel.h"
#include "output.h"
#include "table.h"
#include "timer.h"
#undef DEFEXTS

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/protocols.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include <netdb.h>

static int dumproute=0;
static int tracechange=1;

static void sg_usr1( int signum );
static void sg_usr2( int signum );


/*
 *	SIGUSR1 signal handler  -- bump trace level
 */

static void sg_usr1( int signum )
{
   if( g_tracelevel < 3 )
      g_tracelevel++;
   else if( g_tracenlevel < 4 )
      g_tracenlevel++;
   else {
      g_tracelevel=0;
      g_tracenlevel=0;
   }
   tracechange = 1;
}


/*
 *	SIGUSR2 signal handler  -- dump internal routing table
 */

static void sg_usr2( int signum )
{
   dumproute = 1;
}


int main(int argc, char **argv)
{
   int length, rc;
   struct sockaddr_in ripaddr, fromaddr;
   struct tb_address from;
   int fromaddr_len;
   time_t next, now;
   long pause;
   struct sigaction newsig;
   struct timeval timeout;
   fd_set readfds;
   int one=1;
   struct servent *routedservent;
   
   if( geteuid() ) {
      die0( ERCDR_NOTROOT );
   }

   while(--argc>0) {
      argv++;
      if( (*argv)[0] != '-' ) {
	 die0( ERCDR_SYNTAX );
      }
      switch( (*argv)[1] ) {
      case 't':
	 sg_usr1(0);
	 break;
      default:
	 die0( ERCDR_SYNTAX );
	 break;
      }
      if( (*argv)[2] ) {
	 die0( ERCDR_SYNTAX );
      }
   }

   routedservent = getservbyname( "route", "udp" );
   if(routedservent) {
      g_routedport = ntohs(routedservent->s_port);
   } else {
      warn0( ERCDR_GETSERV );
   }
   note1( ERCDR_USINGPORT, g_routedport );

   ripaddr.sin_family = AF_INET;
   ripaddr.sin_port = htons(g_routedport);
   ripaddr.sin_addr.s_addr = htonl(0);

   g_rs = socket(AF_INET, SOCK_DGRAM, IP_UDP);
   if( g_rs < 0 ) {
      die1( ERCDR_SOCKET, errno );
      exit(1);
   }
   if( fcntl( g_rs, F_SETFL, O_NONBLOCK ) ) {
      die1( ERCDR_FCNTL, errno );
      exit(1);
   }
   if( setsockopt( g_rs, SOL_SOCKET, SO_BROADCAST, 
		  &one, sizeof(one) ) < 0 ) {
      die1( ERCDR_SSOPT, errno );
      exit(1);
   }
   if( bind( g_rs, (struct sockaddr *) &ripaddr, sizeof(ripaddr) ) < 0 ) {
      die1( ERCDR_BIND, errno );
      exit(1);
   }

   newsig.sa_handler = sg_usr1;
   newsig.sa_flags = SA_NOMASK;
   sigaction( SIGUSR1, &newsig, NULL );
   newsig.sa_handler = sg_usr2;
   newsig.sa_flags = SA_NOMASK;
   sigaction( SIGUSR2, &newsig, NULL );

   note0( ERCDR_GETIFCONF );
   kr_getifconf();
   note0( ERCDR_INITROUTE );
   tb_initroute();

   note0( ERCDR_GETTABLE );
   out_gettable( (struct rip *) page, sizeof(page) );

   for(;;) {

      if( dumproute ) {
	 dumproute = 0;
	 er_log( ERL_FORCE, ERCDR_DUMPROUTE );
      }

      if( tracechange ) {
	 tracechange = 0;
	 er_tracechange();
      }

      now = time( NULL );
      next = tm_expire( now );
      if( next ) {
	 pause = next - time( NULL );
	 if( pause < 0 )
	    continue;
	 timeout.tv_sec = pause;
	 timeout.tv_usec = 0;
      }
      note3( ERCDR_SCHED, now, next, pause );
      FD_ZERO( &readfds );
      FD_SET( g_rs, &readfds );
      rc = select( g_rs+1, &readfds, NULL, NULL, next ? &timeout : NULL );
      if( rc == -1 ) {
	 if( errno == EINTR )
	    continue;
	 else
	    warn1( ERCDR_SELECT, errno );
      }
      if( rc == 0 ) {
	 note0( ERCDR_TIMEOUT );
	 continue;  /* must have been a timeout */
      }
      note0( ERCDR_GOTINPUT );
      fromaddr_len=sizeof(fromaddr);
      length = recvfrom( g_rs, page, sizeof(page), 0, 
			(struct sockaddr *) &fromaddr, &fromaddr_len );

      if( length < 0 ) {
	 warn1( ERCDR_RECVFROM, errno );
	 continue;
      }

      if( length > sizeof(page) ) {
	 warn2( ERCDR_BIGRIP, length, sizeof(page) );
	 continue;
      }
      if( fromaddr_len != sizeof(fromaddr) ) {
	 warn2( ERCDR_ADDRSIZE, fromaddr_len, sizeof(fromaddr) );
	 continue;
      }

      from.tba_addr = * (struct sockaddr *) &fromaddr;
      tb_chkaddr( &from );

      in_processrip( (struct rip *) page, length,
		    (struct sockaddr *) &fromaddr );

   }

}
