--- /dev/null
+*.pyo
+*.pyc
--- /dev/null
+#!/usr/bin/python
+#
+# bus type sender interface path member destination args command
+#
+# Examples for N900:
+#
+# Headphones unplugged:
+# S signal * org.freedesktop.Hal.Manager /org/freedesktop/Hal/Manager DeviceRemoved * * echo Headphones unplugged;
+#
+# Call recieved:
+# S signal * com.nokia.csd.Call /com/nokia/csd/call Coming * * echo $DBUS_ARG1 is calling
+#
+
+import sys
+
+if __name__ == '__main__':
+
+ daemon = (len(sys.argv) < 2) or (sys.argv[1] != '-f')
+ if daemon:
+ from dbuscron.daemonize import daemonize
+ daemonize(
+ pidfile='/var/run/dbuscron.pid',
+ logfile='/var/log/dbuscron.log'
+ )
+
+ from dbuscron import DbusBus, DbusRuleMatcher, Command, Commands, CrontabParser
+
+ bus = DbusBus()
+ commands = Commands()
+ crontab = CrontabParser('/etc/dbuscrontab')
+
+ for rule, cmd in crontab:
+ matcher = DbusRuleMatcher(**rule)
+ command = Command(cmd)
+ matcher.register()
+ commands.add(matcher, command)
+
+ commands.environ = crontab.environ
+ bus.attach_handler(commands.handler)
+
+ try:
+ bus.listen()
+ except KeyboardInterrupt:
+ sys.exit(2)
+
+# vim: ts=8 sts=4 sw=4 et
+
--- /dev/null
+
+from dbuscron.bus import DbusRuleMatcher, DbusBus
+from dbuscron.command import Command, Commands
+from dbuscron.daemonize import daemonize
+from dbuscron.parser import CrontabParser
+
+__all__ = ['DbusRuleMatcher', 'DbusBus', 'Command', 'Commands', 'daemonize', 'CrontabParser']
+
--- /dev/null
+
+import dbus
+
+def get_dbus_message_type(message):
+ return message.__class__.__name__.lower()[0:-7]
+
+class DbusBus(object):
+ __bus = None
+ __system_bus = None
+ __session_bus = None
+
+ def __new__(cls):
+ if not cls.__bus:
+ cls.__bus = super(DbusBus, cls).__new__(cls)
+ return cls.__bus
+
+ @property
+ def system(self):
+ if not self.__system_bus:
+ self.__system_bus = dbus.SystemBus()
+ return self.__system_bus
+
+ @property
+ def session(self):
+ if not self.__session_bus:
+ self.__session_bus = dbus.SessionBus()
+ return self.__session_bus
+
+ def attach_handler(self, handler):
+ if self.__system_bus:
+ self.__system_bus.add_message_filter(handler)
+ if self.__session_bus:
+ self.__session_bus.add_message_filter(handler)
+
+ def listen(self):
+ from dbus.mainloop.glib import DBusGMainLoop
+ from gobject import MainLoop
+ DBusGMainLoop(set_as_default=True)
+ loop = MainLoop()
+ loop.run()
+
+class DbusRuleMatcher(object):
+ def __init__(self, bus_=None, type_=None, sender_=None, interface_=None, path_=None, member_=None, destination_=None, args_=[]):
+ self._bus = bus_
+ self._type = type_
+ self._sender = sender_
+ self._interface = interface_
+ self._path = path_
+ self._member = member_
+ self._destination = destination_
+ self._args = args_
+
+ def register(self):
+ rule = str(self)
+ if rule:
+ self._bus.add_match_string(str(self))
+
+ def __str__(self):
+ rule = []
+ for key in ['type', 'sender', 'interface', 'path', 'member', 'destination']:
+ value = getattr(self, '_'+key)
+ if value is not None:
+ rule.append("%s='%s'" % (key, value))
+
+ if self._args:
+ for i, arg in enumerate(self._args):
+ rule.append("arg%d%s='%s'" % (i, 'path' if arg.startswith('/') else '', arg))
+
+ return ','.join(rule)
+
+ def match(self, bus, message):
+
+ if self._bus not in (None, bus):
+ return False
+
+ if self._type is not None:
+ type_ = get_dbus_message_type(message)
+ if self._type != type_:
+ return False
+
+ if self._sender not in (None, message.get_sender()):
+ return False
+
+ if self._interface not in (None, message.get_interface()):
+ return False
+
+ if self._path not in (None, message.get_path()):
+ return False
+
+ if self._member not in (None, message.get_member()):
+ return False
+
+ if self._destination not in (None, message.get_destination()):
+ return False
+
+ args_ = message.get_args_list()
+ for i, arg in enumerate(args_):
+ if i >= len(self._args):
+ break
+ if self._args[i] not in (None, arg):
+ return False
+
+ return True
+
--- /dev/null
+
+import os
+
+class Command(object):
+ def __init__(self, cmd):
+ self.__value = cmd
+ if self.is_shell_cmd:
+ self.__file = os.environ.get('SHELL', '/bin/sh')
+ self.__args = [self.__file, '-c', self.__value]
+ else:
+ self.__args = cmd.split(' ')
+ self.__file = self.__args[0]
+
+ def __call__(self, bus, message, environ):
+
+ args_list = message.get_args_list()
+ env = dict()
+ env.update(environ)
+ env.update(dict(
+ (('DBUS_ARG%d' % i, str(args_list[i])) for i in range(0, len(args_list))),
+ DBUS_ARGN = str(len(args_list)),
+ DBUS_SENDER = str(message.get_sender()),
+ DBUS_DEST = str(message.get_destination()),
+ DBUS_IFACE = str(message.get_interface()),
+ DBUS_PATH = str(message.get_path()),
+ DBUS_MEMBER = str(message.get_member()),
+ DBUS_BUS = bus.__class__.__name__.lower()[0:-3],
+ DBUS_TYPE = get_dbus_message_type(message)
+ ))
+ result = os.spawnvpe(os.P_WAIT, self.__file, self.__args, env)
+ return result
+
+ @property
+ def is_shell_cmd(self):
+ for c in '|><$&;{}':
+ if c in self.__value:
+ return True
+ return False
+
+ def __str__(self):
+ return self.__value
+
+class Commands(object):
+ __commands = {}
+ __environ = {}
+
+ def _get_environ(self):
+ return self.__environ
+
+ def _set_environ(self, value):
+ self.__environ = dict()
+ self.__environ.update(os.environ)
+ self.__environ.update(value)
+
+ environ = property(_get_environ, _set_environ)
+
+ def handler(self, bus, message):
+ for rule, command in self.__commands.iteritems():
+ if rule.match(bus, message):
+ command(bus, message, self.__environ)
+ return
+
+ def add(self, matcher, command):
+ self.__commands[matcher] = command
+
--- /dev/null
+import os, sys
+
+def daemonize(logfile=None, errfile=None, pidfile=None):
+ devnull = os.devnull if hasattr(os, 'devnull') else '/dev/null'
+
+ initwd = os.getcwd()
+ def absolutize(path):
+ if path.startswith('/'):
+ return path
+ return os.path.join(initwd, path)
+
+ try:
+ if os.fork() == 0:
+ os.setsid()
+ if os.fork() == 0:
+ os.chdir('/')
+ os.umask(0)
+ else:
+ os._exit(0)
+ else:
+ os._exit(0)
+ except OSError, e:
+ raise Exception('Failed daemonization: %s' % str(e))
+
+ for i in range(0, 3):
+ os.close(i)
+
+ os.open(devnull, os.O_RDWR)
+
+ def open_trunc(fname):
+ f = os.open(fname, \
+ os.O_WRONLY|os.O_CREAT)
+ os.ftruncate(f, 0)
+ return f
+
+ def open_or_dup(fname, fd=None):
+ if fname:
+ return open_trunc(absolutize(fname))
+ elif fd:
+ os.dup2(*fd)
+
+ open_or_dup(logfile, (0, 1))
+ open_or_dup(errfile, (1, 2))
+
+ pid = os.getpid()
+ if pidfile:
+ pidfile = absolutize(pidfile)
+ fd = open_trunc(pidfile)
+ os.write(fd, str(pid))
+ os.close(fd)
+
+ def remove_pidfile():
+ os.unlink(pidfile)
+ sys.exitfunc = remove_pidfile
+
+ return pid
+
--- /dev/null
+from __future__ import with_statement
+import re
+from dbuscron.bus import DbusBus
+
+try:
+ from itertools import product
+except ImportError:
+ def product(*args):
+ if args:
+ head, tail = args[0], args[1:]
+ for h in head:
+ for t in product(*tail):
+ yield (h,) + t
+
+ else:
+ yield ()
+
+class CrontabParser(object):
+ __fields_sep = re.compile(r'\s+')
+ __envvar_sep = re.compile(r'\s*=\s*')
+ __fields = [
+ 'bus_',
+ 'type_',
+ 'sender_',
+ 'interface_',
+ 'path_',
+ 'member_',
+ 'destination_',
+ 'args_',
+ #'command'
+ ]
+
+ def __init__(self, fname):
+ self.__bus = DbusBus()
+ self.__filename = fname
+ self.__environ = dict()
+
+ @property
+ def environ(self):
+ return self.__environ
+
+ def __iter__(self):
+ # bus type sender interface path member destination args command
+ with open(self.__filename) as f:
+ for line in f:
+ line = line.strip()
+
+ if not line or line.startswith('#'):
+ continue
+
+ parts = self.__fields_sep.split(line, 8)
+ if len(parts) < 9:
+ parts = self.__envvar_sep(line, 1)
+ if len(parts) == 2:
+ self.__environ[parts[0]] = parts[1]
+ continue
+
+ rule = [(None,), (None,), (None,), (None,), (None,), (None,), (None,), (None,)]
+
+ for p in range(1, 8):
+ if parts[p] != '*':
+ rule[p] = parts[p].split(',')
+
+ command = parts[8]
+
+ if parts[0] == '*' or parts[0] == 'S,s' or parts[0] == 's,S':
+ rule[0] = (self.__bus.system, self.__bus.session)
+ elif parts[0] == 's':
+ rule[0] = (self.__bus.session,)
+ elif parts[0] == 'S':
+ rule[0] = (self.__bus.system,)
+
+ for r in product(*rule):
+ if r[7]:
+ r[7] = r[7].split(';')
+ ruled = dict()
+ for i, f in enumerate(self.__fields):
+ ruled[f] = r[i]
+ yield ruled, command
+