fota_cop.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /*
  2. * Copyright (C) 2019-2020 Alibaba Group Holding Limited
  3. */
  4. #include <errno.h>
  5. #include <yoc/fota.h>
  6. #include <yoc/netio.h>
  7. #include <aos/kernel.h>
  8. #include <aos/kv.h>
  9. #include <aos/version.h>
  10. #include <ulog/ulog.h>
  11. #include <yoc/sysinfo.h>
  12. #define COP_IMG_URL "cop_img_url"
  13. #define COP_VERSION "cop_version"
  14. #define TO_URL "flash://misc"
  15. #define TAG "fotacop"
  16. static void cop_res_release(fota_info_t *info)
  17. {
  18. if (info) {
  19. if (info->fota_url) {
  20. aos_free(info->fota_url);
  21. info->fota_url = NULL;
  22. }
  23. if (info->local_changelog) {
  24. aos_free(info->local_changelog);
  25. info->local_changelog = NULL;
  26. }
  27. if (info->changelog) {
  28. aos_free(info->changelog);
  29. info->changelog = NULL;
  30. }
  31. if (info->cur_version) {
  32. aos_free(info->cur_version);
  33. info->cur_version = NULL;
  34. }
  35. if (info->new_version) {
  36. aos_free(info->new_version);
  37. info->new_version = NULL;
  38. }
  39. }
  40. }
  41. #if CONFIG_FOTA_USE_HTTPC == 1
  42. #include <http_client.h>
  43. #include <cJSON.h>
  44. static int cop_get_ota_url(char *ota_url, int len)
  45. {
  46. int ret = -1;
  47. ret = aos_kv_getstring("otaurl", ota_url, len);
  48. if (ret < 0) {
  49. strcpy(ota_url, "http://occ.t-head.cn/api/image/ota/pull");
  50. }
  51. return ret;
  52. }
  53. static int _http_event_handler(http_client_event_t *evt)
  54. {
  55. switch(evt->event_id) {
  56. case HTTP_EVENT_ERROR:
  57. LOGD(TAG, "HTTP_EVENT_ERROR");
  58. break;
  59. case HTTP_EVENT_ON_CONNECTED:
  60. LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  61. break;
  62. case HTTP_EVENT_HEADER_SENT:
  63. LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  64. break;
  65. case HTTP_EVENT_ON_HEADER:
  66. // LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
  67. break;
  68. case HTTP_EVENT_ON_DATA:
  69. // LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
  70. break;
  71. case HTTP_EVENT_ON_FINISH:
  72. LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  73. break;
  74. case HTTP_EVENT_DISCONNECTED:
  75. LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
  76. break;
  77. }
  78. return 0;
  79. }
  80. static bool process_again(int status_code)
  81. {
  82. switch (status_code) {
  83. case HttpStatus_MovedPermanently:
  84. case HttpStatus_Found:
  85. case HttpStatus_TemporaryRedirect:
  86. case HttpStatus_Unauthorized:
  87. return true;
  88. default:
  89. return false;
  90. }
  91. return false;
  92. }
  93. static http_errors_t _http_handle_response_code(http_client_handle_t http_client, int status_code, char *buffer, int buf_size, int data_size)
  94. {
  95. http_errors_t err;
  96. if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found || status_code == HttpStatus_TemporaryRedirect) {
  97. err = http_client_set_redirection(http_client);
  98. if (err != HTTP_CLI_OK) {
  99. LOGE(TAG, "URL redirection Failed");
  100. return err;
  101. }
  102. } else if (status_code == HttpStatus_Unauthorized) {
  103. return HTTP_CLI_FAIL;
  104. } else if(status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden) {
  105. LOGE(TAG, "File not found(%d)", status_code);
  106. return HTTP_CLI_FAIL;
  107. } else if (status_code == HttpStatus_InternalError) {
  108. LOGE(TAG, "Server error occurred(%d)", status_code);
  109. return HTTP_CLI_FAIL;
  110. }
  111. // process_again() returns true only in case of redirection.
  112. if (data_size > 0 && process_again(status_code)) {
  113. /*
  114. * In case of redirection, http_client_read() is called
  115. * to clear the response buffer of http_client.
  116. */
  117. int data_read;
  118. while (data_size > buf_size) {
  119. data_read = http_client_read(http_client, buffer, buf_size);
  120. if (data_read <= 0) {
  121. return HTTP_CLI_OK;
  122. }
  123. data_size -= buf_size;
  124. }
  125. data_read = http_client_read(http_client, buffer, data_size);
  126. if (data_read <= 0) {
  127. return HTTP_CLI_OK;
  128. }
  129. }
  130. return HTTP_CLI_OK;
  131. }
  132. static http_errors_t _http_connect(http_client_handle_t http_client, const char *payload, char *buffer, int buf_size)
  133. {
  134. #define MAX_REDIRECTION_COUNT 10
  135. http_errors_t err = HTTP_CLI_FAIL;
  136. int status_code = 0, header_ret;
  137. int redirect_counter = 0;
  138. do {
  139. if (redirect_counter++ > MAX_REDIRECTION_COUNT) {
  140. LOGE(TAG, "redirect_counter is max");
  141. return HTTP_CLI_FAIL;
  142. }
  143. if (process_again(status_code)) {
  144. LOGD(TAG, "process again,status code:%d", status_code);
  145. }
  146. err = http_client_open(http_client, strlen(payload));
  147. if (err != HTTP_CLI_OK) {
  148. LOGE(TAG, "Failed to open HTTP connection");
  149. return err;
  150. }
  151. int wlen = http_client_write(http_client, payload, strlen(payload));
  152. if (wlen < 0) {
  153. LOGE(TAG, "Write payload failed");
  154. return HTTP_CLI_FAIL;
  155. }
  156. LOGD(TAG, "write payload ok...");
  157. header_ret = http_client_fetch_headers(http_client);
  158. if (header_ret < 0) {
  159. LOGE(TAG, "header_ret:%d", header_ret);
  160. return header_ret;
  161. }
  162. LOGD(TAG, "header_ret:%d", header_ret);
  163. status_code = http_client_get_status_code(http_client);
  164. LOGD(TAG, "status code:%d", status_code);
  165. err = _http_handle_response_code(http_client, status_code, buffer, buf_size, header_ret);
  166. if (err != HTTP_CLI_OK) {
  167. LOGE(TAG, "e handle resp code:%d", err);
  168. return err;
  169. }
  170. } while (process_again(status_code));
  171. return err;
  172. }
  173. static void _http_cleanup(http_client_handle_t client)
  174. {
  175. if (client) {
  176. http_client_cleanup(client);
  177. }
  178. }
  179. static int cop_version_check(fota_info_t *info) {
  180. int ret = 0, rc;
  181. char *payload = NULL;
  182. char getvalue[64];
  183. cJSON *js = NULL;
  184. char *buffer = NULL;
  185. http_errors_t err;
  186. http_client_config_t config = {0};
  187. http_client_handle_t client = NULL;
  188. buffer = aos_zalloc(BUFFER_SIZE + 1);
  189. if (buffer == NULL) {
  190. ret = -ENOMEM;
  191. goto out;
  192. }
  193. if ((payload = aos_malloc(156)) == NULL) {
  194. ret = -ENOMEM;
  195. goto out;
  196. }
  197. #if 1
  198. char * dev_id = aos_get_device_id();
  199. char * pro_model = (char *)aos_get_product_model();
  200. char * app_ver = aos_get_app_version();
  201. snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
  202. dev_id ? dev_id : "null", pro_model ? pro_model : "null", app_ver ? app_ver : "null");
  203. #else
  204. //snprintf(payload, 100, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
  205. // "00A2C6FB32241D9423F5AF00", "hlb_test", "1.0.0-20181125.1321-R-hlb_tes");
  206. // FIXME: for test.
  207. snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
  208. "e6d0d5480440000008376eb1e834a765", "pangu", "1.1.1-20201027.2235-R-pangu");
  209. #endif
  210. LOGD(TAG, "check: %s", payload);
  211. memset(getvalue, 0, sizeof(getvalue));
  212. cop_get_ota_url(getvalue, sizeof(getvalue));
  213. LOGD(TAG, "ota url:%s", getvalue);
  214. config.method = HTTP_METHOD_POST;
  215. config.url = getvalue;
  216. config.timeout_ms = 10000;
  217. config.buffer_size = BUFFER_SIZE;
  218. config.event_handler = _http_event_handler;
  219. LOGD(TAG, "http client init start.");
  220. client = http_client_init(&config);
  221. if (!client) {
  222. LOGE(TAG, "Client init e");
  223. ret = -1;
  224. goto out;
  225. }
  226. LOGD(TAG, "http client init ok.");
  227. http_client_set_header(client, "Content-Type", "application/json");
  228. http_client_set_header(client, "Connection", "keep-alive");
  229. http_client_set_header(client, "Cache-Control", "no-cache");
  230. err = _http_connect(client, payload, buffer, BUFFER_SIZE);
  231. if (err != HTTP_CLI_OK) {
  232. LOGE(TAG, "Client connect e");
  233. ret = -1;
  234. goto out;
  235. }
  236. int read_len = http_client_read(client, buffer, BUFFER_SIZE);
  237. if (read_len <= 0) {
  238. ret = -1;
  239. goto out;
  240. }
  241. buffer[read_len] = 0;
  242. LOGD(TAG, "resp: %s", buffer);
  243. js = cJSON_Parse(buffer);
  244. if (js == NULL) {
  245. ret = -1;
  246. LOGW(TAG, "cJSON_Parse failed");
  247. goto out;
  248. }
  249. cJSON *code = cJSON_GetObjectItem(js, "code");
  250. if (!(code && cJSON_IsNumber(code))) {
  251. ret = -1;
  252. LOGW(TAG, "get code failed");
  253. goto out;
  254. }
  255. LOGD(TAG, "code: %d", code->valueint);
  256. if (code->valueint < 0) {
  257. ret = -1;
  258. goto out;
  259. }
  260. cJSON *result = cJSON_GetObjectItem(js, "result");
  261. if (!(result && cJSON_IsObject(result))) {
  262. LOGW(TAG, "get result failed");
  263. ret = -1;
  264. goto out;
  265. }
  266. cJSON *version = cJSON_GetObjectItem(result, "version");
  267. if (!(version && cJSON_IsString(version))) {
  268. LOGW(TAG, "get version failed");
  269. ret = -1;
  270. goto out;
  271. }
  272. LOGD(TAG, "version: %s", version->valuestring);
  273. aos_kv_setstring(COP_VERSION, version->valuestring);
  274. cJSON *url = cJSON_GetObjectItem(result, "url");
  275. if (!(url && cJSON_IsString(url))) {
  276. ret = -1;
  277. LOGW(TAG, "get url failed");
  278. goto out;
  279. }
  280. LOGD(TAG, "url: %s", url->valuestring);
  281. char *urlbuf = aos_malloc(156);
  282. if (urlbuf == NULL) {
  283. ret = -1;
  284. goto out;
  285. }
  286. rc = aos_kv_getstring(COP_IMG_URL, urlbuf, 156);
  287. if (rc <= 0) {
  288. aos_kv_setstring(COP_IMG_URL, url->valuestring);
  289. } else {
  290. if (strcmp(url->valuestring, urlbuf) == 0) {
  291. aos_kv_getint("fota_offset", &rc);
  292. LOGI(TAG, "continue fota :%d", rc);
  293. } else {
  294. aos_kv_setstring(COP_IMG_URL, url->valuestring);
  295. aos_kv_setint("fota_offset", 0);
  296. LOGI(TAG, "restart fota");
  297. }
  298. }
  299. aos_free(urlbuf);
  300. if (info->fota_url) {
  301. aos_free(info->fota_url);
  302. info->fota_url = NULL;
  303. }
  304. info->fota_url = strdup(url->valuestring);
  305. LOGD(TAG, "get url: %s", info->fota_url);
  306. out:
  307. if (buffer) aos_free(buffer);
  308. if (payload) aos_free(payload);
  309. if (js) cJSON_Delete(js);
  310. _http_cleanup(client);
  311. return ret;
  312. }
  313. #else
  314. #include "../http/http.h"
  315. #include "util/network.h"
  316. char *json_getvalue(char *body, char *key, int *len)
  317. {
  318. char key_buf[16];
  319. char *start_ptr, *end_ptr;
  320. snprintf(key_buf, 16, "\"%s\":", key);
  321. start_ptr = strstr(body, key_buf);
  322. if (start_ptr == NULL || body == start_ptr) {
  323. LOGD(TAG, "key");
  324. return NULL;
  325. }
  326. start_ptr += strlen(key_buf);
  327. while ((*start_ptr == ' ') || (*start_ptr == '\"') || (*start_ptr == 0)) {
  328. start_ptr++;
  329. }
  330. end_ptr = start_ptr;
  331. while (((*end_ptr != ',') && (*end_ptr != '\"')) && (*end_ptr != 0)) {
  332. end_ptr++;
  333. }
  334. if (start_ptr == end_ptr) {
  335. return NULL;
  336. }
  337. if (*end_ptr == '\"') {
  338. end_ptr --;
  339. }
  340. *len = end_ptr - start_ptr + 1;
  341. return start_ptr;
  342. }
  343. static int cop_get_ota_url(char *ota_url, int len)
  344. {
  345. int ret = -1;
  346. ret = aos_kv_getstring("otaurl", ota_url, len);
  347. if (ret < 0) {
  348. strcpy(ota_url, "http://occ.t-head.cn/api/image/ota/pull");
  349. }
  350. return ret;
  351. }
  352. static int cop_version_check(fota_info_t *info) {
  353. http_t *http;
  354. char *payload;
  355. char *body;
  356. int value_len;
  357. char *cptr;
  358. char getvalue[64];
  359. int rc;
  360. if ((payload = aos_malloc(156)) == NULL) {
  361. return -1;
  362. }
  363. snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
  364. aos_get_device_id(), aos_get_product_model(), aos_get_app_version());
  365. //snprintf(payload, 100, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
  366. // "00A2C6FB32241D9423F5AF00", "hlb_test", "1.0.0-20181125.1321-R-hlb_tes");
  367. LOGD(TAG, "check: %s", payload);
  368. memset(getvalue, 0, sizeof(getvalue));
  369. cop_get_ota_url(getvalue, sizeof(getvalue));
  370. if ((http = http_init(getvalue)) == NULL) {
  371. LOGD(TAG, "e cop http init");
  372. aos_free(payload);
  373. return -1;
  374. }
  375. memset(http->buffer, 0, BUFFER_SIZE);
  376. http->buffer_offset = 0;
  377. http_head_sets(http, "Host", http->host);
  378. http_head_seti(http, "Content-Length", strlen(payload));
  379. http_head_sets(http, "Connection", "keep-alive");
  380. http_head_sets(http, "Content-Type", "application/json");
  381. http_head_sets(http, "Cache-Control", "no-cache");
  382. http_post(http, payload, 10000);
  383. if (http_wait_resp(http, &body, 10000) < 0) {
  384. http_deinit(http);
  385. aos_free(payload);
  386. LOGD(TAG, "recv failed");
  387. return -1;
  388. }
  389. aos_free(payload);
  390. LOGD(TAG, "resp body: %s", body);
  391. cptr = json_getvalue(body, "code", &value_len);
  392. if ((cptr == NULL) || (atoi(cptr) < 0)) {
  393. LOGW(TAG, "rsp format");
  394. http_deinit(http);
  395. return -1;
  396. }
  397. cptr = json_getvalue(body, "version", &value_len);
  398. if (cptr == NULL) {
  399. LOGD(TAG, "rsp format %s", "version");
  400. http_deinit(http);
  401. return -1;
  402. }
  403. char *ver = aos_malloc(value_len + 1);
  404. if (!ver) {
  405. return -1;
  406. }
  407. memcpy(ver, cptr, value_len);
  408. ver[value_len] = 0;
  409. LOGD(TAG, "%s: %s", COP_VERSION, ver);
  410. aos_kv_setstring(COP_VERSION, ver);
  411. aos_free(ver);
  412. cptr = json_getvalue(body, "url", &value_len);
  413. if (cptr == NULL) {
  414. LOGD(TAG, "rsp format %s", "url");
  415. http_deinit(http);
  416. return -1;
  417. }
  418. if (http->url) {
  419. aos_free(http->url);
  420. http->url = NULL;
  421. }
  422. http->url = calloc(value_len + 1, 1);
  423. if (http->url == NULL) {
  424. LOGD(TAG, "realloc failed");
  425. http_deinit(http);
  426. return -1;
  427. }
  428. if (strstr(cptr, "https://")) {
  429. snprintf(http->url, value_len, "http://%s", cptr + strlen("https://"));
  430. } else {
  431. memcpy(http->url, cptr, value_len);
  432. }
  433. char *buffer = aos_malloc(156);
  434. if (buffer == NULL) {
  435. http_deinit(http);
  436. return -1;
  437. }
  438. rc = aos_kv_getstring(COP_IMG_URL, buffer, 156);
  439. if (rc <= 0) {
  440. aos_kv_setstring(COP_IMG_URL, http->url);
  441. } else {
  442. if (strcmp(http->url, buffer) == 0) {
  443. aos_kv_getint("fota_offset", &rc);
  444. LOGI(TAG, "continue fota :%d", rc);
  445. } else {
  446. aos_kv_setstring(COP_IMG_URL, http->url);
  447. aos_kv_setint("fota_offset", 0);
  448. LOGI(TAG, "restart fota");
  449. }
  450. }
  451. aos_free(buffer);
  452. if (info->fota_url) {
  453. aos_free(info->fota_url);
  454. info->fota_url = NULL;
  455. }
  456. if (info->changelog) {
  457. aos_free(info->changelog);
  458. info->changelog = NULL;
  459. }
  460. if (info->new_version) {
  461. aos_free(info->new_version);
  462. info->new_version = NULL;
  463. }
  464. info->fota_url = strdup(http->url);
  465. LOGD(TAG, "get url: %s", info->fota_url);
  466. http_deinit(http);
  467. return 0;
  468. }
  469. #endif /* CONFIG_FOTA_USE_HTTPC */
  470. static int finish(fota_info_t *info)
  471. {
  472. cop_res_release(info);
  473. return 0;
  474. }
  475. static int fail(fota_info_t *info)
  476. {
  477. cop_res_release(info);
  478. return 0;
  479. }
  480. const fota_cls_t fota_cop_cls = {
  481. "cop",
  482. NULL,
  483. cop_version_check,
  484. finish,
  485. fail,
  486. NULL
  487. };
  488. int fota_register_cop(void)
  489. {
  490. return fota_register(&fota_cop_cls);
  491. }