20
20
WEBREPL_PUT_FILE = 1
21
21
WEBREPL_GET_FILE = 2
22
22
WEBREPL_GET_VER = 3
23
+ WEBREPL_FRAME_TXT = 0x81
24
+ WEBREPL_FRAME_BIN = 0x82
23
25
24
26
25
27
def debugmsg (msg ):
@@ -36,13 +38,12 @@ def __init__(self, s):
36
38
self .s = s
37
39
self .buf = b""
38
40
39
- def write (self , data ):
41
+ def write (self , data , frame = WEBREPL_FRAME_BIN ):
40
42
l = len (data )
41
43
if l < 126 :
42
- # TODO: hardcoded "binary" type
43
- hdr = struct .pack (">BB" , 0x82 , l )
44
+ hdr = struct .pack (">BB" , frame , l )
44
45
else :
45
- hdr = struct .pack (">BBH" , 0x82 , 126 , l )
46
+ hdr = struct .pack (">BBH" , frame , 126 , l )
46
47
self .s .send (hdr )
47
48
self .s .send (data )
48
49
@@ -116,6 +117,71 @@ def get_ver(ws):
116
117
return d
117
118
118
119
120
+ def do_repl (ws ):
121
+ import termios , select
122
+
123
+ class ConsolePosix :
124
+ def __init__ (self ):
125
+ self .infd = sys .stdin .fileno ()
126
+ self .infile = sys .stdin .buffer .raw
127
+ self .outfile = sys .stdout .buffer .raw
128
+ self .orig_attr = termios .tcgetattr (self .infd )
129
+
130
+ def enter (self ):
131
+ # attr is: [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
132
+ attr = termios .tcgetattr (self .infd )
133
+ attr [0 ] &= ~ (
134
+ termios .BRKINT | termios .ICRNL | termios .INPCK | termios .ISTRIP | termios .IXON
135
+ )
136
+ attr [1 ] = 0
137
+ attr [2 ] = attr [2 ] & ~ (termios .CSIZE | termios .PARENB ) | termios .CS8
138
+ attr [3 ] = 0
139
+ attr [6 ][termios .VMIN ] = 1
140
+ attr [6 ][termios .VTIME ] = 0
141
+ termios .tcsetattr (self .infd , termios .TCSANOW , attr )
142
+
143
+ def exit (self ):
144
+ termios .tcsetattr (self .infd , termios .TCSANOW , self .orig_attr )
145
+
146
+ def readchar (self ):
147
+ res = select .select ([self .infd ], [], [], 0 )
148
+ if res [0 ]:
149
+ return self .infile .read (1 )
150
+ else :
151
+ return None
152
+
153
+ def write (self , buf ):
154
+ self .outfile .write (buf )
155
+
156
+ print ("Use Ctrl-] to exit this shell" )
157
+ console = ConsolePosix ()
158
+ console .enter ()
159
+ try :
160
+ while True :
161
+ sel = select .select ([console .infd , ws .s ], [], [])
162
+ c = console .readchar ()
163
+ if c :
164
+ if c == b"\x1d " : # ctrl-], exit
165
+ break
166
+ else :
167
+ ws .write (c , WEBREPL_FRAME_TXT )
168
+ if ws .s in sel [0 ]:
169
+ c = ws .read (1 , text_ok = True )
170
+ while c is not None :
171
+ # pass character through to the console
172
+ oc = ord (c )
173
+ if oc in (8 , 9 , 10 , 13 , 27 ) or oc >= 32 :
174
+ console .write (c )
175
+ else :
176
+ console .write (b"[%02x]" % ord (c ))
177
+ if ws .buf :
178
+ c = ws .read (1 )
179
+ else :
180
+ c = None
181
+ finally :
182
+ console .exit ()
183
+
184
+
119
185
def put_file (ws , local_file , remote_file ):
120
186
sz = os .stat (local_file )[6 ]
121
187
dest_fname = (SANDBOX + remote_file ).encode ("utf-8" )
@@ -165,11 +231,16 @@ def get_file(ws, local_file, remote_file):
165
231
166
232
def help (rc = 0 ):
167
233
exename = sys .argv [0 ].rsplit ("/" , 1 )[- 1 ]
168
- print ("%s - Perform remote file operations using MicroPython WebREPL protocol" % exename )
234
+ print (
235
+ "%s - Access REPL, perform remote file operations via MicroPython WebREPL protocol"
236
+ % exename
237
+ )
169
238
print ("Arguments:" )
239
+ print (" [-p password] <host> - Access the remote REPL" )
170
240
print (" [-p password] <host>:<remote_file> <local_file> - Copy remote file to local file" )
171
241
print (" [-p password] <local_file> <host>:<remote_file> - Copy local file to remote file" )
172
242
print ("Examples:" )
243
+ print (" %s 192.168.4.1" % exename )
173
244
print (" %s script.py 192.168.4.1:/another_name.py" % exename )
174
245
print (" %s script.py 192.168.4.1:/app/" % exename )
175
246
print (" %s -p password 192.168.4.1:/app/script.py ." % exename )
@@ -191,26 +262,30 @@ def parse_remote(remote):
191
262
192
263
193
264
def main ():
194
- if len (sys .argv ) not in (3 , 5 ):
195
- help (1 )
196
-
197
265
passwd = None
198
266
for i in range (len (sys .argv )):
199
267
if sys .argv [i ] == '-p' :
200
268
sys .argv .pop (i )
201
269
passwd = sys .argv .pop (i )
202
270
break
203
271
204
- if not passwd :
272
+ if len (sys .argv ) not in (2 , 3 ):
273
+ help (1 )
274
+
275
+ if passwd is None :
205
276
import getpass
206
277
passwd = getpass .getpass ()
207
278
208
- if ":" in sys .argv [1 ] and ":" in sys .argv [2 ]:
209
- error ("Operations on 2 remote files are not supported" )
210
- if ":" not in sys .argv [1 ] and ":" not in sys .argv [2 ]:
211
- error ("One remote file is required" )
279
+ if len (sys .argv ) > 2 :
280
+ if ":" in sys .argv [1 ] and ":" in sys .argv [2 ]:
281
+ error ("Operations on 2 remote files are not supported" )
282
+ if ":" not in sys .argv [1 ] and ":" not in sys .argv [2 ]:
283
+ error ("One remote file is required" )
212
284
213
- if ":" in sys .argv [1 ]:
285
+ if len (sys .argv ) == 2 :
286
+ op = "repl"
287
+ host , port , _ = parse_remote (sys .argv [1 ] + ":" )
288
+ elif ":" in sys .argv [1 ]:
214
289
op = "get"
215
290
host , port , src_file = parse_remote (sys .argv [1 ])
216
291
dst_file = sys .argv [2 ]
@@ -227,7 +302,8 @@ def main():
227
302
228
303
if True :
229
304
print ("op:%s, host:%s, port:%d, passwd:%s." % (op , host , port , passwd ))
230
- print (src_file , "->" , dst_file )
305
+ if op in ("get" , "put" ):
306
+ print (src_file , "->" , dst_file )
231
307
232
308
s = socket .socket ()
233
309
@@ -246,7 +322,9 @@ def main():
246
322
# Set websocket to send data marked as "binary"
247
323
ws .ioctl (9 , 2 )
248
324
249
- if op == "get" :
325
+ if op == "repl" :
326
+ do_repl (ws )
327
+ elif op == "get" :
250
328
get_file (ws , dst_file , src_file )
251
329
elif op == "put" :
252
330
put_file (ws , src_file , dst_file )
0 commit comments