/*
 * tools.c: SWMOD deamon
 *
 * Copyright (C) 2020-2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *
 * See LICENSE file for license related information.
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif

#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <uuid/uuid.h>
#include <stdarg.h>
#include <libubus.h>
#include <json-c/json.h>
#include <libubox/blobmsg_json.h>
#include <curl/curl.h>

#include "swmod_uci.h"
#include "swmod_opkg.h"
#include "opkg_utils.h"
#include "swmod_host.h"
#include "tools.h"
#include "swmod_api.h"

#define DEFAULT_LOG_LEVEL (LOG_INFO)

static unsigned char gLogLevel = DEFAULT_LOG_LEVEL;

int get_requested_container_state(char *state)
{
	if (strcmp(state, "start") == 0) {
		return CONT_START;
	} else if (strcmp(state, "stop") == 0) {
		return CONT_STOP;
	} else if (strcmp(state, "pause") == 0) {
		return CONT_PAUSE;
	} else if (strcmp(state, "resume") == 0) {
		return CONT_RESUME;
	} else {
		return __CONT_STATE_MAX;
	}
}

char ee_set_state_error_msg[__ERROR_MAX][MAX_LEN_64] = {
	"",
	"internal failure",
	"LXC not supported",
	"ExecEnv not found",
	"ExecEnv already running",
	"ExecEnv not running",
	"invalid request",
	"CRUN not supported",
};

const char *ee_state_failure_reason(int err)
{
	if (err >= __ERROR_MAX || err < 0)
		return "unknown";

	return ee_set_state_error_msg[err];
}

// Logging utilities
void configure_debug_level(unsigned char level)
{
	if (level > LOG_DEBUG) {
		fprintf(stderr, "Configured log level %d invalid, setting default level", level);
		gLogLevel = DEFAULT_LOG_LEVEL;
	} else {
		gLogLevel = level;
	}
}

void print_error(const char *format, ...)
{
	va_list arglist;

	if (gLogLevel < LOG_ERR)
		return;

	va_start(arglist, format);
	vsyslog(LOG_ERR, format, arglist);
	va_end(arglist);
}

void print_warning(const char *format, ...)
{
	va_list arglist;

	if (gLogLevel < LOG_WARNING)
		return;

	va_start(arglist, format);
	vsyslog(LOG_WARNING, format, arglist);
	va_end(arglist);
}

void print_info(const char *format, ...)
{
	va_list arglist;

	if (gLogLevel < LOG_INFO)
		return;

	va_start(arglist, format);
	vsyslog(LOG_INFO, format, arglist);
	va_end(arglist);
}

void print_debug(const char *format, ...)
{
	va_list arglist;

	if (gLogLevel < LOG_DEBUG)
		return;

	va_start(arglist, format);
	vsyslog(LOG_DEBUG, format, arglist);
	va_end(arglist);
}

bool file_exists(const char *path)
{
	struct stat statbuf;

	return stat(path, &statbuf) == 0;
}

unsigned long get_file_size_kb(const char *path)
{
	struct stat statbuf;
	if (0 == stat(path, &statbuf)) {
		unsigned long size = (unsigned long)statbuf.st_size/1024;
		return size;
	}

	return 0;
}

void remove_new_line(char *buf)
{
	int len;

	if (buf == NULL)
		return;

	len = strlen(buf) - 1;
	if (buf[len] == '\n')
		buf[len] = 0;
}

void create_file(const char *path)
{
	if (!file_exists(path)) {
		/*
		 *File does not exist
		 */
		FILE *fp = fopen(path, "w"); // new empty file
		if (fp) {
			fclose(fp);
		}
		sync();
	}
}

bool dir_exist(const char *dir)
{
	struct stat sb;

	if (stat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
		return true;
	}

	return false;
}

bool create_dir(const char *path)
{
	if (dir_exist(path) == true)
		return true;

	/* not present so create new */
	if (0 != mkdir(path, 0700))
		return false;

	return true;
}

