/*
 * swmod_lxc.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 <stdio.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/statvfs.h>
#include <sys/utsname.h>
#include <errno.h>
#include <fcntl.h>

#include "tools.h"
#include "swmod_uci.h"
#include "swmod_opkg.h"
#include "swmod.h"
#include "swmod_lxc.h"
#include "swmod_api.h"

#define LXC_CONFIG_PATH "/etc/lxc/lxc.conf"
#define LXC_PATH "lxc.lxcpath"

static char g_lxc_buff[MAX_BUFFER_LEN];

typedef struct lxc_attach_args {
	const char *lxcpath;
	const char *value;
	int action;
	int pid;
	int fd;
	const char *user;
	const char *pwd;
	const char *service;
} lxc_attach_args;

struct service_state {
	char *name;
	bool state;
};

const char *get_lxc_path_from_config(void)
{
	return lxc_get_global_config_item(LXC_PATH);
}

bool lxc_is_supported(const char **lxcpath)
{
	if (!file_exists(LXC_CONFIG_PATH))
		return false;

	*lxcpath = get_lxc_path_from_config();

	if (!(*lxcpath) || !file_exists(*lxcpath))
		return false;

	return true;
}

static int lxc_attach_func(struct lxc_container *ct, lxc_attach_exec_t exec_function,
			   lxc_attach_args *command, bool get_output)
{
	int ret;
	pid_t pid;
	int pipefd[2];
	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
	lxc_attach_args lxc_input;

	memset(&lxc_input, 0, sizeof(lxc_attach_args));

	g_lxc_buff[0] = '\0';
	ret = pipe2(pipefd, O_NONBLOCK);
	if (ret < 0) {
		PRINT_ERR("Failed to create pipe");
		return -1;
	}

	if (command) {
		lxc_input.lxcpath = command->lxcpath;
		lxc_input.value = command->value;
		lxc_input.action = command->action;
		lxc_input.pid = command->pid;
		lxc_input.user = command->user;
		lxc_input.pwd = command->pwd;
		lxc_input.service = command->service;
	}
	lxc_input.fd = pipefd[1];

	const char *state = ct->state(ct);

	if (strcmp(state, "RUNNING") != 0) {
		PRINT_ERR("Container might be frozen");
		goto err;
	}

	ret = ct->attach(ct, exec_function, &lxc_input, &attach_options, &pid);
	if (ret < 0)
		goto err;

	wait_for_pid(pid);

	if (get_output == true) {
		ret = read(pipefd[0], &g_lxc_buff, sizeof(g_lxc_buff)-1);
		if (ret != -1) { // error on read
			g_lxc_buff[ret] = '\0';
		}
		PRINT_DEBUG("== Attach Result buff [%s] ##", g_lxc_buff);
	}

err:
	close(pipefd[0]);
	close(pipefd[1]);
	return ret;
}

static void dump_output_fd(int fd, const char *str)
{
	if (str == NULL)
		return;

	write(fd, str, strlen(str));
	close(fd);
}

/************************** Execution Environments **************************/
static int lxc_attach_run_env_func(void *args)
{
	char buff[MAX_LEN_1024];
	struct utsname utsname;
	char vendor[MAX_LEN_128] = {0}, version[MAX_LEN_16] = {0};
	lxc_attach_args *data = (lxc_attach_args *) args;

	if (uname(&utsname) >= 0) {
		swmod_strncpy(vendor, utsname.nodename, MAX_LEN_128);
		swmod_strncpy(version, utsname.release, MAX_LEN_16);
	}

	struct sysinfo sinfo;
	unsigned long alloc_mem = 0, avail_mem = 0;

	if (sysinfo(&sinfo) == 0) {
		alloc_mem = (sinfo.totalram / 1024);
		avail_mem = (sinfo.freeram / 1024);
	}

	struct statvfs dinfo;
	unsigned long allocated_disk_space = 0;
	unsigned long available_disk_space = 0;

	if (statvfs("/", &dinfo) == 0) {
		allocated_disk_space = (dinfo.f_bsize * dinfo.f_blocks) / 1024;
		available_disk_space = (dinfo.f_bsize * dinfo.f_bfree) / 1024;
	}

	/* lxc_attach_result buffer format */
	/* type=<ENV_TYPE> vendor=<ENV_VENDOR> version=<ENV_VERSION>
	 *  alloc_mem=<ENV_ALLOCATED_MEMORY> avail_mem=<ENV_AVAILABLE_MEMORY> */
	snprintf(buff, sizeof(buff), "vendor=%s version=%s alloc_mem=%lu avail_mem=%lu alloc_disk=%lu avail_disk=%lu",
			vendor, version, alloc_mem, avail_mem, allocated_disk_space, available_disk_space);

	dump_output_fd(data->fd, buff);

	return 0;
}

