/*
 * swmod.c: SWMOD deamon
 *
 * Copyright (C) 2020-2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 * Author: Vivek Dutta <vivek.dutta@iopsys.eu>
 *
 * See LICENSE file for license related information.
 */
 
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <libubox/uloop.h>
#include <sys/prctl.h>

#include "swmod_uci.h"
#include "swmod_opkg.h"
#include "swmod_api.h"
#include "tools.h"
#include "swmod_host.h"
#include "swmod.h"
#ifdef SWMOD_LXC
#include "swmod_lxc.h"
#endif

#ifdef SWMOD_CRUN
#include "swmod_crun.h"
#endif

struct ubus_context *ubus_ctx;
ExecEnv_t g_environments[MAX_ENV] = {0};
ConfigParams swmod_config;
static int incr = 0;

struct swmod_runtime {
	struct uloop_timeout timer_dustate;
	struct blob_buf bb_timer_data;
};

struct swmod_runtime g_swmod_runtime;
static bool g_child_process = false;

enum du_status {
	E_DU_INSTALL_SUCCESS,
	E_DU_INSTALL_FAILED,
	E_DU_UNINSTALL_SUCCESS,
	E_DU_UNINSTALL_FAILED,
	__DU_STATUS_MAX
};

char du_status_string[][MAX_LEN_64] = {
	"Installing_success",
	"Installing_failed",
	"Uninstalling_success",
	"Uninstalling_failed",
};

enum {
	DU_INSTALL_ENV_ID,
	DU_INSTALL_ENV,
	DU_INSTALL_UUID,
	DU_INSTALL_URL,
	DU_INSTALL_USERNAME,
	DU_INSTALL_PASSWORD,
	DU_INSTALL_ENVVAR,
	__DU_INSTALL_MAX
};

enum {
	DU_UPDATE_ENV_ID,
	DU_UPDATE_ENV,
	DU_UPDATE_UUID,
	DU_UPDATE_URL,
	DU_UPDATE_USERNAME,
	DU_UPDATE_PASSWORD,
	__DU_UPDATE_MAX
};

enum {
	DU_UNINSTALL_ENV_ID,
	DU_UNINSTALL_ENV,
	DU_UNINSTALL_NAME,
	DU_UNINSTALL_INSTANCE,
	__DU_UNINSTALL_MAX
};

enum {
	EU_DU_LIST_ENV_ID,
	EU_DU_LIST_ENV,
	__EU_DU_LIST_MAX
};

enum {
	SET_CONFIG_ENV_ID,
	SET_CONFIG_ENV,
	SET_CONFIG_UUID,
	SET_CONFIG_EU_NAME,
	SET_CONFIG_PARAM,
	SET_CONFIG_VALUE,
	__SET_CONFIG_MAX
};

enum config_param {
	EE_ALIAS,
	EE_ENABLE,
	DU_ALIAS,
	EU_ALIAS,
	EU_REQUESTED_STATE,
	EU_AUTOSTART,
	__CONFIG_PARAM_MAX
};

struct param_name {
	enum config_param type;
	char param_name[MAX_LEN_32];
};

static const struct param_name param[__CONFIG_PARAM_MAX] = {
	{ EE_ALIAS, "ee_alias" },
	{ EE_ENABLE, "ee_enable" },
	{ DU_ALIAS, "du_alias" },
	{ EU_ALIAS, "eu_alias" },
	{ EU_REQUESTED_STATE, "eu_requested_state" },
	{ EU_AUTOSTART, "eu_autostart" },
};

static int get_du_status(const char *str)
{
	int i;

	if (str == NULL)
		return __DU_STATUS_MAX;

	for (i = E_DU_INSTALL_SUCCESS; i < __DU_STATUS_MAX; i++) {
		if (strcmp(str, du_status_string[i]) == 0)
			return i;
	}

	return __DU_STATUS_MAX;
}

static int get_param_type(const char *param_name)
{
	int i;
	if (param_name == NULL)
		return __CONFIG_PARAM_MAX;

	for (i = 0; i < __CONFIG_PARAM_MAX; i++) {
		if (strcmp(param_name, param[i].param_name) == 0)
			return param[i].type;
	}

	return __CONFIG_PARAM_MAX;
}