void swmod_strncpy(char *dst, const char *src, size_t n)
{
	strncpy(dst, src, n - 1);
	dst[n - 1] = 0;
}

char *generate_uuid(void)
{
	uuid_t binuuid;

	uuid_generate_random(binuuid);
	char *uuid = malloc(37);
	uuid_unparse(binuuid, uuid);

	return uuid;
}

char *generate_duid(bool sysnchronise, int number)
{
	const char buf[] = "0123456789abcdefghijklmnopqrstuvwxyz";
	int div = sizeof(buf) - 1;
	char euid[16] = {0};
	int i;

	srand(time(NULL));
	if (sysnchronise) {
		char euid_num[8] = {0};

		for (i = 0; i < 3; i++)
			euid[i] = buf[rand() % div]; //cppcheck-suppress cert-MSC30-c
		snprintf(euid_num, sizeof(euid_num), "%04d", number);
		strncat(euid, euid_num, 4);
	} else {
		for (i = 0; i < 7; i++)
			euid[i] = buf[rand() % div]; //cppcheck-suppress cert-MSC30-c
	}
	euid[7] = '\0';

	return strdup(euid);
}

/*
 * Description: Run a system command using popen
 *
 * input:
 * // cmd: command to run
 *
 * ouput:
 * // ouput: output of the executed command will be returned to output buffer
 * // out_len: Max length of output buffer
 *
 * returns:
 * // -1: In case of faults
 * // 0: In case of success
 */
int run_cmd(const char *cmd, char *output, size_t out_len)
{
	int ret = -1;
	FILE *pp = NULL; // program pointer

	if (cmd == NULL) // null command to run, silently ignore
		return 0;

	pp = popen(cmd, "r"); // flawfinder: ignore
	if (pp == NULL) {
		fprintf(stderr, "Failed to run [%s]", cmd);
		return -1;
	}

	if (output == NULL || out_len == 0) {
		ret = pclose(pp);
		return ret;
	}

	// init the buffer
	memset(output, 0, out_len);
	if (pp != NULL) {
		char line[512] = {0};

		while(fgets(line, sizeof(line), pp) != NULL) {
			int line_len = strlen(line);
			if (strlen(output) + line_len < out_len) {
				strncat(output, line, line_len);
			}
		}
		pclose(pp);
		ret = 0;
	}

	return ret;
}

/* when command has no output or output is not needed */
int run_cmd_no_output(const char *cmd)
{
	return run_cmd(cmd, NULL, 0);
}

int get_env_type(const char *type)
{
	if (strcmp(type, "lxc") == 0)
		return EE_TYPE_LXC;
	else if (strcmp(type, "host") == 0)
		return EE_TYPE_HOST;
	else
		return EE_TYPE_UNKNOWN;
}

