+++ /dev/null
-#!/usr/bin/python
-
-import logging
-import logging.handlers
-import os
-import pcapy
-import struct
-import subprocess
-import sys
-import time
-
-from ConfigParser import ConfigParser
-from threading import Thread
-
-TYPE_ARP = 0
-
-OPERATION_REQUEST = 0
-OPERATION_RESPONSE = 1
-
-def getAllInterfaces():
- filters = ("lo", "any")
- ret = []
- for dev in pcapy.findalldevs():
- if not dev in filters:
- ret.append(dev)
- return ret
-
-def val2int(val):
- return int("".join(["%02d" % ord(c) for c in val]), 16)
-
-def val2ip4(val):
- return ".".join(["%d" % ord(i) for i in val])
-
-def val2mac(val):
- return ":".join(["%02x" % ord(i) for i in val])
-
-def decode_packet(data):
- for func in (decode_arp_packet,):
- try:
- p =func(data)
- except PacketTypeError:
- continue
-
- return p
-
- raise PacketTypeError, "Could not determine type of packet"
-
-def decode_arp_packet(data):
- operationmap = {
- 1 : OPERATION_REQUEST,
- 2 : OPERATION_RESPONSE,
- }
-
- #if not len(data) == 42:
- # raise DecodeError, "Data has wrong length: %d" % len(data)
-
- ret = {
- "type" : TYPE_ARP,
- }
-
- #"hwtype" : data[:2],
- protocol = val2int(struct.unpack("!2s", data[12:14])[0])
- hw_addr_size = val2int(struct.unpack("!1s", data[18:19])[0])
- hw_prot_size = val2int(struct.unpack("!1s", data[19:20])[0])
- operation = val2int(struct.unpack("!2s", data[20:22])[0])
-
- # Sanity checks
- if not protocol == 0x0806:
- raise PacketTypeError, "Not an ARP packet"
-
- # TODO Must check hwtype here...
-
- try:
- ret["operation"] = operationmap[operation]
- except KeyError:
- raise DecodeError, "Unknown operation type"
-
- address_length = hw_addr_size + hw_prot_size
- unpack_str = "!%ss%ss" % (hw_addr_size, hw_prot_size)
-
- ret["source_address"], ret["source_ip_address"] = \
- struct.unpack(unpack_str, data[22:22 + address_length])
-
- ret["destination_address"], ret["destination_ip_address"] = \
- struct.unpack(unpack_str, data[22 + address_length:22 + address_length * 2])
-
- for i in ("source_address", "destination_address"):
- ret[i] = val2mac(ret[i])
-
- for i in ("source_ip_address", "destination_ip_address"):
- ret[i] = val2ip4(ret[i])
-
- return ret
-
-def decode_ndp_packet(data):
- raise PacketTypeError
-
-class PacketTypeError(Exception):
- pass
-
-class DecodeError(Exception):
- pass
-
-
-class InterfaceError(Exception):
- pass
-
-
-class Database(object):
- def __init__(self, interface):
- self.interface = interface
- self.dev = self.interface.dev
- self.log = self.interface.log
-
- self.__data = {}
-
- def open(self):
- self.log.debug("Opened database for %s" % self.dev)
-
- def close(self):
- self.log.debug("Closing database for %s" % self.dev)
- print self.__data
-
- def get(self, mac):
- if self.has(mac):
- return self.__data[mac]
-
- def has(self, mac):
- return self.__data.has_key(mac)
-
- def put(self, mac, key, val):
- if not self.has(mac):
- self.__data[mac] = {}
-
- # TODO Check key for sanity
-
- self.__data[mac][key] = val
-
-
-class Interface(Thread):
- heartbeat = 0.1
-
- def __init__(self, dev, cappie, promisc=False, mtu=1500):
- Thread.__init__(self)
-
- self.cappie = cappie
- self.dev = dev
- self.log = self.cappie.log
- self.mtu = mtu
- self.promisc = promisc
- self.queue = self.cappie.queue
-
- self.db = Database(self)
-
- self.log.debug("Created new interface %s" % self.dev)
-
- self.__running = True
-
- def _callback(self, header, data):
- self.log.debug("Received packet on %s" % self.dev)
- try:
- p = decode_packet(data)
- except PacketTypeError, e:
- self.log.error("Got unknown packet: %s" % e)
- return
- except DecodeError, e:
- self.log.warning("Got decoding error: %s" % e)
- return
-
- # Dump packet information
- for key, val in p.items():
- self.log.debug(" %s: %s" % (key, val))
-
- if not self.db.has(p["source_address"]):
- self.db.put(p["source_address"], "SOURCE_IP_ADDRESS", p["source_ip_address"])
-
- def run(self):
- self.log.info("Starting interface %s" % self.dev)
-
- self.db.open()
-
- p = pcapy.open_live(self.dev, self.mtu, self.promisc, 0)
- p.setfilter(self.filter)
- #p.loop(0, self._callback)
-
- p.setnonblock(1)
- while True:
- if not self.__running:
- self.db.close()
- return
-
- if p.dispatch(1, self._callback):
- continue
-
- time.sleep(self.heartbeat)
-
- def shutdown(self):
- if not self.__running:
- return
-
- self.log.debug("Sending shutdown signal to %s" % self.dev)
- self.__running = False
-
- @property
- def filter(self):
- return "arp or rarp"
-
-
-class QueueFullError(Exception):
- pass
-
-
-class Queue(Thread):
- heartbeat = 1.0
- maxitems = 100
-
- def __init__(self, log):
- Thread.__init__(self)
-
- self.log = log
-
- self.__running = True
- self.__queue = []
-
- def __len__(self):
- return self.length
-
- def add(self, event):
- if self.length > self.maxitems:
- raise QueueFullError, "Cannot queue new event."
-
- self.__queue.append(event)
-
- @property
- def length(self):
- return len(self.__queue)
-
- def run(self):
- self.log.debug("Started event queue")
-
- while self.__running or self.__queue:
- if not self.__queue:
- #self.log.debug("Queue sleeping for %s seconds" % self.heartbeat)
- time.sleep(self.heartbeat)
- continue
-
- event = self.__queue.pop(0)
- self.log.debug("Processing queue event: %s" % event)
- try:
- event.run()
- except EventException, e:
- self.log.error("Catched event exception: %s" % e)
-
- def shutdown(self):
- self.__running = False
- self.log.debug("Shutting down queue")
- self.log.debug("%d events in queue left" % len(self.__queue))
-
- # Wait until queue handled all events
- self.join()
-
-
-class EventException(Exception):
- pass
-
-class Event(object):
- def __init__(self, interface):
- self.cappie = interface.cappie
- self.interface = interface
- self.log = interface.log
-
- def __str__(self):
- return self.__class__.__name__
-
- def run(self):
- raise NotImplementedError
-
-
-class EventTimeout(EventException):
- pass
-
-
-class EventShell(Event):
- heartbeat = 0.1
- timeout = 10
-
- def __init__(self, interface, script):
- Event.__init__(self, interface)
-
- self.script = script
-
- def run(self):
- args = " ".join([self.script, self.interface.dev])
-
- start = time.time()
- self.log.debug("Running: %s" % args)
-
- p = subprocess.Popen(args,
- close_fds=True,
- shell=True,
- stdin=open("/dev/null", "r"),
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
-
- while p.poll() is None:
- time.sleep(self.heartbeat)
- if (time.time() - start) > self.timeout:
- try:
- os.killpg(p.pid, 9)
- except OSError:
- pass
- raise EventTimeout, "Script took too long to return"
-
- for line in p.stdout.read().splitlines():
- if not line: continue
- self.log.debug(" %s" % line)
-
- self.cappie.log.debug("Child process returned with exit code: %s" % p.returncode)
- return p.returncode
-
-
-class Cappie(object):
- def __init__(self):
- self.__interfaces = []
-
- self.log = logging.getLogger("cappie")
- self.log.setLevel(logging.INFO)
-
- # Log to console
- handler = logging.StreamHandler()
- handler.setFormatter(logging.Formatter("%(levelname)7s %(message)s"))
- self.log.addHandler(handler)
-
- # Setup syslog
- handler = logging.handlers.SysLogHandler("/dev/log")
- handler.setFormatter(logging.Formatter("cappie: %(message)s"))
- self.log.addHandler(handler)
-
- self.queue = Queue(self.log)
-
- self.log.info("Cappie successfully started")
-
- def __del__(self):
- self.shutdown()
- self.log.info("Exiting")
-
- def setDebug(self, debug):
- if debug:
- self.log.setLevel(logging.DEBUG)
- else:
- self.log.setLevel(logging.INFO)
-
- def addInterface(self, dev, **kwargs):
- if not dev in getAllInterfaces():
- raise InterfaceError, "No such interface %s" % dev
-
- kwargs["cappie"] = self
-
- iface = Interface(dev, **kwargs)
- self.__interfaces.append(iface)
-
- def run(self):
- if not self.__interfaces:
- raise RuntimeError, "No interfaces were configured"
-
- # Start queue
- self.queue.start()
-
- # Start a thread for each interface
- for iface in self.__interfaces:
- iface.start()
-
- while True:
- if not self.queue.is_alive():
- self.log.critical("Queue thread died unexpectedly.")
- return
-
- for iface in self.__interfaces:
- if not iface.is_alive():
- self.log.critical("Thread died unexpectedly. %s" % iface.dev)
- return
- time.sleep(60)
-
- def readConfig(self, configfile):
- config = ConfigParser()
- config.read([configfile])
-
- global_opts = {}
- if config.has_section("global"):
- for option, value in config.items("global"):
- global_opts[option] = value
-
- config.remove_section("global")
-
- for iface in config.sections():
- options = {}
- for option, value in config.items(iface):
- options[option] = value
- self.addInterface(iface, **options)
-
- def shutdown(self):
- for iface in self.__interfaces:
- iface.shutdown()
-
- self.queue.shutdown()
-
-
-if __name__ == "__main__":
- from optparse import OptionParser
- op = OptionParser()
- op.add_option("-c", "--config", dest="config",
- help="read configuration from file", metavar="FILE",
- default="/etc/cappie/cappie.conf")
- op.add_option("-d", action="store_true", dest="debug", default=False)
-
- (options, args) = op.parse_args()
-
- cappie = Cappie()
- if options.config:
- cappie.readConfig(options.config)
- cappie.setDebug(options.debug)
-
- try:
- cappie.run()
- except KeyboardInterrupt:
- cappie.shutdown()
- except RuntimeError, e:
- print >>sys.stderr, e
- sys.exit(1)
-
- #sys.exit(0)
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import logging
+import logging.handlers
+import pcapy
+import time
+
+from ConfigParser import ConfigParser
+from threading import Thread
+
+import protocol
+import queue
+
+from errors import *
+
+def getAllInterfaces():
+ filters = ("lo", "any")
+ ret = []
+ for dev in pcapy.findalldevs():
+ if not dev in filters:
+ ret.append(dev)
+ return ret
+
+class Cappie(object):
+ def __init__(self):
+ self.__interfaces = []
+
+ self.log = logging.getLogger("cappie")
+ self.log.setLevel(logging.INFO)
+
+ # Log to console
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter("%(levelname)7s %(message)s"))
+ self.log.addHandler(handler)
+
+ # Setup syslog
+ handler = logging.handlers.SysLogHandler("/dev/log")
+ handler.setFormatter(logging.Formatter("cappie: %(message)s"))
+ self.log.addHandler(handler)
+
+ self.queue = queue.Queue(self.log)
+
+ self.log.info("Cappie successfully started")
+
+ def __del__(self):
+ self.shutdown()
+ self.log.info("Exiting")
+
+ def setDebug(self, debug):
+ if debug:
+ self.log.setLevel(logging.DEBUG)
+ else:
+ self.log.setLevel(logging.INFO)
+
+ def addInterface(self, dev, **kwargs):
+ if not dev in getAllInterfaces():
+ raise InterfaceError, "No such interface %s" % dev
+
+ kwargs["cappie"] = self
+
+ iface = Interface(dev, **kwargs)
+ self.__interfaces.append(iface)
+
+ def run(self):
+ if not self.__interfaces:
+ raise RuntimeError, "No interfaces were configured"
+
+ # Start queue
+ self.queue.start()
+
+ # Start a thread for each interface
+ for iface in self.__interfaces:
+ iface.start()
+
+ while True:
+ if not self.queue.is_alive():
+ self.log.critical("Queue thread died unexpectedly.")
+ return
+
+ for iface in self.__interfaces:
+ if not iface.is_alive():
+ self.log.critical("Thread died unexpectedly. %s" % iface.dev)
+ return
+ time.sleep(60)
+
+ def readConfig(self, configfile):
+ config = ConfigParser()
+ config.read([configfile])
+
+ global_opts = {}
+ if config.has_section("global"):
+ for option, value in config.items("global"):
+ global_opts[option] = value
+
+ config.remove_section("global")
+
+ for iface in config.sections():
+ options = {}
+ for option, value in config.items(iface):
+ options[option] = value
+ self.addInterface(iface, **options)
+
+ def shutdown(self):
+ for iface in self.__interfaces:
+ iface.shutdown()
+
+ self.queue.shutdown()
+
+
+class Interface(Thread):
+ heartbeat = 0.1
+
+ def __init__(self, dev, cappie, promisc=False, mtu=1500):
+ Thread.__init__(self)
+
+ self.cappie = cappie
+ self.dev = dev
+ self.log = self.cappie.log
+ self.mtu = mtu
+ self.promisc = promisc
+ self.queue = self.cappie.queue
+
+ self.db = Database(self)
+
+ self.log.debug("Created new interface %s" % self.dev)
+
+ self.__running = True
+
+ def _callback(self, header, data):
+ self.log.debug("Received packet on %s" % self.dev)
+ try:
+ p = protocol.decode_packet(data)
+ except PacketTypeError, e:
+ self.log.error("Got unknown packet: %s" % e)
+ return
+ except DecodeError, e:
+ self.log.warning("Got decoding error: %s" % e)
+ return
+
+ # Dump packet information
+ for key, val in p.items():
+ self.log.debug(" %s: %s" % (key, val))
+
+ if not self.db.has(p["source_address"]):
+ self.db.put(p["source_address"], "SOURCE_IP_ADDRESS", p["source_ip_address"])
+
+ def run(self):
+ self.log.info("Starting interface %s" % self.dev)
+
+ self.db.open()
+
+ p = pcapy.open_live(self.dev, self.mtu, self.promisc, 0)
+ p.setfilter(self.filter)
+ #p.loop(0, self._callback)
+
+ p.setnonblock(1)
+ while True:
+ if not self.__running:
+ self.db.close()
+ return
+
+ if p.dispatch(1, self._callback):
+ continue
+
+ time.sleep(self.heartbeat)
+
+ def shutdown(self):
+ if not self.__running:
+ return
+
+ self.log.debug("Sending shutdown signal to %s" % self.dev)
+ self.__running = False
+
+ @property
+ def filter(self):
+ return "arp or rarp"
+
+
+class Database(object):
+ def __init__(self, interface):
+ self.interface = interface
+ self.dev = self.interface.dev
+ self.log = self.interface.log
+
+ self.__data = {}
+
+ def open(self):
+ self.log.debug("Opened database for %s" % self.dev)
+
+ def close(self):
+ self.log.debug("Closing database for %s" % self.dev)
+ print self.__data
+
+ def get(self, mac):
+ if self.has(mac):
+ return self.__data[mac]
+
+ def has(self, mac):
+ return self.__data.has_key(mac)
+
+ def put(self, mac, key, val):
+ if not self.has(mac):
+ self.__data[mac] = {}
+
+ # TODO Check key for sanity
+
+ self.__data[mac][key] = val
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+TYPE_ARP = 0
+
+OPERATION_REQUEST = 0
+OPERATION_RESPONSE = 1
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+class DecodeError(Exception):
+ pass
+
+class EventException(Exception):
+ pass
+
+class EventTimeout(EventException):
+ pass
+
+class InterfaceError(Exception):
+ pass
+
+class PacketTypeError(Exception):
+ pass
+
+class QueueFullError(Exception):
+ pass
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import os
+import subprocess
+import time
+
+from errors import *
+
+class Event(object):
+ def __init__(self, interface):
+ self.cappie = interface.cappie
+ self.interface = interface
+ self.log = interface.log
+
+ def __str__(self):
+ return self.__class__.__name__
+
+ def run(self):
+ raise NotImplementedError
+
+
+class EventShell(Event):
+ heartbeat = 0.1
+ timeout = 10
+
+ def __init__(self, interface, script):
+ Event.__init__(self, interface)
+
+ self.script = script
+
+ def run(self):
+ args = " ".join([self.script, self.interface.dev])
+
+ start = time.time()
+ self.log.debug("Running: %s" % args)
+
+ p = subprocess.Popen(args,
+ close_fds=True,
+ shell=True,
+ stdin=open("/dev/null", "r"),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+
+ while p.poll() is None:
+ time.sleep(self.heartbeat)
+ if (time.time() - start) > self.timeout:
+ try:
+ os.killpg(p.pid, 9)
+ except OSError:
+ pass
+ raise EventTimeout, "Script took too long to return"
+
+ for line in p.stdout.read().splitlines():
+ if not line: continue
+ self.log.debug(" %s" % line)
+
+ self.cappie.log.debug("Child process returned with exit code: %s" % \
+ p.returncode)
+
+ return p.returncode
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import struct
+
+from constants import *
+from errors import *
+
+def val2int(val):
+ return int("".join(["%02d" % ord(c) for c in val]), 16)
+
+def val2ip4(val):
+ return ".".join(["%d" % ord(i) for i in val])
+
+def val2mac(val):
+ return ":".join(["%02x" % ord(i) for i in val])
+
+def decode_packet(data):
+ for func in (decode_arp_packet,):
+ try:
+ p = func(data)
+ except PacketTypeError:
+ continue
+
+ return p
+
+ raise PacketTypeError, "Could not determine type of packet"
+
+def decode_arp_packet(data):
+ operationmap = {
+ 1 : OPERATION_REQUEST,
+ 2 : OPERATION_RESPONSE,
+ }
+
+ #if not len(data) == 42:
+ # raise DecodeError, "Data has wrong length: %d" % len(data)
+
+ ret = {
+ "type" : TYPE_ARP,
+ }
+
+ #"hwtype" : data[:2],
+ protocol = val2int(struct.unpack("!2s", data[12:14])[0])
+ hw_addr_size = val2int(struct.unpack("!1s", data[18:19])[0])
+ hw_prot_size = val2int(struct.unpack("!1s", data[19:20])[0])
+ operation = val2int(struct.unpack("!2s", data[20:22])[0])
+
+ # Sanity checks
+ if not protocol == 0x0806:
+ raise PacketTypeError, "Not an ARP packet"
+
+ # TODO Must check hwtype here...
+
+ try:
+ ret["operation"] = operationmap[operation]
+ except KeyError:
+ raise DecodeError, "Unknown operation type"
+
+ address_length = hw_addr_size + hw_prot_size
+ unpack_str = "!%ss%ss" % (hw_addr_size, hw_prot_size)
+
+ ret["source_address"], ret["source_ip_address"] = \
+ struct.unpack(unpack_str, data[22:22 + address_length])
+
+ ret["destination_address"], ret["destination_ip_address"] = \
+ struct.unpack(unpack_str, data[22 + address_length:22 + address_length * 2])
+
+ for i in ("source_address", "destination_address"):
+ ret[i] = val2mac(ret[i])
+
+ for i in ("source_ip_address", "destination_ip_address"):
+ ret[i] = val2ip4(ret[i])
+
+ return ret
+
+def decode_ndp_packet(data):
+ raise PacketTypeError
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import time
+
+from threading import Thread
+
+from errors import *
+
+class Queue(Thread):
+ heartbeat = 1.0
+ maxitems = 100
+
+ def __init__(self, log):
+ Thread.__init__(self)
+
+ self.log = log
+
+ self.__running = True
+ self.__queue = []
+
+ def __len__(self):
+ return self.length
+
+ def add(self, event):
+ if self.length > self.maxitems:
+ raise QueueFullError, "Cannot queue new event."
+
+ self.__queue.append(event)
+
+ @property
+ def length(self):
+ return len(self.__queue)
+
+ def run(self):
+ self.log.debug("Started event queue")
+
+ while self.__running or self.__queue:
+ if not self.__queue:
+ #self.log.debug("Queue sleeping for %s seconds" % self.heartbeat)
+ time.sleep(self.heartbeat)
+ continue
+
+ event = self.__queue.pop(0)
+ self.log.debug("Processing queue event: %s" % event)
+ try:
+ event.run()
+ except EventException, e:
+ self.log.error("Catched event exception: %s" % e)
+
+ def shutdown(self):
+ self.__running = False
+ self.log.debug("Shutting down queue")
+ self.log.debug("%d events in queue left" % len(self.__queue))
+
+ # Wait until queue handled all events
+ self.join()
--- /dev/null
+#!/usr/bin/python
+###############################################################################
+# #
+# Cappie #
+# Copyright (C) 2010 Michael Tremer #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import sys
+
+from cappie import Cappie
+
+def main():
+ from optparse import OptionParser
+ op = OptionParser()
+ op.add_option("-c", "--config", dest="config",
+ help="read configuration from file", metavar="FILE",
+ default="/etc/cappie/cappie.conf")
+ op.add_option("-d", action="store_true", dest="debug", default=False)
+
+ (options, args) = op.parse_args()
+
+ cappie = Cappie()
+ if options.config:
+ cappie.readConfig(options.config)
+ cappie.setDebug(options.debug)
+
+ try:
+ cappie.run()
+ except KeyboardInterrupt:
+ cappie.shutdown()
+ except RuntimeError, e:
+ print >>sys.stderr, e
+ sys.exit(1)
+
+ #sys.exit(0)
+
+main()