Skip to content
Snippets Groups Projects
swmod.c 23.75 KiB
/*
 * swmod.c: SWMOD deamon
 *
 * Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
 *
 * Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */
 
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <libubox/uloop.h>
#include <curl/curl.h>

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

#define DW_PKG_PATH "/tmp/dw_package.ipk"

struct ubus_context *ubus_ctx;
ExecEnv environments[MAX_ENV] = {0};
ExecUnit exec_units[MAX_ENV] = {0};

enum {
	DU_INSTALL_ENV_ID,
	DU_INSTALL_ENV,
	DU_INSTALL_UUID,
	DU_INSTALL_URL,
	DU_INSTALL_USERNAME,
	DU_INSTALL_PASSWORD,
	__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_MAX
};
enum {
	EU_DU_LIST_ENV_ID,
	EU_DU_LIST_ENV,
	__EU_DU_LIST_MAX
};

enum {
	EU_ACTIVATE_SERVICE_ENV_ID,
	EU_ACTIVATE_SERVICE_ENV,
	EU_ACTIVATE_SERVICE_NAME,
	EU_ACTIVATE_SERVICE_STATUS,
	__EU_ACTIVATE_SERVICE_MAX
};

enum {
	EE_SET_STATE_ENV_ID,
	EE_SET_STATE_ENV,
	EE_SET_STATE_STATUS,
	__EE_SET_STATE_MAX
};

static const struct blobmsg_policy ee_set_state_policy[__EE_SET_STATE_MAX] = {
	[EE_SET_STATE_ENV_ID] = { .name = "eid", .type = BLOBMSG_TYPE_INT32 },
	[EE_SET_STATE_ENV] = { .name = "environment", .type = BLOBMSG_TYPE_STRING },
	[EE_SET_STATE_STATUS] = { .name = "state", .type = BLOBMSG_TYPE_STRING },
};

static const struct blobmsg_policy eu_activate_policy[__EU_ACTIVATE_SERVICE_MAX] = {
	[EU_ACTIVATE_SERVICE_ENV_ID] = { .name = "eid", .type = BLOBMSG_TYPE_INT32 },
	[EU_ACTIVATE_SERVICE_ENV] = { .name = "environment", .type = BLOBMSG_TYPE_STRING },
	[EU_ACTIVATE_SERVICE_NAME] = { .name = "service_name", .type = BLOBMSG_TYPE_STRING },
	[EU_ACTIVATE_SERVICE_STATUS] = { .name = "state", .type = BLOBMSG_TYPE_INT8 },
};

static const struct blobmsg_policy du_install_policy[__DU_INSTALL_MAX] = {
	[DU_INSTALL_ENV_ID] = { .name = "eid", .type = BLOBMSG_TYPE_INT32 },
	[DU_INSTALL_ENV] = { .name = "environment", .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 },
};

static const struct blobmsg_policy du_update_policy[__DU_UPDATE_MAX] = {
	[DU_UPDATE_ENV_ID] = { .name = "eid", .type = BLOBMSG_TYPE_INT32 },
	[DU_UPDATE_ENV] = { .name = "environment", .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 = "eid", .type = BLOBMSG_TYPE_INT32 },
	[DU_UNINSTALL_ENV] = { .name = "environment", .type = BLOBMSG_TYPE_STRING },
	[DU_UNINSTALL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
};

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

static void
populate_environments(void)
{
	memset(environments, '\0', sizeof(environments));

	/* Host system */
	populate_host_system_environment();

	/* Linux containers */
#ifdef SWMOD_LXC
	populate_lxc_environment();
#endif
}

int get_eid_from_env_name(char *ename)
{
	int i;

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

		if (strcmp(environments[i].name, ename) == 0)
			return i + 1;
	}

	return 0;
}

