#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include "upgrade.h"
#include "ipc_session.h"
#include "dy_utils/dy_common.h"
#include "upgrade_rel_packages.h"

#define UPGRADE_MAX_IPK_NAME_LEN 64
#define UPGRADE_MAX_IPK_VERSION_LEN 128
#define MAX_LINE_LEN 256
#define MAX_SUB_STRING_LEN 128

#define OPKG_CONF_MAX_LINE_NUM 21
#define OPKG_CONF_MAX_LINE_LEN 256
#define MAX_CMD_LEN  128

typedef struct _ipk
{
    struct list_head list;
    char name[UPGRADE_MAX_IPK_NAME_LEN];
    char expect_ver[UPGRADE_MAX_IPK_VERSION_LEN];
    char work_ver[UPGRADE_MAX_IPK_VERSION_LEN];
}ipk_s;


static int remove_file(const char *file)
{
    if (file == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    if (access(file, F_OK) != 0) {
        return 0;
    }

    if (remove(file) != 0) {
        dy_syslog(LOG_ERR, "remove: %s", strerror(errno));
        return -1;
    }

    return 0;
}

static int call_system(const char *cmd)
{
    if (cmd == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }
    dy_syslog(LOG_INFO, "system func will call \"%s\"", cmd);

    system("killall opkg");
    int status = system(cmd);
    if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == 0)
        {
            dy_syslog(LOG_INFO, "system calls \"%s\" successfully", cmd);
            return 0;
        }
        else
        {
            dy_syslog(LOG_ERR, "system calls \"%s\" failed!!!", cmd);
            return -1;
        }
    }
    else {
        dy_syslog(LOG_ERR, "system calls \"%s\" failed!!!", cmd);
        return -1;
    }
}

static int exec_opkg_update()
{
    const char *cmd_update = "/bin/opkg update";
    return call_system(cmd_update);
}

