#############################################################################
#
#	$Id: NS_NtService.py,v 1.1 2003/03/13 22:00:27 irmen Exp $
#	An NT service that runs the Pyro Name Server
#   Author: Syver Enstad  syver-en@online.no
#
#	This is part of "Pyro" - Python Remote Objects
#	Which is (c) Irmen de Jong - irmen@users.sourceforge.net
#
#############################################################################

import win32serviceutil
import threading
import win32service
from BasicNTService import BasicNTService

import sys
import Pyro
import Pyro.naming
from Pyro.naming import NameServer, PersistentNameServer, BroadcastServer, bcRequestHandler, Log, util, core, constants
import sys

BcServerObject = None



def startServer(hstn='', nsport=0, bcport=0, keep=0, persistent=0, dbdir=None, Guards=(None,None)):
	global BcServerObject
	if not nsport:
		nsport=Pyro.config.PYRO_NS_PORT
	if not bcport:
		bcport=Pyro.config.PYRO_NS_BC_PORT
	core.initServer()
	PyroDaemon = core.Daemon(host=hstn, port=nsport,norange=1)
	if Guards[0]:
		PyroDaemon.setNewConnectionValidator(Guards[0])
	if persistent:
		ns=PersistentNameServer(dbdir)
		PyroDaemon.useNameServer(ns)
		NS_URI=PyroDaemon.connectPersistent(ns,constants.NAMESERVER_NAME)
	else:
		ns=NameServer()
		PyroDaemon.useNameServer(ns)
		NS_URI=PyroDaemon.connect(ns,constants.NAMESERVER_NAME)
	
	BcServerObject = BroadcastServer((hstn or '',bcport),bcRequestHandler)
	if Guards[1]:
		BcServerObject.setRequestValidator(Guards[1])
	BcServerObject.keepRunning(keep)
	if keep:
		print 'Will ignore shutdown requests.'
	else:
		print 'Will accept shutdown requests.'

	if Guards[0] or Guards[1]:
		print 'Using security plugins:'
		if Guards[0]:
			print '  NS new conn validator =',Guards[0].__class__.__name__,'from', Guards[0].__class__.__module__
		else: print '  default NS new conn validator'
		if Guards[1]:
			print '  BC request validator  =',Guards[1].__class__.__name__,'from', Guards[1].__class__.__module__
		else: print '  default BC request validator'

	ns.publishURI(NS_URI)
	BcServerObject.setNS_URI(NS_URI)
	Log.msg('NS daemon','This is the Pyro Name Server.')
	if persistent:
		Log.msg('NS daemon','Persistent mode, database is in',ns.getDBDir())
		print 'Persistent mode, database is in',ns.getDBDir()
	Log.msg('NS daemon','Starting on',PyroDaemon.hostname,'port',
	  PyroDaemon.port, ' broadcast server on port',bcport)

	print 'Name Server started.'

	# I use a timeout here otherwise you can't break gracefully on Windoze
	while not BcServerObject.shutdown:
		try:
			PyroDaemon.handleRequests(BcServerObject.preferredTimeOut,[BcServerObject],BcServerObject.bcCallback)
		except KeyboardInterrupt:
			Log.warn('NS daemon','shutdown on user break signal')
			print 'Shutting down on user break signal.'
			BcServerObject.shutdown=1
		except:
			import traceback
			(exc_type, exc_value, exc_trb) = sys.exc_info()
			out = ''.join(traceback.format_exception(exc_type, exc_value, exc_trb)[-5:])
			Log.error('NS daemon', 'Unexpected exception, type',exc_type,
				'\n--- partial traceback of this exception follows:\n',
				out,'\n--- end of traceback')
			print '*** Exception occured!!! Partial traceback:'
			print out
			print '*** Resuming operations...'

	Log.msg('NS daemon','Shut down gracefully.')
	print 'Name Server gracefully stopped.'



class PyroNSThread(threading.Thread):
	""" The Pyro Naming Service will run in this thread
	"""
	def __init__(self, args, stopcallback):
		threading.Thread.__init__(self)
		self._args = list(args)
		self._stopcallback = stopcallback
		
	def run(self):
		self.startPyroNS()
		self._stopcallback()

	def startPyroNS(self):
		""" ripped out of Pyro.naming and slightly changed to
		accomodate not using sys.argv, but self._args instead
		"""
		Args = util.ArgParser()
		Args.parse(self._args,'hkn:p:b:d:s:')
		try:
			Args.getOpt('h')
			print \
'''Usage: ns [-h] [-k] [-n hostname] [-p port] [-b port]
		[-d [databasefile]] [-s securitymodule]
	where -p = NS server port
		-b = NS broadcast port
		-n = non-default server hostname
		-k = keep, do not respond to shutdown requests
		-d = use persistent database, provide optional storage directory
		-s = use given python module with security code
		-h = print this help'''
			raise SystemExit
		except KeyError:
			pass
		host = Args.getOpt('n','')
		port = int(Args.getOpt('p',Pyro.config.PYRO_NS_PORT))
		bcport = int(Args.getOpt('b',Pyro.config.PYRO_NS_BC_PORT))
		try:
			dbdir = Args.getOpt('d')
			persistent = 1
		except KeyError:
			persistent = 0
			dbdir = None

		try:
			Args.getOpt('k')
			keep=1
		except KeyError:
			keep=0

		try:
			secmod = __import__(Args.getOpt('s'),locals(),globals())
			Guards = (secmod.NSGuard(), secmod.BCGuard())
		except ImportError,x:
			print 'Error loading security module:',x
			raise SystemExit
		except KeyError:
			secmod = None
			Guards = (None,None)

		Args.printIgnored()
		if Args.args:
			print 'Ignored arguments:',''.join(Args.args)
			
		print '*** Pyro Name Server ***'
		startServer(host,port,bcport,keep,
								persistent,dbdir,Guards)


	
class PyroNS_NTService(BasicNTService):
	_svc_name_ = 'PyroNS'
	_svc_display_name_ = "Pyro Naming Service"
	_svc_description_ = 'Provides name resolution services for Pyro objects'
	def __init__(self, args):
		super(PyroNS_NTService, self).__init__(args)
		nsargs = args
		self._nsThread = PyroNSThread(nsargs, self.SvcStop)
		
	def _doRun(self):
		""" Overriden """
		self._nsThread.start()

	def _doStop(self):
		""" Overridden """
		global BcServerObject
		BcServerObject.shutdown = 1
		self._nsThread.join()

	def SvcStop(self):
		"""Overriden """
		super(PyroNS_NTService, self).SvcStop()
		

if __name__ == '__main__':
	PyroNS_NTService.HandleCommandLine()
