X-Git-Url: https://git.ucc.asn.au/?p=uccvend-vendserver.git;a=blobdiff_plain;f=VendServer%2FVendServer.py;h=cf9c8bbdbb689476b1c8fe584a16049691175b2f;hp=dd35cd418a3c6ca160cfbc4ca82b2c452b515942;hb=115d5500cfbdc1543f64602cb2b1b0764dcbb276;hpb=d51a435a9b62496dd3388c6db2280c0bfb3a49eb diff --git a/VendServer/VendServer.py b/VendServer/VendServer.py index dd35cd4..cf9c8bb 100755 --- a/VendServer/VendServer.py +++ b/VendServer/VendServer.py @@ -1,14 +1,12 @@ #!/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 -if USE_DB: import pg 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 LDAPConnector import get_uid,get_uname, set_card_id from OpenDispense import OpenDispense as Dispense +import TracebackPrinter CREDITS=""" This vending machine software brought to you by: @@ -95,36 +93,6 @@ class VendConfigFile: 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. """ @@ -173,7 +141,6 @@ class VendServer(): # 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),] @@ -201,96 +168,6 @@ class VendServer(): # Send it to the display mk.set_messages(msg) - - """ - Verify the users pin - """ - """ - def _check_pin(self, uid, pin): - print "_check_pin('",uid,"',---)" - if uid != self._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 - self._pin_uid = uid - self._pin_pin = pinstr - self._pin_uname = info.pw_name - else: - pinstr = self._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(self, name=None): - if name == None: - name = self._pin_uname - acct, unused = Popen(['dispense', 'acct', self._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(self, uid): - return self._check_pin(uid, None) != None - """ - - """ - Verify the users pin. - """ - """ - def verify_user_pin(self, uid, pin, skip_pin_check=False): - if skip_pin_check or self._check_pin(uid, pin) == True: - info = pwd.getpwuid(uid) - if skip_pin_check: - if self.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)) - 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. """ @@ -458,12 +335,14 @@ class VendServer(): 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 @@ -492,7 +371,8 @@ class VendServer(): self.vstatus.cur_pin = '' self.vstatus.cur_user = '' self.vstatus.cur_selection = '' - _last_card_id = -1 + self._last_card_id = -1 + self.dispense.logOut() self.vstatus.mk.set_messages([(self.center('BYE!'), False, 1.5)]) self.reset_idler(2) return @@ -503,18 +383,22 @@ class VendServer(): 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.vstatus.cur_user: + 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. - self.dispense.getItemInfo(self.vstatus.cur_selection) + (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 @@ -523,6 +407,7 @@ class VendServer(): 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') @@ -561,12 +446,13 @@ class VendServer(): exitcode = os.system('dispense -u "%s" snack:%s'%(self.vstatus.username, self.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, self.vstatus.cur_selection, self.vstatus.username)) (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?') @@ -579,25 +465,6 @@ class VendServer(): self.v.display('UNK ERROR') sleep(1) - """ - Find the price of an item. - """ - """ - def price_check(): - if self.vstatus.cur_selection[1] == '8': - args = ('dispense', 'iteminfo', 'coke:' + self.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( self.vstatus.cur_selection ) - except: - price, shortname, name = get_snack( '--' ) - dollarprice = "$%.2f" % ( price / 100.0 ) - self.v.display(self.vstatus.cur_selection+' - %s'%dollarprice) - """ - """ Triggered when the user presses a button while entering their pin. """ @@ -607,7 +474,8 @@ class VendServer(): if key == 11: if self.vstatus.cur_pin == '': self.vstatus.cur_user = '' - slef.reset_idler() + self.dispense.logOut() + self.reset_idler() return self.vstatus.cur_pin = '' @@ -616,8 +484,8 @@ class VendServer(): 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: - self.dispense.authUserIdPin(self.vstatus.cur_user, self.vstatus.cur_pin) - if self.dispense.getUsername(): + 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) @@ -654,6 +522,7 @@ class VendServer(): if len(self.vstatus.cur_user) <8: if key == 11: self.vstatus.cur_user = '' + self.dispense.logOut() self.reset_idler() return @@ -707,19 +576,7 @@ class VendServer(): return - """ - if not self.has_good_pin(uid): - logging.info('user '+self.vstatus.cur_user+' has a bad PIN') - self.vstatus.mk.set_messages( - [(' '*10+'INVALID PIN SETUP'+' '*11, False, 3)]) - self.vstatus.cur_user = '' - self.vstatus.cur_pin = '' - - self.reset_idler(3) - - return - """ - + # TODO Fix this up do we can check before logging in """ if self.dispense.isDisabled(): logging.info('user '+self.vstatus.cur_user+' is disabled') @@ -745,6 +602,7 @@ class VendServer(): key = params if key == 11: self.vstatus.cur_user = '' + self.dispense.logOut() self.reset_idler() return @@ -909,6 +767,7 @@ class VendServer(): 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 @@ -929,9 +788,7 @@ class VendServer(): self._last_card_id = card_id - self.dispense.authMifareCard(card_id) - logging.info('Mapped card id to uid %s'%self.dispense.getUsername()) - if not self.dispense.isLoggedIn(): + if not self.dispense.authMifareCard(card_id): self.v.beep(40, False) self.vstatus.mk.set_messages( [(self.center('BAD CARD'), False, 1.0), @@ -943,6 +800,7 @@ class VendServer(): 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), @@ -951,6 +809,9 @@ class VendServer(): 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) @@ -968,15 +829,11 @@ class VendServer(): self._last_card_id = card_id - res = self.dispense.addCard(card_id) - - if get_uid(card_id) != None: + 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: - logging.info('Enrolling card %s to uid %s (%s)'%(card_id, self.vstatus.cur_user, self.vstatus.username)) - self.set_card_id(self.vstatus.cur_user, self.card_id) self.vstatus.mk.set_messages( [(self.center('CARD'), False, 0.5), (self.center('ENROLLED'), False, 0.5)]) @@ -1048,18 +905,10 @@ class VendServer(): logging.debug('PING is ' + str(self.v.ping())) - if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword) - self.setup_idlers() self.reset_idler() while True: - if USE_DB: - try: - db.handle_events() - except DispenseDatabaseException, e: - logging.error('Database error: '+str(e)) - timeout = self.time_to_next_update() (event, params) = self.v.next_event(timeout) self.run_handler(event, params) @@ -1115,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('--traceback-file', dest='traceback_file', default='', help='destination to print tracebacks when receiving SIGUSR1') options, args = op.parse_args() if len(args) != 0: @@ -1143,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.traceback_file != '': TracebackPrinter.traceback_init(options.traceback_file) return options, config_opts def clean_up_nicely(options, config_opts): @@ -1207,7 +1057,7 @@ def do_vend_server(options, config_opts): continue # run_forever(rfh, wfh, options, config_opts) - + try: vserver = VendServer() vserver.run_forever(rfh, wfh, options, config_opts)