static int download_packages_gz(const char *url_dir)
{
    char cmd[MAX_CMD_LEN] = {0};

    if (url_dir == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    snprintf(cmd, MAX_CMD_LEN, "wget -t 3 -T 30 -P /tmp/ipks_upgrade %s%s", url_dir, "/Packages.gz");
    return call_system(cmd);
}

static int unzip_packages_gz(const char *file)
{
    char cmd[MAX_CMD_LEN] = {0};

    if (file == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    if (access(file, F_OK) != 0) {
        dy_syslog(LOG_ERR, "File %s does not exist, gunzip failed!!!\n", file);
        return -1;
    }

    snprintf(cmd, MAX_CMD_LEN, "gunzip %s", file);
    return call_system(cmd);
}

// Intput -> source="xxxxxx - yyyyyy"
// Intput -> token=" - "
// Output <- front_token=xxxxxx
// Output <- behind_token=yyyyyy
static int split(char *source, const char *token, char front_token[], char behind_token[])
{
    if (source == NULL || token == NULL || front_token == NULL || behind_token == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    int len_course = strlen(source);
    int len_token  = strlen(token);

    if ((len_course == 0) || (len_token == 0)) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    if (source[len_course-1] == '\n') {
        strtok(source, "\n");
    }

    char *p = source;
    char buf[MAX_SUB_STRING_LEN] = {0};

    while (*p != '\0') {
        strncpy(buf, p, len_token);
        if (strcmp(buf, token) != 0) {
            p++;
            continue;
        }
    
        int n = p - source;
        if (n > MAX_SUB_STRING_LEN) {
            dy_syslog(LOG_ERR, "sub_string_len(%d) is longer than max_len(%d)", n, MAX_SUB_STRING_LEN);
        }
        strncpy(front_token, source, n);
        strncpy(behind_token, source + n + len_token, MAX_SUB_STRING_LEN);
        break;
    }

    return 0;
}

int extract_ipks_name_ver(struct list_head *ipk, const char *packages)
{
    FILE *fp = NULL;
    const char *token = ": ";
    char buf[MAX_LINE_LEN] = {0};
    char front_token[128] = {0};
    char behind_token[128] = {0};
    _Bool name_filled = false;
    _Bool vers_filled = false;
    char name[128];
    char version[128];

    if (ipk == NULL || packages == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    fp = fopen(packages, "r");
    if (NULL == fp) {
        dy_syslog(LOG_ERR, "fopen: %s", strerror(errno));
        return -1;
    }

    memset(buf, 0, sizeof(buf));
    memset(front_token, 0, sizeof(front_token));
    memset(behind_token, 0, sizeof(behind_token));
    memset(name,    0, sizeof(name));
    memset(version, 0, sizeof(version));

	while(fgets(buf, MAX_LINE_LEN, fp)) {                   
        if (split(buf, token, front_token, behind_token) == -1) {   
            dy_syslog(LOG_ERR, "split failed!!!");
            return -1;
        }

        if (strcmp(front_token, "Package") == 0) {
            name_filled = true;
            strcpy(name, behind_token);
        } else if (strcmp(front_token, "Version") == 0) {
            vers_filled = true;
            strcpy(version, behind_token);
        }

        if (name_filled && vers_filled) {
            ipk_s *node = calloc(1, sizeof(ipk_s));
            strcpy(node->name, name);
            strcpy(node->expect_ver, version);
            list_add_tail(&node->list, ipk);

            name_filled = false;
            vers_filled = false;
            memset(name,    0, sizeof(name));
            memset(version, 0, sizeof(version));
        }

        memset(buf, 0, sizeof(buf));
        memset(front_token, 0, sizeof(front_token));
        memset(behind_token, 0, sizeof(behind_token));
    }

    if (-1 == fclose(fp)) {
        dy_syslog(LOG_ERR, "fclose: %s", strerror(errno));
        return -1;
    }

    return 0;
}

static int resp_start_to_platform(ipc_session_t *sesson, const int err_code, const char *err_msg, int mi)
{
    if (sesson == NULL || err_msg == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    } 

    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "cJSON_CreateObject failed!!!");
        return -1;
    }    

    cJSON_AddNumberToObject(root, "err_code", err_code);
    cJSON_AddStringToObject(root, "err_msg", err_msg);
    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/gateway/device/%s/service/%s", sn, sn, "Set_RelIpkUpdateStart");
    dy_syslog(LOG_DEBUG, "pub:topic  :%s", topic);
    dy_syslog(LOG_DEBUG, "pub:payload:%s", str);
    ipc_session_publish(sesson, topic, (unsigned char *)str, (int)strlen(str));

    if (str) {
        free(str);  str = NULL;
    }

    return 0;
}

int send_ipk_update_req(ipc_session_t *sesson, int mi)
{
    if (sesson == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    } 

    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "cJSON_CreateObject failed!!!");
        return -1;
    }    

    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/gateway/device/%s/service/%s", sn, sn, "IpkUpgradeRequest");
    dy_syslog(LOG_DEBUG, "pub:topic  :%s", topic);
    dy_syslog(LOG_DEBUG, "pub:payload:%s", str);
    ipc_session_publish(sesson, topic, (unsigned char *)str, (int)strlen(str));

    if (str) {
        free(str);  str = NULL;
    }

    return 0;
}

static int resp_install_to_platform(ipc_session_t *sesson, const char *name, const char *expe_ver, const char *work_ver,\
                                    const int err_code, const char *err_msg, int mi)
{
    if (sesson == NULL || err_msg == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "cJSON_CreateObject failed!!!");
        return -1;
    }

    cJSON_AddStringToObject(root, "name", name);
    cJSON_AddStringToObject(root, "expe_ver", expe_ver);
    cJSON_AddStringToObject(root, "work_ver", work_ver);
    cJSON_AddNumberToObject(root, "err_code", err_code);
    cJSON_AddStringToObject(root, "err_msg", err_msg);
    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/gateway/device/%s/service/%s", sn, sn, "Set_RelIpkUpdate");
    dy_syslog(LOG_DEBUG, "pub:topic  :%s", topic);
    dy_syslog(LOG_DEBUG, "pub:payload:%s", str);
    ipc_session_publish(sesson, topic, (unsigned char *)str, (int)strlen(str));

    if (str) {
        free(str);  str = NULL;
    }

    return 0;
}

