hydrus/include/ServerController.py

409 lines
12 KiB
Python
Executable File

import httplib
import HydrusConstants as HC
import HydrusController
import HydrusData
import HydrusExceptions
import HydrusGlobals
import HydrusNetworking
import HydrusServer
import HydrusSessions
import HydrusThreading
import os
import ServerDaemons
import ServerDB
import sys
import time
import traceback
from twisted.internet import reactor
from twisted.internet import defer
def GetStartingAction():
action = 'help'
args = sys.argv[1:]
if len( args ) > 0:
command = args[0]
while command.startswith( '-' ):
command = command[ 1: ]
if command == 'help':
action = 'help'
else:
already_running = HydrusData.IsAlreadyRunning( 'server' )
if command == 'start':
if already_running:
raise HydrusExceptions.PermissionException( 'The server is already running!' )
else:
action = 'start'
elif command == 'stop':
if already_running:
action = 'stop'
else:
raise HydrusExceptions.PermissionException( 'The server is not running, so it cannot be stopped!' )
elif command == 'restart':
if already_running:
action = 'restart'
else:
action = 'start'
else:
already_running = HydrusData.IsAlreadyRunning( 'server' )
if not already_running:
action = 'start'
else:
HydrusData.Print( 'The server is already running. Would you like to [s]top it, or [r]estart it?' )
answer = raw_input()
if len( answer ) > 0:
answer = answer[0]
if answer == 's':
action = 'stop'
elif answer == 'r':
action = 'restart'
return action
def ShutdownSiblingInstance():
port_found = False
ports = HydrusData.GetSiblingProcessPorts( 'server' )
if ports is None:
raise HydrusExceptions.PermissionException( 'Could not figure out the existing server\'s ports, so could not shut it down!' )
for port in ports:
try:
connection = HydrusNetworking.GetLocalConnection( port )
connection.request( 'GET', '/' )
response = connection.getresponse()
response.read()
server_name = response.getheader( 'Server' )
except:
text = 'Could not contact existing server\'s port ' + str( port ) + '!'
text += os.linesep
text += traceback.format_exc()
raise HydrusExceptions.PermissionException( text )
if 'server administration' in server_name:
port_found = True
HydrusData.Print( 'Sending shut down instruction...' )
connection.request( 'POST', '/shutdown' )
response = connection.getresponse()
result = response.read()
if response.status != 200:
text = 'When told to shut down, the existing server gave an error!'
text += os.linesep
text += result
raise HydrusExceptions.PermissionException( text )
time_waited = 0
while HydrusData.IsAlreadyRunning( 'server' ):
time.sleep( 1 )
time_waited += 1
if time_waited > 20:
raise HydrusExceptions.PermissionException( 'Attempted to shut the existing server down, but it took too long!' )
break
if not port_found:
raise HydrusExceptions.PermissionException( 'The existing server did not have an administration service!' )
HydrusData.Print( 'The existing server is shut down!' )
class Controller( HydrusController.HydrusController ):
def __init__( self ):
HydrusController.HydrusController.__init__( self )
HydrusGlobals.server_controller = self
def _InitDB( self ):
return ServerDB.DB( self )
def ActionService( self, service_key, action ):
if action != 'stop': ( service_type, options ) = self.Read( 'service_info', service_key )
def TWISTEDDoIt():
def StartService( *args, **kwargs ):
try:
if 'port' not in options: return
port = options[ 'port' ]
try:
connection = HydrusNetworking.GetLocalConnection( port )
connection.close()
raise Exception( 'Something was already bound to port ' + str( port ) )
except:
message = options[ 'message' ]
if service_type == HC.SERVER_ADMIN: service_object = HydrusServer.HydrusServiceAdmin( service_key, service_type, message )
elif service_type == HC.FILE_REPOSITORY: service_object = HydrusServer.HydrusServiceRepositoryFile( service_key, service_type, message )
elif service_type == HC.TAG_REPOSITORY: service_object = HydrusServer.HydrusServiceRepositoryTag( service_key, service_type, message )
elif service_type == HC.MESSAGE_DEPOT: return
self._services[ service_key ] = reactor.listenTCP( port, service_object )
try:
connection = HydrusNetworking.GetLocalConnection( port )
connection.close()
except:
raise Exception( 'Tried to bind port ' + str( port ) + ' but it failed.' )
except Exception as e:
HydrusData.Print( traceback.format_exc() )
if action == 'start': StartService()
else:
if service_key in self._services:
deferred = defer.maybeDeferred( self._services[ service_key ].stopListening )
if action == 'stop': del self._services[ service_key ]
if action == 'restart': deferred.addCallback( StartService )
reactor.callFromThread( TWISTEDDoIt )
def CheckIfAdminPortInUse( self ):
( service_type, options ) = self.Read( 'service_info', HC.SERVER_ADMIN_KEY )
port = options[ 'port' ]
already_bound = False
try:
connection = HydrusNetworking.GetLocalConnection( port )
connection.close()
already_bound = True
except: pass
if already_bound:
raise HydrusExceptions.PermissionException( 'Something was already bound to port ' + str( port ) )
def Exit( self ):
HydrusData.Print( 'Shutting down daemons and services...' )
self.ShutdownView()
HydrusData.Print( 'Shutting down db...' )
self.ShutdownModel()
def InitModel( self ):
HydrusController.HydrusController.InitModel( self )
self._managers[ 'restricted_services_sessions' ] = HydrusSessions.HydrusSessionManagerServer()
self._managers[ 'messaging_sessions' ] = HydrusSessions.HydrusMessagingSessionManagerServer()
self._services = {}
self.sub( self, 'ActionService', 'action_service' )
def InitView( self ):
HydrusController.HydrusController.InitView( self )
self._daemons.append( HydrusThreading.DAEMONQueue( self, 'FlushRequestsMade', ServerDaemons.DAEMONFlushRequestsMade, 'request_made', period = 60 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'CheckMonthlyData', ServerDaemons.DAEMONCheckMonthlyData, period = 3600 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'ClearBans', ServerDaemons.DAEMONClearBans, period = 3600 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'DeleteOrphans', ServerDaemons.DAEMONDeleteOrphans, period = 86400 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'GenerateUpdates', ServerDaemons.DAEMONGenerateUpdates, period = 600 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'CheckDataUsage', ServerDaemons.DAEMONCheckDataUsage, period = 86400 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'UPnP', ServerDaemons.DAEMONUPnP, ( 'notify_new_options', ), period = 43200 ) )
self.CheckIfAdminPortInUse()
service_keys = self.Read( 'service_keys' )
for service_key in service_keys: self.ActionService( service_key, 'start' )
def JustWokeFromSleep( self ): return False
def NotifyPubSubs( self ):
self.CallToThread( self.ProcessPubSub )
def Run( self ):
HydrusData.RecordRunningStart( 'server' )
HydrusData.Print( 'Initialising db...' )
self.InitModel()
HydrusData.Print( 'Initialising daemons and services...' )
self.InitView()
HydrusData.Print( 'Server is running. Press Ctrl+C to quit.' )
interrupt_received = False
while not self._model_shutdown:
try:
time.sleep( 1 )
except KeyboardInterrupt:
if not interrupt_received:
interrupt_received = True
HydrusData.Print( 'Received a keyboard interrupt...' )
def do_it():
self.Exit()
self.CallToThread( do_it )
HydrusData.Print( 'Shutting down controller...' )
def ShutdownView( self ):
service_keys = self.Read( 'service_keys' )
for service_key in service_keys: self.ActionService( service_key, 'stop' )
HydrusController.HydrusController.ShutdownView( self )
def ShutdownFromServer( self ):
HydrusData.Print( 'Received a server shut down request...' )
def do_it():
time.sleep( 1 )
self.Exit()
self.CallToThread( do_it )