diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..783e880f374e9ae0f99a2b8a2137d67a880973b6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+PROG = opkgd
+OBJS =opkg.o common.o
+
+PROG_CFLAGS = $(CFLAGS) -fstrict-aliasing -Wall -Wextra
+PROG_LDFLAGS = $(LDFLAGS)
+PROG_LDFLAGS += -luci -lubus -lubox -ljson-c -lblobmsg_json -luuid
+
+%.o: %.c
+	$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
+
+all: opkgd
+
+opkgd: $(OBJS)
+	$(CC) $(PROG_LDFLAGS) -o $@ $^
+
+clean:
+	rm -f *.o $(PROG)
diff --git a/common.c b/common.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b56fe6041465deecfce702f5174d6a20b750e52
--- /dev/null
+++ b/common.c
@@ -0,0 +1,501 @@
+/*
+ * common.c: OPKG deamon
+ *
+ * Copyright (C) 2019 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 "common.h"
+
+static struct uci_context *uci_ctx_opkg = NULL;
+
+int opkg_uci_init(void)
+{
+	uci_ctx_opkg = uci_alloc_context();
+	if (!uci_ctx_opkg) {
+		return -1;
+	}
+	uci_add_delta_path(uci_ctx_opkg, uci_ctx_opkg->savedir);
+	uci_set_savedir(uci_ctx_opkg, OPKG_SAVEDIR_PATH);
+	uci_set_confdir(uci_ctx_opkg, OPKG_CONFDIR_PATH);
+	return 0;
+}
+
+int opkg_uci_fini(void)
+{
+	if (uci_ctx_opkg) {
+		uci_free_context(uci_ctx_opkg);
+	}
+	return 0;
+}
+
+static bool opkg_uci_validate_section(const char *str)
+{
+	if (!*str)
+		return false;
+
+	for (; *str; str++) {
+		unsigned char c = *str;
+
+		if (isalnum(c) || c == '_')
+			continue;
+
+		return false;
+	}
+	return true;
+}
+
+static int opkg_uci_init_ptr(struct uci_ptr *ptr, char *package, char *section, char *option, char *value)
+{
+	memset(ptr, 0, sizeof(struct uci_ptr));
+
+	/* value */
+	if (value) {
+		ptr->value = value;
+	}
+	ptr->package = package;
+	if (!ptr->package)
+		goto error;
+
+	ptr->section = section;
+	if (!ptr->section) {
+		ptr->target = UCI_TYPE_PACKAGE;
+		goto lastval;
+	}
+
+	ptr->option = option;
+	if (!ptr->option) {
+		ptr->target = UCI_TYPE_SECTION;
+		goto lastval;
+	} else {
+		ptr->target = UCI_TYPE_OPTION;
+	}
+
+lastval:
+	if (ptr->section && !opkg_uci_validate_section(ptr->section))
+		ptr->flags |= UCI_LOOKUP_EXTENDED;
+
+	return 0;
+
+error:
+	return -1;
+}
+
+struct uci_section *opkg_uci_walk_section(char *package, char *section_type, struct uci_section *prev_section)
+{
+	struct uci_ptr ptr;
+	struct uci_element *e;
+	struct uci_section *next_section;
+
+	if (section_type == NULL) {
+		if (prev_section) {
+			e = &prev_section->e;
+			if (e->list.next == &prev_section->package->sections)
+				return NULL;
+			e = container_of(e->list.next, struct uci_element, list);
+			next_section = uci_to_section(e);
+			return next_section;
+		}
+		else {
+			if (opkg_uci_init_ptr(&ptr, package, NULL, NULL, NULL)) {
+				return NULL;
+			}
+			if (uci_lookup_ptr(uci_ctx_opkg, &ptr, NULL, true) != UCI_OK) {
+				return NULL;
+			}
+			if (ptr.p->sections.next == &ptr.p->sections)
+				return NULL;
+			e = container_of(ptr.p->sections.next, struct uci_element, list);
+			next_section = uci_to_section(e);
+
+			return next_section;
+		}
+	}
+	else {
+		struct uci_list *ul = NULL, *shead = NULL;
+
+		if (prev_section) {
+			ul = &prev_section->e.list;
+			shead = &prev_section->package->sections;
+		}
+		else {
+			if (opkg_uci_init_ptr(&ptr, package, NULL, NULL, NULL)) {
+				return NULL;
+			}
+			if (uci_lookup_ptr(uci_ctx_opkg, &ptr, NULL, true) != UCI_OK) {
+				return NULL;
+			}
+			ul = &ptr.p->sections;
+			shead = &ptr.p->sections;
+		}
+		while (ul->next != shead) {
+			e = container_of(ul->next, struct uci_element, list);
+			next_section = uci_to_section(e);
+			if (strcmp(next_section->type, section_type) == 0)
+				return next_section;
+			ul = ul->next;
+		}
+		return NULL;
+	}
+	return NULL;
+}
+
+void opkg_uci_print_list(struct uci_list *uh, char **val, char *delimiter)
+{
+	struct uci_element *e;
+	static char buffer[512];
+	char *buf = buffer;
+	*buf = '\0';
+
+	uci_foreach_element(uh, e) {
+		if (*buf) {
+			strcat(buf, delimiter);
+			strcat(buf, e->name);
+		}
+		else
+			strcpy(buf, e->name);
+	}
+	*val = buf;
+}
+
+struct uci_element *opkg_uci_lookup_list(struct uci_list *list, const char *name)
+{
+	struct uci_element *e;
+
+	uci_foreach_element(list, e) {
+		if (!strcmp(e->name, name))
+			return e;
+	}
+	return NULL;
+}
+
+int opkg_uci_lookup_ptr_by_section(struct uci_ptr *ptr, struct uci_section *section, char *option, char *value)
+{
+	struct uci_element *e;
+	memset(ptr, 0, sizeof(struct uci_ptr));
+
+	ptr->package = section->package->e.name;
+	ptr->section = section->e.name;
+	ptr->option = option;
+	ptr->value = value;
+	ptr->flags |= UCI_LOOKUP_DONE;
+
+	ptr->p = section->package;
+	ptr->s = section;
+
+	if (ptr->option) {
+		e = opkg_uci_lookup_list(&ptr->s->options, ptr->option);
+		if (!e)
+			return UCI_OK;
+		ptr->o = uci_to_option(e);
+		ptr->last = e;
+		ptr->target = UCI_TYPE_OPTION;
+	}
+	else {
+		ptr->last = &ptr->s->e;
+		ptr->target = UCI_TYPE_SECTION;
+	}
+
+	ptr->flags |= UCI_LOOKUP_COMPLETE;
+
+	return UCI_OK;
+}
+
+char *opkg_uci_get_value_by_section(struct uci_section *section, char *option)
+{
+	struct uci_ptr ptr;
+	char *val = "";
+
+	if (opkg_uci_lookup_ptr_by_section(&ptr, section, option, NULL) != UCI_OK)
+		return val;
+
+	if (!ptr.o)
+		return val;
+
+	if(ptr.o->type == UCI_TYPE_LIST) {
+		opkg_uci_print_list(&ptr.o->v.list, &val, " ");
+		return val;
+	}
+
+	if (ptr.o->v.string)
+		return ptr.o->v.string;
+	else
+		return val;
+}
+
+char *opkg_uci_set_value_by_section(struct uci_section *section, char *option, char *value)
+{
+	struct uci_ptr ptr;
+	int ret = UCI_OK;
+
+	if (section == NULL)
+		return "";
+
+	if (opkg_uci_lookup_ptr_by_section(&ptr, section, option, value) != UCI_OK)
+		return "";
+
+	uci_set(uci_ctx_opkg, &ptr);
+
+	if (ret == UCI_OK)
+		ret = uci_save(uci_ctx_opkg, ptr.p);
+
+	if (ptr.o && ptr.o->v.string)
+		return ptr.o->v.string;
+
+	return "";
+}
+
+struct uci_section *opkg_uci_add_section(char *package, char *section_type)
+{
+	struct uci_ptr ptr;
+	struct uci_section *s = NULL;
+
+	if (opkg_uci_init_ptr(&ptr, package, NULL, NULL, NULL))
+		return NULL;
+
+	if (uci_lookup_ptr(uci_ctx_opkg, &ptr, NULL, true) != UCI_OK)
+		return NULL;
+
+	int ret = uci_add_section(uci_ctx_opkg, ptr.p, section_type, &s);
+	if (ret != UCI_OK)
+		return NULL;
+
+	return s;
+}
+
+int opkg_uci_delete_by_section(struct uci_section *section, char *option, char *value)
+{
+	struct uci_ptr ptr = {0};
+
+	if (section == NULL)
+		return -1;
+
+	if (opkg_uci_lookup_ptr_by_section(&ptr, section, option, value) != UCI_OK)
+		return -1;
+
+	if (uci_delete(uci_ctx_opkg, &ptr) != UCI_OK)
+		return -1;
+
+	return 0;
+}
+
+void remove_newline(char *buf)
+{
+	int len;
+	len = strlen(buf) - 1;
+	if (buf[len] == '\n')
+		buf[len] = 0;
+}
+
+char *get_package_name(char *full_name)
+{
+	static char name[32] = "";
+	strncpy(name, full_name, sizeof(name));
+	char *dot = strchr(name, '.');
+	if (dot)
+		*dot = '\0';
+	return name;
+}
+
+int isfileexist(const char *filepath)
+{
+	if( access( filepath, F_OK ) != -1 )
+	    return 1;
+	else
+		return 0;
+}
+
+int islxcrunning(char *lxc_name)
+{
+	char command[256], state[16];
+	FILE *in;
+
+	if (isfileexist(LXC_INFO)) {
+			snprintf(command, sizeof(command), "lxc-info -n %s | grep State | tr -d ' ' | awk -F':' '{print$2}'", lxc_name);
+			if ((in = popen(command, "r"))) {
+				fgets(state, sizeof(state), in);
+				pclose(in);
+				remove_newline(state);
+			}
+			if (strcmp(state, "RUNNING") == 0)
+				return 1;
+			else
+				return 0;
+	}
+	return 0;
+}
+
+char *convert_url(char *url, char *username, char *password)
+{
+	static char buf[256] = "";
+
+	if (strncmp(url, "http://", 7) == 0)
+		snprintf(buf, sizeof(buf), "http://%s:%s@%s", username, password, url+7);
+	else if (strncmp(url, "ftp://", 6) == 0)
+		snprintf(buf, sizeof(buf), "ftp://%s:%s@%s", username, password, url+6);
+
+	return buf;
+}
+
+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)
+{
+	char buf[36] = "0123456789abcdefghijklmnopqrstuvwxyz", euid[64] = "", euid_num[8] = "";
+	int i;
+
+	srand(time(NULL));
+	if (sysnchronise) {
+		for (i = 0; i < 59; i++)
+			euid[i] = buf[rand() % 35];
+		euid[59] = '\0';
+		sprintf(euid_num, "%04d", number);
+		strcat(euid, euid_num);
+	} else {
+		for (i = 0; i < 62; i++)
+			euid[i] = buf[rand() % 35];
+		euid[63] = '\0';
+	}
+	return strdup(euid);
+}
+
+int synchronize_deployment_units_with_map_du_file(void)
+{
+	struct uci_section *s = NULL, *stmp = NULL;
+	char path[256], line[256], name[64], version[64], command[256];
+	FILE *log;
+	DIR *dir;
+	struct dirent *ent;
+	int found, incr = 0;
+
+	opkg_uci_foreach_section_safe("map_du", "deployment", stmp, s) {
+		char *map_du_name = opkg_uci_get_value_by_section(s, "name");
+		char *map_du_env = opkg_uci_get_value_by_section(s, "environment");
+		if (strcmp(map_du_env, "OpenWRT_Linux") == 0)
+			snprintf(path, sizeof(path), "%s/%s.control", OPKG_INFO_PATH, map_du_name);
+		else
+			snprintf(path, sizeof(path), "%s/%s/rootfs/%s/%s.control", LXC_PATH, map_du_env, OPKG_INFO_PATH, map_du_name);
+		if (!isfileexist(path))
+			opkg_uci_delete_by_section(s, NULL, NULL);
+	}
+
+	if ((log = popen("opkg list", "r"))) {
+		while(fgets(line, sizeof(line), log) != NULL) {
+			found = 0;
+			if (sscanf(line, "%63s - %63s", name, version)) {
+				opkg_uci_foreach_section("map_du", "deployment", s) {
+					char *map_name = opkg_uci_get_value_by_section(s, "name");
+					char *map_env = opkg_uci_get_value_by_section(s, "environment");
+					if ((strcmp(map_name, name) == 0) && (strcmp(map_env, "OpenWRT_Linux") == 0)) {
+						found = 1;
+						break;
+					}
+				}
+				if (found)
+					continue;
+
+				struct uci_section *new_s = opkg_uci_add_section("map_du", "deployment");
+				char *uuid = generate_uuid();
+				char *duid = generate_duid(true, incr);
+				incr++;
+				opkg_uci_set_value_by_section(new_s, "name", name);
+				opkg_uci_set_value_by_section(new_s, "version", version);
+				opkg_uci_set_value_by_section(new_s, "uuid", uuid);
+				opkg_uci_set_value_by_section(new_s, "duid", duid);
+				opkg_uci_set_value_by_section(new_s, "environment", "OpenWRT_Linux");
+				FREE(uuid);
+				FREE(duid);
+			}
+		}
+		pclose(log);
+	}
+
+	if (isfileexist(LXC_PATH)) {
+		sysfs_foreach_file(LXC_PATH, dir, ent) {
+			if ((strstr(ent->d_name, ".")) || (!islxcrunning(ent->d_name)))
+				continue;
+			snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg list", ent->d_name);
+			if ((log = popen(command, "r"))) {
+				while(fgets(line, sizeof(line), log) != NULL) {
+					found = 0;
+					if (sscanf(line, "%63s - %63s", name, version)) {
+						opkg_uci_foreach_section("map_du", "deployment", s) {
+							char *map_name = opkg_uci_get_value_by_section(s, "name");
+							char *map_env = opkg_uci_get_value_by_section(s, "environment");
+							if ((strcmp(map_name, name) == 0) && (strcmp(map_env, ent->d_name) == 0)) {
+								found = 1;
+								break;
+							}
+						}
+						if (found)
+							continue;
+
+						struct uci_section *new_s = opkg_uci_add_section("map_du", "deployment");
+						char *uuid = generate_uuid();
+						char *duid = generate_duid(true, incr);
+						incr++;
+						opkg_uci_set_value_by_section(new_s, "name", name);
+						opkg_uci_set_value_by_section(new_s, "version", version);
+						opkg_uci_set_value_by_section(new_s, "uuid", uuid);
+						opkg_uci_set_value_by_section(new_s, "duid", duid);
+						opkg_uci_set_value_by_section(new_s, "environment", ent->d_name);
+						FREE(uuid);
+						FREE(duid);
+					}
+				}
+				pclose(log);
+			}
+		}
+		if (dir) closedir(dir);
+	}
+	return 0;
+}
+
+void opkg_add_data_to_list(struct list_head *dup_list, char *cmd, int pid, unsigned int vsize)
+{
+	struct process_res *proc;
+	proc = calloc(1, sizeof(struct process_res));
+	list_add_tail(&proc->list, dup_list);
+	proc->cmd = strdup(cmd);
+	proc->pid = pid;
+	proc->vsize= vsize;
+}
+
+static void opkg_delete_data_from_list(struct process_res *proc)
+{
+	list_del(&proc->list);
+	FREE(proc->cmd);
+	FREE(proc);
+}
+
+void opkg_free_data_from_list(struct list_head *dup_list)
+{
+	struct process_res *proc;
+	while (dup_list->next != dup_list) {
+		proc = list_entry(dup_list->next, struct process_res, list);
+		opkg_delete_data_from_list(proc);
+	}
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..9a5b4967c869fa164923713805fc09223505dc75
--- /dev/null
+++ b/common.h
@@ -0,0 +1,94 @@
+/*
+ * common.h: OPKG deamon
+ *
+ * Copyright (C) 2019 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
+ */
+
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <uuid/uuid.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <uci.h>
+#include <libubox/list.h>
+
+#define OPKG_SAVEDIR_PATH "/etc/opkg"
+#define OPKG_CONFDIR_PATH "/etc/opkg"
+#define OPKG_INFO_PATH "/usr/lib/opkg/info"
+#define DEPLOYMENT_UNIT_PATH "/etc/opkg/map_du"
+#define PROC_PATH "/proc"
+#define LXC_PATH "/srv/lxc"
+#define LXC_INFO "/usr/bin/lxc-info"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+#define sysfs_foreach_file(path,dir,ent) \
+        if ((dir = opendir(path)) == NULL) return 0; \
+        while ((ent = readdir (dir)) != NULL) \
+
+#ifndef FREE
+#define FREE(x) do { if(x) {free(x); x = NULL;} } while (0)
+#endif
+
+struct process_res
+{
+	struct list_head list;
+	char *cmd;
+	int pid;
+	unsigned int vsize;
+};
+
+int opkg_uci_init(void);
+int opkg_uci_fini(void);
+struct uci_section *opkg_uci_walk_section(char *package, char *section_type, struct uci_section *prev_section);
+struct uci_section *opkg_uci_add_section(char *package, char *section_type);
+int opkg_uci_delete_by_section(struct uci_section *section, char *option, char *value);
+char *opkg_uci_get_value_by_section(struct uci_section *section, char *option);
+char *opkg_uci_set_value_by_section(struct uci_section *section, char *option, char *value);
+char *get_package_name(char *full_name);
+int isfileexist(const char *filepath);
+int synchronize_deployment_units_with_map_du_file(void);
+char *convert_url(char *url, char *username, char *password);
+char *generate_uuid(void);
+char *generate_duid(bool sysnchronise, int number);
+void remove_newline(char *buf);
+int islxcrunning(char *lxc_name);
+void opkg_add_data_to_list(struct list_head *dup_list, char *cmd, int pid, unsigned int vsize);
+void opkg_free_data_from_list(struct list_head *dup_list);
+
+#define opkg_uci_foreach_section(package, section_type, section) \
+	for (section = opkg_uci_walk_section(package, section_type, NULL); \
+		section != NULL; \
+		section = opkg_uci_walk_section(package, section_type, section))
+
+#define opkg_uci_foreach_section_safe(package, section_type, _tmp, section)	\
+	for(section = opkg_uci_walk_section(package, section_type, NULL), \
+		_tmp = (section) ? opkg_uci_walk_section(package, section_type, section) : NULL; \
+		section != NULL; \
+		section = _tmp, _tmp = (section) ? opkg_uci_walk_section(package, section_type, section) : NULL)
+
+#endif //__COMMON_H
+
diff --git a/opkg.c b/opkg.c
new file mode 100644
index 0000000000000000000000000000000000000000..33641c1e0c4d15aa765b8b4a886aba555bf48053
--- /dev/null
+++ b/opkg.c
@@ -0,0 +1,1210 @@
+/*
+ * main.c: OPKG deamon
+ *
+ * Copyright (C) 2019 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 <libubox/blobmsg.h>
+#include <libubox/uloop.h>
+#include <libubus.h>
+
+#include "common.h"
+
+static int opkg_software_environment(struct ubus_context *ctx, struct ubus_object *obj,
+									 struct ubus_request_data *req, const char *method,
+									 struct blob_attr *msg)
+{
+	void *a, *t;
+	DIR *dir;
+	struct dirent *ent;
+	FILE *in;
+	char buf[64], vendor[64], version[64], command[256];
+	char allocateddiskspace[10], availablediskspace[10], allocated_memory[10], available_memory[10];
+	struct blob_buf bb;
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	a = blobmsg_open_array(&bb, "environment");
+
+	t = blobmsg_open_table(&bb, "");
+	blobmsg_add_string(&bb, "name", "OpenWRT_Linux");
+	blobmsg_add_string(&bb, "status", "Up");
+	blobmsg_add_string(&bb, "type", "Linux");
+
+	if ((in = popen("uname -o", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(vendor, buf, sizeof(vendor));
+	}
+	blobmsg_add_string(&bb, "vendor", vendor);
+
+	if ((in = popen("uname -r", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(version, buf, sizeof(version));
+	}
+	blobmsg_add_string(&bb, "version", version);
+
+	if ((in = popen("free | grep Mem | awk '{print$2}'", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(allocateddiskspace, buf, sizeof(allocateddiskspace));
+	}
+	blobmsg_add_u32(&bb, "allocateddiskspace", atoi(allocateddiskspace));
+
+	if ((in = popen("free | grep Mem | awk '{print$4}'", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(availablediskspace, buf, sizeof(availablediskspace));
+	}
+	blobmsg_add_u32(&bb, "availablediskspace", atoi(availablediskspace));
+
+	if ((in = popen("df | grep overlayfs | awk '{print$2}'", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(allocated_memory, buf, sizeof(allocated_memory));
+	}
+	blobmsg_add_u32(&bb, "allocatedmemory", atoi(allocated_memory));
+
+	if ((in = popen("df | grep overlayfs | awk '{print$4}'", "r"))) {
+		fgets(buf, sizeof(buf), in);
+		pclose(in);
+		remove_newline(buf);
+		strncpy(available_memory, buf, sizeof(available_memory));
+	}
+	blobmsg_add_u32(&bb, "availablememory", atoi(available_memory));
+	blobmsg_close_table(&bb, t);
+
+	if (isfileexist(LXC_PATH)) {
+		sysfs_foreach_file(LXC_PATH, dir, ent) {
+			if (strstr(ent->d_name, "."))
+				continue;
+			t = blobmsg_open_table(&bb, "");
+			blobmsg_add_string(&bb, "name", ent->d_name);
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_string(&bb, "status", "Up");
+			else
+				blobmsg_add_string(&bb, "status", "Disabled");
+
+			blobmsg_add_string(&bb, "type", "Linux Container");
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_string(&bb, "vendor", "");
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- uname -o", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(vendor, buf, sizeof(vendor));
+				}
+				blobmsg_add_string(&bb, "vendor", vendor);
+			}
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_string(&bb, "version", "");
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- uname -r", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(version, buf, sizeof(version));
+				}
+				blobmsg_add_string(&bb, "version", version);
+			}
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_u32(&bb, "allocateddiskspace", 0);
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- free | grep Mem | awk '{print$2}'", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(allocateddiskspace, buf, sizeof(allocateddiskspace));
+				}
+				blobmsg_add_u32(&bb, "allocateddiskspace", atoi(allocateddiskspace));
+			}
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_u32(&bb, "availablediskspace", 0);
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- free | grep Mem | awk '{print$4}'", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(availablediskspace, buf, sizeof(availablediskspace));
+				}
+				blobmsg_add_u32(&bb, "availablediskspace", atoi(availablediskspace));
+			}
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_u32(&bb, "allocatedmemory", 0);
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- df | grep overlayfs | awk '{print$2}'", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(allocated_memory, buf, sizeof(allocated_memory));
+				}
+				blobmsg_add_u32(&bb, "allocatedmemory", atoi(allocated_memory));
+			}
+
+			if (islxcrunning(ent->d_name))
+				blobmsg_add_u32(&bb, "availablememory", 0);
+			else {
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- df | grep overlayfs | awk '{print$4}'", ent->d_name);
+				if ((in = popen(command, "r"))) {
+					fgets(buf, sizeof(buf), in);
+					pclose(in);
+					remove_newline(buf);
+					strncpy(available_memory, buf, sizeof(available_memory));
+				}
+				blobmsg_add_u32(&bb, "availablememory", atoi(available_memory));
+			}
+
+			blobmsg_close_table(&bb, t);
+		}
+		if (dir) closedir(dir);
+	}
+
+	blobmsg_close_array(&bb, a);
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+enum {
+	INSTALL_PATH,
+	INSTALL_ENV,
+	__INSTALL_MAX
+};
+
+static const struct blobmsg_policy install_policy[__INSTALL_MAX] = {
+	[INSTALL_PATH]     = { .name = "path",     .type = BLOBMSG_TYPE_STRING },
+	[INSTALL_ENV]     = { .name = "environment",     .type = BLOBMSG_TYPE_STRING },
+};
+
+static int opkg_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[__INSTALL_MAX] = {NULL};
+	char line[512], command[512], package_path[256];
+	struct blob_buf bb;
+	struct dirent *ent;
+	DIR *dir;
+	FILE *log;
+	int found;
+
+	if(blobmsg_parse(install_policy, __INSTALL_MAX, tb, blob_data(msg), blob_len(msg)))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	if (tb[INSTALL_PATH]) {
+		strncpy(package_path, blobmsg_get_string(tb[INSTALL_PATH]), sizeof(package_path));
+		if (!isfileexist(package_path))
+			return UBUS_STATUS_INVALID_ARGUMENT;
+	} else {
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	if (tb[INSTALL_ENV]) {
+		found = 0;
+		if (strcmp(blobmsg_get_string(tb[INSTALL_ENV]), "OpenWRT_Linux") != 0) {
+			if (isfileexist(LXC_PATH)) {
+				sysfs_foreach_file(LXC_PATH, dir, ent) {
+					if (strcmp(blobmsg_get_string(tb[INSTALL_ENV]), ent->d_name) == 0) {
+						found = 1;
+						break;
+					}
+				}
+				if (dir) closedir(dir);
+				if (!found)
+					return UBUS_STATUS_INVALID_ARGUMENT;
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		if (found) {
+			if (islxcrunning(blobmsg_get_string(tb[INSTALL_ENV])))
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", blobmsg_get_string(tb[INSTALL_ENV]), package_path);
+			else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		} else
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", package_path);
+	} else {
+		snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", package_path);
+	}
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if ((log = popen(command, "r"))) {
+		while (fgets(line, sizeof(line), log) != NULL) {
+			if (strstr(line, "Configuring")) {
+				blobmsg_add_u8(&bb,"status", true);
+				break;
+			} else if (strstr(line, "Unknown package")) {
+				blobmsg_add_u8(&bb,"status", false);
+				blobmsg_add_string(&bb, "error", "Cannot install package");
+				break;
+			} else if (strstr(line, "up to date")) {
+				blobmsg_add_u8(&bb,"status", false);
+				blobmsg_add_string(&bb, "error", "This package is already installed with the same version");
+				break;
+			} else if (strstr(line, "Not downgrading package")) {
+				blobmsg_add_u8(&bb,"status", false);
+				blobmsg_add_string(&bb, "error", "Cannot downgrade package");
+				break;
+			} else {
+				blobmsg_add_u8(&bb,"status", false);
+				blobmsg_add_string(&bb, "error", "dependencies package");
+				break;
+			}
+		}
+		pclose(log);
+	}
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+enum {
+	REMOVE_NAME,
+	REMOVE_ENV,
+	__REMOVE_MAX
+};
+
+static const struct blobmsg_policy remove_policy[__REMOVE_MAX] = {
+	[REMOVE_NAME]     = { .name = "name",     .type = BLOBMSG_TYPE_STRING },
+	[REMOVE_ENV]     = { .name = "environment",     .type = BLOBMSG_TYPE_STRING },
+};
+
+static int opkg_remove(struct ubus_context *ctx, struct ubus_object *obj,
+					   struct ubus_request_data *req, const char *method,
+					   struct blob_attr *msg)
+{
+	struct blob_attr *tb[__REMOVE_MAX] = {NULL};
+	struct blob_buf bb;
+	char line[512], command[256];
+	struct dirent *ent;
+	DIR *dir;
+	FILE *log;
+	int found;
+
+	if(blobmsg_parse(remove_policy, __REMOVE_MAX, tb, blob_data(msg), blob_len(msg)))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	if (!tb[REMOVE_NAME])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[REMOVE_ENV]) {
+		found = 0;
+		if (strcmp(blobmsg_get_string(tb[REMOVE_ENV]), "OpenWRT_Linux") != 0) {
+			if (isfileexist(LXC_PATH)) {
+				sysfs_foreach_file(LXC_PATH, dir, ent) {
+					if (strcmp(blobmsg_get_string(tb[REMOVE_ENV]), ent->d_name) == 0) {
+						found = 1;
+						break;
+					}
+				}
+				if (dir) closedir(dir);
+				if (!found)
+					return UBUS_STATUS_INVALID_ARGUMENT;
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		if (found) {
+			if (islxcrunning(blobmsg_get_string(tb[REMOVE_ENV])))
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", blobmsg_get_string(tb[REMOVE_ENV]), blobmsg_get_string(tb[REMOVE_NAME]));
+			else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		} else
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[REMOVE_NAME]));
+	} else {
+		snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[REMOVE_NAME]));
+	}
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if ((log = popen(command, "r"))) {
+		while(fgets(line, sizeof(line), log) != NULL) {
+			if (strstr(line, "Removing package")) {
+				blobmsg_add_u8(&bb,"status", true);
+				break;
+			} else if (strstr(line, "No packages removed")) {
+				blobmsg_add_u8(&bb,"status", false);
+				blobmsg_add_string(&bb, "error", "Cannot remove package");
+				break;
+			}
+		}
+		pclose(log);
+	}
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+enum {
+	DU_INSTALL_URL,
+	DU_INSTALL_UUID,
+	DU_INSTALL_USERNAME,
+	DU_INSTALL_PASSWORD,
+	DU_INSTALL_ENV,
+	__DU_INSTALL_MAX
+};
+
+static const struct blobmsg_policy du_install_policy[__DU_INSTALL_MAX] = {
+	[DU_INSTALL_URL]     = { .name = "url",     .type = BLOBMSG_TYPE_STRING },
+	[DU_INSTALL_UUID]     = { .name = "uuid",     .type = BLOBMSG_TYPE_STRING },
+	[DU_INSTALL_USERNAME]     = { .name = "username",     .type = BLOBMSG_TYPE_STRING },
+	[DU_INSTALL_PASSWORD]     = { .name = "password",     .type = BLOBMSG_TYPE_STRING },
+	[DU_INSTALL_ENV]     = { .name = "environment",     .type = BLOBMSG_TYPE_STRING },
+};
+
+static int opkg_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};
+	char line[512], command[512], name[64], version[64];
+	DIR *dir;
+	struct dirent *ent;
+	struct blob_buf bb;
+	int i = 0, found;
+	FILE *log;
+
+	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_URL])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[DU_INSTALL_ENV]) {
+		found = 0;
+		if (strcmp(blobmsg_get_string(tb[DU_INSTALL_ENV]), "OpenWRT_Linux") != 0) {
+			if (isfileexist(LXC_PATH)) {
+				sysfs_foreach_file(LXC_PATH, dir, ent) {
+					if (strcmp(blobmsg_get_string(tb[DU_INSTALL_ENV]), ent->d_name) == 0) {
+						found = 1;
+						break;
+					}
+				}
+				if (dir) closedir(dir);
+				if (!found)
+					return UBUS_STATUS_INVALID_ARGUMENT;
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		if (found) {
+			if (islxcrunning(blobmsg_get_string(tb[DU_INSTALL_ENV]))) {
+				if (tb[DU_INSTALL_USERNAME] &&
+					*(blobmsg_get_string(tb[DU_INSTALL_USERNAME])) != '\0' &&
+					tb[DU_INSTALL_PASSWORD] &&
+					*(blobmsg_get_string(tb[DU_INSTALL_PASSWORD])) != '\0') {
+					char *url = convert_url(blobmsg_get_string(tb[DU_INSTALL_URL]), blobmsg_get_string(tb[DU_INSTALL_USERNAME]), blobmsg_get_string(tb[DU_INSTALL_PASSWORD]));
+					snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_INSTALL_ENV]), url);
+				} else
+					snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_INSTALL_ENV]), blobmsg_get_string(tb[DU_INSTALL_URL]));
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		} else {
+			if (tb[DU_INSTALL_USERNAME] &&
+				*(blobmsg_get_string(tb[DU_INSTALL_USERNAME])) != '\0' &&
+				tb[DU_INSTALL_PASSWORD] &&
+				*(blobmsg_get_string(tb[DU_INSTALL_PASSWORD])) != '\0') {
+				char *url = convert_url(blobmsg_get_string(tb[DU_INSTALL_URL]), blobmsg_get_string(tb[DU_INSTALL_USERNAME]), blobmsg_get_string(tb[DU_INSTALL_PASSWORD]));
+				snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", url);
+			} else
+				snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_INSTALL_URL]));
+		}
+	} else {
+		if (tb[DU_INSTALL_USERNAME] &&
+			*(blobmsg_get_string(tb[DU_INSTALL_USERNAME])) != '\0' &&
+			tb[DU_INSTALL_PASSWORD] &&
+			*(blobmsg_get_string(tb[DU_INSTALL_PASSWORD])) != '\0') {
+			char *url = convert_url(blobmsg_get_string(tb[DU_INSTALL_URL]), blobmsg_get_string(tb[DU_INSTALL_USERNAME]), blobmsg_get_string(tb[DU_INSTALL_PASSWORD]));
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", url);
+		} else
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_INSTALL_URL]));
+	}
+
+	synchronize_deployment_units_with_map_du_file();
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if ((log = popen(command, "r"))) {
+		while(fgets(line, sizeof(line), log) != NULL) {
+			if (strstr(line, "Downloading"))
+				i++;
+
+			if (strstr(line, "Installing") || strstr(line, "installed")) {
+				sscanf(line, "Installing %63s (%63[^)] to root...", name, version);
+				i++;
+			}
+
+			if (strstr(line, "Configuring"))
+				i++;
+		}
+		pclose(log);
+	}
+
+	if (i == 1) {
+		blobmsg_add_u8(&bb,"status", false);
+		blobmsg_add_string(&bb,"error", "Download");
+	} else if (i == 2) {
+		blobmsg_add_u8(&bb,"status", false);
+		blobmsg_add_string(&bb,"error", "Install");
+	} else {
+		blobmsg_add_u8(&bb,"status", true);
+		blobmsg_add_string(&bb,"name", name);
+		blobmsg_add_string(&bb,"version", version);
+
+		struct uci_section *new_s = opkg_uci_add_section("map_du", "deployment");
+		opkg_uci_set_value_by_section(new_s, "name", name);
+		opkg_uci_set_value_by_section(new_s, "version", version);
+		opkg_uci_set_value_by_section(new_s, "url", blobmsg_get_string(tb[DU_INSTALL_URL]));
+
+		if (tb[DU_INSTALL_USERNAME] && *(blobmsg_get_string(tb[DU_INSTALL_USERNAME])) != '\0')
+			opkg_uci_set_value_by_section(new_s, "username", blobmsg_get_string(tb[DU_INSTALL_USERNAME]));
+
+		if (tb[DU_INSTALL_PASSWORD] && *(blobmsg_get_string(tb[DU_INSTALL_PASSWORD])) != '\0')
+			opkg_uci_set_value_by_section(new_s, "password", blobmsg_get_string(tb[DU_INSTALL_PASSWORD]));
+
+		if (tb[DU_INSTALL_ENV]) {
+			opkg_uci_set_value_by_section(new_s, "environment", blobmsg_get_string(tb[DU_INSTALL_ENV]));
+			blobmsg_add_string(&bb,"environment", blobmsg_get_string(tb[DU_INSTALL_ENV]));
+		} else {
+			opkg_uci_set_value_by_section(new_s, "environment", "OpenWRT_Linux");
+			blobmsg_add_string(&bb,"environment", "OpenWRT_Linux");
+		}
+
+		if (tb[DU_INSTALL_UUID] && *(blobmsg_get_string(tb[DU_INSTALL_UUID])) != '\0') {
+			//use the given UUID
+			opkg_uci_set_value_by_section(new_s, "uuid", blobmsg_get_string(tb[DU_INSTALL_UUID]));
+			blobmsg_add_string(&bb,"uuid", blobmsg_get_string(tb[DU_INSTALL_UUID]));
+		} else {
+			//generate a UUID
+			char *uuid = generate_uuid();
+			opkg_uci_set_value_by_section(new_s, "uuid", uuid);
+			blobmsg_add_string(&bb,"uuid", uuid);
+			FREE(uuid);
+		}
+
+		//generate a DUID
+		char *duid = generate_duid(false, 0);
+		opkg_uci_set_value_by_section(new_s, "duid", duid);
+		FREE(duid);
+	}
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+enum {
+	DU_UPDATE_UUID,
+	DU_UPDATE_URL,
+	DU_UPDATE_VERSION,
+	DU_UPDATE_USERNAME,
+	DU_UPDATE_PASSWORD,
+	__DU_UPDATE_MAX
+};
+
+static const struct blobmsg_policy du_update_policy[__DU_UPDATE_MAX] = {
+	[DU_UPDATE_UUID]     = { .name = "uuid",     .type = BLOBMSG_TYPE_STRING },
+	[DU_UPDATE_URL]     = { .name = "url",     .type = BLOBMSG_TYPE_STRING },
+	[DU_UPDATE_VERSION]     = { .name = "version",     .type = BLOBMSG_TYPE_STRING },
+	[DU_UPDATE_USERNAME]     = { .name = "username",     .type = BLOBMSG_TYPE_STRING },
+	[DU_UPDATE_PASSWORD]     = { .name = "password",     .type = BLOBMSG_TYPE_STRING },
+};
+
+
+static int opkg_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};
+	char line[512], command[512], name[64], version[64];
+	char *map_env = NULL, *map_uuid = NULL;
+	struct blob_buf bb;
+	struct uci_section *s = NULL;
+	DIR *dir;
+	struct dirent *ent;
+	int i = 0, found;
+	FILE *log;
+
+	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])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	synchronize_deployment_units_with_map_du_file();
+
+	opkg_uci_foreach_section("map_du", "deployment", s) {
+		map_uuid = opkg_uci_get_value_by_section(s, "uuid");
+		if (strcmp(map_uuid, blobmsg_get_string(tb[DU_UPDATE_UUID])) == 0) {
+			map_env = opkg_uci_get_value_by_section(s, "environment");
+			opkg_uci_set_value_by_section(s, "url", blobmsg_get_string(tb[DU_UPDATE_URL]));
+			if (tb[DU_UPDATE_USERNAME] && *(blobmsg_get_string(tb[DU_UPDATE_USERNAME])) != '\0')
+				opkg_uci_set_value_by_section(s, "username", blobmsg_get_string(tb[DU_UPDATE_USERNAME]));
+			if (tb[DU_UPDATE_PASSWORD] && *(blobmsg_get_string(tb[DU_UPDATE_PASSWORD])) != '\0')
+				opkg_uci_set_value_by_section(s, "password", blobmsg_get_string(tb[DU_UPDATE_PASSWORD]));
+			break;
+		}
+	}
+	if (map_env) {
+		found = 0;
+		if (strcmp(map_env, "OpenWRT_Linux") != 0) {
+			if (isfileexist(LXC_PATH)) {
+				sysfs_foreach_file(LXC_PATH, dir, ent) {
+					if (strcmp(map_env, ent->d_name) == 0) {
+						found = 1;
+						break;
+					}
+				}
+				if (dir) closedir(dir);
+				if (!found)
+					return UBUS_STATUS_INVALID_ARGUMENT;
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		if (found) {
+			if (islxcrunning(map_env)) {
+				if (tb[DU_UPDATE_USERNAME] &&
+					*(blobmsg_get_string(tb[DU_UPDATE_USERNAME])) != '\0' &&
+					tb[DU_UPDATE_PASSWORD] &&
+					*(blobmsg_get_string(tb[DU_UPDATE_PASSWORD])) != '\0') {
+					char *url = convert_url(blobmsg_get_string(tb[DU_UPDATE_URL]), blobmsg_get_string(tb[DU_UPDATE_USERNAME]), blobmsg_get_string(tb[DU_UPDATE_PASSWORD]));
+					snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", map_env, url);
+				} else
+					snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg install %s 2>/dev/null", map_env, blobmsg_get_string(tb[DU_UPDATE_URL]));
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		} else {
+			if (tb[DU_UPDATE_USERNAME] &&
+				*(blobmsg_get_string(tb[DU_UPDATE_USERNAME])) != '\0' &&
+				tb[DU_UPDATE_PASSWORD] &&
+				*(blobmsg_get_string(tb[DU_UPDATE_PASSWORD])) != '\0') {
+				char *url = convert_url(blobmsg_get_string(tb[DU_UPDATE_URL]), blobmsg_get_string(tb[DU_UPDATE_USERNAME]), blobmsg_get_string(tb[DU_UPDATE_PASSWORD]));
+				snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", url);
+			} else
+				snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_UPDATE_URL]));
+		}
+	} else {
+		if (tb[DU_UPDATE_USERNAME] &&
+			*(blobmsg_get_string(tb[DU_UPDATE_USERNAME])) != '\0' &&
+			tb[DU_UPDATE_PASSWORD] &&
+			*(blobmsg_get_string(tb[DU_UPDATE_PASSWORD])) != '\0') {
+			char *url = convert_url(blobmsg_get_string(tb[DU_UPDATE_URL]), blobmsg_get_string(tb[DU_UPDATE_USERNAME]), blobmsg_get_string(tb[DU_UPDATE_PASSWORD]));
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", url);
+		} else
+			snprintf(command, sizeof(command), "opkg install %s 2>/dev/null", blobmsg_get_string(tb[DU_UPDATE_URL]));
+	}
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if ((log = popen(command, "r"))) {
+		while(fgets(line, sizeof(line), log) != NULL) {
+			if (strstr(line, "Downloading"))
+				i++;
+
+			if (strstr(line, "Upgrading")) {
+				sscanf(line, "Upgrading %63s on root from %*s to %63s...", name, version);
+				i++;
+			}
+
+			if (strstr(line, "Configuring"))
+				i++;
+		}
+		pclose(log);
+	}
+	if (i != 3) {
+		blobmsg_add_u8(&bb,"status", false);
+	} else {
+		blobmsg_add_u8(&bb,"status", true);
+		blobmsg_add_string(&bb,"name", name);
+
+		//delete "..." from version string
+		char *p = strstr(version, "...");
+		if (p) *p = '\0';
+
+		blobmsg_add_string(&bb,"version", version);
+		blobmsg_add_string(&bb,"uuid", blobmsg_get_string(tb[DU_UPDATE_UUID]));
+		if (map_env)
+			blobmsg_add_string(&bb,"environment", map_env);
+		else
+			blobmsg_add_string(&bb,"environment", "OpenWRT_Linux");
+
+		//update version in map_du file
+		opkg_uci_foreach_section("map_du", "deployment", s) {
+			map_uuid = opkg_uci_get_value_by_section(s, "uuid");
+			if (strcmp(map_uuid, blobmsg_get_string(tb[DU_UPDATE_UUID])) == 0) {
+				opkg_uci_set_value_by_section(s, "version", version);
+				break;
+			}
+		}
+	}
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+
+	return 0;
+}
+
+enum {
+	DU_UNINSTALL_NAME,
+	DU_UNINSTALL_ENV,
+	__DU_UNINSTALL_MAX
+};
+
+static const struct blobmsg_policy du_uninstall_policy[__DU_UNINSTALL_MAX] = {
+	[DU_UNINSTALL_NAME]     = { .name = "name",     .type = BLOBMSG_TYPE_STRING },
+	[DU_UNINSTALL_ENV]     = { .name = "environment",     .type = BLOBMSG_TYPE_STRING },
+};
+
+static int opkg_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 line[512], command[512];
+	DIR *dir;
+	struct dirent *ent;
+	struct blob_buf bb;
+	int found;
+	FILE *log;
+
+	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;
+
+	if (tb[DU_UNINSTALL_ENV]) {
+		found = 0;
+		if (strcmp(blobmsg_get_string(tb[DU_UNINSTALL_ENV]), "OpenWRT_Linux") != 0) {
+			if (isfileexist(LXC_PATH)) {
+				sysfs_foreach_file(LXC_PATH, dir, ent) {
+					if (strcmp(blobmsg_get_string(tb[DU_UNINSTALL_ENV]), ent->d_name) == 0) {
+						found = 1;
+						break;
+					}
+				}
+				if (dir) closedir(dir);
+				if (!found)
+					return UBUS_STATUS_INVALID_ARGUMENT;
+			} else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		if (found) {
+			if (islxcrunning(blobmsg_get_string(tb[DU_UNINSTALL_ENV])))
+				snprintf(command, sizeof(command), "lxc-attach -n %s -- opkg remove %s 2>/dev/null", blobmsg_get_string(tb[DU_UNINSTALL_ENV]), blobmsg_get_string(tb[DU_UNINSTALL_NAME]));
+			else
+				return UBUS_STATUS_INVALID_ARGUMENT;
+		} else
+			snprintf(command, sizeof(command), "opkg remove %s 2>/dev/null", blobmsg_get_string(tb[DU_UNINSTALL_NAME]));
+	} else {
+		snprintf(command, sizeof(command), "opkg remove %s 2>/dev/null", blobmsg_get_string(tb[DU_UNINSTALL_NAME]));
+	}
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if ((log = popen(command, "r"))) {
+		while(fgets(line, sizeof(line), log) != NULL) {
+			if (strstr(line, "Removing package")) {
+				blobmsg_add_u8(&bb,"status", true);
+				break;
+			} else if (strstr(line, "No packages removed")) {
+				blobmsg_add_u8(&bb,"status", false);
+				break;
+			}
+		}
+		pclose(log);
+	}
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+enum {
+	DU_LIST_INDEX,
+	__DU_LIST_MAX
+};
+
+static const struct blobmsg_policy opkg_du_list_policy[__DU_LIST_MAX] = {
+	[DU_LIST_INDEX]     = { .name = "index",     .type = BLOBMSG_TYPE_INT32 },
+};
+
+static int opkg_du_list(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_LIST_MAX] = {NULL};
+	struct uci_section *ss = NULL;
+	struct blob_buf bb;
+	void *a, *t;
+	char *config = NULL;
+	FILE *fp;
+	int idx = 0, max = 0, incr = 0;
+
+	if(blobmsg_parse(opkg_du_list_policy, __DU_LIST_MAX, tb, blob_data(msg), blob_len(msg)))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	synchronize_deployment_units_with_map_du_file();
+
+	memset(&bb,0,sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	if (tb[DU_LIST_INDEX]) {
+		idx = blobmsg_get_u32(tb[DU_LIST_INDEX]);
+		max = idx + 100;
+	}
+
+	a = blobmsg_open_array(&bb, "deployment_unit");
+
+	opkg_uci_foreach_section("map_du", "deployment", ss) {
+		if ((incr < idx) && (idx != 0)) {
+			incr++;
+			continue;
+		}
+		char line[256], path[512], *spch = NULL;
+		char *soft_name = opkg_uci_get_value_by_section(ss, "name");
+		char *soft_env = opkg_uci_get_value_by_section(ss, "environment");
+		char *soft_uuid = opkg_uci_get_value_by_section(ss, "uuid");
+		char *soft_duid = opkg_uci_get_value_by_section(ss, "duid");
+		char *soft_version = opkg_uci_get_value_by_section(ss, "version");
+		char *soft_url = opkg_uci_get_value_by_section(ss, "url");
+
+		t = blobmsg_open_table(&bb, "");
+		blobmsg_add_string(&bb, "name", soft_name);
+		blobmsg_add_string(&bb, "version", soft_version);
+		blobmsg_add_string(&bb, "environment", soft_env);
+		blobmsg_add_string(&bb, "uuid", soft_uuid);
+		blobmsg_add_string(&bb, "duid", soft_duid);
+		if (soft_url && *soft_url != '\0')
+			blobmsg_add_string(&bb, "url", soft_url);
+
+		if (strcmp(soft_env, "OpenWRT_Linux") == 0)
+			snprintf(path, sizeof(path), "%s/%s.control", OPKG_INFO_PATH, soft_name);
+		else
+			snprintf(path, sizeof(path), "%s/%s/rootfs/%s/%s.control", LXC_PATH, soft_env, OPKG_INFO_PATH, soft_name);
+		fp = fopen(path, "r");
+		if ( fp != NULL) {
+			while (fgets(line, 256, fp) != NULL) {
+				if ((spch = strstr(line, "Maintainer:"))) {
+					remove_newline(spch);
+					blobmsg_add_string(&bb, "vendor", spch+12);
+				}
+
+				if ((spch = strstr(line, "Description:"))) {
+					remove_newline(spch);
+					blobmsg_add_string(&bb, "description", spch+14);
+					break;
+				}
+			}
+			fclose(fp);
+		}
+
+		if (strcmp(soft_env, "OpenWRT_Linux") == 0)
+			snprintf(path, sizeof(path), "%s/%s.list", OPKG_INFO_PATH, soft_name);
+		else
+			snprintf(path, sizeof(path), "%s/%s/rootfs/%s/%s.list", LXC_PATH, soft_env, OPKG_INFO_PATH, soft_name);
+		fp = fopen(path, "r");
+		if ( fp != NULL) {
+			while (fgets(line, 256, fp) != NULL) {
+				if ((config = strstr(line, "/etc/config/"))) {
+					remove_newline(line);
+					blobmsg_add_string(&bb, "config", config+12);
+					break;
+				}
+			}
+			fclose(fp);
+		}
+
+		blobmsg_close_table(&bb, t);
+
+		incr++;
+		if (incr == max) break;
+	}
+	blobmsg_close_array(&bb, a);
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+static int opkg_eu_list(struct ubus_context *ctx, struct ubus_object *obj,
+				 	 	struct ubus_request_data *req, const char *method,
+						struct blob_attr *msg)
+{
+	DIR *dir, *dir_opkg, *dir_lxc;
+	struct dirent *ent, *ent_opkg, *ent_lxc;
+	char fstat[64], fcmd[64], cmdline[256], fopkg[256], line[256], lxc_proc_path[256], lxc_opkg_info[256];
+	char *package_name;
+	int dgt, pid, ppid, found;
+	unsigned int bsize, vsize;
+	struct blob_buf bb;
+	void *a, *t;
+	FILE *fp;
+
+	memset(&bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	a = blobmsg_open_array(&bb, "execution_unit");
+
+	LIST_HEAD(proc_list);
+
+	//parse processes for OpenWRT_Linux
+	sysfs_foreach_file(PROC_PATH, dir, ent) {
+		dgt = ent->d_name[0] - '0';
+		if (dgt < 0 || dgt > 9)
+			continue;
+
+		snprintf(fcmd, sizeof(fcmd), "%s/%s/cmdline", PROC_PATH, ent->d_name);
+		fp = fopen(fcmd, "r");
+		if (fp == NULL)
+			continue;
+		if ( fgets (cmdline , 255 , fp) == NULL ) {
+			fclose(fp);
+			continue;
+		}
+		fclose(fp);
+
+		snprintf(fstat, sizeof(fstat), "%s/%s/stat", PROC_PATH, ent->d_name);
+		if( access(fstat, F_OK ) == -1 ) {
+			continue;
+		}
+
+		fp = fopen(fstat, "r");
+		if (fp == NULL)
+			continue;
+		if (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", &pid, &ppid, &bsize) == 0) {
+			fclose(fp);
+			continue;
+		}
+		fclose(fp);
+
+		if (ppid != 1)
+			continue;
+
+		vsize = bsize / 1024;
+		opkg_add_data_to_list(&proc_list, cmdline, pid, vsize);
+	}
+	if (dir) closedir(dir);
+
+	sysfs_foreach_file(OPKG_INFO_PATH, dir_opkg, ent_opkg) {
+		if (strstr(ent_opkg->d_name, "busybox")
+		 || strstr(ent_opkg->d_name, "bcmkernel")
+		 || strstr(ent_opkg->d_name, "juci")
+		 || strstr(ent_opkg->d_name, "lib")
+		 || strstr(ent_opkg->d_name, ".control")
+		 || strstr(ent_opkg->d_name, ".postrm")
+		 || strstr(ent_opkg->d_name, ".prerm")
+		 || strstr(ent_opkg->d_name, ".preinst")
+		 || strstr(ent_opkg->d_name, ".postinst"))
+			continue;
+
+		snprintf(fopkg, sizeof(fopkg), "%s/%s", OPKG_INFO_PATH, ent_opkg->d_name);
+		fp = fopen(fopkg, "r");
+		if (fp == NULL)
+			continue;
+		while (fgets(line, 256, fp) != NULL) {
+			found = 0;
+			if (strstr(line, "bin/")) {
+				struct process_res *p;
+				list_for_each_entry(p, &proc_list, list) {
+					if (strstr(line, p->cmd)) {
+						char line[256], path[512], version[64], *spch = NULL, *config = NULL;
+						int disk_space;
+						FILE *fp;
+
+						t = blobmsg_open_table(&bb, "");
+						package_name = get_package_name(ent_opkg->d_name);
+						blobmsg_add_string(&bb, "name", package_name);
+						blobmsg_add_string(&bb, "command", p->cmd);
+						blobmsg_add_u32(&bb, "euid", p->pid);
+						blobmsg_add_u32(&bb, "memory_space", p->vsize);
+						blobmsg_add_string(&bb, "environment", "OpenWRT_Linux");
+
+						snprintf(path, sizeof(path), "%s/%s.control", OPKG_INFO_PATH, package_name);
+						fp = fopen(path, "r");
+						if ( fp != NULL) {
+							while (fgets(line, 256, fp) != NULL) {
+								if(sscanf(line, "Version: %63s", version))
+									blobmsg_add_string(&bb, "version", version);
+
+								if(sscanf(line, "Installed-Size: %d", &disk_space))
+									blobmsg_add_u32(&bb, "disk_space", disk_space);
+
+								if ((spch = strstr(line, "Maintainer:"))) {
+									remove_newline(spch);
+									blobmsg_add_string(&bb, "vendor", spch+12);
+								}
+
+								if ((spch = strstr(line, "Description:"))) {
+									remove_newline(spch);
+									blobmsg_add_string(&bb, "description", spch+14);
+									break;
+								}
+							}
+							fclose(fp);
+						}
+
+						snprintf(path, sizeof(path), "%s/%s.list", OPKG_INFO_PATH, package_name);
+						fp = fopen(path, "r");
+						if ( fp != NULL) {
+							while (fgets(line, 256, fp) != NULL) {
+								if ((config = strstr(line, "/etc/config/"))) {
+									remove_newline(line);
+									blobmsg_add_string(&bb, "config", config+12);
+									break;
+								}
+							}
+							fclose(fp);
+						}
+
+						blobmsg_close_table(&bb, t);
+						found = 1;
+						break;
+					}
+				}
+			}
+			if (found)
+				break;
+		}
+		fclose(fp);
+	}
+	if (dir_opkg) closedir(dir_opkg);
+
+	//parse processes for linux containers
+	if (isfileexist(LXC_PATH)) {
+		sysfs_foreach_file(LXC_PATH, dir_lxc, ent_lxc) {
+			if ((strstr(ent_lxc->d_name, ".")) || (!islxcrunning(ent_lxc->d_name)))
+				continue;
+
+			snprintf(lxc_proc_path, sizeof(lxc_opkg_info), "%s/%s/rootfs/proc", LXC_PATH, ent_lxc->d_name);
+			sysfs_foreach_file(lxc_proc_path, dir, ent) {
+				dgt = ent->d_name[0] - '0';
+				if (dgt < 0 || dgt > 9)
+					continue;
+
+				snprintf(fcmd, sizeof(fcmd), "%s/%s/rootfs/proc/%s/cmdline", LXC_PATH, ent_lxc->d_name, ent->d_name);
+				fp = fopen(fcmd, "r");
+				if (fp == NULL)
+					continue;
+				if ( fgets (cmdline , 255 , fp) == NULL ) {
+					fclose(fp);
+					continue;
+				}
+				fclose(fp);
+
+				snprintf(fstat, sizeof(fstat), "%s/%s/rootfs/proc/%s/stat", LXC_PATH, ent_lxc->d_name, ent->d_name);
+				if( access(fstat, F_OK ) == -1 ) {
+					continue;
+				}
+
+				fp = fopen(fstat, "r");
+				if (fp == NULL)
+					continue;
+				if (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", &pid, &ppid, &bsize) == 0) {
+					fclose(fp);
+					continue;
+				}
+				fclose(fp);
+
+				if (ppid != 1)
+					continue;
+
+				vsize = bsize / 1024;
+				opkg_add_data_to_list(&proc_list, cmdline, pid, vsize);
+			}
+			if (dir) closedir(dir);
+
+			snprintf(lxc_opkg_info, sizeof(lxc_opkg_info), "%s/%s/rootfs%s", LXC_PATH, ent_lxc->d_name, OPKG_INFO_PATH);
+			sysfs_foreach_file(lxc_opkg_info, dir_opkg, ent_opkg) {
+				if (strstr(ent_opkg->d_name, "busybox")
+				 || strstr(ent_opkg->d_name, "bcmkernel")
+				 || strstr(ent_opkg->d_name, "juci")
+				 || strstr(ent_opkg->d_name, "lib")
+				 || strstr(ent_opkg->d_name, ".control")
+				 || strstr(ent_opkg->d_name, ".postrm")
+				 || strstr(ent_opkg->d_name, ".prerm")
+				 || strstr(ent_opkg->d_name, ".preinst")
+				 || strstr(ent_opkg->d_name, ".postinst"))
+					continue;
+
+				snprintf(fopkg, sizeof(fopkg), "%s/%s/rootfs%s/%s", LXC_PATH, ent_lxc->d_name, OPKG_INFO_PATH, ent_opkg->d_name);
+				fp = fopen(fopkg, "r");
+				if (fp == NULL)
+					continue;
+				while (fgets(line, 256, fp) != NULL) {
+					found = 0;
+					if (strstr(line, "bin/")) {
+						struct process_res *p;
+						list_for_each_entry(p, &proc_list, list) {
+							if (strstr(line, p->cmd)) {
+								char line[256], path[512], version[64], *spch = NULL, *config = NULL;
+								int disk_space;
+								FILE *fp;
+
+								t = blobmsg_open_table(&bb, "");
+								package_name = get_package_name(ent_opkg->d_name);
+								blobmsg_add_string(&bb, "name", package_name);
+								blobmsg_add_string(&bb, "command", p->cmd);
+								blobmsg_add_u32(&bb, "euid", p->pid);
+								blobmsg_add_u32(&bb, "memory_space", p->vsize);
+								blobmsg_add_string(&bb, "environment", ent_lxc->d_name);
+
+								snprintf(path, sizeof(path), "%s/%s/rootfs%s/%s.control", LXC_PATH, ent_lxc->d_name, OPKG_INFO_PATH, package_name);
+								fp = fopen(path, "r");
+								if ( fp != NULL) {
+									while (fgets(line, 256, fp) != NULL) {
+										if(sscanf(line, "Version: %63s", version))
+											blobmsg_add_string(&bb, "version", version);
+
+										if(sscanf(line, "Installed-Size: %d", &disk_space))
+											blobmsg_add_u32(&bb, "disk_space", disk_space);
+
+										if ((spch = strstr(line, "Maintainer:"))) {
+											remove_newline(spch);
+											blobmsg_add_string(&bb, "vendor", spch+12);
+										}
+
+										if ((spch = strstr(line, "Description:"))) {
+											remove_newline(spch);
+											blobmsg_add_string(&bb, "description", spch+14);
+											break;
+										}
+									}
+									fclose(fp);
+								}
+
+								snprintf(path, sizeof(path), "%s/%s/rootfs%s/%s.list", LXC_PATH, ent_lxc->d_name, OPKG_INFO_PATH, package_name);
+								fp = fopen(path, "r");
+								if ( fp != NULL) {
+									while (fgets(line, 256, fp) != NULL) {
+										if ((config = strstr(line, "/etc/config/"))) {
+											remove_newline(line);
+											blobmsg_add_string(&bb, "config", config+12);
+											break;
+										}
+									}
+									fclose(fp);
+								}
+
+								blobmsg_close_table(&bb, t);
+								found = 1;
+								break;
+							}
+						}
+					}
+					if (found)
+						break;
+				}
+				fclose(fp);
+			}
+			if (dir_opkg) closedir(dir_opkg);
+		}
+		if (dir_lxc) closedir(dir_lxc);
+	}
+
+	blobmsg_close_array(&bb, a);
+
+	ubus_send_reply(ctx, req, bb.head);
+	blob_buf_free(&bb);
+	opkg_free_data_from_list(&proc_list);
+	return 0;
+}
+
+static struct ubus_method opkg_object_methods[] = {
+	UBUS_METHOD_NOARG("environment", opkg_software_environment),
+	UBUS_METHOD("install", opkg_install, install_policy),
+	UBUS_METHOD("remove", opkg_remove, remove_policy),
+	UBUS_METHOD("du_install", opkg_du_install, du_install_policy),
+	UBUS_METHOD("du_update", opkg_du_update, du_update_policy),
+	UBUS_METHOD("du_uninstall", opkg_du_uninstall, du_uninstall_policy),
+	UBUS_METHOD("du_list", opkg_du_list, opkg_du_list_policy),
+	UBUS_METHOD_NOARG("eu_list", opkg_eu_list),
+};
+
+static struct ubus_object_type opkg_object_type = UBUS_OBJECT_TYPE("softwaremanagement", opkg_object_methods);
+
+static struct ubus_object opkg_object = {
+	.name = "softwaremanagement",
+	.type = &opkg_object_type,
+	.methods = opkg_object_methods,
+	.n_methods = ARRAY_SIZE(opkg_object_methods),
+};
+
+static void opkg_init(struct ubus_context *ctx)
+{
+	int ret;
+
+	ret = ubus_add_object(ctx, &opkg_object);
+	if (ret)
+		fprintf(stderr, "Failed to publish '%s' object : %s\n", opkg_object.name, ubus_strerror(ret));
+
+	uloop_run();
+}
+
+int main(void)
+{
+	const char *ubus_socket = NULL;
+	struct ubus_context *ctx = NULL;
+
+	uloop_init();
+	opkg_uci_init();
+	ctx = ubus_connect(ubus_socket);
+	if (!ctx) {
+		fprintf(stderr, "Failed to connect to ubus\n");
+		return -1;
+	}
+
+	synchronize_deployment_units_with_map_du_file();
+	ubus_add_uloop(ctx);
+	opkg_init(ctx);
+
+	uloop_run();
+	ubus_free(ctx);
+	opkg_uci_fini();
+	if (ctx) ubus_free(ctx);
+	uloop_done();
+
+	return 0;
+}