Merge branch 'remote_exec' into plugins
This commit is contained in:
commit
afcc387002
3 changed files with 163 additions and 4 deletions
34
src/core.py
34
src/core.py
|
@ -49,6 +49,7 @@ from contact import Contact, Resource
|
||||||
from text_buffer import TextBuffer
|
from text_buffer import TextBuffer
|
||||||
from keyboard import read_char
|
from keyboard import read_char
|
||||||
from theming import get_theme
|
from theming import get_theme
|
||||||
|
from fifo import Fifo
|
||||||
|
|
||||||
# http://xmpp.org/extensions/xep-0045.html#errorstatus
|
# http://xmpp.org/extensions/xep-0045.html#errorstatus
|
||||||
ERROR_AND_STATUS_CODES = {
|
ERROR_AND_STATUS_CODES = {
|
||||||
|
@ -94,6 +95,7 @@ class Core(object):
|
||||||
sys.excepthook = self.on_exception
|
sys.excepthook = self.on_exception
|
||||||
self.running = True
|
self.running = True
|
||||||
self.xmpp = singleton.Singleton(connection.Connection)
|
self.xmpp = singleton.Singleton(connection.Connection)
|
||||||
|
self.remote_fifo = None
|
||||||
# a unique buffer used to store global informations
|
# a unique buffer used to store global informations
|
||||||
# that are displayed in almost all tabs, in an
|
# that are displayed in almost all tabs, in an
|
||||||
# information window.
|
# information window.
|
||||||
|
@ -157,7 +159,6 @@ class Core(object):
|
||||||
'M-z': self.go_to_previous_tab,
|
'M-z': self.go_to_previous_tab,
|
||||||
'^L': self.full_screen_redraw,
|
'^L': self.full_screen_redraw,
|
||||||
'M-j': self.go_to_room_number,
|
'M-j': self.go_to_room_number,
|
||||||
# 'M-c': self.coucou,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add handlers
|
# Add handlers
|
||||||
|
@ -191,9 +192,6 @@ class Core(object):
|
||||||
for plugin in plugins.split():
|
for plugin in plugins.split():
|
||||||
self.plugin_manager.load(plugin)
|
self.plugin_manager.load(plugin)
|
||||||
|
|
||||||
def coucou(self):
|
|
||||||
self.command_pubsub('pubsub.louiz.org')
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
Init curses, create the first tab, etc
|
Init curses, create the first tab, etc
|
||||||
|
@ -1707,3 +1705,31 @@ class Core(object):
|
||||||
return False
|
return False
|
||||||
self.current_tab().command_say(msg)
|
self.current_tab().command_say(msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def exec_command(self, command):
|
||||||
|
"""
|
||||||
|
Execute an external command on the local or a remote
|
||||||
|
machine, depending on the conf. For example, to open a link in a
|
||||||
|
browser, do exec_command("firefox http://poezio.eu"),
|
||||||
|
and this will call the command on the correct computer.
|
||||||
|
The remote execution is done by writing the command on a fifo.
|
||||||
|
That fifo has to be on the machine where poezio is running, and
|
||||||
|
accessible (through sshfs for example) from the local machine (where
|
||||||
|
poezio is not running). A very simple daemon reads on that fifo,
|
||||||
|
and executes any command that is read in it.
|
||||||
|
"""
|
||||||
|
if config.get('exec_remote', 'false') == 'true':
|
||||||
|
# We just write the command in the fifo
|
||||||
|
if not self.remote_fifo:
|
||||||
|
try:
|
||||||
|
self.remote_fifo = Fifo(os.path.join(config.get('remote_fifo_path', './'), 'poezio.fifo'), 'w')
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
self.information('Could not open fifo file for writing: %s' % (e,), 'Error')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.remote_fifo.write(command)
|
||||||
|
except (IOError) as e:
|
||||||
|
self.information('Could not execute [%s]: %s' % (command, e,), 'Error')
|
||||||
|
self.remote_fifo = None
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
63
src/daemon.py
Executable file
63
src/daemon.py
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
# Copyright 2011 Florent Le Coz <louiz@louiz.org>
|
||||||
|
#
|
||||||
|
# This file is part of Poezio.
|
||||||
|
#
|
||||||
|
# Poezio is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the zlib license. See the COPYING file.
|
||||||
|
|
||||||
|
"""
|
||||||
|
This file is a standalone program that creates a fifo file (if it doesn’t exist
|
||||||
|
yet), opens it for reading, reads commands from it and executes them (each line
|
||||||
|
should be a command).
|
||||||
|
|
||||||
|
Usage: ./daemon.py <path_tofifo>
|
||||||
|
|
||||||
|
That fifo should be in a directory, shared through sshfs, with the remote
|
||||||
|
machine running poezio. Poezio then writes command in it, and this daemon
|
||||||
|
executes them on the local machine.
|
||||||
|
Note that you should not start this daemon if you do not trust the remote
|
||||||
|
machine that is running poezio, since this could make it run any (dangerous)
|
||||||
|
command on your local machine.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from fifo import Fifo
|
||||||
|
|
||||||
|
class Executor(threading.Thread):
|
||||||
|
"""
|
||||||
|
Just a class to execute commands in a thread.
|
||||||
|
This way, the execution can totally fail, we don’t care,
|
||||||
|
and we can start commands without having to wait for them
|
||||||
|
to return
|
||||||
|
"""
|
||||||
|
def __init__(self, command):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.command = command
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
print('executing %s' % (self.command,))
|
||||||
|
subprocess.call(self.command.split())
|
||||||
|
|
||||||
|
def main(path):
|
||||||
|
while True:
|
||||||
|
fifo = Fifo(path, 'r')
|
||||||
|
while True:
|
||||||
|
line = fifo.readline()
|
||||||
|
if line == '':
|
||||||
|
del fifo
|
||||||
|
break
|
||||||
|
e = Executor(line)
|
||||||
|
e.start()
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
print('Usage: %s <fifo_name>' % (sys.argv[0],))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
argc = len(sys.argv)
|
||||||
|
if argc != 2:
|
||||||
|
usage()
|
||||||
|
else:
|
||||||
|
main(sys.argv[1])
|
70
src/fifo.py
Normal file
70
src/fifo.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# Copyright 2011 Florent Le Coz <louiz@louiz.org>
|
||||||
|
#
|
||||||
|
# This file is part of Poezio.
|
||||||
|
#
|
||||||
|
# Poezio is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the zlib license. See the COPYING file.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Defines the Fifo class
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
|
||||||
|
class OpenTrick(threading.Thread):
|
||||||
|
"""
|
||||||
|
A threaded trick to make the open for writing succeed.
|
||||||
|
A fifo cannot be opened for writing if it has not been
|
||||||
|
yet opened by the other hand for reading.
|
||||||
|
So, we just open the fifo for reading and close it
|
||||||
|
immediately afterwards.
|
||||||
|
Once that is done, we can freely keep the fifo open for
|
||||||
|
writing and write things in it. The writing can fail if
|
||||||
|
there’s still nothing reading that fifo, but we just yell
|
||||||
|
an error in that case.
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
open(self.path, 'r').close()
|
||||||
|
|
||||||
|
|
||||||
|
class Fifo(object):
|
||||||
|
"""
|
||||||
|
Just a simple file handler, writing and reading in a fifo.
|
||||||
|
Mode is either 'r' or 'w', just like the mode for the open()
|
||||||
|
function.
|
||||||
|
"""
|
||||||
|
def __init__(self, path, mode):
|
||||||
|
self.trick = None
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkfifo(path)
|
||||||
|
if mode == 'w':
|
||||||
|
self.trick = OpenTrick(path)
|
||||||
|
# that thread will wait until we open it for writing
|
||||||
|
self.trick.start()
|
||||||
|
self.fd = open(path, mode)
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
"""
|
||||||
|
Try to write on the fifo. If that fails, this means
|
||||||
|
that nothing has that fifo opened, so the writing is useless,
|
||||||
|
so we just return (and display an error telling that, somewhere).
|
||||||
|
"""
|
||||||
|
self.fd.write(data)
|
||||||
|
self.fd.flush()
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
return self.fd.readline()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
self.fd.close()
|
||||||
|
except:
|
||||||
|
pass
|
Loading…
Reference in a new issue