static int resp_finish_to_platform(ipc_session_t *sesson, int mi)
{
    if (sesson == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    } 

    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        dy_syslog(LOG_ERR, "cJSON_CreateObject failed!!!");
        return -1;
    }    

    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/gateway/device/%s/service/%s", sn, sn, "Set_RelIpkUpdateFinish");
    dy_syslog(LOG_DEBUG, "pub:topic  :%s", topic);
    dy_syslog(LOG_DEBUG, "pub:payload:%s", str);
    ipc_session_publish(sesson, topic, (unsigned char *)str, (int)strlen(str));

    if (str) {
        free(str);  str = NULL;
    }

    return 0;
}

static int fill_work_version(ipk_s *ipk, const char *work_version)
{
    FILE *fp = NULL;
    const char *token = " - ";
    char *work_ver = NULL;
    char cmd[MAX_CMD_LEN] = {0};
    char buf[MAX_LINE_LEN] = {0};
    char front_token[MAX_SUB_STRING_LEN] = {0};
    char behind_token[MAX_SUB_STRING_LEN] = {0};

    if (ipk == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    if (work_version != NULL) {
        strncpy(ipk->work_ver, work_version, sizeof(ipk->work_ver) - 0x1);
		ipk->work_ver[sizeof(ipk->work_ver) - 0x1] = '\0';
        return 0;
    }

    snprintf(cmd, MAX_CMD_LEN, "opkg list-installed | grep %s", ipk->name);
    dy_syslog(LOG_INFO, "cmd: %s", cmd);

    fp = popen(cmd, "r");
    if (fp == NULL) {
        dy_syslog(LOG_ERR, "fopen: %s", strerror(errno));
        return -1;
    }
    if (fgets(buf, sizeof(buf), fp) == NULL) {
        dy_syslog(LOG_ERR, "buf is empty!");
        return -1;
    }
    
    if(pclose(fp) == -1) {
        dy_syslog(LOG_ERR, "pclose failed!!!\n");
        return -1;
    }

    dy_syslog(LOG_INFO, "buf: %s", buf);
    if (split(buf, token, front_token, behind_token) == -1) {
        dy_syslog(LOG_ERR, "split failed!");
        return -1;
    }
    dy_syslog(LOG_INFO, "front_token: %s", front_token);
    dy_syslog(LOG_INFO, "behind_token: %s", behind_token);
    work_ver = behind_token;
    strncpy(ipk->work_ver, work_ver, sizeof(ipk->work_ver) - 0x1);
	ipk->work_ver[sizeof(ipk->work_ver) - 0x1] = '\0';

    return 0;
}

static int exec_ipk_install(const char *cmd)
{
    if (cmd == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    int status = system(cmd);
    if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == 0) {
            dy_syslog(LOG_INFO, "%s successfully!\n", cmd);
            usleep(200 * 1000);
            return 0;
        } else {
            dy_syslog(LOG_INFO, "%s failed!!!", cmd);
            return -1;
        }
    } else {
        // killed
        dy_syslog(LOG_INFO, "%s failed!!! killed", cmd);
        return -1;
    }
}

