static const char* version_id =
  "$Id: msgd.cc,v 3.5 1999/10/22 20:50:46 v2kea318 Exp $";

// $Log: msgd.cc,v $
// Revision 3.5  1999/10/22  20:50:46  v2kea318
// Use the OO Thread interface for thread things.
//
// Revision 3.4  1999/10/14  16:41:47  v2kea318
// Use the pseudo machine independant thread.h header to define all
// thread related things.
//
// Revision 3.3  1999/10/06  14:27:46  v2kea318
// When terminating, cleanup after the autoreply system with the
// AutoReply::cleanup method.
//
// Revision 3.2  1999/10/05  15:14:30  v2kea318
// Reimplement the autoreply system in the AutoReply class.  Much of the
// code added for version 3.1 is moved to that class.
//
// Revision 3.1  1999/09/30  20:35:02  v2kea318
// Added a new thread to handle an autoreply system.  This thread will
// read from a FIFO file the autoreply string, if the feature is being
// turned on.  These parameters are used in handle_message, so they
// are passed in the HandleMessageParms structure.
//
// Revision 2.8  1999/09/21  20:40:32  v2kea318
// Tell the ServerSocket::accept method to build the client socket to be
// thread-safe.
//
// Revision 2.7  1999/09/08  15:36:14  v2kea318
// If we take an exception when registering the process, only sleep for 5
// seconds before trying again.  Otherwise, sleep for 3 minutes.
//
// Revision 2.6  1999/08/16  16:38:08  v2kea318
// Remove the registration in the main routine.  The check_name_server_thread
// thread will take care of the registration for us, and there's no need for
// two.
//
// Revision 2.5  1999/08/12  19:25:17  v2kea318
// Do not embed C++ (//) style comments inside C (/* */) style comments.
//
// Revision 2.4  1999/08/10  21:16:40  v2kea318
// When we take an exception in the main routine and exit, call the
// termination handler to properly cleanup.  Catch all exceptions in the
// handle_term routine.  Start a thread that will periodically run,
// registering the daemon again.  This ensures that, if the server has
// crashed and been restarted, this daemon will get reregistered without
// having to restart it.
//
// Revision 2.3  1999/08/06  19:01:05  v2kea318
// Spawn the message handler thread sooner, immediately on verifying the
// action code.  The handler thread will take care of reading the message
// from the socket.
//
// Revision 2.2  1999/08/06  17:53:12  v2kea318
// Use write_int() to send the port number when registering.
//
// Revision 2.1  1999/08/06  17:49:54  v2kea318
// Redesigned the daemon.  This program will now act as a server, listening
// on a port number derived from the numeric user id of the user.  The
// daemon will receive messages directly from a user, rather than having
// them routed from the server.
//
// Revision 1.6  1999/07/30  20:40:50  v2kea318
// Use Pthread.h instead of pthread.h to define threads.  This is a
// temporary kludge because of some extraneous spaces in the standard
// version.
//
// Revision 1.5  1999/07/30  17:55:48  v2kea318
// Reformat the header file names for Standard C++ compliance.
//
// Revision 1.4  1999/07/09  14:51:39  v2kea318
// Reset the read_string() function usage to get rid of the optional boolean
// flag.  The routine now blocks until all bytes are read, so there is no need
// for such a flag.
//
// Revision 1.3  1999/07/06  19:49:10  v2kea318
// Add a catch block for the SocketIOBadNBytesException socket exception.
// Add the retry flag to the call to read_string for reading the message text.
//
// Revision 1.2  1999/07/06  16:59:50  v2kea318
// Initialize the message handler system before doing anything else.
//
// Revision 1.1  1999/05/17  14:27:18  v2kea318
// Initial revision
//
/***************************************************************************

Provide the user daemon program.  This program will connect to the
msg server and will accept messages routed by the server.  The messages
will be displayed in windows.

***************************************************************************/

#include "Thread.h"
#include "msg_codes.h"
#include "msg_constants.h"
#include <iostream>
#include <cstdlib>
#include <stdexcept>
#include "Socket.h"
#include "ServerSocket.h"
#include "action_evaluation.h"
#include "action_transfer.h"
#include "server_name.h"
#include "string-transfer.h"
#include "get_userid.h"
#include "cfailure.h"
#include <csignal>
#include "handle_message.h"
#include "autoreply.h"

// subroutine prototypes.
static void register_proc(const int prtnum); // register the process
static int get_port();

// handle termination signals.  These should deregister the process.
static void handle_term(int s);