static int get_map_filename_from_eid(unsigned int eid, char *fname, int size)
{
	if (!fname)
		return -1;

	memset(fname, 0, size);
	if (eid == 1) {
		strncpy(fname, SWMOD_MAP_DU, size - 1);
		return 0;
	} else {
		if (!environments[eid - 1].exists)
			return -1;

		snprintf(fname, size, "%s_%s", SWMOD_MAP_DU, environments[eid - 1].name);

		return 0;
	}
}

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 (!environments[i].exists)
			continue;

		t = blobmsg_open_table(&bb, "");

		blobmsg_add_string(&bb, "name", environments[i].name);
		blobmsg_add_u32(&bb, "eid", i + 1);
		blobmsg_add_string(&bb, "status", environments[i].status);
		blobmsg_add_u32(&bb, "pause", environments[i].pause);
		blobmsg_add_string(&bb, "type", environments[i].type);
		blobmsg_add_string(&bb, "vendor", environments[i].vendor);
		blobmsg_add_string(&bb, "version", environments[i].version);
		blobmsg_add_u64(&bb, "allocated_disk_space", environments[i].allocated_disk_space);
		blobmsg_add_u64(&bb, "available_disk_space", environments[i].available_disk_space);
		blobmsg_add_u64(&bb, "allocated_memory", environments[i].allocated_memory);
		blobmsg_add_u64(&bb, "available_memory", environments[i].available_memory);

		blobmsg_close_table(&bb, t);
	}

	blobmsg_close_array(&bb, a);

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

int update_map_du_file(struct blob_buf *bb, char *pkg_name,
		char *pkg_version, char *url, char *username,
		char *password, char *env, char *uuid)
{
	char uci_map_du_path[128] = {0};

#ifdef SWMOD_LXC
	if (!env || strcmp(env, HOST_SYSTEM) == 0)
		swmod_strncpy(uci_map_du_path, SWMOD_PATH, 12);
	else {
		const char *lxc_config_path = get_lxc_path_from_config();
		snprintf(uci_map_du_path, sizeof(uci_map_du_path), "%s/%s/rootfs%s",
				lxc_config_path ? lxc_config_path : "",
				env,
				SWMOD_PATH);
	}
#else
	swmod_strncpy(uci_map_du_path, SWMOD_PATH, 12);
#endif

	swmod_uci_init(uci_map_du_path);

	struct uci_section *new_s = swmod_uci_add_section(SWMOD_MAP_DU, "deployment");

	swmod_uci_set_value_by_section(new_s, "name", pkg_name);
	swmod_uci_set_value_by_section(new_s, "version", pkg_version);
	swmod_uci_set_value_by_section(new_s, "url", url);
	swmod_uci_set_value_by_section(new_s, "username", (username && *username) ? username : "");
	swmod_uci_set_value_by_section(new_s, "password", (password && *password) ? password : "");

	swmod_uci_set_value_by_section(new_s, "environment", (env) ? env : HOST_SYSTEM);
	blobmsg_add_string(bb,"environment", (env) ? env : HOST_SYSTEM);

	if (uuid && *uuid != '\0') {
		//use the given UUID
		swmod_uci_set_value_by_section(new_s, "uuid", uuid);
		blobmsg_add_string(bb,"uuid", uuid);
	} else {
		//generate a UUID
		char *new_uuid = generate_uuid();
		swmod_uci_set_value_by_section(new_s, "uuid", new_uuid);
		blobmsg_add_string(bb,"uuid", new_uuid);
		FREE(new_uuid);
	}

	//generate a DUID
	char *duid = generate_duid(false, 0);
	swmod_uci_set_value_by_section(new_s, "duid", duid);
	FREE(duid);

	/* Get Description from package_name.control and Set it to map_du file */
	//get_description_from_package(new_s, pkg_name);

	/* Get Config from package_name.list and Set it to map_du file */
	//get_config_from_package(new_s, pkg_name);

	swmod_uci_fini(SWMOD_MAP_DU);

	return 0;
}

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

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

		if (username)
			curl_easy_setopt(curl, CURLOPT_USERNAME, username);

		if (password)
			curl_easy_setopt(curl, CURLOPT_PASSWORD, password);

		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 100);

		FILE *fp = fopen(DW_PKG_PATH, "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;
}

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)
{
	struct blob_attr *tb[__DU_INSTALL_MAX] = {NULL};
	struct blob_buf bb;
	int eid = 0, err = -1;
	char url[2048] = {0};
	bool remote_file = false;

	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])
		eid = get_eid_from_env_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])
		eid = blobmsg_get_u32(tb[DU_INSTALL_ENV_ID]);

	if (!tb[DU_INSTALL_URL])
		return UBUS_STATUS_INVALID_ARGUMENT;

	snprintf(url, sizeof(url), "%s", blobmsg_get_string(tb[DU_INSTALL_URL]));
	if (strchr(url, ':') != NULL) {
		/* This is a remote package, need to download first */
		if(0 != download_remote_package(url, blobmsg_get_string(tb[DU_INSTALL_USERNAME]),
						blobmsg_get_string(tb[DU_INSTALL_PASSWORD]))) {
			goto end;
		}

		snprintf(url, sizeof(url), "%s", DW_PKG_PATH);
		remote_file = true;
	}

	err = swmod_install_package(url, eid, remote_file);

	//synchronize_deployment_units_with_map_du_file();