static const struct blobmsg_policy set_config_policy[__SET_CONFIG_MAX] = {
	[SET_CONFIG_ENV_ID] = { .name = "eeid", .type = BLOBMSG_TYPE_INT32 },
	[SET_CONFIG_ENV] = { .name = "ee_name", .type = BLOBMSG_TYPE_STRING },
	[SET_CONFIG_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
	[SET_CONFIG_EU_NAME] = { .name = "eu_name", .type = BLOBMSG_TYPE_STRING },
	[SET_CONFIG_PARAM] = { .name = "parameter", .type = BLOBMSG_TYPE_STRING },
	[SET_CONFIG_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy du_install_policy[__DU_INSTALL_MAX] = {
	[DU_INSTALL_ENV_ID] = { .name = "eeid", .type = BLOBMSG_TYPE_INT32 },
	[DU_INSTALL_ENV] = { .name = "ee_name", .type = BLOBMSG_TYPE_STRING },
	[DU_INSTALL_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
	[DU_INSTALL_URL] = { .name = "url", .type = BLOBMSG_TYPE_STRING },
	[DU_INSTALL_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING },
	[DU_INSTALL_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING },
	[DU_INSTALL_ENVVAR] = { .name = "env_var", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy du_update_policy[__DU_UPDATE_MAX] = {
	[DU_UPDATE_ENV_ID] = { .name = "eeid", .type = BLOBMSG_TYPE_INT32 },
	[DU_UPDATE_ENV] = { .name = "ee_name", .type = BLOBMSG_TYPE_STRING },
	[DU_UPDATE_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
	[DU_UPDATE_URL] = { .name = "url", .type = BLOBMSG_TYPE_STRING },
	[DU_UPDATE_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING },
	[DU_UPDATE_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy du_uninstall_policy[__DU_UNINSTALL_MAX] = {
	[DU_UNINSTALL_ENV_ID] = { .name = "eeid", .type = BLOBMSG_TYPE_INT32 },
	[DU_UNINSTALL_ENV] = { .name = "ee_name", .type = BLOBMSG_TYPE_STRING },
	[DU_UNINSTALL_NAME] = { .name = "du_name", .type = BLOBMSG_TYPE_STRING },
	[DU_UNINSTALL_INSTANCE] = { .name = "instance", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy eu_du_list_policy[__EU_DU_LIST_MAX] = {
	[EU_DU_LIST_ENV_ID] = { .name = "eeid", .type = BLOBMSG_TYPE_INT32 },
	[EU_DU_LIST_ENV] = { .name = "ee_name", .type = BLOBMSG_TYPE_STRING },
};

static void
populate_environments(void)
{
	struct list_head ee_head;
	memset(&ee_head, 0, sizeof(struct list_head));
	memset(g_environments, '\0', sizeof(g_environments));

	INIT_LIST_HEAD(&ee_head);
	/* Host system */
	populate_host_system_environment(&ee_head, swmod_config.oci_bundle_root);

	/* Linux containers */
#ifdef SWMOD_LXC
	populate_lxc_environment(&ee_head, swmod_config.lxc_bundle_root);
#endif

	sync_eeid_with_uci(&ee_head, g_environments, swmod_config.lxc_bundle_root);
	swmod_delete_ee_list(&ee_head);

	int i;
	for (i = 0; i < MAX_ENV; i++) {
		if (!g_environments[i].exists)
			continue;

		swmod_get_env_info(&g_environments[i]);
	}
}

ExecEnv_t* get_ee_from_eeid(unsigned int eid)
{
	int i;
	for (i = 0; i < MAX_ENV; i++) {
		if (!g_environments[i].exists)
			continue;

		if (eid == g_environments[i].eeid) {
			return &g_environments[i];
		}
	}

	return NULL;
}

ExecEnv_t* get_ee_from_name(char *ename)
{
	if (ename == NULL)
		return NULL;

	int i;
	for (i = 0; i < MAX_ENV; i++) {
		if (!g_environments[i].exists)
			continue;

		if (strcmp(g_environments[i].name, ename) == 0) {
			return &g_environments[i];
		}
	}

	return NULL;
}

static int
swmod_environment(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_buf bb;
	void *a, *t;
	int i;

	populate_environments();

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	a = blobmsg_open_array(&bb, "environment");

	for (i = 0; i < MAX_ENV; i++) {
		if (!g_environments[i].exists)
			continue;

		t = blobmsg_open_table(&bb, "");

		blobmsg_add_string(&bb, "ee_name", g_environments[i].name);
		blobmsg_add_string(&bb, "ee_alias", g_environments[i].alias);
		blobmsg_add_u32(&bb, "eeid", g_environments[i].eeid);
		blobmsg_add_string(&bb, "status", g_environments[i].status);
		blobmsg_add_u32(&bb, "pause", g_environments[i].pause);
		blobmsg_add_u32(&bb, "autoboot", g_environments[i].autoboot);
		blobmsg_add_string(&bb, "type", g_environments[i].type);
		blobmsg_add_string(&bb, "vendor", g_environments[i].vendor);
		blobmsg_add_string(&bb, "version", g_environments[i].version);
		blobmsg_add_u64(&bb, "allocated_disk_space", g_environments[i].allocated_disk_space);
		blobmsg_add_u64(&bb, "available_disk_space", g_environments[i].available_disk_space);
		blobmsg_add_u64(&bb, "allocated_memory", g_environments[i].allocated_memory);
		blobmsg_add_u64(&bb, "available_memory", g_environments[i].available_memory);
		blobmsg_add_u32(&bb, "parent_ee_ref", g_environments[i].parent_eeid);

		blobmsg_close_table(&bb, t);
	}

	blobmsg_close_array(&bb, a);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);
	return 0;
}

static void def_req_timeout_cb(struct uloop_timeout *t)
{
	struct deferred_data *data = container_of(t, struct deferred_data, timeout);
	if (data == NULL)
		return;

	struct uci_section *s = NULL, *stmp = NULL;
	char *uci = NULL;

	const char *uci_configs[][2] = {
		{swmod_config.oci_bundle_root, SWMOD_OCI_DU_UCI},
		{swmod_config.lxc_bundle_root, SWMOD_LXC_DU_UCI}
	};

	for (int i = 0; i < 2 && uci == NULL; i++) {
		if (0 != swmod_uci_init(uci_configs[i][0])) {
			continue;
		}

		swmod_uci_foreach_section_safe(uci_configs[i][1], "du_eu_assoc", stmp, s) {
			const char *uuid = swmod_uci_get_value_by_section(s, "uuid");
			if (strcmp(uuid, data->uuid) == 0) {
				uci = (char *)uci_configs[i][1];
				break;
			}
		}

		if (uci == NULL) {
			swmod_uci_fini(uci_configs[i][1]);
		}
	}

	if (uci == NULL) {
		goto end;
	}

	const char *status = swmod_uci_get_value_by_section(s, "du_status");
	if (strcmp(status, "Installing") == 0 || strcmp(status, "Uninstalling") == 0) {
		swmod_uci_fini(uci);
		if (data->retry_count == 0)
			goto end;

		data->retry_count = data->retry_count - 1;
		uloop_timeout_set(t, DEFER_HANDLER_TIMEOUT); // deferred req timeout 10sec
		return;
	} else {
		int code = 7002;
		const char *fault_code = NULL, *fault_str = NULL;

		const char *s_time = swmod_uci_get_value_by_section(s, "start_time");
		const char *version = swmod_uci_get_value_by_section(s, "version");
		const char *du_inst = swmod_uci_get_value_by_section(s, "du_inst");

		time_t start_time = convert_str_to_time(s_time);
		time_t end_time = time(NULL);

		int state = get_du_status(status);
		unsigned long instance = strtoul(du_inst, NULL, 10);

		switch (state) {
		case E_DU_INSTALL_SUCCESS:
			swmod_uci_set_value_by_section(s, "du_status", "Installed");
			swmod_send_event_du_state_change(false, 0, start_time, end_time, data->uuid,
						version, SWMOD_INSTALL, "", instance);
			break;
		case E_DU_INSTALL_FAILED:
			PRINT_ERR("DU Installation failed");
			fault_code = swmod_uci_get_value_by_section(s, "fault_code");
			fault_str = swmod_uci_get_value_by_section(s, "fault_string");
			swmod_uci_delete_by_section(s, NULL, NULL);

			if (strlen(fault_code) != 0)
				code = (int)strtoul(fault_code, NULL, 10);

			if (strlen(fault_str) == 0)
				fault_str = "Internal Error, see log for more details";

			swmod_send_event_du_state_change(true, code, start_time, end_time, data->uuid,
						version, SWMOD_INSTALL, fault_str, instance);
			break;
		case E_DU_UNINSTALL_SUCCESS:
			swmod_uci_delete_by_section(s, NULL, NULL);
			swmod_send_event_du_state_change(false, 0, start_time, end_time, data->uuid,
						version, SWMOD_REMOVE, "", instance);
			break;
		case E_DU_UNINSTALL_FAILED:
			PRINT_ERR("DU Uninstallation failed");
			fault_code = swmod_uci_get_value_by_section(s, "fault_code");
			fault_str = swmod_uci_get_value_by_section(s, "fault_string");
			swmod_uci_set_value_by_section(s, "du_status", "Installed");

			if (strlen(fault_code) != 0)
				code = (int)strtoul(fault_code, NULL, 10);

			if (strlen(fault_str) == 0)
				fault_str = "Internal Error, see log for more details";

			swmod_send_event_du_state_change(true, code, start_time, end_time, data->uuid,
						version, SWMOD_REMOVE, fault_str, instance);
			break;
		default:
			PRINT_DEBUG("This sate has nothing to do (%s)", status);
			swmod_uci_fini(uci);
			if (data->retry_count == 0)
				goto end;

			data->retry_count = data->retry_count - 1;
			uloop_timeout_set(t, DEFER_HANDLER_TIMEOUT); // deferred req timeout 10sec
			return;
		}
		swmod_uci_fini(uci);
	}

end:
	struct blob_buf bb;
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_u8(&bb, "status", true);
	ubus_send_reply(data->ctx, &data->req, bb.head);
	blob_buf_free(&bb);

	ubus_complete_deferred_request(data->ctx, &data->req, 0);
	free(data);
}

static void swmod_perform_package_uninstall(PkgInfo *pkg)
{
	char output[MAX_LEN_1024] = {0};
	ExecEnv_t *env = NULL;
	time_t finish;
	bool reload_crun = false;

	if (pkg == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Invalid DU arguments");
		swmod_send_event_du_state_change(true, 7002, finish, finish, "", "",
						 SWMOD_REMOVE, output, 0);
		return;
	}

	env = get_ee_from_name((char*)pkg->env_name);
	if (env == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Invalid ExecEnvReference");
		swmod_send_event_du_state_change(true, 7223, pkg->start, finish, "", "",
						 SWMOD_REMOVE, output, pkg->instance);
		return;
	}

	/* Now write the DU params in UCI */
	char uci_file[MAX_LEN_256] = {0};
	int ret = swmod_ee_uci_init(env, uci_file, sizeof(uci_file));
	if (ret != 0 || strlen(uci_file) == 0) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Internal Error");
		swmod_send_event_du_state_change(true, 7002, pkg->start, finish, "", "",
						 SWMOD_REMOVE, output, pkg->instance);
		return;
	}

	bool found = false;
	struct uci_section *s = NULL, *stmp = NULL;
	swmod_uci_foreach_section_safe(uci_file, "du_eu_assoc", stmp, s) {
		char *ee_name = swmod_uci_get_value_by_section(s, "ee_name");
		char *du_name = swmod_uci_get_value_by_section(s, "name");

		if (strcmp(ee_name, env->name) == 0 && strcmp(du_name, pkg->url) == 0) {
			char *uuid = swmod_uci_get_value_by_section(s, "uuid");
			memset(pkg->uuid, 0, sizeof(pkg->uuid));
			snprintf(pkg->uuid, sizeof(pkg->uuid), "%s", uuid);
			found = true;
			break;
		}
	}

	if (found == false) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "DU (%.64s) not exists in (%s) env", pkg->url, env->name);
		swmod_send_event_du_state_change(true, 7002, pkg->start, finish, "", "",
						 SWMOD_REMOVE, output, pkg->instance);
		swmod_uci_fini(uci_file);
		return;
	}

	char start_time[MAX_LEN_32] = {0};
	snprintf(start_time, sizeof(start_time), "%lu", (unsigned long)pkg->start);

	swmod_uci_set_value_by_section(s, "start_time", start_time);
	swmod_uci_set_value_by_section(s, "du_status", "Uninstalling");

	char du_inst[MAX_LEN_16] = {0};
	snprintf(du_inst, sizeof(du_inst), "%lu", (unsigned long)pkg->instance);

	swmod_uci_set_value_by_section(s, "du_inst", du_inst);

	int err = swmod_remove_package(env, pkg, output, sizeof(output));

	struct du_info info;
	memset(&info, 0, sizeof(struct du_info));

	if (err == 0) {
		if (strstr(output, "Removing package") != NULL) {
			char *ptr = strstr(output, "Package Name:");
			if (ptr != NULL) {
				sscanf(ptr, "Package Name:%63s Version:%63s", info.name, info.version);
			}
		} else if (strstr(output, "Container") != NULL) {
				sscanf(output, "Container %63s uninstalled", info.name);
				reload_crun = true;
		} else {
			err = -1;
		}
	}

	bool failed = (err) ? true : false;

	if (!failed) {
		if (reload_crun) {
			// for crun containers
			swmod_uci_fini(uci_file);
			swmod_uci_commit("crun");
		} else {
			// for lxc packages
			swmod_uci_set_value_by_section(s, "du_status", "Uninstalling_success");
			swmod_uci_fini(uci_file);
		}
	} else {
		swmod_uci_set_value_by_section(s, "du_status", "Uninstalling_failed");
		swmod_uci_fini(uci_file);
	}

}

static void swmod_perform_package_install(PkgInfo *pkg)
{
	char output[MAX_LEN_1024] = {0};
	ExecEnv_t *env = NULL;
	time_t finish;
	bool docker_image = false;

	if (pkg == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Invalid DU arguments");
		swmod_send_event_du_state_change(true, 7002, finish, finish, "", "",
						 SWMOD_INSTALL, output, 0);
		return;
	}

	env = get_ee_from_name((char*)pkg->env_name);
	if (env == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Invalid ExecEnvReference");
		swmod_send_event_du_state_change(true, 7223, pkg->start, finish, pkg->uuid, "",
						 SWMOD_INSTALL, output, 0);
		return;
	}

	char uci_file[MAX_LEN_256] = {0};
	int ret = swmod_ee_uci_init(env, uci_file, sizeof(uci_file));
	if (ret != 0 || strlen(uci_file) == 0) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Internal Error");
		swmod_send_event_du_state_change(true, 7002, pkg->start, finish, pkg->uuid, "",
						 SWMOD_INSTALL, output, 0);
		return;
	}

	char start_time[MAX_LEN_32] = {0};
	snprintf(start_time, sizeof(start_time), "%lu", (unsigned long)pkg->start);

	struct uci_section *s = swmod_uci_add_section(uci_file, "du_eu_assoc", false);
	swmod_uci_set_value_by_section(s, "start_time", start_time);
	swmod_uci_set_value_by_section(s, "url", pkg->url);
	swmod_uci_set_value_by_section(s, "username", pkg->uname);
	swmod_uci_set_value_by_section(s, "password", pkg->psw);
	swmod_uci_set_value_by_section(s, "uuid", pkg->uuid);
	swmod_uci_set_value_by_section(s, "ee_name", pkg->env_name);
	swmod_uci_set_value_by_section(s, "du_status", "Installing");

	if (strlen(pkg->env_var) != 0) {
		char *tok = NULL, *ptr = NULL;
		char env_var_buff[MAX_ENV_VAR_BUFF] = {0};

		snprintf(env_var_buff, sizeof(env_var_buff), "%s", pkg->env_var);

		for (tok = strtok_r(env_var_buff, ",", &ptr); tok != NULL; tok = strtok_r(NULL, ",", &ptr)) {
			swmod_uci_set_value_by_section_list(s, "env_var", tok);
		}
	}

	char *duid = generate_duid(true, incr++);
	swmod_uci_set_value_by_section(s, "duid", duid);
	FREE(duid);

	// set the corresponding section in pkg for further use if any
	pkg->section = s;

	// Now perform installation
	int err = swmod_install_package(env, pkg, output, sizeof(output));

	struct du_info info;
	memset(&info, 0, sizeof(struct du_info));
	if (err == 0) {
		if (strstr(output, "Installing") != NULL) {
			sscanf(output, "Installing %63s (%63[^)] to root...", info.name, info.version);
			get_opkg_description(env, info.name, info.desc, sizeof(info.desc));
			get_opkg_service_name(env, info.name, info.eu_name, sizeof(info.eu_name));
			snprintf(info.du_type, sizeof(info.du_type), "lxc");
		} else if (strstr(output, "Container") != NULL) {
			sscanf(output, "Container %63s to be installed", info.name);
			snprintf(info.du_type, sizeof(info.du_type), "crun");
			snprintf(info.eu_name, sizeof(info.eu_name), "%.32s", info.name);
			docker_image = true; // installation performed externally
		} else if (strstr(output, "Tar") != NULL) {
			sscanf(output, "Tar %63s installed", info.name);
			char path[1024] = {0};

			snprintf(path, sizeof(path), "%s/%s/config", swmod_config.oci_bundle_root, info.name);
			if (file_exists(path)) {
				snprintf(info.du_type, sizeof(info.du_type), "lxc");
			} else {
				snprintf(info.du_type, sizeof(info.du_type), "crun");
			}
			snprintf(info.eu_name, sizeof(info.eu_name), "%.32s", info.name);
		} else {
			err = -1;
		}
	}

	bool failed = (err) ? true : false;

	if (!failed) {
		// write the DU informations
		swmod_uci_set_value_by_section(s, "name", info.name);
		swmod_uci_set_value_by_section(s, "version", info.version);
		swmod_uci_set_value_by_section(s, "description", info.desc);
		swmod_uci_set_value_by_section(s, "type", info.du_type);
		swmod_uci_set_value_by_section(s, "autostart", "1");
		swmod_uci_set_value_by_section(s, "requested_state", "Active");
		swmod_uci_set_value_by_section(s, "eu_name", info.eu_name);

		if (docker_image == false) {
			// internally handled so need to set status accordingly
			swmod_uci_set_value_by_section(s, "du_status", "Installing_success");
			swmod_uci_delete_by_section(s, "username", pkg->uname);
			swmod_uci_delete_by_section(s, "password", pkg->psw);
		}

		swmod_uci_fini(uci_file);

		PRINT_INFO("# Installed DU reload %s service #", info.du_type);
		swmod_uci_commit("crun");
	} else {
		PRINT_ERR("DU Installation failed");
		swmod_uci_set_value_by_section(s, "du_status", "Installing_failed");
		swmod_uci_fini(uci_file);
	}
}

static int swmod_perform_du_operation(PkgInfo *pkg, size_t size)
{
	int ret = -1;
	int fd[2];

	if (pkg == NULL)
		return ret;

	if (pipe(fd) < 0)
		return ret;

	pid_t child;
	child = fork();
	if (child == -1) {
		PRINT_ERR("fork error");
		return ret;
	} else if (child != 0) {
		// parent process
		close(fd[0]);
		write(fd[1], pkg, size);
		close(fd[1]);
		ret = 0;
	} else {
		// child process
		g_child_process = true;
		prctl(PR_SET_NAME, "swmodd_du_operation");
		uloop_done();
		ubus_shutdown(ubus_ctx);
		ubus_ctx = NULL;
		fclose(stderr);
		fclose(stdout);
		fclose(stdin);

		close(fd[1]);
		PkgInfo p_info;
		memset(&p_info, 0, sizeof(PkgInfo));
		int res = read(fd[0], &p_info, sizeof(PkgInfo));
		close(fd[0]);

		if (res == -1) {
			PRINT_DEBUG("DU param value read failed");
			exit(EXIT_FAILURE);
		}

		PRINT_DEBUG("Perform DU operation: %d", p_info.operation);
		ubus_ctx = ubus_connect(NULL);
		if (ubus_ctx == NULL) {
			PRINT_ERR("Failed to connect UBUS");
			exit(EXIT_FAILURE);
		}

		switch (p_info.operation) {
		case SWMOD_INSTALL:
			swmod_perform_package_install(&p_info);
			break;
		case SWMOD_UPDATE:
			break;
		case SWMOD_REMOVE:
			swmod_perform_package_uninstall(&p_info);
			break;
		}

		ubus_free(ubus_ctx);
		exit(EXIT_SUCCESS);
	}

	return ret;
}

static int
swmod_du_uninstall(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_attr *tb[__DU_UNINSTALL_MAX] = {NULL};
	char output[MAX_LEN_1024] = {0};
	ExecEnv_t *env = NULL;
	time_t finish;
	PkgInfo pkg;
	char *instance = NULL, *uuid = NULL, *status = NULL;

	memset(&pkg, 0, sizeof(PkgInfo));
	pkg.start = time(NULL);

	if (blobmsg_parse(du_uninstall_policy, __DU_UNINSTALL_MAX, tb, blob_data(msg), blob_len(msg)))
		return UBUS_STATUS_UNKNOWN_ERROR;

	if (tb[DU_UNINSTALL_INSTANCE]) {
		instance = blobmsg_get_string(tb[DU_UNINSTALL_INSTANCE]);
		pkg.instance = strtoul(instance, NULL, 10);
	}

	if (!tb[DU_UNINSTALL_NAME]) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "No DU Name found");
		swmod_send_event_du_state_change(true, 7002, pkg.start, finish, "", "",
						 SWMOD_REMOVE, output, pkg.instance);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	snprintf(pkg.url, sizeof(pkg.url), "%s", blobmsg_get_string(tb[DU_UNINSTALL_NAME]));

	if (tb[DU_UNINSTALL_ENV])
		env = get_ee_from_name(blobmsg_get_string(tb[DU_UNINSTALL_ENV]));

	/* Priority should be given on EID if both EID and ENV name are provided */
	if (tb[DU_UNINSTALL_ENV_ID])
		env = get_ee_from_eeid(blobmsg_get_u32(tb[DU_UNINSTALL_ENV_ID]));

	if (env == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Could not find Execution Environment");
		swmod_send_event_du_state_change(true, 7223, pkg.start, finish, "", "",
						 SWMOD_REMOVE, output, pkg.instance);
		return UBUS_STATUS_INVALID_ARGUMENT;
	} else {
		char uci_file[MAX_LEN_256] = {0};
		int ret = swmod_ee_uci_init(env, uci_file, sizeof(uci_file));
		if (ret != 0 || strlen(uci_file) == 0) {
			finish = time(NULL);
			snprintf(output, sizeof(output), "Internal Error");
			swmod_send_event_du_state_change(true, 7002, pkg.start, finish, "", "",
						 SWMOD_REMOVE, output, pkg.instance);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}

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

			if (strcmp(ee_name, env->name) == 0 && strcmp(du_name, pkg.url) == 0) {
				uuid = swmod_uci_get_value_by_section(s, "uuid");
				status = swmod_uci_get_value_by_section(s, "du_status");
				break;
			}
		}
		swmod_uci_fini(uci_file);
	}

	if (status == NULL || strcmp(status, "Installed") != 0 || uuid == NULL || strlen(uuid) == 0) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Package not installed");
		swmod_send_event_du_state_change(true, 7002, pkg.start, finish, "", "",
						 SWMOD_REMOVE, output, pkg.instance);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	snprintf(pkg.env_name, sizeof(pkg.env_name), "%s", env->name);
	pkg.operation = SWMOD_REMOVE;

	int ret = swmod_perform_du_operation(&pkg, sizeof(pkg));

	if (ret == 0) {
		struct deferred_data *def_req = (struct deferred_data *)calloc(1, sizeof(struct deferred_data));
		if (def_req == NULL)
			goto end;

		snprintf(def_req->uuid, sizeof(def_req->uuid), "%s", uuid);
		def_req->retry_count = REQ_DEFER_MAX_RETRY; // max retry count 60
		def_req->timeout.cb = def_req_timeout_cb;
		def_req->ctx = ctx;
		uloop_timeout_set(&def_req->timeout, DEFER_HANDLER_TIMEOUT); // defer req handling timeout 10sec

		ubus_defer_request(ctx, req, &def_req->req);

		return 0;
	}

end:
	struct blob_buf bb;
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_u8(&bb, "status", ret ? false : true);
	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return 0;
}

static int
swmod_du_install(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	time_t finish;
	struct blob_attr *tb[__DU_INSTALL_MAX] = {NULL};
	ExecEnv_t *env = NULL;
	PkgInfo pkg;
	char output[MAX_LEN_1024] = {0};

	memset(&pkg, 0, sizeof(PkgInfo));
	pkg.start = time(NULL);

	if (blobmsg_parse(du_install_policy, __DU_INSTALL_MAX, tb, blob_data(msg), blob_len(msg)))
		return UBUS_STATUS_UNKNOWN_ERROR;

	if (tb[DU_INSTALL_ENV])
		env = get_ee_from_name(blobmsg_get_string(tb[DU_INSTALL_ENV]));

	/* Priority should be given on EID if both EID and ENV name are provided */
	if (tb[DU_INSTALL_ENV_ID])
		env = get_ee_from_eeid(blobmsg_get_u32(tb[DU_INSTALL_ENV_ID]));

	if (tb[DU_INSTALL_UUID]) {
		snprintf(pkg.uuid, sizeof(pkg.uuid), "%s", blobmsg_get_string(tb[DU_INSTALL_UUID]));
		if (!valid_uuid(pkg.uuid, swmod_config.lxc_bundle_root, swmod_config.oci_bundle_root)) {
			finish = time(NULL);
			snprintf(output, sizeof(output), "UUID is not valid");
			swmod_send_event_du_state_change(true, 7002, pkg.start, finish, pkg.uuid, "",
							 SWMOD_INSTALL, output, 0);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}
	}

	// Generate UUID if not provided
	if (strlen(pkg.uuid) == 0) {
		char *id = generate_uuid();
		if (id == NULL) {
			finish = time(NULL);
			snprintf(output, sizeof(output), "Failed to generate UUID");
			swmod_send_event_du_state_change(true, 7002, pkg.start, finish, "", "",
							 SWMOD_INSTALL, output, 0);
			return UBUS_STATUS_UNKNOWN_ERROR;
		}

		snprintf(pkg.uuid, sizeof(pkg.uuid), "%s", id);
		free(id);
	}

	if (!tb[DU_INSTALL_URL]) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "No URL provided");
		swmod_send_event_du_state_change(true, 7002, pkg.start, finish, pkg.uuid, "",
						 SWMOD_INSTALL, output, 0);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (env == NULL) {
		finish = time(NULL);
		snprintf(output, sizeof(output), "Could not find Execution Environment");
		swmod_send_event_du_state_change(true, 7223, pkg.start, finish, pkg.uuid, "",
						 SWMOD_INSTALL, output, 0);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	snprintf(pkg.url, sizeof(pkg.url), "%s", blobmsg_get_string(tb[DU_INSTALL_URL]));
	if (strncmp(pkg.url, "docker://", 9) != 0 && strchr(pkg.url, '@') != NULL) { /* In docker link there could be '@' for specific sha */
		finish = time(NULL);
		snprintf(output, sizeof(output), "URL contains user info");
		swmod_send_event_du_state_change(true, 7004, pkg.start, finish, pkg.uuid, "",
						 SWMOD_INSTALL, output, 0);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (tb[DU_INSTALL_USERNAME])
		snprintf(pkg.uname, sizeof(pkg.uname), "%s", blobmsg_get_string(tb[DU_INSTALL_USERNAME]));

	if (tb[DU_INSTALL_PASSWORD])
		snprintf(pkg.psw, sizeof(pkg.psw), "%s", blobmsg_get_string(tb[DU_INSTALL_PASSWORD]));

	if (tb[DU_INSTALL_ENVVAR])
		snprintf(pkg.env_var, sizeof(pkg.env_var), "%s", blobmsg_get_string(tb[DU_INSTALL_ENVVAR]));

	snprintf(pkg.env_name, sizeof(pkg.env_name), "%s", env->name);
	pkg.operation = SWMOD_INSTALL;

	int ret = swmod_perform_du_operation(&pkg, sizeof(pkg));

	if (ret == 0) {
		struct deferred_data *def_req = (struct deferred_data *)calloc(1, sizeof(struct deferred_data));
		if (def_req == NULL)
			goto end;

		snprintf(def_req->uuid, sizeof(def_req->uuid), "%s", pkg.uuid);
		def_req->retry_count = REQ_DEFER_MAX_RETRY; // max retry count 60
		def_req->timeout.cb = def_req_timeout_cb;
		def_req->ctx = ctx;
		uloop_timeout_set(&def_req->timeout, DEFER_HANDLER_TIMEOUT); // defer req handling timeout 10sec

		ubus_defer_request(ctx, req, &def_req->req);

		return 0;
	}

end:
	struct blob_buf bb;
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_u8(&bb, "status", ret ? false : true);
	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return 0;
}

static char *get_performed_operation(int action)
{
	switch(action) {
	case SWMOD_INSTALL:
		return "Install";
	case SWMOD_UPDATE:
		return "Update";
	case SWMOD_REMOVE:
		return "Uninstall";
	}

	return "Invalid";
}

static char *get_current_du_state(int action, bool failed)
{
	switch(action) {
	case SWMOD_INSTALL:
		if (failed)
			return "Failed";

		return "Installed";
	case SWMOD_UPDATE:
		return "Installed";
	case SWMOD_REMOVE:
		if (failed)
			return "Installed";
		return "UnInstalled";
	}

	return "Invalid";
}

static void _send_du_state_change(struct uloop_timeout *t)
{
	struct swmod_runtime *runtime;

	PRINT_INFO("DuStateChange timer called");
	runtime = container_of(t, struct swmod_runtime, timer_dustate);
	if (runtime == NULL) {
		PRINT_ERR("Failed to get the swmodd runtime context");
		return;
	}

	ubus_send_event(ubus_ctx, "dustate.changed", runtime->bb_timer_data.head);
	blob_buf_free(&runtime->bb_timer_data);
}

void swmod_send_event_du_state_change(bool failed, int code, time_t start, time_t end,
			const char *uuid, const char *ver, int action, const char *err,
			unsigned long instance)
{
	char start_time[MAX_LEN_32] = {0};
	char end_time[MAX_LEN_32] = {0};

	if (ubus_ctx == NULL) {
		return;
	}

	strftime(start_time, sizeof(start_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&start));
	strftime(end_time, sizeof(end_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&end));
	char *operation = get_performed_operation(action);
	char *state = get_current_du_state(action, failed);

	blob_buf_init(&g_swmod_runtime.bb_timer_data, 0);

	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "UUID", uuid);
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "Version", ver);
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "CurrentState", state);
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "Resolved", failed ? "0" : "1");
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "StartTime", start_time);
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "CompleteTime", end_time);
	blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "OperationPerformed", operation);

	if (instance) {
		char du_ref[64] = {0};
		snprintf(du_ref, sizeof(du_ref), "Device.SoftwareModules.DeploymentUnit.%lu", instance);
		blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "DeploymentUnitRef", du_ref);
	}

	if (failed) {
		blobmsg_add_u32(&g_swmod_runtime.bb_timer_data, "FaultCode", code);
		if (err && err[0] != '\0')
			blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "FaultString", err);
		else
			blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "FaultString", "Internal Error");
	} else {
		blobmsg_add_u32(&g_swmod_runtime.bb_timer_data, "FaultCode", 0);
		blobmsg_add_string(&g_swmod_runtime.bb_timer_data, "FaultString", "");
	}

	if (g_child_process) {
		ubus_send_event(ubus_ctx, "dustate.changed", g_swmod_runtime.bb_timer_data.head);
		blob_buf_free(&g_swmod_runtime.bb_timer_data);
	} else {
		g_swmod_runtime.timer_dustate.cb = _send_du_state_change;
		uloop_timeout_set(&g_swmod_runtime.timer_dustate, 10000);
	}

	return;
}

