Add (private) OTA libs
This commit is contained in:
29
libs/ota-http-client/include/mgos_ota_http_client.h
Normal file
29
libs/ota-http-client/include/mgos_ota_http_client.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* An updater implementation that fetches FW from the given URL.
|
||||
*/
|
||||
|
||||
#ifndef CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_
|
||||
#define CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "mgos_updater_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if MGOS_ENABLE_UPDATER
|
||||
bool mgos_ota_http_client_init(void);
|
||||
|
||||
void mgos_ota_http_start(struct update_context *ctx, const char *url);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_ */
|
28
libs/ota-http-client/mos.yml
Normal file
28
libs/ota-http-client/mos.yml
Normal file
@ -0,0 +1,28 @@
|
||||
author: mongoose-os
|
||||
description: Implements Mongoose OS OTA HTTP client
|
||||
type: lib
|
||||
version: 1.18
|
||||
|
||||
sources:
|
||||
- src
|
||||
includes:
|
||||
- include
|
||||
|
||||
config_schema:
|
||||
- ["update.url", "s", {title : "Fetch updates form here"}]
|
||||
- ["update.interval", "i", {title : "Check for updates this often"}]
|
||||
|
||||
# Default CA bundle for updating from mongoose-os.com (and any other site that uses LetsEncrypt) and S3.
|
||||
- ["update.ssl_ca_file", "s", "ca.pem", {title : "TLS CA file"}]
|
||||
- ["update.ssl_client_cert_file", "s", {title: "TLS client cert file"}]
|
||||
- ["update.ssl_server_name", "s", {title : "TLS Server Name"}]
|
||||
|
||||
tags:
|
||||
- c
|
||||
- ota
|
||||
- http
|
||||
|
||||
build_vars:
|
||||
MGOS_ENABLE_UPDATER: 1
|
||||
|
||||
manifest_version: 2017-09-29
|
187
libs/ota-http-client/src/mgos_ota_http_client.c
Normal file
187
libs/ota-http-client/src/mgos_ota_http_client.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "mgos_ota_http_client.h"
|
||||
|
||||
#include "common/cs_dbg.h"
|
||||
#include "mgos_hal.h"
|
||||
#include "mgos_mongoose.h"
|
||||
#include "mgos_config.h"
|
||||
#include "mgos_ro_vars.h"
|
||||
#include "mgos_timers.h"
|
||||
#include "mgos_utils.h"
|
||||
|
||||
#if MGOS_ENABLE_UPDATER
|
||||
|
||||
static void fw_download_handler(struct mg_connection *c, int ev, void *p,
|
||||
void *user_data) {
|
||||
struct mbuf *io = &c->recv_mbuf;
|
||||
struct update_context *ctx = (struct update_context *) user_data;
|
||||
int res = 0;
|
||||
struct mg_str *loc;
|
||||
(void) p;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_CONNECT: {
|
||||
int result = *((int *) p);
|
||||
if (result != 0) LOG(LL_ERROR, ("connect error: %d", result));
|
||||
break;
|
||||
}
|
||||
case MG_EV_RECV: {
|
||||
if (ctx->file_size == 0) {
|
||||
LOG(LL_DEBUG, ("Looking for HTTP header"));
|
||||
struct http_message hm;
|
||||
int parsed = mg_parse_http(io->buf, io->len, &hm, 0);
|
||||
if (parsed <= 0) {
|
||||
return;
|
||||
}
|
||||
if (hm.resp_code != 200) {
|
||||
if (hm.resp_code == 304) {
|
||||
ctx->result = 1;
|
||||
ctx->need_reboot = false;
|
||||
ctx->status_msg = "Not Modified";
|
||||
updater_finish(ctx);
|
||||
} else if ((hm.resp_code == 301 || hm.resp_code == 302) &&
|
||||
(loc = mg_get_http_header(&hm, "Location")) != NULL) {
|
||||
/* NUL-terminate the URL. Every header must be followed by \r\n,
|
||||
* so there is deifnitely space there. */
|
||||
((char *) loc->p)[loc->len] = '\0';
|
||||
/* We were told to look elsewhere. Detach update context from this
|
||||
* connection so that it doesn't get finalized when it's closed. */
|
||||
mgos_ota_http_start(ctx, loc->p);
|
||||
c->user_data = NULL;
|
||||
} else {
|
||||
ctx->result = -hm.resp_code;
|
||||
ctx->need_reboot = false;
|
||||
ctx->status_msg = "Invalid HTTP response code";
|
||||
updater_finish(ctx);
|
||||
}
|
||||
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
return;
|
||||
}
|
||||
if (hm.body.len != 0) {
|
||||
LOG(LL_DEBUG, ("HTTP header: file size: %d", (int) hm.body.len));
|
||||
if (hm.body.len == (size_t) ~0) {
|
||||
LOG(LL_ERROR, ("Invalid content-length, perhaps chunked-encoding"));
|
||||
ctx->status_msg =
|
||||
"Invalid content-length, perhaps chunked-encoding";
|
||||
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
break;
|
||||
} else {
|
||||
ctx->file_size = hm.body.len;
|
||||
}
|
||||
|
||||
mbuf_remove(io, parsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (io->len != 0) {
|
||||
res = updater_process(ctx, io->buf, io->len);
|
||||
mbuf_remove(io, io->len);
|
||||
|
||||
if (res == 0) {
|
||||
if (is_write_finished(ctx)) res = updater_finalize(ctx);
|
||||
if (res == 0) {
|
||||
/* Need more data, everything is OK */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
/* Error */
|
||||
LOG(LL_ERROR, ("Update error: %d %s", ctx->result, ctx->status_msg));
|
||||
}
|
||||
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
if (ctx == NULL) break;
|
||||
|
||||
if (is_write_finished(ctx)) updater_finalize(ctx);
|
||||
|
||||
if (!is_update_finished(ctx)) {
|
||||
/* Update failed or connection was terminated by server */
|
||||
if (ctx->status_msg == NULL) ctx->status_msg = "Update failed";
|
||||
ctx->result = -1;
|
||||
} else if (is_reboot_required(ctx)) {
|
||||
LOG(LL_INFO, ("Rebooting device"));
|
||||
mgos_system_restart_after(100);
|
||||
}
|
||||
updater_finish(ctx);
|
||||
updater_context_free(ctx);
|
||||
c->user_data = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mgos_ota_http_start(struct update_context *ctx, const char *url) {
|
||||
LOG(LL_INFO, ("Update URL: %s, ct: %d, isv? %d", url,
|
||||
ctx->fctx.commit_timeout, ctx->ignore_same_version));
|
||||
|
||||
struct mg_connect_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
#if MG_ENABLE_SSL
|
||||
if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) {
|
||||
opts.ssl_server_name = mgos_sys_config_get_update_ssl_server_name();
|
||||
opts.ssl_ca_cert = mgos_sys_config_get_update_ssl_ca_file();
|
||||
opts.ssl_cert = mgos_sys_config_get_update_ssl_client_cert_file();
|
||||
}
|
||||
#endif
|
||||
|
||||
char ehb[150];
|
||||
char *extra_headers = ehb;
|
||||
mg_asprintf(&extra_headers, sizeof(ehb),
|
||||
"X-MGOS-Device-ID: %s %s\r\n"
|
||||
"X-MGOS-FW-Version: %s %s %s\r\n",
|
||||
(mgos_sys_config_get_device_id() ? mgos_sys_config_get_device_id() : "-"),
|
||||
mgos_sys_ro_vars_get_mac_address(),
|
||||
mgos_sys_ro_vars_get_arch(),
|
||||
mgos_sys_ro_vars_get_fw_version(),
|
||||
mgos_sys_ro_vars_get_fw_id());
|
||||
|
||||
struct mg_connection *c = mg_connect_http_opt(
|
||||
mgos_get_mgr(), fw_download_handler, ctx, opts, url, extra_headers, NULL);
|
||||
|
||||
if (extra_headers != ehb) free(extra_headers);
|
||||
|
||||
if (c == NULL) {
|
||||
LOG(LL_ERROR, ("Failed to connect to %s", url));
|
||||
ctx->result = -10;
|
||||
ctx->need_reboot = false;
|
||||
ctx->status_msg = "Failed to connect";
|
||||
updater_finish(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->nc = c;
|
||||
}
|
||||
|
||||
static void mgos_ota_timer_cb(void *arg) {
|
||||
const struct mgos_config_update *mcu = mgos_sys_config_get_update();
|
||||
if (mcu->url == NULL) return;
|
||||
struct update_context *ctx = updater_context_create();
|
||||
if (ctx == NULL) return;
|
||||
ctx->ignore_same_version = true;
|
||||
ctx->fctx.commit_timeout = mcu->commit_timeout;
|
||||
mgos_ota_http_start(ctx, mcu->url);
|
||||
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
bool mgos_ota_http_client_init(void) {
|
||||
const struct mgos_config_update *mcu = mgos_sys_config_get_update();
|
||||
if (mcu->url != NULL && mcu->interval > 0) {
|
||||
LOG(LL_INFO,
|
||||
("Updates from %s, every %d seconds", mcu->url, mcu->interval));
|
||||
mgos_set_timer(mcu->interval * 1000, true /* repeat */, mgos_ota_timer_cb,
|
||||
mcu->url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* MGOS_ENABLE_UPDATER */
|
23
libs/ota-http-server/include/mgos_ota_http_server.h
Normal file
23
libs/ota-http-server/include/mgos_ota_http_server.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef CS_MOS_LIBS_OTA_HTTP_SERVER_SRC_MGOS_OTA_HTTP_SERVER_H_
|
||||
#define CS_MOS_LIBS_OTA_HTTP_SERVER_SRC_MGOS_OTA_HTTP_SERVER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if MGOS_ENABLE_UPDATER
|
||||
bool mgos_ota_http_server_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CS_MOS_LIBS_OTA_HTTP_SERVER_SRC_MGOS_OTA_HTTP_SERVER_H_ */
|
22
libs/ota-http-server/mos.yml
Normal file
22
libs/ota-http-server/mos.yml
Normal file
@ -0,0 +1,22 @@
|
||||
author: mongoose-os
|
||||
description: Implements Mongoose OS OTA HTTP server
|
||||
type: lib
|
||||
version: 1.18
|
||||
|
||||
sources:
|
||||
- src
|
||||
includes:
|
||||
- include
|
||||
config_schema:
|
||||
- ["update.enable_post", "b", true, {title : "Enable POST updates"}]
|
||||
|
||||
libs:
|
||||
- origin: https://github.com/mongoose-os-libs/http-server
|
||||
- origin: libs/ota-http-client
|
||||
|
||||
tags:
|
||||
- c
|
||||
- ota
|
||||
- http
|
||||
|
||||
manifest_version: 2017-09-29
|
238
libs/ota-http-server/src/mgos_ota_http_server.c
Normal file
238
libs/ota-http-server/src/mgos_ota_http_server.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "mgos_ota_http_server.h"
|
||||
#include "mgos_ota_http_client.h"
|
||||
|
||||
#include "mgos_http_server.h"
|
||||
|
||||
#include "common/cs_dbg.h"
|
||||
#include "mgos_hal.h"
|
||||
#include "mgos_mongoose.h"
|
||||
#include "mgos_config.h"
|
||||
#include "mgos_timers.h"
|
||||
#include "mgos_utils.h"
|
||||
|
||||
static void handle_update_post(struct mg_connection *c, int ev, void *p) {
|
||||
struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p;
|
||||
struct update_context *ctx = (struct update_context *) c->user_data;
|
||||
if (ctx == NULL && ev != MG_EV_HTTP_MULTIPART_REQUEST) return;
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_MULTIPART_REQUEST: {
|
||||
ctx = updater_context_create();
|
||||
if (ctx != NULL) {
|
||||
ctx->nc = c;
|
||||
c->user_data = ctx;
|
||||
} else {
|
||||
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_PART_BEGIN: {
|
||||
LOG(LL_DEBUG, ("MG_EV_HTTP_PART_BEGIN: %p %s %s", ctx, mp->var_name,
|
||||
mp->file_name));
|
||||
/* We use ctx->file_name as a temp buffer for non-file variable values. */
|
||||
if (mp->file_name[0] == '\0') {
|
||||
ctx->file_name[0] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_PART_DATA: {
|
||||
LOG(LL_DEBUG, ("MG_EV_HTTP_PART_DATA: %p %s %s %d", ctx, mp->var_name,
|
||||
mp->file_name, (int) mp->data.len));
|
||||
|
||||
if (mp->file_name[0] == '\0') {
|
||||
/* It's a non-file form variable. */
|
||||
size_t l = strlen(ctx->file_name);
|
||||
size_t avail = sizeof(ctx->file_name) - l - 1;
|
||||
strncat(ctx->file_name, mp->data.p, MIN(mp->data.len, avail));
|
||||
break;
|
||||
} else if (!is_update_finished(ctx)) {
|
||||
updater_process(ctx, mp->data.p, mp->data.len);
|
||||
LOG(LL_DEBUG, ("updater_process res: %d", ctx->result));
|
||||
} else {
|
||||
/* Don't close connection just yet, not all browsers like that. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_PART_END: {
|
||||
LOG(LL_DEBUG, ("MG_EV_HTTP_PART_END: %p %s %s %d", ctx, mp->var_name,
|
||||
mp->file_name, mp->status));
|
||||
/* Part finished with an error. REQUEST_END will follow. */
|
||||
if (mp->status < 0) break;
|
||||
if (mp->file_name[0] == '\0') {
|
||||
/* It's a non-file form variable. Value is in ctx->file_name. */
|
||||
LOG(LL_DEBUG, ("Got var: %s=%s", mp->var_name, ctx->file_name));
|
||||
/* Commit timeout can be set after flashing. */
|
||||
if (strcmp(mp->var_name, "commit_timeout") == 0) {
|
||||
ctx->fctx.commit_timeout = atoi(ctx->file_name);
|
||||
}
|
||||
} else {
|
||||
/* End of the fw part, but there may still be parts with vars to follow,
|
||||
* which can modify settings (that can be applied post-flashing). */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_MULTIPART_REQUEST_END: {
|
||||
LOG(LL_DEBUG,
|
||||
("MG_EV_HTTP_MULTIPART_REQUEST_END: %p %d", ctx, mp->status));
|
||||
/* Whatever happens, this is the last thing we do. */
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
|
||||
if (ctx == NULL) break;
|
||||
if (is_write_finished(ctx)) updater_finalize(ctx);
|
||||
if (!is_update_finished(ctx)) {
|
||||
ctx->result = -1;
|
||||
ctx->status_msg = "Update aborted";
|
||||
updater_finish(ctx);
|
||||
}
|
||||
if (mp->status < 0) {
|
||||
/* mp->status < 0 means connection is dead, do not send reply */
|
||||
} else {
|
||||
int code = (ctx->result > 0 ? 200 : 400);
|
||||
mg_send_response_line(c, code,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(c, "%s\r\n",
|
||||
ctx->status_msg ? ctx->status_msg : "Unknown error");
|
||||
if (is_reboot_required(ctx)) {
|
||||
LOG(LL_INFO, ("Rebooting device"));
|
||||
mgos_system_restart_after(101);
|
||||
}
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
updater_context_free(ctx);
|
||||
c->user_data = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct mg_connection *s_update_request_conn;
|
||||
|
||||
static void mgos_ota_result_cb(struct update_context *ctx) {
|
||||
if (ctx != updater_context_get_current()) return;
|
||||
if (s_update_request_conn != NULL) {
|
||||
int code = (ctx->result > 0 ? 200 : 500);
|
||||
mg_send_response_line(s_update_request_conn, code,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(s_update_request_conn, "(%d) %s\r\n", ctx->result,
|
||||
ctx->status_msg);
|
||||
s_update_request_conn->flags |= MG_F_SEND_AND_CLOSE;
|
||||
s_update_request_conn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_handler(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *user_data) {
|
||||
switch (ev) {
|
||||
case MG_EV_HTTP_MULTIPART_REQUEST:
|
||||
case MG_EV_HTTP_PART_BEGIN:
|
||||
case MG_EV_HTTP_PART_DATA:
|
||||
case MG_EV_HTTP_PART_END:
|
||||
case MG_EV_HTTP_MULTIPART_REQUEST_END: {
|
||||
if (mgos_sys_config_get_update_enable_post()) {
|
||||
handle_update_post(c, ev, ev_data);
|
||||
} else {
|
||||
mg_send_response_line(c, 400,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(c, "POST updates are disabled.");
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
if (updater_context_get_current() != NULL) {
|
||||
mg_send_response_line(c, 409,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(c, "Another update is in progress.\r\n");
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
const struct mgos_config_update *mcu = mgos_sys_config_get_update();
|
||||
char *url = mcu->url;
|
||||
int commit_timeout = mcu->commit_timeout;
|
||||
bool ignore_same_version = true;
|
||||
struct mg_str params =
|
||||
(mg_vcmp(&hm->method, "POST") == 0 ? hm->body : hm->query_string);
|
||||
size_t buf_len = params.len;
|
||||
char *buf = calloc(params.len, 1), *p = buf;
|
||||
int len = mg_get_http_var(¶ms, "url", p, buf_len);
|
||||
if (len > 0) {
|
||||
url = p;
|
||||
p += len + 1;
|
||||
buf_len -= len + 1;
|
||||
}
|
||||
len = mg_get_http_var(¶ms, "commit_timeout", p, buf_len);
|
||||
if (len > 0) {
|
||||
commit_timeout = atoi(p);
|
||||
}
|
||||
len = mg_get_http_var(¶ms, "ignore_same_version", p, buf_len);
|
||||
if (len > 0) {
|
||||
ignore_same_version = (atoi(p) > 0);
|
||||
}
|
||||
if (url != NULL) {
|
||||
s_update_request_conn = c;
|
||||
struct update_context *ctx = updater_context_create();
|
||||
if (ctx == NULL) {
|
||||
mg_send_response_line(c, 409,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(c, "Failed to create updater context.\r\n");
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
ctx->ignore_same_version = ignore_same_version;
|
||||
ctx->fctx.commit_timeout = commit_timeout;
|
||||
ctx->result_cb = mgos_ota_result_cb;
|
||||
mgos_ota_http_start(ctx, url);
|
||||
|
||||
} else {
|
||||
mg_send_response_line(c, 400,
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n");
|
||||
mg_printf(c, "Update URL not specified and none is configured.\r\n");
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
if (s_update_request_conn == c) {
|
||||
/* Client went away while waiting for response. */
|
||||
s_update_request_conn = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void) user_data;
|
||||
}
|
||||
|
||||
static void update_action_handler(struct mg_connection *c, int ev, void *p,
|
||||
void *user_data) {
|
||||
if (ev != MG_EV_HTTP_REQUEST) return;
|
||||
struct http_message *hm = (struct http_message *) p;
|
||||
bool is_commit = (mg_vcmp(&hm->uri, "/update/commit") == 0);
|
||||
bool ok =
|
||||
(is_commit ? mgos_upd_commit() : mgos_upd_revert(false /* reboot */));
|
||||
mg_send_response_line(c, (ok ? 200 : 400),
|
||||
"Content-Type: text/html\r\n"
|
||||
"Connection: close");
|
||||
mg_printf(c, "\r\n%s\r\n", (ok ? "Ok" : "Error"));
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
if (ok && !is_commit) mgos_system_restart_after(100);
|
||||
(void) user_data;
|
||||
}
|
||||
|
||||
bool mgos_ota_http_server_init(void) {
|
||||
mgos_register_http_endpoint("/update/commit", update_action_handler, NULL);
|
||||
mgos_register_http_endpoint("/update/revert", update_action_handler, NULL);
|
||||
mgos_register_http_endpoint("/update", update_handler, NULL);
|
||||
return true;
|
||||
}
|
26
libs/rpc-service-ota/include/mgos_rpc_service_ota.h
Normal file
26
libs/rpc-service-ota/include/mgos_rpc_service_ota.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#ifndef CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_
|
||||
#define CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "common/mg_str.h"
|
||||
#include "mgos_features.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
bool mgos_rpc_service_ota_init(void);
|
||||
void mgos_updater_rpc_finish(int error_code, int64_t id,
|
||||
const struct mg_str src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_ */
|
17
libs/rpc-service-ota/mos.yml
Normal file
17
libs/rpc-service-ota/mos.yml
Normal file
@ -0,0 +1,17 @@
|
||||
author: mongoose-os
|
||||
description: Support for Over-The-Air update via RPC
|
||||
type: lib
|
||||
version: 1.18
|
||||
manifest_version: 2017-09-29
|
||||
sources:
|
||||
- src
|
||||
includes:
|
||||
- include
|
||||
libs:
|
||||
- origin: https://github.com/mongoose-os-libs/rpc-common
|
||||
- origin: libs/ota-http-client
|
||||
tags:
|
||||
- c
|
||||
- ota
|
||||
- rpc
|
||||
- updater
|
227
libs/rpc-service-ota/src/mgos_rpc_service_ota.c
Normal file
227
libs/rpc-service-ota/src/mgos_rpc_service_ota.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "mgos_rpc_service_ota.h"
|
||||
|
||||
#include "mg_rpc.h"
|
||||
#include "mgos_rpc.h"
|
||||
#include "mgos_ota_http_client.h"
|
||||
|
||||
#include "common/cs_dbg.h"
|
||||
#include "common/mg_str.h"
|
||||
#include "mgos_mongoose.h"
|
||||
#include "mgos_updater_common.h"
|
||||
#include "mgos_utils.h"
|
||||
|
||||
static struct mg_rpc_request_info *s_update_req;
|
||||
|
||||
static void mg_rpc_updater_result(struct update_context *ctx) {
|
||||
if (s_update_req == NULL) return;
|
||||
mg_rpc_send_errorf(s_update_req, (ctx->result > 0 ? 0 : -1), ctx->status_msg);
|
||||
s_update_req = NULL;
|
||||
}
|
||||
|
||||
static void handle_update_req(struct mg_rpc_request_info *ri, void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
char *blob_url = NULL;
|
||||
struct json_token url_tok = JSON_INVALID_TOKEN;
|
||||
int commit_timeout = 0;
|
||||
struct update_context *ctx = NULL;
|
||||
|
||||
LOG(LL_DEBUG, ("Update request received: %.*s", (int) args.len, args.p));
|
||||
|
||||
const char *reply = "Malformed request";
|
||||
|
||||
if (args.len == 0) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
json_scanf(args.p, args.len, ri->args_fmt, &url_tok, &commit_timeout);
|
||||
|
||||
if (url_tok.len == 0 || url_tok.type != JSON_TYPE_STRING) goto clean;
|
||||
|
||||
LOG(LL_DEBUG, ("URL: %.*s commit_timeout: %d", url_tok.len, url_tok.ptr,
|
||||
commit_timeout));
|
||||
|
||||
/*
|
||||
* If user setup callback for updater, just call it.
|
||||
* User can start update with Sys.updater.start()
|
||||
*/
|
||||
|
||||
blob_url = calloc(1, url_tok.len + 1);
|
||||
if (blob_url == NULL) {
|
||||
LOG(LL_ERROR, ("Out of memory"));
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(blob_url, url_tok.ptr, url_tok.len);
|
||||
|
||||
ctx = updater_context_create();
|
||||
if (ctx == NULL) {
|
||||
reply = "Failed to init updater";
|
||||
goto clean;
|
||||
}
|
||||
ctx->fctx.commit_timeout = commit_timeout;
|
||||
ctx->result_cb = mg_rpc_updater_result;
|
||||
s_update_req = ri;
|
||||
|
||||
mgos_ota_http_start(ctx, blob_url);
|
||||
free(blob_url);
|
||||
return;
|
||||
|
||||
clean:
|
||||
if (blob_url != NULL) free(blob_url);
|
||||
LOG(LL_ERROR, ("Failed to start update: %s", reply));
|
||||
mg_rpc_send_errorf(ri, -1, reply);
|
||||
ri = NULL;
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
static void handle_commit_req(struct mg_rpc_request_info *ri, void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
if (mgos_upd_commit()) {
|
||||
mg_rpc_send_responsef(ri, NULL);
|
||||
} else {
|
||||
mg_rpc_send_errorf(ri, -1, NULL);
|
||||
}
|
||||
ri = NULL;
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void handle_revert_req(struct mg_rpc_request_info *ri, void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
if (mgos_upd_revert(false /* reboot */)) {
|
||||
mg_rpc_send_responsef(ri, NULL);
|
||||
mgos_system_restart_after(100);
|
||||
} else {
|
||||
mg_rpc_send_errorf(ri, -1, NULL);
|
||||
}
|
||||
ri = NULL;
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void handle_create_snapshot_req(struct mg_rpc_request_info *ri,
|
||||
void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
const char *err_msg = NULL;
|
||||
int ret = -1;
|
||||
if (mgos_upd_is_committed()) {
|
||||
ret = mgos_upd_create_snapshot();
|
||||
if (ret >= 0) {
|
||||
bool set_as_revert = false;
|
||||
int commit_timeout = -1;
|
||||
json_scanf(args.p, args.len, ri->args_fmt, &set_as_revert,
|
||||
&commit_timeout);
|
||||
if (set_as_revert) {
|
||||
struct mgos_upd_boot_state bs;
|
||||
if (mgos_upd_boot_get_state(&bs)) {
|
||||
bs.is_committed = false;
|
||||
bs.revert_slot = ret;
|
||||
if (mgos_upd_boot_set_state(&bs)) {
|
||||
if (commit_timeout >= 0 &&
|
||||
!mgos_upd_set_commit_timeout(commit_timeout)) {
|
||||
ret = -4;
|
||||
err_msg = "Failed to set commit timeout";
|
||||
}
|
||||
} else {
|
||||
ret = -3;
|
||||
err_msg = "Failed to set boot state";
|
||||
}
|
||||
} else {
|
||||
ret = -2;
|
||||
err_msg = "Failed to get boot state";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err_msg = "Failed to create snapshot";
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
err_msg = "Cannot create snapshots in uncommitted state";
|
||||
}
|
||||
if (ret >= 0) {
|
||||
mg_rpc_send_responsef(ri, "{slot: %d}", ret);
|
||||
} else {
|
||||
mg_rpc_send_errorf(ri, ret, err_msg);
|
||||
}
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
static void handle_get_boot_state_req(struct mg_rpc_request_info *ri,
|
||||
void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
struct mgos_upd_boot_state bs;
|
||||
if (!mgos_upd_boot_get_state(&bs)) {
|
||||
mg_rpc_send_errorf(ri, -1, NULL);
|
||||
} else {
|
||||
mg_rpc_send_responsef(ri,
|
||||
"{active_slot: %d, is_committed: %B, revert_slot: "
|
||||
"%d, commit_timeout: %d}",
|
||||
bs.active_slot, bs.is_committed, bs.revert_slot,
|
||||
mgos_upd_get_commit_timeout());
|
||||
}
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void handle_set_boot_state_req(struct mg_rpc_request_info *ri,
|
||||
void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi,
|
||||
struct mg_str args) {
|
||||
int ret = 0;
|
||||
struct mgos_upd_boot_state bs;
|
||||
if (mgos_upd_boot_get_state(&bs)) {
|
||||
int commit_timeout = -1;
|
||||
if (json_scanf(args.p, args.len, ri->args_fmt, &bs.active_slot,
|
||||
&bs.is_committed, &bs.revert_slot, &commit_timeout) > 0) {
|
||||
ret = mgos_upd_boot_set_state(&bs) ? 0 : -3;
|
||||
if (ret == 0 && commit_timeout >= 0) {
|
||||
ret = mgos_upd_set_commit_timeout(commit_timeout) ? 0 : -4;
|
||||
}
|
||||
} else {
|
||||
ret = -2;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
mg_rpc_send_responsef(ri, NULL);
|
||||
} else {
|
||||
mg_rpc_send_errorf(ri, ret, NULL);
|
||||
}
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
bool mgos_rpc_service_ota_init(void) {
|
||||
struct mg_rpc *mg_rpc = mgos_rpc_get_global();
|
||||
if (mg_rpc == NULL) return true;
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.Update", "{url: %T, commit_timeout: %d}",
|
||||
handle_update_req, NULL);
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.Commit", "", handle_commit_req, NULL);
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.Revert", "", handle_revert_req, NULL);
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.CreateSnapshot",
|
||||
"{set_as_revert: %B, commit_timeout: %d}",
|
||||
handle_create_snapshot_req, NULL);
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.GetBootState", "", handle_get_boot_state_req,
|
||||
NULL);
|
||||
mg_rpc_add_handler(mg_rpc, "OTA.SetBootState",
|
||||
"{active_slot: %d, is_committed: %B, revert_slot: %d, "
|
||||
"commit_timeout: %d}",
|
||||
handle_set_boot_state_req, NULL);
|
||||
return true;
|
||||
}
|
5
mos.yml
5
mos.yml
@ -77,7 +77,6 @@ libs:
|
||||
- origin: https://github.com/mongoose-os-libs/http-server
|
||||
- origin: https://github.com/mongoose-os-libs/rpc-service-config
|
||||
- origin: https://github.com/mongoose-os-libs/rpc-service-fs
|
||||
- origin: https://github.com/mongoose-os-libs/rpc-uart
|
||||
- origin: https://github.com/mongoose-os-libs/prometheus-metrics
|
||||
- origin: https://github.com/mongoose-os-libs/mqtt
|
||||
- origin: https://github.com/mongoose-os-libs/pwm
|
||||
@ -85,6 +84,10 @@ libs:
|
||||
- origin: https://github.com/mongoose-os-libs/spi
|
||||
- origin: https://github.com/mongoose-os-libs/ili9341-spi
|
||||
- origin: https://github.com/mongoose-os-libs/stmpe610-spi
|
||||
- origin: libs/ota-http-client
|
||||
- origin: libs/ota-http-server
|
||||
- origin: libs/rpc-service-ota
|
||||
|
||||
|
||||
# Used by the mos tool to catch mos binaries incompatible with this file format
|
||||
manifest_version: 2017-05-18
|
||||
|
Reference in New Issue
Block a user