예제 #1
0
파일: daemon.py 프로젝트: Aniverse/deluge-2
class Daemon(object):
    """The Deluge Daemon class"""
    def __init__(self,
                 listen_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.
            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,
                         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', interface,
                  port, listen_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(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)
예제 #2
0
파일: daemon.py 프로젝트: NoGare/deluge1
class Daemon(object):
    def __init__(self, options=None, args=None, classic=False):
        # Check for another running instance of the daemon
        if os.path.isfile(deluge.configmanager.get_config_dir("deluged.pid")):
            # Get the PID and the port of the supposedly running daemon
            try:
                (pid, port) = open(
                    deluge.configmanager.get_config_dir("deluged.pid")
                ).read().strip().split(";")
                pid = int(pid)
                port = int(port)
            except ValueError:
                pid = None
                port = None


            def process_running(pid):
                if deluge.common.windows_check():
                    # Do some fancy WMI junk to see if the PID exists in Windows
                    from win32com.client import GetObject
                    def get_proclist():
                        WMI = GetObject('winmgmts:')
                        processes = WMI.InstancesOf('Win32_Process')
                        return [process.Properties_('ProcessID').Value for process in processes]
                    return pid in get_proclist()
                else:
                    # We can just use os.kill on UNIX to test if the process is running
                    try:
                        os.kill(pid, 0)
                    except OSError:
                        return False
                    else:
                        return True

            if pid is not None and process_running(pid):
                # Ok, so a process is running with this PID, let's make doubly-sure
                # it's a deluged process by trying to open a socket to it's port.
                import socket
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect(("127.0.0.1", port))
                except socket.error:
                    # Can't connect, so it must not be a deluged process..
                    pass
                else:
                    # This is a deluged!
                    s.close()
                    raise deluge.error.DaemonRunningError(
                        "There is a deluge daemon 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 deluge.common.windows_check():
            from win32api import SetConsoleCtrlHandler
            from win32con import CTRL_CLOSE_EVENT
            from win32con import CTRL_SHUTDOWN_EVENT
            def win_handler(ctrl_type):
                log.debug("ctrl_type: %s", ctrl_type)
                if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT:
                    self._shutdown()
                    return 1
            SetConsoleCtrlHandler(win_handler)

        version = deluge.common.get_version()

        log.info("Deluge daemon %s", version)
        log.debug("options: %s", options)
        log.debug("args: %s", args)
        # Set the config directory
        if options and options.config:
            deluge.configmanager.set_config_dir(options.config)

        if options and options.listen_interface:
            listen_interface = options.listen_interface
        else:
            listen_interface = ""

        from deluge.core.core import Core
        # Start the core as a thread and join it until it's done
        self.core = Core(listen_interface=listen_interface)

        port = self.core.config["daemon_port"]
        if options and options.port:
            port = options.port
        if options and options.ui_interface:
            interface = options.ui_interface
        else:
            interface = ""

        self.rpcserver = RPCServer(
            port=port,
            allow_remote=self.core.config["allow_remote"],
            listen=not classic,
            interface=interface
        )

        # 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 classic:
            # Write out a pid file all the time, we use this to see if a deluged is running
            # We also include the running port number to do an additional test
            open(deluge.configmanager.get_config_dir("deluged.pid"), "wb").write(
                "%s;%s\n" % (os.getpid(), port))

            component.start()
            try:
                reactor.run()
            finally:
                self._shutdown()

    @export()
    def shutdown(self, *args, **kwargs):
        reactor.callLater(0, reactor.stop)

    def _shutdown(self, *args, **kwargs):
        if os.path.exists(deluge.configmanager.get_config_dir("deluged.pid")):
            try:
                os.remove(deluge.configmanager.get_config_dir("deluged.pid"))
            except Exception, e:
                log.exception(e)
                log.error("Error removing deluged.pid!")

        log.info("Waiting for components to shutdown..")
        d = component.shutdown()
        return d
예제 #3
0
파일: daemon.py 프로젝트: orther/Deluge
class Daemon(object):
    def __init__(self, options=None, args=None, classic=False):
        # Check for another running instance of the daemon
        if os.path.isfile(deluge.configmanager.get_config_dir("deluged.pid")):
            # Get the PID and the port of the supposedly running daemon
            try:
                (pid, port) = open(
                    deluge.configmanager.get_config_dir(
                        "deluged.pid")).read().strip().split(";")
                pid = int(pid)
                port = int(port)
            except ValueError:
                pid = None
                port = None

            def process_running(pid):
                if deluge.common.windows_check():
                    # Do some fancy WMI junk to see if the PID exists in Windows
                    from win32com.client import GetObject

                    def get_proclist():
                        WMI = GetObject('winmgmts:')
                        processes = WMI.InstancesOf('Win32_Process')
                        return [
                            process.Properties_('ProcessID').Value
                            for process in processes
                        ]

                    return pid in get_proclist()
                else:
                    # We can just use os.kill on UNIX to test if the process is running
                    try:
                        os.kill(pid, 0)
                    except OSError:
                        return False
                    else:
                        return True

            if pid is not None and process_running(pid):
                # Ok, so a process is running with this PID, let's make doubly-sure
                # it's a deluged process by trying to open a socket to it's port.
                import socket
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect(("127.0.0.1", port))
                except socket.error:
                    # Can't connect, so it must not be a deluged process..
                    pass
                else:
                    # This is a deluged!
                    s.close()
                    raise deluge.error.DaemonRunningError(
                        "There is a deluge daemon running with this config "
                        "directory!")

        # Initialize gettext
        deluge.common.setup_translations()

        # Twisted catches signals to terminate, so just have it call the shutdown
        # method.
        reactor.addSystemEventTrigger("after", "shutdown", self.shutdown)

        # Catch some Windows specific signals
        if deluge.common.windows_check():
            from win32api import SetConsoleCtrlHandler
            from win32con import CTRL_CLOSE_EVENT
            from win32con import CTRL_SHUTDOWN_EVENT

            def win_handler(ctrl_type):
                log.debug("ctrl_type: %s", ctrl_type)
                if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT:
                    self.__shutdown()
                    return 1

            SetConsoleCtrlHandler(win_handler)

        version = deluge.common.get_version()

        log.info("Deluge daemon %s", version)
        log.debug("options: %s", options)
        log.debug("args: %s", args)
        # Set the config directory
        if options and options.config:
            deluge.configmanager.set_config_dir(options.config)

        from deluge.core.core import Core
        # Start the core as a thread and join it until it's done
        self.core = Core()

        port = self.core.config["daemon_port"]
        if options and options.port:
            port = options.port
        if options and options.ui_interface:
            interface = options.ui_interface
        else:
            interface = ""

        self.rpcserver = RPCServer(
            port=port,
            allow_remote=self.core.config["allow_remote"],
            listen=not classic,
            interface=interface)

        # 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 classic:
            # Write out a pid file all the time, we use this to see if a deluged is running
            # We also include the running port number to do an additional test
            open(deluge.configmanager.get_config_dir("deluged.pid"),
                 "wb").write("%s;%s\n" % (os.getpid(), port))

            component.start()
            try:
                reactor.run()
            finally:
                self._shutdown()

    @export()
    def shutdown(self, *args, **kwargs):
        reactor.callLater(0, reactor.stop)

    def _shutdown(self, *args, **kwargs):
        try:
            os.remove(deluge.configmanager.get_config_dir("deluged.pid"))
        except Exception, e:
            log.exception(e)
            log.error("Error removing deluged.pid!")

        component.shutdown()
        try:
            reactor.stop()
        except twisted.internet.error.ReactorNotRunning:
            log.debug("Tried to stop the reactor but it is not running..")
예제 #4
0
class RPCServerTestCase(BaseTestCase, TestCaseDebug):
    """
    This class tests that the exported RPC functions in core work throught the
    Deluge RPC protocol which requires all transmitted data to be serializable
    with rencode.
    """
    def set_up(self):
        self.set_unittest_maxdiff(None)
        self.rpcserver = RPCServer(listen=False)
        self.rpcserver.factory.protocol = DelugeRPCProtocolTester

        self.factory = self.rpcserver.factory
        self.session_id = '0'
        self.request_id = 11
        self.protocol = self.rpcserver.factory.protocol(self.session_id)
        self.protocol.factory = self.factory
        self.factory.session_protocols[self.session_id] = self.protocol
        self.factory.interested_events[self.session_id] = [
            'TorrentFolderRenamedEvent'
        ]
        self.protocol.sessionno = self.session_id
        self.factory.authorized_sessions[
            self.session_id] = self.protocol.AuthLevel(
                rpcserver.AUTH_LEVEL_DEFAULT, '')

        self.config = test_common.get_test_config()
        self.core = Core("test")
        # Must call enable to create the RSSFeedScheduler in core
        self.core.enable(config=self.config)
        self.rpcserver.register_object(self.core)
        return component.start()

    def tear_down(self):
        def on_shutdown(result):
            del self.rpcserver

        return component.shutdown().addCallback(on_shutdown)

    def test_core_get_completion_paths(self):
        tmp_paths_dir = TempDir(prefix='yarss2_unit_tests')
        test_dirs = []

        for i in range(3):
            dirpath = tmp_paths_dir.mkdirs("dir%d" % (i))
            test_dirs.append("%s/" % dirpath)

        method = "core.get_completion_paths"
        args = []

        completion_text = "%s/d" % tmp_paths_dir.path
        arg = {"completion_text": completion_text, "show_hidden_files": False}
        args.append(arg)

        self.protocol.dispatch(self.request_id, method, args, {})
        msg_bytes = self.protocol.transport.messages_written[0]

        self.protocol.transport.dataReceived(msg_bytes)
        msg_received = self.protocol.transport.messages_received[0]

        self.assertEqual(msg_received[0], rpcserver.RPC_RESPONSE,
                         str(msg_received))
        self.assertEqual(msg_received[1], self.request_id, str(msg_received))

        expected_result = dict(arg)
        expected_result.update({
            'paths': tuple(test_dirs),
        })
        self.assertEqual(expected_result, msg_received[2])

    def test_core_get_rssfeed_parsed(self):
        method = "core.get_rssfeed_parsed"
        args = []

        filename = "ezrss-rss-2.xml"
        file_url = yarss2.util.common.get_resource(filename,
                                                   path="tests/data/feeds")

        rssfeed_data = {
            'active': True,
            'key': '3',
            'last_update': '2019-10-22T23:28:21+00:00',
            'name': 'hd-torrents.org',
            'obey_ttl': False,
            'prefer_magnet': False,
            'site': 'hd-torrents.org',
            'update_interval': 5,
            'update_on_startup': False,
            'url': file_url,
            'user_agent': ''
        }
        args.append(rssfeed_data)

        # Makes a call to core.get_rssfeed_parsed
        self.protocol.dispatch(self.request_id, method, args, {})

        msg_bytes = self.protocol.transport.messages_written[0]

        self.protocol.transport.dataReceived(msg_bytes)
        msg_received = self.protocol.transport.messages_received[0]

        self.assertEqual(msg_received[0], rpcserver.RPC_RESPONSE,
                         str(msg_received))
        self.assertEqual(msg_received[1], self.request_id, str(msg_received))
        self.assertEqual(msg_received[2]['user_agent'], None)

        items = msg_received[2]['raw_result']['items']

        expected_item0 = {
            'title':
            'Lolly Tang 2009 09 26 WEB x264-TBS',
            'link':
            'https://eztv.io/ep/1369854/lolly-tang-2009-09-26-web-x264-tbs/',
            'description':
            None,
            'author':
            None,
            'categories': ('TV', ),
            'comments':
            None,
            'enclosures': ({
                'url':
                'https://zoink.ch/torrent/Lolly.Tang.2009.09.26.WEB.x264-TBS[eztv].mkv.torrent',
                'length': 288475596,
                'type': 'application/x-bittorrent'
            }, ),
            'guid':
            'https://eztv.io/ep/1369854/lolly-tang-2009-09-26-web-x264-tbs/',
            'source':
            None,
            'torrent': {
                'filename':
                'Lolly.Tang.2009.09.26.WEB.x264-TBS[eztv].mkv',
                'contentlength':
                '288475596',
                'infohash':
                '4CF874831F61F5DB9C3299E503E28A8103047BA0',
                'magneturi':
                'magnet:?xt=urn:btih:4CF874831F61F5DB9C3299E503E28A8103047BA0&dn=Lolly.Tang.2009.09.26.WEB.x264-TBS%5Beztv%5D.mkv&tr=udp%3A%2F%2Ftracker.publicbt.com%2Fannounce&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=http%3A%2F%2Ftracker.trackerfix.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969'  # noqa: E501
            },
            'torrent_item':
            None,
            'content_encoded':
            None,
            'published_date':
            '2019-09-27T08:12:48-04:00'
        }
        self.assertEqual(items[0], expected_item0)
예제 #5
0
class Daemon(object):
    """The Deluge Daemon class"""

    def __init__(self, listen_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.
            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,
                         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', interface, port, listen_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(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)