static int
swmod_du_update(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_buf bb;
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_u8(&bb, "status", false);
	blobmsg_add_string(&bb, "info", "DU update not supported");
	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return 0;
}

static ExecEnv_t* get_ee_from_blob(struct blob_attr *msg)
{
	ExecEnv_t *env = NULL;
	struct blob_attr *tb[__EU_DU_LIST_MAX] = {NULL};

	if (blobmsg_parse(eu_du_list_policy, __EU_DU_LIST_MAX, tb, blob_data(msg), blob_len(msg)) == 0) {
		if (tb[EU_DU_LIST_ENV]) {
			env = get_ee_from_name(blobmsg_get_string(tb[EU_DU_LIST_ENV]));
		}

		/* Priority given to EID if both EID and ENV name are provided */
		if (tb[EU_DU_LIST_ENV_ID]) {
			env = get_ee_from_eeid(blobmsg_get_u32(tb[EU_DU_LIST_ENV_ID]));
		}
	}

	return env;
}

static void prepare_du_list_result(ExecEnv_t *env, struct blob_buf *bb)
{
	struct uci_section *ss = NULL;

	if (env == NULL)
		return;

	if (env->exists == false)
		return;

	char uci_file[MAX_LEN_256] = {0};
	int ret = swmod_ee_uci_init(env, uci_file, sizeof(uci_file));
	if (ret != 0 || strlen(uci_file) == 0) {
		return;
	}

	swmod_uci_foreach_section(uci_file, "du_eu_assoc", ss) {

		const char *ee_name = swmod_uci_get_value_by_section(ss, "ee_name");
		if (strlen(ee_name) == 0)
			continue;

		if (strcmp(ee_name, env->name) != 0)
			continue;

		char *uuid = swmod_uci_get_value_by_section(ss, "uuid");
		if (strlen(uuid) == 0) {
			uuid = generate_uuid();
			swmod_uci_set_value_by_section(ss, "uuid", uuid ? uuid : "");
			FREE(uuid);
		}

		char *duid = swmod_uci_get_value_by_section(ss, "duid");
		if (strlen(duid) == 0) {
			duid = generate_duid(true, incr++);
			swmod_uci_set_value_by_section(ss, "duid", duid ? duid : "");
			FREE(duid);
		}

		void *t = blobmsg_open_table(bb, "");

		blobmsg_add_string(bb, "du_name", swmod_uci_get_value_by_section(ss, "name"));
		blobmsg_add_string(bb, "du_alias", swmod_uci_get_value_by_section(ss, "du_alias"));
		blobmsg_add_string(bb, "ee_name", ee_name);
		blobmsg_add_u32(bb, "eeid", env->eeid);
		blobmsg_add_string(bb, "uuid", swmod_uci_get_value_by_section(ss, "uuid"));
		blobmsg_add_string(bb, "duid", swmod_uci_get_value_by_section(ss, "duid"));
		blobmsg_add_string(bb, "url", swmod_uci_get_value_by_section(ss, "url"));
		blobmsg_add_string(bb, "version", swmod_uci_get_value_by_section(ss, "version"));
		blobmsg_add_string(bb, "config", swmod_uci_get_value_by_section(ss, "config"));
		blobmsg_add_string(bb, "description", swmod_uci_get_value_by_section(ss, "description"));
		blobmsg_add_string(bb, "du_status", swmod_uci_get_value_by_section(ss, "du_status"));
		blobmsg_add_string(bb, "eu_name", swmod_uci_get_value_by_section(ss, "eu_name"));
		blobmsg_add_string(bb, "vendor", swmod_uci_get_value_by_section(ss, "vendor"));

		blobmsg_close_table(bb, t);
	}

	swmod_uci_fini(uci_file);
}