end:
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);
	blobmsg_add_u8(&bb, "status", (err) ? false : true);

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

int update_corresponding_section_in_map_du_file(char *pkg_version, char *url,
		char *username, char *password, const char *env, char *uuid)
{
	char uci_map_du_path[128] = {0};

#ifdef SWMOD_LXC
	if (!env || strcmp(env, HOST_SYSTEM) == 0)
		swmod_strncpy(uci_map_du_path, SWMOD_PATH, 12);
	else {
		const char *lxc_config_path = get_lxc_path_from_config();
		snprintf(uci_map_du_path, sizeof(uci_map_du_path), "%s/%s/rootfs%s",
				lxc_config_path ? lxc_config_path : "",
				env,
				SWMOD_PATH);
	}
#else
	swmod_strncpy(uci_map_du_path, SWMOD_PATH, 12);
#endif

	swmod_uci_init(uci_map_du_path);

	struct uci_section *s = NULL;
	swmod_uci_foreach_section(SWMOD_MAP_DU, "deployment", s) {
		const char *map_uuid = swmod_uci_get_value_by_section(s, "uuid");
		if (strcmp(map_uuid, uuid) == 0) {
			swmod_uci_set_value_by_section(s, "version", pkg_version);
			swmod_uci_set_value_by_section(s, "url", url);
			swmod_uci_set_value_by_section(s, "username", (username && *username) ? username : "");
			swmod_uci_set_value_by_section(s, "password", (password && *password) ? password : "");
			break;
		}
	}

	swmod_uci_fini(SWMOD_MAP_DU);

	return 0;
}

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_attr *tb[__DU_UPDATE_MAX] = {NULL};
	struct blob_buf bb;
	char url[2048] = {0};
	int eid = 0, err = -1;
	bool remote_file = false;

	if (blobmsg_parse(du_update_policy, __DU_UPDATE_MAX, tb, blob_data(msg), blob_len(msg)))
		return UBUS_STATUS_UNKNOWN_ERROR;

	if (!tb[DU_UPDATE_URL] || !tb[DU_UPDATE_UUID])
		return UBUS_STATUS_INVALID_ARGUMENT;

	if (tb[DU_UPDATE_ENV])
		eid = get_eid_from_env_name(blobmsg_get_string(tb[DU_UPDATE_ENV]));

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

	snprintf(url, sizeof(url), "%s", blobmsg_get_string(tb[DU_UPDATE_URL]));
	if (strchr(url, ':') != NULL) {
		/* This is a remote package, need to download first */
		if (0 != download_remote_package(url, blobmsg_get_string(tb[DU_UPDATE_USERNAME]),
						blobmsg_get_string(tb[DU_UPDATE_PASSWORD]))) {
			goto end;
		}

		snprintf(url, sizeof(url), "%s", DW_PKG_PATH);
		remote_file = true;
	}

	err = swmod_update_package(url, eid, remote_file);

	//synchronize_deployment_units_with_map_du_file();

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

	blobmsg_add_u8(&bb, "status", (!err) ? true : false);

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

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};
	struct blob_buf bb;
	int eid = 0;

	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_NAME])
		return UBUS_STATUS_INVALID_ARGUMENT;

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

	if (tb[DU_UNINSTALL_ENV])
		eid = get_eid_from_env_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])
		eid = blobmsg_get_u32(tb[DU_UNINSTALL_ENV_ID]);

	int err = swmod_remove_package(blobmsg_get_string(tb[DU_UNINSTALL_NAME]), eid);

	blobmsg_add_u8(&bb, "status", (!err) ? true : false);

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

