diff --git a/CMakeLists.txt b/CMakeLists.txt index f72115a..d9204c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,10 @@ set(PLATFORM "linux") #set(COMPILE_TOOLS "MSVC") #set(PLATFORM "windows") + +# 是否使能设备动态注册 +set(FEATURE_DEV_DYN_REG_ENABLED ON) + # 是否使能网关功能 set(FEATURE_GATEWAY_ENABLED OFF) @@ -28,7 +32,7 @@ set(FEATURE_MULTITHREAD_ENABLED OFF) set(FEATURE_EVENT_POST_ENABLED OFF) # 是否打开数据模板行为功能 -set(FEATURE_ACTION_ENABLED ON) +set(FEATURE_ACTION_ENABLED OFF) # 是否打开OTA固件升级总开关 set(FEATURE_OTA_COMM_ENABLED ON) @@ -45,6 +49,7 @@ set(FEATURE_DEBUG_DEV_INFO_USED OFF) # 是否使用HTTPS下载固件 set(FEATURE_OTA_USE_HTTPS ON) + # 使用SDK AT组件实现通用TCP模组网络读写需要配置 --->begin # 是否打开AT模组TCP功能 set(FEATURE_AT_TCP_ENABLED OFF) @@ -115,6 +120,7 @@ if(${FEATURE_OTA_COMM_ENABLED} STREQUAL "ON") option(OTA_MQTT_CHANNEL "Enable OTA_MQTT_CHANNEL" ON) endif() +option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG" ${FEATURE_DEV_DYN_REG_ENABLED}) option(GATEWAY_ENABLED "Enable GATEWAY" ${FEATURE_GATEWAY_ENABLED}) option(AUTH_WITH_NOTLS "Enable AUTH_WITH_NOTLS" ${FEATURE_AUTH_WITH_NOTLS}) option(EVENT_POST_ENABLED "Enable EVENT_POST" ${FEATURE_EVENT_POST_ENABLED}) diff --git a/Makefile b/Makefile index d9db38b..f93288c 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,10 @@ $(call CompLib_Map, SYSTEM_COMM_ENABLED, \ $(SRC_DIR)/services/system \ ) +$(call CompLib_Map, DEV_DYN_REG_ENABLED, \ + $(SRC_DIR)/services/dynreg \ +) + $(call CompLib_Map, OTA_COMM_ENABLED, \ $(SRC_DIR)/services/ota \ ) diff --git a/include/config.h b/include/config.h index 3731897..2a17924 100644 --- a/include/config.h +++ b/include/config.h @@ -1,13 +1,13 @@ /* #undef AUTH_MODE_CERT */ #define AUTH_MODE_KEY /* #undef AUTH_WITH_NOTLS */ -#define GATEWAY_ENABLED +/* #undef GATEWAY_ENABLED */ /* #undef COAP_COMM_ENABLED */ #define OTA_MQTT_CHANNEL /* #undef SYSTEM_COMM */ /* #undef EVENT_POST_ENABLED */ -/* #undef ACTION_ENABLED*/ -/* #undef DEV_DYN_REG_ENABLED */ +/* #undef ACTION_ENABLED */ +#define DEV_DYN_REG_ENABLED /* #undef LOG_UPLOAD */ /* #undef IOT_DEBUG */ /* #undef DEBUG_DEV_INFO_USED */ @@ -15,6 +15,6 @@ /* #undef AT_UART_RECV_IRQ */ /* #undef AT_OS_USED */ /* #undef AT_DEBUG */ -/* #undef OTA_USE_HTTPS */ -/* #undef GATEWAY_ENABLED*/ +#define OTA_USE_HTTPS +/* #undef GATEWAY_ENABLED */ /* #undef MULTITHREAD_ENABLED */ diff --git a/include/exports/qcloud_iot_export_dynreg.h b/include/exports/qcloud_iot_export_dynreg.h new file mode 100644 index 0000000..843dc96 --- /dev/null +++ b/include/exports/qcloud_iot_export_dynreg.h @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making IoT Hub available. + * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. + + * Licensed under the MIT License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef QLCOUD_IOT_EXPORT_DYNREG_H_ +#define QLCOUD_IOT_EXPORT_DYNREG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "qcloud_iot_export.h" + +/** + * @brief Do dynamic register/create device + * + * @param pDevInfo In: device info with [ProductId, ProductKey, DeviceName] + * Out: device info with [ProductId, DeviceName, DeviceSecret or Device cert/key file] + * + * @return QCLOUD_RET_SUCCESS for success, or err code for failure + */ +int IOT_DynReg_Device(DeviceInfo *pDevInfo); + + +#ifdef __cplusplus +} +#endif + +#endif //QLCOUD_IOT_EXPORT_DYNREG_H_ \ No newline at end of file diff --git a/include/exports/qcloud_iot_export_ota.h b/include/exports/qcloud_iot_export_ota.h index 17778ac..920708b 100644 --- a/include/exports/qcloud_iot_export_ota.h +++ b/include/exports/qcloud_iot_export_ota.h @@ -148,6 +148,15 @@ int IOT_OTA_StartDownload(void *handle, uint32_t offset, uint32_t size); */ void IOT_OTA_UpdateClientMd5(void *handle, char * buff, uint32_t size); + +/** + * @brief Reset MD5 of local firmware + * + * @param handle: OTA module handle + * + */ +int IOT_OTA_ResetClientMD5(void *handle); + /** * @brief Report local firmware version to server * NOTE: do this report before real download diff --git a/include/qcloud_iot_export.h b/include/qcloud_iot_export.h index c6c4768..d26d4c0 100644 --- a/include/qcloud_iot_export.h +++ b/include/qcloud_iot_export.h @@ -54,6 +54,12 @@ extern "C" { #define MAX_NUM_SUB_DEV (50) /**************** QCloud IoT C-SDK constants end *************************/ +typedef enum{ + eCOMMON_DEV = 0, //common dev + eGW_DEV = 1, //Gateway dev + eGW_SUB_DEV = 2, //sub dev of Gateway + eDEFAULT_DEV +}eDevType; typedef struct { char product_id[MAX_SIZE_OF_PRODUCT_ID + 1]; @@ -70,6 +76,7 @@ typedef struct { #ifdef DEV_DYN_REG_ENABLED char product_secret[MAX_SIZE_OF_PRODUCT_SECRET + 1]; #endif + eDevType dev_type; } DeviceInfo; #ifdef GATEWAY_ENABLED @@ -88,6 +95,8 @@ typedef struct { #include "qcloud_iot_export_data_template.h" #include "qcloud_iot_export_ota.h" #include "qcloud_iot_export_gateway.h" +#include "qcloud_iot_export_dynreg.h" + diff --git a/make.settings b/make.settings index f61e3de..40b9009 100644 --- a/make.settings +++ b/make.settings @@ -25,6 +25,9 @@ FEATURE_GATEWAY_ENABLED = n # 是否使能多线程 FEATURE_MULTITHREAD_ENABLED = n +# 是否使能设备动态注册 +FEATURE_DEV_DYN_REG_ENABLED = y + # 是否打开OTA固件升级总开关 FEATURE_OTA_COMM_ENABLED = y diff --git a/platform/os/linux/HAL_Device_linux.c b/platform/os/linux/HAL_Device_linux.c index b91482f..df690e5 100644 --- a/platform/os/linux/HAL_Device_linux.c +++ b/platform/os/linux/HAL_Device_linux.c @@ -510,6 +510,12 @@ static int iot_save_devinfo_to_json_file(DeviceInfo *pDevInfo) Log_d("JsonDoc(%d):%s", MAX_DEV_INFO_FILE_LEN - remain_size, JsonDoc); + if(eGW_SUB_DEV == pDevInfo->dev_type) { + memset(sg_device_info_file, '\0', MAX_SIZE_OF_DEVICE_INFO_FILE); + HAL_Snprintf(sg_device_info_file, MAX_SIZE_OF_DEVICE_INFO_FILE, "./%s_%s_device_info.json", \ + pDevInfo->product_id, pDevInfo->device_name); + } + fp = fopen(sg_device_info_file, "w"); if (NULL == fp) { Log_e("open file %s failed", sg_device_info_file); diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 1d322e3..5e99289 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -61,4 +61,9 @@ if(${FEATURE_OTA_COMM_ENABLED} STREQUAL "ON") target_link_libraries(ota_mqtt_sample ${lib}) endif() - +# DYN_REG +if(${FEATURE_DEV_DYN_REG_ENABLED} STREQUAL "ON") + file(GLOB src_dynreg_dev_sample ${PROJECT_SOURCE_DIR}/samples/dynreg_dev/dynreg_dev_sample.c) + add_executable(dynreg_dev_sample ${src_dynreg_dev_sample}) + target_link_libraries(dynreg_dev_sample ${lib}) +endif() diff --git a/samples/Makefile b/samples/Makefile index e0c6f4f..9ba4210 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -39,9 +39,9 @@ ifeq ($(FEATURE_AUTH_MODE),CERT) CFLAGS += -DAUTH_MODE_CERT endif -.PHONY: ota_mqtt_sample data_template_sample gateway_sample mqtt_sample +.PHONY: ota_mqtt_sample data_template_sample gateway_sample mqtt_sample dynreg_dev_sample -all: ota_mqtt_sample data_template_sample gateway_sample mqtt_sample +all: ota_mqtt_sample data_template_sample gateway_sample mqtt_sample dynreg_dev_sample ifneq (,$(filter -DOTA_COMM_ENABLED,$(CFLAGS))) @@ -88,6 +88,15 @@ gateway_sample: mv $@ $(FINAL_DIR)/bin endif +ifneq (,$(filter -DDEV_DYN_REG_ENABLED,$(CFLAGS))) +dynreg_dev_sample: + $(TOP_Q) \ + $(PLATFORM_CC) $(CFLAGS) $(SAMPLE_DIR)/dynreg_dev/$@.c $(LDFLAGS) -o $@ + + $(TOP_Q) \ + mv $@ $(FINAL_DIR)/bin +endif + clean: rm -rf $(FINAL_DIR)/bin/* diff --git a/samples/dynreg_dev/dynreg_dev_sample.c b/samples/dynreg_dev/dynreg_dev_sample.c new file mode 100644 index 0000000..5dfcba3 --- /dev/null +++ b/samples/dynreg_dev/dynreg_dev_sample.c @@ -0,0 +1,112 @@ +/* + * Tencent is pleased to support the open source community by making IoT Hub available. + * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. + + * Licensed under the MIT License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "utils_getopt.h" +#include "qcloud_iot_export.h" +#include "qcloud_iot_import.h" + +#ifdef AUTH_MODE_CERT +/* NULL cert file */ +#define QCLOUD_IOT_NULL_CERT_FILENAME "YOUR_DEVICE_CERT_FILE_NAME" +/* NULL key file */ +#define QCLOUD_IOT_NULL_KEY_FILENAME "YOUR_DEVICE_PRIVATE_KEY_FILE_NAME" +#else +/* NULL device secret */ +#define QCLOUD_IOT_NULL_DEVICE_SECRET "YOUR_IOT_PSK" +#endif + + +int main(int argc, char **argv) +{ + int c; + while ((c = utils_getopt(argc, argv, "c:")) != EOF) + switch (c) { + case 'c': + if (HAL_SetDevInfoFile(utils_optarg)) + return -1; + break; + + default: + HAL_Printf("usage: %s [options]\n" + " [-c ] \n" + , argv[0]); + + return -1; + } + + + //init log level + IOT_Log_Set_Level(eLOG_DEBUG); + + int ret; + DeviceInfo sDevInfo; + bool infoNullFlag = false; + + memset((char *)&sDevInfo, 0, sizeof(DeviceInfo)); + + ret = HAL_GetDevInfo(&sDevInfo); + +#ifndef GATEWAY_ENABLED + sDevInfo.dev_type = eCOMMON_DEV; +#else + sDevInfo.dev_type = eGW_SUB_DEV; +#endif + +#ifdef AUTH_MODE_CERT + /* just demo the cert/key files are empty */ + if (!strcmp(sDevInfo.dev_cert_file_name, QCLOUD_IOT_NULL_CERT_FILENAME) + || !strcmp(sDevInfo.dev_key_file_name, QCLOUD_IOT_NULL_KEY_FILENAME)) { + Log_d("dev Cert not exist!"); + infoNullFlag = true; + } else { + Log_d("dev Cert exist"); + } +#else + /* just demo the PSK is empty */ + if (!strcmp(sDevInfo.device_secret, QCLOUD_IOT_NULL_DEVICE_SECRET)) { + Log_d("dev psk not exist!"); + infoNullFlag = true; + } else { + Log_d("dev psk exist"); + } +#endif + + /* device cert/key files or PSK is empty, do dynamic register to fetch */ + if (infoNullFlag) { + if (QCLOUD_RET_SUCCESS == IOT_DynReg_Device(&sDevInfo)) { + ret = HAL_SetDevInfo(&sDevInfo); + if (QCLOUD_RET_SUCCESS != ret) { + Log_e("devices info save fail"); + } else { +#ifdef AUTH_MODE_CERT + Log_d("dynamic register success, productID: %s, devName: %s, CertFile: %s, KeyFile: %s", \ + sDevInfo.product_id, sDevInfo.device_name, sDevInfo.dev_cert_file_name, sDevInfo.dev_key_file_name); +#else + Log_d("dynamic register success,productID: %s, devName: %s, device_secret: %s", \ + sDevInfo.product_id, sDevInfo.device_name, sDevInfo.device_secret); +#endif + } + } else { + Log_e("%s dynamic register fail", sDevInfo.device_name); + } + } + + return ret; +} diff --git a/samples/ota/ota_mqtt_sample.c b/samples/ota/ota_mqtt_sample.c index c7db1ba..b68aa06 100644 --- a/samples/ota/ota_mqtt_sample.c +++ b/samples/ota/ota_mqtt_sample.c @@ -22,23 +22,44 @@ #include "lite-utils.h" -#ifdef AUTH_MODE_CERT -static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file -static char sg_key_file[PATH_MAX + 1]; // full path of device key file -#endif +#define FW_RUNNING_VERSION "1.0.0" +#define KEY_VER "version" +#define KEY_SIZE "downloaded_size" + +#define FW_VERSION_MAX_LEN 32 +#define FW_FILE_PATH_MAX_LEN 128 +#define OTA_BUF_LEN 5000 +#define FW_INFO_FILE_DATA_LEN 128 -static DeviceInfo sg_devInfo; +typedef struct OTAContextData { + void *ota_handle; + void *mqtt_client; + char fw_file_path[FW_FILE_PATH_MAX_LEN]; + char fw_info_file_path[FW_FILE_PATH_MAX_LEN]; -#define OTA_BUF_LEN (5000) + // remote_version means version for the FW in the cloud and to be downloaded + char remote_version[FW_VERSION_MAX_LEN]; + uint32_t fw_file_size; -static bool sg_pub_ack = false; -static int sg_packet_id = 0; + // for resuming download + /* local_version means downloading but not running */ + char local_version[FW_VERSION_MAX_LEN]; + int downloaded_size; + + // to make sure report is acked + bool report_pub_ack; + int report_packet_id; + +} OTAContextData; + +static DeviceInfo sg_devInfo; -static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg) +static void _event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg) { uintptr_t packet_id = (uintptr_t)msg->msg; + OTAContextData *ota_ctx = (OTAContextData *)handle_context; switch (msg->event_type) { case MQTT_EVENT_UNDEF: @@ -67,8 +88,8 @@ static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg case MQTT_EVENT_PUBLISH_SUCCESS: Log_i("publish success, packet-id=%u", (unsigned int)packet_id); - if (sg_packet_id == packet_id) - sg_pub_ack = true; + if (ota_ctx->report_packet_id == packet_id) + ota_ctx->report_pub_ack = true; break; case MQTT_EVENT_PUBLISH_TIMEOUT: @@ -84,95 +105,128 @@ static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg } } -/* demo of firmware info management in device side for resuming download from break point */ -#define VERSION_FILE_PATH "./local_fw_info.json" -#define KEY_VER "version" -#define KEY_MD5 "md5" -#define KEY_SIZE "downloadSize" -#define KEY_STATE "state" -#define KEY_PREVER "running_version" -static char * get_local_fw_version(char **ver, char **md5, char **size) + +static int _setup_connect_init_params(MQTTInitParams* initParams, void *ota_ctx, DeviceInfo *device_info) { -#define INFO_FILE_MAX_LEN 256 + initParams->product_id = device_info->product_id; + initParams->device_name = device_info->device_name; - FILE *fp; - int len; - int rlen; - char *preVer; - char *reportVer = NULL; +#ifdef AUTH_MODE_CERT + char certs_dir[16] = "certs"; + char current_path[128]; + char *cwd = getcwd(current_path, sizeof(current_path)); - fp = fopen(VERSION_FILE_PATH, "r"); - if (NULL == fp) { - Log_e("open file %s failed", VERSION_FILE_PATH); - goto exit; + if (cwd == NULL) { + Log_e("getcwd return NULL"); + return QCLOUD_ERR_FAILURE; } - fseek(fp, 0L, SEEK_END); - len = ftell(fp); - if (len > INFO_FILE_MAX_LEN) { - Log_e("%s is too big, pls check", VERSION_FILE_PATH); - goto exit; - } +#ifdef WIN32 + HAL_Snprintf(initParams->cert_file, FILE_PATH_MAX_LEN, "%s\\%s\\%s", current_path, certs_dir, device_info->dev_cert_file_name); + HAL_Snprintf(initParams->key_file, FILE_PATH_MAX_LEN, "%s\\%s\\%s", current_path, certs_dir, device_info->dev_key_file_name); +#else + HAL_Snprintf(initParams->cert_file, FILE_PATH_MAX_LEN, "%s/%s/%s", current_path, certs_dir, device_info->dev_cert_file_name); + HAL_Snprintf(initParams->key_file, FILE_PATH_MAX_LEN, "%s/%s/%s", current_path, certs_dir, device_info->dev_key_file_name); +#endif - char *JsonDoc = (char *)HAL_Malloc(len + 10); - if (NULL == JsonDoc) { - Log_e("malloc buffer for json file read fail"); - goto exit; - } +#else + initParams->device_secret = device_info->device_secret; +#endif - rewind(fp); - rlen = fread(JsonDoc, 1, len, fp); + initParams->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT; + initParams->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL; - if (len != rlen) { - Log_e("read data len (%d) less than needed (%d), %s", rlen, len, JsonDoc); - } + initParams->auto_connect_enable = 1; + initParams->event_handle.h_fp = _event_handler; + initParams->event_handle.context = ota_ctx; - *ver = LITE_json_value_of(KEY_VER, JsonDoc); - *md5 = LITE_json_value_of(KEY_MD5, JsonDoc); - *size = LITE_json_value_of(KEY_SIZE, JsonDoc); - preVer = LITE_json_value_of(KEY_PREVER, JsonDoc); + return QCLOUD_RET_SUCCESS; +} - if ((NULL != *ver) && (NULL != preVer) && (0 == strcmp(*ver, preVer))) { - reportVer = *ver; - HAL_Free(preVer); - } else { - reportVer = preVer; +static void _wait_for_pub_ack(OTAContextData *ota_ctx, int packet_id) +{ + int wait_cnt = 10; + ota_ctx->report_pub_ack = false; + ota_ctx->report_packet_id = packet_id; + + while (!ota_ctx->report_pub_ack) { + HAL_SleepMs(500); + IOT_MQTT_Yield(ota_ctx->mqtt_client, 500); + if (wait_cnt-- == 0) { + Log_e("wait report pub ack timeout!"); + break; + } } + ota_ctx->report_pub_ack = false; + return ; +} -exit: +/********************************************************************************** + * OTA file operations START + * these are platform-dependant functions + * POSIX FILE is used in this sample code + **********************************************************************************/ +// calculate left MD5 for resuming download from break point +static int _cal_exist_fw_md5(OTAContextData *ota_ctx) +{ + char buff[OTA_BUF_LEN]; + size_t rlen, total_read = 0; + int ret = QCLOUD_RET_SUCCESS; - if (NULL != fp) { - fclose(fp); + ret = IOT_OTA_ResetClientMD5(ota_ctx->ota_handle); + if (ret) { + Log_e("reset MD5 failed: %d", ret); + return QCLOUD_ERR_FAILURE; + } + + FILE *fp = fopen(ota_ctx->fw_file_path, "ab+"); + if (NULL == fp) { + Log_e("open file %s failed", ota_ctx->fw_file_path); + return QCLOUD_ERR_FAILURE; } - return reportVer; -#undef INFO_FILE_MAX_LEN + //rewind(fp); + size_t size = ota_ctx->downloaded_size; + + while ((size > 0) && (!feof(fp))) { + rlen = (size > OTA_BUF_LEN) ? OTA_BUF_LEN : size; + if (rlen != fread(buff, 1, rlen, fp)) { + Log_e("read data len not expected"); + ret = QCLOUD_ERR_FAILURE; + break; + } + IOT_OTA_UpdateClientMd5(ota_ctx->ota_handle, buff, rlen); + size -= rlen; + total_read += rlen; + } + + fclose(fp); + Log_d("total read: %d", total_read); + return ret; } + /* update local firmware info for resuming download from break point */ -static int update_local_fw_info(const char *version, const char *preVer, const char *md5, uint32_t downloadedSize) +static int _update_local_fw_info(OTAContextData *ota_ctx) { -#define INFO_FILE_MAX_LEN 256 - FILE *fp; int wlen; int ret = QCLOUD_RET_SUCCESS; - char dataBuff[INFO_FILE_MAX_LEN]; + char data_buf[FW_INFO_FILE_DATA_LEN]; - memset(dataBuff, 0, INFO_FILE_MAX_LEN); - HAL_Snprintf(dataBuff, INFO_FILE_MAX_LEN, "{\"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d,\"%s\":\"%s\"}", \ - KEY_VER, version, KEY_MD5, md5, KEY_SIZE, downloadedSize, \ - KEY_PREVER, (NULL == preVer) ? "1.0.0" : preVer); + memset(data_buf, 0, sizeof(data_buf)); + HAL_Snprintf(data_buf, sizeof(data_buf), "{\"%s\":\"%s\", \"%s\":%d}", \ + KEY_VER, ota_ctx->remote_version, KEY_SIZE, ota_ctx->downloaded_size); - fp = fopen(VERSION_FILE_PATH, "w"); + fp = fopen(ota_ctx->fw_info_file_path, "w"); if (NULL == fp) { - Log_e("open file %s failed", VERSION_FILE_PATH); + Log_e("open file %s failed", ota_ctx->fw_info_file_path); ret = QCLOUD_ERR_FAILURE; goto exit; } - wlen = fwrite(dataBuff, 1, strlen(dataBuff), fp); - if (wlen != strlen(dataBuff)) { + wlen = fwrite(data_buf, 1, strlen(data_buf), fp); + if (wlen != strlen(data_buf)) { Log_e("save version to file err"); ret = QCLOUD_ERR_FAILURE; } @@ -184,209 +238,185 @@ static int update_local_fw_info(const char *version, const char *preVer, const c } return ret; -#undef INFO_FILE_MAX_LEN } -/* get local firmware offset for resuming download from break point */ -static int getFwOffset(void *h_ota, char *local_ver, char *local_md5, char *local_size, uint32_t *offset, uint32_t *size_file) -{ - char version[128], md5sum[33]; - uint32_t local_len; - int Ret; - Ret = IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128); - Ret |= IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33); - Ret |= IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, size_file, 4); +static int _get_local_fw_info(char *file_name, char *local_version) +{ + int len; + int rlen; + char json_doc[FW_INFO_FILE_DATA_LEN] = {0}; - local_len = (NULL == local_size) ? 0 : atoi(local_size); + FILE *fp = fopen(file_name, "r"); + if (NULL == fp) { + Log_e("open file %s failed", file_name); + return 0; + } + fseek(fp, 0L, SEEK_END); + len = ftell(fp); + if (len > FW_INFO_FILE_DATA_LEN) { + Log_e("%s is too big, pls check", file_name); + fclose(fp); + return 0; + } - if ((NULL == local_ver) || (NULL == local_md5) || (NULL == local_size)) { - *offset = 0; - } else if ((0 != strcmp(local_ver, version)) || (0 != strcmp(local_md5, md5sum)) || (local_len > *size_file)) { - *offset = 0; - } else { - *offset = local_len; + rewind(fp); + rlen = fread(json_doc, 1, len, fp); + if (len != rlen) { + Log_e("read data len (%d) less than needed (%d), %s", rlen, len, json_doc); + fclose(fp); + return 0; } - return Ret; -} + char *version = LITE_json_value_of(KEY_VER, json_doc); + char *size = LITE_json_value_of(KEY_SIZE, json_doc); -/* calculate left MD5 for resuming download from break point */ -static int cal_exist_fw_md5(void *h_ota, FILE *fp, size_t size) -{ -#define BUFF_LEN 1024 + if((NULL == version)||(NULL == size)) { + if (version) HAL_Free(version); + if (size) HAL_Free(size); + fclose(fp); + return 0; + } - char buff[BUFF_LEN]; - size_t rlen; - int Ret = QCLOUD_RET_SUCCESS; + int local_size = atoi(size); + HAL_Free(size); - while ((size > 0) && (!feof(fp))) { - rlen = (size > BUFF_LEN) ? BUFF_LEN : size; - if (rlen != fread(buff, 1, rlen, fp)) { - Log_e("read data len not expected"); - Ret = QCLOUD_ERR_FAILURE; - break; - } - IOT_OTA_UpdateClientMd5(h_ota, buff, rlen); - size -= rlen; + if (local_size <= 0) { + Log_w("local info offset invalid: %d", local_size); + HAL_Free(version); + local_size = 0; } - return Ret; -#undef BUFF_LEN -} -static int _setup_connect_init_params(MQTTInitParams* initParams) -{ - int ret; + strncpy(local_version, version, FW_VERSION_MAX_LEN); + HAL_Free(version); + fclose(fp); + return local_size; - ret = HAL_GetDevInfo((void *)&sg_devInfo); - if (QCLOUD_RET_SUCCESS != ret) { - return ret; - } +} - initParams->device_name = sg_devInfo.device_name; - initParams->product_id = sg_devInfo.product_id; -#ifdef AUTH_MODE_CERT - char certs_dir[PATH_MAX + 1] = "certs"; - char current_path[PATH_MAX + 1]; - char *cwd = getcwd(current_path, sizeof(current_path)); - if (cwd == NULL) { - Log_e("getcwd return NULL"); - return QCLOUD_ERR_FAILURE; +/* get local firmware offset for resuming download from break point */ +static int _update_fw_downloaded_size(OTAContextData *ota_ctx) +{ + int local_size = _get_local_fw_info(ota_ctx->fw_info_file_path, ota_ctx->local_version); + if (local_size == 0) { + ota_ctx->downloaded_size = 0; + return 0; } - sprintf(sg_cert_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_cert_file_name); - sprintf(sg_key_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_key_file_name); - - initParams->cert_file = sg_cert_file; - initParams->key_file = sg_key_file; -#else - initParams->device_secret = sg_devInfo.device_secret; -#endif - - initParams->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT; - initParams->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL; - initParams->auto_connect_enable = 1; - initParams->event_handle.h_fp = event_handler; - - return QCLOUD_RET_SUCCESS; + if((0 != strcmp(ota_ctx->local_version, ota_ctx->remote_version)) + ||(ota_ctx->downloaded_size > ota_ctx->fw_file_size)) { + ota_ctx->downloaded_size = 0; + return 0; + } + + ota_ctx->downloaded_size = local_size; + Log_i("calc MD5 for resuming download from offset: %d", ota_ctx->downloaded_size); + int ret = _cal_exist_fw_md5(ota_ctx); + if (ret) { + Log_e("regen OTA MD5 error: %d", ret); + remove(ota_ctx->fw_info_file_path); + ota_ctx->downloaded_size = 0; + return 0; + } + Log_d("local MD5 update done!"); + return local_size; } -int main(int argc, char **argv) +static int _delete_fw_info_file(char *file_name) { - IOT_Log_Set_Level(eLOG_DEBUG); - int rc; - uint32_t firmware_valid; - char version[128], md5sum[33]; - uint32_t len, size_downloaded, size_file; - - int ota_over = 0; - bool upgrade_fetch_success = true; - char buf_ota[OTA_BUF_LEN]; - FILE *fp = NULL; - uint32_t offset = 0; - - - MQTTInitParams init_params = DEFAULT_MQTTINIT_PARAMS; - rc = _setup_connect_init_params(&init_params); - if (rc != QCLOUD_RET_SUCCESS) { - Log_e("init params err,rc=%d", rc); - return rc; - } + return remove(file_name); +} - void *client = IOT_MQTT_Construct(&init_params); - if (client != NULL) { - Log_i("Cloud Device Construct Success"); - } else { - Log_e("Cloud Device Construct Failed"); +static int _save_fw_data_to_file(char *file_name, uint32_t offset, char *buf, int len) +{ + FILE *fp; + if(offset > 0) { + if (NULL == (fp = fopen(file_name, "ab+"))) { + Log_e("open file failed"); + return QCLOUD_ERR_FAILURE; + } + } else { + if (NULL == (fp = fopen(file_name, "wb+"))) { + Log_e("open file failed"); + return QCLOUD_ERR_FAILURE; + } + } + + fseek(fp, offset, SEEK_SET); + + if (1 != fwrite(buf, len, 1, fp)) { + Log_e("write data to file failed"); + fclose(fp); return QCLOUD_ERR_FAILURE; } + fflush(fp); + fclose(fp); - void *h_ota = IOT_OTA_Init(sg_devInfo.product_id, sg_devInfo.device_name, client); - if (NULL == h_ota) { - Log_e("initialize OTA failed"); - return QCLOUD_ERR_FAILURE; - } + return 0; +} - IOT_MQTT_Yield(client, 1000); //make sure subscribe success +static char * _get_local_fw_running_version() +{ + // asuming the version is inside the code and binary + // you can also get from a meta file + Log_i("FW running version: %s", FW_RUNNING_VERSION); + return FW_RUNNING_VERSION; +} +/********************************************************************************** + * OTA file operations END + **********************************************************************************/ - char *local_ver = NULL, *local_md5 = NULL, *local_size = NULL, *reportVer = NULL; - reportVer = get_local_fw_version(&local_ver, &local_md5, &local_size); - Log_d("local_ver:%s local_md5:%s, local_size:%s", local_ver, local_md5, local_size); +// main OTA cycle +bool process_ota(OTAContextData *ota_ctx) +{ + bool download_finished = false; + bool upgrade_fetch_success = true; + char buf_ota[OTA_BUF_LEN]; + int rc; + void *h_ota = ota_ctx->ota_handle; /* Must report version first */ - if (0 > IOT_OTA_ReportVersion(h_ota, (NULL == reportVer) ? "1.0.0" : reportVer)) { + if (0 > IOT_OTA_ReportVersion(h_ota, _get_local_fw_running_version())) { Log_e("report OTA version failed"); - goto exit; + return false; } - do { - Log_i("wait for ota upgrade command..."); + do { - IOT_MQTT_Yield(client, 200); + IOT_MQTT_Yield(ota_ctx->mqtt_client, 200); + + Log_i("wait for ota upgrade command..."); + // recv the upgrade cmd if (IOT_OTA_IsFetching(h_ota)) { - /*check pre-download finished or not*/ - /*if version & MD5 is the same,then pre-download not finished,get breakpoint continue download.otherwise the version is new*/ - rc = getFwOffset(h_ota, local_ver, local_md5, local_size, &offset, &size_file); - if (QCLOUD_RET_SUCCESS != rc) { - Log_e("get fw offset err,rc:%d", rc); - upgrade_fetch_success = false; - break; - } - - //finish download but not report state - if (offset == size_file) { - Log_d("download success last time without report!"); - upgrade_fetch_success = true; + IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, &ota_ctx->fw_file_size, 4); + IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, ota_ctx->remote_version, FW_VERSION_MAX_LEN); + + DeviceInfo *device_info =&sg_devInfo; + HAL_Snprintf(ota_ctx->fw_file_path, FW_FILE_PATH_MAX_LEN, "./FW_%s_%s.bin", device_info->client_id, ota_ctx->remote_version); + HAL_Snprintf(ota_ctx->fw_info_file_path, FW_FILE_PATH_MAX_LEN, "./FW_%s.json", device_info->client_id); - /* get fw information */ - IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33); - IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128); - size_downloaded = size_file; - break; - } + /* check if pre-downloading finished or not */ + /* if local FW downloaded size (ota_ctx->downloaded_size) is not zero, it will do resuming download */ + _update_fw_downloaded_size(ota_ctx); - /*start http connect*/ - rc = IOT_OTA_StartDownload(h_ota, offset, size_file); + /*set offset and start http connect*/ + rc = IOT_OTA_StartDownload(h_ota, ota_ctx->downloaded_size, ota_ctx->fw_file_size); if (QCLOUD_RET_SUCCESS != rc) { Log_e("OTA download start err,rc:%d", rc); upgrade_fetch_success = false; break; } - /*cal file md5*/ - //Log_d("Get offset:%d(%x)", offset, offset); - if (offset > 0) { - if (NULL == (fp = fopen("ota.bin", "ab+"))) { - Log_e("open file failed"); - upgrade_fetch_success = false; - break; - } - - if (QCLOUD_RET_SUCCESS != cal_exist_fw_md5(h_ota, fp, offset)) { - Log_e("cal exist fw md5 failed"); - upgrade_fetch_success = false; - break; - } - - /*set offset*/ - fseek(fp, offset, SEEK_SET); - } else { - if (NULL == (fp = fopen("ota.bin", "wb+"))) { - Log_e("open file failed"); - upgrade_fetch_success = false; - break; - } - } - - + // download and save the fw do { - len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1); + int len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1); if (len > 0) { - if (1 != fwrite(buf_ota, len, 1, fp)) { + rc = _save_fw_data_to_file(ota_ctx->fw_file_path, ota_ctx->downloaded_size, buf_ota, len); + if (rc) { Log_e("write data to file failed"); upgrade_fetch_success = false; break; @@ -396,127 +426,139 @@ int main(int argc, char **argv) upgrade_fetch_success = false; break; } - fflush(fp); /* get OTA information and update local info */ - IOT_OTA_Ioctl(h_ota, IOT_OTAG_FETCHED_SIZE, &size_downloaded, 4); - IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, &size_file, 4); - IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33); - IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128); - rc = update_local_fw_info(version, reportVer, md5sum, size_downloaded); + IOT_OTA_Ioctl(h_ota, IOT_OTAG_FETCHED_SIZE, &ota_ctx->downloaded_size, 4); + rc = _update_local_fw_info(ota_ctx); if (QCLOUD_RET_SUCCESS != rc) { Log_e("update local fw info err,rc:%d", rc); } - IOT_MQTT_Yield(client, 100); + // quit ota process as something wrong with mqtt + rc = IOT_MQTT_Yield(ota_ctx->mqtt_client, 100); + if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) { + Log_e("MQTT error: %d", rc); + return false; + } } while (!IOT_OTA_IsFetchFinish(h_ota)); - fclose(fp); - fp = NULL; - /* Must check MD5 match or not */ if (upgrade_fetch_success) { + // download is finished, delete the fw info file + _delete_fw_info_file(ota_ctx->fw_info_file_path); + + uint32_t firmware_valid; IOT_OTA_Ioctl(h_ota, IOT_OTAG_CHECK_FIRMWARE, &firmware_valid, 4); if (0 == firmware_valid) { Log_e("The firmware is invalid"); - upgrade_fetch_success = false; - rc = update_local_fw_info(NULL, reportVer, NULL, 0); - if (QCLOUD_RET_SUCCESS != rc) { - Log_e("update local fw info err,rc:%d", rc); - } + upgrade_fetch_success = false; } else { Log_i("The firmware is valid"); upgrade_fetch_success = true; } } - ota_over = 1; + download_finished = true; } - HAL_SleepMs(2000); - } while (!ota_over); - - - if (upgrade_fetch_success) { - /* begin execute OTA files, should report upgrade begin */ - sg_packet_id = IOT_OTA_ReportUpgradeBegin(h_ota); - if (0 > sg_packet_id) { - Log_e("report OTA begin failed error:%d", sg_packet_id); - return QCLOUD_ERR_FAILURE; - } - while (!sg_pub_ack) { + if (!download_finished) HAL_SleepMs(1000); - IOT_MQTT_Yield(client, 200); - } - sg_pub_ack = false; - - //* add your own upgrade logic here*// - //* fw_upgrade..... - - if (QCLOUD_RET_SUCCESS == rc) { - /* if upgrade success */ - /* after execute OTA files, should report upgrade result */ - sg_packet_id = IOT_OTA_ReportUpgradeSuccess(h_ota, NULL); - if (0 > sg_packet_id) { - Log_e("report OTA result failed error:%d", sg_packet_id); - return QCLOUD_ERR_FAILURE; - } - while (!sg_pub_ack) { - HAL_SleepMs(1000); - IOT_MQTT_Yield(client, 200); - } - rc = update_local_fw_info(version, version, md5sum, size_downloaded); // just for example, add your own logic - sg_pub_ack = false; - - } else { - /* if upgrade fail */ - sg_packet_id = IOT_OTA_ReportUpgradeFail(h_ota, NULL); - if (0 > sg_packet_id) { - Log_e("report OTA result failed error:%d", sg_packet_id); - return QCLOUD_ERR_FAILURE; - } - while (!sg_pub_ack) { - HAL_SleepMs(1000); - IOT_MQTT_Yield(client, 200); - } - rc = update_local_fw_info(NULL, reportVer, NULL, 0); // just for example, add your own logic - sg_pub_ack = false; + + } while (!download_finished); + + //do some post-download stuff for your need + + // report result + int packet_id; + if (upgrade_fetch_success) + packet_id = IOT_OTA_ReportUpgradeSuccess(h_ota, NULL); + else + packet_id = IOT_OTA_ReportUpgradeFail(h_ota, NULL); + _wait_for_pub_ack(ota_ctx, packet_id); + + return upgrade_fetch_success; +} - } - } -exit: - - if (NULL != fp) { - fclose(fp); - fp = NULL; +int main(int argc, char **argv) +{ + int rc; + OTAContextData *ota_ctx = NULL; + void *mqtt_client = NULL; + void *h_ota = NULL; + + IOT_Log_Set_Level(eLOG_DEBUG); + ota_ctx = (OTAContextData *)HAL_Malloc(sizeof(OTAContextData)); + if (ota_ctx == NULL) { + Log_e("malloc failed"); + goto exit; } + memset(ota_ctx, 0, sizeof(OTAContextData)); - if (NULL != local_ver) { - reportVer = (reportVer == local_ver) ? NULL : reportVer; - HAL_Free(local_ver); - local_ver = NULL; + rc = HAL_GetDevInfo(&sg_devInfo); + if (QCLOUD_RET_SUCCESS != rc) { + Log_e("get device info failed: %d", rc); + goto exit; } - - if (NULL != local_md5) { - HAL_Free(local_md5); - local_md5 = NULL; + + // setup MQTT init params + MQTTInitParams init_params = DEFAULT_MQTTINIT_PARAMS; + rc = _setup_connect_init_params(&init_params, ota_ctx, &sg_devInfo); + if (rc != QCLOUD_RET_SUCCESS) { + Log_e("init params err,rc=%d", rc); + return rc; } - if (NULL != local_size) { - HAL_Free(local_size); - local_size = NULL; + // create MQTT mqtt_client and connect to server + mqtt_client = IOT_MQTT_Construct(&init_params); + if (mqtt_client != NULL) { + Log_i("Cloud Device Construct Success"); + } else { + Log_e("Cloud Device Construct Failed"); + return QCLOUD_ERR_FAILURE; } - if (NULL != reportVer) { - HAL_Free(reportVer); - reportVer = NULL; + // init OTA handle + h_ota = IOT_OTA_Init(sg_devInfo.product_id, sg_devInfo.device_name, mqtt_client); + if (NULL == h_ota) { + Log_e("initialize OTA failed"); + goto exit; } - IOT_OTA_Destroy(h_ota); + ota_ctx->ota_handle = h_ota; + ota_ctx->mqtt_client = mqtt_client; - IOT_MQTT_Destroy(&client); + bool ota_success; + do { + // mqtt should be ready first + rc = IOT_MQTT_Yield(mqtt_client, 500); + if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) { + HAL_SleepMs(1000); + continue; + } else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) { + Log_e("exit with error: %d", rc); + break; + } + + // OTA process + ota_success = process_ota(ota_ctx); + if (!ota_success) { + Log_e("OTA failed! Do it again"); + HAL_SleepMs(2000); + } + } while (!ota_success); + +exit: + + if (NULL != ota_ctx) + HAL_Free(ota_ctx); + + if (NULL != h_ota) + IOT_OTA_Destroy(h_ota); + + IOT_MQTT_Destroy(&mqtt_client); return 0; } + diff --git a/sdk_src/CMakeLists.txt b/sdk_src/CMakeLists.txt index 5561b58..19b394d 100644 --- a/sdk_src/CMakeLists.txt +++ b/sdk_src/CMakeLists.txt @@ -25,9 +25,8 @@ if(${FEATURE_OTA_COMM_ENABLED} STREQUAL "ON") ${CMAKE_CURRENT_SOURCE_DIR}/services/ota/ota_mqtt.c) list(APPEND src_sdk ${src_mqtt_ota}) endif() - - + # HTTP if(${FEATURE_OTA_COMM_ENABLED} STREQUAL "ON") # http @@ -35,6 +34,12 @@ if(${FEATURE_OTA_COMM_ENABLED} STREQUAL "ON") list(APPEND src_sdk ${src_http}) endif() +# DYN REG +if(${FEATURE_DEV_DYN_REG_ENABLED} STREQUAL "ON") + file(GLOB src_dyn_reg ${CMAKE_CURRENT_SOURCE_DIR}/services/dynreg/*.c) + list(APPEND src_sdk ${src_dyn_reg}) +endif() + # AT OR TCP if(${FEATURE_AT_TCP_ENABLED} STREQUAL "ON") # at diff --git a/sdk_src/protocol/mqtt/mqtt_client.c b/sdk_src/protocol/mqtt/mqtt_client.c index 723917b..14b1ec8 100644 --- a/sdk_src/protocol/mqtt/mqtt_client.c +++ b/sdk_src/protocol/mqtt/mqtt_client.c @@ -152,7 +152,11 @@ int IOT_MQTT_Destroy(void **pClient) Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)(*pClient); - int rc = qcloud_iot_mqtt_disconnect(mqtt_client); + int rc = qcloud_iot_mqtt_disconnect(mqtt_client); + if (rc != QCLOUD_RET_SUCCESS) { + mqtt_client->network_stack.disconnect(&(mqtt_client->network_stack)); + set_client_conn_state(mqtt_client, NOTCONNECTED); + } int i = 0; for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) { diff --git a/sdk_src/services/data_template/data_template_client.c b/sdk_src/services/data_template/data_template_client.c index 4eb8639..a1d132e 100644 --- a/sdk_src/services/data_template/data_template_client.c +++ b/sdk_src/services/data_template/data_template_client.c @@ -734,6 +734,7 @@ void* IOT_Template_Construct(TemplateInitParams *pParams, void *pMqttClient) Qcloud_IoT_Template *template_client = NULL; if ((template_client = (Qcloud_IoT_Template *)HAL_Malloc(sizeof(Qcloud_IoT_Template))) == NULL) { Log_e("memory not enough to malloc TemplateClient"); + return NULL; } MQTTInitParams mqtt_init_params; @@ -767,6 +768,7 @@ void* IOT_Template_Construct(TemplateInitParams *pParams, void *pMqttClient) rc = qcloud_iot_template_init(template_client); if (rc != QCLOUD_RET_SUCCESS) { IOT_MQTT_Destroy(&(template_client->mqtt)); + IOT_Template_Destroy(template_client); HAL_Free(template_client); goto End; } @@ -795,7 +797,7 @@ void* IOT_Template_Construct(TemplateInitParams *pParams, void *pMqttClient) rc = IOT_Event_Init(template_client); if (rc < 0) { Log_e("event init failed: %d", rc); - IOT_Template_Destroy(&(template_client->mqtt)); + IOT_Template_Destroy(template_client); HAL_Free(template_client); goto End; } @@ -805,7 +807,7 @@ void* IOT_Template_Construct(TemplateInitParams *pParams, void *pMqttClient) rc = IOT_Action_Init(template_client); if (rc < 0) { Log_e("action init failed: %d", rc); - IOT_Template_Destroy(&(template_client->mqtt)); + IOT_Template_Destroy(template_client); HAL_Free(template_client); goto End; } @@ -824,7 +826,7 @@ int IOT_Template_Destroy(void *handle) POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL); Qcloud_IoT_Template* template_client = (Qcloud_IoT_Template*)handle; - qcloud_iot_template_reset(handle); + qcloud_iot_template_reset(template_client); IOT_MQTT_Destroy(&template_client->mqtt); diff --git a/sdk_src/services/data_template/data_template_client_manager.c b/sdk_src/services/data_template/data_template_client_manager.c index ee6aede..88134a6 100644 --- a/sdk_src/services/data_template/data_template_client_manager.c +++ b/sdk_src/services/data_template/data_template_client_manager.c @@ -267,18 +267,27 @@ void qcloud_iot_template_reset(void *pClient) POINTER_SANITY_CHECK_RTN(pClient); Qcloud_IoT_Template *template_client = (Qcloud_IoT_Template *)pClient; + + _unsubscribe_template_downstream_topic(template_client->mqtt); + if (template_client->inner_data.property_handle_list) { list_destroy(template_client->inner_data.property_handle_list); + template_client->inner_data.property_handle_list = NULL; } - _unsubscribe_template_downstream_topic(template_client->mqtt); - if (template_client->inner_data.reply_list) { list_destroy(template_client->inner_data.reply_list); + template_client->inner_data.reply_list = NULL; } if (template_client->inner_data.event_list) { list_destroy(template_client->inner_data.event_list); + template_client->inner_data.event_list = NULL; + } + + if (NULL != template_client->inner_data.action_handle_list) { + list_destroy(template_client->inner_data.action_handle_list); + template_client->inner_data.action_handle_list = NULL; } } diff --git a/sdk_src/services/dynreg/dynreg.c b/sdk_src/services/dynreg/dynreg.c new file mode 100644 index 0000000..5a34848 --- /dev/null +++ b/sdk_src/services/dynreg/dynreg.c @@ -0,0 +1,498 @@ +/* + * Tencent is pleased to support the open source community by making IoT Hub available. + * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. + + * Licensed under the MIT License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "qcloud_iot_ca.h" +#include "qcloud_iot_device.h" +#include "qcloud_iot_common.h" +#include "qcloud_iot_import.h" +#include "qcloud_iot_export.h" +#include "utils_httpc.h" +#include "utils_hmac.h" +#include "utils_aes.h" +#include "lite-utils.h" +#include "utils_base64.h" + + +#define REG_URL_MAX_LEN (128) +#define DYN_REG_SIGN_LEN (64) +#define DYN_BUFF_DATA_MORE (10) +#define BASE64_ENCODE_OUT_LEN(x) (((x+3)*4)/3) +#define DYN_REG_RES_HTTP_TIMEOUT_MS (2000) + +#ifdef AUTH_MODE_CERT +#define DYN_RESPONSE_BUFF_LEN (5*1024) +#define DECODE_BUFF_LEN (5*1024) +#else +#define DYN_RESPONSE_BUFF_LEN (256) +#define DECODE_BUFF_LEN (256) +#endif + + +/* Knuth's TAOCP section 3.6 */ +#define M ((1U<<31) -1) +#define A 48271 +#define Q 44488 // M/A +#define R 3399 // M%A; R < Q !!! + +#define CODE_RESAULT "code" +#define ENCRYPT_TYPE "encryptionType" +#define PSK_DATA "psk" +#define CERT_DATA "clientCert" +#define KEY_DATA "clientKey" + +typedef enum { + eCERT_TYPE = 1, + ePSK_TYPE = 2, +} eAuthType; + +/*Global value*/ +static unsigned int _seed = 1; + +int rand_r(unsigned int* seed) +{ + int32_t X; + + X = *seed; + X = A * (X % Q) - R * (int32_t) (X / Q); + if (X < 0) + X += M; + + *seed = X; + return X; +} + +int rand_d(void) +{ + return rand_r(&_seed); +} + +void srand_d(unsigned int i) +{ + _seed = i; +} + +static int _get_json_resault_code(char *json) +{ + int resault = -1; + char *v = LITE_json_value_of(CODE_RESAULT, json); + + if (v == NULL) { + Log_e("Invalid json content: %s", json); + return -1; + } + + if (LITE_get_int32(&resault, v) != QCLOUD_RET_SUCCESS) { + Log_e("Invalid json content: %s", json); + HAL_Free(v); + return -1; + } + + HAL_Free(v); + + return resault; +} + +static int _get_json_encry_type(char *json) +{ + int type = -1; + char *v = LITE_json_value_of(ENCRYPT_TYPE, json); + + if (v == NULL) { + Log_e("Get encry type fail, %s", json); + return -1; + } + + if (LITE_get_int32(&type, v) != QCLOUD_RET_SUCCESS) { + Log_e("Invalid json content: %s", json); + HAL_Free(v); + return -1; + } + + HAL_Free(v); + + return type; +} + +#ifndef AUTH_MODE_CERT + +static char * _get_json_psk(char *json) +{ + + char *psk = LITE_json_value_of(PSK_DATA, json); + + if (psk == NULL) { + Log_e("Get psk fail: %s", json); + } + + return psk; +} + +#else +static char * _get_json_cert_data(char *json) +{ + + char *cert = LITE_json_value_of(CERT_DATA, json); + + if (cert == NULL) { + Log_e("Get clientCert fail: %s", json); + } + + return cert; +} + +static char * _get_json_key_data(char *json) +{ + + char *key = LITE_json_value_of(KEY_DATA, json); + + if (key == NULL) { + Log_e("Get clientCert fail: %s", json); + } + + return key; +} + +/*\\n in data change to '\n'*/ +static void _deal_transfer(char *data, uint32_t dataLen) +{ + int i; + + for (i = 0; i < dataLen; i++) { + if ((data[i] == '\\') && (data[i + 1] == 'n')) { + data[i] = ' '; + data[i + 1] = '\n'; + } + } + +} + +static int _cert_file_save(const char *fileName, char *data, uint32_t dataLen) +{ + FILE *fp; + char filePath[FILE_PATH_MAX_LEN]; + uint32_t len; + int Ret = QCLOUD_ERR_FAILURE; + + + memset(filePath, 0, FILE_PATH_MAX_LEN); + HAL_Snprintf(filePath, FILE_PATH_MAX_LEN, "./certs/%s", fileName); + + if ( ( fp = fopen(filePath, "w+" ) ) == NULL ) { + Log_e("fail to open file %s", fileName); + goto exit; + } + + _deal_transfer(data, dataLen); + len = fprintf(fp, "%s", data); + fclose(fp); + + if (len == dataLen) { + Log_d("save %s file succes", fileName); + Ret = QCLOUD_RET_SUCCESS; + } + +exit: + return Ret; +} + +#endif + +static int _parse_devinfo(char *jdoc, DeviceInfo *pDevInfo) +{ + int ret = 0; + size_t len; + int datalen; + int enType; + unsigned int keybits; + char key[UTILS_AES_BLOCK_LEN + 1]; + char decodeBuff[DECODE_BUFF_LEN] = {0}; + unsigned char iv[16]; + char *payload = NULL; + +#ifdef AUTH_MODE_CERT + char *clientCert; + char *clientKey; +#else + char *psk; +#endif + + Log_d("recv: %s", jdoc); + + ret = _get_json_resault_code(jdoc); + if (QCLOUD_RET_SUCCESS != ret) { + Log_e("response err, ret:%d", ret); + goto exit; + } + + payload = LITE_json_value_of("payload", jdoc); + if (payload == NULL) { + Log_e("Invalid json content: %s", jdoc); + ret = QCLOUD_ERR_FAILURE; + goto exit; + } else { + Log_d("payload:%s", payload); + } + + ret = qcloud_iot_utils_base64decode((uint8_t *)decodeBuff, sizeof(decodeBuff), &len, (uint8_t *)payload, strlen(payload)); + if (ret != QCLOUD_RET_SUCCESS) { + Log_e("Response decode err, response:%s", payload); + ret = QCLOUD_ERR_FAILURE; + goto exit; + } + + datalen = len + (UTILS_AES_BLOCK_LEN - len % UTILS_AES_BLOCK_LEN); + keybits = AES_KEY_BITS_128; + memset(key, 0, UTILS_AES_BLOCK_LEN); + strncpy(key, pDevInfo->product_secret, UTILS_AES_BLOCK_LEN); + memset( iv, '0', UTILS_AES_BLOCK_LEN); + ret = utils_aes_cbc((uint8_t *)decodeBuff, datalen, (uint8_t *)decodeBuff, + DECODE_BUFF_LEN, UTILS_AES_DECRYPT, (uint8_t *)key, keybits, iv); + if (QCLOUD_RET_SUCCESS == ret) { + //Log_d("The decrypted data is:%s", decodeBuff); + + } else { + Log_e("data decry err,ret:%d", ret); + goto exit; + } + + enType = _get_json_encry_type(decodeBuff); + if (enType < 0) { + Log_e("invlid encryt type, decrypt maybe faild"); + ret = QCLOUD_ERR_FAILURE; + goto exit; + } + +#ifdef AUTH_MODE_CERT + if (eCERT_TYPE != enType) { + Log_e("encryt type should be cert type"); + ret = QCLOUD_ERR_FAILURE; + goto exit; + } + + clientCert = _get_json_cert_data(decodeBuff); + if (NULL != clientCert) { + memset(pDevInfo->dev_cert_file_name, 0, MAX_SIZE_OF_DEVICE_CERT_FILE_NAME); + HAL_Snprintf(pDevInfo->dev_cert_file_name, MAX_SIZE_OF_DEVICE_CERT_FILE_NAME, "%s_cert.crt", pDevInfo->device_name); + if (QCLOUD_RET_SUCCESS != _cert_file_save(pDevInfo->dev_cert_file_name, clientCert, strlen(clientCert))) { + Log_e("save %s file fail", pDevInfo->dev_cert_file_name); + ret = QCLOUD_ERR_FAILURE; + } + + HAL_Free(clientCert); + + } else { + Log_e("Get clientCert data fail"); + ret = QCLOUD_ERR_FAILURE; + } + + clientKey = _get_json_key_data(decodeBuff); + if (NULL != clientKey) { + memset(pDevInfo->dev_key_file_name, 0, MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME); + HAL_Snprintf(pDevInfo->dev_key_file_name, MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME, "%s_private.key", pDevInfo->device_name); + if (QCLOUD_RET_SUCCESS != _cert_file_save(pDevInfo->dev_key_file_name, clientKey, strlen(clientKey))) { + Log_e("save %s file fail", pDevInfo->dev_key_file_name); + ret = QCLOUD_ERR_FAILURE; + } + + HAL_Free(clientKey); + + } else { + Log_e("Get clientCert data fail"); + ret = QCLOUD_ERR_FAILURE; + } + +#else + if (ePSK_TYPE != enType) { + Log_e("encryt type should be psk type"); + ret = QCLOUD_ERR_FAILURE; + goto exit; + } + + psk = _get_json_psk(decodeBuff); + if (NULL != psk) { + if (strlen(psk) > MAX_SIZE_OF_DEVICE_SECRET) { + Log_e("psk exceed max len,%s", psk); + strcpy(pDevInfo->device_secret, psk); + } else { + strncpy(pDevInfo->device_secret, psk, MAX_SIZE_OF_DEVICE_SECRET); + pDevInfo->device_secret[MAX_SIZE_OF_DEVICE_SECRET] = '\0'; + } + HAL_Free(psk); + } else { + Log_e("Get psk data fail"); + } +#endif +exit: + + if (payload) { + HAL_Free(payload); + } + + return ret; +} + +static int _post_reg_request_by_http(char *request_buf, DeviceInfo *pDevInfo) +{ + int Ret = 0; + HTTPClient http_client; /* http client */ + HTTPClientData http_data; /* http client data */ + + const char *url_format = "%s://%s/register/dev"; + char url[REG_URL_MAX_LEN] = {0}; + int port; + const char *ca_crt = NULL; + char respbuff[DYN_RESPONSE_BUFF_LEN]; + + /*format URL*/ +#ifndef AUTH_WITH_NOTLS + HAL_Snprintf(url, REG_URL_MAX_LEN, url_format, "https", DYN_REG_SERVER_URL); + port = DYN_REG_SERVER_PORT_TLS; + ca_crt = iot_ca_get(); +#else + HAL_Snprintf(url, REG_URL_MAX_LEN, url_format, "http", DYN_REG_SERVER_URL); + port = DYN_REG_SERVER_PORT; +#endif + + + memset((char *)&http_client, 0, sizeof(HTTPClient)); + memset((char *)&http_data, 0, sizeof(HTTPClientData)); + + http_client.header = "Accept: text/xml,application/json;*/*\r\n"; + + http_data.post_content_type = "application/x-www-form-urlencoded"; + http_data.post_buf = request_buf; + http_data.post_buf_len = strlen(request_buf); + + Ret = qcloud_http_client_common(&http_client, url, port, ca_crt, HTTP_POST, &http_data); + if (QCLOUD_RET_SUCCESS != Ret) { + Log_e("qcloud_http_client_common failed, Ret = %d", Ret); + return Ret; + } + + memset(respbuff, 0, DYN_RESPONSE_BUFF_LEN); + http_data.response_buf_len = DYN_RESPONSE_BUFF_LEN; + http_data.response_buf = respbuff; + + Ret = qcloud_http_recv_data(&http_client, DYN_REG_RES_HTTP_TIMEOUT_MS, &http_data); + if (QCLOUD_RET_SUCCESS != Ret) { + Log_e("dynamic register response fail, Ret = %d", Ret); + } else { + /*Parse dev info*/ + Ret = _parse_devinfo(http_data.response_buf, pDevInfo); + if (QCLOUD_RET_SUCCESS != Ret) { + Log_e("parse device info err"); + } + } + + qcloud_http_client_close(&http_client); + + return Ret; +} + +static int _cal_dynreg_sign(DeviceInfo *pDevInfo, char *signout, int max_signlen, int nonce, uint32_t timestamp) +{ + int sign_len; + size_t olen = 0; + char *pSignSource = NULL; + const char *sign_fmt = "deviceName=%s&nonce=%d&productId=%s×tamp=%d"; + char sign[DYN_REG_SIGN_LEN] = {0}; + + /*format sign data*/ + sign_len = strlen(sign_fmt) + strlen(pDevInfo->device_name) + strlen(pDevInfo->product_id) + sizeof(int) + sizeof(uint32_t) + DYN_BUFF_DATA_MORE; + pSignSource = HAL_Malloc(sign_len); + if (pSignSource == NULL) { + Log_e("malloc sign source buff fail"); + return QCLOUD_ERR_FAILURE; + } + memset(pSignSource, 0, sign_len); + HAL_Snprintf((char *)pSignSource, sign_len, sign_fmt, pDevInfo->device_name, nonce, pDevInfo->product_id, timestamp); + + /*cal hmac sha1*/ + utils_hmac_sha1(pSignSource, strlen(pSignSource), sign, pDevInfo->product_secret, strlen(pDevInfo->product_secret)); + + /*base64 encode*/ + qcloud_iot_utils_base64encode((uint8_t *)signout, max_signlen, &olen, (const uint8_t *)sign, strlen(sign)); + + HAL_Free(pSignSource); + + return (olen > max_signlen) ? QCLOUD_ERR_FAILURE : QCLOUD_RET_SUCCESS; +} + +int IOT_DynReg_Device(DeviceInfo *pDevInfo) +{ + const char *para_format = "{\"deviceName\":\"%s\",\"nonce\":%d,\"productId\":\"%s\",\"timestamp\":%d,\"signature\":\"%s\"}"; + int nonce; + int Ret; + uint32_t timestamp; + int len; + char sign[DYN_REG_SIGN_LEN] = {0}; + char *pRequest = NULL; + + if (strlen(pDevInfo->product_secret) < UTILS_AES_BLOCK_LEN) { + Log_e("product key inllegal"); + return QCLOUD_ERR_FAILURE; + } + + srand_d(HAL_GetTimeMs()); + nonce = rand_d(); + timestamp = time(0); + + /*cal sign*/ + if (QCLOUD_RET_SUCCESS == _cal_dynreg_sign(pDevInfo, sign, DYN_REG_SIGN_LEN, nonce, timestamp)) { + Log_d("sign:%s", sign); + } else { + Log_e("cal sign fail"); + return QCLOUD_ERR_FAILURE; + } + + /*format http request*/ + len = strlen(para_format) + strlen(pDevInfo->product_id) + strlen(pDevInfo->device_name) + sizeof(int)\ + + sizeof(uint32_t) + strlen(sign) + DYN_BUFF_DATA_MORE; + pRequest = HAL_Malloc(len); + if (!pRequest) { + Log_e("malloc request memory fail"); + return QCLOUD_ERR_FAILURE; + } + memset(pRequest, 0, len); + HAL_Snprintf(pRequest, len, para_format, pDevInfo->device_name, nonce, pDevInfo->product_id, timestamp, sign); + Log_d("request:%s", pRequest); + + Log_d("resbuff len:%d", DYN_RESPONSE_BUFF_LEN); + /*post request*/ + Ret = _post_reg_request_by_http(pRequest, pDevInfo); + if (QCLOUD_RET_SUCCESS == Ret) { + Log_d("request dev info success"); + } else { + Log_e("request dev info fail"); + } + + HAL_Free(pRequest); + + return Ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/sdk_src/services/ota/ota_client.c b/sdk_src/services/ota/ota_client.c index bca9efe..8a5380e 100644 --- a/sdk_src/services/ota/ota_client.c +++ b/sdk_src/services/ota/ota_client.c @@ -367,6 +367,21 @@ void IOT_OTA_UpdateClientMd5(void *handle, char * buff, uint32_t size) qcloud_otalib_md5_update(h_ota->md5, buff, size); } +/*support continuous transmission of breakpoints*/ +int IOT_OTA_ResetClientMD5(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *) handle; + + qcloud_otalib_md5_deinit(h_ota->md5); + h_ota->md5 = qcloud_otalib_md5_init(); + if (NULL == h_ota->md5) { + return QCLOUD_ERR_FAILURE; + } + + return QCLOUD_RET_SUCCESS; +} + + int IOT_OTA_ReportVersion(void *handle, const char *version) { #define MSG_INFORM_LEN (128) diff --git a/tools/build_scripts/parse_make_settings.mk b/tools/build_scripts/parse_make_settings.mk index e927f12..938d98d 100644 --- a/tools/build_scripts/parse_make_settings.mk +++ b/tools/build_scripts/parse_make_settings.mk @@ -29,6 +29,7 @@ SWITCH_VARS := \ FEATURE_DEBUG_DEV_INFO_USED \ FEATURE_OTA_USE_HTTPS \ FEATURE_MULTITHREAD_ENABLED \ + FEATURE_DEV_DYN_REG_ENABLED \ $(foreach v, \