The FirmwareUpdater is a mbed firmware update library with HTTP server on cloud.
Dependents: FirmwareUpdater_TestProgram geigercounter04 firm LPC1768_up_frim
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 }
Generated on Wed Jul 13 2022 06:52:56 by 1.7.2