void sync_eeid_with_uci(struct list_head *ee_list, ExecEnv_t *environments, const char *lxc_uci_path)
{
	if (list_empty(ee_list))
		return;

	if (lxc_uci_path == NULL)
		return;

	if (strlen(lxc_uci_path) == 0)
		return;

	if (0 != swmod_uci_init(lxc_uci_path)) {
		return;
	}

	char eeid_str[12] = {0};
	unsigned int new_eeid = 3;
	struct uci_section *s = NULL, *stmp = NULL;
	int index = 0;
	bool global_context_exist = false;

	swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "globals", stmp, s) {
		if (strcmp(s->e.name, "globals") == 0) {
			const char *next_eeid = swmod_uci_get_value_by_section(s, "next_eeid");
			if (next_eeid != NULL && strlen(next_eeid) != 0) {
				new_eeid = (unsigned int)strtoul(next_eeid, NULL, 10);
			}

			global_context_exist = true;
			break;
		}
	}

	/* check the ee that has valid eeid, then put it in g_environments (upto MAX_ENV) */
	ExecEnvNode *iter = NULL;
	list_for_each_entry(iter, ee_list, list) {
		if (iter->env.eeid != 0) {
			if (index < MAX_ENV) {
				memcpy(&environments[index], &iter->env, sizeof(ExecEnv_t));
				index ++;
			}
		}
	}

	/* now those have no eeid, initialize eeid and put in g_environments (upto MAX_ENV),
	 * as well make entry in uci file.
	 */
	iter = NULL;
	list_for_each_entry(iter, ee_list, list) {
		if (index == MAX_ENV)
			break;

		if (iter->env.eeid == 0) {
			struct uci_section *sc = NULL, *sctmp = NULL;
			swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "container", sctmp, sc) {
				const char *name = swmod_uci_get_value_by_section(sc, "name");
				const char *type = swmod_uci_get_value_by_section(sc, "type");
				if (strcmp(name, iter->env.name) == 0 && iter->env.ee_type == get_env_type(type)) {
					iter->env.eeid = new_eeid;
					new_eeid = new_eeid + 1;

					snprintf(eeid_str, sizeof(eeid_str), "%u", iter->env.eeid);
					swmod_uci_set_value_by_section(sc, "eeid", eeid_str);
					memcpy(&environments[index], &iter->env, sizeof(ExecEnv_t));
					index ++;
					break;
				}
			}
		}
	}

	PRINT_DEBUG("Total %d number of ExecEnv populated", index);
	/* now keep the record for next eeid, so that if any ee is removed or added then the
	 * eeid of newly created ee shall be synchronised */
	if (global_context_exist == false) {
		s = swmod_uci_add_section(SWMOD_LXC_DU_UCI, "globals", true);
	}

	snprintf(eeid_str, sizeof(eeid_str), "%u", new_eeid);
	swmod_uci_set_value_by_section(s, "next_eeid", eeid_str);

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
}