static _Bool upgrade_restart(void)
{
    int i = 0;
    int cnt = 0;
    FILE *fp = NULL;
    char cmd[MAX_CMD_LEN] = {0};
    char buf[MAX_LINE_LEN] = {0};
    snprintf(cmd, MAX_CMD_LEN, "ps | grep /app/dy_upgrade");

    for (i=0; i<10; i++) {
        fp = popen(cmd, "r");
        if (fp == NULL) {
            dy_syslog(LOG_ERR, "fopen: %s", strerror(errno));
            return false;
        }

        while (fgets(buf, sizeof(buf), fp) != NULL) {
            cnt++;
            dy_syslog(LOG_INFO, "cnt: %d\n", cnt);
            dy_syslog(LOG_INFO, "buf: %s\n", buf);
        }

        if(pclose(fp) == -1) {
            dy_syslog(LOG_ERR, "pclose failed!!!\n");
            return -1;
        }

        if (cnt == 4) {
            dy_syslog(LOG_INFO, "cnt == 4");
            return true;
        } else {
            dy_syslog(LOG_INFO, "cnt != 4, cnt: %d", cnt);
            cnt = 0;
            usleep(500 * 1000);
        }
    }

    return false;
}

static int exec_install_upgrade_ipk(ipk_s *ipk, int mi)
{
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork failed");
        return -1;
    }
    if (pid == 0) {
        upgrade_var_t var_child = {0};
        char clientId[MAX_CLIENT_ID_LEN] = {0};
        char cmd[MAX_CMD_LEN] = {0};

        snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_upgrade_child_process");
        var_child.session = ipc_session_new(clientId, (void*)(&var_child), IPC_DEFAULT);
        if (var_child.session == NULL) {
            exit(1);
        }
        ipc_session_start(var_child.session);

        snprintf(cmd, MAX_CMD_LEN, "/bin/opkg install upgrade");
        dy_syslog(LOG_INFO, "cmd: %s", cmd);

        if (exec_ipk_install(cmd) == -1) {
            dy_syslog(LOG_ERR, "%s failed!!!\n", cmd);
            resp_install_to_platform(var_child.session , ipk->name, ipk->expect_ver, NULL, 1, "install failed", mi);
            exit(1);
        }
        fill_work_version(ipk, NULL);
        resp_install_to_platform(var_child.session, ipk->name, ipk->expect_ver, ipk->work_ver, 0, "install success", mi);
        if(upgrade_restart()) {
            exit(0);
        } else {
            resp_install_to_platform(var_child.session, ipk->name, ipk->expect_ver, ipk->work_ver, 2, "upgrade restart failed", mi);
            exit(1);
        }
        exit(0);
    } else {
        int i;
        for(i=0; i<20; i++) {
            dy_syslog(LOG_INFO, "Waiting for reinstall\n, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(2);
        }
        exit(0);
    }
}

static int upgrade_upgrade_ipk_self(ipc_session_t *sesson, struct list_head *head, int mi)
{
    FILE *fp = NULL;
    const char *token = " - ";
    char *local_version = NULL;
    char cmd[MAX_CMD_LEN] = {0};
    char buf[MAX_LINE_LEN] = {0};
    char front_token[MAX_SUB_STRING_LEN] = {0};
    char behind_token[MAX_SUB_STRING_LEN] = {0};
    ipk_s *ipk = NULL;

    if (sesson == NULL || head == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    list_for_each_entry(ipk, head, list) {
        if (strcmp(ipk->name, "upgrade") != 0) {
            continue;
        }

        snprintf(cmd, MAX_CMD_LEN, "opkg list-installed | grep %s", ipk->name);
        dy_syslog(LOG_INFO, "cmd: %s", cmd);

        fp = popen(cmd, "r");
        if (fp == NULL) {
            dy_syslog(LOG_ERR, "fopen: %s", strerror(errno));
            return -1;
        }

        // fgets() will return !NULL always unless failed, because the code is called in upgrade
        if (fgets(buf, sizeof(buf), fp) == NULL) {
            dy_syslog(LOG_ERR, "%s failed!!!\n", cmd);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, NULL, 1, "install failed", mi);
            return -1;
        }

        if(pclose(fp) == -1) {
            dy_syslog(LOG_ERR, "pclose failed!!!\n");
            return -1;
        }

        if (split(buf, token, front_token, behind_token) == -1) {   
            dy_syslog(LOG_ERR, "split failed!!!");
            return -1;
        }
        dy_syslog(LOG_INFO, "front_token: %s", front_token);
        dy_syslog(LOG_INFO, "behind_token: %s", behind_token);
        local_version = behind_token;

        if (strcmp(ipk->expect_ver, local_version) == 0) {
            fill_work_version(ipk, local_version);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 0, "the latest version, no need to upgrade", mi);
            return 0;
        } else if (strcmp(ipk->expect_ver, local_version) > 0) {
            memset(cmd, 0, sizeof(cmd));
            snprintf(cmd, MAX_CMD_LEN, "/bin/opkg install %s", ipk->name);
            dy_syslog(LOG_INFO, "cmd: %s", cmd);
            if (exec_install_upgrade_ipk(ipk, mi) == -1) {
                dy_syslog(LOG_ERR, "%s failed!!!\n", cmd);
                resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, NULL, 1, "install failed", mi);
                return -1;
            }
        } else {
            // downgrade, 暂时不考虑
            fill_work_version(ipk, local_version);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 1, "downgrade is not supported", mi);
        }
	}

    return 0;
}

