8ae1989a7c59a667774331bedcaeef4f5baae2ee
[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             if len(self.__args) == 1 \
18                 and self.__file.startswith('!'):
19                 self.__file = self.__file.lstrip('!')
20                 self.__auto_args = True
21             else:
22                 self.__auto_args = False
23
24     def __call__(self, bus, message, environ):
25         args_list = map(dbus_to_str, message.get_args_list())
26         env = dict()
27         env.update(environ)
28         try:
29             dbus_env = dict(
30                     (('DBUS_ARG%d' % i, a) for i, a in enumerate(args_list)),
31                     DBUS_ARGN   = str(len(args_list)),
32                     DBUS_SENDER = str(message.get_sender()),
33                     DBUS_DEST   = str(message.get_destination()),
34                     DBUS_IFACE  = str(message.get_interface()),
35                     DBUS_PATH   = str(message.get_path()),
36                     DBUS_MEMBER = str(message.get_member()),
37                     DBUS_BUS    = bus.__class__.__name__.lower()[0:-3],
38                     # TODO
39                     #DBUS_ERROR = str(message.get_error_name()),
40                     DBUS_TYPE   = get_dbus_message_type(message)
41                     )
42             env.update(dbus_env)
43         except Exception, e:
44             log.error('environ exception', e)
45             raise e
46
47         if self.__auto_args:
48             if dbus_env['DBUS_TYPE'] in ('signal', 'method_call'):
49                 args_list[0:0] = [
50                     dbus_env['DBUS_IFACE'],
51                     dbus_env['DBUS_MEMBER']]
52             # TODO
53             #elif dbus_env['DBUS_TYPE'] == 'error':
54             #    args_list.insert(0, dbus_env['DBUS_ERROR'])
55
56             args_list[0:0] = [
57                     self.__file,
58                     dbus_env['DBUS_SENDER'],
59                     dbus_env['DBUS_DEST']]
60         else:
61             args_list = self.__args
62
63         result = os.spawnvpe(os.P_WAIT, self.__file, args_list, env)
64         if result != 0:
65             log.warn('command returned non-zero status', self.__file, args_list, dbus_env, result)
66         return result
67
68     @property
69     def is_shell_cmd(self):
70         for c in '|><$&;{}':
71             if c in self.__value:
72                 return True
73         return False
74
75     def __str__(self):
76         return self.__value
77
78 class Commands(object):
79     __commands = {}
80     __environ = {}
81
82     def __iter__(self):
83         for m, c in self.__commands.iteritems():
84             yield m, c
85
86     def _get_environ(self):
87         return self.__environ
88
89     def _set_environ(self, value):
90         self.__environ = dict()
91         self.__environ.update(os.environ)
92         self.__environ.update(value)
93
94     environ = property(_get_environ, _set_environ)
95
96     def handler(self, bus, message):
97         for rule, command in self.__commands.iteritems():
98             if rule.match(bus, message):
99                 log('rule matched', rule, command)
100                 command(bus, message, self.__environ)
101                 return
102
103     def add(self, matcher, command):
104         self.__commands[matcher] = command
105
106     def clear(self):
107         self.__commands = {}
108