static int
swmod_du_list(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_buf bb;
	void *a;
	ExecEnv_t *env = get_ee_from_blob(msg);

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	a = blobmsg_open_array(&bb, "deployment_unit");

	if (env != NULL) {
		prepare_du_list_result(env, &bb);
	} else {
		for (int i = 0; i < MAX_ENV; i++) {
			prepare_du_list_result(&g_environments[i], &bb);
		}
	}

	blobmsg_close_array(&bb, a);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);
	return 0;
}

static void prepare_eu_list_result(ExecEnv_t *env, struct blob_buf *bb)
{
	void *t;

	if (env == NULL)
		return;

	if (env->exists == false)
		return;

	if (list_empty(&env->eu_list))
		return;

	EuNode *iter = NULL;

	list_for_each_entry(iter, &env->eu_list, list) {
		if (iter->eu.eu_exists == false) {
			continue;
		}

		t = blobmsg_open_table(bb, "");

		blobmsg_add_string(bb, "eu_name", iter->eu.name);
		blobmsg_add_string(bb, "eu_alias", iter->eu.eu_alias);
		blobmsg_add_string(bb, "command", iter->eu.command);
		blobmsg_add_string(bb, "state", iter->eu.state);
		blobmsg_add_string(bb, "config", iter->eu.config);
		blobmsg_add_string(bb, "version", iter->eu.version);
		blobmsg_add_string(bb, "description", iter->eu.description);
		blobmsg_add_string(bb, "ee_name", iter->eu.environment);
		blobmsg_add_u32(bb, "eeid", env->eeid);
		blobmsg_add_string(bb, "euid", iter->eu.euid);
		blobmsg_add_u32(bb, "disk_space", iter->eu.disk_space);
		blobmsg_add_u32(bb, "memory_space", iter->eu.memory_space);
		blobmsg_add_string(bb, "vendor", iter->eu.vendor); //TODO
		blobmsg_add_string(bb, "req_state", iter->eu.req_state);
		blobmsg_add_u8(bb, "autostart", iter->eu.autostart);
		blobmsg_add_string(bb, "du_name", iter->eu.du_name);
		blobmsg_add_string(bb, "fault_code", iter->eu.fault_code);

		blobmsg_close_table(bb, t);
	}

	swmod_delete_eu_list(&env->eu_list);
}