unsigned long long lxc_get_cgroup_item_ull(struct lxc_container *ct, const char *config)
{
	unsigned long long value = 0;
	char buffer[MAX_LEN_32] = {0};

	int res = ct->get_cgroup_item(ct, config, buffer, sizeof(buffer));
	if (res >= 0) {
		sscanf(buffer, "%llu", &value);
	}
	return value;
}

void get_lxc_environment_info(ExecEnv_t *ee, const char *lxc_uci_path)
{
	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0)
		return;

	if (ee == NULL)
		return;

	struct lxc_container *ct = lxc_container_new(ee->name, NULL);
	if (!ct) {
		swmod_strncpy(ee->status, "Error", MAX_LEN_32);
		PRINT_ERR("Failed in opening container: %s", ee->name);
		return;
	}

	bool ct_running = ct->is_running(ct);

	ee->autoboot = get_autoboot_from_config_file(ct->name, lxc_uci_path);
	swmod_strncpy(ee->status, ct_running ? "Up" : "Disabled", MAX_LEN_32);

	if (ct_running && ((strcmp(ct->state(ct), "FROZEN") == 0) ||
			strcmp(ct->state(ct), "FREEZING") == 0)) {
		ee->pause = 1;
	}

	if (!ct_running || ee->pause) {
		PRINT_INFO("lxc container not running or frozen");
		lxc_container_put(ct);
		return;
	}

	/* get memory space limit in cgroup */
	unsigned long long val = 0;
	val = lxc_get_cgroup_item_ull(ct, "memory.limit_in_bytes");
	ee->allocated_memory = val / 1024;

	/* get free space from lxc info */
	val = lxc_get_cgroup_item_ull(ct, "memory.usage_in_bytes");
	ee->available_memory = ee->allocated_memory - val / 1024;

	int ret = lxc_attach_func(ct, lxc_attach_run_env_func, NULL, true);
	if (ret >= 0 && strlen(g_lxc_buff)) {
		sscanf(g_lxc_buff, "vendor=%127s version=%15s alloc_mem=%lu avail_mem=%lu alloc_disk=%lu avail_disk=%lu",
			ee->vendor, ee->version, &ee->allocated_memory, &ee->available_memory,
				&ee->allocated_disk_space, &ee->available_disk_space);
	}

	if (ret == -1) {
		swmod_strncpy(ee->status, "Error", MAX_LEN_32);
	}

	lxc_container_put(ct);
}

void populate_lxc_environment(struct list_head *ee_head, const char *lxc_uci_path)
{
	const char *lxcpath = NULL;

	if (lxc_uci_path == NULL)
		return;

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

	if (!lxc_is_supported(&lxcpath))
		return;

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

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "container", stmp, 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 *eeid = swmod_uci_get_value_by_section(s, "eeid");
		const char *alias = swmod_uci_get_value_by_section(s, "alias");

		if (strlen(name) != 0 && strcmp(type, "lxc") == 0) {
			ExecEnv_t ee;
			memset(&ee, 0, sizeof(ExecEnv_t));

			ee.exists = true;
			ee.ee_type = EE_TYPE_LXC;
			ee.parent_eeid = PARENT_EEID;

			if (strlen(eeid) != 0)
				ee.eeid = strtol(eeid, NULL, 10);

			swmod_strncpy(ee.type, "Linux Container", MAX_LEN_64);
			swmod_strncpy(ee.name, name, MAX_LEN_32);
			swmod_strncpy(ee.alias, alias, MAX_LEN_65);
			swmod_add_ee_in_list(ee_head, &ee);
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
}

void lxc_service_get_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
	char *str;
	lxc_attach_args *input = (lxc_attach_args *) req->priv;

	str = blobmsg_format_json(msg, true);
	if (str != NULL) {
		write(input->fd, str, strlen(str));
		free(str);
	}
}

