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