Add SIGUSR1 traceback (made by jimbo, committed by TPG)
[uccvend-vendserver.git] / VendServer / VendServer.py
index 860c1c8..cf9c8bb 100755 (executable)
@@ -1,14 +1,12 @@
 #!/usr/bin/python
 #!/usr/bin/python
-# vim:ts=4
+# vim: ts=4 sts=4 sw=4 noexpandtab
 
 
-USE_DB = 0
 USE_MIFARE = 1
 
 import ConfigParser
 import sys, os, string, re, pwd, signal, math, syslog
 import logging, logging.handlers
 from traceback import format_tb
 USE_MIFARE = 1
 
 import ConfigParser
 import sys, os, string, re, pwd, signal, math, syslog
 import logging, logging.handlers
 from traceback import format_tb
-if USE_DB: import pg
 from time import time, sleep, mktime, localtime
 from subprocess import Popen, PIPE
 from LATClient import LATClient, LATClientException
 from time import time, sleep, mktime, localtime
 from subprocess import Popen, PIPE
 from LATClient import LATClient, LATClientException
@@ -21,8 +19,8 @@ from Idler import GreetingIdler,TrainIdler,GrayIdler,StringIdler,ClockIdler,Fort
 from SnackConfig import get_snack#, get_snacks
 import socket
 from posix import geteuid
 from SnackConfig import get_snack#, get_snacks
 import socket
 from posix import geteuid
-from LDAPConnector import get_uid,get_uname, set_card_id
 from OpenDispense import OpenDispense as Dispense
 from OpenDispense import OpenDispense as Dispense
+import TracebackPrinter
 
 CREDITS="""
 This vending machine software brought to you by:
 
 CREDITS="""
 This vending machine software brought to you by:
@@ -66,14 +64,6 @@ STATE_GRANDFATHER_CLOCK,
 TEXT_SPEED = 0.8
 IDLE_SPEED = 0.05
 
 TEXT_SPEED = 0.8
 IDLE_SPEED = 0.05
 
