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
3 # found in the LICENSE file.
5 """WSGI application to manage a USB gadget.
16 from tornado
import httpserver
17 from tornado
import ioloop
18 from tornado
import web
22 VERSION_PATTERN
= re
.compile(r
'.*usb_gadget-([a-z0-9]{32})\.zip')
27 default
= default_gadget
.DefaultGadget()
34 def SwitchGadget(new_gadget
):
35 if chip
.IsConfigured():
40 gadget
.AddStringDescriptor(3, address
)
44 class VersionHandler(web
.RequestHandler
):
47 version
= 'unpackaged'
49 match
= VERSION_PATTERN
.match(path
)
51 version
= match
.group(1)
57 class UpdateHandler(web
.RequestHandler
):
60 fileinfo
= self
.request
.files
['file'][0]
62 match
= VERSION_PATTERN
.match(fileinfo
['filename'])
64 self
.write('Filename must contain MD5 hash.')
68 content
= fileinfo
['body']
69 md5sum
= hashlib
.md5(content
).hexdigest()
70 if md5sum
!= match
.group(1):
71 self
.write('File hash does not match.')
75 filename
= 'usb_gadget-{}.zip'.format(md5sum
)
76 with
open(filename
, 'wb') as f
:
79 args
= ['/usr/bin/python', filename
,
80 '--interface', interface
,
82 '--hardware', hardware
]
83 if claimed_by
is not None:
84 args
.extend(['--start-claimed', claimed_by
])
86 print 'Reloading with version {}...'.format(md5sum
)
89 if chip
.IsConfigured():
93 child
= subprocess
.Popen(args
, close_fds
=True)
97 if child
.returncode
is not None:
98 self
.write('New package exited with error {}.'
99 .format(child
.returncode
))
102 http_server
= httpserver
.HTTPServer(app
)
103 http_server
.listen(port
)
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
)
114 except urllib2
.URLError
:
119 class ClaimHandler(web
.RequestHandler
):
124 if claimed_by
is None:
125 claimed_by
= self
.get_argument('session_id')
127 self
.write('Device is already claimed by "{}".'.format(claimed_by
))
131 class UnclaimHandler(web
.RequestHandler
):
136 if gadget
!= default
:
137 SwitchGadget(default
)
140 class UnconfigureHandler(web
.RequestHandler
):
143 SwitchGadget(default
)
146 class DisconnectHandler(web
.RequestHandler
):
149 if chip
.IsConfigured():
153 class ReconnectHandler(web
.RequestHandler
):
156 if not chip
.IsConfigured():
160 app
= web
.Application([
161 (r
'/version', VersionHandler
),
162 (r
'/update', UpdateHandler
),
163 (r
'/claim', ClaimHandler
),
164 (r
'/unclaim', UnclaimHandler
),
165 (r
'/unconfigure', UnconfigureHandler
),
166 (r
'/disconnect', DisconnectHandler
),
167 (r
'/reconnect', ReconnectHandler
),
170 http_server
= httpserver
.HTTPServer(app
)