Compare commits

...
5 Commits
Author SHA1 Message Date
robertc2000 ce35cbf586 Merge pull request #3716 from cesanta/crash
Print crash record optionally
2026-10-01 14:48:36 +03:00
cpq 3aa5f2a1fd Print crash record optionally 2026-10-01 14:42:16 +03:00
cpq 1b0906510b Rename MG_OTA_FIRMWARE_VERSION -> MG_FIRMWARE_VERSION, rw612 nits 2026-10-01 10:10:42 +01:00
Sergey Lyubka 8a116d9289 Merge pull request #3715 from cesanta/is_resp
Remove c->is_resp, keep it private to http.c
2026-10-01 08:25:38 +01:00
cpq 71e0176944 Remove c->is_resp, keep it private to http.c 2026-09-30 15:12:54 +01:00
23 changed files with 176 additions and 144 deletions

No files matched your search

+1 -1
View File
@@ -486,7 +486,7 @@ embedded devices and needs only a few lines added to `mongoose_config.h`:
```c
#define MG_OTA_URL "https://mongoose.ws/ota/u/PREFIX/ota.json" // Copy from instructions panel
#define MG_OTA_FIRMWARE_VERSION "MY_PRODUCT_1.4.2" // Change this
#define MG_FIRMWARE_VERSION "MY_PRODUCT_1.4.2" // Change this
```
No backend programming required. The device registers itself and checks in at
+67 -53
View File
@@ -1578,7 +1578,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
// The response has been send in EV_HDRS path, so we're not reponding
// anything but clearing the marker for the next request.
d->marker = 0;
c->is_resp = 0;
c->pfn_data = NULL; // Response done
} else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
@@ -3848,7 +3848,7 @@ static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
if (c->send.len >= len + 10) {
mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
c->send.buf[len + 8] = '\r';
if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
if (c->send.len == len + 10) c->pfn_data = NULL; // Last chunk, done
}
if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
}
@@ -3863,7 +3863,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
mg_printf(c, "%lx\r\n", (unsigned long) len);
if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
if (len == 0) c->is_resp = 0;
if (len == 0) c->pfn_data = NULL; // Last chunk, response done
}
// clang-format off
@@ -3952,15 +3952,14 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers,
(unsigned long) (c->send.len - len));
c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
}
c->is_resp = 0;
c->pfn_data = NULL; // Response done
}
static void http_cb(struct mg_connection *, int, void *);
static void restore_http_cb(struct mg_connection *c) {
mg_fs_close((struct mg_fd *) c->pfn_data);
c->pfn_data = NULL;
c->pfn_data = NULL; // Response done
c->pfn = http_cb;
c->is_resp = 0;
}
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
@@ -4148,7 +4147,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
range, hdrs);
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
c->is_resp = 0;
c->pfn_data = NULL; // Response done
mg_fs_close(fd);
} else { // start serving static content only if not closing, see #3354
// Track to-be-sent content length at the end of c->data, aligned
@@ -4217,7 +4216,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
if (n > sizeof(tmp)) n = 0;
memcpy(c->send.buf + off - 12, tmp, n); // Set content length
c->is_resp = 0; // Mark response end
c->pfn_data = NULL; // Response done
}
// Map requested URI to the file path (buf,len). Use root directory r.
@@ -4271,7 +4270,7 @@ static int uri_to_file_status(struct mg_connection *c,
"Content-Length: 0\r\n"
"\r\n",
(int) hm->uri.len, hm->uri.buf);
c->is_resp = 0;
c->pfn_data = NULL; // Response done
flags = -1;
} else if (flags & MG_FS_DIR) {
if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
@@ -4616,7 +4615,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
c->recv.len > 0)) { // see #2796
struct mg_http_message hm;
size_t ofs = 0; // Parsing offset
while (c->is_resp == 0 && ofs < c->recv.len) {
// c->pfn_data is the "response generation in progress" marker. While it
// is set, don't parse the next pipelined request, to keep responses in order
while (c->pfn_data == NULL && ofs < c->recv.len) {
const char *buf = (char *) c->recv.buf + ofs;
int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
struct mg_str *te; // Transfer - encoding header
@@ -4709,9 +4710,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
ofs += (size_t) n + hm.body.len;
}
if (c->is_accepted) c->is_resp = 1; // Start generating response
mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp
if (c->is_accepted && !c->is_resp) {
if (c->is_accepted) c->pfn_data = c; // Response started
mg_call(c, MG_EV_HTTP_MSG, &hm); // Handler sets pfn_data NULL when done
if (c->is_accepted && c->pfn_data == NULL) {
struct mg_str *cc = mg_http_get_header(&hm, "Connection");
if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) {
c->is_draining = 1; // honor "Connection: close"
@@ -6990,7 +6991,10 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
// 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 = {.ca = mg_str(mg_mdash_ca_pem), .name = host};
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);
}
}
@@ -7000,16 +7004,18 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
// mDashTriggerEvent(s_mDashState, NULL);
} else if (ev == MG_EV_WS_MSG) {
struct mg_iobuf io = {0, 0, 0, 512};
struct mg_ws_message *wm = ev_data;
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));
struct mg_rpc_req r = {.head = &c->mgr->rpcs,
.rpc = NULL,
.pfn = mg_pfn_iobuf,
.pfn_data = &io,
.req_data = NULL,
.frame = s};
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));
@@ -7025,32 +7031,35 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
(void) c;
}
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t n = va_arg(*ap, size_t);
uint32_t *p = va_arg(*ap, uint32_t *);
size_t len = 0;
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
return len;
}
static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t len = 0;
if (mg_health_valid()) {
uint32_t *data = mg_health_record.backtrace;
mg_rpc_ok(r,
"{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:true,"
"%m:{%m:\"0x%08lx\",%m:\"0x%08lx\",%m:\"0x%08lx\"},"
"%m:{%m:\"0x%08lx\",%m:%m}}}",
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION),
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000),
MG_ESC("reboot_reason"),
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"),
MG_ARCH, MG_ESC("report"), MG_ESC("valid"), MG_ESC("regs"),
MG_ESC("sp"), (unsigned long) data[0], MG_ESC("lr"),
(unsigned long) data[1], MG_ESC("pc"), (unsigned long) data[2],
MG_ESC("stack"), MG_ESC("addr"), (unsigned long) data[0],
MG_ESC("data"), mg_print_base64,
(int) ((MG_HEALTH_BACKTRACE - 3) * sizeof(data[0])),
(uint8_t *) &data[3]);
} else {
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:false}}",
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION),
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000),
MG_ESC("reboot_reason"),
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"),
MG_ARCH, MG_ESC("report"), MG_ESC("valid"));
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"),
MG_ESC("version"), mg_health_record.magic[3], //
MG_ESC("cpuid"), mg_health_record.cpuid, //
MG_ESC("data"), print_uint32, n, mg_health_record.data);
}
(void) ap;
return len;
}
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}", //
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, //
print_crash);
}
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
@@ -7069,7 +7078,7 @@ static void mg_mdash_rpc_ota_write(struct mg_rpc_req *r) {
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
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();
@@ -7114,6 +7123,10 @@ void mg_mdash_poll(struct mg_mgr *mgr) {
}
}
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;
@@ -7121,6 +7134,9 @@ void mg_mdash_init(struct mg_mgr *mgr) {
void mg_mdash_poll(struct mg_mgr *mgr) {
(void) mgr;
}
void mg_mdash_free(struct mg_mgr *mgr) {
(void) mgr;
}
#endif
#ifdef MG_ENABLE_LINES
@@ -8270,6 +8286,9 @@ void mg_mgr_free(struct mg_mgr *mgr) {
mgr->timers = NULL; // Important. Next call to poll won't touch timers
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ENABLE_MDASH
mg_mdash_free(mgr);
#endif
#if MG_ENABLE_FREERTOS_TCP
FreeRTOS_DeleteSocketSet(mgr->ss);
#endif
@@ -10851,7 +10870,7 @@ void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) {
// Scannable version tag embedded in every firmware binary, for server-side
// version extraction. The version string starts after the "MG_VERSION:" prefix.
static const char s_fw_version[] = "MG_VERSION:" MG_OTA_FIRMWARE_VERSION;
static const char s_fw_version[] = "MG_VERSION:" MG_FIRMWARE_VERSION;
static bool s_autocommit_ok; // True after OTA server confirms "same version"
@@ -15268,13 +15287,8 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
for (c = mgr->conns; c != NULL; c = tmp) {
long flush = 0;
bool is_resp = c->is_resp;
tmp = c->next;
mg_call(c, MG_EV_POLL, &now);
if (is_resp && !c->is_resp) {
long n = 0;
mg_call(c, MG_EV_READ, &n);
}
MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
@@ -27021,7 +27035,7 @@ void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
ws_handshake(c, wskey, wsproto, fmt, &ap);
va_end(ap);
c->is_websocket = 1;
c->is_resp = 0;
c->pfn_data = NULL; // HTTP response done; mg_ws_cb uses it as an offset
mg_call(c, MG_EV_WS_OPEN, hm);
}
}
+8 -11
View File
@@ -2488,7 +2488,6 @@ struct mg_connection {
unsigned is_closing : 1; // Close and free immediately on next poll
unsigned is_full : 1; // Pause incoming reads until cleared
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
unsigned is_resp : 1; // HTTP: response is still being generated
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
};
@@ -4860,7 +4859,7 @@ enum {
// needs to be defined. Fetches metadata_url, which must
// return a JSON object like:
// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
// If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads
// If the server version differs from MG_FIRMWARE_VERSION, downloads
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
// NULL on successful flash, "Same version" when already up to date, or an
// error string on failure. Pass NULL to use the default handler which logs
@@ -4887,8 +4886,8 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
#endif
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
#ifndef MG_OTA_FIRMWARE_VERSION
#define MG_OTA_FIRMWARE_VERSION "1.0.0"
#ifndef MG_FIRMWARE_VERSION
#define MG_FIRMWARE_VERSION "1.0.0"
#endif
// Maximum version string length
@@ -4968,14 +4967,15 @@ enum mg_health_reason {
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
};
#ifndef MG_HEALTH_BACKTRACE
#define MG_HEALTH_BACKTRACE 20 // Return addresses in mg_health::backtrace
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 20
#endif
struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint32_t reset_reason; // enum mg_health_reason that started this boot
uint32_t backtrace[MG_HEALTH_BACKTRACE]; // Crash backtrace, frame 0 first
uint32_t cpuid; // ARM CPU ID
uint32_t data[MG_HEALTH_DATA_SIZE];
};
extern struct mg_health mg_health_record; // Defined in health.c
@@ -5051,16 +5051,13 @@ static inline void mg_health_init(void) {
#define MG_MDASH_KEY ""
#endif
#ifndef MG_MDASH_FIRMWARE_VERSION
#define MG_MDASH_FIRMWARE_VERSION "1.0.0"
#endif
#ifndef MG_ENABLE_MDASH
#define MG_ENABLE_MDASH 0
#endif
void mg_mdash_init(struct mg_mgr *);
void mg_mdash_poll(struct mg_mgr *);
void mg_mdash_free(struct mg_mgr *);
+1 -1
View File
@@ -603,7 +603,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
// The response has been send in EV_HDRS path, so we're not reponding
// anything but clearing the marker for the next request.
d->marker = 0;
c->is_resp = 0;
c->pfn_data = NULL; // Response done
} else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
+4 -3
View File
@@ -52,14 +52,15 @@ enum mg_health_reason {
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
};
#ifndef MG_HEALTH_BACKTRACE
#define MG_HEALTH_BACKTRACE 20 // Return addresses in mg_health::backtrace
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 20
#endif
struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint32_t reset_reason; // enum mg_health_reason that started this boot
uint32_t backtrace[MG_HEALTH_BACKTRACE]; // Crash backtrace, frame 0 first
uint32_t cpuid; // ARM CPU ID
uint32_t data[MG_HEALTH_DATA_SIZE];
};
extern struct mg_health mg_health_record; // Defined in health.c
+13 -12
View File
@@ -358,7 +358,7 @@ static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
if (c->send.len >= len + 10) {
mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
c->send.buf[len + 8] = '\r';
if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
if (c->send.len == len + 10) c->pfn_data = NULL; // Last chunk, done
}
if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
}
@@ -373,7 +373,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
mg_printf(c, "%lx\r\n", (unsigned long) len);
if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
if (len == 0) c->is_resp = 0;
if (len == 0) c->pfn_data = NULL; // Last chunk, response done
}
// clang-format off
@@ -462,15 +462,14 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers,
(unsigned long) (c->send.len - len));
c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
}
c->is_resp = 0;
c->pfn_data = NULL; // Response done
}
static void http_cb(struct mg_connection *, int, void *);
static void restore_http_cb(struct mg_connection *c) {
mg_fs_close((struct mg_fd *) c->pfn_data);
c->pfn_data = NULL;
c->pfn_data = NULL; // Response done
c->pfn = http_cb;
c->is_resp = 0;
}
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
@@ -658,7 +657,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
range, hdrs);
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
c->is_resp = 0;
c->pfn_data = NULL; // Response done
mg_fs_close(fd);
} else { // start serving static content only if not closing, see #3354
// Track to-be-sent content length at the end of c->data, aligned
@@ -727,7 +726,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
if (n > sizeof(tmp)) n = 0;
memcpy(c->send.buf + off - 12, tmp, n); // Set content length
c->is_resp = 0; // Mark response end
c->pfn_data = NULL; // Response done
}
// Map requested URI to the file path (buf,len). Use root directory r.
@@ -781,7 +780,7 @@ static int uri_to_file_status(struct mg_connection *c,
"Content-Length: 0\r\n"
"\r\n",
(int) hm->uri.len, hm->uri.buf);
c->is_resp = 0;
c->pfn_data = NULL; // Response done
flags = -1;
} else if (flags & MG_FS_DIR) {
if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
@@ -1126,7 +1125,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
c->recv.len > 0)) { // see #2796
struct mg_http_message hm;
size_t ofs = 0; // Parsing offset
while (c->is_resp == 0 && ofs < c->recv.len) {
// c->pfn_data is the "response generation in progress" marker. While it
// is set, don't parse the next pipelined request, to keep responses in order
while (c->pfn_data == NULL && ofs < c->recv.len) {
const char *buf = (char *) c->recv.buf + ofs;
int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
struct mg_str *te; // Transfer - encoding header
@@ -1219,9 +1220,9 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
ofs += (size_t) n + hm.body.len;
}
if (c->is_accepted) c->is_resp = 1; // Start generating response
mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp
if (c->is_accepted && !c->is_resp) {
if (c->is_accepted) c->pfn_data = c; // Response started
mg_call(c, MG_EV_HTTP_MSG, &hm); // Handler sets pfn_data NULL when done
if (c->is_accepted && c->pfn_data == NULL) {
struct mg_str *cc = mg_http_get_header(&hm, "Connection");
if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) {
c->is_draining = 1; // honor "Connection: close"
+48 -33
View File
@@ -55,7 +55,10 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
// 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 = {.ca = mg_str(mg_mdash_ca_pem), .name = host};
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);
}
}
@@ -65,16 +68,18 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
// mDashTriggerEvent(s_mDashState, NULL);
} else if (ev == MG_EV_WS_MSG) {
struct mg_iobuf io = {0, 0, 0, 512};
struct mg_ws_message *wm = ev_data;
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));
struct mg_rpc_req r = {.head = &c->mgr->rpcs,
.rpc = NULL,
.pfn = mg_pfn_iobuf,
.pfn_data = &io,
.req_data = NULL,
.frame = s};
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));
@@ -90,32 +95,35 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
(void) c;
}
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t n = va_arg(*ap, size_t);
uint32_t *p = va_arg(*ap, uint32_t *);
size_t len = 0;
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
return len;
}
static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t len = 0;
if (mg_health_valid()) {
uint32_t *data = mg_health_record.backtrace;
mg_rpc_ok(r,
"{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:true,"
"%m:{%m:\"0x%08lx\",%m:\"0x%08lx\",%m:\"0x%08lx\"},"
"%m:{%m:\"0x%08lx\",%m:%m}}}",
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION),
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000),
MG_ESC("reboot_reason"),
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"),
MG_ARCH, MG_ESC("report"), MG_ESC("valid"), MG_ESC("regs"),
MG_ESC("sp"), (unsigned long) data[0], MG_ESC("lr"),
(unsigned long) data[1], MG_ESC("pc"), (unsigned long) data[2],
MG_ESC("stack"), MG_ESC("addr"), (unsigned long) data[0],
MG_ESC("data"), mg_print_base64,
(int) ((MG_HEALTH_BACKTRACE - 3) * sizeof(data[0])),
(uint8_t *) &data[3]);
} else {
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:{%m:false}}",
MG_ESC("fw_version"), MG_ESC(MG_MDASH_FIRMWARE_VERSION),
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000),
MG_ESC("reboot_reason"),
MG_ESC(mg_health_reason_str(mg_health_reason())), MG_ESC("arch"),
MG_ARCH, MG_ESC("report"), MG_ESC("valid"));
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"),
MG_ESC("version"), mg_health_record.magic[3], //
MG_ESC("cpuid"), mg_health_record.cpuid, //
MG_ESC("data"), print_uint32, n, mg_health_record.data);
}
(void) ap;
return len;
}
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}", //
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, //
print_crash);
}
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
@@ -134,7 +142,7 @@ static void mg_mdash_rpc_ota_write(struct mg_rpc_req *r) {
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
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();
@@ -179,6 +187,10 @@ void mg_mdash_poll(struct mg_mgr *mgr) {
}
}
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;
@@ -186,4 +198,7 @@ void mg_mdash_init(struct mg_mgr *mgr) {
void mg_mdash_poll(struct mg_mgr *mgr) {
(void) mgr;
}
void mg_mdash_free(struct mg_mgr *mgr) {
(void) mgr;
}
#endif
+1 -4
View File
@@ -12,13 +12,10 @@
#define MG_MDASH_KEY ""
#endif
#ifndef MG_MDASH_FIRMWARE_VERSION
#define MG_MDASH_FIRMWARE_VERSION "1.0.0"
#endif
#ifndef MG_ENABLE_MDASH
#define MG_ENABLE_MDASH 0
#endif
void mg_mdash_init(struct mg_mgr *);
void mg_mdash_poll(struct mg_mgr *);
void mg_mdash_free(struct mg_mgr *);
+3
View File
@@ -300,6 +300,9 @@ void mg_mgr_free(struct mg_mgr *mgr) {
mgr->timers = NULL; // Important. Next call to poll won't touch timers
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ENABLE_MDASH
mg_mdash_free(mgr);
#endif
#if MG_ENABLE_FREERTOS_TCP
FreeRTOS_DeleteSocketSet(mgr->ss);
#endif
-1
View File
@@ -95,7 +95,6 @@ struct mg_connection {
unsigned is_closing : 1; // Close and free immediately on next poll
unsigned is_full : 1; // Pause incoming reads until cleared
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
unsigned is_resp : 1; // HTTP: response is still being generated
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
};
+1 -1
View File
@@ -13,7 +13,7 @@
// Scannable version tag embedded in every firmware binary, for server-side
// version extraction. The version string starts after the "MG_VERSION:" prefix.
static const char s_fw_version[] = "MG_VERSION:" MG_OTA_FIRMWARE_VERSION;
static const char s_fw_version[] = "MG_VERSION:" MG_FIRMWARE_VERSION;
static bool s_autocommit_ok; // True after OTA server confirms "same version"
+3 -3
View File
@@ -154,7 +154,7 @@ enum {
// needs to be defined. Fetches metadata_url, which must
// return a JSON object like:
// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
// If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads
// If the server version differs from MG_FIRMWARE_VERSION, downloads
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
// NULL on successful flash, "Same version" when already up to date, or an
// error string on failure. Pass NULL to use the default handler which logs
@@ -181,8 +181,8 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
#endif
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
#ifndef MG_OTA_FIRMWARE_VERSION
#define MG_OTA_FIRMWARE_VERSION "1.0.0"
#ifndef MG_FIRMWARE_VERSION
#define MG_FIRMWARE_VERSION "1.0.0"
#endif
// Maximum version string length
-5
View File
@@ -772,13 +772,8 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
for (c = mgr->conns; c != NULL; c = tmp) {
long flush = 0;
bool is_resp = c->is_resp;
tmp = c->next;
mg_call(c, MG_EV_POLL, &now);
if (is_resp && !c->is_resp) {
long n = 0;
mg_call(c, MG_EV_READ, &n);
}
MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
+1 -1
View File
@@ -287,7 +287,7 @@ void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
ws_handshake(c, wskey, wsproto, fmt, &ap);
va_end(ap);
c->is_websocket = 1;
c->is_resp = 0;
c->pfn_data = NULL; // HTTP response done; mg_ws_cb uses it as an offset
mg_call(c, MG_EV_WS_OPEN, hm);
}
}
+3 -1
View File
@@ -1,6 +1,6 @@
SRCS = mongoose.c unit_test.c packed_fs.c
HDRS = $(wildcard ../src/*.h) $(wildcard ../src/drivers/*.h)
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6)
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6) -DMG_ENABLE_MDASH=$(MDASH)
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
OPTS ?= -O3 -g3
INCS ?= -Isrc -I.
@@ -13,6 +13,7 @@ DOCKER_BIN ?= docker
DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(ROOT_DIR):$(ROOT_DIR) -w $(CWD)
VCFLAGS ?= /nologo /W3 /O2 /MD /I.
IPV6 ?= 1
MDASH ?= 1
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
ASAN_OPTIONS ?= detect_leaks=1
# This does not include ESP32, ESP8266, Microchip; all examples that are directly placed under an OS tutorial. Also, it is needed that way...
@@ -269,6 +270,7 @@ clean: clean_tutorials clean_tutorials_embedded
# MIP (net_builtin + L2 + driver) TESTS
mip_test: MDASH = 0
mip_test: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile
$(CC) mip_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
+1 -1
View File
@@ -3351,7 +3351,7 @@ static void eY(struct mg_connection *c, int ev, void *ev_data) {
c->data[0]++;
if (c->data[0] == 10) mg_send(c, "a", 1);
if (c->data[0] == 12) mg_send(c, "bc", 2);
if (c->data[0] == 30) mg_send(c, "d", 1), c->is_resp = 0, c->data[0] = 0;
if (c->data[0] == 30) mg_send(c, "d", 1), c->pfn_data = NULL, c->data[0] = 0;
}
(void) ev_data;
}
+1 -1
View File
@@ -65,7 +65,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
}
c->fn_data = c2;
forward_request(hm, c2);
c->is_resp = 0; // process further msgs in keep-alive connection
c->pfn_data = NULL; // process further msgs in keep-alive connection
c2->is_hexdumping = 1;
}
} else if (ev == MG_EV_CLOSE) {
+9 -7
View File
@@ -1,33 +1,31 @@
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -Wno-cast-function-type -Wno-old-style-declaration
CFLAGS += -Wno-unused-parameter
CFLAGS += -I. -Imongoose -Icmsis_core/CMSIS/Core/Include/
CFLAGS += -I. -I$(MGDIR) -Imongoose -Icmsis_core/CMSIS/Core/Include/
CFLAGS += -Icmsis_rw612/devices/RW612/ -Icmsis_rw612/devices/RW612/periph/ -Icmsis_rw612/devices/RW612/drivers/
CFLAGS += -DCPU_RW612ETA2I -DCPU_RW612ETA2I_cm33_no_dsp
CFLAGS += -DBOOT_HEADER_ENABLE=1
CFLAGS += -mcpu=cortex-m33+nodsp -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c hal.c startup.c flexspi.c flash_config.c mongoose/mongoose.c
SOURCES = main.c hal.c startup.c flexspi.c flash_config.c $(MGDIR)/mongoose.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_clock.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_trng.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_reset.c
all build: firmware.bin
mongoose/mongoose.c mongoose/mongoose.h:
mkdir -p mongoose
cp ../../../../mongoose.[ch] mongoose/
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
arm-none-eabi-size --format=berkeley $<
firmware.elf: cmsis_core cmsis_rw612 $(SOURCES) $(wildcard *.h) link.ld Makefile
firmware.elf: cmsis_core cmsis_rw612 $(SOURCES) $(wildcard *.h) link.ld Makefile mongoose_config.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
cmsis_core:
@@ -38,5 +36,9 @@ cmsis_rw612:
unzip -q NXP.RW612_DFP.25.12.00.pack -d $@
rm NXP.RW612_DFP.25.12.00.pack
flash: firmware.bin
(echo 'loadfile firmware.elf'; echo 'reset'; echo 'exit';) | JLinkExe -device RW612 -if SWD -speed 4000 -autoconnect 1 -NoGui 1 -ExitOnError 1
# JRun --nogui --device RW612 --if SWD firmware.elf
clean:
rm -rf firmware.* cmsis_* mongoose/mongoose.*
+1 -1
View File
@@ -18,7 +18,7 @@ static void log_fn(char ch, void *param) {
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
if (hal_timer_expired(&blink_timer, 100, hal_get_tick())) {
hal_gpio_toggle(LED1);
}
}
@@ -8,6 +8,11 @@
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_OTA MG_OTA_RW612
#define MG_TLS MG_TLS_BUILTIN
#define MG_ENABLE_MDASH 0 // Set to 1 to enable https://mdash.net
#define MG_MDASH_KEY "..." // Edit, see https://mdash.net/docs/#mongoose-library
#define MG_FIRMWARE_VERSION "1.0.1"
// #define MG_DRIVER_MDC_CR 51 // RMII MDC clock divider
// #define MG_DRIVER_MDC_HOLDTIME 3 // RMII MDC Holdtime (valid values [0-7])
@@ -87,7 +87,7 @@ static double s_volume = 17.2;
static int s_log_level = MG_LL_DEBUG;
static char s_name[20] = "Dublin";
static int s_ota_interval = 30;
static char s_ota_version[] = MG_OTA_FIRMWARE_VERSION;
static char s_ota_version[] = MG_FIRMWARE_VERSION;
static char s_ota_status[40] = "No scans yet";
static char s_ota_url[100] = "https://my-product.com/ota.json";
+3 -2
View File
@@ -46,15 +46,16 @@ __attribute__((used, noinline)) static void fault_c(uint32_t *frame,
uint32_t exc_return) {
extern uint32_t _estack;
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
uint32_t *r = mg_health_record.backtrace;
uint32_t *r = mg_health_record.data;
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
r[CRASH_SP] = (uint32_t) (uintptr_t) stack;
r[CRASH_LR] = frame[5];
r[CRASH_PC] = frame[6];
for (size_t i = 0; i < MG_HEALTH_BACKTRACE - CRASH_STACK; i++) {
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - CRASH_STACK; i++) {
r[CRASH_STACK + i] = stack + i < &_estack ? stack[i] : 0;
}
mg_health_record.cpuid = SCB->CPUID;
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
NVIC_SystemReset();
}
@@ -28,7 +28,7 @@
// Crash report support. Health record lives in the .mg_health region, see link.ld
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
#define MG_HEALTH_BACKTRACE 1022 // fill the 4KB backup SRAM
#define MG_HEALTH_DATA_SIZE 1000 // fill the 4KB backup SRAM
// mdash.net device management service support
#define MG_ENABLE_MDASH 1