static int
swmod_eu_list(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_buf bb;
	void *a;

	ExecEnv_t *env = get_ee_from_blob(msg);

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	a = blobmsg_open_array(&bb, "execution_unit");

	if (env != NULL) {
		populate_execution_unit(env);
		prepare_eu_list_result(env, &bb);
	} else {
		for (int i = 0; i < MAX_ENV; i++) {
			populate_execution_unit(&g_environments[i]);
			prepare_eu_list_result(&g_environments[i], &bb);
		}
	}

	blobmsg_close_array(&bb, a);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);
	return 0;
}

static int
swmod_set_config(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	struct blob_buf bb;
	struct blob_attr *tb[__SET_CONFIG_MAX] = {NULL};
	char param_name[MAX_LEN_32] = {0};
	ExecEnv_t *env = NULL;
	char alias[65] = {0};
	bool enable = false;

	if (blobmsg_parse(set_config_policy, __SET_CONFIG_MAX, tb, blob_data(msg), blob_len(msg)) != 0) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (!tb[SET_CONFIG_PARAM] || !tb[SET_CONFIG_VALUE]) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (tb[SET_CONFIG_ENV]) {
		env = get_ee_from_name(blobmsg_get_string(tb[SET_CONFIG_ENV]));
	}

	/* Priority should be given on EID if both EID and ENV name are provided */
	if (tb[SET_CONFIG_ENV_ID]) {
		env = get_ee_from_eeid(blobmsg_get_u32(tb[SET_CONFIG_ENV_ID]));
	}

	if (env == NULL)
		return UBUS_STATUS_INVALID_ARGUMENT;

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	snprintf(param_name, sizeof(param_name), "%s", blobmsg_get_string(tb[SET_CONFIG_PARAM]));
	int param = get_param_type(param_name);

	switch (param) {
	case EE_ALIAS:
		snprintf(alias, sizeof(alias), "%s", blobmsg_get_string(tb[SET_CONFIG_VALUE]));
		swmod_set_ee_alias(env, alias, &bb);
		break;
	case EE_ENABLE:
		enable = uci_str_to_bool(blobmsg_get_string(tb[SET_CONFIG_VALUE]));
		enable ? swmod_change_ee_state(env, "start", &bb) : swmod_change_ee_state(env, "stop", &bb);
		break;
	case DU_ALIAS:
		if (!tb[SET_CONFIG_UUID]) {
			blob_buf_free(&bb);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}

		snprintf(alias, sizeof(alias), "%s", blobmsg_get_string(tb[SET_CONFIG_VALUE]));
		swmod_set_du_alias(env, blobmsg_get_string(tb[SET_CONFIG_UUID]), alias, &bb);
		break;
	case EU_ALIAS:
		if (!tb[SET_CONFIG_EU_NAME]) {
			blob_buf_free(&bb);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}

		snprintf(alias, sizeof(alias), "%s", blobmsg_get_string(tb[SET_CONFIG_VALUE]));
		swmod_set_eu_alias(env, blobmsg_get_string(tb[SET_CONFIG_EU_NAME]), alias, &bb);
		break;
	case EU_REQUESTED_STATE:
		if (!tb[SET_CONFIG_EU_NAME]) {
			blob_buf_free(&bb);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}

		if (strcmp(blobmsg_get_string(tb[SET_CONFIG_VALUE]), "Idle") == 0) {
			swmod_set_service_state(env, blobmsg_get_string(tb[SET_CONFIG_EU_NAME]), false, &bb);
		} else if (strcmp(blobmsg_get_string(tb[SET_CONFIG_VALUE]), "Active") == 0) {
			swmod_set_service_state(env, blobmsg_get_string(tb[SET_CONFIG_EU_NAME]), true, &bb);
		} else {
			blob_buf_free(&bb);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}
		break;
	case EU_AUTOSTART:
		if (!tb[SET_CONFIG_EU_NAME]) {
			blob_buf_free(&bb);
			return UBUS_STATUS_INVALID_ARGUMENT;
		}

		enable = uci_str_to_bool(blobmsg_get_string(tb[SET_CONFIG_VALUE]));
		swmod_set_eu_autostart(env, blobmsg_get_string(tb[SET_CONFIG_EU_NAME]), enable, &bb);
		break;
	default:
		blob_buf_free(&bb);
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return 0;
}

static const struct ubus_method swmod_object_methods[] = {
	UBUS_METHOD_NOARG("ee_list", swmod_environment),
	UBUS_METHOD("du_list", swmod_du_list, eu_du_list_policy),
	UBUS_METHOD("eu_list", swmod_eu_list, eu_du_list_policy),
	UBUS_METHOD("du_install", swmod_du_install, du_install_policy),
	UBUS_METHOD("du_update", swmod_du_update, du_update_policy),
	UBUS_METHOD("du_uninstall", swmod_du_uninstall, du_uninstall_policy),
	UBUS_METHOD("set_config", swmod_set_config, set_config_policy),
};

static struct ubus_object_type swmod_object_type = UBUS_OBJECT_TYPE("swmodules", swmod_object_methods);

static struct ubus_object swmod_object = {
	.name = "swmodules",
	.type = &swmod_object_type,
	.methods = swmod_object_methods,
	.n_methods = ARRAY_SIZE(swmod_object_methods),
};

static int swmod_pre_init(void)
{
	/* get config */
	memset(&swmod_config, 0, sizeof(ConfigParams));
	get_swmod_config_params(&swmod_config);

	memset(&g_swmod_runtime.bb_timer_data, 0 , sizeof(struct blob_buf));

	// Populate ExecEnv details
	populate_environments();

	return 0;
}

static int swmod_init(struct ubus_context *ctx)
{
	int ret;

	ret = ubus_add_object(ctx, &swmod_object);
	if (ret)
		PRINT_ERR("Failed to publish '%s' object : %s", swmod_object.name, ubus_strerror(ret));

	return ret;
}

static void usage(char *prog)
{
	fprintf(stderr, "Usage: %s [options]\n", prog);
	fprintf(stderr, "\n");
	fprintf(stderr, "options:\n");
	fprintf(stderr, "    -s <socket path>   ubus socket\n");
	fprintf(stderr, "    -l <log_level>     Configures log level\n");
	fprintf(stderr, "\n");
}

int main(int argc, char **argv)
{
	const char *ubus_socket = NULL;
	int ret, ch;

	while ((ch = getopt(argc, argv, "hs:l:")) != -1) {
		switch (ch) {
		case 's':
			ubus_socket = optarg;
			break;
		case 'l':
			configure_debug_level(strtol(optarg, NULL, 10));
			break;
		case 'h':
			usage(argv[0]);
			exit(0);
		default:
			break;
		}
	}

	openlog("swmodd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);

	uloop_init();

	ubus_ctx = ubus_connect(ubus_socket);
	if (!ubus_ctx) {
		fprintf(stderr, "Failed to connect to ubus\n");
		return -1;
	}

	swmod_pre_init();

	ubus_add_uloop(ubus_ctx);

	ret = swmod_init(ubus_ctx);
	if (ret)
		goto out;

	uloop_run();
out:
	uloop_done();

	blob_buf_free(&g_swmod_runtime.bb_timer_data);
	ubus_free(ubus_ctx);
	closelog();

	return 0;
}