static int get_eid_from_blob(struct blob_attr *msg)
{
	unsigned int eid = 0;
	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]) {
			eid = get_eid_from_env_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]) {
			eid = blobmsg_get_u32(tb[EU_DU_LIST_ENV_ID]);
		}
	}

	return eid;
}

static void prepare_du_list_result(unsigned int eid, struct blob_buf *bb)
{
	struct uci_section *ss = NULL;
	char map_du_fname[64] = {0};

	if (0 != get_map_filename_from_eid(eid, map_du_fname, sizeof(map_du_fname)))
		return;

	swmod_uci_foreach_section(map_du_fname, "deployment", ss) {

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

		blobmsg_add_string(bb, "name", swmod_uci_get_value_by_section(ss, "name"));
		blobmsg_add_string(bb, "environment", swmod_uci_get_value_by_section(ss, "environment"));
		blobmsg_add_string(bb, "eid", swmod_uci_get_value_by_section(ss, "eid"));
		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, "vendor", ""); //TODO

		blobmsg_close_table(bb, t);
	}
}

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;
	unsigned int eid = get_eid_from_blob(msg);

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

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

	if (eid) {
		eid <= MAX_ENV ? prepare_du_list_result(eid, &bb) :
			PRINT_ERR("Invalid eid '%d', valid range [1 - %d]", eid, MAX_ENV);
	} else {
		for (int i = 1; i <= MAX_ENV; i++) {
			prepare_du_list_result(i, &bb);
		}
	}

	swmod_uci_fini(SWMOD_MAP_DU);

	blobmsg_close_array(&bb, a);

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

static void
populate_execution_unit()
{
	memset(exec_units, '\0', sizeof(exec_units));

	/* Host system */
	populate_service_list(service_get_cb);

	/* Linux containers */
#ifdef SWMOD_LXC
	populate_lxc_eu();
#endif
}

static void prepare_eu_list_result(unsigned int idx, struct blob_buf *bb)
{
	int j;
	void *t;

	if (!exec_units[idx].exists)
		return;

	for (j = 0; j < MAX_EU; j++) {
		if (!exec_units[idx].eu_exists[j])
			continue;

		t = blobmsg_open_table(bb, "");

		blobmsg_add_string(bb, "name", exec_units[idx].name[j]);
		blobmsg_add_string(bb, "command", exec_units[idx].command[j]);
		blobmsg_add_string(bb, "state", exec_units[idx].state[j]);
		blobmsg_add_string(bb, "config", exec_units[idx].config[j]);
		blobmsg_add_string(bb, "version", exec_units[idx].version[j]);
		blobmsg_add_string(bb, "description", exec_units[idx].description[j]);
		blobmsg_add_string(bb, "environment", exec_units[idx].environment[j]);
		blobmsg_add_u32(bb, "eid", idx + 1);
		blobmsg_add_u32(bb, "euid", exec_units[idx].euid[j]);
		blobmsg_add_u32(bb, "disk_space", exec_units[idx].disk_space[j]);
		blobmsg_add_u32(bb, "memory_space", exec_units[idx].memory_space[j]);
		blobmsg_add_string(bb, "vendor", exec_units[idx].vendor[j]); //TODO

		blobmsg_close_table(bb, t);
	}
}

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;

	unsigned int eid = get_eid_from_blob(msg);

	populate_execution_unit();

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

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

	if (eid) {
		eid <= MAX_ENV ? prepare_eu_list_result(eid - 1, &bb) :
			PRINT_ERR("Invalid eid '%d', valid range [1 - %d]", eid, MAX_ENV);
	} else {
		for (int i = 0; i < MAX_ENV; i++) {
			prepare_eu_list_result(i, &bb);
		}
	}

	blobmsg_close_array(&bb, a);

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