-_pin_uid = 0
-_pin_uname = 'root'
-_pin_pin = '----'
-
-_last_card_id = -1
-
-idlers = []
-idler = None
 
 config_options = {
        'DBServer': ('Database', 'Server'),
 
 config_options = {
        'DBServer': ('Database', 'Server'),
@@ -103,36 +93,6 @@ class VendConfigFile:
                except ConfigParser.Error, e:
                        raise SystemExit("Error reading config file "+config_file+": " + str(e))
 
                except ConfigParser.Error, e:
                        raise SystemExit("Error reading config file "+config_file+": " + str(e))
 
-class DispenseDatabaseException(Exception): pass
-
-class DispenseDatabase:
-       def __init__(self, vending_machine, host, name, user, password):
-               self.vending_machine = vending_machine
-               self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
-               self.db.query('LISTEN vend_requests')
-
-       def process_requests(self):
-               logging.debug('database processing')
-               query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
-               try:
-                       outstanding = self.db.query(query).getresult()
-               except (pg.error,), db_err:
-                       raise DispenseDatabaseException('Failed to query database: %s\n'%(db_err.strip()))
-               for (id, slot) in outstanding:
-                       (worked, code, string) = self.vending_machine.vend(slot)
-                       logging.debug (str((worked, code, string)))
-                       if worked:
-                               query = 'SELECT vend_success(%s)'%id
-                               self.db.query(query).getresult()
-                       else:
-                               query = 'SELECT vend_failed(%s)'%id
-                               self.db.query(query).getresult()
-
-       def handle_events(self):
-               notifier = self.db.getnotify()
-               while notifier is not None:
-                       self.process_requests()
-                       notify = self.db.getnotify()
 """
 This class manages the current state of the vending machine.
 """
 """
 This class manages the current state of the vending machine.
 """
@@ -158,937 +118,805 @@ class VendState:
                if newcounter is not None and self.counter != newcounter:
                        self.counter = newcounter
 
                if newcounter is not None and self.counter != newcounter:
                        self.counter = newcounter
 
-"""
-Show information to the user as to what can be dispensed.
-"""
-def scroll_options(username, mk, welcome = False):
-       # If the user has just logged in, show them their balance
-       if welcome:
-               # Balance checking
-               acct, unused = Popen(['dispense', 'acct', username], close_fds=True, stdout=PIPE).communicate()
-               # this is fucking appalling
-               balance = acct[acct.find("$")+1:acct.find("(")].strip()
-        
-               msg = [(center('WELCOME'), False, TEXT_SPEED),
-                          (center(username), False, TEXT_SPEED),
-                          (center(balance), False, TEXT_SPEED),]
-       else:
-               msg = []
-       choices = ' '*10+'CHOICES: '
-
-       # Show what is in the coke machine
-       cokes = []
-       for i in range(0, 7):
-               args = ('dispense', 'iteminfo', 'coke:%i' % i)
-               info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
-               m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
-               cents = int(m.group(1))*100 + int(m.group(2))
-               cokes.append('%i %i %s' % (i, cents, m.group(3)));
-
-       for c in cokes:
-               c = c.strip()
-               (slot_num, price, slot_name) = c.split(' ', 2)
-               if slot_name == 'dead': continue
-               choices += '%s-(%sc)-%s8 '%(slot_name, price, slot_num)
-
-       # Show the final few options
-       choices += '55-DOOR '
-       choices += 'OR ANOTHER SNACK. '
-       choices += '99 TO READ AGAIN. '
-       choices += 'CHOICE?   '
-       msg.append((choices, False, None))
-       # Send it to the display
-       mk.set_messages(msg)
-
-
-"""
-Verify the users pin
-"""
-def _check_pin(uid, pin):
-       global _pin_uid
-       global _pin_uname
-       global _pin_pin
-       print "_check_pin('",uid,"',---)"
-       if uid != _pin_uid:
-               try:
-                       info = pwd.getpwuid(uid)
-               except KeyError:
-                       logging.info('getting pin for uid %d: user not in password file'%uid)
-                       return None
-               if info.pw_dir == None: return False
-               pinfile = os.path.join(info.pw_dir, '.pin')
-               try:
-                       s = os.stat(pinfile)
-               except OSError:
-                       logging.info('getting pin for uid %d: .pin not found in home directory'%uid)
-                       return None
-               if s.st_mode & 077:
-                       logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%uid)
-                       os.chmod(pinfile, 0600)
-               try:
-                       f = file(pinfile)
-               except IOError:
-                       logging.info('getting pin for uid %d: I cannot read pin file'%uid)
-                       return None
-               pinstr = f.readline()
-               f.close()
-               if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
-                       logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
-                       return None
-               _pin_uid = uid
-               _pin_pin = pinstr
-               _pin_uname = info.pw_name
-       else:
-               pinstr = _pin_pin
-       if pin == int(pinstr):
-               logging.info("Pin correct for %d",uid)
-       else:
-               logging.info("Pin incorrect for %d",uid)
-       return pin == int(pinstr)
-
-"""
-Check if the users account has been disabled
-"""
-def acct_is_disabled(name=None):
-       global _pin_uname
-       if name == None:
-               name = _pin_uname
-       acct, unused = Popen(['dispense', 'acct', _pin_uname], close_fds=True, stdout=PIPE).communicate()
-       # this is fucking appalling
-       flags = acct[acct.find("(")+1:acct.find(")")].strip()
-       if 'disabled' in flags:
-               return True
-       if 'internal' in flags:
-               return True
-       return False
-
-"""
-Check that the user has a valid pin set
-"""
-def has_good_pin(uid):
-       return _check_pin(uid, None) != None
-
-"""
-Verify the users pin.
-"""
-def verify_user_pin(uid, pin, skip_pin_check=False):
-       if skip_pin_check or _check_pin(uid, pin) == True:
-               info = pwd.getpwuid(uid)
-               if skip_pin_check:
-                       if acct_is_disabled(info.pw_name):
-                               logging.info('refused mifare for disabled acct uid %d (%s)'%(uid,info.pw_name))
-                               return '-disabled-'
-                       logging.info('accepted mifare for uid %d (%s)'%(uid,info.pw_name))
+class VendServer():
+
+       v = None
+       vstatus = None
+       state = None
+       event = None
+       idlers = []
+       idler = None
+
+       _pin_uid = 0
+       _pin_uname = 'root'
+       _pin_pin = '----'
+       _last_card_id = -1
+
+       dispense = None
+
+       """
+       Show information to the user as to what can be dispensed.
+       """
+       def scroll_options(self, username, mk, welcome = False):
+               # If the user has just logged in, show them their balance
+               if welcome:
+                       balance = self.dispense.getBalance()
+                       msg = [(self.center('WELCOME'), False, TEXT_SPEED),
+                                  (self.center(self.dispense.getUsername()), False, TEXT_SPEED),
+                                  (self.center(balance), False, TEXT_SPEED),]
                else:
                else:
-                       logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
-               return info.pw_name
-       else:
-               logging.info('refused pin for uid %d'%(uid))
-               return None
-
-"""
-In here just for fun.
-"""
-def cookie(v):
-       seed(time())
-       messages = ['  WASSUP! ', 'PINK FISH ', ' SECRETS ', '  ESKIMO  ', ' FORTUNES ', 'MORE MONEY']
-       choice = int(random()*len(messages))
-       msg = messages[choice]
-       left = range(len(msg))
-       for i in range(len(msg)):
-               if msg[i] == ' ': left.remove(i)
-       reveal = 1
-       while left:
-               s = ''
-               for i in range(0, len(msg)):
-                       if i in left:
-                               if reveal == 0:
-                                       left.remove(i)
-                                       s += msg[i]
+                       msg = []
+               choices = ' '*10+'CHOICES: '
+
+               # Show what is in the coke machine
+               # Need to update this so it uses the abstracted system
+               cokes = []
+               for i in ['08', '18', '28', '38', '48', '58', '68']:
+                       cokes.append((i, self.dispense.getItemInfo(i)))
+
+               for c in cokes:
+                       if c[1][0] == 'dead':
+                               continue
+                       choices += '%s-(%sc)-%s8 '%(c[1][0], c[1][1], c[0])
+
+               # Show the final few options
+               choices += '55-DOOR '
+               choices += 'OR ANOTHER SNACK. '
+               choices += '99 TO READ AGAIN. '
+               choices += 'CHOICE?   '
+               msg.append((choices, False, None))
+               # Send it to the display
+               mk.set_messages(msg)
+
+       """
+       In here just for fun.
+       """
+       def cookie(self):
+               seed(time())
+               messages = ['  WASSUP! ', 'PINK FISH ', ' SECRETS ', '  ESKIMO  ', ' FORTUNES ', 'MORE MONEY']
+               choice = int(random()*len(messages))
+               msg = messages[choice]
+               left = range(len(msg))
+               for i in range(len(msg)):
+                       if msg[i] == ' ': left.remove(i)
+               reveal = 1
+               while left:
+                       s = ''
+                       for i in range(0, len(msg)):
+                               if i in left:
+                                       if reveal == 0:
+                                               left.remove(i)
+                                               s += msg[i]
+                                       else:
+                                               s += chr(int(random()*26)+ord('A'))
+                                       reveal += 1
+                                       reveal %= 17
                                else:
                                else:
-                                       s += chr(int(random()*26)+ord('A'))
-                               reveal += 1
-                               reveal %= 17
-                       else:
-                               s += msg[i]
-               v.display(s)
-
-"""
-Format text so it will appear centered on the screen.
-"""
-def center(str):
-       LEN = 10
-       return ' '*((LEN-len(str))/2)+str
-
-"""
-Configure the things that will appear on screen whil the machine is idling.
-"""
-def setup_idlers(v):
-       global idlers, idler
-       idlers = [
-               #
-               GrayIdler(v),
-               GrayIdler(v,one="*",zero="-"),
-               GrayIdler(v,one="/",zero="\\"),
-               GrayIdler(v,one="X",zero="O"),
-               GrayIdler(v,one="*",zero="-",reorder=1),
-               GrayIdler(v,one="/",zero="\\",reorder=1),
-               GrayIdler(v,one="X",zero="O",reorder=1),
-               #
-               ClockIdler(v),
-               ClockIdler(v),
-               ClockIdler(v),
-               #
-               StringIdler(v), # Hello Cruel World
-               StringIdler(v, text="Kill 'em all", repeat=False),
-               StringIdler(v, text=CREDITS),
-               StringIdler(v, text=str(math.pi) + "            "),
-               StringIdler(v, text=str(math.e) + "            "),
-               StringIdler(v, text="    I want some pizza - please call Broadway Pizza on +61 8 9389 8500 - and order as Quinn - I am getting really hungry", repeat=False),
-               # "Hello World" in brainfuck
-               StringIdler(v, text=">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++."),
-               #
-               TrainIdler(v),
-               #
-               FileIdler(v, '/usr/share/common-licenses/GPL-2',affinity=2),
-               #
-               PipeIdler(v, "/usr/bin/getent", "passwd"),
-               FortuneIdler(v,affinity=20),
-               ]
-       disabled = [
-               ]
-
-"""
-Go back to the default idler.
-"""
-def reset_idler(v, vstatus, t = None):
-       global idlers, idler
-       idler = GreetingIdler(v, t)
-       vstatus.time_of_next_idlestep = time()+idler.next()
-       vstatus.time_of_next_idler = None
-       vstatus.time_to_autologout = None
-       vstatus.change_state(STATE_IDLE, 1)
-
-"""
-Change to a random idler.
-"""
-def choose_idler():
-       global idlers, idler
-
-       # Implementation of the King Of the Hill algorithm from;
-       # http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
-
-       #def weighted_choice_king(weights):
-       #       total = 0
-       #       winner = 0
-       #       for i, w in enumerate(weights):
-       #               total += w
-       #               if random.random() * total < w:
-       #                       winner = i
-       #       return winner
-       #       
-
-       total = 0
-       winner = None
-       
-       for choice in idlers:
-               weight = choice.affinity()
-               total += weight
-               if random() * total < weight:
-                       winner = choice
-
-       idler = winner
-
-       if idler:
-               idler.reset()
-"""
-Run every step while the machine is idling.
-"""
-def idle_step(vstatus):
-       global idler
-       if idler.finished():
-               choose_idler()
-               vstatus.time_of_next_idler = time() + 30
-       nextidle = idler.next()
-       if nextidle is None:
-               nextidle = IDLE_SPEED
-       vstatus.time_of_next_idlestep = time()+nextidle
-
-"""
-These next two events trigger no response in the code.
-"""
-def handle_tick_event(event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-def handle_switch_event(event, params, v, vstatus):
-       # don't care right now.
-       pass
-
-"""
-Don't do anything for this event.
-"""
-def do_nothing(state, event, params, v, vstatus):
-       print "doing nothing (s,e,p)", state, " ", event, " ", params
-       pass
+                                       s += msg[i]
+                       self.v.display(s)
+
+       """
+       Format text so it will appear centered on the screen.
+       """
+       def center(self, str):
+               LEN = 10
+               return ' '*((LEN-len(str))/2)+str
+
+       """
+       Configure the things that will appear on screen whil the machine is idling.
+       """
+       def setup_idlers(self):
+               self.idlers = [
+                       #
+                       GrayIdler(self.v),
+                       GrayIdler(self.v,one="*",zero="-"),
+                       GrayIdler(self.v,one="/",zero="\\"),
+                       GrayIdler(self.v,one="X",zero="O"),
+                       GrayIdler(self.v,one="*",zero="-",reorder=1),
+                       GrayIdler(self.v,one="/",zero="\\",reorder=1),
+                       GrayIdler(self.v,one="X",zero="O",reorder=1),
+                       #
+                       ClockIdler(self.v),
+                       ClockIdler(self.v),
+                       ClockIdler(self.v),
+                       #
+                       StringIdler(self.v), # Hello Cruel World
+                       StringIdler(self.v, text="Kill 'em all", repeat=False),
+                       StringIdler(self.v, text=CREDITS),
+                       StringIdler(self.v, text=str(math.pi) + "            "),
+                       StringIdler(self.v, text=str(math.e) + "            "),
+                       StringIdler(self.v, text="    I want some pizza - please call Broadway Pizza on +61 8 9389 8500 - and order as Quinn - I am getting really hungry", repeat=False),
+                       # "Hello World" in brainfuck
+                       StringIdler(self.v, text=">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++."),
+                       #
+                       TrainIdler(self.v),
+                       #
+                       FileIdler(self.v, '/usr/share/common-licenses/GPL-2',affinity=2),
+                       #
+                       PipeIdler(self.v, "/usr/bin/getent", "passwd"),
+                       FortuneIdler(self.v,affinity=20),
+                       ]
+               disabled = [
+                       ]
+
+       """
+       Go back to the default idler.
+       """
+       def reset_idler(self, t = None):
+               self.idler = GreetingIdler(self.v, t)
+               self.vstatus.time_of_next_idlestep = time()+self.idler.next()
+               self.vstatus.time_of_next_idler = None
+               self.vstatus.time_to_autologout = None
+               self.vstatus.change_state(STATE_IDLE, 1)
+
+       """
+       Change to a random idler.
+       """
+       def choose_idler(self):
+
+               # Implementation of the King Of the Hill algorithm from;
+               # http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
+
+               #def weighted_choice_king(weights):
+               #       total = 0
+               #       winner = 0
+               #       for i, w in enumerate(weights):
+               #               total += w
+               #               if random.random() * total < w:
+               #                       winner = i
+               #       return winner
+               #       
+
+               total = 0
+               winner = None
+               
+               for choice in self.idlers:
+                       weight = choice.affinity()
+                       total += weight
+                       if random() * total < weight:
+                               winner = choice
+
+               self.idler = winner
+
+               if self.idler:
+                       self.idler.reset()
+       """
+       Run every step while the machine is idling.
+       """
+       def idle_step(self):
+               if self.idler.finished():
+                       self.choose_idler()
+                       self.vstatus.time_of_next_idler = time() + 30
+               nextidle = self.idler.next()
+               if nextidle is None:
+                       nextidle = IDLE_SPEED
+               self.vstatus.time_of_next_idlestep = time()+nextidle
+
+       """
+       These next two events trigger no response in the code.
+       """
+       def handle_tick_event(self, event, params):
+               # don't care right now.
+               pass
 
 
-"""
-These next few entrie tell us to do nothing while we are idling
-"""
-def handle_getting_uid_idle(state, event, params, v, vstatus):
-       # don't care right now.
-       pass
+       def handle_switch_event(self, event, params):
+               # don't care right now.
+               pass
 
 
-def handle_getting_pin_idle(state, event, params, v, vstatus):
-       # don't care right now.
-       pass
+       """
+       Don't do anything for this event.
+       """
+       def do_nothing(self, event, params):
+               print "doing nothing (s,e,p)", state, " ", event, " ", params
+               pass
 
 
-"""
-While logged in and waiting for user input, slowly get closer to logging out.
-"""
-def handle_get_selection_idle(state, event, params, v, vstatus):
-       global _last_card_id
-       # don't care right now.
-       ###
-       ### State logging out ..
-       if vstatus.time_to_autologout != None:
-               time_left = vstatus.time_to_autologout - time()
-               if time_left < 6 and (vstatus.last_timeout_refresh is None or vstatus.last_timeout_refresh > time_left):
-                       vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
-                       vstatus.last_timeout_refresh = int(time_left)
-                       vstatus.cur_selection = ''
-
-       if vstatus.time_to_autologout != None and vstatus.time_to_autologout - time() <= 0:
-               vstatus.time_to_autologout = None
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               vstatus.cur_selection = ''
-               _last_card_id = -1
-               reset_idler(v, vstatus)
-
-       ### State fully logged out ... reset variables
-       if vstatus.time_to_autologout and not vstatus.mk.done(): 
-               vstatus.time_to_autologout = None
-       if vstatus.cur_user == '' and vstatus.time_to_autologout: 
-               vstatus.time_to_autologout = None
-       
-       ### State logged in
-       if len(vstatus.cur_pin) == PIN_LENGTH and vstatus.mk.done() and vstatus.time_to_autologout == None:
-               # start autologout
-               vstatus.time_to_autologout = time() + 15
-               vstatus.last_timeout_refresh = None
+       """
+       These next few entrie tell us to do nothing while we are idling
+       """
+       def handle_getting_uid_idle(self, event, params):
+               # don't care right now.
+               pass
 
 
-       ## FIXME - this may need to be elsewhere.....
-       # need to check
-       vstatus.mk.update_display()
+       def handle_getting_pin_idle(self, event, params):
+               # don't care right now.
+               pass
 
 
-"""
-Triggered on user input while logged in.
-"""
-def handle_get_selection_key(state, event, params, v, vstatus):
-       global _last_card_id
-       key = params
-       if len(vstatus.cur_selection) == 0:
-               if key == 11:
-                       vstatus.cur_pin = ''
-                       vstatus.cur_user = ''
-                       vstatus.cur_selection = ''
-                       _last_card_id = -1
-                       vstatus.mk.set_messages([(center('BYE!'), False, 1.5)])
-                       reset_idler(v, vstatus, 2)
-                       return
-               vstatus.cur_selection += chr(key + ord('0'))
-               vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
-               vstatus.time_to_autologout = None
-       elif len(vstatus.cur_selection) == 1:
-               if key == 11:
-                       vstatus.cur_selection = ''
-                       vstatus.time_to_autologout = None
-                       scroll_options(vstatus.username, vstatus.mk)
+       """
+       While logged in and waiting for user input, slowly get closer to logging out.
+       """
+       def handle_get_selection_idle(self, event, params):
+               # don't care right now.
+               ###
+               ### State logging out ..
+               if self.vstatus.time_to_autologout != None:
+                       time_left = self.vstatus.time_to_autologout - time()
+                       if time_left < 6 and (self.vstatus.last_timeout_refresh is None or self.vstatus.last_timeout_refresh > time_left):
+                               self.vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
+                               self.vstatus.last_timeout_refresh = int(time_left)
+                               self.vstatus.cur_selection = ''
+
+               # Login timed out: Log out the current user.
+               if self.vstatus.time_to_autologout != None and self.vstatus.time_to_autologout - time() <= 0:
+                       self.vstatus.time_to_autologout = None
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+                       self.vstatus.cur_selection = ''
+                       self._last_card_id = -1
+                       self.dispense.logOut()
+                       self.reset_idler()
+
+               ### State fully logged out ... reset variables
+               if self.vstatus.time_to_autologout and not self.vstatus.mk.done(): 
+                       self.vstatus.time_to_autologout = None
+               if self.vstatus.cur_user == '' and self.vstatus.time_to_autologout: 
+                       self.vstatus.time_to_autologout = None
+               
+               ### State logged in
+               if len(self.vstatus.cur_pin) == PIN_LENGTH and self.vstatus.mk.done() and self.vstatus.time_to_autologout == None:
+                       # start autologout
+                       self.vstatus.time_to_autologout = time() + 15
+                       self.vstatus.last_timeout_refresh = None
+
+               ## FIXME - this may need to be elsewhere.....
+               # need to check
+               self.vstatus.mk.update_display()
+
+       """
+       Triggered on user input while logged in.
+       """
+       def handle_get_selection_key(self, event, params):
+               key = params
+               if len(self.vstatus.cur_selection) == 0:
+                       if key == 11:
+                               self.vstatus.cur_pin = ''
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_selection = ''
+                               self._last_card_id = -1
+                               self.dispense.logOut()
+                               self.vstatus.mk.set_messages([(self.center('BYE!'), False, 1.5)])
+                               self.reset_idler(2)
+                               return
+                       self.vstatus.cur_selection += chr(key + ord('0'))
+                       self.vstatus.mk.set_message('SELECT: '+self.vstatus.cur_selection)
+                       self.vstatus.time_to_autologout = None
+               elif len(self.vstatus.cur_selection) == 1:
+                       if key == 11:
+                               self.vstatus.cur_selection = ''
+                               self.vstatus.time_to_autologout = None
+                               self.dispense.logOut()
+                               self.scroll_options(self.vstatus.username, self.vstatus.mk)
+                               return
+                       else:
+                               self.vstatus.cur_selection += chr(key + ord('0'))
+                               if self.dispense.isLoggedIn():
+                                       self.make_selection()
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.time_to_autologout = time() + 8
+                                       self.vstatus.last_timeout_refresh = None
+                               else:
+                                       # Price check mode.
+                                       (name,price) = self.dispense.getItemInfo(self.vstatus.cur_selection)
+                                       dollarprice = "$%.2f" % ( price / 100.0 )
+                                       self.v.display( self.vstatus.cur_selection+' - %s'%dollarprice)
+
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.time_to_autologout = None
+                                       self.vstatus.last_timeout_refresh = None
+
+       """
+       Triggered when the user has entered the id of something they would like to purchase.
+       """
+       def make_selection(self):
+               logging.debug('Dispense item "%s"' % (self.vstatus.cur_selection,))
+               # should use sudo here
+               if self.vstatus.cur_selection == '55':
+                       self.vstatus.mk.set_message('OPENSESAME')
+                       logging.info('dispensing a door for %s'%self.vstatus.username)
+                       if geteuid() == 0:
+                               ret = os.system('dispense -u "%s" door'%self.vstatus.username)
+                       else:
+                               ret = os.system('dispense door')
+                       if ret == 0:
+                               logging.info('door opened')
+                               self.vstatus.mk.set_message(self.center('DOOR OPEN'))
+                       else:
+                               logging.warning('user %s tried to dispense a bad door'%self.vstatus.username)
+                               self.vstatus.mk.set_message(self.center('BAD DOOR'))
+                       sleep(1)
+               elif self.vstatus.cur_selection == '81':
+                       self.cookie()
+               elif self.vstatus.cur_selection == '99':
+                       self.scroll_options(self.vstatus.username, self.vstatus.mk)
+                       self.vstatus.cur_selection = ''
                        return
                        return
-               else:
-                       vstatus.cur_selection += chr(key + ord('0'))
-                       if vstatus.cur_user:
-                               make_selection(v,vstatus)
-                               vstatus.cur_selection = ''
-                               vstatus.time_to_autologout = time() + 8
-                               vstatus.last_timeout_refresh = None
+               elif self.vstatus.cur_selection[1] == '8':
+                       self.v.display('GOT DRINK?')
+                       if ((os.system('dispense -u "%s" coke:%s'%(self.vstatus.username, self.vstatus.cur_selection[0])) >> 8) != 0):
+                               self.v.display('SEEMS NOT')
                        else:
                        else:
-                               # Price check mode.
-                               price_check(v,vstatus)
-                               vstatus.cur_selection = ''
-                               vstatus.time_to_autologout = None
-                               vstatus.last_timeout_refresh = None
-
-"""
-Triggered when the user has entered the id of something they would like to purchase.
-"""
-def make_selection(v, vstatus):
-       # should use sudo here
-       if vstatus.cur_selection == '55':
-               vstatus.mk.set_message('OPENSESAME')
-               logging.info('dispensing a door for %s'%vstatus.username)
-               if geteuid() == 0:
-                       ret = os.system('dispense -u "%s" door'%vstatus.username)
+                               self.v.display('GOT DRINK!')
                else:
                else:
-                       ret = os.system('dispense door')
-               if ret == 0:
-                       logging.info('door opened')
-                       vstatus.mk.set_message(center('DOOR OPEN'))
-               else:
-                       logging.warning('user %s tried to dispense a bad door'%vstatus.username)
-                       vstatus.mk.set_message(center('BAD DOOR'))
-               sleep(1)
-       elif vstatus.cur_selection == '81':
-               cookie(v)
-       elif vstatus.cur_selection == '99':
-               scroll_options(vstatus.username, vstatus.mk)
-               vstatus.cur_selection = ''
-               return
-       elif vstatus.cur_selection[1] == '8':
-               v.display('GOT DRINK?')
-               if ((os.system('dispense -u "%s" coke:%s'%(vstatus.username, vstatus.cur_selection[0])) >> 8) != 0):
-                       v.display('SEEMS NOT')
-               else:
-                       v.display('GOT DRINK!')
-       else:
-               # first see if it's a named slot
-               try:
-                       price, shortname, name = get_snack( vstatus.cur_selection )
-               except:
-                       price, shortname, name = get_snack( '--' )
-               dollarprice = "$%.2f" % ( price / 100.0 )
-               v.display(vstatus.cur_selection+' - %s'%dollarprice)
-               exitcode = os.system('dispense -u "%s" snack:%s'%(vstatus.username, vstatus.cur_selection)) >> 8
-               if (exitcode == 0):
-                       # magic dispense syslog service
-                       syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "vended %s (slot %s) for %s" % (name, vstatus.cur_selection, vstatus.username))
-                       (worked, code, string) = v.vend(vstatus.cur_selection)
-                       if worked:
-                               v.display('THANK YOU')
+                       # first see if it's a named slot
+                       try:
+                               price, shortname, name = get_snack( self.vstatus.cur_selection )
+                       except:
+                               price, shortname, name = get_snack( '--' )
+                       dollarprice = "$%.2f" % ( price / 100.0 )
+                       self.v.display(self.vstatus.cur_selection+' - %s'%dollarprice)
+                       exitcode = os.system('dispense -u "%s" snack:%s'%(self.vstatus.username, self.vstatus.cur_selection)) >> 8
+                       if (exitcode == 0):
+                               # magic dispense syslog service
+                               (worked, code, string) = self.v.vend(self.vstatus.cur_selection)
+                               if worked:
+                                       self.v.display('THANK YOU')
+                                       syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "vended %s (slot %s) for %s" % (name, self.vstatus.cur_selection, self.vstatus.username))
+                               else:
+                                       print "Vend Failed:", code, string
+                                       syslog.syslog(syslog.LOG_WARNING | syslog.LOG_LOCAL4, "vending %s (slot %s) for %s FAILED %r %r" % (name, self.vstatus.cur_selection, self.vstatus.username, code, string))
+                                       self.v.display('VEND FAIL')
+                       elif (exitcode == 5):   # RV_BALANCE
+                               self.v.display('NO MONEY?')
+                       elif (exitcode == 4):   # RV_ARGUMENTS (zero give causes arguments)
+                               self.v.display('EMPTY SLOT')
+                       elif (exitcode == 1):   # RV_BADITEM (Dead slot)
+                               self.v.display('EMPTY SLOT')
                        else:
                        else:
