more refactoring
[zanchey/dispense2.git] / sql-edition / servers / VendServer.py
1 #!/usr/bin/python
2 # vim:ts=4
3
4 USE_DB = 0
5
6 import ConfigParser
7 import sys, os, string, re, pwd, signal, math
8 import logging, logging.handlers
9 from traceback import format_tb
10 if USE_DB: import pg
11 from time import time, sleep
12 from popen2 import popen2
13 from LATClient import LATClient, LATClientException
14 from SerialClient import SerialClient, SerialClientException
15 from VendingMachine import VendingMachine, VendingException
16 from MessageKeeper import MessageKeeper
17 from HorizScroll import HorizScroll
18 from random import random, seed
19 from Idler import TrainIdler,GrayIdler,StringIdler,ClockIdler,FortuneIdler,FileIdler,PipeIdler
20 import socket
21 from posix import geteuid
22
23 CREDITS="""
24 This vending machine software brought to you by:
25 Bernard Blackham
26 Mark Tearle
27 Nick Bannon
28 Cameron Patrick
29 and a collective of hungry alpacas.
30
31
32
33 For a good time call +61 8 6488 3901
34
35
36
37 """
38
39 GREETING = 'UCC SNACKS'
40 PIN_LENGTH = 4
41
42 DOOR = 1
43 SWITCH = 2
44 KEY = 3
45 TICK = 4
46
47 class DispenseDatabaseException(Exception): pass
48
49 class DispenseDatabase:
50         def __init__(self, vending_machine, host, name, user, password):
51                 self.vending_machine = vending_machine
52                 self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
53                 self.db.query('LISTEN vend_requests')
54
55         def process_requests(self):
56                 logging.debug('database processing')
57                 query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
58                 try:
59                         outstanding = self.db.query(query).getresult()
60                 except (pg.error,), db_err:
61                         raise DispenseDatabaseException('Failed to query database: %s\n'%(db_err.strip()))
62                 for (id, slot) in outstanding:
63                         (worked, code, string) = self.vending_machine.vend(slot)
64                         logging.debug (str((worked, code, string)))
65                         if worked:
66                                 query = 'SELECT vend_success(%s)'%id
67                                 self.db.query(query).getresult()
68                         else:
69                                 query = 'SELECT vend_failed(%s)'%id
70                                 self.db.query(query).getresult()
71
72         def handle_events(self):
73                 notifier = self.db.getnotify()
74                 while notifier is not None:
75                         self.process_requests()
76                         notify = self.db.getnotify()
77
78 def scroll_options(username, mk, welcome = False):
79         if welcome:
80                 msg = [(center('WELCOME'), False, 0.8),
81                            (center(username), False, 0.8)]
82         else:
83                 msg = []
84         choices = ' '*10+'CHOICES: '
85         try:
86                 coke_machine = file('/home/other/coke/coke_contents')
87                 cokes = coke_machine.readlines()
88                 coke_machine.close()
89         except:
90                 cokes = []
91                 pass
92         for c in cokes:
93                 c = c.strip()
94                 (slot_num, price, slot_name) = c.split(' ', 2)
95                 if slot_name == 'dead': continue
96                 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
97         choices += '55-DOOR '
98         choices += 'OR A SNACK. '
99         choices += '99 TO READ AGAIN. '
100         choices += 'CHOICE?   '
101         msg.append((choices, False, None))
102         mk.set_messages(msg)
103
104 def get_pin(uid):
105         try:
106                 info = pwd.getpwuid(uid)
107         except KeyError:
108                 logging.info('getting pin for uid %d: user not in password file'%uid)
109                 return None
110         if info.pw_dir == None: return False
111         pinfile = os.path.join(info.pw_dir, '.pin')
112         try:
113                 s = os.stat(pinfile)
114         except OSError:
115                 logging.info('getting pin for uid %d: .pin not found in home directory'%uid)
116                 return None
117         if s.st_mode & 077:
118                 logging.info('getting pin for uid %d: .pin has wrong permissions'%uid)
119                 return None
120         try:
121                 f = file(pinfile)
122         except IOError:
123                 logging.info('getting pin for uid %d: I cannot read pin file'%uid)
124                 return None
125         pinstr = f.readline()
126         f.close()
127         if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
128                 logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
129                 return None
130         return int(pinstr)
131
132 def has_good_pin(uid):
133         return get_pin(uid) != None
134
135 def verify_user_pin(uid, pin):
136         if get_pin(uid) == pin:
137                 info = pwd.getpwuid(uid)
138                 logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
139                 return info.pw_name
140         else:
141                 logging.info('refused pin for uid %d'%(uid))
142                 return None
143
144 def door_open_mode(v):
145         logging.warning("Entering open door mode")
146         v.display("-FEED  ME-")
147         while True:
148                 e = v.next_event()
149                 if e == None: break
150                 (event, params) = e
151                 if event == TICK: break
152
153                 if event == DOOR:
154                         if params == 1: # door closed
155                                 logging.warning('Leaving open door mode')
156                                 v.display("-YUM YUM!-")
157                                 sleep(1)
158                                 return
159
160 def cookie(v):
161         seed(time())
162         messages = ['  WASSUP! ', 'PINK FISH ', ' SECRETS ', '  ESKIMO  ', ' FORTUNES ', 'MORE MONEY']
163         choice = int(random()*len(messages))
164         msg = messages[choice]
165         left = range(len(msg))
166         for i in range(len(msg)):
167                 if msg[i] == ' ': left.remove(i)
168         reveal = 1
169         while left:
170                 s = ''
171                 for i in range(0, len(msg)):
172                         if i in left:
173                                 if reveal == 0:
174                                         left.remove(i)
175                                         s += msg[i]
176                                 else:
177                                         s += chr(int(random()*26)+ord('A'))
178                                 reveal += 1
179                                 reveal %= 17
180                         else:
181                                 s += msg[i]
182                 v.display(s)
183
184 def center(str):
185         LEN = 10
186         return ' '*((LEN-len(str))/2)+str
187
188
189
190 idlers = []
191 idler = None
192
193 def setup_idlers(v):
194         global idlers, idler
195         idlers = [
196                  GrayIdler(v),
197                 StringIdler(v, text="Kill 'em all", repeat=False),
198                  GrayIdler(v,one="*",zero="-"),
199                 StringIdler(v, text=CREDITS),
200                  GrayIdler(v,one="/",zero="\\"),
201                 ClockIdler(v),
202                  GrayIdler(v,one="X",zero="O"),
203                 FileIdler(v, '/usr/share/common-licenses/GPL-2'),
204                  GrayIdler(v,one="*",zero="-",reorder=1),
205                 StringIdler(v, text=str(math.pi) + "            "),
206                 ClockIdler(v),
207                  GrayIdler(v,one="/",zero="\\",reorder=1),
208                 StringIdler(v, text=str(math.e) + "            "),
209                  GrayIdler(v,one="X",zero="O",reorder=1),
210                 StringIdler(v, text="    I want some pizza - please call Pizza Hut Shenton Park on +61 8 9381 9979 - and order as Quinn - I am getting really hungry", repeat=False),
211                 PipeIdler(v, "/usr/bin/ypcat", "passwd"),
212                 FortuneIdler(v),
213                 ClockIdler(v),
214                 StringIdler(v),
215                 TrainIdler(v),
216                 ]
217     disabled = [
218                 ]
219         idler = choose_idler()
220
221 def choose_idler():
222         global idler
223         iiindex = 0
224
225         if idler:
226                 iiindex = idlers.index(idler)
227
228         iilen = len(idlers)
229
230         move = int(random()*len(idlers)) + 1
231
232         while move >= 0:
233                 idler = idlers[( (iiindex + 1) % iilen)]
234                 move = move - idler.affinity()
235
236         idler.reset()
237
238 def idle_step():
239         global idler
240         if idler.finished():
241                 choose_idler()
242         idler.next()
243
244 class VendState:
245         def __init__(self,v):
246                 self.mk = MessageKeeper(v)
247                 self.cur_user = ''
248                 self.cur_pin = ''
249                 self.cur_selection = ''
250                 self.time_to_autologout = None
251                 self.time_to_idle = None
252                 self.last_timeout_refresh = None
253
254
255 def handle_door_event(event, params, v, vstatus):
256         if params == 0:
257                 door_open_mode(v);
258                 vstatus.cur_user = ''
259                 vstatus.cur_pin = ''
260                 vstatus.mk.set_message(GREETING)
261
262 def handle_tick_event(event, params, v, vstatus):
263         # don't care right now.
264         pass
265
266 def handle_switch_event(event, params, v, vstatus):
267         # don't care right now.
268         pass
269
270 def handle_key_event(event, params, v, vstatus):
271         key = params
272         # complicated key handling here:
273         if len(vstatus.cur_user) < 5:
274                 if key == 11:
275                         vstatus.cur_user = ''
276                         vstatus.mk.set_message(GREETING)
277                         return
278                 vstatus.cur_user += chr(key + ord('0'))
279                 vstatus.mk.set_message('UID: '+vstatus.cur_user)
280                 if len(vstatus.cur_user) == 5:
281                         uid = int(vstatus.cur_user)
282                         if not has_good_pin(uid):
283                                 logging.info('user '+vstatus.cur_user+' has a bad PIN')
284                                 #mk.set_messages(
285                                         #[(center('INVALID'), False, 0.7),
286                                          #(center('PIN'), False, 0.7),
287                                          #(center('SETUP'), False, 1.0),
288                                          #(GREETING, False, None)])
289                                 vstatus.mk.set_messages(
290                                         [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
291                                          (GREETING, False, None)])
292                                 vstatus.cur_user = ''
293                                 vstatus.cur_pin = ''
294                                 return
295                         vstatus.cur_pin = ''
296                         vstatus.mk.set_message('PIN: ')
297                         logging.info('need pin for user %s'%vstatus.cur_user)
298                         return
299         elif len(vstatus.cur_pin) < PIN_LENGTH:
300                 if key == 11:
301                         if vstatus.cur_pin == '':
302                                 vstatus.cur_user = ''
303                                 vstatus.mk.set_message(GREETING)
304                                 return
305                         vstatus.cur_pin = ''
306                         vstatus.mk.set_message('PIN: ')
307                         return
308                 vstatus.cur_pin += chr(key + ord('0'))
309                 vstatus.mk.set_message('PIN: '+'X'*len(vstatus.cur_pin))
310                 if len(vstatus.cur_pin) == PIN_LENGTH:
311                         username = verify_user_pin(int(vstatus.cur_user), int(vstatus.cur_pin))
312                         if username:
313                                 v.beep(0, False)
314                                 vstatus.cur_selection = ''
315                                 scroll_options(username, vstatus.mk, True)
316                                 return
317                         else:
318                                 v.beep(40, False)
319                                 vstatus.mk.set_messages(
320                                         [(center('BAD PIN'), False, 1.0),
321                                          (center('SORRY'), False, 0.5),
322                                          (GREETING, False, None)])
323                                 vstatus.cur_user = ''
324                                 vstatus.cur_pin = ''
325                                 return
326         elif len(vstatus.cur_selection) == 0:
327                 if key == 11:
328                         vstatus.cur_pin = ''
329                         vstatus.cur_user = ''
330                         vstatus.cur_selection = ''
331                         vstatus.mk.set_messages(
332                                 [(center('BYE!'), False, 1.5),
333                                  (GREETING, False, None)])
334                         return
335                 vstatus.cur_selection += chr(key + ord('0'))
336                 vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
337                 vstatus.time_to_autologout = None
338         elif len(vstatus.cur_selection) == 1:
339                 if key == 11:
340                         vstatus.cur_selection = ''
341                         vstatus.time_to_autologout = None
342                         scroll_options(username, vstatus.mk)
343                         return
344                 else:
345                         vstatus.cur_selection += chr(key + ord('0'))
346                         #make_selection(cur_selection)
347                         # XXX this should move somewhere else:
348                         if vstatus.cur_selection == '55':
349                                 vstatus.mk.set_message('OPENSESAME')
350                                 logging.info('dispensing a door for %s'%username)
351                                 if geteuid() == 0:
352                                         ret = os.system('su - "%s" -c "dispense door"'%username)
353                                 else:
354                                         ret = os.system('dispense door')
355                                 if ret == 0:
356                                         logging.info('door opened')
357                                         vstatus.mk.set_message(center('DOOR OPEN'))
358                                 else:
359                                         logging.warning('user %s tried to dispense a bad door'%username)
360                                         vstatus.mk.set_message(center('BAD DOOR'))
361                                 sleep(1)
362                         elif vstatus.cur_selection == '91':
363                                 cookie(v)
364                         elif vstatus.cur_selection == '99':
365                                 scroll_options(username, vstatus.mk)
366                                 vstatus.cur_selection = ''
367                                 return
368                         elif vstatus.cur_selection[1] == '8':
369                                 v.display('GOT COKE?')
370                                 if ((os.system('su - "%s" -c "dispense %s"'%(username, vstatus.cur_selection[0])) >> 8) != 0):
371                                         v.display('SEEMS NOT')
372                                 else:
373                                         v.display('GOT COKE!')
374                         else:
375                                 v.display(vstatus.cur_selection+' - $1.00')
376                                 if ((os.system('su - "%s" -c "dispense snack"'%(username)) >> 8) == 0):
377                                         v.vend(vstatus.cur_selection)
378                                         v.display('THANK YOU')
379                                 else:
380                                         v.display('NO MONEY?')
381                         sleep(1)
382                         vstatus.cur_selection = ''
383                         vstatus.time_to_autologout = time() + 8
384                         vstatus.last_timeout_refresh = None
385
386
387 def run_forever(rfh, wfh, options, cf):
388         v = VendingMachine(rfh, wfh)
389         vstatus = VendState(v)
390
391         logging.debug('PING is ' + str(v.ping()))
392
393         if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword)
394
395         vstatus.mk.set_message(GREETING)
396         setup_idlers(v)
397
398
399         # This main loop is hideous and the work of the devil - mtearle
400         #
401         #
402         # notes for later surgery
403         #   (event, counter, ' ')
404         #        V
405         #   d[      ] = (method)
406         #
407         #  return state
408
409         while True:
410                 if USE_DB:
411                         try:
412                                 db.handle_events()
413                         except DispenseDatabaseException, e:
414                                 logging.error('Database error: '+str(e))
415
416                 if vstatus.time_to_autologout != None:
417                         time_left = vstatus.time_to_autologout - time()
418                         if time_left < 6 and (vstatus.last_timeout_refresh is None or vstatus.last_timeout_refresh > time_left):
419                                 vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
420                                 vstatus.last_timeout_refresh = int(time_left)
421                                 vstatus.cur_selection = ''
422
423                 if vstatus.time_to_autologout != None and vstatus.time_to_autologout - time() <= 0:
424                         vstatus.time_to_autologout = None
425                         vstatus.cur_user = ''
426                         vstatus.cur_pin = ''
427                         vstatus.cur_selection = ''
428                         vstatus.mk.set_message(GREETING)
429
430                 if vstatus.time_to_autologout and not vstatus.mk.done(): 
431                         vstatus.time_to_autologout = None
432                 if vstatus.cur_user == '' and vstatus.time_to_autologout: 
433                         vstatus.time_to_autologout = None
434                 if len(vstatus.cur_pin) == PIN_LENGTH and vstatus.mk.done() and vstatus.time_to_autologout == None:
435                         # start autologout
436                         vstatus.time_to_autologout = time() + 15
437                         vstatus.last_timeout_refresh = None
438
439                 if vstatus.time_to_idle == None and vstatus.cur_user == '':
440                         vstatus.time_to_idle = time() + 5
441                         choose_idler()
442                 if vstatus.time_to_idle is not None and vstatus.cur_user != '': 
443                         vstatus.time_to_idle = None
444
445                 if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
446                         idle_step()
447
448                 if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle + 30:
449                         vstatus.time_to_idle = time()
450                         choose_idler()
451
452                 vstatus.mk.update_display()
453
454                 e = v.next_event(0)
455                 (event, params) = e
456
457                 if event == TICK:
458                         e = v.next_event(0.05)
459                         (event, params) = e
460
461                         if event == TICK:
462                                 handle_tick_event(event, params, v, vstatus)
463                                 continue
464                 vstatus.time_to_idle = None
465                 logging.debug('Got event: ' + repr(e))
466
467                 if event == DOOR:
468                         handle_door_event(event, params, v, vstatus)
469                 elif event == SWITCH:
470                         handle_switch_event(event, params, v, vstatus)
471                 elif event == KEY:
472                         handle_key_event(event, params, v, vstatus)
473
474 def connect_to_vend(options, cf):
475
476         if options.use_lat:
477                 logging.info('Connecting to vending machine using LAT')
478                 latclient = LATClient(service = cf.ServiceName, password = cf.ServicePassword, server_name = cf.ServerName, connect_password = cf.ConnectPassword, priv_password = cf.PrivPassword)
479                 rfh, wfh = latclient.get_fh()
480         elif options.use_serial:
481                 # Open vending machine via serial.
482                 logging.info('Connecting to vending machine using serial')
483                 serialclient = SerialClient(port = '/dev/ttyS1', baud = 9600)
484                 rfh,wfh = serialclient.get_fh()
485         else:
486                 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
487                 logging.info('Connecting to virtual vending machine on %s:%d'%(options.host,options.port))
488                 import socket
489                 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
490                 sock.connect((options.host, options.port))
491                 rfh = sock.makefile('r')
492                 wfh = sock.makefile('w')
493                 
494         return rfh, wfh
495
496 def parse_args():
497         from optparse import OptionParser
498
499         op = OptionParser(usage="%prog [OPTION]...")
500         op.add_option('-f', '--config-file', default='/etc/dispense/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
501         op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port')
502         op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
503         op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
504         op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)')
505         op.add_option('-p', '--port', dest='port', default=5150, type='int', help='the port number to connect to (default: 5150)')
506         op.add_option('-l', '--log-file', metavar='FILE', dest='log_file', default='', help='log output to the specified file')
507         op.add_option('-s', '--syslog', dest='syslog', metavar='FACILITY', default=None, help='log output to given syslog facility')
508         op.add_option('-d', '--daemon', dest='daemon', action='store_true', default=False, help='run as a daemon')
509         op.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='spit out lots of debug output')
510         op.add_option('-q', '--quiet', dest='quiet', action='store_true', default=False, help='only report errors')
511         op.add_option('--pid-file', dest='pid_file', metavar='FILE', default='', help='store daemon\'s pid in the given file')
512         options, args = op.parse_args()
513
514         if len(args) != 0:
515                 op.error('extra command line arguments: ' + ' '.join(args))
516
517         return options
518
519 config_options = {
520         'DBServer': ('Database', 'Server'),
521         'DBName': ('Database', 'Name'),
522         'DBUser': ('VendingMachine', 'DBUser'),
523         'DBPassword': ('VendingMachine', 'DBPassword'),
524         
525         'ServiceName': ('VendingMachine', 'ServiceName'),
526         'ServicePassword': ('VendingMachine', 'Password'),
527         
528         'ServerName': ('DecServer', 'Name'),
529         'ConnectPassword': ('DecServer', 'ConnectPassword'),
530         'PrivPassword': ('DecServer', 'PrivPassword'),
531         }
532
533 class VendConfigFile:
534         def __init__(self, config_file, options):
535                 try:
536                         cp = ConfigParser.ConfigParser()
537                         cp.read(config_file)
538
539                         for option in options:
540                                 section, name = options[option]
541                                 value = cp.get(section, name)
542                                 self.__dict__[option] = value
543                 
544                 except ConfigParser.Error, e:
545                         raise SystemExit("Error reading config file "+config_file+": " + str(e))
546
547 def create_pid_file(name):
548         try:
549                 pid_file = file(name, 'w')
550                 pid_file.write('%d\n'%os.getpid())
551                 pid_file.close()
552         except IOError, e:
553                 logging.warning('unable to write to pid file '+name+': '+str(e))
554
555 def set_stuff_up():
556         def do_nothing(signum, stack):
557                 signal.signal(signum, do_nothing)
558         def stop_server(signum, stack): raise KeyboardInterrupt
559         signal.signal(signal.SIGHUP, do_nothing)
560         signal.signal(signal.SIGTERM, stop_server)
561         signal.signal(signal.SIGINT, stop_server)
562
563         options = parse_args()
564         config_opts = VendConfigFile(options.config_file, config_options)
565         if options.daemon: become_daemon()
566         set_up_logging(options)
567         if options.pid_file != '': create_pid_file(options.pid_file)
568
569         return options, config_opts
570
571 def clean_up_nicely(options, config_opts):
572         if options.pid_file != '':
573                 try:
574                         os.unlink(options.pid_file)
575                         logging.debug('Removed pid file '+options.pid_file)
576                 except OSError: pass  # if we can't delete it, meh
577
578 def set_up_logging(options):
579         logger = logging.getLogger()
580         
581         if not options.daemon:
582                 stderr_logger = logging.StreamHandler(sys.stderr)
583                 stderr_logger.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
584                 logger.addHandler(stderr_logger)
585         
586         if options.log_file != '':
587                 try:
588                         file_logger = logging.FileHandler(options.log_file)
589                         file_logger.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
590                         logger.addHandler(file_logger)
591                 except IOError, e:
592                         logger.warning('unable to write to log file '+options.log_file+': '+str(e))
593
594         if options.syslog != None:
595                 sys_logger = logging.handlers.SysLogHandler('/dev/log', options.syslog)
596                 sys_logger.setFormatter(logging.Formatter('vendserver[%d]'%(os.getpid()) + ' %(levelname)s: %(message)s'))
597                 logger.addHandler(sys_logger)
598
599         if options.quiet:
600                 logger.setLevel(logging.WARNING)
601         elif options.verbose:
602                 logger.setLevel(logging.DEBUG)
603         else:
604                 logger.setLevel(logging.INFO)
605
606 def become_daemon():
607         dev_null = file('/dev/null')
608         fd = dev_null.fileno()
609         os.dup2(fd, 0)
610         os.dup2(fd, 1)
611         os.dup2(fd, 2)
612         try:
613                 if os.fork() != 0:
614                         sys.exit(0)
615                 os.setsid()
616         except OSError, e:
617                 raise SystemExit('failed to fork: '+str(e))
618
619 def do_vend_server(options, config_opts):
620         while True:
621                 try:
622                         rfh, wfh = connect_to_vend(options, config_opts)
623                 except (SerialClientException, socket.error), e:
624                         (exc_type, exc_value, exc_traceback) = sys.exc_info()
625                         del exc_traceback
626                         logging.error("Connection error: "+str(exc_type)+" "+str(e))
627                         logging.info("Trying again in 5 seconds.")
628                         sleep(5)
629                         continue
630                 
631                 try:
632                         run_forever(rfh, wfh, options, config_opts)
633                 except VendingException:
634                         logging.error("Connection died, trying again...")
635                         logging.info("Trying again in 5 seconds.")
636                         sleep(5)
637
638 if __name__ == '__main__':
639         options, config_opts = set_stuff_up()
640         while True:
641                 try:
642                         logging.warning('Starting Vend Server')
643                         do_vend_server(options, config_opts)
644                         logging.error('Vend Server finished unexpectedly, restarting')
645                 except KeyboardInterrupt:
646                         logging.info("Killed by signal, cleaning up")
647                         clean_up_nicely(options, config_opts)
648                         logging.warning("Vend Server stopped")
649                         break
650                 except SystemExit:
651                         break
652                 except:
653                         (exc_type, exc_value, exc_traceback) = sys.exc_info()
654                         tb = format_tb(exc_traceback, 20)
655                         del exc_traceback
656                         
657                         logging.critical("Uh-oh, unhandled " + str(exc_type) + " exception")
658                         logging.critical("Message: " + str(exc_value))
659                         logging.critical("Traceback:")
660                         for event in tb:
661                                 for line in event.split('\n'):
662                                         logging.critical('    '+line)
663                         logging.critical("This message should be considered a bug in the Vend Server.")
664                         logging.critical("Please report this to someone who can fix it.")
665                         sleep(10)
666                         logging.warning("Trying again anyway (might not help, but hey...)")
667

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