static int lxc_attach_pid(void *args)
{
	char buff[MAX_LEN_1024];
	char cmdline[MAX_LEN_32] = {0};
	int vsize = 0;
	lxc_attach_args *lxc_input = (lxc_attach_args *) args;

	get_pid_details(lxc_input->pid, cmdline, MAX_LEN_32, &vsize);
	/* lxc_attach_result buffer format */
	/* type=<ENV_TYPE> vendor=<ENV_VENDOR> version=<ENV_VERSION>
	 *  alloc_mem=<ENV_ALLOCATED_MEMORY> avail_mem=<ENV_AVAILABLE_MEMORY> */
	snprintf(buff, sizeof(buff), "cmdline=%s vsize=%d", cmdline, vsize);

	dump_output_fd(lxc_input->fd, buff);

	return 0;
}

void get_pid_details_lxc(ExecEnv_t *env, int pid, char *cmdline, int cmd_len, int *vsize)
{
	if (env == NULL || cmdline == NULL || cmd_len < MAX_LEN_32 || vsize == NULL)
		return;

	if (env->name[0] == '\0')
		return;

	struct lxc_container *ct = lxc_container_new(env->name, NULL);
	if (!ct) {
		PRINT_ERR("Failed in opening container: %s", env->name);
		return;
	}

	if (!ct->is_running(ct)) {
		lxc_container_put(ct);
		return;
	}

	lxc_attach_args lxc_data;

	memset(&lxc_data, 0, sizeof(lxc_attach_args));
	lxc_data.pid = pid;
	int ret = lxc_attach_func(ct, lxc_attach_pid, &lxc_data, true);
	if (ret >= 0 && strlen(g_lxc_buff)) {
		sscanf(g_lxc_buff, "cmdline=%31s vsize=%d", cmdline, vsize);
	}

	lxc_container_put(ct);
}

static int lxc_attach_eu_list(void *args)
{
	get_service_list("service", "list", lxc_service_get_cb, args);
	return 0;
}

void populate_lxc_eu(ExecEnv_t *env, const char *lxc_uci_path)
{
	struct blob_buf bb;

	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0) {
		PRINT_ERR("LXC DU uci path unknown");
		return;
	}

	if (env == NULL) {
		PRINT_ERR("No environment information given");
		return;
	}

	if (env->name[0] == '\0' || env->exists == false) {
		PRINT_ERR("Environment name unknown or not exists");
		return;
	}

	struct lxc_container *ct = lxc_container_new(env->name, NULL);
	if (!ct) {
		PRINT_ERR("Failed in opening container: %s", env->name);
		return;
	}

	if (!ct->is_running(ct)) {
		lxc_container_put(ct);
		return;
	}

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

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "du_eu_assoc", stmp, s) {
		const char *type = swmod_uci_get_value_by_section(s, "type");
		const char *status = swmod_uci_get_value_by_section(s, "du_status");

		if (strcmp(type, "lxc") == 0 && strcmp(status, "Installed") == 0) {
			const char *eu_name = swmod_uci_get_value_by_section(s, "eu_name");
			if (strlen(eu_name) == 0)
				continue;

			const char *ee_name = swmod_uci_get_value_by_section(s, "ee_name");
			if (strcmp(ee_name, env->name) != 0)
				continue;

			const char *req_state = swmod_uci_get_value_by_section(s, "requested_state");
			const char *auto_start = swmod_uci_get_value_by_section(s, "autostart");
			const char *du_name = swmod_uci_get_value_by_section(s, "name");
			const char *eu_alias = swmod_uci_get_value_by_section(s, "eu_alias");

			lxc_attach_args lxc_data;
			memset(&lxc_data, 0, sizeof(lxc_attach_args));
			lxc_data.service = eu_name;
			int ret = lxc_attach_func(ct, lxc_attach_eu_list, (lxc_attach_args *)&lxc_data, true);
			if (ret >= 0 && strlen(g_lxc_buff)) {
				memset(&bb, 0, sizeof(struct blob_buf));
				blob_buf_init(&bb, 0);
				blobmsg_add_json_from_string(&bb, g_lxc_buff);

				ExecUnit node;
				memset(&node, 0, sizeof(ExecUnit));
				if (0 == update_eu_from_blob(env, bb.head, &node)) {
					if (req_state)
						swmod_strncpy(node.req_state, req_state, sizeof(node.req_state));

					if (du_name)
						swmod_strncpy(node.du_name, du_name, sizeof(node.du_name));

					if (eu_alias)
						swmod_strncpy(node.eu_alias, eu_alias, sizeof(node.eu_alias));

					node.autostart = uci_str_to_bool(auto_start);

					swmod_add_eu_in_list(&env->eu_list, &node);
				}
				blob_buf_free(&bb);
			}
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);
	lxc_container_put(ct);
}

