This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / 5pkgs / autowifi / autowifi.py
7.5 kB 290 lines
1import argparse 2import json 3import logging 4import os 5import re 6import signal 7import socket 8import struct 9import subprocess 10import time 11import urllib.request 12 13logger = logging.getLogger() 14got_signal = False 15 16 17def signal_handler(_, __): 18 global got_signal 19 got_signal = True 20 21 22def get_default_gateway() -> str: 23 """Read the default gateway directly from /proc.""" 24 with open("/proc/net/route") as route_file: 25 for line in route_file: 26 fields = line.strip().split() 27 if fields[1] != '00000000' or not int(fields[3], 16) & 2: 28 continue 29 30 return socket.inet_ntoa(struct.pack("<L", int(fields[2], 16))) 31 32 33def connect(ssid, psk=None): 34 subprocess.run( 35 ["nmcli", "connection", "delete", "autowifi"], 36 stdout=subprocess.PIPE, 37 ) 38 logging.info('connecting to %s', ssid) 39 if psk is None: 40 subprocess.run( 41 [ 42 "nmcli", 43 "device", 44 "wifi", 45 "connect", 46 ssid, 47 "name", 48 "autowifi", 49 ], 50 stdout=subprocess.PIPE, 51 ) 52 else: 53 subprocess.run( 54 [ 55 "nmcli", 56 "device", 57 "wifi", 58 "connect", 59 ssid, 60 "name", 61 "autowifi", 62 "password", 63 psk, 64 ], 65 stdout=subprocess.PIPE, 66 ) 67 time.sleep(5) 68 69 70def scan(): 71 logging.debug('scanning wifis') 72 wifis_raw = subprocess.check_output([ 73 "nmcli", 74 "-e", "no", 75 "-f", "ssid,signal,security,rsn-flags,bssid", 76 "-t", 77 "device", "wifi", "list", 78 "--rescan", "auto", 79 ]) 80 wifis_list = wifis_raw.split(b'\n') 81 logging.debug('scanning wifis finished') 82 wifis = [] 83 for line in wifis_list: 84 logging.debug(line) 85 ls = re.split(rb'(?<!\\):', line) 86 if len(ls) == 10: 87 wifis.append({ 88 "ssid": ls[0], 89 "signal": int(ls[1]), 90 "crypto": ls[2], 91 "flags": ls[3], 92 "bssid": b':'.join(ls[4:]), 93 }) 94 return wifis 95 96 97def get_known_wifis(wifi_dirs): 98 wifis_lines = [] 99 for wdir in wifi_dirs: 100 for wfile in os.listdir(wdir): 101 with open(os.path.join(wdir, wfile)) as f: 102 wifis_lines += f.read().splitlines() 103 wifis = [] 104 for line in wifis_lines: 105 ls = line.split('/') 106 wifis.append({"ssid": ls[0].encode(), "psk": ls[1].encode()}) 107 return wifis 108 109 110def check_network(): 111 logging.debug('checking network') 112 113 gateway = get_default_gateway() 114 if gateway: 115 response = subprocess.run( 116 [ 117 'ping', 118 '-q', 119 '-c', 120 '1', 121 gateway, 122 ], 123 stdout=subprocess.PIPE, 124 ) 125 if response.returncode == 0: 126 logging.debug('host %s is up', gateway) 127 return True 128 else: 129 logging.debug('host %s is down', gateway) 130 return False 131 else: 132 logging.debug('no gateway') 133 return False 134 135 136def check_internet(): 137 logging.debug('checking internet') 138 139 try: 140 beacon = urllib.request.urlopen('http://krebsco.de/secret') 141 except Exception as e: # noqa 142 logging.debug(e) 143 logging.info('no internet exc') 144 return False 145 if beacon.read() == b'1337\n': 146 return True 147 logging.info('no internet oh') 148 return False 149 150 151def is_wifi_open(wifi): 152 if wifi['crypto'] == b'': 153 return True 154 else: 155 return False 156 157 158def is_wifi_seen(wifi, seen_wifis): 159 for seen_wifi in seen_wifis: 160 if seen_wifi["ssid"] == wifi["ssid"]: 161 return True 162 return False 163 164 165def run_hooks(dirs, wifi): 166 outputs = {} 167 for directory in dirs: 168 for hfile in os.listdir(directory): 169 try: 170 hook_output = subprocess.check_output([ 171 str(os.path.join(directory, hfile)), 172 json.dumps(wifi) 173 ]) 174 outputs[hfile] = hook_output 175 except OSError as e: 176 logging.debug(e) 177 logging.info('running plugin: {} failed'.format(hfile)) 178 return outputs 179 180 181def stay_connected_loop(allowed_fails=5): 182 fails = 0 183 if check_network(): 184 while True: 185 global got_signal 186 if got_signal: 187 logging.info('got disconnect signal') 188 got_signal = False 189 break 190 else: 191 if check_network(): 192 time.sleep(10) 193 else: 194 fails += 1 195 if fails > allowed_fails: 196 logging.debug('network gone, giving up') 197 break 198 else: 199 logging.debug('hmm, missed ping, will try again shortly') 200 time.sleep(3) 201 202 203def main(): 204 parser = argparse.ArgumentParser() 205 206 parser.add_argument( 207 '-x', '--scan-hooks', 208 dest='scan_hooks', 209 nargs='*', 210 help='directories with scan hooks (executed after scan)', 211 default=['/etc/autowifi/scan_hooks'], 212 ) 213 214 parser.add_argument( 215 '-c', '--connection-hooks', 216 dest='connect_hooks', 217 nargs='*', 218 help='directories with connection hooks (executed after connection)', 219 default=['/etc/autowifi/connect_hooks'], 220 ) 221 222 parser.add_argument( 223 '-w', '--wifi_dirs', 224 dest='wifi_dirs', 225 nargs='*', 226 help='directories with wifi configs', 227 default=['/etc/autowifi/wifis'], 228 ) 229 230 parser.add_argument( 231 '-l', '--loglevel', 232 dest='loglevel', 233 help='loglevel to use', 234 default=logging.INFO, 235 ) 236 237 parser.add_argument( 238 '-p', '--pidfile', 239 dest='pidfile', 240 help='file to write the pid to', 241 default=None, 242 ) 243 244 parser.add_argument( 245 '--no-open', 246 dest='no_open_networks', 247 help='dont connect to open networks', 248 default=False, 249 action='store_true', 250 ) 251 252 args = parser.parse_args() 253 254 wifi_dirs = args.wifi_dirs 255 logger.setLevel(args.loglevel) 256 257 signal.signal(signal.SIGUSR1, signal_handler) 258 259 if args.pidfile: 260 with open(args.pidfile, 'w+') as f: 261 f.write(str(os.getpid())) 262 263 while True: 264 time.sleep(1) 265 stay_connected_loop() 266 wifis = scan() 267 known_wifis = get_known_wifis(wifi_dirs) 268 known_seen_wifis = [ 269 wifi for wifi in known_wifis if is_wifi_seen(wifi, wifis) 270 ] 271 for wifi in known_seen_wifis: 272 connect(wifi['ssid'], wifi['psk']) 273 if check_network(): 274 logging.debug('connected to secure wifi, going into busy loop') 275 break 276 if check_network(): 277 continue 278 logging.debug('no known secure wifi, connecting to open ones') 279 if not args.no_open_networks: 280 logging.debug('connecting to open wifis') 281 open_wifis = filter(is_wifi_open, wifis) 282 for wifi in open_wifis: 283 connect(wifi['ssid']) 284 if check_network(): 285 run_hooks(args.connect_hooks, wifi) 286 break 287 288 289if __name__ == '__main__': 290 main()