def on_binary(self, buf, handler): try: l = len(buf) if self.padding_length > l: self.tmpfile.write(buf) self.padding_length -= l else: if self.padding_length == l: self.tmpfile.write(buf) else: self.tmpfile.write(buf[:self.padding_length]) logger.error("Recv data length error") handler.binary_mode = False logger.info("New firmware received") self.tmpfile.file.flush() self.tmpfile.seek(0) s = Storage("update_fw") with s.open("upload.fxfw", "wb") as f: f.write(self.tmpfile.read()) proc = Popen(["fxupdate.py", "--dryrun", self.tmpfile.name]) self.subwatcher = self.stack.loop.child( proc.pid, False, self.on_verified, (handler, proc)) self.subwatcher.start() except RuntimeError as e: handler.send_text(("error %s" % e.args[0]).encode()) except Exception: logger.exception("Unhandle Error") handler.send_text("error %s" % UNKNOWN_ERROR)
def on_binary(self, buf, handler): try: l = len(buf) if self.padding_length > l: self.tmpfile.write(buf) self.padding_length -= l else: if self.padding_length == l: self.tmpfile.write(buf) else: self.tmpfile.write(buf[:self.padding_length]) handler.binary_mode = False self.tmpfile.seek(0) s = Storage("update_fw") with s.open("mainboard.bin", "wb") as f: f.write(self.tmpfile.read()) logging.warn("Fireware uploaded, start processing") self.send_upload_request() handler.send_text("ok") self.stack.exit_task(self, True) except RuntimeError as e: handler.send_text(("error %s" % e.args[0]).encode()) except Exception: logger.exception("Unhandle Error") handler.send_text("error %s" % UNKNOWN_ERROR)
def on_binary(self, buf, handler): try: l = len(buf) if self.padding_length > l: self.tmpfile.write(buf) self.padding_length -= l else: if self.padding_length == l: self.tmpfile.write(buf) else: self.tmpfile.write(buf[:self.padding_length]) handler.binary_mode = False self.tmpfile.seek(0) s = Storage("update_fw") with s.open("head.bin", "wb") as f: f.write(self.tmpfile.read()) logger.info("Head fireware uploaded, start processing") handler.send_text("ok") self.process_update(handler) # Note: self.process_update will invoke self.stack.exit_task except RuntimeError as e: handler.send_text(("error %s" % e.args[0]).encode()) except Exception: logger.exception("Unhandle Error") handler.send_text("error " + UNKNOWN_ERROR)
def start(self): GPIOUtils.setup() hwprofile = GPIOUtils.get_hardware_profile() while hwprofile is None or "HARDWARE_VERSION" not in hwprofile: logger.error("Fetch hardware profile failed: %s", hwprofile) try: logger.error("Re-burn firmware") fsrc = resource_stream("fluxmonitor", "data/mainboard.bin") storage = Storage("update_fw") with storage.open("mainboard.bin", "wb") as fdst: copyfileobj(fsrc, fdst) GPIOUtils.update_mbfw() except RuntimeWarning as e: logger.error("%s", e.args[0]) except Exception: logger.exception("Re-burn firmware failed") sleep(3.0) hwprofile = GPIOUtils.get_hardware_profile() self.hwprofile = hwprofile if get_model_id() == MODEL_D1P: self.gpio = PinMappingV1(self.meta) else: self.gpio = PinMappingV0(self.meta) self.btn_monitor = FrontButtonMonitor(self.gpio, self.kernel.loop, self.send_button_event) self.connect_uart()
def update_mbfw(cls): cls.reset_mainboard() sleep(1.0) storage = Storage("update_fw") tty = cls.get_mainboard_port() if not storage.exists("mainboard.bin"): raise RuntimeWarning("mainboard.bin not found") if os.system("stty -F %s 1200" % tty) != 0: raise RuntimeWarning("stty exec failed") sleep(2.0) fw_path = storage.get_path("mainboard.bin") if os.system("bossac -p %s -e -w -v -b %s" % (tty.split("/")[-1], fw_path)) != 0: raise RuntimeWarning("bossac exec failed") os.rename(fw_path, fw_path + ".updated") os.system("sync") sleep(0.75) cls.reset_mainboard() sleep(0.75)
def autoplay(self): storage = Storage("general", "meta") if storage["replay"] != "N": pathlist = (("USB", "autoplay.fc"), ( "SD", "autoplay.fc", ), ( "SD", "Recent/recent-1.fc", )) else: pathlist = (("USB", "autoplay.fc"), ( "SD", "autoplay.fc", )) us = UserSpace() for candidate in pathlist: try: abspath = us.get_path(*candidate, require_file=True) logger.debug("Autoplay: %s", abspath) pm = PlayerManager(self.loop, abspath, self.release_exclusive) self.exclusive(pm) return except RuntimeError: continue logger.debug("Autoplay failed")
def setup_upnp_sock(self): try: logger.info("Upnp going UP") storage = Storage("general", "meta") bare = storage["bare"] == "Y" bcst_config = storage["broadcast"] mcst_if = create_interface_v2(MulticaseNotifyInterface)( self) if bare else MulticaseNotifyInterface(self) # noqa mcst_watcher = self.loop.io(mcst_if, pyev.EV_READ, mcst_if.on_message) mcst_watcher.start() self.mcst = (mcst_if, mcst_watcher) ucst_if = create_interface_v2(UnicasetInterface)( self) if bare else UnicasetInterface(self) # noqa ucst_watcher = self.loop.io(ucst_if, pyev.EV_READ, ucst_if.on_message) ucst_watcher.start() self.ucst = (ucst_if, ucst_watcher) if bcst_config != "N": bcst_if = create_interface_v2(BroadcaseNotifyInterface)( self, bcst_config) if bare else BroadcaseNotifyInterface( self, bcst_config) # noqa bcst_watcher = self.loop.io(bcst_if, pyev.EV_READ, bcst_if.on_message) self.bcst = (bcst_if, bcst_watcher) self.mversion = self.meta.mversion except socket.error: logger.exception("Error while upnp going UP") self.teardown_upnp_sock()
def setUp(self): host = socket.socket() host.bind(("127.0.0.1", 0)) host.listen(1) self.client_sock = cs = socket.socket() cs.setblocking(False) cs.connect_ex(host.getsockname()) self.serverSock, self.clientEndpoint = host.accept() host.close() self.loop = pyev.Loop() _prepare_cert() s = Storage("security", "private") self.certfile = s.get_path("cert.pem") self.keyfile = s.get_path("sslkey.pem")
def dispatch_cmd(self, handler, cmd, *args): try: if cmd == "player": return self.dispatch_playmanage_cmd(handler, *args) elif cmd == "file": return self.dispatch_filemanage_cmd(handler, *args) elif cmd == "task": return self.dispatch_task_cmd(handler, *args) elif cmd == "config": return self.dispatch_config_cmd(handler, *args) except TypeError: tb = sys.exc_info()[2] if tb.tb_next is None or tb.tb_next.tb_next is None: raise RuntimeError(BAD_PARAMS) else: raise try: if cmd == "update_fw": mimetype, filesize, upload_to = args if mimetype != mimetypes.MIMETYPE_FLUX_FIRMWARE: raise RuntimeError(BAD_FILE_FORMAT) self.update_fw(handler, int(filesize, 10)) elif cmd == "kick": self.stack.kernel.destory_exclusive() handler.send_text("ok") elif cmd == "update_mbfw" and allow_god_mode(): mimetype, filesize, upload_to = args return self.update_mbfw(handler, int(filesize, 10)) elif cmd == "deviceinfo": self.deviceinfo(handler) elif cmd == "scan": # TODO: going tobe remove self.dispatch_task_cmd(handler, "scan") elif cmd == "start": # TODO: going tobe removed self.dispatch_playmanage_cmd(handler, "start") self.play(handler) elif cmd == "maintain": # TODO: going tobe removed self.dispatch_task_cmd(handler, "maintain") elif cmd == "cloud_validation_code": self.cloud_validation_code(handler) elif cmd == "oracle": s = Storage("general", "meta") s["debug"] = args[0].encode("utf8") handler.send_text("oracle") elif cmd == "fetch_log": self.fetch_log(handler, *args) else: logger.debug("Can not handle: %s" % repr(cmd)) raise RuntimeError(UNKNOWN_COMMAND) except TypeError: tb = sys.exc_info()[2] if tb.tb_next is None: raise RuntimeError(BAD_PARAMS) else: raise
def __init__(self, loop, taskfile, terminated_callback=None): storage = Storage("run") oldpid = load_pid(storage.get_path("fluxplayerd.pid")) if oldpid is not None: try: os.kill(oldpid, SIGKILL) logger.error("Kill old player process: %i", oldpid) except Exception: logger.exception("Error while kill old process: %i", oldpid) s = create_mainboard_socket() try: s.send("\n@DISABLE_LINECHECK\nX5S115\n") if os.path.exists(PLAY_ENDPOINT): os.unlink(PLAY_ENDPOINT) ff = FCodeFile(taskfile) self.playinfo = ff.metadata, ff.image_buf cmd = [ "fluxplayer", "-c", PLAY_ENDPOINT, "--task", taskfile, "--log", storage.get_path("fluxplayerd.log"), "--pid", storage.get_path("fluxplayerd.pid") ] if logger.getEffectiveLevel() <= 10: cmd += ["--debug"] f = open(storage.get_path("fluxplayerd.err.log"), "a") proc = Popen(cmd, stdin=PIPE, stderr=f.fileno()) child_watcher = loop.child(proc.pid, False, self.on_process_dead, terminated_callback) child_watcher.start() metadata.update_device_status(1, 0, "N/A", err_label="") self.child_watcher = child_watcher self.proc = proc except FCodeError as e: s.send("X5S0\n") raise RuntimeError(FILE_BROKEN, *e.args) except Exception as e: s.send("X5S0\n") raise RuntimeError(UNKNOWN_ERROR) finally: s.close()
def __config_del(self, key): storage = Storage("general", "meta") if key in self.__VALUES: struct = self.__VALUES[key] if hasattr(metadata, struct["key"]): delattr(metadata, struct["key"]) else: del storage[struct["key"]] else: raise RuntimeError(BAD_PARAMS)
def __init__(self, options): super(CloudService, self).__init__(logger, options) mqttlogger = logging.getLogger( "AWSIoTPythonSDK.core.protocol.mqttCore") if logger.getEffectiveLevel() < logging.INFO: mqttlogger.setLevel(logging.DEBUG) else: mqttlogger.setLevel(logging.WARNING) self.storage = Storage("cloud") self.uuidhex = security.get_uuid() if options.cloud.endswith("/"): self.cloud_netloc = options.cloud[:-1] else: self.cloud_netloc = options.cloud self.diagnosis()
def postback_status(self, st_id): url = Storage("general", "meta")["player_postback_url"] if url: try: if '"' in url or '\\' in url: logger.error("Bad url: %r", url) else: url = url % {"st_id": st_id} os.system("curl -s -o /dev/null \"%s\"" % url) except Exception: logger.exception("Error while post back status, url: %s", url)
def __config_set(self, key, val): storage = Storage("general", "meta") if key in self.__VALUES: struct = self.__VALUES[key] # Check input correct cval = struct["type"](val) # Check enum if "enum" in struct and cval not in struct["enum"]: raise RuntimeError(BAD_PARAMS) if "min" in struct and cval < struct["min"]: raise RuntimeError(BAD_PARAMS) if "max" in struct and cval > struct["max"]: raise RuntimeError(BAD_PARAMS) if "maxlen" in struct and len(cval) > struct["maxlen"]: raise RuntimeError(BAD_PARAMS) if hasattr(metadata, struct["key"]): setattr(metadata, struct["key"], val) else: storage[struct["key"]] = val elif key == "backlash": d = {} for kv in val.split(" "): if ":" in kv: k, sv = kv.split(":") try: v = float(sv) if k in "ABC" and v >= 0 and v <= 100: d[k] = v except ValueError: pass Preference.instance().backlash = d elif key == "leveling": d = {} for kv in val.split(" "): if ":" in kv: k, sv = kv.split(":") try: v = float(sv) if k in "XYZ" and v <= 0 and v >= -2: d[k] = v elif k in "R" and v <= 101 and v >= 92: d[k] = v elif k == "H" and v > 120 and v < 243: d[k] = v except ValueError: pass Preference.instance().plate_correction = d else: raise RuntimeError(BAD_PARAMS)
def allow_god_mode(): from fluxmonitor.security._security import is_dev_model from fluxmonitor.storage import Storage if is_dev_model(): return True else: s = Storage("general", "meta") magic_str = s["debug"] if magic_str: return sha1(magic_str).hexdigest() == DEBUG_STR else: return False
def get_cert(): from binascii import b2a_base64 as to_base64 from OpenSSL import crypto from fluxmonitor.storage import Storage s = Storage("security", "private") try: key = crypto.load_privatekey(crypto.FILETYPE_PEM, s["sslkey.pem"]) except (crypto.Error, TypeError): pkey = get_private_key() s["sslkey.pem"] = pem = pkey.export_pem() key = crypto.load_privatekey(crypto.FILETYPE_PEM, pem) try: cert = crypto.load_certificate(crypto.FILETYPE_PEM, s["cert2.pem"]) except (crypto.Error, TypeError): issuersubj = crypto.X509Name(crypto.X509().get_subject()) issuersubj.C = "TW" issuersubj.L = "Taipei" issuersubj.O = "FLUX Inc." cert = crypto.X509() subj = cert.get_subject() subj.O = "FLUX 3D Delta Printer" subj.CN = (get_uuid() + ":" + get_serial() + ":") ext = crypto.X509Extension("nsComment", True, to_base64(get_identify())) cert.add_extensions((ext, )) cert.set_serial_number(1001) cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(60 * 365 * 24 * 60 * 60) cert.set_issuer(cert.get_subject()) cert.set_pubkey(key) cert.sign(key, 'sha512') s["cert2.pem"] = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) return s.get_path("cert2.pem"), s.get_path("sslkey.pem")
def __config_get(self, key): storage = Storage("general", "meta") if key in self.__VALUES: struct = self.__VALUES[key] if hasattr(metadata, struct["key"]): return getattr(metadata, struct["key"]) else: return storage[struct["key"]] elif key == "backlash": return " ".join("%s:%.4f" % (k, v) for k, v in Preference.instance().backlash.items()) elif key == "leveling": return " ".join( "%s:%.4f" % (k, v) for k, v in Preference.instance().plate_correction.items() if k in "XYZRH") else: raise RuntimeError(BAD_PARAMS)
def __init__(self): camera_model = halprofile.PROFILE.get("scan_camera_model") if camera_model == 2: self._camera = Camera(0, width=1280, height=720) self.rotate = 1 else: global delta_camera_option if delta_camera_option is None: from fluxmonitor.storage import Storage storage = Storage("general", "meta") delta_camera_option = 1 if storage["camera_version"] == "1" \ else 0 if delta_camera_option == 1: self._camera = Camera(0, width=1280, height=720) self.rotate = 1 else: self._camera = Camera(0)
def on_compute_cab(step, m): m = m.split()[1] data["calibrate_param"].append(m) if len(data["calibrate_param"]) < 3: begin_compute_cab() else: if 'fail' in data["calibrate_param"]: output = ' '.join(data["calibrate_param"]) on_loop('fail laser ' + output) elif all(abs(float(r) - float(data["calibrate_param"][0])) < 72 for r in data["calibrate_param"][1:]): # so naive check s = Storage('camera') s['calibration'] = ' '.join( map(lambda x: str(round(float(x))), data["calibrate_param"])) output = ' '.join(data["calibrate_param"]) on_loop(output) else: output = ' '.join(data["calibrate_param"]) on_loop('fail laser ' + output)
def __init__(self, options): ServiceBase.__init__(self, logger, options) self.internl_interface = RobotUnixStreamInterface(self) if Storage("general", "meta")["bare"] == "Y": self.tcp_interface = RobotSSLInterface(self) else: self.tcp_interface = RobotTcpInterface(self) self._hal_reset_timer = self.loop.timer(5, 0, self._connect2hal) self._cloud_conn = set() try: if options.taskfile: logger.debug("Autoplay: %s", options.taskfile) pm = PlayerManager(self.loop, options.taskfile, self.release_exclusive) self.exclusive(pm) elif options.autoplay: self.autoplay() except Exception: logger.exception("Error while setting task at init")
def __init__(self, taskloader=None, head=None): storage = Storage("general", "meta") metadata = taskloader.metadata if taskloader else {} self.additional_macros = [] self._setup_toolhead(head, metadata) self._setup_leveling(storage, metadata) self._setup_filament_detect(storage, metadata) self._setup_toolhead_error_level(storage, metadata) self._setup_backlash(storage, metadata) self._setup_max_xyzr(metadata) self._setup_common(storage) self._setup_fcode_special_request(metadata) # ALL DIRTY THINGS START FROM HERE # TODO: enable hardware error for toolhead pwm issue self.head_error_level |= 64 if self.head != "EXTRUDER": # Only EXTRUDER need filament detect self.filament_detect = False # Only EXTRUDER allowed correction self.correction = "N" self.movement_test = False
def cloud_validation_code(self, handler): handler.send_text("ok %s %s" % (Storage("cloud")["token"], b2a_base64(metadata.cloud_hash)))
class CloudService(ServiceBase): config_ts = -1 error_counter = 0 config_enable = None cloud_netloc = None storage = None _notify_up_required = False _notify_last_st = {"st_id": None} _notify_last_ts = 0 _notify_aggressive = 0 _notify_retry_counter = 0 aws_client = None postback_url = None def __init__(self, options): super(CloudService, self).__init__(logger, options) mqttlogger = logging.getLogger( "AWSIoTPythonSDK.core.protocol.mqttCore") if logger.getEffectiveLevel() < logging.INFO: mqttlogger.setLevel(logging.DEBUG) else: mqttlogger.setLevel(logging.WARNING) self.storage = Storage("cloud") self.uuidhex = security.get_uuid() if options.cloud.endswith("/"): self.cloud_netloc = options.cloud[:-1] else: self.cloud_netloc = options.cloud self.diagnosis() def on_start(self): logger.info("Cloud service started (version=%s)", __version__) if security.get_serial() == "XXXXXXXXXX": logger.error("Serial invalid, cloud deamon will be silenced.") else: self.timer = self.loop.timer(3., 3., self.on_timer) self.timer.start() def require_identify(self): logger.debug("Require identify") url = urlparse("%s/axon/require_identify" % self.cloud_netloc) pkey = security.get_private_key() publickey_base64 = b2a_base64(pkey.export_pubkey_der()) serial = security.get_serial() model = get_model_id() identify_base64 = b2a_base64(security.get_identify()) try: conn = get_connection(url) logger.debug("Require identify request sent") conn.post_request( url.path, { "serial": serial, "uuid": self.uuidhex, "model": model, "version": __version__, "publickey": publickey_base64, "signature": identify_base64 }) resp = conn.get_json_response() if resp.get("status") == "ok": return resp["token"].encode(), resp["subject"] elif resp.get("status") == "error": raise RuntimeWarning(*resp["error"]) else: logger.error("require_identify response unknown response: %s", resp) raise RuntimeWarning("RESPONSE_ERROR") except socket.gaierror as e: raise RuntimeError("REQUIRE_IDENTIFY", "DNS_ERROR", e) except socket.error as e: raise RuntimeError("REQUIRE_IDENTIFY", "CONNECTION_ERROR", e) except RuntimeWarning as e: raise RuntimeError("REQUIRE_IDENTIFY", *e.args) except Exception as e: logger.exception("Error in require identify") raise RuntimeError("REQUIRE_IDENTIFY", "UNKNOWN_ERROR", e) def get_identify(self, token, request_asn1): logger.debug("Get identify") url = urlparse("%s/axon/identify" % self.cloud_netloc) uuidhex = security.get_uuid() try: conn = get_connection(url) logger.debug("Get identify request sent") conn.post_request( url.path, { "uuid": uuidhex, "token": token, "x509_request": request_asn1, "metadata": { "version": __version__ } }) resp = conn.get_json_response() if resp.get("status") == "ok": return resp elif resp.get("status") == "error": raise RuntimeWarning(*resp["error"]) else: logger.error("get identify response unknown response: %s", resp) raise RuntimeWarning("RESPONSE_ERROR") except socket.gaierror as e: raise RuntimeError("GET_IDENTIFY", "DNS_ERROR", e) except socket.error as e: raise RuntimeError("GET_IDENTIFY", "CONNECTION_ERROR", e) except RuntimeWarning as e: raise RuntimeError("GET_IDENTIFY", *e.args) except Exception as e: logger.exception("Error in get identify") raise RuntimeError("GET_IDENTIFY", "UNKNOWN_ERROR", e) def generate_certificate_request(self, subject_list): from subprocess import Popen, PIPE fxkey = security.get_private_key() self.storage["key.pem"] = fxkey.export_pem() subject_str = "/" + "/".join(("=".join(i) for i in subject_list)) proc = Popen([ "openssl", "req", "-new", "-key", self.storage.get_path("key.pem"), "-subj", subject_str, "-keyform", "pem", "-nodes", "-sha256" ], stdin=PIPE, stderr=PIPE, stdout=PIPE) stdoutdata, stderrdata = proc.communicate(input="") while proc.poll() is None: pass if proc.returncode > 0: error = stdoutdata if stdoutdata else stderrdata if error: error = error.decode("utf8") else: error = "Process return %i" % proc.returncode raise SystemError(error) else: return stdoutdata # csr = crypto.X509Req() # subj = csr.get_subject() # for name, value in subject_list: # if name == "CN": # add_result = crypto._lib.X509_NAME_add_entry_by_NID( # subj._name, 13, crypto._lib.MBSTRING_UTF8, # value.encode('utf-8'), -1, -1, 0) # if not add_result: # crypto._raise_current_error() # else: # setattr(subj, name, value) # fxkey = security.get_private_key() # opensslkey = crypto.load_privatekey(crypto.FILETYPE_ASN1, # fxkey.export_der()) # self.storage["key.pem"] = fxkey.export_pem() # csr.set_pubkey(opensslkey) # csr.sign(opensslkey, "sha256") # return crypto.dump_certificate_request(crypto.FILETYPE_PEM, csr) def fetch_identify(self): logger.info("Fetch identify") token, subject_list = self.require_identify() logger.info("Require identify return token=%s, subject=%s", token, subject_list) request_asn1 = self.generate_certificate_request(subject_list) doc = self.get_identify(token, request_asn1) logger.info("Get identify return %s", doc["status"]) self.storage["token"] = token self.storage["endpoint"] = doc["endpoint"] self.storage["client_id"] = doc["client_id"] self.storage["certificate_reqs.pem"] = doc["certificate_reqs"] self.storage["certificate.pem"] = doc["certificate"] def notify_up(self, c): payload = json.dumps({ "state": { "reported": { "version": __version__, "token": self.storage["token"], "nickname": metadata.nickname } } }) c.publish(self._notify_topic, payload, 1) def postback_status(self, st_id): url = Storage("general", "meta")["player_postback_url"] if url: try: if '"' in url or '\\' in url: logger.error("Bad url: %r", url) else: url = url % {"st_id": st_id} os.system("curl -s -o /dev/null \"%s\"" % url) except Exception: logger.exception("Error while post back status, url: %s", url) def notify_update(self, new_st, now): if metadata.verify_mversion() is False: self._notify_up_required = True new_st_id = new_st["st_id"] if self._notify_last_st["st_id"] == new_st_id: if self._notify_last_ts > self._notify_aggressive: # notify aggressive is invalid if now - self._notify_last_ts < 1200: # update every 1200 seconds return else: # notify aggressive is valid if new_st_id <= 0 and now - self._notify_last_ts < 1200: # update every 1200 seconds if device is idle or occupy return elif new_st_id in (48, 64, 128): # paused, completed, aborted self.postback_status(new_st_id) c = self.aws_client.getMQTTConnection() payload = json.dumps({"state": {"reported": new_st}}) if self._notify_up_required: self.notify_up(c) self._notify_up_required = False c.publish(self._notify_topic, payload, 0) self._notify_last_st = new_st self._notify_last_ts = now def aws_on_request_callback(self, client, userdata, message): # incommint topic format: "device/{token}/request/{action}" # response topic format: "device/{token}/response/{action}" action = message.topic.split("/", 3)[-1] logger.debug("IoT request: %s", action) response_topic = "device/%s/response/%s" % (self.aws_token, action) try: payload = json.loads(message.payload) except ValueError: logger.error("IoT request payload error: %s", message.payload) client.publish(response_topic, message.payload) return if payload.get("uuid") != self.uuidhex: client.publish( response_topic, json.dumps({ "status": "reject", "cmd_index": payload.get("cmd_index") })) return cmd_index = payload.get("cmd_index") try: if action == "getchu": try: current_hash = metadata.cloud_hash access_id, signature = payload.get("validate_message") client_key = security.get_keyobj(access_id=access_id) if client_key.verify(current_hash, a2b_base64(signature)): client.publish( response_topic, json.dumps({ "status": "ok", "cmd_index": cmd_index })) else: client.publish( response_topic, json.dumps({ "status": "reject", "cmd_index": cmd_index })) finally: metadata.cloud_hash = os.urandom(32) elif action == "monitor": self._notify_aggressive = time() + 180 self._notify_last_st["st_id"] = None client.publish( response_topic, json.dumps({ "status": "ok", "cmd_index": cmd_index })) elif action == "camera": self.require_camera(payload["camera_id"], payload["endpoint"], payload["token"]) client.publish( response_topic, json.dumps({ "status": "ok", "cmd_index": cmd_index })) elif action == "control": self.require_control(payload["endpoint"], payload["token"]) client.publish( response_topic, json.dumps({ "status": "ok", "cmd_index": cmd_index })) else: client.publish( response_topic, json.dumps({ "status": "error", "cmd_index": cmd_index })) except Exception: logger.exception("Handle aws request error") client.publish( response_topic, json.dumps({ "status": "error", "cmd_index": cmd_index })) def begin_session(self): logger.info("Begin Session") if not self.storage["certificate.pem"]: metadata.cloud_status = (False, ("INIT", )) self.fetch_identify() self.setup_session() metadata.cloud_status = (True, ()) def setup_session(self): logger.info("Setup session") from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient from AWSIoTPythonSDK.core.protocol.mqttCore import ( connectTimeoutException) client_id, token = self.storage["client_id"], self.storage["token"] if not client_id or not token: raise SystemError("client_id or token error") try: ipaddr, port = self.storage["endpoint"].split(":") except (AttributeError, ValueError): raise SystemError("endpoint error") cafile = self.storage.get_path("certificate_reqs.pem") cert = self.storage.get_path("certificate.pem") key = self.storage.get_path("key.pem") if False in map(lambda p: os.path.exists(p), (cafile, cert, key)): raise SystemError("cafile or cert or key not exist") client = AWSIoTMQTTShadowClient(client_id) client.configureEndpoint(ipaddr, int(port)) client.configureCredentials(cafile, key, cert) client.configureConnectDisconnectTimeout(10) client.configureMQTTOperationTimeout(5) try: client.connect() except SSLError as e: raise RuntimeError("SESSION", "TLS_ERROR", "%s" % e.reason) except (connectTimeoutException, socket.gaierror, socket.error): raise RuntimeError("SESSION", "CONNECTION_ERROR") conn = client.getMQTTConnection() conn.subscribe("device/%s/request/+" % self.storage["token"], 1, self.aws_on_request_callback) self.aws_client = client self.aws_token = token self._notify_topic = "$aws/things/%s/shadow/update" % ( self.storage["client_id"]) self.notify_up(conn) metadata.cloud_hash = os.urandom(32) logger.info("Session ready") def teardown_session(self): if self.aws_client: aws_client = self.aws_client self.aws_client = None conn = aws_client.getMQTTConnection() try: if conn._mqttCore._pahoClient._thread.isAlive(): aws_client.disconnect() else: logger.error("MQTT thread closed, remove directly") except Exception: logger.exception("AWS panic while disconnect from aws, " "make a ugly bugfix") try: conn._mqttCore._pahoClient._thread_terminate = True if conn._mqttCore._pahoClient._sock: conn._mqttCore._pahoClient._sock.close() return except Exception: logger.exception("AWS panic ugly bugfix failed") def on_timer(self, watcher, revent): try: if self.config_ts != metadata.mversion: self.error_counter = 0 if metadata.enable_cloud == "R": logger.warning("Refetch required") if self.aws_client: self.teardown_session() metadata.enable_cloud = "A" metadata.cloud_status = (False, ("INIT", )) self.fetch_identify() self.config_ts = metadata.mversion self.config_enable = (metadata.enable_cloud == "A") if self.config_enable is False: metadata.cloud_status = (False, ("DISABLE", )) if self.config_enable: if self.aws_client: self.notify_update(metadata.format_device_status, time()) self.error_counter = max(self.error_counter - 1, 0) else: if self.error_counter in ERROR_COUNTER_MATCH: self.begin_session() self.error_counter = max(self.error_counter - 1, 0) elif self.error_counter > ERROR_COUNTER_MATCH[-1]: self.error_counter = ERROR_COUNTER_MATCH[-2] else: self.error_counter += 1 else: if self.aws_client: self.teardown_session() self._notify_retry_counter = 0 except publishQueueDisabledException: metadata.cloud_status = (False, ("SESSION", "CONNECTION_ERROR")) self._notify_retry_counter += 1 logger.exception("publishQueueDisabledException raise in notify") if self._notify_retry_counter > 10: self.teardown_session() self.error_counter += 1 except RuntimeError as e: logger.error(e) metadata.cloud_status = (False, e.args) self.error_counter += 1 self.diagnosis() except Exception: logger.exception("Unhandle error") metadata.cloud_status = (False, ("UNKNOWN_ERROR", )) self.error_counter += 1 def on_shutdown(self): pass def require_camera(self, camera_id, endpoint, token): payload = msgpack.packb((0x80, camera_id, endpoint, token)) s = socket.socket(socket.AF_UNIX) s.connect(CAMERA_ENDPOINT) s.send(payload) rl = select((s, ), (), (), 0.25)[0] if rl: logger.debug("Require camera return %s", msgpack.unpackb(s.recv(4096))) s.close() def require_control(self, endpoint, token): payload = msgpack.packb((0x80, endpoint, token)) s = socket.socket(socket.AF_UNIX) s.connect(ROBOT_ENDPOINT) s.send(payload) rl = select((s, ), (), (), 0.25)[0] if rl: logger.debug("Require robot return %s", msgpack.unpackb(s.recv(4096))) s.close() def diagnosis(self): from fluxmonitor.misc._process import Process from time import time as epoch logger.info("Diagnosis...") try: logger.info( 'ntp dns testing...\n%s\n%s', Process.call_with_output('getent', 'hosts', '0.debian.pool.ntp.org'), Process.call_with_output('getent', 'hosts', '1.debian.pool.ntp.org')) except Exception: logger.exception('ntp dns testing failed') try: logger.info('ntp service status...\n%s', Process.call_with_output('service', 'ntp', 'status')) except Exception: logger.exception('fetch ntp service status failed') try: if epoch() < 1505720271: logger.error( "System time error, fixing...\n%s", Process.call_with_output('ntp-wait', '-s', '1', '-n', '1')) if epoch() < 1505720271: logger.error("System time sync failed") except Exception: logger.exception("cloud diagnosis error")
def __init__(self, storage=None): from fluxmonitor.storage import Storage self.storage = storage if storage else Storage("security", "pub")
def update_hbfw(self, stage_cb=lambda m: None): raspi_uart = Serial(port="/dev/ttyAMA0", baudrate=115200, stopbits=1, xonxoff=0, rtscts=0, timeout=0) def wait_ack(stage): t = time() while time() - t < 5.0: rl = select((raspi_uart, ), (), (), 5.0)[0] if rl: ret = raspi_uart.read(1) if ret == 'y': return elif ret == '\x1f': raise RuntimeError(SUBSYSTEM_ERROR, "HEAD RETURN 0x1f") else: raise RuntimeError(UNKNOWN_ERROR, "HEAD RETURN %s" % repr(ret)) raise RuntimeError(NO_RESPONSE, stage) def send_cmd(cmd, stage): raspi_uart.write(chr(cmd)) raspi_uart.write(chr(cmd ^ 0xFF)) wait_ack(stage) sleep(0.1) def crc8(msg, init=0): crc = init for c in msg: crc = crc ^ ord(c) return chr(crc) def bootloader_hello(): raspi_uart.write('\x7f') wait_ack("HELLO") logger.debug("Update head fw") storage = Storage("update_fw") if not storage.exists("head.bin"): raise RuntimeError(NOT_FOUND) with storage.open("head.bin", "rb") as f: fw = f.read() size = len(fw) pages = size // 2048 if size % 2048 > 0: pages += 1 logger.debug("Fw size=%i, use page=%i", size, pages) try: # Bootstrap stage_cb(b"B") raspi_uart.parity = PARITY_EVEN GPIO.output(self.TOOLHEAD_POWER, self.TOOLHEAD_POWER_OFF) GPIO.output(self.TOOLHEAD_BOOT, GPIO.HIGH) sleep(0.5) GPIO.output(self.TOOLHEAD_POWER, self.TOOLHEAD_POWER_ON) sleep(0.5) raspi_uart.setRTS(0) raspi_uart.setDTR(0) sleep(0.1) raspi_uart.setDTR(1) sleep(0.5) # Hello stage_cb(b"H") try: bootloader_hello() except Exception: sleep(0.5) bootloader_hello() send_cmd(0x00, "G_VR") l = ord(raspi_uart.read()) version = raspi_uart.read(1) a = raspi_uart.read(l) logger.debug("VER %s", repr(a)) wait_ack("G_VRL") logger.debug("Update head: bootloader ver=%s", version) if version != '1': raise RuntimeError(NOT_SUPPORT) # Earse stage_cb(b"E") send_cmd(0x44, "G_ERASE") cmd = struct.pack(">H", pages - 1) cmd += "".join([struct.pack(">H", i) for i in xrange(pages)]) raspi_uart.write(cmd) raspi_uart.write(crc8(cmd)) wait_ack("G_E") # Write stage_cb(b"W") offset = 0 while offset < size: stage_cb(("%07x" % (size - offset)).encode()) l = min(size - offset, 128) send_cmd(0x31, "G_WINIT") addr = struct.pack(">I", 0x08000000 + offset) raspi_uart.write(addr) raspi_uart.write(crc8(addr)) wait_ack("G_WREADY") payload = chr(l - 1) + fw[offset:offset + l] raspi_uart.write(payload) raspi_uart.write(crc8(payload)) wait_ack("G_WDONE") offset += l stage_cb("00000000") finally: GPIO.output(self.TOOLHEAD_BOOT, GPIO.LOW) GPIO.output(self.TOOLHEAD_POWER, self.TOOLHEAD_POWER_OFF) sleep(0.5) GPIO.output(self.TOOLHEAD_POWER, self._head_power_stat) raspi_uart.parity = PARITY_NONE raspi_uart.close() logger.debug("Update fw end")
def get_cab(self, handler): s = Storage('camera') a = s.readall('calibration') if a is None: a = '320 320 320' handler.send_text("ok " + a)
def storage(self): return Storage("net")
def get_storage(): from fluxmonitor.storage import Storage return Storage("security", "private")
def try_config_network(dryrun=False): anti_garbage_usb_mass_storage() if os.path.exists("/media/usb/config_flux.txt"): print("USB Disk found") from ConfigParser import RawConfigParser from fluxmonitor.security import get_serial from fluxmonitor.storage import Storage from hashlib import md5 storage = Storage("general", "meta") with open("/media/usb/config_flux.txt") as f: h = md5(f.read()).hexdigest() print("Fingerprint local=%s, disk=%s" % (storage["flashconfig_history"], h)) if storage["flashconfig_history"] == h: print("This config file is already done, ignore") return c = RawConfigParser() c.read("/media/usb/config_flux.txt") if "device" in c.sections(): device = dict(c.items("device")) s = device.get("serial") if s and get_serial() != s: print("Device serial not match") return else: print("Device section not found, ignore") if "general" in c.sections(): general_config = dict(c.items("general")) name = general_config.get("name") if name: if dryrun: print("[Dryrun] Config name to: %s" % name) else: from fluxmonitor.storage import Metadata m = Metadata() m.nickname = general_config["name"] password = general_config.get("password") if password: if dryrun: print("[Dryrun] Set password to: ***") else: from fluxmonitor.security import set_password set_password(general_config["password"]) if "network" in c.sections(): network_config = dict(c.items("network")) if dryrun: print("[Dryrun] Config network: %s" % network_config) else: from fluxmonitor.misc import network_config_encoder as NCE # noqa from fluxmonitor.config import NETWORK_MANAGE_ENDPOINT import socket s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) s.connect(NETWORK_MANAGE_ENDPOINT) s.send(b"%s\x00%s" % (b"config_network", NCE.to_bytes(network_config))) storage["flashconfig_history"] = h