-                               print "Vend Failed:", code, string
-                               v.display('VEND FAIL')
-               elif (exitcode == 5):   # RV_BALANCE
-                       v.display('NO MONEY?')
-               elif (exitcode == 4):   # RV_ARGUMENTS (zero give causes arguments)
-                       v.display('EMPTY SLOT')
-               elif (exitcode == 1):   # RV_BADITEM (Dead slot)
-                       v.display('EMPTY SLOT')
-               else:
-                       syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, vstatus.cur_selection, vstatus.username, exitcode))
-                       v.display('UNK ERROR')
-       sleep(1)
-
-"""
-Find the price of an item.
-"""
-def price_check(v, vstatus):
-       if vstatus.cur_selection[1] == '8':
-               args = ('dispense', 'iteminfo', 'coke:' + vstatus.cur_selection[0])
-               info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
-               dollarprice = re.match("\s*[a-z]+:\d+\s+(\d+\.\d\d)\s+([^\n]+)", info).group(1)
-       else:
-               # first see if it's a named slot
-               try:
-                       price, shortname, name = get_snack( vstatus.cur_selection )
-               except:
-                       price, shortname, name = get_snack( '--' )
-               dollarprice = "$%.2f" % ( price / 100.0 )
-       v.display(vstatus.cur_selection+' - %s'%dollarprice)
-
-"""
-Triggered when the user presses a button while entering their pin.
-"""
-def handle_getting_pin_key(state, event, params, v, vstatus):
-       #print "handle_getting_pin_key (s,e,p)", state, " ", event, " ", params
-       key = params
-       if len(vstatus.cur_pin) < PIN_LENGTH:
-               if key == 11:
-                       if vstatus.cur_pin == '':
-                               vstatus.cur_user = ''
-                               reset_idler(v, vstatus)
+                               syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, self.vstatus.cur_selection, self.vstatus.username, exitcode))
+                               self.v.display('UNK ERROR')
+               sleep(1)
 
 
+       """
+       Triggered when the user presses a button while entering their pin.
+       """
+       def handle_getting_pin_key(self, event, params):
+               key = params
+               if len(self.vstatus.cur_pin) < PIN_LENGTH:
+                       if key == 11:
+                               if self.vstatus.cur_pin == '':
+                                       self.vstatus.cur_user = ''
+                                       self.dispense.logOut()
+                                       self.reset_idler()
+
+                                       return
+                               self.vstatus.cur_pin = ''
+                               self.vstatus.mk.set_message('PIN: ')
                                return
                                return
