deluge/deluge/ui/gtkui/ipcinterface.py

214 lines
7.8 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-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.
#
import base64
import logging
import os
import sys
from glob import glob
from tempfile import mkstemp
from urllib import url2pathname
from urlparse import urlparse
import twisted.internet.error
from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory, Factory, Protocol
import deluge.component as component
from deluge.common import is_magnet, is_url, windows_check
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.ui.client import client
try:
import rencode
except ImportError:
import deluge.rencode as rencode
log = logging.getLogger(__name__)
class IPCProtocolServer(Protocol):
def __init__(self):
pass
def dataReceived(self, data): # NOQA
config = ConfigManager("gtkui.conf")
data = rencode.loads(data, decode_utf8=True)
if not data or config["focus_main_window_on_add"]:
component.get("MainWindow").present()
process_args(data)
class IPCProtocolClient(Protocol):
def __init__(self):
pass
def connectionMade(self): # NOQA
self.transport.write(rencode.dumps(self.factory.args))
self.transport.loseConnection()
def connectionLost(self, reason): # NOQA
reactor.stop()
self.factory.stop = True
class IPCClientFactory(ClientFactory):
protocol = IPCProtocolClient
def __init__(self):
self.stop = False
def clientConnectionFailed(self, connector, reason): # NOQA
log.warning("Connection to running instance failed.")
reactor.stop()
class IPCInterface(component.Component):
def __init__(self, args):
component.Component.__init__(self, "IPCInterface")
ipc_dir = get_config_dir("ipc")
if not os.path.exists(ipc_dir):
os.makedirs(ipc_dir)
socket = os.path.join(ipc_dir, "deluge-gtk")
if windows_check():
# If we're on windows we need to check the global mutex to see if deluge is
# already running.
import win32event
import win32api
import winerror
self.mutex = win32event.CreateMutex(None, False, "deluge")
if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS:
# Create listen socket
self.factory = Factory()
self.factory.protocol = IPCProtocolServer
import random
port = random.randrange(20000, 65535)
reactor.listenTCP(port, self.factory)
# Store the port number in the socket file
open(socket, "w").write(str(port))
# We need to process any args when starting this process
process_args(args)
else:
# Send to existing deluge process
port = int(open(socket, "r").readline())
self.factory = ClientFactory()
self.factory.args = args
self.factory.protocol = IPCProtocolClient
reactor.connectTCP("127.0.0.1", port, self.factory)
reactor.run()
sys.exit(0)
else:
# Find and remove any restart tempfiles
restart_tempfile = glob(os.path.join(ipc_dir, 'tmp*deluge'))
for f in restart_tempfile:
os.remove(f)
lockfile = socket + ".lock"
log.debug("Checking if lockfile exists: %s", lockfile)
if os.path.lexists(lockfile):
def delete_lockfile():
log.debug("Removing lockfile since it's stale.")
try:
os.remove(lockfile)
os.remove(socket)
except OSError as ex:
log.error("Failed to delete lockfile: %s", ex)
try:
os.kill(int(os.readlink(lockfile)), 0)
except OSError:
delete_lockfile()
else:
if restart_tempfile:
log.warning("Found running PID but it is not a Deluge process, removing lockfile...")
delete_lockfile()
try:
self.factory = Factory()
self.factory.protocol = IPCProtocolServer
reactor.listenUNIX(socket, self.factory, wantPID=True)
except twisted.internet.error.CannotListenError as ex:
log.info("Deluge is already running! Sending arguments to running instance...")
self.factory = IPCClientFactory()
self.factory.args = args
reactor.connectUNIX(socket, self.factory, checkPID=True)
reactor.run()
if self.factory.stop:
log.info("Success sending arguments to running Deluge.")
import gtk
gtk.gdk.notify_startup_complete()
sys.exit(0)
else:
if restart_tempfile:
log.error("Deluge restart failed: %s", ex)
sys.exit(1)
else:
log.warning('Restarting Deluge... (%s)', ex)
# Create a tempfile to keep track of restart
mkstemp('deluge', dir=ipc_dir)
os.execv(sys.argv[0], sys.argv)
else:
process_args(args)
def shutdown(self):
if windows_check():
import win32api
win32api.CloseHandle(self.mutex)
def process_args(args):
"""Process arguments sent to already running Deluge"""
# Make sure args is a list
args = list(args)
log.debug("Processing args from other process: %s", args)
if not client.connected():
# We're not connected so add these to the queue
log.debug("Not connected to host.. Adding to queue.")
component.get("QueuedTorrents").add_to_queue(args)
return
config = ConfigManager("gtkui.conf")
for arg in args:
if not arg.strip():
continue
log.debug("arg: %s", arg)
if is_url(arg):
log.debug("Attempting to add url (%s) from external source...", arg)
if config["interactive_add"]:
component.get("AddTorrentDialog").add_from_url(arg)
component.get("AddTorrentDialog").show(config["focus_add_dialog"])
else:
client.core.add_torrent_url(arg, None)
elif is_magnet(arg):
log.debug("Attempting to add magnet (%s) from external source...", arg)
if config["interactive_add"]:
component.get("AddTorrentDialog").add_from_magnets([arg])
component.get("AddTorrentDialog").show(config["focus_add_dialog"])
else:
client.core.add_torrent_magnet(arg, {})
else:
log.debug("Attempting to add file (%s) from external source...", arg)
if urlparse(arg).scheme == "file":
arg = url2pathname(urlparse(arg).path)
path = os.path.abspath(deluge.common.decode_string(arg))
if not os.path.exists(path):
log.error("No such file: %s", path)
continue
if config["interactive_add"]:
component.get("AddTorrentDialog").add_from_files([path])
component.get("AddTorrentDialog").show(config["focus_add_dialog"])
else:
client.core.add_torrent_file(os.path.split(path)[-1],
base64.encodestring(open(path, "rb").read()), None)