static int upgrade_other_ipks(ipc_session_t *sesson, struct list_head *head, int mi)
{
    FILE *fp = NULL;
    const char *token = " - ";
    char *local_version = NULL;
    char cmd[MAX_CMD_LEN] = {0};
    char buf[MAX_LINE_LEN] = {0};
    char front_token[MAX_SUB_STRING_LEN] = {0};
    char behind_token[MAX_SUB_STRING_LEN] = {0};
    ipk_s *ipk = NULL;

    if (sesson == NULL || head == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    list_for_each_entry(ipk, head, list)
	{
        memset(cmd, 0, sizeof(cmd));
        memset(buf, 0, sizeof(buf));
        memset(front_token, 0, sizeof(front_token));
        memset(behind_token, 0, sizeof(behind_token));

        if (strcmp(ipk->name, "upgrade") == 0) {
            continue;
        }

        snprintf(cmd, MAX_CMD_LEN, "opkg list-installed | grep %s", ipk->name);
        dy_syslog(LOG_INFO, "cmd: %s", cmd);


        fp = popen(cmd, "r");
        if (fp == NULL) {
            dy_syslog(LOG_ERR, "fopen: %s", strerror(errno));
            return -1;
        }

        // The ipk has never been installed
        if (fgets(buf, sizeof(buf), fp) == NULL) {
            if(pclose(fp) == -1) {
                dy_syslog(LOG_ERR, "pclose failed!!!\n");
                return -1;
            }

            memset(cmd, 0, sizeof(cmd));
            snprintf(cmd, MAX_CMD_LEN, "/bin/opkg install %s", ipk->name);
            dy_syslog(LOG_INFO, "cmd: %s", cmd);
            if (exec_ipk_install(cmd) == -1) {
                dy_syslog(LOG_ERR, "%s failed!!!\n", cmd);
                resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, NULL, 1, "install failed", mi);
                continue;
            }

            fill_work_version(ipk, NULL);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 0, "install success", mi);

            continue;
        }

        if(pclose(fp) == -1) {
            dy_syslog(LOG_ERR, "pclose failed!!!\n");
            return -1;
        }

        split(buf, token, front_token, behind_token);
        local_version = behind_token;

        if (strcmp(ipk->expect_ver, local_version) == 0) {
            fill_work_version(ipk, local_version);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 0, "the latest version, no need to upgrade", mi);
            continue;
        } else if (strcmp(ipk->expect_ver, local_version) > 0) {
            memset(cmd, 0, sizeof(cmd));
            snprintf(cmd, MAX_CMD_LEN, "/bin/opkg install %s", ipk->name);
            dy_syslog(LOG_INFO, "cmd: %s", cmd);
            if (exec_ipk_install(cmd) == -1) {
                dy_syslog(LOG_ERR, "%s failed!!!\n", cmd);
                resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, NULL, 1, "install failed", mi);
                continue;
            }
            fill_work_version(ipk, NULL);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 0, "install success", mi);
        } else {
            // downgrade, 暂时不考虑
            fill_work_version(ipk, local_version);
            resp_install_to_platform(sesson, ipk->name, ipk->expect_ver, ipk->work_ver, 1, "downgrade is not supported", mi);
        }

	}

    return 0;
}