static int
swmod_eu_activate(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[__EU_ACTIVATE_SERVICE_MAX] = {NULL};
	char name[32] = {0};
	bool state = false;
	int eid = 0;

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

	if (!tb[EU_ACTIVATE_SERVICE_NAME] || !tb[EU_ACTIVATE_SERVICE_STATUS]) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (tb[EU_ACTIVATE_SERVICE_ENV]) {
		eid = get_eid_from_env_name(blobmsg_get_string(tb[EU_ACTIVATE_SERVICE_ENV]));
	}

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

	if (eid <= 0 || eid > MAX_ENV)
		return UBUS_STATUS_INVALID_ARGUMENT;

	snprintf(name, sizeof(name), "%s", blobmsg_get_string(tb[EU_ACTIVATE_SERVICE_NAME]));
	state = blobmsg_get_bool(tb[EU_ACTIVATE_SERVICE_STATUS]);

	int index = eid - 1;
	if (!environments[index].exists) {
		PRINT_INFO("No environment exists at index: %d", index);
		return -1;
	}

	int err = -1;
	if (strcmp(environments[index].name, HOST_SYSTEM) == 0) {
		err = swmod_set_host_service_state(name, state);
	} else {
#ifdef SWMOD_LXC
		err = swmod_set_lxc_service_state(index, name, state);
#endif
	}

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

	blobmsg_add_u8(&bb, "status", (err >= 0) ? true : false);

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

static int
swmod_ee_set_state(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[__EE_SET_STATE_MAX] = {NULL};
	int eid = 0;

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

	if (!tb[EE_SET_STATE_STATUS]) {
		return UBUS_STATUS_INVALID_ARGUMENT;
	}

	if (tb[EE_SET_STATE_ENV]) {
		eid = get_eid_from_env_name(blobmsg_get_string(tb[EE_SET_STATE_ENV]));
	}

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

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

	if (eid <= 0 || eid > MAX_ENV) {
		blobmsg_add_u8(&bb, "status", false);
		blobmsg_add_string(&bb, "reason", "invalid eid/environment");
		goto end;
	}

	int index = eid - 1;
	if (!environments[index].exists) {
		blobmsg_add_u8(&bb, "status", false);
		blobmsg_add_string(&bb, "reason", "no environment exist at this index");
		goto end;
	}

	if (strcmp(environments[index].name, HOST_SYSTEM) == 0) {
		blobmsg_add_u8(&bb, "status", false);
		blobmsg_add_string(&bb, "reason", "request can not be performed on host system");
		goto end;
	} else {
#ifdef SWMOD_LXC
		char state[10] = {0};
		snprintf(state, sizeof(state), "%s", blobmsg_get_string(tb[EE_SET_STATE_STATUS]));

		int err = swmod_set_ee_state(index, state);
		blobmsg_add_u8(&bb, "status", (err == 0) ? true : false);
		blobmsg_add_string(&bb, "reason", ee_state_failure_reason(err));
#else
		blobmsg_add_u8(&bb, "status", false);
		blobmsg_add_string(&bb, "reason", "LXC service not present");
#endif
	}

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

static const struct ubus_method swmod_object_methods[] = {
	UBUS_METHOD_NOARG("environment", 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("eu_activate", swmod_eu_activate, eu_activate_policy),
	UBUS_METHOD("ee_set_state", swmod_ee_set_state, ee_set_state_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)
{
	populate_environments();
	return synchronize_deployment_units_with_map_du_file();
}

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(atoi(optarg));
			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:
	ubus_free(ubus_ctx);
	uloop_done();
	closelog();

	return 0;
}