// -*- Mode: C++; -*-
//                              File      : ReadyChannel_d.cc
//                              Package   : omniNotify-daemon
//                              Created on: 1-Jan-1998
//                              Authors   : gruber&panagos
//
//    Copyright (C) 1998-2001 AT&T Laboratories -- Research
//
//    This file is part of executable program omniNotify-daemon
//    and is distributed with the omniNotify release.
//
//    omniNotify-daemon 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//    USA.
//
// Description:
//    Implementation of the omniNotify daemon process
//
 
/*
$Log: ReadyChannel_d.cc,v $
Revision 1.64  2001/10/09 04:27:22  alcfp
preparing for 11 release

Revision 1.63  2001/10/06 00:41:22  alcfp
type fix

Revision 1.62  2001/10/02 14:07:49  alcfp
improved error messages for command line errors

Revision 1.61  2001/09/28 02:10:10  alcfp
oops

Revision 1.60  2001/09/27 17:31:42  alcfp
fixed target unavailable case

Revision 1.59  2001/09/27 17:20:02  alcfp
better error messages for prop combo problem

Revision 1.58  2001/09/19 21:09:59  alcfp
Added cleanup support to interactive api

Revision 1.57  2001/08/03 17:54:12  alcfp
added support for AttNotification

Revision 1.56  2001/06/26 20:01:00  alcfp
updated copyright notices, added support for omniORB4, switched default to POA

Revision 1.55  2001/06/22 07:00:19  alcfp
moved to new logging scheme

Revision 1.54  2001/06/12 17:56:21  alcfp
minor cleanup

Revision 1.53  2001/05/29 20:37:57  alcfp
larger maxtcp

Revision 1.52  2000/11/17 21:39:33  alcfp
small fixes to get rid of some Sun CC 5.0 compiler warnings

Revision 1.51  2000/11/16 04:20:34  alcfp
changes to make use of omniNotify.h and wrapper files consistent

Revision 1.50  2000/11/15 21:19:14  alcfp
changed to new corba_wrappers approach

Revision 1.49  2000/11/05 04:48:07  alcfp
changed in defaults, env variable overrride, try_pull variants

Revision 1.48  2000/10/09 19:47:20  alcfp
modified version number

Revision 1.47  2000/09/27 19:30:10  alcfp
Stopped using tcAliasExpand=1 : dynamic evaluation now uses typecode equivalence test or DynFoo narrow test rather than checking for a specific TCKind

Revision 1.46  2000/08/22 18:21:47  alcfp
getopt fix

Revision 1.45  2000/08/16 20:18:50  alcfp
Added licensing notice to each .h and .cc file where library files get GLPL notice and daemon file gets GPL notice -- examples do not claim any license but point out that the library and daemon code does have a license notice

*/
 
#include <stdio.h>
#include <stdlib.h>
#include "corba_wrappers.h"
#include "CosNotifyShorthands.h"
#include "omniNotify.h"
#include "RDIOSWrappers.h"
#include "RDIStringDefs.h"
#include "RDIstrstream.h"

#define OMNINOTIFY_VERSION "1.1"
 
// ----------------------------------------------------------//

void processUserInput(const char* prompt, AttN::Server_ptr server);

// ----------------------------------------------------------//

int main(int argc, char** argv)
{
  AttN::Server_var server = AttN::Server::_nil();
  int indx=0, interactive=0;
  char* pn = argv[0];
  while (RDI_STRCHR(pn, '/')) { pn = RDI_STRCHR(pn, '/'); pn++; }
  while (RDI_STRCHR(pn, '\\')) { pn = RDI_STRCHR(pn, '\\'); pn++; }
  if (RDI_STRLEN(pn) == 0) { pn = (char*)"notifd"; }
  char* pname = CORBA_STRING_DUP(pn);

  // Increase the limit of open file descriptor to the maximum
  RDI_OS_MAX_OPEN_FILE_DESCRIPTORS;

  while ( indx < argc ) {
    if (RDI_STR_EQ(argv[indx], "-i")) {
      RDI_rm_arg(argc, argv, indx);
      interactive = 1;
    } else if (RDI_STR_EQ(argv[indx], "-h")) {
      fprintf(stdout, "Usage: %s [-i] [-h] [-n] [-c <f>]\n", pname);
      fprintf(stdout, "   -i       : interactive mode\n");
      fprintf(stdout, "   -h       : usage information\n");
      fprintf(stdout, "   -n       : do NOT register with the NameService\n");
      fprintf(stdout, "   -c <f>   : read config file <f>\n");
      fprintf(stdout, "   -v       : print software version\n");
      return 0;
    } else if (RDI_STR_EQ(argv[indx], "-v")) {
      fprintf(stdout, "notifd:  -- omniNotify server version %s\n", OMNINOTIFY_VERSION);
      return 0;
    } else {
      indx += 1;
    }
  }

  // Configure the undelying ORB and BOA/POA before creating
  // omniNotify server instance

  // We use 1000 here because using a small # limits
  // the number of outstanding pull calls from pull consumers
  // XXX What happens with an OS that does not support this call?
  WRAPPED_ORB_SETMAXTCP(1000); 
  WRAPPED_ORB_OA::init(argc, argv);

  // initialize the server
  server = omniNotify::init_server(argc, argv);
  if (CORBA::is_nil(server)) {
    fprintf(stdout, "\nnotifd: failed to create a new omniNotify server\n");
    exit(-1);
  }
  WRAPPED_ORB_OA::activate_oas();
  if ( interactive ) {
    processUserInput(pname, server);
  } else {
    WRAPPED_ORB_OA::run();
  }
  // destroy server
  try {
    server->destroy();
  } catch (...) {
    // server already destroyed?
  }
  CORBA_STRING_FREE(pname);
  WRAPPED_ORB_OA::cleanup();
  return 0;
}