/************************** Deployment/Execution Unit **************************/
/************************** Install/Remove **************************/
static int lxc_attach_run_opkg_method_func(void *args)
{
	lxc_attach_args *data = (lxc_attach_args *)args;

	if (!data || *(data->value) == '\0') {
		PRINT_ERR("No data available");
		return -1;
	}

	int err = -1;

	char output[MAX_LEN_1024] = {0};
	if (data->action == SWMOD_INSTALL)
		err = swmod_perform_installation(data->value, data->user, data->pwd, output, sizeof(output));
	else if (data->action == SWMOD_UPDATE)
		err = swmod_perform_upgrade(data->value, data->user, data->pwd, output, sizeof(output));
	else if (data->action == SWMOD_REMOVE)
		err = swmod_perform_uninstall(data->value, output, sizeof(output));
	else
		err = -1;

	dump_output_fd(data->fd, output);
	return err;
}

int swmod_lxc_install_update_remove_package(const PkgInfo *pkg, char *out, int outlen, const char *ct_name, int action)
{
	int err = -1;
	struct lxc_container *ct = NULL;

	if (out == NULL) {
		PRINT_ERR("No output buffer");
		return err;
	}

	if (pkg == NULL) {
		snprintf(out, outlen, "Internal Error");
		PRINT_ERR("No package information");
		return err;
	}

	if (ct_name == NULL) {
		snprintf(out, outlen, "No ExecEnv information received");
		PRINT_ERR("No environment information given");
		return err;
	}

	if (ct_name[0] == '\0') {
		snprintf(out, outlen, "ExecEnv name unknown");
		PRINT_ERR("No environment name given");
		return err;
	}

	ct = lxc_container_new(ct_name, NULL);
	if (!ct) {
		snprintf(out, outlen, "Error in opening container: %s", ct_name);
		PRINT_ERR("Failed in opening container: %s", ct_name);
		return err;
	}

	if (!ct->is_running(ct)) {
		snprintf(out, outlen, "ExecEnv not running");
		lxc_container_put(ct);
		return err;
	}

	PRINT_DEBUG("## Container %s", ct->name);
	lxc_attach_args lxc_args = (lxc_attach_args){.value = pkg->url, .action = action, .user = pkg->uname, .pwd = pkg->psw};
	err = lxc_attach_func(ct, lxc_attach_run_opkg_method_func, &lxc_args, true);
	PRINT_DEBUG("# Reading i/u/r %d, lxc[%s], err %d", action, ct->name, err);
	if (err >= 0  && strlen(g_lxc_buff)) {
		err = 0;
		snprintf(out, outlen, "%s", g_lxc_buff);
	}

	lxc_container_put(ct);

	return err;
}

