2019-01-09 22:59:03 +00:00
from . import HydrusConstants as HC
from . import HydrusController
from . import HydrusData
from . import HydrusExceptions
from . import HydrusGlobals as HG
from . import HydrusNetworking
from . import HydrusSessions
from . import HydrusThreading
2015-09-02 23:16:09 +00:00
import os
2019-01-09 22:59:03 +00:00
from . import ServerDB
from . import ServerServer
import requests
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
2019-01-09 22:59:03 +00:00
answer = 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
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( ' Exiting! ' )
2016-10-12 21:52:50 +00:00
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 :
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( ' 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 :
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( ' Could not figure out the existing server \' s ports, so could not shut it down! ' )
2015-09-16 18:11:00 +00:00
2019-01-09 22:59:03 +00:00
session = requests . Session ( )
session . verify = False
2015-09-16 18:11:00 +00:00
for port in ports :
try :
2019-01-09 22:59:03 +00:00
r = session . get ( ' https://127.0.0.1: ' + str ( port ) + ' / ' )
2015-09-16 18:11:00 +00:00
2019-01-09 22:59:03 +00:00
server_name = r . headers [ ' Server ' ]
2015-09-16 18:11:00 +00:00
except :
text = ' Could not contact existing server \' s port ' + str ( port ) + ' ! '
text + = os . linesep
text + = traceback . format_exc ( )
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( text )
2015-09-16 18:11:00 +00:00
if ' server administration ' in server_name :
port_found = True
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' Sending shut down instruction \u2026 ' )
2015-09-16 18:11:00 +00:00
2019-01-09 22:59:03 +00:00
r = session . post ( ' https://127.0.0.1: ' + str ( port ) + ' /shutdown ' )
2015-09-16 18:11:00 +00:00
2019-01-09 22:59:03 +00:00
if not r . ok :
2015-09-16 18:11:00 +00:00
text = ' When told to shut down, the existing server gave an error! '
text + = os . linesep
2019-01-09 22:59:03 +00:00
text + = r . text
2015-09-16 18:11:00 +00:00
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( text )
2015-09-16 18:11:00 +00:00
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 :
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( ' Attempted to shut the existing server down, but it took too long! ' )
2015-09-16 18:11:00 +00:00
break
if not port_found :
2019-02-06 22:41:35 +00:00
raise HydrusExceptions . ShutdownException ( ' The existing server did not have an administration service! ' )
2015-09-16 18:11:00 +00:00
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 ) :
2019-03-20 21:22:10 +00:00
def __init__ ( self , db_dir ) :
2015-09-16 18:11:00 +00:00
2019-03-20 21:22:10 +00:00
HydrusController . HydrusController . __init__ ( self , db_dir )
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
2019-02-13 22:26:43 +00:00
def _GetUPnPServices ( self ) :
return self . _services
2015-09-02 23:16:09 +00:00
def _InitDB ( self ) :
2019-03-20 21:22:10 +00:00
return ServerDB . DB ( self , self . db_dir , ' server ' )
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
2019-01-09 22:59:03 +00:00
if HydrusNetworking . LocalPortInUse ( port ) :
2013-10-02 22:06:06 +00:00
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
2019-01-09 22:59:03 +00:00
if service_type == HC . SERVER_ADMIN :
http_factory = ServerServer . HydrusServiceAdmin ( service )
2013-10-02 22:06:06 +00:00
2019-01-09 22:59:03 +00:00
elif service_type == HC . FILE_REPOSITORY :
2013-10-02 22:06:06 +00:00
2019-01-09 22:59:03 +00:00
http_factory = ServerServer . HydrusServiceRepositoryFile ( service )
2016-12-14 21:19:07 +00:00
2019-01-09 22:59:03 +00:00
elif service_type == HC . TAG_REPOSITORY :
2017-05-24 20:28:24 +00:00
2019-01-09 22:59:03 +00:00
http_factory = ServerServer . HydrusServiceRepositoryTag ( service )
2016-12-14 21:19:07 +00:00
2019-01-09 22:59:03 +00:00
else :
return
( ssl_cert_path , ssl_key_path ) = self . db . GetSSLPaths ( )
sslmethod = twisted . internet . ssl . SSL . TLSv1_2_METHOD
context_factory = twisted . internet . ssl . DefaultOpenSSLContextFactory ( ssl_key_path , ssl_cert_path , sslmethod )
self . _service_keys_to_connected_ports [ service_key ] = reactor . listenSSL ( port , http_factory , context_factory )
if not HydrusNetworking . LocalPortInUse ( port ) :
2017-01-04 22:48:23 +00:00
2019-01-09 22:59:03 +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 )
2019-02-13 22:26:43 +00:00
def DeleteOrphans ( self ) :
self . WriteSynchronous ( ' delete_orphans ' )
2015-09-02 23:16:09 +00:00
def Exit ( self ) :
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' Shutting down daemons and services \u2026 ' )
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
self . ShutdownView ( )
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' 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 )
2019-02-13 22:26:43 +00:00
self . _services = self . Read ( ' services ' )
[ self . _admin_service ] = [ service for service in self . _services if service . GetServiceType ( ) == HC . SERVER_ADMIN ]
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
2017-03-02 02:14:56 +00:00
port = self . _admin_service . GetPort ( )
2015-04-01 20:44:54 +00:00
2019-01-09 22:59:03 +00:00
if HydrusNetworking . LocalPortInUse ( port ) :
2016-10-12 21:52:50 +00:00
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
2019-02-13 22:26:43 +00:00
#
job = self . CallRepeating ( 5.0 , 600.0 , self . SyncRepositories )
self . _daemon_jobs [ ' sync_repositories ' ] = job
job = self . CallRepeating ( 0.0 , 30.0 , self . SaveDirtyObjects )
self . _daemon_jobs [ ' save_dirty_objects ' ] = job
job = self . CallRepeating ( 0.0 , 86400.0 , self . DeleteOrphans )
self . _daemon_jobs [ ' delete_orphans ' ] = job
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
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' Initialising db \u2026 ' )
2015-11-18 22:44:07 +00:00
2015-09-16 18:11:00 +00:00
self . InitModel ( )
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' 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 :
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' Received a keyboard interrupt \u2026 ' )
2018-01-03 22:37:30 +00:00
2013-02-19 00:11:43 +00:00
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' 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
2019-02-13 22:26:43 +00:00
self . CallToThread ( self . services_upnp_manager . SetServices , self . _services )
2017-03-02 02:14:56 +00:00
[ 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 ) :
2019-01-09 22:59:03 +00:00
HydrusData . Print ( ' 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 ) :
2019-02-13 22:26:43 +00:00
if HG . server_busy :
return
2017-03-02 02:14:56 +00:00
repositories = [ service for service in self . _services if service . GetServiceType ( ) in HC . REPOSITORIES ]
for service in repositories :
service . Sync ( )