void set_autoboot_to_config_file(const char *env, bool state, const char *lxc_uci_path)
{
	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0)
		return;

	if (0 != swmod_uci_init(lxc_uci_path))
		return;

	struct uci_section *s = NULL;
	swmod_uci_foreach_section(SWMOD_LXC_DU_UCI, "container", s) {
		const char *name = swmod_uci_get_value_by_section(s, "name");
		const char *type = swmod_uci_get_value_by_section(s, "type");
		if (strcmp(name, env) == 0 && strcmp(type, "lxc") == 0) {
			swmod_uci_set_value_by_section(s, "autostart", state ? "1" : "0");
			swmod_uci_set_value_by_section(s, "timeout", "300");
			break;
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
}

bool get_autoboot_from_config_file(const char *env, const char *lxc_uci_path)
{
	bool found = false;

	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0)
		return found;

	if (swmod_uci_init(lxc_uci_path) != 0)
		return found;

	struct uci_section *s = NULL;
	swmod_uci_foreach_section(SWMOD_LXC_DU_UCI, "container", s) {
		const char *name = swmod_uci_get_value_by_section(s, "name");
		const char *type = swmod_uci_get_value_by_section(s, "type");
		const char *autostart = swmod_uci_get_value_by_section(s, "autostart");
		if (strcmp(name, env) == 0 && strcmp(type, "lxc") == 0 && uci_str_to_bool(autostart)) {
			found = true;
			break;
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
	return found;
}

int get_pid_details(int pid, char *cmdline, int cmd_len, int *vsize)
{
	char fcmd[512];
	char fstat[512];
	unsigned int bsize = 0;
	int spid, ppid;
	FILE *fp;

	if (cmdline == NULL || vsize == NULL || cmd_len < MAX_LEN_32)
		return 0;

	snprintf(fcmd, sizeof(fcmd), "%s/%d/cmdline", PROC_PATH, pid);

	*vsize = 0;
	strncpy(cmdline, "", cmd_len);

	fp = fopen(fcmd, "r");
	if (fp == NULL)
		return 0;

	if (fgets(cmdline , 31 , fp) == NULL) {
		fclose(fp);
		return 0;
	}
	fclose(fp);

	snprintf(fstat, sizeof(fstat), "%s/%d/stat", PROC_PATH, pid);
	if (file_exists(fstat) == false)
		return 0;

	fp = fopen(fstat, "r");
	if (fp == NULL)
		return 0;

	int ret = fscanf(fp, "%d %*s %*c %d %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %u",
			&spid,
			&ppid,
			&bsize);

	if (ret != 3) {
		fclose(fp);
		return 0;
	}

	fclose(fp);
	*vsize = bsize / 1024;

	return 0;
}

int wait_for_pid(pid_t pid)
{
	int status, ret;

again:
	ret = waitpid(pid, &status, 0);
	if (ret == -1) {
		if (errno == EINTR)
			goto again;

		return -1;
	}

	if (ret != pid)
		goto again;

	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
		return -1;

	return 0;
}

int update_eu_from_blob(ExecEnv_t *env, struct blob_attr *msg, ExecUnit *node)
{
	struct blob_attr *service, *instances, *inst;
	size_t rem, rem2, rem3;
	enum {
		P_RUN,
		P_PID,
		__P_MAX
	};
	const struct blobmsg_policy p[__P_MAX] = {
		{ "running", BLOBMSG_TYPE_BOOL },
		{ "pid", BLOBMSG_TYPE_INT32 },
	};

	if (env == NULL) {
		PRINT_ERR("Environment info not provided");
		return -1;
	}

	if (env->exists == false) {
		PRINT_ERR("Environment not exists");
		return -1;
	}

	if (node == NULL) {
		PRINT_ERR("Node not provided");
		return -1;
	}

	blobmsg_for_each_attr(service, msg, rem) {
		blobmsg_for_each_attr(instances, service, rem2) {
			memset(node, 0, sizeof(ExecUnit));
			node->eu_exists = true;
			swmod_strncpy(node->name, blobmsg_name(service), sizeof(node->name));
			swmod_strncpy(node->environment, env->name, sizeof(node->environment));
			node->disk_space = -1;

			blobmsg_for_each_attr(inst, instances, rem3) {
				struct blob_attr *tb[__P_MAX] = {NULL};

				if (blobmsg_parse(p, __P_MAX, tb, blobmsg_data(inst), blobmsg_len(inst)) != 0)
					continue;

				if (tb[P_PID]) {
					int euid = blobmsg_get_u32(tb[P_PID]);
					snprintf(node->euid, sizeof(node->euid), "%d", euid);
					swmod_get_pid_detail(env, euid, node->command, sizeof(node->command), &node->memory_space);
				} else {
					snprintf(node->euid, sizeof(node->euid), "Unknown");
				}

				if (tb[P_RUN] && blobmsg_get_bool(tb[P_RUN])) {
					swmod_strncpy(node->state, "Active", sizeof(node->state));
				} else {
					swmod_strncpy(node->state, "Idle", sizeof(node->state));
				}

				swmod_strncpy(node->vendor, "", sizeof(node->vendor));  // TODO
				break;
			}

			return 0;
		}
	}
	/*
	swmod_strncpy(exec_units[0].version[nbr], version, 32);
	exec_units[0].disk_space[nbr] = disk_space;
	swmod_strncpy(exec_units[0].description[nbr], spch+14, 1024);
	swmod_strncpy(exec_units[0].config[nbr], spch+12, 32);
	*/
	return -1;
}

int ubus_call_service_state(char *service, bool state)
{
	uint32_t id;
	int rc = -1;

	/* This function can be called inside LXC, so need a new ubux ctx */
	struct ubus_context *ctx = ubus_connect(NULL);
	if (ctx == NULL) {
		return rc;
	}

	struct blob_buf b;
	memset(&b, 0, sizeof(struct blob_buf));

	blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "spawn", state);
	blobmsg_add_string(&b, "name", service);

	if (!ubus_lookup_id(ctx, "service", &id))
		rc = ubus_invoke(ctx, id, "state", b.head, NULL, NULL, UBUS_TIMEOUT);

	blob_buf_free(&b);
	ubus_free(ctx);

	return rc;
}

int get_timeout_from_config_file(const char *env, const char *lxc_uci_path)
{
	long timeout = 300; /* default timeout */

	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0) {
		return timeout;
	}

	if (swmod_uci_init(lxc_uci_path) != 0) {
		return timeout;
	}

	struct uci_section *s = NULL;
	swmod_uci_foreach_section(SWMOD_LXC_DU_UCI, "container", s) {
		const char *name = swmod_uci_get_value_by_section(s, "name");
		const char *type = swmod_uci_get_value_by_section(s, "type");
		if (strcmp(name, env) == 0 && strcmp(type, "lxc") == 0) {
			const char *time = swmod_uci_get_value_by_section(s, "timeout");
			if (time != NULL && time[0] != '\0') {
				timeout = strtol(time, NULL, 10);
			}
			break;
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
	return timeout;
}

void swmod_add_ee_in_list(struct list_head *ee_list, ExecEnv_t *ee)
{
	if (ee == NULL) {
		return;
	}

	ExecEnvNode *node = (ExecEnvNode *)malloc(sizeof(ExecEnvNode));
	if (node == NULL) {
		PRINT_ERR("Out of memory");
		return;
	}
	memset(node, 0, sizeof(ExecEnvNode));
	memcpy(&node->env, ee, sizeof(ExecEnv_t));
	INIT_LIST_HEAD(&node->list);
	list_add_tail(&node->list, ee_list);
}

void swmod_delete_ee_list(struct list_head *ee_list)
{
	ExecEnvNode *iter = NULL, *node = NULL;

	list_for_each_entry_safe (iter, node, ee_list, list) {
		list_del(&iter->list);
		free(iter);
	}
}

void swmod_add_eu_in_list(struct list_head *eu_list, ExecUnit *eu)
{
	if (eu == NULL) {
		return;
	}

	EuNode *node = (EuNode *)malloc(sizeof(EuNode));
	if (node == NULL) {
		PRINT_ERR("Out of memory");
		return;
	}

	memset(node, 0, sizeof(EuNode));
	memcpy(&node->eu, eu, sizeof(ExecUnit));
	INIT_LIST_HEAD(&node->list);
	list_add_tail(&node->list, eu_list);
}

void swmod_delete_eu_list(struct list_head *eu_list)
{
	EuNode *iter = NULL, *node;

	list_for_each_entry_safe (iter, node, eu_list, list) {
		list_del(&iter->list);
		free(iter);
	}
}

void get_swmod_config_params(ConfigParams *cfg)
{
	const char *root = NULL;
	char bundle_root[MAX_LEN_64] = {0};

	if (cfg == NULL)
		exit(0);

	if (swmod_uci_init(STD_UCI_PATH) != 0)
		exit(0);

	struct uci_section *s = NULL;
	swmod_uci_foreach_section(SWMOD_UCI_FILE, "globals", s) {
		if (strcmp(s->e.name, "globals") == 0) {
			root = swmod_uci_get_value_by_section(s, "root");
			break;
		}
	}

	int root_len = (root != NULL) ? strlen(root) : 0;
	if (root_len == 0)
		exit(0);

	s = NULL;
	swmod_uci_foreach_section(SWMOD_UCI_FILE, "execenv", s) {
		const char *name = swmod_uci_get_value_by_section(s, "name");

		if (name == NULL || strlen(name) == 0)
			continue;

		if (root[root_len - 1] == '/')
			snprintf(bundle_root, MAX_LEN_64, "%s%s", root, name);
		else
			snprintf(bundle_root, MAX_LEN_64, "%s/%s", root, name);

		break;
	}

	swmod_uci_fini(SWMOD_UCI_FILE);

	if (strlen(bundle_root) == 0 || !dir_exist(bundle_root))
		exit(0);

	snprintf(cfg->oci_bundle_root, MAX_LEN_64, "%s", bundle_root);
	snprintf(cfg->lxc_bundle_root, MAX_LEN_64, "%s", bundle_root);

	char oci_du_uci[MAX_LEN_256] = {0};
	snprintf(oci_du_uci, sizeof(oci_du_uci), "%s/%s", cfg->oci_bundle_root, SWMOD_OCI_DU_UCI);
	if (!file_exists(oci_du_uci)) {
		/* oci du uci file not present so create an empty one */
		FILE *fp = fopen(oci_du_uci, "w");
		if (fp == NULL)
			exit(0);
		fclose(fp);
	}

	char lxc_du_uci[MAX_LEN_256] = {0};
	snprintf(lxc_du_uci, sizeof(lxc_du_uci), "%s/%s", cfg->lxc_bundle_root, SWMOD_LXC_DU_UCI);
	if (!file_exists(lxc_du_uci)) {
		/* lxc container uci file not present so create an empty one */
		FILE *fp = fopen(lxc_du_uci, "w");
		if (fp == NULL)
			exit(0);
		fclose(fp);
	}

	return;
}

static bool validate_uuid_in_uci(const char *uuid, const char *uci_path, const char *uci_file)
{
	bool ret = true;

	if (uuid == NULL || uci_path == NULL || uci_file == NULL ||
			strlen(uci_file) == 0 || strlen(uci_path) == 0) {
		return !ret;
	}

	if (swmod_uci_init(uci_path) != 0)
		return !ret;

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(uci_file, "du_eu_assoc", stmp, s) {
		char *id = swmod_uci_get_value_by_section(s, "uuid");
		if (strcmp(id, uuid) == 0) {
			ret = false;
			break;
		}
	}
	swmod_uci_fini(uci_file);

	return ret;
}

bool valid_uuid(char *uuid, const char *lxc_uci_path, const char *oci_uci_path)
{
	bool ret = true;

	if (uuid == NULL)
		return !ret;

	int len = strlen(uuid);
	if (len != 36 && len != 0)
		return !ret;

	if (len != 0) {
		/* check if already exists */
		if (lxc_uci_path != NULL && strlen(lxc_uci_path) != 0) {
			ret = validate_uuid_in_uci(uuid, lxc_uci_path, SWMOD_LXC_DU_UCI);
		}

		if (oci_uci_path != NULL && strlen(oci_uci_path) != 0) {
			ret &= validate_uuid_in_uci(uuid, oci_uci_path, SWMOD_OCI_DU_UCI);
		}
	}

	return ret;
}

void get_opkg_description(ExecEnv_t *env, const char *pkg_name, char *desc, size_t desc_size)
{
	char cmd[MAX_LEN_256];
	char output[MAX_LEN_1024];

	if (env == NULL || pkg_name == NULL || desc == NULL)
		return;

	memset(desc, 0, desc_size);
	memset(cmd, 0, sizeof(cmd));
	memset(output, 0, sizeof(output));

	snprintf(cmd, sizeof(cmd), "grep Description: /usr/lib/opkg/info/%s.control", pkg_name);
	swmod_run_cmd(env, cmd, output, sizeof(output));
	char *spch = strstr(output, "Description:");
	if (spch) {
		remove_new_line(spch);
		snprintf(desc, desc_size, "%s", spch+14);
	}
}

void buffer_add_line(struct list_head *head, char *entry)
{
	struct buffer_list *node = NULL;

	if (entry == NULL)
		return;

	node = (struct buffer_list *) malloc(sizeof(*node));
	if (!node) {
		PRINT_ERR("Out of memory!");
		return;
	}

	memset(node, 0, sizeof(*node));
	swmod_strncpy(node->line, entry, MAX_LEN_128);

	INIT_LIST_HEAD(&node->list);
	list_add_tail(&node->list, head);
}

void create_buffer_list(struct list_head *opkg_list, char *buffer)
{
	char *token = NULL;

	if (buffer == NULL)
		return;

	if (strlen(buffer) == 0)
		return;

	token = strtok(buffer, "\n");
	while (token != NULL) {
		buffer_add_line(opkg_list, token);
		token = strtok(NULL, "\n");
	}
}

void delete_buffer_list(struct list_head *opkg_list)
{
	struct buffer_list *iter, *node;

	list_for_each_entry_safe (iter, node, opkg_list, list) {
		list_del(&iter->list);
		free(iter);
	}
}

void get_opkg_service_name(ExecEnv_t *env, const char *pkg_name, char *service, size_t serv_size)
{
	char cmd[MAX_LEN_256];
	char output[MAX_LEN_1024];

	if (env == NULL || pkg_name == NULL || service == NULL)
		return;

	memset(service, 0, serv_size);
	memset(cmd, 0, sizeof(cmd));
	memset(output, 0, sizeof(output));

	snprintf(cmd, sizeof(cmd), "opkg files %s 2>&1", pkg_name);
	swmod_run_cmd(env, cmd, output, sizeof(output));

	if (strlen(output) == 0)
		return;

	LIST_HEAD(opkg_files_list);
	create_buffer_list(&opkg_files_list, output);

	if (!list_empty(&opkg_files_list)) {
		struct buffer_list *iter;

		list_for_each_entry(iter, &opkg_files_list, list) {
			char *spch = strstr(iter->line, SWMOD_INIT_PATH);
			if (spch) {
				remove_new_line(spch);
				snprintf(service, serv_size, "%s", spch+strlen(SWMOD_INIT_PATH));
				break;
			}
		}
	}

	delete_buffer_list(&opkg_files_list);
}

time_t convert_str_to_time(const char *time)
{
	unsigned long tm = 0;

	if (time != NULL)
		sscanf(time, "%lu", &tm);

	return (time_t)tm;
}

bool memory_available(unsigned long req_kb, const char *dst)
{
	bool ret = true;

	if (dst == NULL)
		return false;

	/* get the available memory */
	struct statvfs dinfo;
	if (statvfs(dst, &dinfo) == 0) {
		unsigned long free_space = (dinfo.f_bsize * dinfo.f_bavail)/1024;
		PRINT_DEBUG("Free memory: %lu Kb", free_space);
		if (req_kb >= free_space) {
			ret = false;
		}
	} else {
		ret = false;
	}

	return ret;
}

unsigned long get_remote_file_size(const char *url, const char *uname, const char *pwd)
{
	CURL *curl;
	CURLcode res;
	unsigned long file_size_kb = 0;

	if (url == NULL)
		return file_size_kb;

	curl_global_init(CURL_GLOBAL_DEFAULT);
	curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, url);
		curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
		curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
		if (uname != NULL) {
			if (uname[0] != '\0')
				curl_easy_setopt(curl, CURLOPT_USERNAME, uname);
		}

		if (pwd != NULL) {
			if (pwd[0] != '\0')
				curl_easy_setopt(curl, CURLOPT_PASSWORD, pwd);
		}

		res = curl_easy_perform(curl);

		if (CURLE_OK == res) {
			curl_off_t cl = 0;
			res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl);
			if ((res == CURLE_OK) && (cl > 0)) {
				file_size_kb = cl/1024;
				PRINT_ERR("File size: %lu Kb, raw %lu ..", file_size_kb, cl);
			}
		}

		curl_easy_cleanup(curl);
	}

	curl_global_cleanup();

	return file_size_kb;
}