static int lxc_attach_service_state_func(void *args)
{
	struct service_state *st = (struct service_state *)args;

	return ubus_call_service_state(st->name, st->state);
}

int swmod_set_lxc_ee_state(ExecEnv_t *env, char *state, const char *lxc_uci_path)
{
	int timeout;
	int ret = SUCCESS;

	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0) {
		ret = INVALID_REQUEST;
		PRINT_ERR("LXC DU uci path unknown");
		return ret;
	}

	struct lxc_container *ct = NULL;
	if (env == NULL) {
		ret = INVALID_REQUEST;
		PRINT_ERR("No environment information given");
		return ret;
	}

	if (env->name[0] == '\0'|| state == NULL) {
		ret = INVALID_REQUEST;
		PRINT_ERR("No environment information or state given");
		return ret;
	}

	ct = lxc_container_new(env->name, NULL);
	if (!ct) {
		ret = NOT_FOUND;
		PRINT_ERR("Failed in opening container: %s", env->name);
		return ret;
	}

	int req_state = get_requested_container_state(state);

	switch (req_state) {
	case CONT_START:
		set_autoboot_to_config_file(ct->name, true, lxc_uci_path);

		if (ct->is_running(ct)) {
			ret = ALREADY_RUNNING;
			break;
		}

		ct->want_close_all_fds(ct, true);
		ret = ct->start(ct, 0, NULL) ? SUCCESS: FAILURE;
		break;
	case CONT_STOP:
		timeout = get_timeout_from_config_file(ct->name, lxc_uci_path);

		set_autoboot_to_config_file(ct->name, false, lxc_uci_path);

		if (!ct->is_running(ct)) {
			ret = NOT_RUNNING;
			break;
		}

		if (strcmp(ct->state(ct), "FROZEN") == 0 || strcmp(ct->state(ct), "FREEZING") == 0) {
			/* to stop a frozen container first need to unfreeze it */
			if (!ct->unfreeze(ct)) {
				ret = FAILURE;
				break;
			}
		}

		ret = ct->shutdown(ct, timeout) ? SUCCESS : FAILURE;
		if (ret == FAILURE) {
			PRINT_DEBUG("Going to force stop [%s]", ct->name);
			ret = ct->stop(ct) ? SUCCESS : FAILURE;
		}
		break;
	case CONT_PAUSE:
		if (!ct->is_running(ct)) {
			ret = NOT_RUNNING;
			break;
		}

		ret = ct->freeze(ct) ? SUCCESS : FAILURE;
		break;
	case CONT_RESUME:
		if (!ct->is_running(ct)) {
			ret = NOT_RUNNING;
			break;
		}

		ret = ct->unfreeze(ct) ? SUCCESS : FAILURE;
		break;
	default:
		ret = INVALID_REQUEST;
	}

	lxc_container_put(ct);

	return ret;
}

int swmod_set_lxc_service_state(ExecEnv_t *env, char *name, bool state, const char *lxc_uci_path)
{
	int ret = -1;
	struct lxc_container *ct = NULL;

	if (lxc_uci_path == NULL || strlen(lxc_uci_path) == 0) {
		PRINT_ERR("LXC DU uci path unknown");
		return ret;
	}

	if (env == NULL) {
		PRINT_ERR("No environment info");
		return ret;
	}

	if (env->name[0] == '\0') {
		PRINT_ERR("No ExecEnv name found");
		return ret;
	}

	if (name == NULL) {
		PRINT_ERR("No eu name information");
		return ret;
	}

	if (0 != swmod_uci_init(lxc_uci_path)) {
		PRINT_ERR("Failed to write the requested value");
		return ret;
	}

	struct uci_section *s = NULL, *stmp = NULL;
	bool found = false;
	swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "du_eu_assoc", stmp, s) {
		const char *eu = swmod_uci_get_value_by_section(s, "eu_name");
		const char *ee_name = swmod_uci_get_value_by_section(s, "ee_name");

		if (strcmp(name, eu) == 0 && strcmp(env->name, ee_name) == 0) {
			found = true;
			if (state) {
				swmod_uci_set_value_by_section(s, "requested_state", "Active");
			} else {
				swmod_uci_set_value_by_section(s, "requested_state", "Idle");
			}

			break;
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);

	if (found == false) {
		PRINT_ERR("No DU exist with name: %s in ExecEnv: %s", name, env->name);
		return ret;
	}

	ct = lxc_container_new(env->name, NULL);
	if (!ct) {
		PRINT_ERR("Failed in opening container: %s", env->name);
		return ret;
	}

	if (!ct->is_running(ct)) {
		PRINT_INFO("lxc container not running");
		lxc_container_put(ct);
		return ret;
	}

	struct service_state st;
	st.name = name;
	st.state = state;
	ret = lxc_attach_func(ct, lxc_attach_service_state_func, (lxc_attach_args *)&st, false);
	lxc_container_put(ct);

	return ret;
}

