The FirmwareUpdater is a mbed firmware update library with HTTP server on cloud.

Dependents:   FirmwareUpdater_TestProgram geigercounter04 firm LPC1768_up_frim

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FirmwareUpdater.cpp Source File

FirmwareUpdater.cpp

00001 /**
00002  * =============================================================================
00003  * Firmware updater (Version 0.0.2)
00004  * =============================================================================
00005  * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems)
00006  *
00007  * Permission is hereby granted, free of charge, to any person obtaining a copy
00008  * of this software and associated documentation files (the "Software"), to deal
00009  * in the Software without restriction, including without limitation the rights
00010  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011  * copies of the Software, and to permit persons to whom the Software is
00012  * furnished to do so, subject to the following conditions:
00013  *
00014  * The above copyright notice and this permission notice shall be included in
00015  * all copies or substantial portions of the Software.
00016  *
00017  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00020  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00023  * THE SOFTWARE.
00024  * =============================================================================
00025  */
00026 
00027 #include "FirmwareUpdater.h"
00028 
00029 #include <stdio.h>
00030 #include <stdarg.h>
00031 
00032 extern "C" void mbed_reset();
00033 
00034 const std::string FirmwareUpdater::EXT_BIN = ".bin";
00035 const std::string FirmwareUpdater::EXT_BINTMP = ".b__";
00036 const std::string FirmwareUpdater::EXT_TXT = ".txt";
00037 const std::string FirmwareUpdater::EXT_TXTTMP = ".t__";
00038 
00039 /**
00040  * Create.
00041  *
00042  * @param url URL for firmware. Do not include a target file name.
00043  * @param name An application name. Do not include a extention.
00044  * @param log True if logging.
00045  */
00046 FirmwareUpdater::FirmwareUpdater(std::string url, std::string name, bool log)
00047         : url(url), name(name), log(log), local("local") {
00048     client.setTimeout(10000);
00049 
00050     /*
00051      * A file name on the mbed local file system should keep '8 + 3' types of name.
00052      */
00053     if (MAXNAMELEN < name.length()) {
00054         LOG("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", name.c_str(), MAXNAMELEN);
00055         error("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", name.c_str(), MAXNAMELEN);
00056     }
00057 }
00058 
00059 /**
00060  * Dispose.
00061  */
00062 FirmwareUpdater::~FirmwareUpdater() {
00063 }
00064 
00065 /**
00066  * Get a URL.
00067  *
00068  * @return URL.
00069  */
00070 const std::string FirmwareUpdater:: getURL() const {
00071     return url;
00072 }
00073 
00074 /**
00075  * Get a name.
00076  *
00077  * @return name.
00078  */
00079 const std::string FirmwareUpdater:: getName() const {
00080     return name;
00081 }
00082 
00083 /**
00084  * Checking a new firmware.
00085  * Compare versions of the software between local storage on mbed and on webserver.
00086  *
00087  * @return Return 0 if a new firmware exists.
00088  */
00089 int FirmwareUpdater::exist() {
00090     int ver_local, ver_server;
00091 
00092     /*
00093      * Fetch the version from a local.
00094      */
00095     std::string file_local = "/local/" + name + EXT_TXT;
00096     ver_local = readVersionFromFile(file_local.c_str());
00097     if (ver_local < 0) {
00098         return -1;
00099     }
00100 
00101     /*
00102      * Fetch the version from a server.
00103      */
00104     std::string file_server = url + "/" + name + EXT_TXT;
00105     ver_server = readVersionFromURL(file_server.c_str());
00106     if (ver_server < 0) {
00107         return -2;
00108     }
00109 
00110     return (ver_local < ver_server) ? 0 : 1;
00111 }
00112 
00113 /**
00114  * Execute update.
00115  *
00116  * @return Return 0 if it succeed.
00117  */
00118 int FirmwareUpdater::execute() {
00119     /*
00120      * Fetch the files.
00121      */
00122     std::string serv_txt = url + "/" + name + EXT_TXT;
00123     std::string file_txttmp = "/local/" + name + EXT_TXTTMP;
00124     if (fetch(serv_txt, file_txttmp) != 0) {
00125         LOG("ERR : Aborted...\n");
00126         return -1;
00127     }
00128     std::string serv_bin = url + "/" + name + EXT_BIN;
00129     std::string file_bintmp = "/local/" + name + EXT_BINTMP;
00130     if (fetch(serv_bin, file_bintmp) != 0) {
00131         LOG("ERR : Aborted...\n");
00132         return -2;
00133     }
00134 
00135     /*
00136      * Check the firmware versions.
00137      */
00138     std::string file_txt = "/local/" + name + EXT_TXT;
00139     int ver_old = readVersionFromFile(file_txt.c_str());
00140     int ver_new = readVersionFromFile(file_txttmp.c_str());
00141     if (ver_old < 0) {
00142         LOG("ERR : Could not read the previous firmware version.\n");
00143         LOG("ERR : Aborted...\n");
00144         return -3;
00145     }
00146     if (ver_new < 0) {
00147         LOG("ERR : Could not read the new firmware version.\n");
00148         LOG("ERR : Aborted...\n");
00149         return -4;
00150     }
00151     if (ver_new < ver_old) {
00152         LOG("ERR : Ignore the new firmware. (old=%d, new=%d)\n", ver_old, ver_new);
00153         LOG("ERR : Aborted...\n");
00154         return -5;
00155     }
00156     LOG("INFO: Firmware updating... (%d -> %d)\n", ver_old, ver_new);
00157 
00158     /*
00159      * Cleanup the previous versions.
00160      *
00161      * Note:
00162      *  A file time stamp on mbed is always '12:00 01/01/2008'.
00163      *  mbed can't sense updated firmware when the file name is same as previous version.
00164      *
00165      *  So I decided to cleanup all bin files.
00166      *  And the new firmware name is 'name-VERSION.bin'.
00167      *  To remove previous versions at first means 'start critical section on the system'.
00168      */
00169     cleanupAllBinFiles();
00170 
00171     /*
00172      * Copy it.
00173      */
00174     char nn[32];
00175     createNewBinName(ver_new, nn, sizeof(nn));
00176     std::string file_bin = "/local/" + std::string(nn) + EXT_BIN;
00177     if (copy(file_txttmp, file_txt) != 0) {
00178         return -6;
00179     }
00180     if (copy(file_bintmp, file_bin) != 0) {
00181         return -7;
00182     }
00183     /*
00184      * Delete the temporary files.
00185      */
00186     remove(file_txttmp.c_str());
00187     remove(file_bintmp.c_str());
00188     return 0;
00189 }
00190 
00191 /**
00192  * Reset system.
00193  */
00194 void FirmwareUpdater::reset() {
00195     mbed_reset();
00196 }
00197 
00198 /**
00199  * Fetch a file.
00200  *
00201  * @param src_url URL of a source file.
00202  * @param local_file Local file name.
00203  *
00204  * @return Return 0 if it succeed.
00205  */
00206 int FirmwareUpdater::fetch(std::string src_url, std::string local_file) {
00207     /*
00208      * Fetch the source file from URL to a temporary file on local.
00209      */
00210     HTTPFile file(local_file.c_str());
00211     int r = client.get(src_url.c_str(), &file);
00212     if (r != HTTP_OK) {
00213         LOG("ERR : Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());
00214         return -1;
00215     }
00216     LOG("INFO: Fetched '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());
00217     return 0;
00218 }
00219 
00220 /**
00221  * Copy a file.
00222  *
00223  * @param local_file1 Source file.
00224  * @param local_file2 Destination file.
00225  *
00226  * @return Return 0 if it succeed.
00227  */
00228 int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) {
00229     LOG("INFO: File copying... (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
00230     FILE *rp = fopen(local_file1.c_str(), "rb");
00231     if (rp == NULL) {
00232         LOG("ERR : File '%s' open failed.\n", local_file1.c_str());
00233         return -1;
00234     }
00235     remove(local_file2.c_str());
00236     FILE *wp = fopen(local_file2.c_str(), "wb");
00237     if (wp == NULL) {
00238         LOG("ERR : File '%s' open failed.\n", local_file2.c_str());
00239         fclose(rp);
00240         return -2;
00241     }
00242     int c;
00243     while ((c = fgetc(rp)) != EOF) {
00244         fputc(c, wp);
00245     }
00246     fclose(rp);
00247     fclose(wp);
00248     LOG("INFO: File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
00249     return 0;
00250 }
00251 
00252 /**
00253  * Output a message to a log file.
00254  *
00255  * @param format ...
00256  */
00257 void FirmwareUpdater::LOG(const char* format, ...) {
00258     if (log) {
00259         FILE *fplog = fopen("/local/update.log", "a");
00260         if (fplog != NULL) {
00261             char buf[BUFSIZ];
00262             va_list p;
00263             va_start(p, format);
00264             vsnprintf(buf, sizeof(buf) - 1, format, p);
00265             fprintf(fplog, "%s", buf);
00266             // printf("%s", buf); /* If you want to check a message from a console. */
00267             va_end(p);
00268             fclose(fplog);
00269         }
00270     }
00271 }
00272 
00273 /**
00274  * Cleanup all bin files.
00275  */
00276 int FirmwareUpdater::cleanupAllBinFiles(void) {
00277     struct dirent *p;
00278     DIR *dir = opendir("/local");
00279     if (dir == NULL) {
00280         return -1;
00281     }
00282     while ((p = readdir(dir)) != NULL) {
00283         char *str = p->d_name;
00284         if ((strstr(str, ".bin") != NULL) || (strstr(str, ".BIN") != NULL)) {
00285             char buf[BUFSIZ];
00286             snprintf(buf, sizeof(buf) - 1, "/local/%s", str);
00287             if (remove(buf) == 0) {
00288                 LOG("INFO: Deleted '%s'.\n", buf);
00289             } else {
00290                 LOG("ERR : Delete '%s' failed.\n", buf);
00291             }
00292         }
00293     }
00294     closedir(dir);
00295     return 0;
00296 }
00297 
00298 /**
00299  * Create a new binary file name.
00300  *
00301  * @param ver Version.
00302  * @param buf A pointer to a buffer.
00303  * @param siz A size of the buffer.
00304  *
00305  * @return Return 0 if it succeed.
00306  */
00307 int FirmwareUpdater::createNewBinName(const int ver, char *buf, size_t siz) {
00308     if (siz <= name.length()) {
00309         return -1;
00310     }
00311     snprintf(buf, siz - 1, "%s", name.c_str());
00312     char nb[32];
00313     snprintf(nb, sizeof(nb) - 1, "-%d", ver);
00314     if (strlen(buf) + strlen(nb) <= MAXNAMELEN) {
00315         strcat(buf, nb);
00316         return 0;
00317     } else {
00318         strcpy(buf + (MAXNAMELEN - strlen(nb)), nb);
00319         return 0;
00320     }
00321 }
00322 
00323 /**
00324  * Read a version from a file.
00325  *
00326  * @param filename file name.
00327  * @return A version.
00328  */
00329 int FirmwareUpdater::readVersionFromFile(const char *filename) {
00330     int ver;
00331     FILE *fp = fopen(filename, "rb");
00332     if (fp == NULL) {
00333         LOG("ERR : Version file '%s' open failed.\n", filename);
00334         return -1;
00335     }
00336     if (fscanf(fp, "%d", &ver) != 1) {
00337         LOG("ERR : Version file '%s' is invalid.\n", filename);
00338         return -2;
00339     }
00340     fclose(fp);
00341     LOG("INFO: Version file '%s': Version %d.\n", filename, ver);
00342     return ver;
00343 }
00344 
00345 /**
00346  * Read a version from a URL.
00347  *
00348  * @param url URL.
00349  * @return A version.
00350  */
00351 int FirmwareUpdater::readVersionFromURL(const char *url) {
00352     int ver;
00353     HTTPText text;
00354     HTTPResult r = client.get(url, &text);
00355     if (r != HTTP_OK) {
00356         LOG("ERR : Version file '%s' open failed.\n", url);
00357         return -1;
00358     }
00359     if (sscanf(text.gets(), "%d", &ver) != 1) {
00360         LOG("ERR : Version file '%s' is invalid.\n", url);
00361         return -2;
00362     }
00363     LOG("INFO: Version file '%s': Version %d.\n", url, ver);
00364     return ver;
00365 }