Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/aml_commonlib/utils/fota_upgrade.c
T
2026-08-24 10:03:24 +08:00

630 lines
19 KiB
C

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include "../aml_utility/unifykey.h"
#include "../fota/dmgr_api.h"
#include "aml_device_property.h"
#define CHECK_NETWORK_DEALY 20 // 60
#define INFO_LEN 128
struct test_context {
char DEVINFO_OEM[INFO_LEN];
char DEVINFO_DEVICETYPE[INFO_LEN];
char DEVINFO_PLATFORM[INFO_LEN];
char DEVINFO_MODELS[INFO_LEN];
char DEVINFO_VERSION[INFO_LEN];
char DEVINFO_PRODUCTID[INFO_LEN];
char DEVINFO_PRODUCTSECRET[INFO_LEN];
const char *DEVINFO_APPVERSION;
const char *DEVINFO_NETWORKTYPE;
const char *DEVINFO_CHK_URL;
char DEVINFO_MID[256];
int DEVINFO_SERVER_OPT;
struct notifier_block nb_checkversion;
struct notifier_block nb_download;
int has_new_version;
struct version_info cloned_version;
int download_exit;
};
#define INIT_DEFAULT_TEST_CONTEXT() \
{ \
.DEVINFO_OEM = "Amlogic", .DEVINFO_DEVICETYPE = "smarthome", \
.DEVINFO_PLATFORM = "A113D", .DEVINFO_MODELS = "OTT", \
.DEVINFO_MID = "20220809043042", .DEVINFO_VERSION = "0.5.0", \
.DEVINFO_PRODUCTID = "1660115992", \
.DEVINFO_PRODUCTSECRET = "fa7bfc8d755c4c0b8c52fdb00d431d3f", \
.DEVINFO_APPVERSION = "2", .DEVINFO_NETWORKTYPE = "WIFI", \
.DEVINFO_CHK_URL = NULL, \
.DEVINFO_SERVER_OPT = SERVER_TRANSOPT_IOT | SERVER_TRANSOPT_HTTP, \
.has_new_version = 0, .download_exit = 0, \
}
#define TEST_ASSERT_EQUAL_INT(expected, actual) \
do { \
if (expected != actual) \
return -1; \
} while (0)
#ifndef CONTAINER_OF
#define CONTAINER_OF(ptr, type, member) \
(type *)((char *)(ptr) - (char *)&((type *)0)->member)
#endif
#define FOTA_WEB_SERVER "www.abupdate.com"
#define AML_SYS_SAVE_PATH "/data"
static char *__save_file_path = "/data/software.swu";
static char *__version_file_path = "/version.txt";
static char *__new_version_file = "new_version";
static int write_new_version(struct version_info *ver) {
FILE *ver_fd = NULL;
char *save_path = AML_SYS_SAVE_PATH;
char cmd_buf[200];
snprintf(cmd_buf, 200, "%s/%s", save_path, __new_version_file);
if (access(cmd_buf, F_OK) != -1) {
snprintf(cmd_buf, 200, "rm %s/%s", save_path, __new_version_file);
system(cmd_buf);
}
snprintf(cmd_buf, 200, "%s/%s", save_path, __new_version_file);
ver_fd = fopen(cmd_buf, "w+");
if (NULL == ver_fd) {
fprintf(stdout, "Cannot open version file %s\n", cmd_buf);
return -1;
}
fprintf(ver_fd, "version_name:%s\n", ver->version_name);
// fprintf(ver_fd, "file_size:%d\n", ver->file_size);
// fprintf(ver_fd, "delta_id:%s\n", ver->delta_id);
// fprintf(ver_fd, "md5sum:%s\n", ver->md5sum);
// fprintf(ver_fd, "delta_url:%s\n", ver->delta_url);
// fprintf(ver_fd, "release_note:%s\n", ver->release_note);
// fprintf(ver_fd, "upgrade_from_time:%s\n", ver->upgrade_from_time);
// fprintf(ver_fd, "upgrade_to_time:%s\n", ver->upgrade_to_time);
// fprintf(ver_fd, "upgrade_gap:%s\n", ver->upgrade_gap);
// fprintf(ver_fd, "meta_data:%s\n", ver->meta_data);
// fprintf(ver_fd, "event_id:%s\n", ver->event_id);
fclose(ver_fd);
return 0;
}
static char *simple_strndup(const char *s, size_t n) {
size_t max_len = strnlen(s, n);
char *dest = malloc(max_len + 1);
if (dest) {
memcpy(dest, s, max_len);
dest[max_len] = '\0';
}
return dest;
}
static char *simple_strdup(const char *s) {
return simple_strndup(s, 0x7fffff);
}
static void clone_version(struct test_context *test_ctx,
struct version_info *info) {
test_ctx->cloned_version.file_size = info->file_size;
test_ctx->cloned_version.flags = info->flags;
if (info->version_name) {
test_ctx->cloned_version.version_name = simple_strdup(info->version_name);
}
if (info->delta_id) {
test_ctx->cloned_version.delta_id = simple_strdup(info->delta_id);
}
if (info->md5sum) {
test_ctx->cloned_version.md5sum = simple_strdup(info->md5sum);
}
if (info->delta_url) {
test_ctx->cloned_version.delta_url = simple_strdup(info->delta_url);
}
if (info->release_note) {
test_ctx->cloned_version.release_note = simple_strdup(info->release_note);
}
if (info->upgrade_from_time) {
test_ctx->cloned_version.upgrade_from_time =
strdup(info->upgrade_from_time);
}
if (info->upgrade_to_time) {
test_ctx->cloned_version.upgrade_to_time = strdup(info->upgrade_to_time);
}
if (info->upgrade_gap) {
test_ctx->cloned_version.upgrade_gap = strdup(info->upgrade_gap);
}
if (info->meta_data) {
test_ctx->cloned_version.meta_data = simple_strdup(info->meta_data);
}
if (info->event_id) {
test_ctx->cloned_version.event_id = strdup(info->event_id);
}
}
static void free_cloned_version(struct test_context *test_ctx) {
struct version_info *info = &test_ctx->cloned_version;
if (!test_ctx->has_new_version) {
return;
}
if (info->version_name) {
free(info->version_name);
}
if (info->delta_id) {
free(info->delta_id);
}
if (info->md5sum) {
free(info->md5sum);
}
if (info->delta_url) {
free(info->delta_url);
}
if (info->release_note) {
free(info->release_note);
}
if (info->upgrade_from_time) {
free(info->upgrade_from_time);
}
if (info->upgrade_to_time) {
free(info->upgrade_to_time);
}
if (info->upgrade_gap) {
free(info->upgrade_gap);
}
if (info->meta_data) {
free(info->meta_data);
}
if (info->event_id) {
free(info->event_id);
}
memset(info, 0x00, sizeof(*info));
test_ctx->has_new_version = 0;
}
static int checkversion_notifier(struct notifier_block *nb,
unsigned long action, void *data) {
struct test_context *test_ctx =
CONTAINER_OF(nb, struct test_context, nb_checkversion);
switch (action) {
case NOTIFY_CHECKVERSION_PREPARE:
fprintf(stdout, "[callback: checkversion_prepare]\n");
break;
case NOTIFY_CHECKVERSION_ABORTED:
fprintf(stdout, "[callback: checkversion_abored]\n");
break;
case NOTIFY_CHECKVERSION_FINISHED:
fprintf(stdout, "[callback: checkversion_finished]\n");
if (data) {
fprintf(stdout, "[callback: new version %s]\n",
((struct version_info *)data)->version_name);
test_ctx->has_new_version = 1;
clone_version(test_ctx, data);
}
break;
}
return 0;
}
static int download_notifier(struct notifier_block *nb, unsigned long action,
void *data) {
struct test_context *test_ctx =
CONTAINER_OF(nb, struct test_context, nb_download);
notifier_data_t *notify_data = (notifier_data_t *)data;
long total_Kbytes = notify_data->u.dlinfo.total_bytes / 1024;
long saved_Kbytes = notify_data->u.dlinfo.saved_bytes / 1024;
unsigned int percent = (saved_Kbytes * 100) / total_Kbytes;
static unsigned int pre_percent = 0;
// fprintf(stdout, "[download_notifier] percent:%d, total_Kbytes:%d,
// saved_Kbytes:%d\n", percent, total_Kbytes, saved_Kbytes);
switch (action) {
case NOTIFY_DOWNLOAD_PREPARE: {
fprintf(stdout, "DOWNLOAD: prepare\n");
pre_percent = 0;
} break;
case NOTIFY_DOWNLOAD_ABORTED: {
fprintf(stdout, "ERROR: Download abored!\n");
test_ctx->download_exit = -1;
} break;
case NOTIFY_DOWNLOAD_TRANSLATING: {
if (percent < 100) {
if (pre_percent != percent) {
pre_percent = percent;
fprintf(stdout, "DOWNLOAD: %d\n", percent);
}
}
} break;
case NOTIFY_DOWNLOAD_FINISHED: {
test_ctx->download_exit = 1;
percent = 100;
fprintf(stdout, "DOWNLOAD: %d\n", percent);
} break;
case NOTIFY_DOWNLOAD_PARTTIAL_FINISHED: {
fprintf(stdout, "DOWNLOAD: partial finished\n");
test_ctx->download_exit = 1;
} break;
}
fflush(stdout);
return 0;
}
static int init_notifier(dmgr_t dm_id, struct test_context *test_ctx) {
test_ctx->nb_checkversion.notifier_call = checkversion_notifier;
test_ctx->nb_checkversion.priority = 0;
test_ctx->nb_download.notifier_call = download_notifier;
test_ctx->nb_download.priority = 0;
TEST_ASSERT_EQUAL_INT(0, dmgr_register_notifier(dm_id,
&test_ctx->nb_checkversion,
NOTIFY_ID_CHECK_VERSION));
TEST_ASSERT_EQUAL_INT(0, dmgr_register_notifier(dm_id, &test_ctx->nb_download,
NOTIFY_ID_DOWNLOAD));
return 0;
}
static void WAIT_DOWNLOAD_COMPLETED(struct test_context *test_ctx) {
if (!test_ctx) {
return;
}
for (;;) {
if (test_ctx->download_exit != 0) {
fprintf(stdout, ">>>>>download_exit = %d\n", test_ctx->download_exit);
break;
}
sleep(1);
}
}
static int getMid(char *readbuf) {
#define DEVICE_NAME "deviceid"
int fd, ret = -1;
ret = Aml_Util_UnifyKeyInit(UNIFYKEYS_PATH);
if (0 > ret) {
fprintf(stdout, "%s() %d, init unifykey failed @ %d!\n", __func__, __LINE__,
ret);
}
ret = Aml_Util_UnifyKeyRead(UNIFYKEYS_PATH, DEVICE_NAME, readbuf);
return ret;
}
static int get_sw_version(char *sw_version) {
FILE *ver_fd = NULL;
char line[200];
ver_fd = fopen(__version_file_path, "r");
if (NULL == ver_fd) {
fprintf(stdout, "Cannot open version file %s\n", __version_file_path);
return -1;
}
while (fgets(line, sizeof(line), ver_fd)) {
char *pLine = strstr(line, "imagename");
if (NULL != pLine) {
// 20220809043042
memcpy(sw_version, pLine + strlen(pLine) - 15, 14);
break;
}
}
fclose(ver_fd);
return 0;
}
static int get_deviceinfo(struct test_context *test_ctx) {
int ret;
char out_value[INFO_LEN];
ret = get_sw_version(test_ctx->DEVINFO_VERSION);
// SDK_VERSION of device.properties, only can sync time base on clean build
// ret = AmlDeviceGetProperty("SDK_VERSION", out_value, INFO_LEN);
// if (ret == AMLDEVICE_SUCCESS) {
// memcpy(test_ctx->DEVINFO_VERSION, out_value, strlen(out_value));
//}
ret = AmlDeviceGetProperty("devinfo.oem", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_OEM, out_value, sizeof(test_ctx->DEVINFO_OEM) - 1);
test_ctx->DEVINFO_OEM[sizeof(test_ctx->DEVINFO_OEM) - 1] = '\0';
}
ret = AmlDeviceGetProperty("devinfo.devicetype", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_DEVICETYPE, out_value, sizeof(test_ctx->DEVINFO_DEVICETYPE) - 1);
test_ctx->DEVINFO_DEVICETYPE[sizeof(test_ctx->DEVINFO_DEVICETYPE) - 1] = '\0';
}
ret = AmlDeviceGetProperty("devinfo.platform", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_PLATFORM, out_value, sizeof(test_ctx->DEVINFO_PLATFORM) - 1);
test_ctx->DEVINFO_PLATFORM[sizeof(test_ctx->DEVINFO_PLATFORM) - 1] = '\0';
}
ret = AmlDeviceGetProperty("devinfo.models", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_MODELS, out_value, sizeof(test_ctx->DEVINFO_MODELS) - 1);
test_ctx->DEVINFO_MODELS[sizeof(test_ctx->DEVINFO_MODELS) - 1] = '\0';
}
ret = AmlDeviceGetProperty("devinfo.productid", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_PRODUCTID, out_value, sizeof(test_ctx->DEVINFO_PRODUCTID) - 1);
test_ctx->DEVINFO_PRODUCTID[sizeof(test_ctx->DEVINFO_PRODUCTID) - 1] = '\0';
}
ret = AmlDeviceGetProperty("devinfo.productsecret", out_value, INFO_LEN);
if (ret == AMLDEVICE_SUCCESS) {
strncpy(test_ctx->DEVINFO_PRODUCTSECRET, out_value, sizeof(test_ctx->DEVINFO_PRODUCTSECRET) - 1);
test_ctx->DEVINFO_PRODUCTSECRET[sizeof(test_ctx->DEVINFO_PRODUCTSECRET) - 1] = '\0';
}
return ret;
}
static int report_upgraded_version(dmgr_t dm_id, char *ver) {
struct version_info cur_ver;
int ret;
if (ver == NULL)
return -1;
memset(&cur_ver, 0, sizeof(struct version_info));
cur_ver.version_name = ver;
ret = dmgr_report_upgraded_version(dm_id, &cur_ver, "Upgrade done");
fprintf(stdout, "dmgr_report_upgraded_version %s ret = %d\n", ver, ret);
return ret;
}
#define STR_VALUE(val) #val
#define STR(name) STR_VALUE(name)
#define PATH_LEN 256
#define MD5_LEN 32
static int CalcFileMD5(char *file_name, char *md5_sum) {
#define MD5SUM_CMD_FMT "md5sum %." STR(PATH_LEN) "s 2>/dev/null"
char cmd[PATH_LEN + sizeof(MD5SUM_CMD_FMT)];
sprintf(cmd, MD5SUM_CMD_FMT, file_name);
#undef MD5SUM_CMD_FMT
FILE *p = popen(cmd, "r");
if (p == NULL)
return 0;
int i, ch;
for (i = 0; i < MD5_LEN && isxdigit(ch = fgetc(p)); i++) {
*md5_sum++ = ch;
}
*md5_sum = '\0';
pclose(p);
return i == MD5_LEN;
}
static int check_md5(char *md5sum) {
char md5_str[MD5_LEN + 1];
if (!CalcFileMD5(__save_file_path, md5_str)) {
fprintf(stdout, "Cannot calculate the md5 sum\n");
return -1;
}
fprintf(stdout, "Calculate the md5 sum = %s\n", md5_str);
if (!strncmp(md5_str, md5sum, MD5_LEN)) {
fprintf(stdout, "Check md5sum OK\n");
return 0;
}
return -1;
}
int checkNet(const char *web_server) {
char buffer[200];
FILE *fp;
char *p, num[3];
snprintf(buffer, 200, "ping %s -c 1 -w 2 > /tmp/netlog", web_server);
// fprintf(stdout, "command: %s\n", buffer);
system(buffer);
// sleep(2);
usleep(50000);
fp = fopen("/tmp/netlog", "r");
if (fp < 0) {
fprintf(stdout, "open file failed.\n");
return 0;
}
fseek(fp, 0, SEEK_SET);
memset(buffer, 0, sizeof(buffer));
// read penultimate row, 1 packets transmitted, 1 received, 0% packet loss,
// time 0ms
while (fgets(buffer, 200, fp) != NULL) {
p = strstr(buffer, "received,");
if (p) {
// fprintf(stdout, "string is: %s", p);
memcpy(num, p + 10, 3);
// fprintf(stdout, "num: %s\n", num);
if (atoi(num) == 0)
return 1;
else
return 0;
}
memset(buffer, 0, sizeof(buffer));
}
fclose(fp);
system("rm /tmp/netlog");
return 0;
}
int main(void) {
dmgr_t dm_id, ret;
char rbuf[64];
struct test_context test_ctx = INIT_DEFAULT_TEST_CONTEXT();
struct policy_info policy = POLICY_INFO_INIT(policy);
policy.so_transfer_timeout = 10;
policy.so_connect_timeout = 5;
policy.sstate_cached_path = "/var";
policy.sub_certificates = "/var/client.crt";
policy.server_transfer_opt = SERVER_TRANSOPT_IOT | SERVER_TRANSOPT_HTTP;
ret = getMid(rbuf);
if ((0 != strcmp(rbuf, "")) && (0 < ret)) {
strcpy(test_ctx.DEVINFO_MID, rbuf);
} else {
fprintf(stdout, "ERROR: Get mid failed!\n");
return -1;
}
ret = get_deviceinfo(&test_ctx);
if (ret < 0) {
fprintf(stdout, "ERROR: Get device info failed!\n");
return -1;
}
#if 1
fprintf(stdout, "INFO: DEVINFO_OEM = %s \n", test_ctx.DEVINFO_OEM);
fprintf(stdout, "INFO: DEVINFO_DEVICETYPE = %s \n",
test_ctx.DEVINFO_DEVICETYPE);
fprintf(stdout, "INFO: DEVINFO_PLATFORM = %s \n", test_ctx.DEVINFO_PLATFORM);
fprintf(stdout, "INFO: DEVINFO_MODELS = %s \n", test_ctx.DEVINFO_MODELS);
fprintf(stdout, "INFO: DEVINFO_MID = %s \n", test_ctx.DEVINFO_MID);
fprintf(stdout, "INFO: DEVINFO_VERSION = %s \n", test_ctx.DEVINFO_VERSION);
fprintf(stdout, "INFO: DEVINFO_PRODUCTID = %s \n",
test_ctx.DEVINFO_PRODUCTID);
fprintf(stdout, "INFO: DEVINFO_PRODUCTSECRET = %s \n",
test_ctx.DEVINFO_PRODUCTSECRET);
fprintf(stdout, "INFO: DEVINFO_APPVERSION = %s \n",
test_ctx.DEVINFO_APPVERSION);
fprintf(stdout, "INFO: DEVINFO_NETWORKTYPE = %s \n",
test_ctx.DEVINFO_NETWORKTYPE);
#endif
while (1) {
if (0 == checkNet(FOTA_WEB_SERVER)) {
fprintf(stdout, "ERROR: Network not connected!\n ");
// sleep(CHECK_NETWORK_DEALY);
return -1;
}
dm_id = dmgr_alloc(&policy, LOG_ERROR, LOGGER_STDIO, stdout);
if (0 >= dm_id) {
fprintf(stdout, "ERROR: Alloc dm_id failed!\n");
return -1;
}
if (0 != dmgr_register_deviceinfo(dm_id, "oem", test_ctx.DEVINFO_OEM) ||
0 != dmgr_register_deviceinfo(dm_id, "deviceType",
test_ctx.DEVINFO_DEVICETYPE) ||
0 != dmgr_register_deviceinfo(dm_id, "platform",
test_ctx.DEVINFO_PLATFORM) ||
0 != dmgr_register_deviceinfo(dm_id, "models",
test_ctx.DEVINFO_MODELS) ||
0 != dmgr_register_deviceinfo(dm_id, "mid", test_ctx.DEVINFO_MID) ||
0 != dmgr_register_deviceinfo(dm_id, "version",
test_ctx.DEVINFO_VERSION) ||
0 != dmgr_register_deviceinfo(dm_id, "productId",
test_ctx.DEVINFO_PRODUCTID) ||
0 != dmgr_register_deviceinfo(dm_id, "productSecret",
test_ctx.DEVINFO_PRODUCTSECRET) ||
0 != dmgr_register_deviceinfo(dm_id, "appversion",
test_ctx.DEVINFO_APPVERSION) ||
0 != dmgr_register_deviceinfo(dm_id, "networkType",
test_ctx.DEVINFO_NETWORKTYPE)) {
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
continue;
}
if (test_ctx.DEVINFO_CHK_URL) {
if (0 != dmgr_register_serverinfo(dm_id, SERI_CHK_URL,
test_ctx.DEVINFO_CHK_URL)) {
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
continue;
}
}
init_notifier(dm_id, &test_ctx);
if (0 != dmgr_check_version(dm_id)) {
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
fprintf(stdout, "ERROR: No new version detected!\n");
break;
}
if (0 == test_ctx.has_new_version) {
if ((access("/data/software.swu", F_OK)) != -1) {
system("rm /data/software.swu");
}
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
fprintf(stdout, "ERROR: No new version!\n");
ret = 0;
break;
} else {
fprintf(stdout, "INFO: has_new_version \n");
}
if (0 != dmgr_async_download_version(dm_id, &test_ctx.cloned_version,
__save_file_path,
DOWNLOAD_FILE_SEEK_END, 0, -1)) {
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
fprintf(stdout, "ERROR: Download image failed!\n");
ret = -1;
break;
}
WAIT_DOWNLOAD_COMPLETED(&test_ctx);
if (test_ctx.has_new_version && (test_ctx.download_exit == 1)) {
write_new_version(&test_ctx.cloned_version);
// ota package not match md5sum, remove software.swu
if (check_md5(test_ctx.cloned_version.md5sum) == 0) {
fprintf(stdout, "INFO: secure upgrade check pass!\n");
system("reboot recovery");
ret = 1;
} else {
system("rm /data/software.swu");
system("sync");
TEST_ASSERT_EQUAL_INT(0, dmgr_free(dm_id));
fprintf(stdout, "ERROR: OTA package not match md5sum!\n");
ret = -1;
}
}
free_cloned_version(&test_ctx);
break;
}
return ret;
}