int download_remote_package(const char *url, const char *username, const char *password, const char *dst)
{
	long res_code = 0;

	if (url == NULL || dst == NULL)
		return -1;

	CURL *curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, url);

		if (username != NULL) {
			if (username[0] != '\0')
				curl_easy_setopt(curl, CURLOPT_USERNAME, username);
		}

		if (password != NULL) {
			if (password[0] != '\0')
				curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
		}

		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 100);

		FILE *fp = fopen(dst, "wb");
		if (fp) {
			curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
			curl_easy_perform(curl);
			fclose(fp);
		} else {
			curl_easy_cleanup(curl);
			return -1;
		}

		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code);
		curl_easy_cleanup(curl);

		if ((strncmp(url, "http", 4) == 0 && res_code != 200) ||
			(strncmp(url, "ftp", 3) == 0 && res_code != 226) ||
			(strncmp(url, "http", 4) && strncmp(url, "ftp", 3))) {
			return -1;
		}

		return 0;
	}

	return -1;
}

int swmod_ee_uci_init(ExecEnv_t *env, char *uci_file, int len)
{
	int ret = -1;

	if (env == NULL || uci_file == NULL)
		return ret;

	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		if (strlen(swmod_config.oci_bundle_root) != 0) {
			snprintf(uci_file, len, "%s", SWMOD_OCI_DU_UCI);
			ret = swmod_uci_init(swmod_config.oci_bundle_root);
		}
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		if (strlen(swmod_config.lxc_bundle_root) != 0) {
			snprintf(uci_file, len, "%s", SWMOD_LXC_DU_UCI);
			ret = swmod_uci_init(swmod_config.lxc_bundle_root);
		}
#endif
		break;
	default:
		ret = -1;
	}

	return ret;
}