static int lxc_attach_run_cmd(void *args)
{
	lxc_attach_args *lxc_input = (lxc_attach_args *) args;
	FILE *pp; // program pointer
	const char *cmd;

	cmd = lxc_input->value;
	if (cmd == NULL) { // null command to run, silently ignore
		PRINT_ERR("Empty command to run");
		return 0;
	}

	pp = popen(cmd, "r"); // flawfinder: ignore
	if (pp != NULL) {
		char line[MAX_LEN_512] = {0};

		while(fgets(line, sizeof(line), pp) != NULL) {
			write(lxc_input->fd, line, strlen(line));
		}
		pclose(pp);
	}

	return 0;
}

int lxc_run_cmd(ExecEnv_t *ee, const char *cmd, char *output, int out_len)
{
	struct lxc_container *ct;
	lxc_attach_args lxc_data;

	if (ee->exists == false || ee->ee_type != EE_TYPE_LXC) {
		PRINT_ERR("lxc not enabled");
		return -1;
	}

	memset(&lxc_data, 0, sizeof(lxc_attach_args));
	lxc_data.value = cmd;
	ct = lxc_container_new(ee->name, NULL);
	if (!ct) {
		PRINT_ERR("failed to open container");
		return -1;
	}

	if (!ct->is_running(ct)) {
		PRINT_ERR("container not running");
		lxc_container_put(ct);
		return -1;
	}

	int ret = lxc_attach_func(ct, lxc_attach_run_cmd, &lxc_data, true);
	if (ret >= 0 && strlen(g_lxc_buff)) {
		snprintf(output, out_len, "%s", g_lxc_buff);
	}

	lxc_container_put(ct);

	return ret;
}

void get_service_list(char *object, char *method, ubus_data_handler_t callback, void *data)
{
	/* This function will be called inside LXC, so need a new ubux ctx */
	uint32_t id;
	int fault = 0;
	struct ubus_context *ctx = ubus_connect(NULL);
	struct blob_buf bb;

	fault = ubus_lookup_id(ctx, object, &id);
	if (fault) {
		PRINT_ERR("ubus service not found %d", fault);
		ubus_free(ctx);
		return;
	}

	lxc_attach_args *input = (lxc_attach_args *)data;
	if (input == NULL) {
		PRINT_ERR("Service not found");
		ubus_free(ctx);
		return;
	}

	if (input->service == NULL) {
		PRINT_ERR("Service name not found");
		ubus_free(ctx);
		return;
	}

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "name", input->service);

	// Invoke Ubus to get data from uspd
	fault = ubus_invoke(ctx, id, method, bb.head, callback, data, UBUS_TIMEOUT);

	blob_buf_free(&bb);
	ubus_free(ctx);
}

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

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

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

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

	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(SWMOD_LXC_DU_UCI, "container", stmp, s) {
		char *name = swmod_uci_get_value_by_section(s, "name");
		char *type = swmod_uci_get_value_by_section(s, "type");

		if (strcmp(ee_name, name) == 0 && strcmp(type, "lxc") == 0) {
			swmod_uci_set_value_by_section(s, "alias", alias);
			ret = 0;
			break;
		}
	}

	swmod_uci_fini(SWMOD_LXC_DU_UCI);

	return ret;
}
