hydrus/include/ServerController.py

479 lines
14 KiB
Python
Raw Normal View History

2013-02-19 00:11:43 +00:00
import HydrusConstants as HC
2015-04-01 20:44:54 +00:00
import HydrusController
2015-03-25 22:04:19 +00:00
import HydrusData
2015-09-02 23:16:09 +00:00
import HydrusExceptions
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2015-06-10 19:40:25 +00:00
import HydrusNetworking
2013-03-15 02:38:12 +00:00
import HydrusSessions
2014-05-21 21:37:35 +00:00
import HydrusThreading
2015-09-02 23:16:09 +00:00
import os
2015-03-04 22:44:32 +00:00
import ServerDaemons
2013-02-19 00:11:43 +00:00
import ServerDB
2015-12-02 22:32:18 +00:00
import ServerServer
2015-09-02 23:16:09 +00:00
import sys
import time
2013-02-19 00:11:43 +00:00
import traceback
2016-12-14 21:19:07 +00:00
import twisted.internet.ssl
2013-10-02 22:06:06 +00:00
from twisted.internet import reactor
2013-10-23 21:36:47 +00:00
from twisted.internet import defer
2013-02-19 00:11:43 +00:00
2016-10-12 21:52:50 +00:00
def ProcessStartingAction( db_dir, action ):
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
already_running = HydrusData.IsAlreadyRunning( db_dir, 'server' )
2015-04-01 20:44:54 +00:00
2016-10-12 21:52:50 +00:00
if action == 'start':
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
if already_running:
2013-02-19 00:11:43 +00:00
2016-10-12 21:52:50 +00:00
HydrusData.Print( 'The server is already running. Would you like to [s]top it, [r]estart it, or e[x]it?' )
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
answer = raw_input()
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
if len( answer ) > 0:
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
answer = answer[0]
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
if answer == 's':
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
return 'stop'
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
elif answer == 'r':
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
return 'restart'
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
raise HydrusExceptions.PermissionException( 'Exiting!' )
else:
return action
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
elif action == 'stop':
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
if already_running:
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
return action
2015-09-02 23:16:09 +00:00
else:
2016-10-12 21:52:50 +00:00
raise HydrusExceptions.PermissionException( 'The server is not running, so it cannot be stopped!' )
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
elif action == 'restart':
if already_running:
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
return action
else:
return 'start'
2015-09-02 23:16:09 +00:00
2016-10-12 21:52:50 +00:00
def ShutdownSiblingInstance( db_dir ):
2015-09-16 18:11:00 +00:00
port_found = False
2016-10-12 21:52:50 +00:00
ports = HydrusData.GetSiblingProcessPorts( db_dir, 'server' )
2015-09-16 18:11:00 +00:00
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:
2017-01-25 22:56:55 +00:00
connection = HydrusNetworking.GetLocalConnection( port, https = True )
2015-09-16 18:11:00 +00:00
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
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Sending shut down instruction\u2026' )
2015-09-16 18:11:00 +00:00
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
2016-10-12 21:52:50 +00:00
while HydrusData.IsAlreadyRunning( db_dir, 'server' ):
2015-09-16 18:11:00 +00:00
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!' )
2015-11-18 22:44:07 +00:00
HydrusData.Print( 'The existing server is shut down!' )
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
class Controller( HydrusController.HydrusController ):
2016-10-19 20:02:56 +00:00
def __init__( self, db_dir, no_daemons, no_wal ):
2015-09-16 18:11:00 +00:00
2016-10-19 20:02:56 +00:00
HydrusController.HydrusController.__init__( self, db_dir, no_daemons, no_wal )
2015-09-16 18:11:00 +00:00
2017-03-08 23:23:12 +00:00
self._name = 'server'
2018-01-24 23:09:42 +00:00
self._shutdown = False
2017-05-10 21:33:58 +00:00
HG.server_controller = self
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
def _InitDB( self ):
2017-07-12 20:03:45 +00:00
return ServerDB.DB( self, self.db_dir, 'server', no_wal = self._no_wal )
2013-02-19 00:11:43 +00:00
2017-03-02 02:14:56 +00:00
def StartService( self, service ):
2013-07-10 20:25:57 +00:00
2014-09-17 21:28:26 +00:00
def TWISTEDDoIt():
2013-10-02 22:06:06 +00:00
2017-03-02 02:14:56 +00:00
service_key = service.GetServiceKey()
service_type = service.GetServiceType()
def Start( *args, **kwargs ):
2013-10-02 22:06:06 +00:00
try:
2017-03-02 02:14:56 +00:00
port = service.GetPort()
2013-10-02 22:06:06 +00:00
try:
2015-06-10 19:40:25 +00:00
connection = HydrusNetworking.GetLocalConnection( port )
2013-10-02 22:06:06 +00:00
connection.close()
2015-11-04 22:30:28 +00:00
raise Exception( 'Something was already bound to port ' + str( port ) )
2013-10-02 22:06:06 +00:00
except:
2017-03-02 02:14:56 +00:00
if service_type == HC.SERVER_ADMIN:
http_factory = ServerServer.HydrusServiceAdmin( service )
elif service_type == HC.FILE_REPOSITORY:
http_factory = ServerServer.HydrusServiceRepositoryFile( service )
elif service_type == HC.TAG_REPOSITORY:
http_factory = ServerServer.HydrusServiceRepositoryTag( service )
else:
return
2013-10-02 22:06:06 +00:00
2017-07-12 20:03:45 +00:00
( ssl_cert_path, ssl_key_path ) = self.db.GetSSLPaths()
2016-12-14 21:19:07 +00:00
2017-05-24 20:28:24 +00:00
sslmethod = twisted.internet.ssl.SSL.TLSv1_2_METHOD
context_factory = twisted.internet.ssl.DefaultOpenSSLContextFactory( ssl_key_path, ssl_cert_path, sslmethod )
2016-12-14 21:19:07 +00:00
2017-03-02 02:14:56 +00:00
self._service_keys_to_connected_ports[ service_key ] = reactor.listenSSL( port, http_factory, context_factory )
2017-01-04 22:48:23 +00:00
2013-10-02 22:06:06 +00:00
try:
2015-06-10 19:40:25 +00:00
connection = HydrusNetworking.GetLocalConnection( port )
2013-10-02 22:06:06 +00:00
connection.close()
except:
2015-11-04 22:30:28 +00:00
raise Exception( 'Tried to bind port ' + str( port ) + ' but it failed.' )
2013-10-02 22:06:06 +00:00
except Exception as e:
2015-11-18 22:44:07 +00:00
HydrusData.Print( traceback.format_exc() )
2013-10-02 22:06:06 +00:00
2013-10-09 18:13:42 +00:00
2018-01-17 22:52:10 +00:00
2017-03-02 02:14:56 +00:00
if service_key in self._service_keys_to_connected_ports:
2013-10-02 22:06:06 +00:00
2017-03-02 02:14:56 +00:00
deferred = defer.maybeDeferred( self._service_keys_to_connected_ports[ service_key ].stopListening )
deferred.addCallback( Start )
2013-10-02 22:06:06 +00:00
2017-03-02 02:14:56 +00:00
else:
2013-10-02 22:06:06 +00:00
2017-03-02 02:14:56 +00:00
Start()
reactor.callFromThread( TWISTEDDoIt )
def StopService( self, service_key ):
def TWISTEDDoIt():
deferred = defer.maybeDeferred( self._service_keys_to_connected_ports[ service_key ].stopListening )
del self._service_keys_to_connected_ports[ service_key ]
2013-10-02 22:06:06 +00:00
2014-09-17 21:28:26 +00:00
reactor.callFromThread( TWISTEDDoIt )
2015-09-02 23:16:09 +00:00
def Exit( self ):
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Shutting down daemons and services\u2026' )
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
self.ShutdownView()
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Shutting down db\u2026' )
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
self.ShutdownModel()
2015-04-01 20:44:54 +00:00
2017-07-12 20:03:45 +00:00
HydrusData.CleanRunningFile( self.db_dir, 'server' )
2016-10-12 21:52:50 +00:00
2017-03-02 02:14:56 +00:00
2016-10-12 21:52:50 +00:00
def GetFilesDir( self ):
2017-07-12 20:03:45 +00:00
return self.db.GetFilesDir()
2016-06-15 18:59:44 +00:00
2014-09-17 21:28:26 +00:00
2017-05-31 21:50:53 +00:00
def GetServices( self ):
return list( self._services )
2015-09-02 23:16:09 +00:00
def InitModel( self ):
HydrusController.HydrusController.InitModel( self )
2017-12-06 22:06:56 +00:00
self.server_session_manager = HydrusSessions.HydrusSessionManagerServer()
2015-09-02 23:16:09 +00:00
2017-03-02 02:14:56 +00:00
self._service_keys_to_connected_ports = {}
2015-09-02 23:16:09 +00:00
2014-09-17 21:28:26 +00:00
2015-09-02 23:16:09 +00:00
def InitView( self ):
2015-08-26 21:18:39 +00:00
2015-09-02 23:16:09 +00:00
HydrusController.HydrusController.InitView( self )
2015-08-26 21:18:39 +00:00
2016-01-20 23:57:33 +00:00
if not self._no_daemons:
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'DeleteOrphans', ServerDaemons.DAEMONDeleteOrphans, period = 86400 ) )
2017-03-02 02:14:56 +00:00
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'GenerateUpdates', ServerDaemons.DAEMONGenerateUpdates, period = 600, init_wait = 10 ) )
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'SaveDirtyObjects', ServerDaemons.DAEMONSaveDirtyObjects, period = 30 ) )
2016-01-20 23:57:33 +00:00
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'UPnP', ServerDaemons.DAEMONUPnP, ( 'notify_new_options', ), period = 43200 ) )
2015-08-26 21:18:39 +00:00
2016-10-12 21:52:50 +00:00
#
2017-03-02 02:14:56 +00:00
self._services = self.Read( 'services' )
2016-10-12 21:52:50 +00:00
2017-03-02 02:14:56 +00:00
[ self._admin_service ] = [ service for service in self._services if service.GetServiceType() == HC.SERVER_ADMIN ]
port = self._admin_service.GetPort()
2015-04-01 20:44:54 +00:00
2016-10-12 21:52:50 +00:00
already_bound = False
2015-04-01 20:44:54 +00:00
2016-10-12 21:52:50 +00:00
try:
connection = HydrusNetworking.GetLocalConnection( port )
connection.close()
already_bound = True
2017-01-25 22:56:55 +00:00
except:
pass
2016-10-12 21:52:50 +00:00
if already_bound:
HydrusData.Print( 'Something is already bound to port ' + str( port ) + ', so your administration service cannot be started. Please quit the server and retry once the port is clear.' )
else:
2017-03-02 02:14:56 +00:00
for service in self._services:
self.StartService( service )
2016-10-12 21:52:50 +00:00
2015-09-02 23:16:09 +00:00
2017-06-14 21:19:11 +00:00
def JustWokeFromSleep( self ):
return False
2015-04-01 20:44:54 +00:00
2016-05-11 18:16:39 +00:00
def MaintainDB( self, stop_time = None ):
2016-01-06 21:17:20 +00:00
stop_time = HydrusData.GetNow() + 10
2016-04-20 20:42:21 +00:00
self.WriteSynchronous( 'analyze', stop_time )
2016-01-06 21:17:20 +00:00
2017-06-21 21:15:59 +00:00
def ReportDataUsed( self, num_bytes ):
2017-03-02 02:14:56 +00:00
2017-06-21 21:15:59 +00:00
self._admin_service.ServerReportDataUsed( num_bytes )
def ReportRequestUsed( self ):
self._admin_service.ServerReportRequestUsed()
2017-03-02 02:14:56 +00:00
2015-11-18 22:44:07 +00:00
def Run( self ):
2014-09-17 21:28:26 +00:00
2017-07-12 20:03:45 +00:00
HydrusData.RecordRunningStart( self.db_dir, 'server' )
2015-09-02 23:16:09 +00:00
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Initialising db\u2026' )
2015-11-18 22:44:07 +00:00
2015-09-16 18:11:00 +00:00
self.InitModel()
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Initialising daemons and services\u2026' )
2015-11-18 22:44:07 +00:00
2015-09-16 18:11:00 +00:00
self.InitView()
2015-11-18 22:44:07 +00:00
HydrusData.Print( 'Server is running. Press Ctrl+C to quit.' )
2015-09-16 18:11:00 +00:00
2018-01-03 22:37:30 +00:00
try:
2018-01-24 23:09:42 +00:00
while not self._model_shutdown and not self._shutdown:
2014-09-17 21:28:26 +00:00
2015-09-16 18:11:00 +00:00
time.sleep( 1 )
2018-01-03 22:37:30 +00:00
except KeyboardInterrupt:
2018-01-24 23:09:42 +00:00
HydrusData.Print( u'Received a keyboard interrupt\u2026' )
2018-01-03 22:37:30 +00:00
2013-02-19 00:11:43 +00:00
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Shutting down controller\u2026' )
2015-09-16 18:11:00 +00:00
2018-01-24 23:09:42 +00:00
self.Exit()
2015-09-16 18:11:00 +00:00
2017-03-02 02:14:56 +00:00
def SaveDirtyObjects( self ):
2017-05-10 21:33:58 +00:00
with HG.dirty_object_lock:
2017-03-02 02:14:56 +00:00
dirty_services = [ service for service in self._services if service.IsDirty() ]
if len( dirty_services ) > 0:
self.WriteSynchronous( 'dirty_services', dirty_services )
2017-12-06 22:06:56 +00:00
dirty_accounts = self.server_session_manager.GetDirtyAccounts()
2017-03-02 02:14:56 +00:00
if len( dirty_accounts ) > 0:
self.WriteSynchronous( 'dirty_accounts', dirty_accounts )
2017-06-07 22:05:15 +00:00
def ServerBandwidthOK( self ):
2017-03-02 02:14:56 +00:00
2017-06-07 22:05:15 +00:00
return self._admin_service.ServerBandwidthOK()
2015-09-16 18:11:00 +00:00
2017-03-02 02:14:56 +00:00
def SetServices( self, services ):
2015-09-16 18:11:00 +00:00
2017-06-14 21:19:11 +00:00
# doesn't need the dirty_object_lock because the caller takes it
2017-03-02 02:14:56 +00:00
self._services = services
[ self._admin_service ] = [ service for service in self._services if service.GetServiceType() == HC.SERVER_ADMIN ]
current_service_keys = set( self._service_keys_to_connected_ports.keys() )
future_service_keys = set( [ service.GetServiceKey() for service in self._services ] )
stop_service_keys = current_service_keys.difference( future_service_keys )
for service_key in stop_service_keys:
self.StopService( service_key )
for service in self._services:
self.StartService( service )
def ShutdownView( self ):
for service in self._services:
service_key = service.GetServiceKey()
if service_key in self._service_keys_to_connected_ports:
self.StopService( service_key )
2015-09-16 18:11:00 +00:00
HydrusController.HydrusController.ShutdownView( self )
def ShutdownFromServer( self ):
2017-03-08 23:23:12 +00:00
HydrusData.Print( u'Received a server shut down request\u2026' )
2015-09-16 18:11:00 +00:00
2018-01-24 23:09:42 +00:00
self._shutdown = True
2015-09-16 18:11:00 +00:00
2016-12-14 21:19:07 +00:00
2017-03-02 02:14:56 +00:00
def SyncRepositories( self ):
repositories = [ service for service in self._services if service.GetServiceType() in HC.REPOSITORIES ]
for service in repositories:
service.Sync()