-                       vstatus.cur_pin = ''
-                       vstatus.mk.set_message('PIN: ')
+                       self.vstatus.cur_pin += chr(key + ord('0'))
+                       self.vstatus.mk.set_message('PIN: '+'X'*len(self.vstatus.cur_pin))
+                       if len(self.vstatus.cur_pin) == PIN_LENGTH:
+                               if self.dispense.authUserIdPin(self.vstatus.cur_user, self.vstatus.cur_pin):
+                                       self.vstatus.username = self.dispense.getUsername()
+                                       self.v.beep(0, False)
+                                       self.vstatus.cur_selection = ''
+                                       self.vstatus.change_state(STATE_GET_SELECTION)
+                                       self.scroll_options(self.vstatus.username, self.vstatus.mk, True)
+                                       return
+                               else:
+                                       self.v.beep(40, False)
+                                       self.vstatus.mk.set_messages(
+                                               [(self.center('BAD PIN'), False, 1.0),
+                                                (self.center('SORRY'), False, 0.5)])
+                                       self.vstatus.cur_user = ''
+                                       self.vstatus.cur_pin = ''
+                               
+                                       self.reset_idler(2)
+
+                                       return
+
+       """
+       Triggered when the user presses a button while entering their user id.
+       """
+       def handle_getting_uid_key(self, event, params):
+               key = params
+               # complicated key handling here:
+
+               if len(self.vstatus.cur_user) == 0 and key == 9:
+                       self.vstatus.cur_selection = ''
+                       self.vstatus.time_to_autologout = None
+                       self.vstatus.mk.set_message('PRICECHECK')
+                       sleep(0.5)
+                       self.scroll_options('', self.vstatus.mk)
+                       self.vstatus.change_state(STATE_GET_SELECTION)
                        return
                        return
