summaryrefslogtreecommitdiffstats
path: root/deluge/core/daemon.py
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/core/daemon.py')
-rw-r--r--deluge/core/daemon.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py
new file mode 100644
index 0000000..0185dd8
--- /dev/null
+++ b/deluge/core/daemon.py
@@ -0,0 +1,203 @@
+#
+# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+"""The Deluge daemon"""
+import logging
+import os
+import socket
+
+from twisted.internet import reactor
+
+import deluge.component as component
+from deluge.common import get_version, is_ip, is_process_running, windows_check
+from deluge.configmanager import get_config_dir
+from deluge.core.core import Core
+from deluge.core.rpcserver import RPCServer, export
+from deluge.error import DaemonRunningError
+
+if windows_check():
+ from win32api import SetConsoleCtrlHandler
+ from win32con import CTRL_CLOSE_EVENT, CTRL_SHUTDOWN_EVENT
+
+log = logging.getLogger(__name__)
+
+
+def is_daemon_running(pid_file):
+ """
+ Check for another running instance of the daemon using the same pid file.
+
+ Args:
+ pid_file: The location of the file with pid, port values.
+
+ Returns:
+ bool: True is daemon is running, False otherwise.
+
+ """
+
+ try:
+ with open(pid_file) as _file:
+ pid, port = (int(x) for x in _file.readline().strip().split(';'))
+ except (OSError, ValueError):
+ return False
+
+ if is_process_running(pid):
+ # Ensure it's a deluged process by trying to open a socket to it's port.
+ _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ _socket.connect(('127.0.0.1', port))
+ except OSError:
+ # Can't connect, so pid is not a deluged process.
+ return False
+ else:
+ # This is a deluged process!
+ _socket.close()
+ return True
+
+
+class Daemon:
+ """The Deluge Daemon class"""
+
+ def __init__(
+ self,
+ listen_interface=None,
+ outgoing_interface=None,
+ interface=None,
+ port=None,
+ standalone=False,
+ read_only_config_keys=None,
+ ):
+ """
+ Args:
+ listen_interface (str, optional): The IP address to listen to
+ BitTorrent connections on.
+ outgoing_interface (str, optional): The network interface name or
+ IP address to open outgoing BitTorrent connections on.
+ interface (str, optional): The IP address the daemon will
+ listen for UI connections on.
+ port (int, optional): The port the daemon will listen for UI
+ connections on.
+ standalone (bool, optional): If True the client is in Standalone
+ mode otherwise, if False, start the daemon as separate process.
+ read_only_config_keys (list of str, optional): A list of config
+ keys that will not be altered by core.set_config() RPC method.
+ """
+ self.standalone = standalone
+ self.pid_file = get_config_dir('deluged.pid')
+ log.info('Deluge daemon %s', get_version())
+ if is_daemon_running(self.pid_file):
+ raise DaemonRunningError(
+ 'Deluge daemon already running with this config directory!'
+ )
+
+ # Twisted catches signals to terminate, so just have it call the shutdown method.
+ reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown)
+
+ # Catch some Windows specific signals
+ if windows_check():
+
+ def win_handler(ctrl_type):
+ """Handle the Windows shutdown or close events."""
+ log.debug('windows handler ctrl_type: %s', ctrl_type)
+ if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT:
+ self._shutdown()
+ return 1
+
+ SetConsoleCtrlHandler(win_handler)
+
+ # Start the core as a thread and join it until it's done
+ self.core = Core(
+ listen_interface=listen_interface,
+ outgoing_interface=outgoing_interface,
+ read_only_config_keys=read_only_config_keys,
+ )
+
+ if port is None:
+ port = self.core.config['daemon_port']
+ self.port = port
+
+ if interface and not is_ip(interface):
+ log.error('Invalid UI interface (must be IP Address): %s', interface)
+ interface = None
+
+ self.rpcserver = RPCServer(
+ port=port,
+ allow_remote=self.core.config['allow_remote'],
+ listen=not standalone,
+ interface=interface,
+ )
+
+ log.debug(
+ 'Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s',
+ interface,
+ port,
+ listen_interface,
+ outgoing_interface,
+ )
+
+ def start(self):
+ # Register the daemon and the core RPCs
+ self.rpcserver.register_object(self.core)
+ self.rpcserver.register_object(self)
+
+ # Make sure we start the PreferencesManager first
+ component.start('PreferencesManager')
+
+ if not self.standalone:
+ log.info('Deluge daemon starting...')
+ # Create pid file to track if deluged is running, also includes the port number.
+ pid = os.getpid()
+ log.debug('Storing pid %s & port %s in: %s', pid, self.port, self.pid_file)
+ with open(self.pid_file, 'w') as _file:
+ _file.write(f'{pid};{self.port}\n')
+
+ component.start()
+
+ try:
+ reactor.run()
+ finally:
+ log.debug('Remove pid file: %s', self.pid_file)
+ os.remove(self.pid_file)
+ log.info('Deluge daemon shutdown successfully')
+
+ @export()
+ def shutdown(self, *args, **kwargs):
+ log.debug('Deluge daemon shutdown requested...')
+ reactor.callLater(0, reactor.stop)
+
+ def _shutdown(self, *args, **kwargs):
+ log.info('Deluge daemon shutting down, waiting for components to shutdown...')
+ if not self.standalone:
+ return component.shutdown()
+
+ @export()
+ def get_method_list(self):
+ """Returns a list of the exported methods."""
+ return self.rpcserver.get_method_list()
+
+ @export()
+ def get_version(self):
+ """Returns the daemon version"""
+ return get_version()
+
+ @export(1)
+ def authorized_call(self, rpc):
+ """Determines if session auth_level is authorized to call RPC.
+
+ Args:
+ rpc (str): A RPC, e.g. core.get_torrents_status
+
+ Returns:
+ bool: True if authorized to call RPC, otherwise False.
+ """
+ if rpc not in self.get_method_list():
+ return False
+
+ return (
+ self.rpcserver.get_session_auth_level()
+ >= self.rpcserver.get_rpc_auth_level(rpc)
+ )