From d1772d410235592b482e3b08b1863f6624d9fe6b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 19 Feb 2023 15:52:21 +0100 Subject: Adding upstream version 2.0.3. Signed-off-by: Daniel Baumann --- deluge/core/daemon.py | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 deluge/core/daemon.py (limited to 'deluge/core/daemon.py') diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py new file mode 100644 index 0000000..d7ab813 --- /dev/null +++ b/deluge/core/daemon.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007-2009 Andrew Resch +# +# 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""" +from __future__ import unicode_literals + +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 (EnvironmentError, 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 socket.error: + # Can't connect, so pid is not a deluged process. + return False + else: + # This is a deluged process! + _socket.close() + return True + + +class Daemon(object): + """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('%s;%s\n' % (pid, self.port)) + + 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 + ) -- cgit v1.2.3