|
1 | 1 | import sys |
2 | | -import socket |
3 | | -import time |
| 2 | +#import uos as os |
| 3 | +import os |
| 4 | +import machine |
4 | 5 |
|
5 | | -from websocket import * |
6 | | -import websocket_helper |
| 6 | +RC = "./boot.py" |
| 7 | +CONFIG = "./webrepl_cfg.py" |
7 | 8 |
|
8 | | - |
9 | | -def setup_server(): |
10 | | - s = socket.socket() |
11 | | - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
12 | | - |
13 | | - ai = socket.getaddrinfo("0.0.0.0", 8266) |
14 | | - addr = ai[0][4] |
15 | | - |
16 | | - s.bind(addr) |
17 | | - s.listen(1) |
18 | | - return s |
19 | | - |
20 | | -def getpass(stream, prompt): |
21 | | - stream.write(prompt) |
22 | | - passwd = b"" |
| 9 | +def input_choice(prompt, choices): |
23 | 10 | while 1: |
24 | | - c = stream.read(1) |
25 | | - if c in (b"\r", b"\n"): |
26 | | - stream.write("\r\n") |
27 | | - return passwd |
28 | | - passwd += c |
29 | | - stream.write("*") |
30 | | - |
31 | | -def handle_conn(listen_sock): |
32 | | - cl, remote_addr = listen_sock.accept() |
33 | | - |
34 | | - print(""" |
35 | | -
|
36 | | -First-time WebREPL connection has been received. WebREPL initial setup |
37 | | -will now start over this connection. During setup, UART REPL will be |
38 | | -non-responsive. After setup finishes, the board will be rebooted. In |
39 | | -case of error during setup, current session will continue. |
40 | | -
|
41 | | -If you receive this message unexpectedly, it may mean that your WebREPL |
42 | | -connection is being hacked (power off board if unsure). |
43 | | -""") |
44 | | - |
45 | | - websocket_helper.server_handshake(cl) |
46 | | - ws = websocket(cl) |
47 | | - |
48 | | - ws.write("""\ |
49 | | -Welcome to MicroPython WebREPL!\r |
50 | | -\r |
51 | | -This is the first time you connect to WebREPL, so please set a password\r |
52 | | -to use for the following WebREPL sessions. Once you enter the password\r |
53 | | -twice, your board will reboot with WebREPL running in active mode. On\r |
54 | | -some boards, you may need to press reset button or reconnect power.\r |
55 | | -\r |
56 | | -""") |
| 11 | + resp = input(prompt) |
| 12 | + if resp in choices: |
| 13 | + return resp |
57 | 14 |
|
| 15 | +def getpass(prompt): |
| 16 | + return input(prompt) |
| 17 | + |
| 18 | +def input_pass(): |
58 | 19 | while 1: |
59 | | - passwd1 = getpass(ws, "New password: ") |
| 20 | + passwd1 = getpass("New password: ") |
60 | 21 | if len(passwd1) < 4: |
61 | | - ws.write("Password too short\r\n") |
| 22 | + print("Password too short") |
62 | 23 | continue |
63 | 24 | elif len(passwd1) > 9: |
64 | | - ws.write("Password too long\r\n") |
| 25 | + print("Password too long") |
65 | 26 | continue |
66 | | - passwd2 = getpass(ws, "Confirm password: ") |
| 27 | + passwd2 = getpass("Confirm password: ") |
67 | 28 | if passwd1 == passwd2: |
68 | | - break |
69 | | - ws.write("Passwords do not match\r\n") |
70 | | - |
71 | | - with open("port_config.py", "w") as f: |
72 | | - f.write("WEBREPL_PASS = %r\n" % passwd1.decode("ascii")) |
| 29 | + return passwd1 |
| 30 | + print("Passwords do not match") |
73 | 31 |
|
74 | | - ws.write("Password successfully set, restarting...\r\n") |
75 | | - cl.close() |
76 | | - time.sleep(2) |
77 | | - import machine |
78 | | - machine.reset() |
79 | 32 |
|
| 33 | +def exists(fname): |
| 34 | + try: |
| 35 | + with open(fname): |
| 36 | + pass |
| 37 | + return True |
| 38 | + except OSError: |
| 39 | + return False |
80 | 40 |
|
81 | | -def test(): |
82 | | - s = setup_server() |
83 | | - handle_conn(s) |
| 41 | +def copy_stream(s_in, s_out): |
| 42 | + buf = bytearray(64) |
| 43 | + while 1: |
| 44 | + sz = s_in.readinto(buf) |
| 45 | + s_out.write(buf, sz) |
| 46 | + |
| 47 | + |
| 48 | +def get_daemon_status(): |
| 49 | + with open(RC) as f: |
| 50 | + for l in f: |
| 51 | + if "webrepl" in l: |
| 52 | + if l.startswith("#"): |
| 53 | + return False |
| 54 | + return True |
| 55 | + return None |
| 56 | + |
| 57 | +def add_daemon(): |
| 58 | + with open(RC) as old_f, open(RC + ".tmp", "w") as new_f: |
| 59 | + new_f.write("import webrepl\nwebrepl.start()\n") |
| 60 | + copy_stream(old_f, new_f) |
| 61 | + |
| 62 | +def change_daemon(action): |
| 63 | + LINES = ("import webrepl", "webrepl.start()") |
| 64 | + with open(RC) as old_f, open(RC + ".tmp", "w") as new_f: |
| 65 | + for l in old_f: |
| 66 | + for patt in LINES: |
| 67 | + if patt in l: |
| 68 | + if action and l.startswith("#"): |
| 69 | + l = l[1:] |
| 70 | + elif not action and not l.startswith("#"): |
| 71 | + l = "#" + l |
| 72 | + new_f.write(l) |
| 73 | + # FatFs rename() is not POSIX compliant, will raise OSError if |
| 74 | + # dest file exists. |
| 75 | + os.remove(RC) |
| 76 | + os.rename(RC + ".tmp", RC) |
| 77 | + |
| 78 | + |
| 79 | +def main(): |
| 80 | + status = get_daemon_status() |
| 81 | + |
| 82 | + print("WebREPL daemon auto-start status:", "enabled" if status else "disabled") |
| 83 | + print("\nWould you like to (E)nable or (D)isable it running on boot?") |
| 84 | + print("(Empty line to quit)") |
| 85 | + resp = input("> ").upper() |
| 86 | + |
| 87 | + if resp == "E": |
| 88 | + if exists(CONFIG): |
| 89 | + resp2 = input_choice("Would you like to change WebREPL password? (y/n) ", ("y", "n", "")) |
| 90 | + else: |
| 91 | + print("To enable WebREPL, you must set password for it") |
| 92 | + resp2 = "y" |
| 93 | + |
| 94 | + if resp2 == "y": |
| 95 | + passwd = input_pass() |
| 96 | + with open(CONFIG, "w") as f: |
| 97 | + f.write("PASS = %r\n" % passwd) |
| 98 | + |
| 99 | + |
| 100 | + if resp not in ("D", "E") or (resp == "D" and not status) or (resp == "E" and status): |
| 101 | + print("No further action required") |
| 102 | + sys.exit() |
| 103 | + |
| 104 | + change_daemon(resp == "E") |
| 105 | + |
| 106 | + print("Changes will be activated after reboot") |
| 107 | + resp = input_choice("Would you like to reboot now? (y/n) ", ("y", "n", "")) |
| 108 | + if resp == "y": |
| 109 | + machine.reset() |
| 110 | + |
| 111 | +main() |
0 commit comments