-               vstatus.cur_pin += chr(key + ord('0'))
-               vstatus.mk.set_message('PIN: '+'X'*len(vstatus.cur_pin))
-               if len(vstatus.cur_pin) == PIN_LENGTH:
-                       vstatus.username = verify_user_pin(int(vstatus.cur_user), int(vstatus.cur_pin))
-                       if vstatus.username:
-                               v.beep(0, False)
-                               vstatus.cur_selection = ''
-                               vstatus.change_state(STATE_GET_SELECTION)
-                               scroll_options(vstatus.username, vstatus.mk, True)
-                               return
-                       else:
-                               v.beep(40, False)
-                               vstatus.mk.set_messages(
-                                       [(center('BAD PIN'), False, 1.0),
-                                        (center('SORRY'), False, 0.5)])
-                               vstatus.cur_user = ''
-                               vstatus.cur_pin = ''
-                       
-                               reset_idler(v, vstatus, 2)
 
 
+               if len(self.vstatus.cur_user) <8:
+                       if key == 11:
+                               self.vstatus.cur_user = ''
+                               self.dispense.logOut()
+
+                               self.reset_idler()
                                return
                                return
+                       self.vstatus.cur_user += chr(key + ord('0'))
+                       #logging.info('dob: '+vstatus.cur_user)
+                       if len(self.vstatus.cur_user) > 5:
+                               self.vstatus.mk.set_message('>'+self.vstatus.cur_user)
+                       else:
+                               self.vstatus.mk.set_message('UID: '+self.vstatus.cur_user)
+               
+               if len(self.vstatus.cur_user) == 5:
+                       uid = int(self.vstatus.cur_user)
 
 
-"""
-Triggered when the user presses a button while entering their user id.
-"""
-def handle_getting_uid_key(state, event, params, v, vstatus):
-       #print "handle_getting_uid_key (s,e,p)", state, " ", event, " ", params
-       key = params
-
-       # complicated key handling here:
-
-       if len(vstatus.cur_user) == 0 and key == 9:
-               vstatus.cur_selection = ''
-               vstatus.time_to_autologout = None
-               vstatus.mk.set_message('PRICECHECK')
-               sleep(0.5)
-               scroll_options('', vstatus.mk)
-               vstatus.change_state(STATE_GET_SELECTION)
-               return
-
-       if len(vstatus.cur_user) <8:
-               if key == 11:
-                       vstatus.cur_user = ''
+                       if uid == 0:
+                               logging.info('user '+self.vstatus.cur_user+' has a bad PIN')
+                               pfalken="""
+       CARRIER DETECTED
 
 
-                       reset_idler(v, vstatus)
-                       return
-               vstatus.cur_user += chr(key + ord('0'))
-               #logging.info('dob: '+vstatus.cur_user)
-               if len(vstatus.cur_user) > 5:
-                       vstatus.mk.set_message('>'+vstatus.cur_user)
-               else:
-                       vstatus.mk.set_message('UID: '+vstatus.cur_user)
-       
-       if len(vstatus.cur_user) == 5:
-               uid = int(vstatus.cur_user)
+       CONNECT 128000
 
 
-               if uid == 0:
-                       logging.info('user '+vstatus.cur_user+' has a bad PIN')
-                       pfalken="""
-CARRIER DETECTED
+       Welcome to Picklevision Sytems, Sunnyvale, CA
 
 
-CONNECT 128000
+       Greetings Professor Falken.
 
 
-Welcome to Picklevision Sytems, Sunnyvale, CA
 
 
-Greetings Professor Falken.
 
 
 
 
+       Shall we play a game?
 
 
 
 
-Shall we play a game?
+       Please choose from the following menu:
 
 
+       1. Tic-Tac-Toe
+       2. Chess
+       3. Checkers
+       4. Backgammon
+       5. Poker
+       6. Toxic and Biochemical Warfare
+       7. Global Thermonuclear War
 
 
-Please choose from the following menu:
+       7 [ENTER]
 
 
-1. Tic-Tac-Toe
-2. Chess
-3. Checkers
-4. Backgammon
-5. Poker
-6. Toxic and Biochemical Warfare
-7. Global Thermonuclear War
+       Wouldn't you prefer a nice game of chess?
 
 
-7 [ENTER]
+       """.replace('\n','    ')
+                               self.vstatus.mk.set_messages([(pfalken, False, 10)])
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_pin = ''
+                               
+                               self.reset_idler(10)
 
 
-Wouldn't you prefer a nice game of chess?
+                               return
 
 
-""".replace('\n','    ')
-                       vstatus.mk.set_messages([(pfalken, False, 10)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 10)
+                       # TODO Fix this up do we can check before logging in
+                       """
+                       if self.dispense.isDisabled():
+                               logging.info('user '+self.vstatus.cur_user+' is disabled')
+                               self.vstatus.mk.set_messages(
+                                       [(' '*11+'ACCOUNT DISABLED'+' '*11, False, 3)])
+                               self.vstatus.cur_user = ''
+                               self.vstatus.cur_pin = ''
+                               
+                               self.reset_idler(3)
+                               return
+                       """
 
 
+                       self.vstatus.cur_pin = ''
+                       self.vstatus.mk.set_message('PIN: ')
+                       logging.info('need pin for user %s'%self.vstatus.cur_user)
+                       self.vstatus.change_state(STATE_GETTING_PIN)
                        return
 
                        return
 
-               if not has_good_pin(uid):
-                       logging.info('user '+vstatus.cur_user+' has a bad PIN')
-                       vstatus.mk.set_messages(
-                               [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 3)
-
+       """
+       Triggered when a key is pressed and the machine is idling.
+       """
+       def handle_idle_key(self, event, params):
+               key = params
+               if key == 11:
+                       self.vstatus.cur_user = ''
+                       self.dispense.logOut()
+                       self.reset_idler()
                        return
                
                        return
                
-               if acct_is_disabled():
-                       logging.info('user '+vstatus.cur_user+' is disabled')
-                       vstatus.mk.set_messages(
-                               [(' '*11+'ACCOUNT DISABLED'+' '*11, False, 3)])
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       
-                       reset_idler(v, vstatus, 3)
-                       return
-
-
-               vstatus.cur_pin = ''
-               vstatus.mk.set_message('PIN: ')
-               logging.info('need pin for user %s'%vstatus.cur_user)
-               vstatus.change_state(STATE_GETTING_PIN)
-               return
-
-"""
-Triggered when a key is pressed and the machine is idling.
-"""
-def handle_idle_key(state, event, params, v, vstatus):
-       #print "handle_idle_key (s,e,p)", state, " ", event, " ", params
-
-       key = params
-
-       if key == 11:
-               vstatus.cur_user = ''
-               reset_idler(v, vstatus)
-               return
-       
-       vstatus.change_state(STATE_GETTING_UID)
-       run_handler(event, key, v, vstatus)
-
-"""
-What to do when there is nothing to do.
-"""
-def handle_idle_tick(state, event, params, v, vstatus):
-       ### State idling
-       if vstatus.mk.done():
-               idle_step(vstatus)
-
-       if vstatus.time_of_next_idler and time() > vstatus.time_of_next_idler:
-               vstatus.time_of_next_idler = time() + 30
-               choose_idler()
-       
-       ###
-
-       vstatus.mk.update_display()
-
-       vstatus.change_state(STATE_GRANDFATHER_CLOCK)
-       run_handler(event, params, v, vstatus)
-       sleep(0.05)
-
-"""
-Manages the beeps for the grandfather clock
-"""
-def beep_on(when, before=0):
-       start = int(when - before)
-       end = int(when)
-       now = int(time())
-
-       if now >= start and now <= end:
-               return 1
-       return 0
-
-def handle_idle_grandfather_tick(state, event, params, v, vstatus):
-       ### check for interesting times
-       now = localtime()
-
-       quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
-       halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
-       threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
-       fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
-
-       hourfromnow = localtime(time() + 3600)
-       
-       #onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
-       onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
-               0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
-
-       ## check for X seconds to the hour
-       ## if case, update counter to 2
-       if beep_on(onthehour,15) \
-               or beep_on(halfhour,0) \
-               or beep_on(quarterhour,0) \
-               or beep_on(threequarterhour,0) \
-               or beep_on(fivetothehour,0):
-               vstatus.change_state(STATE_GRANDFATHER_CLOCK,2)
-               run_handler(event, params, v, vstatus)
-       else:
-               vstatus.change_state(STATE_IDLE)
+               self.vstatus.change_state(STATE_GETTING_UID)
+               self.run_handler(event, params)
+
+       """
+       What to do when there is nothing to do.
+       """
+       def handle_idle_tick(self, event, params):
+               ### State idling
+               if self.vstatus.mk.done():
+                       self.idle_step()
+
+               if self.vstatus.time_of_next_idler and time() > self.vstatus.time_of_next_idler:
+                       self.vstatus.time_of_next_idler = time() + 30
+                       self.choose_idler()
+               
+               ###
 
 
-def handle_grandfather_tick(state, event, params, v, vstatus):
-       go_idle = 1
+               self.vstatus.mk.update_display()
 
 
-       msg = []
-       ### we live in interesting times
-       now = localtime()
+               self.vstatus.change_state(STATE_GRANDFATHER_CLOCK)
+               self.run_handler(event, params)
+               sleep(0.05)
 
 
-       quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
-       halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
-       threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
-       fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
+       """
+       Manages the beeps for the grandfather clock
+       """
+       def beep_on(self, when, before=0):
+               start = int(when - before)
+               end = int(when)
+               now = int(time())
 
 
-       hourfromnow = localtime(time() + 3600)
-       
-#      onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
-       onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
-               0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
+               if now >= start and now <= end:
+                       return 1
+               return 0
 
 
+       def handle_idle_grandfather_tick(self, event, params):
+               ### check for interesting times
+               now = localtime()
 
 
-       #print "when it fashionable to wear a onion on your hip"
+               quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
+               halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
+               threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
+               fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
 
 
-       if beep_on(onthehour,15):
-               go_idle = 0
-               next_hour=((hourfromnow[3] + 11) % 12) + 1
-               if onthehour - time() < next_hour and onthehour - time() > 0:
-                       v.beep(0, False)
+               hourfromnow = localtime(time() + 3600)
+               
+               #onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
+               onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
+                       0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
+
+               ## check for X seconds to the hour
+               ## if case, update counter to 2
+               if self.beep_on(onthehour,15) \
+                       or self.beep_on(halfhour,0) \
+                       or self.beep_on(quarterhour,0) \
+                       or self.beep_on(threequarterhour,0) \
+                       or self.beep_on(fivetothehour,0):
+                       self.vstatus.change_state(STATE_GRANDFATHER_CLOCK,2)
+                       self.run_handler(event, params)
+               else:
+                       self.vstatus.change_state(STATE_IDLE)
 
 
-                       t = int(time())
-                       if (t % 2) == 0:
-                               msg.append(("DING!", False, None))
-                       else:
-                               msg.append(("     DING!", False, None))
-               elif int(onthehour - time()) == 0:
-                       v.beep(255, False)
-                       msg.append(("   BONG!", False, None))
-                       msg.append(("     IT'S "+ str(next_hour) + "O'CLOCK AND ALL IS WELL .....", False, TEXT_SPEED*4))
-       elif beep_on(halfhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" HALFHOUR ", False, 50))
-       elif beep_on(quarterhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" QTR HOUR ", False, 50))
-       elif beep_on(threequarterhour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append((" 3 QTR HR ", False, 50))
-       elif beep_on(fivetothehour,0):
-               go_idle = 0
-               v.beep(0, False)
-               msg.append(("Quick run to your lectures!  Hurry! Hurry!", False, TEXT_SPEED*4))
-       else:
+       def handle_grandfather_tick(self, event, params):
                go_idle = 1
                go_idle = 1
-       
-       ## check for X seconds to the hour
 
 
-       if len(msg):
-               vstatus.mk.set_messages(msg)
-               sleep(1)
-
-       vstatus.mk.update_display()
-       ## if no longer case, return to idle
+               msg = []
+               ### we live in interesting times
+               now = localtime()
 
 
-       ## change idler to be clock
-       if go_idle and vstatus.mk.done():
-               vstatus.change_state(STATE_IDLE,1)
+               quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
+               halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
+               threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
+               fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
 
 
-"""
-What to do when the door is open.
-"""
-def handle_door_idle(state, event, params, v, vstatus):
-       def twiddle(clock,v,wise = 2):
-               if (clock % 4 == 0):
-                       v.display("-FEED  ME-")
-               elif (clock % 4 == 1+wise):
-                       v.display("\\FEED  ME/")
-               elif (clock % 4 == 2):
-                       v.display("-FEED  ME-")
-               elif (clock % 4 == 3-wise):
-                       v.display("/FEED  ME\\")
-
-       # don't care right now.
-       now = int(time())
-
-       if ((now % 60 % 2) == 0):
-               twiddle(now, v)
-       else:
-               twiddle(now, v, wise=0)
-
-"""
-What to do when the door is opened or closed.
-"""
-def handle_door_event(state, event, params, v, vstatus):
-       if params == 0:  #door open
-               vstatus.change_state(STATE_DOOR_OPENING)
-               logging.warning("Entering open door mode")
-               v.display("-FEED  ME-")
-               #door_open_mode(v);
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-       elif params == 1:  #door closed
-               vstatus.change_state(STATE_DOOR_CLOSING)
-               reset_idler(v, vstatus, 3)
-
-               logging.warning('Leaving open door mode')
-               v.display("-YUM YUM!-")
-
-"""
-Triggered when a user swipes their caed, and the machine is logged out.
-"""
-def handle_mifare_event(state, event, params, v, vstatus):
-       global _last_card_id
-       card_id = params
-       # Translate card_id into uid.
-       if card_id == None or card_id == _last_card_id:
-               return
-
-       _last_card_id = card_id
-       
-       try:
-               vstatus.cur_user = get_uid(card_id)
-               logging.info('Mapped card id to uid %s'%vstatus.cur_user)
-               vstatus.username = get_uname(vstatus.cur_user)
-               if acct_is_disabled(vstatus.username):
-                       vstatus.username = '-disabled-'
-       except ValueError:
-               vstatus.username = None
-       if vstatus.username == '-disabled-':
-               v.beep(40, False)
-               vstatus.mk.set_messages(
-                       [(center('ACCT DISABLED'), False, 1.0),
-                        (center('SORRY'), False, 0.5)])
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               vstatus.username = None
-       
-               reset_idler(v, vstatus, 2)
-               return
-       elif vstatus.username:
-               v.beep(0, False)
-               vstatus.cur_selection = ''
-               vstatus.change_state(STATE_GET_SELECTION)
-               scroll_options(vstatus.username, vstatus.mk, True)
-               return
-       else:
-               v.beep(40, False)
-               vstatus.mk.set_messages(
-                       [(center('BAD CARD'), False, 1.0),
-                        (center('SORRY'), False, 0.5)])
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               _last_card_id = -1
-       
-               reset_idler(v, vstatus, 2)
-               return
+               hourfromnow = localtime(time() + 3600)
+               
+       #       onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
+               onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
+                       0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
 
 
-"""
-Triggered when a user swipes their card and the machine is logged in.
-"""
-def handle_mifare_add_user_event(state, event, params, v, vstatus):
-       global _last_card_id
-       card_id = params
 
 
-       # Translate card_id into uid.
-       if card_id == None or card_id == _last_card_id:
-               return
+               #print "when it fashionable to wear a onion on your hip"
 
 
-       _last_card_id = card_id
-       
-       try:
-               if get_uid(card_id) != None:
-                       vstatus.mk.set_messages(
-                               [(center('ALREADY'), False, 0.5),
-                                (center('ENROLLED'), False, 0.5)])
+               if self.beep_on(onthehour,15):
+                       go_idle = 0
+                       next_hour=((hourfromnow[3] + 11) % 12) + 1
+                       if onthehour - time() < next_hour and onthehour - time() > 0:
+                               self.v.beep(0, False)
 
 
-                       # scroll_options(vstatus.username, vstatus.mk)
+                               t = int(time())
+                               if (t % 2) == 0:
+                                       msg.append(("DING!", False, None))
+                               else:
+                                       msg.append(("     DING!", False, None))
+                       elif int(onthehour - time()) == 0:
+                               self.v.beep(255, False)
+                               msg.append(("   BONG!", False, None))
+                               msg.append(("     IT'S "+ str(next_hour) + "O'CLOCK AND ALL IS WELL .....", False, TEXT_SPEED*4))
+               elif self.beep_on(halfhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" HALFHOUR ", False, 50))
+               elif self.beep_on(quarterhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" QTR HOUR ", False, 50))
+               elif self.beep_on(threequarterhour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append((" 3 QTR HR ", False, 50))
+               elif self.beep_on(fivetothehour,0):
+                       go_idle = 0
+                       self.v.beep(0, False)
+                       msg.append(("Quick run to your lectures!  Hurry! Hurry!", False, TEXT_SPEED*4))
+               else:
+                       go_idle = 1
+               
+               ## check for X seconds to the hour
+
+               if len(msg):
+                       self.vstatus.mk.set_messages(msg)
+                       sleep(1)
+
+               self.vstatus.mk.update_display()
+               ## if no longer case, return to idle
+
+               ## change idler to be clock
+               if go_idle and self.vstatus.mk.done():
+                       self.vstatus.change_state(STATE_IDLE,1)
+
+       """
+       What to do when the door is open.
+       """
+       def handle_door_idle(self, event, params):
+               def twiddle(clock,v,wise = 2):
+                       if (clock % 4 == 0):
+                               v.display("-FEED  ME-")
+                       elif (clock % 4 == 1+wise):
+                               v.display("\\FEED  ME/")
+                       elif (clock % 4 == 2):
+                               v.display("-FEED  ME-")
+                       elif (clock % 4 == 3-wise):
+                               v.display("/FEED  ME\\")
+
+               # don't care right now.
+               now = int(time())
+
+               if ((now % 60 % 2) == 0):
+                       twiddle(now, self.v)
+               else:
+                       twiddle(now, self.v, wise=0)
+
+       """
+       What to do when the door is opened or closed.
+       """
+       def handle_door_event(self, event, params):
+               if params == 0:  #door open
+                       self.vstatus.change_state(STATE_DOOR_OPENING)
+                       logging.warning("Entering open door mode")
+                       self.v.display("-FEED  ME-")
+                       #door_open_mode(v);
+                       self.dispense.logOut()
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+               elif params == 1:  #door closed
+                       self.vstatus.change_state(STATE_DOOR_CLOSING)
+                       self.reset_idler(3)
+
+                       logging.warning('Leaving open door mode')
+                       self.v.display("-YUM YUM!-")
+
+       """
+       Triggered when a user swipes their caed, and the machine is logged out.
+       """
+       def handle_mifare_event(self, event, params):
+               card_id = params
+               # Translate card_id into uid.
+               if card_id == None or card_id == self._last_card_id:
                        return
                        return
-       except ValueError:
-               pass
 
 
-       logging.info('Enrolling card %s to uid %s (%s)'%(card_id, vstatus.cur_user, vstatus.username))
-       set_card_id(vstatus.cur_user, card_id)
-       vstatus.mk.set_messages(
-               [(center('CARD'), False, 0.5),
-                (center('ENROLLED'), False, 0.5)])
-
-       # scroll_options(vstatus.username, vstatus.mk)
-
-def return_to_idle(state,event,params,v,vstatus):
-       reset_idler(v, vstatus)
-
-"""
-Maps what to do when the state changes.
-"""
-def create_state_table(vstatus):
-       vstatus.state_table[(STATE_IDLE,TICK,1)] = handle_idle_tick
-       vstatus.state_table[(STATE_IDLE,KEY,1)] = handle_idle_key
-       vstatus.state_table[(STATE_IDLE,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_IDLE,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = handle_door_idle
-       vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_DOOR_OPENING,MIFARE,1)] = do_nothing
-
-       vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = return_to_idle
-       vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_DOOR_CLOSING,MIFARE,1)] = do_nothing
-
-       vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = handle_getting_uid_idle
-       vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = handle_getting_uid_key
-       vstatus.state_table[(STATE_GETTING_UID,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = handle_getting_pin_idle
-       vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = handle_getting_pin_key
-       vstatus.state_table[(STATE_GETTING_PIN,MIFARE,1)] = handle_mifare_event
-
-       vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = handle_get_selection_idle
-       vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = handle_get_selection_key
-       vstatus.state_table[(STATE_GET_SELECTION,MIFARE,1)] = handle_mifare_add_user_event
-
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = handle_idle_grandfather_tick
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = handle_grandfather_tick
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,1)] = handle_door_event
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = handle_door_event
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = do_nothing
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = do_nothing
-       vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = handle_mifare_event
-
-"""
-Get what to do on a state change.
-"""
-def get_state_table_handler(vstatus, state, event, counter):
-       return vstatus.state_table[(state,event,counter)]
-
-def time_to_next_update(vstatus):
-       idle_update = vstatus.time_of_next_idlestep - time()
-       if not vstatus.mk.done() and vstatus.mk.next_update is not None:
-               mk_update = vstatus.mk.next_update - time()
-               if mk_update < idle_update:
-                       idle_update = mk_update
-       return idle_update
-
-def run_forever(rfh, wfh, options, cf):
-       v = VendingMachine(rfh, wfh, USE_MIFARE)
-       vstatus = VendState(v)
-       create_state_table(vstatus)
-
-       logging.debug('PING is ' + str(v.ping()))
-
-       if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword)
-
-       setup_idlers(v)
-       reset_idler(v, vstatus)
+               self._last_card_id = card_id
+               
+               if not self.dispense.authMifareCard(card_id):
+                       self.v.beep(40, False)
+                       self.vstatus.mk.set_messages(
+                               [(self.center('BAD CARD'), False, 1.0),
+                                (self.center('SORRY'), False, 0.5)])
+                       self.vstatus.cur_user = ''
+                       self.vstatus.cur_pin = ''
+                       self._last_card_id = -1
+               
+                       self.reset_idler(2)
+                       return
+               elif self.dispense.isDisabled():
+                       logging.info('Mapped card id to uid %s'%self.dispense.getUsername())
+                       self.v.beep(40, False)
+                       self.vstatus.mk.set_messages(
+                               [(self.center('ACCT DISABLED'), False, 1.0),
+                                (self.center('SORRY'), False, 0.5)])
+                       self.dispense.logOut()
+                       self.reset_idler(2)
+                       return
+               else:
+                       logging.info('Mapped card id to uid %s'%self.dispense.getUsername())
+                       self.vstatus.cur_user = '----'
+                       self.vstatus.username = self.dispense.getUsername()
+                       self.vstatus.cur_selection = ''
+                       self.vstatus.change_state(STATE_GET_SELECTION)
+                       self.scroll_options(self.vstatus.username, self.vstatus.mk, True)
+                       return
 
 
-       while True:
-               if USE_DB:
-                       try:
-                               db.handle_events()
-                       except DispenseDatabaseException, e:
-                               logging.error('Database error: '+str(e))
+       """
+       Triggered when a user swipes their card and the machine is logged in.
+       """
+       def handle_mifare_add_user_event(self, event, params):
+               card_id = params
 
 
-               timeout = time_to_next_update(vstatus)
-               e = v.next_event(timeout)
-               (event, params) = e
+               # Translate card_id into uid.
+               if card_id == None or card_id == self._last_card_id:
+                       return
 
 
-               run_handler(event, params, v, vstatus)
+               self._last_card_id = card_id
 
 
-def run_handler(event, params, v, vstatus):
-       handler = get_state_table_handler(vstatus,vstatus.state,event,vstatus.counter)
-       if handler:
-               handler(vstatus.state, event, params, v, vstatus)
+               if not self.dispense.addCard(card_id):
+                       self.vstatus.mk.set_messages(
+                               [(self.center('ALREADY'), False, 0.5),
+                                (self.center('ENROLLED'), False, 0.5)])
+               else:
+                       self.vstatus.mk.set_messages(
+                               [(self.center('CARD'), False, 0.5),
+                                (self.center('ENROLLED'), False, 0.5)])
+
+       def return_to_idle(self, event, params):
+               self.reset_idler()
+
+       """
+       Maps what to do when the state changes.
+       """
+       def create_state_table(self):
+               self.vstatus.state_table[(STATE_IDLE,TICK,1)] = self.handle_idle_tick
+               self.vstatus.state_table[(STATE_IDLE,KEY,1)] = self.handle_idle_key
+               self.vstatus.state_table[(STATE_IDLE,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_IDLE,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = self.handle_door_idle
+               self.vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_DOOR_OPENING,MIFARE,1)] = self.do_nothing
+
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = self.return_to_idle
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_DOOR_CLOSING,MIFARE,1)] = self.do_nothing
+
+               self.vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = self.handle_getting_uid_idle
+               self.vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = self.handle_getting_uid_key
+               self.vstatus.state_table[(STATE_GETTING_UID,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = self.handle_getting_pin_idle
+               self.vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = self.handle_getting_pin_key
+               self.vstatus.state_table[(STATE_GETTING_PIN,MIFARE,1)] = self.handle_mifare_event
+
+               self.vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = self.handle_get_selection_idle
+               self.vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = self.handle_get_selection_key
+               self.vstatus.state_table[(STATE_GET_SELECTION,MIFARE,1)] = self.handle_mifare_add_user_event
+
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = self.handle_idle_grandfather_tick
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = self.handle_grandfather_tick
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,1)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = self.handle_door_event
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = self.do_nothing
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = self.do_nothing
+               self.vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = self.handle_mifare_event
+
+       """
+       Get what to do on a state change.
+       """
+       def get_state_table_handler(self, state, event, counter):
+               return self.vstatus.state_table[(state,event,counter)]
+
+       def time_to_next_update(self):
+               idle_update = self.vstatus.time_of_next_idlestep - time()
+               if not self.vstatus.mk.done() and self.vstatus.mk.next_update is not None:
+                       mk_update = self.vstatus.mk.next_update - time()
+                       if mk_update < idle_update:
+                               idle_update = mk_update
+               return idle_update
+
+       def run_forever(self, rfh, wfh, options, cf):
+               self.v = VendingMachine(rfh, wfh, USE_MIFARE)
+               self.dispense = Dispense()
+               self.vstatus = VendState(self.v)
+               self.create_state_table()
+
+               logging.debug('PING is ' + str(self.v.ping()))
+
+               self.setup_idlers()
+               self.reset_idler()
+
+               while True:
+                       timeout = self.time_to_next_update()
+                       (event, params) = self.v.next_event(timeout)
+                       self.run_handler(event, params)
+
+       def run_handler(self, event, params):
+               handler = self.get_state_table_handler(self.vstatus.state,event,self.vstatus.counter)
+               if handler:
+                       handler(event, params)
 
 """
 Connect to the machine.
 
 """
 Connect to the machine.
@@ -1125,7 +953,7 @@ def parse_args():
 
        op = OptionParser(usage="%prog [OPTION]...")
        op.add_option('-f', '--config-file', default='/etc/dispense2/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
 
        op = OptionParser(usage="%prog [OPTION]...")
        op.add_option('-f', '--config-file', default='/etc/dispense2/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
-       op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port')
+       op.add_option('--serial', action='store_true', default=False, dest='use_serial', help='use the serial port')
        op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
        op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
        op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)')
        op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
        op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
        op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)')
@@ -1136,6 +964,7 @@ def parse_args():
        op.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='spit out lots of debug output')
        op.add_option('-q', '--quiet', dest='quiet', action='store_true', default=False, help='only report errors')
        op.add_option('--pid-file', dest='pid_file', metavar='FILE', default='', help='store daemon\'s pid in the given file')
        op.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='spit out lots of debug output')
        op.add_option('-q', '--quiet', dest='quiet', action='store_true', default=False, help='only report errors')
        op.add_option('--pid-file', dest='pid_file', metavar='FILE', default='', help='store daemon\'s pid in the given file')
