Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: EthernetNetIf NTPClient_NetServices mbed ConfigFile
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 }
Generated on Tue Jul 12 2022 10:57:43 by
1.7.2