From 534780507041d0f1058b11c7c74c0f89a2bc6e37 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sat, 17 Apr 2010 20:55:02 +0200 Subject: [PATCH] Splitted into daemon and python module. --- cappie.py | 431 -------------------------------------------- cappie/__init__.py | 226 +++++++++++++++++++++++ cappie/constants.py | 25 +++ cappie/errors.py | 38 ++++ cappie/events.py | 79 ++++++++ cappie/protocol.py | 95 ++++++++++ cappie/queue.py | 75 ++++++++ cappied | 51 ++++++ 8 files changed, 589 insertions(+), 431 deletions(-) delete mode 100644 cappie.py create mode 100644 cappie/__init__.py create mode 100644 cappie/constants.py create mode 100644 cappie/errors.py create mode 100644 cappie/events.py create mode 100644 cappie/protocol.py create mode 100644 cappie/queue.py create mode 100755 cappied diff --git a/cappie.py b/cappie.py deleted file mode 100644 index 5065a9d..0000000 --- a/cappie.py +++ /dev/null @@ -1,431 +0,0 @@ -#!/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) diff --git a/cappie/__init__.py b/cappie/__init__.py new file mode 100644 index 0000000..2fea5e0 --- /dev/null +++ b/cappie/__init__.py @@ -0,0 +1,226 @@ +#!/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 . # +# # +############################################################################### + +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 diff --git a/cappie/constants.py b/cappie/constants.py new file mode 100644 index 0000000..50fb016 --- /dev/null +++ b/cappie/constants.py @@ -0,0 +1,25 @@ +#!/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 . # +# # +############################################################################### + +TYPE_ARP = 0 + +OPERATION_REQUEST = 0 +OPERATION_RESPONSE = 1 diff --git a/cappie/errors.py b/cappie/errors.py new file mode 100644 index 0000000..ec9e481 --- /dev/null +++ b/cappie/errors.py @@ -0,0 +1,38 @@ +#!/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 . # +# # +############################################################################### + +class DecodeError(Exception): + pass + +class EventException(Exception): + pass + +class EventTimeout(EventException): + pass + +class InterfaceError(Exception): + pass + +class PacketTypeError(Exception): + pass + +class QueueFullError(Exception): + pass diff --git a/cappie/events.py b/cappie/events.py new file mode 100644 index 0000000..d403b52 --- /dev/null +++ b/cappie/events.py @@ -0,0 +1,79 @@ +#!/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 . # +# # +############################################################################### + +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 diff --git a/cappie/protocol.py b/cappie/protocol.py new file mode 100644 index 0000000..ed5f4a3 --- /dev/null +++ b/cappie/protocol.py @@ -0,0 +1,95 @@ +#!/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 . # +# # +############################################################################### + +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 diff --git a/cappie/queue.py b/cappie/queue.py new file mode 100644 index 0000000..e2d0dd6 --- /dev/null +++ b/cappie/queue.py @@ -0,0 +1,75 @@ +#!/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 . # +# # +############################################################################### + +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() diff --git a/cappied b/cappied new file mode 100755 index 0000000..0d27392 --- /dev/null +++ b/cappied @@ -0,0 +1,51 @@ +#!/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 . # +# # +############################################################################### + +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() -- 2.39.2