The FirmwareUpdater is a mbed firmware update library with HTTP server on cloud.
Fork of FirmwareUpdater by
FirmwareUpdater.cpp
- Committer:
- vinajarr
- Date:
- 2014-11-13
- Revision:
- 4:8bfdadb09544
- Parent:
- 3:851bd91fa0ae
File content as of revision 4:8bfdadb09544:
/** * ============================================================================= * Firmware updater (Version 0.0.2) * ============================================================================= * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ============================================================================= */ #include "FirmwareUpdater.h" #include <stdio.h> #include <stdarg.h> extern "C" void mbed_reset(); const std::string FirmwareUpdater::EXT_BIN = ".bin"; const std::string FirmwareUpdater::EXT_BINTMP = ".b__"; const std::string FirmwareUpdater::EXT_TXT = ".txt"; const std::string FirmwareUpdater::EXT_TXTTMP = ".t__"; /** * Create. * * @param url URL for firmware. Do not include a target file name. * @param name An application name. Do not include a extention. * @param log True if logging. */ FirmwareUpdater::FirmwareUpdater(std::string url, std::string name, bool log) : url(url), name(name), log(log), local("local") { client.setTimeout(10000); /* * A file name on the mbed local file system should keep '8 + 3' types of name. */ if (MAXNAMELEN < name.length()) { LOG("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", name.c_str(), MAXNAMELEN); error("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", name.c_str(), MAXNAMELEN); } } /** * Dispose. */ FirmwareUpdater::~FirmwareUpdater() { } /** * Get a URL. * * @return URL. */ const std::string FirmwareUpdater:: getURL() const { return url; } /** * Get a name. * * @return name. */ const std::string FirmwareUpdater:: getName() const { return name; } /** * Checking a new firmware. * Compare versions of the software between local storage on mbed and on webserver. * * @return Return 0 if a new firmware exists. */ int FirmwareUpdater::exist() { int ver_local, ver_server; /* * Fetch the version from a local. */ 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; ver_server = readVersionFromURL(file_server.c_str()); if (ver_server < 0) { return -2; } return (ver_local < ver_server) ? 0 : 1; } /** * Execute update. * * @return Return 0 if it succeed. */ int FirmwareUpdater::execute() { /* * Fetch the files. */ std::string serv_txt = url + "/" + name + EXT_TXT; std::string file_txttmp = "/local/" + name + EXT_TXTTMP; 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. */ char nn[32]; createNewBinName(ver_new, nn, sizeof(nn)); std::string file_bin = "/local/" + std::string(nn) + EXT_BIN; if (copy(file_txttmp, file_txt) != 0) { return -6; } if (copy(file_bintmp, file_bin) != 0) { return -7; } /* * Delete the temporary files. */ remove(file_txttmp.c_str()); remove(file_bintmp.c_str()); return 0; } /** * Reset system. */ void FirmwareUpdater::reset() { mbed_reset(); } /** * Fetch a file. * * @param src_url URL of a source file. * @param local_file Local file name. * * @return Return 0 if it succeed. */ int FirmwareUpdater::fetch(std::string src_url, std::string local_file) { /* * Fetch the source file from URL to a temporary file on local. */ HTTPFile file(local_file.c_str()); int r = client.get(src_url.c_str(), &file); if (r != HTTP_OK) { LOG("ERR : Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str()); return -1; } LOG("INFO: Fetched '%s' to '%s'.\n", src_url.c_str(), local_file.c_str()); return 0; } /** * Copy a file. * * @param local_file1 Source file. * @param local_file2 Destination file. * * @return Return 0 if it succeed. */ int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) { 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("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("ERR : File '%s' open failed.\n", local_file2.c_str()); fclose(rp); return -2; } int c; while ((c = fgetc(rp)) != EOF) { fputc(c, wp); } fclose(rp); fclose(wp); LOG("INFO: File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str()); return 0; } /** * Output a message to a log file. * * @param format ... */ void FirmwareUpdater::LOG(const char* format, ...) { if (log) { FILE *fplog = fopen("/local/update.log", "a"); if (fplog != NULL) { char buf[BUFSIZ]; va_list p; va_start(p, format); vsnprintf(buf, sizeof(buf) - 1, format, p); fprintf(fplog, "%s", buf); // printf("%s", buf); /* If you want to check a message from a console. */ 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; } /** * Create a new binary file name. * * @param ver Version. * @param buf A pointer to a buffer. * @param siz A size of the buffer. * * @return Return 0 if it succeed. */ int FirmwareUpdater::createNewBinName(const int ver, char *buf, size_t siz) { if (siz <= name.length()) { return -1; } snprintf(buf, siz - 1, "%s", name.c_str()); char nb[32]; snprintf(nb, sizeof(nb) - 1, "-%d", ver); if (strlen(buf) + strlen(nb) <= MAXNAMELEN) { strcat(buf, nb); return 0; } else { strcpy(buf + (MAXNAMELEN - strlen(nb)), nb); 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; }