+        op.add_option('--traceback-file', dest='traceback_file', default='', help='destination to print tracebacks when receiving SIGUSR1')
        options, args = op.parse_args()
 
        if len(args) != 0:
        options, args = op.parse_args()
 
        if len(args) != 0:
@@ -1164,7 +993,7 @@ def set_stuff_up():
        if options.daemon: become_daemon()
        set_up_logging(options)
        if options.pid_file != '': create_pid_file(options.pid_file)
        if options.daemon: become_daemon()
        set_up_logging(options)
        if options.pid_file != '': create_pid_file(options.pid_file)
-
+       if options.traceback_file != '': TracebackPrinter.traceback_init(options.traceback_file)
        return options, config_opts
 
 def clean_up_nicely(options, config_opts):
        return options, config_opts
 
 def clean_up_nicely(options, config_opts):
@@ -1228,9 +1057,10 @@ def do_vend_server(options, config_opts):
                        continue
                
 #              run_forever(rfh, wfh, options, config_opts)
                        continue
                
 #              run_forever(rfh, wfh, options, config_opts)
-               
+
                try:
                try:
-                       run_forever(rfh, wfh, options, config_opts)
+                       vserver = VendServer()
+                       vserver.run_forever(rfh, wfh, options, config_opts)
                except VendingException:
                        logging.error("Connection died, trying again...")
                        logging.info("Trying again in 5 seconds.")
                except VendingException:
                        logging.error("Connection died, trying again...")
                        logging.info("Trying again in 5 seconds.")

UCC git Repository :: git.ucc.asn.au