static int get_file_packages(const char *url_dir, const char *packages)
{
    const char *packages_gz = "/tmp/ipks_upgrade/Packages.gz";

    if (url_dir == NULL || packages == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    if (remove_file(packages) == -1) {
        return -1;
    }
    if (remove_file(packages_gz) == -1) {
        return -1;
    }
    if (download_packages_gz(url_dir) == -1) {
        return -1;
    }
    if (unzip_packages_gz(packages_gz) == -1) {
        return -1;
    }

    return 0;
}

static int update_opkg_distfeeds_conf(const char *url_dir)
{
    char cmd[MAX_CMD_LEN] = {0};

    if (url_dir == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    snprintf(cmd, MAX_CMD_LEN, "/app/upgrade.sh update_opkg_conf %s", url_dir);
    return call_system(cmd);
}

static void traverse_linked_list(struct list_head *head)
{
    ipk_s *ipk = NULL;

    if (head == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return;
    }

    list_for_each_entry(ipk, head, list) {
        dy_syslog(LOG_INFO, "name: %s\n", ipk->name);
        dy_syslog(LOG_INFO, "expect_ver: %s\n", ipk->expect_ver);
    }
}

static void free_list(struct list_head *head)
{
    ipk_s *ipk = NULL;
    ipk_s *tmp = NULL;

    if (head == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return;
    }

    if (list_empty(head)) {
        return;
    }

    list_for_each_entry_safe(ipk, tmp, head, list)
    {
        list_del(&ipk->list);
        free(ipk);
    }
}

void install_rel_ipks(upgrade_var_t *var, ipc_msg_t *mqtt_msg)
{
    cJSON *root = NULL;
    char *url_dir = NULL;
    const char *packages = "/tmp/ipks_upgrade/Packages";
    int mi = 0;

    ipk_s ipk = {0};
    memset(&ipk, 0, sizeof(ipk));
    INIT_LIST_HEAD(&(ipk.list));

    root = cJSON_Parse(mqtt_msg->payload);
    if (!root)
    {
        dy_syslog(LOG_WARNING, "parse payload failed");
    }
    GET_JSON_VALUE_DY_STRING(root, "url_dir", url_dir);
    GET_JSON_VALUE_INT(root, "mi", mi);
    cJSON_Delete(root);

    dy_syslog(LOG_INFO, "url_dir: %s", url_dir ? : "nullptr");
    
    if (update_opkg_distfeeds_conf(url_dir) == -1) {
        resp_start_to_platform(var->session, 1, "upgrade opkg distfeeds conf failed!", mi);
        goto out;
    }
    if (exec_opkg_update() == -1) {
        resp_start_to_platform(var->session, 2, "excute opkg update failed!", mi);
        goto out;
    }
    if (get_file_packages(url_dir, packages) == -1) {
        resp_start_to_platform(var->session, 3, "get file packages failed!", mi);
        goto out;
    }
    if (extract_ipks_name_ver(&ipk.list, packages) == -1) {
        resp_start_to_platform(var->session, 4, "extract ipks name ver failed!", mi);
        goto out;
    }

    if (resp_start_to_platform(var->session, 0, "release ipk update start success!", mi)) {
        goto out;
    }

    if (list_empty(&ipk.list)) {
        goto out;
    }

    traverse_linked_list(&ipk.list);

    if (upgrade_upgrade_ipk_self(var->session, &ipk.list, mi) == -1) {
        goto out;
    }
    if (upgrade_other_ipks(var->session, &ipk.list, mi) == -1) {
        goto out;
    }

out:
    if (url_dir) {
        free(url_dir);  url_dir = NULL;
    }
    free_list(&ipk.list);
    resp_finish_to_platform(var->session, mi);
}
