more robust and unicode safe dbus string handling
[dbuscron] / dbuscron / command.py
1 # encoding: utf-8
2
3 import os
4 from dbuscron.bus import get_dbus_message_type, dbus_to_str
5 from dbuscron.logger import Logger
6 log = Logger(__name__)
7
8 class Command(object):
9     def __init__(self, cmd):
10         self.__value = cmd
11         if self.is_shell_cmd:
12             self.__file = os.environ.get('SHELL', '/bin/sh')
13             self.__args = [self.__file, '-c', self.__value]
14         else:
15             self.__args = cmd.split(' ')
16             self.__file = self.__args[0]
17
18     def __call__(self, bus, message, environ):
19         args_list = message.get_args_list()
20         env = dict()
21         env.update(environ)
22         try:
23             dbus_env = dict(
24                     (('DBUS_ARG%d' % i, dbus_to_str(a)) for i, a in enumerate(args_list)),
25                     DBUS_ARGN   = str(len(args_list)),
26                     DBUS_SENDER = str(message.get_sender()),
27                     DBUS_DEST   = str(message.get_destination()),
28                     DBUS_IFACE  = str(message.get_interface()),
29                     DBUS_PATH   = str(message.get_path()),
30                     DBUS_MEMBER = str(message.get_member()),
31                     DBUS_BUS    = bus.__class__.__name__.lower()[0:-3],
32                     DBUS_TYPE   = get_dbus_message_type(message)
33                     )
34             env.update(dbus_env)
35         except Exception, e:
36             log.error('environ exception', e)
37             raise e
38
39         result = os.spawnvpe(os.P_WAIT, self.__file, self.__args, env)
40         if result != 0:
41             log.warn('command returned non-zero status', self.__file, self.__args, dbus_env, result)
42         return result
43
44     @property
45     def is_shell_cmd(self):
46         for c in '|><$&;{}':
47             if c in self.__value:
48                 return True
49         return False
50
51     def __str__(self):
52         return self.__value
53
54 class Commands(object):
55     __commands = {}
56     __environ = {}
57
58     def __iter__(self):
59         for m, c in self.__commands.iteritems():
60             yield m, c
61
62     def _get_environ(self):
63         return self.__environ
64
65     def _set_environ(self, value):
66         self.__environ = dict()
67         self.__environ.update(os.environ)
68         self.__environ.update(value)
69
70     environ = property(_get_environ, _set_environ)
71
72     def handler(self, bus, message):
73         for rule, command in self.__commands.iteritems():
74             if rule.match(bus, message):
75                 log.info('matched %s %s' % (rule, command))
76                 command(bus, message, self.__environ)
77                 return
78
79     def add(self, matcher, command):
80         self.__commands[matcher] = command
81
82     def clear(self):
83         self.__commands = {}
84