The FirmwareUpdater is a mbed firmware update library with HTTP server on cloud.
Dependents: FirmwareUpdater_TestProgram geigercounter04 firm LPC1768_up_frim
Diff: FirmwareUpdater.cpp
- Revision:
- 1:0305a8120f06
- Parent:
- 0:f9bdb06ab672
- Child:
- 2:a9a32355af69
--- a/FirmwareUpdater.cpp Wed Nov 03 12:57:51 2010 +0000 +++ b/FirmwareUpdater.cpp Wed Nov 03 22:03:18 2010 +0000 @@ -1,6 +1,6 @@ /** * ============================================================================= - * Firmware updater (Version 0.0.1) + * Firmware updater (Version 0.0.2) * ============================================================================= * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems) * @@ -10,7 +10,7 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * @@ -50,9 +50,10 @@ /* * A file name on the mbed local file system should keep '8 + 3' types of name. */ - if (name.length() > 8) { - printf("Invalid firmware name '%s' found. The maximum length is 8.\n", name.c_str()); - LOG("Invalid firmware name '%s' found. The maximum length is 8.\n", name.c_str()); + const int PREFIX_MAXLEN = 4; + if (name.length() > PREFIX_MAXLEN) { + LOG("ERR : Invalid firmware name '%s' found. The maximum length is PREFIX_MAXLEN.\n", name.c_str()); + error("ERR : Invalid firmware name '%s' found. The maximum length is PREFIX_MAXLEN.\n", name.c_str()); } } @@ -92,36 +93,19 @@ /* * Fetch the version from a local. */ - { - std::string file_local = "/local/" + name + EXT_TXT; - FILE *fp = fopen(file_local.c_str(), "rb"); - if (fp == NULL) { - LOG("Local firmware version file '%s' open failed.\n", file_local.c_str()); - return -1; - } - if (fscanf(fp, "%d", &ver_local) != 1) { - LOG("Local firmware version file '%s' is invalid.\n", file_local.c_str()); - return -2; - } - fclose(fp); - LOG("Local firmware version is %d. (%s)\n", ver_local, file_local.c_str()); + std::string file_local = "/local/" + name + EXT_TXT; + ver_local = readVersionFromFile(file_local.c_str()); + if (ver_local < 0) { + return -1; } + /* * Fetch the version from a server. */ - { - std::string file_server = url + "/" + name + EXT_TXT; - HTTPText text; - HTTPResult r = client.get(file_server.c_str(), &text); - if (r != HTTP_OK) { - LOG("Server firmware version file '%s' open failed.\n", file_server.c_str()); - return -3; - } - if (sscanf(text.gets(), "%d", &ver_server) != 1) { - LOG("Server firmware version file '%s' is invalid.\n", file_server.c_str()); - return -4; - } - LOG("Server firmware version is %d. (%s)\n", ver_server, file_server.c_str()); + std::string file_server = url + "/" + name + EXT_TXT; + ver_server = readVersionFromURL(file_server.c_str()); + if (ver_server < 0) { + return -2; } return (ver_local < ver_server) ? 0 : 1; @@ -137,25 +121,65 @@ * Fetch the files. */ std::string serv_txt = url + "/" + name + EXT_TXT; - std::string serv_bin = url + "/" + name + EXT_BIN; std::string file_txttmp = "/local/" + name + EXT_TXTTMP; - std::string file_bintmp = "/local/" + name + EXT_BINTMP; if (fetch(serv_txt, file_txttmp) != 0) { + LOG("ERR : Aborted...\n"); return -1; } + std::string serv_bin = url + "/" + name + EXT_BIN; + std::string file_bintmp = "/local/" + name + EXT_BINTMP; if (fetch(serv_bin, file_bintmp) != 0) { + LOG("ERR : Aborted...\n"); return -2; } + + /* + * Check the firmware versions. + */ + std::string file_txt = "/local/" + name + EXT_TXT; + int ver_old = readVersionFromFile(file_txt.c_str()); + int ver_new = readVersionFromFile(file_txttmp.c_str()); + if (ver_old < 0) { + LOG("ERR : Could not read the previous firmware version.\n"); + LOG("ERR : Aborted...\n"); + return -3; + } + if (ver_new < 0) { + LOG("ERR : Could not read the new firmware version.\n"); + LOG("ERR : Aborted...\n"); + return -4; + } + if (ver_new < ver_old) { + LOG("ERR : Ignore the new firmware. (old=%d, new=%d)\n", ver_old, ver_new); + LOG("ERR : Aborted...\n"); + return -5; + } + LOG("INFO: Firmware updating... (%d -> %d)\n", ver_old, ver_new); + + /* + * Cleanup the previous versions. + * + * Note: + * A file time stamp on mbed is always '12:00 01/01/2008'. + * mbed can't sense updated firmware when the file name is same as previous version. + * + * So I decided to cleanup all bin files. + * And the new firmware name is 'name-VERSION.bin'. + * To remove previous versions at first means 'start critical section on the system'. + */ + cleanupAllBinFiles(); + /* * Copy it. */ - std::string file_txt = "/local/" + name + EXT_TXT; - std::string file_bin = "/local/" + name + EXT_BIN; + char nbuf[32]; + snprintf(nbuf, sizeof(nbuf) - 1, "-%d", ver_new); + std::string file_bin = "/local/" + name + std::string(nbuf) + EXT_BIN; if (copy(file_txttmp, file_txt) != 0) { - return -3; + return -6; } if (copy(file_bintmp, file_bin) != 0) { - return -4; + return -7; } /* * Delete the temporary files. @@ -187,10 +211,10 @@ HTTPFile file(local_file.c_str()); int r = client.get(src_url.c_str(), &file); if (r != HTTP_OK) { - LOG("Fetch '%s' from '%s' failed.\n", local_file.c_str(), src_url.c_str()); + LOG("ERR : Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str()); return -1; } - LOG("Fetch '%s' from '%s' succeed.\n", local_file.c_str(), src_url.c_str()); + LOG("INFO: Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str()); return 0; } @@ -203,18 +227,16 @@ * @return Return 0 if it succeed. */ int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) { - - LOG("File copying... (%s->%s)\n", local_file1.c_str(), local_file2.c_str()); - + LOG("INFO: File copying... (%s->%s)\n", local_file1.c_str(), local_file2.c_str()); FILE *rp = fopen(local_file1.c_str(), "rb"); if (rp == NULL) { - LOG("File '%s' open failed.\n", local_file1.c_str()); + LOG("ERR : File '%s' open failed.\n", local_file1.c_str()); return -1; } remove(local_file2.c_str()); FILE *wp = fopen(local_file2.c_str(), "wb"); if (wp == NULL) { - LOG("File '%s' open failed.\n", local_file2.c_str()); + LOG("ERR : File '%s' open failed.\n", local_file2.c_str()); fclose(rp); return -2; } @@ -224,7 +246,7 @@ } fclose(rp); fclose(wp); - LOG("File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str()); + LOG("INFO: File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str()); return 0; } @@ -242,8 +264,80 @@ va_start(p, format); vsnprintf(buf, sizeof(buf) - 1, format, p); fprintf(fplog, "%s", buf); + + printf("%s", buf); + va_end(p); fclose(fplog); } } } + +/** + * Cleanup all bin files. + */ +int FirmwareUpdater::cleanupAllBinFiles(void) { + struct dirent *p; + DIR *dir = opendir("/local"); + if (dir == NULL) { + return -1; + } + while ((p = readdir(dir)) != NULL) { + char *str = p->d_name; + if ((strstr(str, ".bin") != NULL) || (strstr(str, ".BIN") != NULL)) { + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf) - 1, "/local/%s", str); + if (remove(buf) == 0) { + LOG("INFO: Deleted '%s'.\n", buf); + } else { + LOG("ERR : Delete '%s' failed.\n", buf); + } + } + } + closedir(dir); + return 0; +} + +/** + * Read a version from a file. + * + * @param filename file name. + * @return A version. + */ +int FirmwareUpdater::readVersionFromFile(const char *filename) { + int ver; + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) { + LOG("ERR : Version file '%s' open failed.\n", filename); + return -1; + } + if (fscanf(fp, "%d", &ver) != 1) { + LOG("ERR : Version file '%s' is invalid.\n", filename); + return -2; + } + fclose(fp); + LOG("INFO: Version file '%s': Version %d.\n", filename, ver); + return ver; +} + +/** + * Read a version from a URL. + * + * @param url URL. + * @return A version. + */ +int FirmwareUpdater::readVersionFromURL(const char *url) { + int ver; + HTTPText text; + HTTPResult r = client.get(url, &text); + if (r != HTTP_OK) { + LOG("ERR : Version file '%s' open failed.\n", url); + return -1; + } + if (sscanf(text.gets(), "%d", &ver) != 1) { + LOG("ERR : Version file '%s' is invalid.\n", url); + return -2; + } + LOG("INFO: Version file '%s': Version %d.\n", url, ver); + return ver; +}