mirror of
https://github.com/logos-storage/deluge.git
synced 2026-01-09 08:33:08 +00:00
The move to using auto-formatter makes it easier to read, submit and speeds up development time. https://github.com/ambv/black/ Although I would prefer 79 chars, the default line length of 88 chars used by black suffices. The flake8 line length remains at 120 chars since black does not touch comments or docstrings and this will require another round of fixes. The only black setting that is not standard is the use of double-quotes for strings so disabled any formatting of these. Note however that flake8 will still flag usage of double-quotes. I may change my mind on double vs single quotes but for now leave them. A new pyproject.toml file has been created for black configuration.
197 lines
6.5 KiB
Python
197 lines
6.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# 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"""
|
|
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 IP address to open outgoing BitTorrent connections on.
|
|
interface (str, optional): Adapter name 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(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
|
|
)
|