// ----------------------------------------------------------//

void processUserInput(const char* prompt, AttN::Server_ptr server)
{
  char cmnd[1024], tname[1024];
  CORBA::Boolean success, target_changed, res_to_file;
  CORBA::String_var cmdres;
  AttN::NameSeq* target_nm = 0;
  const char* final_nm = "server";
  do {
    RDIstrstream str;
    str << "\n\t\t-------------------------------";
    str << "\n\t\tomniNotify Server Version " << OMNINOTIFY_VERSION;
    str << "\n\t\t AT&T Laboratories -- Research ";
    str << "\n\t\t-------------------------------\n\n";
    fprintf(stdout, "%s", str.buf());
  } while (0);

  AttN::Interactive_var target      = AttN::Interactive::_nil();
  AttN::Interactive_var next_target = AttN::Interactive::_narrow(server);
  target_changed = 1;
  success = 1;

  res_to_file = server->results_to_file(); // XXX can this change
  while ( 1 ) {
    if (target_changed) {
      target = next_target;
      if (CORBA::is_nil(target)) {
	goto server_problem;
      }
      if (target_nm) {
	delete target_nm; target_nm = 0;
      }
      CORBA::Boolean name_problem = 0;
      try {
	target_nm = target->my_name();
      } 
      catch ( CORBA::INV_OBJREF& e ) { name_problem = 1; } \
      catch ( CORBA::OBJECT_NOT_EXIST& e ) { name_problem = 1; } \
      catch ( CORBA::COMM_FAILURE& e ) { name_problem = 1; }
      if (name_problem || (target_nm->length() == 0)) {
	// target may have become invalid
	fprintf(stdout, "\nomniNotify: target %s not available, reverting to top-level server target\n", final_nm);
	target = AttN::Interactive::_narrow(server);
	fprintf(stdout, "\nomniNotify: new target ==> server\n");
	name_problem = 0;
	try {
	  target_nm = target->my_name();
	} 
	catch ( CORBA::INV_OBJREF& e ) { name_problem = 1; } \
	catch ( CORBA::OBJECT_NOT_EXIST& e ) { name_problem = 1; } \
	catch ( CORBA::COMM_FAILURE& e ) { name_problem = 1; }
	if (name_problem || (target_nm->length() == 0)) {
	  // something is very wrong
	  goto server_problem;
	}
      }
      final_nm = (*target_nm)[target_nm->length()-1];
    }
    success = 1;
    do {
      RDIstrstream str;
      str << "\n" << prompt << " [";
      for (unsigned int i = 0; i < target_nm->length(); i++) {
	if (i > 0) str << '.';
	str << (*target_nm)[i];
      }
      str << "]: ";
      fprintf(stdout, "%s", str.buf());
    } while (0);
    omni_thread::yield();
    if ( fgets(cmnd, 1024, stdin) ) {
      cmnd[ RDI_STRLEN(cmnd) - 1 ] = '\0';    // Remove new line
      RDIParseCmd p(cmnd);
      fprintf(stdout, "\n");
      if (p.argc == 0) {
	continue;
      }
      // first see if daemon can handle request 
      if ((p.argc == 1) && (RDI_STR_EQ_I(p.argv[0], "exit") || RDI_STR_EQ_I(p.argv[0], "quit"))) {
	fprintf(stdout, "omniNotify:  exiting....\n\n");
	break;
      }
      if ((p.argc == 1) && RDI_STR_EQ_I(p.argv[0], "children")) {
	AttN::NameSeq* child_names = 0;
	CORBA::Boolean cnames_problem = 0;
	try {
	  child_names = target->child_names();
	} 
	catch ( CORBA::INV_OBJREF& e ) { cnames_problem = 1; } \
	catch ( CORBA::OBJECT_NOT_EXIST& e ) { cnames_problem = 1; } \
	catch ( CORBA::COMM_FAILURE& e ) { cnames_problem = 1; }
	if (cnames_problem) {
	  fprintf(stdout, "\nomniNotify: target %s not available, reverting to top-level server target\n", final_nm);
	  next_target = AttN::Interactive::_narrow(server);
	  target_changed = 1;
	  fprintf(stdout, "\nomniNotify: new target ==> server\n");
	  continue;
	}
	if (child_names) {
	  RDIstrstream str;
	  if (child_names->length()) {
	    str << "Children of " << final_nm << ": ";
	    for (unsigned int i = 0; i < child_names->length(); i++) {
	      if (i > 0) str << ", ";
	      str << (*child_names)[i];
	    }
	    str << "\n  (Use 'go <name>' to go to one of the children)\n";
	  } else {
	    str << "No children\n";
	  }
	  delete child_names;
	  fprintf(stdout, "%s", str.buf());
	} else {
	  fprintf(stdout, "**Error getting children**\n");
	}
	continue;
      }
      if ((p.argc == 2) && RDI_STR_EQ_I(p.argv[0], "go") && RDI_STR_EQ_I(p.argv[1], "server")) {
	next_target = AttN::Interactive::_narrow(server);
	target_changed = 1;
	fprintf(stdout, "\nomniNotify: new target ==> server\n");
	continue;
      }
      // now see if server or target can handle cmnd
      // direct some commands to server regardless of currrent target
      CORBA::Boolean docmd_problem = 0;
      if ( ( (p.argc == 2) && RDI_STR_EQ_I(p.argv[0], "go") && 
	     ( RDI_STR_EQ_I(p.argv[1], "chanfact") ||
	       RDI_STR_EQ_I(p.argv[1], "filtfact") ||
	       RDI_STRN_EQ_I(p.argv[1], "chanfact.", 9) ||
	       RDI_STRN_EQ_I(p.argv[1], "filtfact.", 9) ) ) ||
	   ( (p.argc == 1) && 
	     ( RDI_STR_EQ_I(p.argv[0], "flags") ||
	       ((RDI_STRLEN(p.argv[0]) > 1) && ((p.argv[0][0] == '+') || (p.argv[0][0] == '-'))))) ) {
	try {
	  cmdres = server->do_command(cmnd, success, target_changed, next_target);
	}
	catch ( CORBA::INV_OBJREF& e ) { docmd_problem = 1; } \
	catch ( CORBA::OBJECT_NOT_EXIST& e ) { docmd_problem = 1; } \
	catch ( CORBA::COMM_FAILURE& e ) { docmd_problem = 1; }
	if (docmd_problem) {
	  // something is very wrong
	  goto server_problem;
	}
      } else {
	try {
	  cmdres = target->do_command(cmnd, success, target_changed, next_target);
	}
	catch ( CORBA::INV_OBJREF& e ) { docmd_problem = 1; } \
	catch ( CORBA::OBJECT_NOT_EXIST& e ) { docmd_problem = 1; } \
	catch ( CORBA::COMM_FAILURE& e ) { docmd_problem = 1; }
      }
      if (docmd_problem) {
	fprintf(stdout, "\nomniNotify: target %s not available, reverting to top-level server target\n", final_nm);
	next_target = AttN::Interactive::_narrow(server);
	target_changed = 1;
	fprintf(stdout, "\nomniNotify: new target ==> server\n");
	continue;
      }
      if (res_to_file) {
	fprintf(stdout, "%s\n", cmdres.in());
      }
      if (! success) {
	fprintf(stdout, "\nUse 'help' for a list of valid commands\n");
      }
      if ((p.argc == 1) && RDI_STR_EQ_I(p.argv[0], "help")) {
	RDIstrstream str;
	str << "The following commands work regardless of the current target:\n"
	    << "  exit              : terminate omniNotify daemon\n"
	    << "  go server         : change target to server\n"
	    << "  go chanfact       : change target to channel factory\n"
	    << "  go filtfact       : change target to filter factory\n"
	    << "  children          : list children of current target\n"
	    << "  flags             : show debug/report flag settings\n"
	    << "  +<flag-name>      : enable a debug/report flag\n"
	    << "  -<flag-name>      : disable a debug/report flag\n"
	    << "  +alldebug         : enable all debug flags\n"
	    << "  -alldebug         : disable all debug flags\n"
	    << "  +allreport        : enable all report flags\n"
	    << "  -allreport        : disable all report flags\n";
	fprintf(stdout, "%s", str.buf());
      }
    }
  }
  if (target_nm) {
    delete target_nm;
  }
  return;
 server_problem:
  if (target_nm) {
    delete target_nm;
  }
  fprintf(stdout, "\nomniNotify: server target unavailable; exiting interactive mode\n");
  WRAPPED_ORB_OA::run();
}
