| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """WSGI application to manage a USB gadget. | 5 """WSGI application to manage a USB gadget. |
| 6 """ | 6 """ |
| 7 | 7 |
| 8 import datetime |
| 9 import hashlib |
| 8 import re | 10 import re |
| 11 import subprocess |
| 9 import sys | 12 import sys |
| 13 import time |
| 14 import urllib2 |
| 10 | 15 |
| 11 from tornado import httpserver | 16 from tornado import httpserver |
| 17 from tornado import ioloop |
| 12 from tornado import web | 18 from tornado import web |
| 13 | 19 |
| 14 import default_gadget | 20 import default_gadget |
| 15 | 21 |
| 16 VERSION_PATTERN = re.compile(r'.*usb_gadget-([a-z0-9]{32})\.zip') | 22 VERSION_PATTERN = re.compile(r'.*usb_gadget-([a-z0-9]{32})\.zip') |
| 17 | 23 |
| 18 address = None | 24 address = None |
| 19 chip = None | 25 chip = None |
| 20 claimed_by = None | 26 claimed_by = None |
| 21 default = default_gadget.DefaultGadget() | 27 default = default_gadget.DefaultGadget() |
| 22 gadget = None | 28 gadget = None |
| 29 hardware = None |
| 30 interface = None |
| 31 port = None |
| 23 | 32 |
| 24 | 33 |
| 25 def SwitchGadget(new_gadget): | 34 def SwitchGadget(new_gadget): |
| 26 if chip.IsConfigured(): | 35 if chip.IsConfigured(): |
| 27 chip.Destroy() | 36 chip.Destroy() |
| 28 | 37 |
| 29 global gadget | 38 global gadget |
| 30 gadget = new_gadget | 39 gadget = new_gadget |
| 31 gadget.AddStringDescriptor(3, address) | 40 gadget.AddStringDescriptor(3, address) |
| 32 chip.Create(gadget) | 41 chip.Create(gadget) |
| 33 | 42 |
| 34 | 43 |
| 35 class VersionHandler(web.RequestHandler): | 44 class VersionHandler(web.RequestHandler): |
| 36 | 45 |
| 37 def get(self): | 46 def get(self): |
| 38 version = 'unpackaged' | 47 version = 'unpackaged' |
| 39 for path in sys.path: | 48 for path in sys.path: |
| 40 match = VERSION_PATTERN.match(path) | 49 match = VERSION_PATTERN.match(path) |
| 41 if match: | 50 if match: |
| 42 version = match.group(1) | 51 version = match.group(1) |
| 43 break | 52 break |
| 44 | 53 |
| 45 self.write(version) | 54 self.write(version) |
| 46 | 55 |
| 47 | 56 |
| 57 class UpdateHandler(web.RequestHandler): |
| 58 |
| 59 def post(self): |
| 60 fileinfo = self.request.files['file'][0] |
| 61 |
| 62 match = VERSION_PATTERN.match(fileinfo['filename']) |
| 63 if match is None: |
| 64 self.write('Filename must contain MD5 hash.') |
| 65 self.set_status(400) |
| 66 return |
| 67 |
| 68 content = fileinfo['body'] |
| 69 md5sum = hashlib.md5(content).hexdigest() |
| 70 if md5sum != match.group(1): |
| 71 self.write('File hash does not match.') |
| 72 self.set_status(400) |
| 73 return |
| 74 |
| 75 filename = 'usb_gadget-{}.zip'.format(md5sum) |
| 76 with open(filename, 'wb') as f: |
| 77 f.write(content) |
| 78 |
| 79 args = ['/usr/bin/python', filename, |
| 80 '--interface', interface, |
| 81 '--port', str(port), |
| 82 '--hardware', hardware] |
| 83 if claimed_by is not None: |
| 84 args.extend(['--start-claimed', claimed_by]) |
| 85 |
| 86 print 'Reloading with version {}...'.format(md5sum) |
| 87 |
| 88 global http_server |
| 89 if chip.IsConfigured(): |
| 90 chip.Destroy() |
| 91 http_server.stop() |
| 92 |
| 93 child = subprocess.Popen(args, close_fds=True) |
| 94 |
| 95 while True: |
| 96 child.poll() |
| 97 if child.returncode is not None: |
| 98 self.write('New package exited with error {}.' |
| 99 .format(child.returncode)) |
| 100 self.set_status(500) |
| 101 |
| 102 http_server = httpserver.HTTPServer(app) |
| 103 http_server.listen(port) |
| 104 SwitchGadget(gadget) |
| 105 return |
| 106 |
| 107 try: |
| 108 f = urllib2.urlopen('http://{}/version'.format(address)) |
| 109 if f.getcode() == 200: |
| 110 # Update complete, wait 1 second to make sure buffers are flushed. |
| 111 io_loop = ioloop.IOLoop.instance() |
| 112 io_loop.add_timeout(datetime.timedelta(seconds=1), io_loop.stop) |
| 113 return |
| 114 except urllib2.URLError: |
| 115 pass |
| 116 time.sleep(0.1) |
| 117 |
| 118 |
| 48 class ClaimHandler(web.RequestHandler): | 119 class ClaimHandler(web.RequestHandler): |
| 49 | 120 |
| 50 def post(self): | 121 def post(self): |
| 51 global claimed_by | 122 global claimed_by |
| 52 | 123 |
| 53 if claimed_by is None: | 124 if claimed_by is None: |
| 54 claimed_by = self.get_argument('session_id') | 125 claimed_by = self.get_argument('session_id') |
| 55 else: | 126 else: |
| 56 self.write('Device is already claimed by "{}".'.format(claimed_by)) | 127 self.write('Device is already claimed by "{}".'.format(claimed_by)) |
| 57 self.set_status(403) | 128 self.set_status(403) |
| (...skipping 23 matching lines...) Expand all Loading... |
| 81 | 152 |
| 82 class ReconnectHandler(web.RequestHandler): | 153 class ReconnectHandler(web.RequestHandler): |
| 83 | 154 |
| 84 def post(self): | 155 def post(self): |
| 85 if not chip.IsConfigured(): | 156 if not chip.IsConfigured(): |
| 86 chip.Create(gadget) | 157 chip.Create(gadget) |
| 87 | 158 |
| 88 | 159 |
| 89 app = web.Application([ | 160 app = web.Application([ |
| 90 (r'/version', VersionHandler), | 161 (r'/version', VersionHandler), |
| 162 (r'/update', UpdateHandler), |
| 91 (r'/claim', ClaimHandler), | 163 (r'/claim', ClaimHandler), |
| 92 (r'/unclaim', UnclaimHandler), | 164 (r'/unclaim', UnclaimHandler), |
| 93 (r'/unconfigure', UnconfigureHandler), | 165 (r'/unconfigure', UnconfigureHandler), |
| 94 (r'/disconnect', DisconnectHandler), | 166 (r'/disconnect', DisconnectHandler), |
| 95 (r'/reconnect', ReconnectHandler), | 167 (r'/reconnect', ReconnectHandler), |
| 96 ]) | 168 ]) |
| 97 | 169 |
| 98 http_server = httpserver.HTTPServer(app) | 170 http_server = httpserver.HTTPServer(app) |
| OLD | NEW |