eichi kowata / Mbed 2 deprecated geiger

Dependencies:   EthernetNetIf NTPClient_NetServices mbed ConfigFile

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FirmwareUpdater.cpp Source File

FirmwareUpdater.cpp

00001 /*******************************************************************************
00002 modify 
00003 2011/08
00004         - Create version text file when read error.
00005         - Use commonClient.
00006         - Reset HTTP request headers whe GET.
00007         - Delete LOG when over 100KB.
00008 2011/07
00009         - '\n' -> '\r\n' (when write to LOG)
00010         - Add MD5 checksum.
00011         - Add fclose(fp) when error & return
00012 
00013 *******************************************************************************/
00014 
00015 /**
00016  * =============================================================================
00017  * Firmware updater (Version 0.0.2)
00018  * =============================================================================
00019  * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems)
00020  *
00021  * Permission is hereby granted, free of charge, to any person obtaining a copy
00022  * of this software and associated documentation files (the "Software"), to deal
00023  * in the Software without restriction, including without limitation the rights
00024  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00025  * copies of the Software, and to permit persons to whom the Software is
00026  * furnished to do so, subject to the following conditions:
00027  *
00028  * The above copyright notice and this permission notice shall be included in
00029  * all copies or substantial portions of the Software.
00030  *
00031  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00032  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00033  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00034  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00035  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00036  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00037  * THE SOFTWARE.
00038  * =============================================================================
00039  */
00040 
00041 #include "FirmwareUpdater.h"
00042 #include "md5.h"            //  2011/07
00043 
00044 #include <stdio.h>
00045 #include <stdarg.h>
00046 
00047 extern "C" void mbed_reset();
00048 
00049 const std::string FirmwareUpdater::EXT_BIN = ".bin";
00050 const std::string FirmwareUpdater::EXT_BINTMP = ".b__";
00051 const std::string FirmwareUpdater::EXT_TXT = ".txt";
00052 const std::string FirmwareUpdater::EXT_TXTTMP = ".t__";
00053 const std::string FirmwareUpdater::EXT_MD5 = ".md5";        //  2011/07
00054 const std::string FirmwareUpdater::EXT_MD5TMP = ".m__";     //  2011/07
00055 
00056 /**
00057  * Create.
00058  *
00059  * @param url URL for firmware. Do not include a target file name.
00060  * @param name An application name. Do not include a extention.
00061  * @param log True if logging.
00062  */
00063 //FirmwareUpdater::FirmwareUpdater(std::string url, std::string name, bool log)
00064 FirmwareUpdater::FirmwareUpdater(std::string url, std::string name, HTTPClient *pclient, bool log) //  2011/08
00065         : url(url), name(name), log(log), local("local") {
00066     client = pclient;           //  2011/08
00067     client->setTimeout(10000);  //  2011/08
00068 
00069     /*
00070      * A file name on the mbed local file system should keep '8 + 3' types of name.
00071      */
00072     if (MAXNAMELEN < name.length()) {
00073         LOG("ERR : Invalid firmware name '%s' found. The maximum length is %d.\r\n", name.c_str(), MAXNAMELEN);
00074         error("ERR : Invalid firmware name '%s' found. The maximum length is %d.\r\n", name.c_str(), MAXNAMELEN);
00075     }
00076 }
00077 
00078 /**
00079  * Dispose.
00080  */
00081 FirmwareUpdater::~FirmwareUpdater() {
00082 }
00083 
00084 /**
00085  * Get a URL.
00086  *
00087  * @return URL.
00088  */
00089 const std::string FirmwareUpdater:: getURL() const {
00090     return url;
00091 }
00092 
00093 /**
00094  * Get a name.
00095  *
00096  * @return name.
00097  */
00098 const std::string FirmwareUpdater:: getName() const {
00099     return name;
00100 }
00101 
00102 /**
00103  * Checking a new firmware.
00104  * Compare versions of the software between local storage on mbed and on webserver.
00105  *
00106  * @return Return 0 if a new firmware exists.
00107  */
00108 int FirmwareUpdater::exist() {
00109     int ver_local, ver_server;
00110 
00111     /*
00112      * Fetch the version from a local.
00113      */
00114     std::string file_local = "/local/" + name + EXT_TXT;
00115     ver_local = readVersionFromFile(file_local.c_str());
00116     if (ver_local < 0) {
00117         return -1;
00118     }
00119 
00120     /*
00121      * Fetch the version from a server.
00122      */
00123     std::string file_server = url + "/" + name + EXT_TXT;
00124     ver_server = readVersionFromURL(file_server.c_str());
00125     if (ver_server < 0) {
00126         return -2;
00127     }
00128 
00129     return (ver_local < ver_server) ? 0 : 1;
00130 }
00131 
00132 /**
00133  * Execute update.
00134  *
00135  * @return Return 0 if it succeed.
00136  */
00137 int FirmwareUpdater::execute() {
00138     /*
00139      * Fetch the files.
00140      */
00141     std::string serv_txt = url + "/" + name + EXT_TXT;
00142     std::string file_txttmp = "/local/" + name + EXT_TXTTMP;
00143     if (fetch(serv_txt, file_txttmp) != 0) {
00144         LOG("ERR : Aborted...\r\n");
00145         return -1;
00146     }
00147     std::string serv_bin = url + "/" + name + EXT_BIN;
00148     std::string file_bintmp = "/local/" + name + EXT_BINTMP;
00149     if (fetch(serv_bin, file_bintmp) != 0) {
00150         LOG("ERR : Aborted...\r\n");
00151         return -2;
00152     }
00153     std::string serv_md5 = url + "/" + name + EXT_MD5;
00154     std::string file_md5tmp = "/local/" + name + EXT_MD5TMP;
00155     if (fetch(serv_md5, file_md5tmp) != 0) {
00156         LOG("ERR : Aborted...\r\n");
00157         return -8;
00158     }
00159 
00160     /*
00161      * Check the firmware versions.
00162      */
00163     std::string file_txt = "/local/" + name + EXT_TXT;
00164     int ver_old = readVersionFromFile(file_txt.c_str());
00165     int ver_new = readVersionFromFile(file_txttmp.c_str());
00166     if (ver_old < 0) {
00167         LOG("ERR : Could not read the previous firmware version.\r\n");
00168         LOG("ERR : Aborted...\r\n");
00169         return -3;
00170     }
00171     if (ver_new < 0) {
00172         LOG("ERR : Could not read the new firmware version.\r\n");
00173         LOG("ERR : Aborted...\r\n");
00174         return -4;
00175     }
00176     if (ver_new < ver_old) {
00177         LOG("ERR : Ignore the new firmware. (old=%d, new=%d)\r\n", ver_old, ver_new);
00178         LOG("ERR : Aborted...\r\n");
00179         return -5;
00180     }
00181     /*
00182      * MD5 check sum
00183      *  2011/07
00184      *
00185      */
00186     char md5str[33];
00187     int readret = readMd5FromFile(file_md5tmp.c_str(), md5str);
00188     if (readret < 0) {
00189         LOG("ERR : Could not read the new firmware MD5.\r\n");
00190         LOG("ERR : Aborted...\r\n");
00191         return -9;
00192     }
00193     
00194     #define BUFF_SIZE 1024
00195 
00196     MD5 md5;
00197     FILE *fp;
00198     size_t len;
00199     unsigned char buff[BUFF_SIZE];
00200     
00201     fp = fopen(file_bintmp.c_str(),"rb");
00202     if ( fp == NULL ) {
00203         LOG("ERR : Could not read the new firmware bin.\r\n");
00204         LOG("ERR : Aborted...\r\n");
00205         return -10;
00206     }
00207     while (len = fread(buff, 1, BUFF_SIZE, fp)) {
00208         md5.update( buff, len);
00209     }
00210     fclose( fp );
00211     md5.finalize();
00212     
00213     string hash;
00214     hash = md5.hexdigest ();
00215     if (strncmp(md5str, hash.c_str(), 32) != 0) {
00216         LOG("ERR : MD5 checksum error. Server MD5=%s, calc MD5 from binary=%s\r\n", md5str, hash.c_str() );
00217         LOG("ERR : Aborted...\r\n");
00218         return -11;
00219     }
00220 
00221     
00222     
00223     LOG("INFO: Firmware updating... (%d -> %d)\r\n", ver_old, ver_new);
00224 
00225     /*
00226      * Cleanup the previous versions.
00227      *
00228      * Note:
00229      *  A file time stamp on mbed is always '12:00 01/01/2008'.
00230      *  mbed can't sense updated firmware when the file name is same as previous version.
00231      *
00232      *  So I decided to cleanup all bin files.
00233      *  And the new firmware name is 'name-VERSION.bin'.
00234      *  To remove previous versions at first means 'start critical section on the system'.
00235      */
00236     //cleanupAllBinFiles();
00237     cleanupAllBbkFiles();   //  2011/07
00238     BackupBinFiles();       //  2011/07
00239 
00240     /*
00241      * Copy it.
00242      */
00243     char nn[32];
00244     createNewBinName(ver_new, nn, sizeof(nn));
00245     std::string file_bin = "/local/" + std::string(nn) + EXT_BIN;
00246     if (copy(file_bintmp, file_bin) != 0) {
00247         return -7;
00248     }
00249     if (copy(file_txttmp, file_txt) != 0) {
00250         return -6;
00251     }
00252     /*
00253      * Delete the temporary files.
00254      */
00255     remove(file_txttmp.c_str());
00256     remove(file_bintmp.c_str());
00257     remove(file_md5tmp.c_str());    //  2011/07
00258     return 0;
00259 }
00260 
00261 /**
00262  * Reset system.
00263  */
00264 void FirmwareUpdater::reset() {
00265     mbed_reset();
00266 }
00267 
00268 /**
00269  * Fetch a file.
00270  *
00271  * @param src_url URL of a source file.
00272  * @param local_file Local file name.
00273  *
00274  * @return Return 0 if it succeed.
00275  */
00276 int FirmwareUpdater::fetch(std::string src_url, std::string local_file) {
00277     /*
00278      * Fetch the source file from URL to a temporary file on local.
00279      */
00280     HTTPFile file(local_file.c_str());
00281     client->resetRequestHeaders();                  //  2011/08
00282     int r = client->get(src_url.c_str(), &file);    //  2011/08
00283     if (r != HTTP_OK) {
00284         LOG("ERR : Fetch '%s' to '%s'.\r\n", src_url.c_str(), local_file.c_str());
00285         return -1;
00286     }
00287     LOG("INFO: Fetched '%s' to '%s'.\r\n", src_url.c_str(), local_file.c_str());
00288     return 0;
00289 }
00290 
00291 /**
00292  * Copy a file.
00293  *
00294  * @param local_file1 Source file.
00295  * @param local_file2 Destination file.
00296  *
00297  * @return Return 0 if it succeed.
00298  */
00299 int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) {
00300     LOG("INFO: File copying... (%s->%s)\r\n", local_file1.c_str(), local_file2.c_str());
00301     FILE *rp = fopen(local_file1.c_str(), "rb");
00302     if (rp == NULL) {
00303         LOG("ERR : File '%s' open failed.\r\n", local_file1.c_str());
00304         return -1;
00305     }
00306     remove(local_file2.c_str());
00307     FILE *wp = fopen(local_file2.c_str(), "wb");
00308     if (wp == NULL) {
00309         LOG("ERR : File '%s' open failed.\r\n", local_file2.c_str());
00310         fclose(rp);
00311         return -2;
00312     }
00313     int c;
00314     while ((c = fgetc(rp)) != EOF) {
00315         fputc(c, wp);
00316     }
00317 
00318     fclose(rp);
00319     fclose(wp);
00320     LOG("INFO: File copied. (%s->%s)\r\n", local_file1.c_str(), local_file2.c_str());
00321     return 0;
00322 }
00323 
00324 /**
00325  * Output a message to a log file.
00326  *
00327  * @param format ...
00328  */
00329 void FirmwareUpdater::LOG(const char* format, ...) {
00330     if (log) {
00331         FILE *fplog = fopen("/local/update.log", "a");
00332 
00333         if (fplog != NULL) {
00334             char buf[BUFSIZ];
00335             va_list p;
00336             va_start(p, format);
00337             vsnprintf(buf, sizeof(buf) - 1, format, p);
00338             fprintf(fplog, "%s", buf);
00339             // printf("%s", buf); /* If you want to check a message from a console. */
00340             va_end(p);
00341             fclose(fplog);
00342         }
00343     }
00344 }
00345 
00346 /**
00347  * Cleanup all bin files.
00348  */
00349 int FirmwareUpdater::cleanupAllBinFiles(void) {
00350     struct dirent *p;
00351     DIR *dir = opendir("/local");
00352     if (dir == NULL) {
00353         return -1;
00354     }
00355     while ((p = readdir(dir)) != NULL) {
00356         char *str = p->d_name;
00357         if ((strstr(str, ".bin") != NULL) || (strstr(str, ".BIN") != NULL)) {
00358             char buf[BUFSIZ];
00359             snprintf(buf, sizeof(buf) - 1, "/local/%s", str);
00360             if (remove(buf) == 0) {
00361                 LOG("INFO: Deleted '%s'.\r\n", buf);
00362             } else {
00363                 LOG("ERR : Delete '%s' failed.\r\n", buf);
00364             }
00365         }
00366     }
00367     closedir(dir);
00368     return 0;
00369 }
00370 
00371 /**
00372  * Cleanup all bbk(binary backup) files.
00373  *   2011/07
00374  */
00375 int FirmwareUpdater::cleanupAllBbkFiles(void) {
00376     struct dirent *p;
00377     DIR *dir = opendir("/local");
00378     if (dir == NULL) {
00379         return -1;
00380     }
00381     while ((p = readdir(dir)) != NULL) {
00382         char *str = p->d_name;
00383         if ((strstr(str, ".bbk") != NULL) || (strstr(str, ".BBK") != NULL)) {
00384             char buf[BUFSIZ];
00385             snprintf(buf, sizeof(buf) - 1, "/local/%s", str);
00386             if (remove(buf) == 0) {
00387                 LOG("INFO: Deleted '%s'.\r\n", buf);
00388             } else {
00389                 LOG("ERR : Delete '%s' failed.\r\n", buf);
00390             }
00391         }
00392     }
00393     closedir(dir);
00394     return 0;
00395 }
00396 
00397 /**
00398  * Backup BIN files.
00399  *   2011/07
00400  */
00401 int FirmwareUpdater::BackupBinFiles() {
00402     struct dirent *p;
00403     DIR *dir = opendir("/local");
00404     if (dir == NULL) {
00405         return -1;
00406     }
00407     while ((p = readdir(dir)) != NULL) {
00408         char *str = p->d_name;
00409         if ((strstr(str, ".bin") != NULL) || (strstr(str, ".BIN") != NULL)) {
00410             char buf[BUFSIZ], bakname[BUFSIZ];
00411             int len;
00412             snprintf(buf, sizeof(buf) - 1, "/local/%s", str);
00413             len = strlen(buf);
00414 #if 0
00415             int dotpos=0;
00416             for (int i=len-1; i>=0; i--) {
00417                 if (buf[i]=='.') {
00418                     dotpos = i;
00419                     break;
00420                 }
00421             }
00422             strncpy(bakname,buf, dotpos); bakname[dotpos]='\0'; strcat(bakname,".BBK");
00423 #endif
00424             for (int i=0; i<len; i++) {
00425                 bakname[i] = buf[i];
00426                 if (bakname[i]=='.') {
00427                     bakname[i+1] = '\0';
00428                     strcat(bakname, "BBK");
00429                     break;
00430                 }
00431             }
00432                 
00433             LOG("INFO: Copy '%s'->'%s'.\r\n", buf,bakname);
00434             if (copy(buf,bakname) == 0) {
00435                 remove(buf);
00436                 LOG("INFO: Remove '%s'.\r\n", buf);
00437             } else {
00438                 LOG("ERR : Copy '%s'->'%s' failed.\r\n", buf,bakname);
00439             }
00440         }
00441     }
00442     closedir(dir);
00443     return 0;
00444 }
00445 
00446 /**
00447  * Create a new binary file name.
00448  *
00449  * @param ver Version.
00450  * @param buf A pointer to a buffer.
00451  * @param siz A size of the buffer.
00452  *
00453  * @return Return 0 if it succeed.
00454  */
00455 int FirmwareUpdater::createNewBinName(const int ver, char *buf, size_t siz) {
00456     if (siz <= name.length()) {
00457         return -1;
00458     }
00459     snprintf(buf, siz - 1, "%s", name.c_str());
00460     char nb[32];
00461     snprintf(nb, sizeof(nb) - 1, "-%d", ver);
00462     if (strlen(buf) + strlen(nb) <= MAXNAMELEN) {
00463         strcat(buf, nb);
00464         return 0;
00465     } else {
00466         strcpy(buf + (MAXNAMELEN - strlen(nb)), nb);
00467         return 0;
00468     }
00469 }
00470 
00471 /**
00472  * Read a version from a file.
00473  *
00474  * @param filename file name.
00475  * @return A version.
00476  */
00477 int FirmwareUpdater::readVersionFromFile(const char *filename) {
00478     int ver;
00479 
00480     remove("/local/update.log");        //   2011/08
00481 
00482     FILE *fp = fopen(filename, "rb");
00483     if (fp == NULL) {
00484         fp = fopen(filename, "wt");     //  2011/08
00485         if (fp == NULL) {               //  2011/08
00486             LOG("ERR : Version file '%s' write open failed.\r\n", filename);
00487             return -1;
00488         }
00489         fprintf(fp, "1\r\n");          //  2011/08
00490         fclose(fp);                     //  2011/08
00491         return 1;                      //  2011/08
00492     }
00493     if (fscanf(fp, "%d", &ver) != 1) {
00494         fclose(fp);     //  2011/07
00495         LOG("ERR : Version file '%s' is invalid.\r\n", filename);
00496         return -2;
00497     }
00498     fclose(fp);
00499     LOG("INFO: Version file '%s': Version %d.\r\n", filename, ver);
00500     return ver;
00501 }
00502 
00503 /**
00504  * Read a version from a URL.
00505  *
00506  * @param url URL.
00507  * @return A version.
00508  */
00509 int FirmwareUpdater::readVersionFromURL(const char *url) {
00510     int ver;
00511     HTTPText text;
00512     client->resetRequestHeaders();                  //  2011/08
00513     HTTPResult r = client->get(url, &text);         //  2011/08
00514     if (r != HTTP_OK) {
00515         LOG("ERR : Version file '%s' open failed.\r\n", url);
00516         return -1;
00517     }
00518     if (sscanf(text.gets(), "%d", &ver) != 1) {
00519         LOG("ERR : Version file '%s' is invalid.\r\n", url);
00520         return -2;
00521     }
00522     LOG("INFO: Version file '%s': Version %d.\r\n", url, ver);
00523     return ver;
00524 }
00525 
00526 /**
00527  * Read a MD5 from a file.
00528  *  2011/07
00529  * @param filename file name.
00530  * @param md5str MD5 string.
00531  * @return Error code.
00532  */
00533 int FirmwareUpdater::readMd5FromFile(const char *filename, char *md5str) {
00534     int ret=0;
00535     FILE *fp = fopen(filename, "rb");
00536     if (fp == NULL) {
00537         LOG("ERR : MD5 file '%s' open failed.\r\n", filename);
00538         return -1;
00539     }
00540     if (fscanf(fp, "%32s", md5str) != 1) {
00541         fclose(fp);
00542         LOG("ERR : MD5 file '%s' is invalid.\r\n", filename);
00543         return -2;
00544     }
00545     fclose(fp);
00546     LOG("INFO: MD5 file '%s': Checksum %s.\r\n", filename, md5str);
00547     return ret;
00548 }