123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- # Copyright 2014 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """WSGI application to manage a USB gadget.
- """
- from __future__ import print_function
- import datetime
- import hashlib
- import re
- import subprocess
- import sys
- import time
- import urllib2
- from tornado import httpserver
- from tornado import ioloop
- from tornado import web
- import default_gadget
- VERSION_PATTERN = re.compile(r'.*usb_gadget-([a-z0-9]{32})\.zip')
- address = None
- chip = None
- claimed_by = None
- default = default_gadget.DefaultGadget()
- gadget = None
- hardware = None
- interface = None
- port = None
- def SwitchGadget(new_gadget):
- if chip.IsConfigured():
- chip.Destroy()
- global gadget
- gadget = new_gadget
- gadget.AddStringDescriptor(3, address)
- chip.Create(gadget)
- class VersionHandler(web.RequestHandler):
- def get(self):
- version = 'unpackaged'
- for path in sys.path:
- match = VERSION_PATTERN.match(path)
- if match:
- version = match.group(1)
- break
- self.write(version)
- class UpdateHandler(web.RequestHandler):
- def post(self):
- fileinfo = self.request.files['file'][0]
- match = VERSION_PATTERN.match(fileinfo['filename'])
- if match is None:
- self.write('Filename must contain MD5 hash.')
- self.set_status(400)
- return
- content = fileinfo['body']
- md5sum = hashlib.md5(content).hexdigest()
- if md5sum != match.group(1):
- self.write('File hash does not match.')
- self.set_status(400)
- return
- filename = 'usb_gadget-{}.zip'.format(md5sum)
- with open(filename, 'wb') as f:
- f.write(content)
- args = ['/usr/bin/python', filename,
- '--interface', interface,
- '--port', str(port),
- '--hardware', hardware]
- if claimed_by is not None:
- args.extend(['--start-claimed', claimed_by])
- print('Reloading with version {}...'.format(md5sum))
- global http_server
- if chip.IsConfigured():
- chip.Destroy()
- http_server.stop()
- child = subprocess.Popen(args, close_fds=True)
- while True:
- child.poll()
- if child.returncode is not None:
- self.write('New package exited with error {}.'
- .format(child.returncode))
- self.set_status(500)
- http_server = httpserver.HTTPServer(app)
- http_server.listen(port)
- SwitchGadget(gadget)
- return
- try:
- f = urllib2.urlopen('http://{}/version'.format(address))
- if f.getcode() == 200:
- # Update complete, wait 1 second to make sure buffers are flushed.
- io_loop = ioloop.IOLoop.instance()
- io_loop.add_timeout(datetime.timedelta(seconds=1), io_loop.stop)
- return
- except urllib2.URLError:
- pass
- time.sleep(0.1)
- class ClaimHandler(web.RequestHandler):
- def post(self):
- global claimed_by
- if claimed_by is None:
- claimed_by = self.get_argument('session_id')
- else:
- self.write('Device is already claimed by "{}".'.format(claimed_by))
- self.set_status(403)
- class UnclaimHandler(web.RequestHandler):
- def post(self):
- global claimed_by
- claimed_by = None
- if gadget != default:
- SwitchGadget(default)
- class UnconfigureHandler(web.RequestHandler):
- def post(self):
- SwitchGadget(default)
- class DisconnectHandler(web.RequestHandler):
- def post(self):
- if chip.IsConfigured():
- chip.Destroy()
- class ReconnectHandler(web.RequestHandler):
- def post(self):
- if not chip.IsConfigured():
- chip.Create(gadget)
- app = web.Application([
- (r'/version', VersionHandler),
- (r'/update', UpdateHandler),
- (r'/claim', ClaimHandler),
- (r'/unclaim', UnclaimHandler),
- (r'/unconfigure', UnconfigureHandler),
- (r'/disconnect', DisconnectHandler),
- (r'/reconnect', ReconnectHandler),
- ])
- http_server = httpserver.HTTPServer(app)
|