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; +}