int set_du_alias_to_config(const char *ee_name, const char *uuid,
			const char *alias, const char *bundle, const char *uci_file)
{
	int ret = -1;

	if (ee_name == NULL || uuid == NULL || alias == NULL ||
			bundle == NULL || uci_file == NULL)
		return ret;

	if (strlen(bundle) == 0)
		return ret;

	if (strlen(uci_file) == 0)
		return ret;

	if (swmod_uci_init(bundle) != 0)
		return ret;

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(uci_file, "du_eu_assoc", stmp, s) {
		char *name = swmod_uci_get_value_by_section(s, "ee_name");
		char *du_uuid = swmod_uci_get_value_by_section(s, "uuid");

		if (strcmp(ee_name, name) == 0 && strcmp(uuid, du_uuid) == 0) {
			swmod_uci_set_value_by_section(s, "du_alias", alias);
			ret = 0;
			break;
		}
	}

	swmod_uci_fini(uci_file);

	return ret;
}

int set_eu_alias_to_config(const char *ee_name, const char *eu_name,
			const char *alias, const char *bundle, const char *uci_file)
{
	int ret = -1;

	if (ee_name == NULL || eu_name == NULL || alias == NULL ||
			bundle == NULL || uci_file == NULL)
		return ret;

	if (strlen(bundle) == 0)
		return ret;

	if (strlen(uci_file) == 0)
		return ret;

	if (swmod_uci_init(bundle) != 0)
		return ret;

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(uci_file, "du_eu_assoc", stmp, s) {
		char *ee = swmod_uci_get_value_by_section(s, "ee_name");
		char *eu = swmod_uci_get_value_by_section(s, "eu_name");

		if (strcmp(ee_name, ee) == 0 && strcmp(eu_name, eu) == 0) {
			swmod_uci_set_value_by_section(s, "eu_alias", alias);
			ret = 0;
			break;
		}
	}

	swmod_uci_fini(uci_file);

	return ret;
}

int set_eu_autostart_to_config(const char *ee_name, const char *eu_name,
			bool enable, const char *bundle, const char *uci_file)
{
	int ret = -1;

	if (ee_name == NULL || eu_name == NULL ||
			bundle == NULL || uci_file == NULL)
		return ret;

	if (strlen(bundle) == 0)
		return ret;

	if (strlen(uci_file) == 0)
		return ret;

	if (swmod_uci_init(bundle) != 0)
		return ret;

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(uci_file, "du_eu_assoc", stmp, s) {
		char *ee = swmod_uci_get_value_by_section(s, "ee_name");
		char *eu = swmod_uci_get_value_by_section(s, "eu_name");

		if (strcmp(ee_name, ee) == 0 && strcmp(eu_name, eu) == 0) {
			swmod_uci_set_value_by_section(s, "autostart", enable ? "1" : "0");
			ret = 0;
			break;
		}
	}

	swmod_uci_fini(uci_file);

	if (ret == 0)
		swmod_uci_commit("crun");

	return ret;
}
