Files
mongoose/src/mdash.c
T

196 lines
7.4 KiB
C

#include "mdash.h"
#if MG_ENABLE_MDASH
#include "base64.h"
#include "health.h"
#include "log.h"
#include "ota.h"
#include "printf.h"
#include "rpc.h"
#include "sntp.h"
#include "tls.h"
#include "url.h"
#include "ws.h"
static const char *mg_mdash_ca_pem =
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
"-----END CERTIFICATE-----\n";
static struct mg_connection *mg_mdash_conn = NULL;
// static struct mg_str mg_mdash_key;
// static void mg_connection *mg_mdash_conn = NULL;
static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_CONNECT) {
// c->is_hexdumping = true;
if (mg_url_is_ssl(MG_MDASH_URL)) {
struct mg_str host = mg_url_host(MG_MDASH_URL);
struct mg_tls_opts opts;
memset(&opts, 0, sizeof(struct mg_tls_opts));
opts.ca = mg_str(mg_mdash_ca_pem);
opts.name = host;
mg_tls_init(c, &opts);
}
}
if (ev == MG_EV_WS_OPEN) {
MG_INFO(("Connected to %s", MG_MDASH_URL));
// s_mDashState = MDASH_STATE_CLOUD_CONNECTED;
// mDashTriggerEvent(s_mDashState, NULL);
} else if (ev == MG_EV_WS_MSG) {
struct mg_iobuf io = {0, 0, 0, 512};
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
struct mg_str s = wm->data;
struct mg_rpc_req r;
while (s.len > 0 && isspace((uint8_t) s.buf[s.len - 1])) s.len--;
MG_INFO(("RPC: %.*s", (int) s.len, s.buf));
memset(&r, 0, sizeof(struct mg_rpc_req));
r.head = &c->mgr->rpcs;
r.rpc = NULL;
r.pfn = mg_pfn_iobuf;
r.pfn_data = &io;
r.req_data = NULL;
r.frame = s;
mg_rpc_process(&r);
if (io.buf != NULL) {
MG_INFO((" %s", io.buf));
mg_ws_send(c, (const char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
} else {
MG_DEBUG(("%s", "RPC unhandled"));
}
mg_iobuf_free(&io);
} else if (ev == MG_EV_CLOSE) {
mg_mdash_conn = NULL;
MG_INFO(("disconnected, ram %lu", 0));
}
(void) c;
}
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
const char *reset_reason = mg_health_reason_str(mg_health_reason());
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", //
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
MG_ESC("arch"), MG_ARCH, //
MG_ESC("crash"), mg_print_crash_record);
}
static void mg_mdash_rpc_mem_stats(struct mg_rpc_req *r) {
mg_rpc_ok(r, "%M", mg_print_mem_stats);
}
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
size_t total = (size_t) mg_json_get_long(r->frame, "$.params.size", -1);
if (mg_ota_begin(total)) {
mg_rpc_ok(r, "true");
} else {
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
}
}
static void mg_mdash_rpc_ota_write(struct mg_rpc_req *r) {
int len = 0, ofs = 0;
if ((ofs = mg_json_get(r->frame, "$.params.data", &len)) <= 0 ||
r->frame.buf[ofs] != '"') {
mg_rpc_err(r, 500, "%m", MG_ESC("data required"));
} else {
struct mg_str s = mg_str_n(&r->frame.buf[ofs] + 1, (size_t) (len - 2));
size_t n = mg_base64_decode(s.buf, s.len, s.buf, s.len); // Decode in-place
if (n == 0) {
mg_rpc_ok(r, "%m", MG_ESC("finished"));
mg_ota_end();
} else if (mg_ota_write(s.buf, n)) {
mg_rpc_ok(r, "{%m:%d}", MG_ESC("queued"), n);
} else {
mg_rpc_err(r, 500, "%m", MG_ESC("write failed"));
mg_ota_end();
}
// mDashNotify("State.Set", "{%m:{%m:%d}}", MG_ESC("ota"),
// MG_ESC("percent"), (int) (s_ota_written * 100 / s_ota_size));
}
}
static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
if (mg_ota_end()) {
mg_rpc_ok(r, "true");
} else {
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
}
// mDashNotify("State.Set", "{%m:{%m:null}}", MG_ESC("ota"),
// MG_ESC("percent"));
}
void mg_mdash_init(struct mg_mgr *mgr) {
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
mg_rpc_add(&mgr->rpcs, mg_str("Sys.GetInfo"), mg_mdash_rpc_get_info, NULL);
mg_rpc_add(&mgr->rpcs, mg_str("Sys.MemStats"), mg_mdash_rpc_mem_stats, NULL);
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
}
void mg_mdash_poll(struct mg_mgr *mgr) {
#if MG_TLS == MG_TLS_NONE
(void) mgr, (void) mg_mdash_fn; // mdash needs wss://, nothing to do
#else
static uint64_t timer = 0;
// No RPC handlers means mdash was never set up, or mg_mdash_free() ran.
// No key means mdash would reject us, so do not connect at all
if (mg_mdash_conn == NULL && mgr->rpcs != NULL && MG_MDASH_KEY[0] != '\0' &&
mg_timer_expired(&timer, 1000, mg_millis())) {
MG_INFO(("Connecting to %s", MG_MDASH_URL));
mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL,
"Origin: %s\r\n"
"Authorization: Bearer %s\r\n",
MG_MDASH_URL, MG_MDASH_KEY);
}
#endif
}
void mg_mdash_free(struct mg_mgr *mgr) {
mg_rpc_del(&mgr->rpcs, NULL);
}
#else
void mg_mdash_init(struct mg_mgr *mgr) {
(void) mgr;
}
void mg_mdash_poll(struct mg_mgr *mgr) {
(void) mgr;
}
void mg_mdash_free(struct mg_mgr *mgr) {
(void) mgr;
}
#endif