Merge branch 'remote_exec' into plugins

This commit is contained in:
Florent Le Coz 2011-10-29 05:21:27 +02:00
commit afcc387002
3 changed files with 163 additions and 4 deletions

View file

@ -49,6 +49,7 @@ from contact import Contact, Resource
from text_buffer import TextBuffer
from keyboard import read_char
from theming import get_theme
from fifo import Fifo
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
@ -94,6 +95,7 @@ class Core(object):
sys.excepthook = self.on_exception
self.running = True
self.xmpp = singleton.Singleton(connection.Connection)
self.remote_fifo = None
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
@ -157,7 +159,6 @@ class Core(object):
'M-z': self.go_to_previous_tab,
'^L': self.full_screen_redraw,
'M-j': self.go_to_room_number,
# 'M-c': self.coucou,
}
# Add handlers
@ -191,9 +192,6 @@ class Core(object):
for plugin in plugins.split():
self.plugin_manager.load(plugin)
def coucou(self):
self.command_pubsub('pubsub.louiz.org')
def start(self):
"""
Init curses, create the first tab, etc
@ -1707,3 +1705,31 @@ class Core(object):
return False
self.current_tab().command_say(msg)
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
View 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 doesnt 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 dont 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
View 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
theres 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