// periodically check on the name server.
static void* check_name_server_thread(void* p);

//***************************************************************************

int main(int argc, char* argv[])
{
  try {
    // initialize the message handler system.
    handle_message_initialize();

    // determine on what port we are to listen.
    const int port = get_port();

    // Start a server socket on that port.
    ServerSocket server(port);

    // Start the autoreply system.
    AutoReply autoreply;
    AutoReply::start_autoreply_monitor_thread();

    /* Spawn a thread to periodically check that the server is still
       running.  This thread will take care of registering the daemon. */
    Thread::create()->start(check_name_server_thread,
			    const_cast<int *>(&port));
    
    // set up signal handlers to handle termination by deregistering.
    int sigs[] = {
      SIGINT, SIGQUIT, SIGABRT, SIGKILL, SIGDANGER, SIGTERM
    };
    size_t nsigs = (sizeof sigs) / sizeof(int);
    for (size_t i = 0; i < nsigs; i++) signal(sigs[i], handle_term);

    // start accepting connections.
    while (true) {
      Pointer<Socket> client = server.accept(true);

      // get the desired action.
      msg_action_code action = get_action(client);

      // Is this a test?
      if (action == TEST) {
	  send_action(client, success, false);
	  continue;
      }      
      
      // the action code can only be ROUTE or COPY.
      if (action != ROUTE && action != COPY) {
	send_action(client, unrecognized_action, false);
	continue;
      }
      send_action(client, success, false);

      // dispatch a thread to handle this.
      Thread::create()->start(handle_message,
			    new HandleMessageParms(client, action, autoreply));
    }
  } catch (const SocketBase::ConnectFailure& cf) {
    cerr << cf << endl;
  } catch (const SocketBase::UnknownHost& uh) {
    cerr << uh.hostname() << " wanted for server but is " << uh.what() << endl;
  } catch (const SocketBase::SocketIOBadNBytesException& bnb) {
    cerr << "bad nbytes exceptions, wanted " << bnb.nbytes_wanted()
	 << ", used " << bnb.nbytes_used() << endl;
  } catch (const SocketBase::SocketException& se) {
    cerr << "socket exception --> " << se.what() << endl;
  } catch (const exception& e) {
    cerr << e.what() << endl;
  } catch (...) {
    cerr << "unhandled exception" << endl;
    throw;
  }

  /* If we're here, we must have taken an exception.  Call the terminate
     handler to properly cleanup.  That routine will exit. */
  handle_term(EXIT_FAILURE);
}

//***************************************************************************

static void register_proc(const int port_num)
{
  // get the user's user id.
  string uid = get_userid();

  // on what machine is the name server running?
  string nserver_host = get_server_name();

  // Open a socket to the name server.
  Socket s(nserver_host, msg_port);

  // send the REGISTER_USER code.
  send_action(s, REGISTER_USER);

  // send the user id.
  write_string(s, uid);

  // send the port number.
  write_int(s, port_num);

  // OK?
  eval_response(s);
}

//***************************************************************************

static void handle_term(int s)
{
    try {	
	// open a new connection to the server.
	Socket srv(get_server_name(), msg_port);
	
	// send the CLOSE_SESSION code.
	send_action(srv, CLOSE_SESSION);
	
	// send the userid.
	string uid = get_userid();
	write_string(srv, uid);
	eval_response(srv);
    } catch (...) {
	// took an exception.  Who cares.
    }    

    // cleanup for the auto reply system.
    AutoReply::cleanup();
    
    // exit.
    exit(s);
}

//***************************************************************************

#include <pwd.h>
#include <userpw.h>
#include <unistd.h>

// Use the user's numeric user id as the port number.
int get_port()
{
  return getpwuid(getuid())->pw_uid;
}

//***************************************************************************

void* check_name_server_thread(void* p)
{
    const int* port = reinterpret_cast<const int*>(p);
    
    // get the server name.
    const string server_name(get_server_name());
    
    // start an infinite loop.
    while (true) {
	try {
	    /* Try to register the process again.  If this daemon is
	       already registered, i.e. the server hasn't crashed, 
	       nothing will happen; the server will prevent multiple
	       registration.  If the daemon isn't registered, it will
	       be. */
	    register_proc(*port);
	    sleep(180);  // rest for a few minutes.
	} catch (const SocketBase::ConnectFailure& cf) {
	  // Server is down so try again quickly.
	  sleep(5);
	}
    }
    
    // return value.
    return NULL;
}


