mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
629 lines
16 KiB
Text
629 lines
16 KiB
Text
--- /dev/null
|
|
+++ b/minissdpd/ssdpd.c
|
|
@@ -0,0 +1,626 @@
|
|
+/*
|
|
+ * Copyright (C) 2022 iopsys Software Solutions AB
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1
|
|
+ * as published by the Free Software Foundation
|
|
+ *
|
|
+ * Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
|
+ */
|
|
+
|
|
+#include <unistd.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdbool.h>
|
|
+#include <string.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/socket.h>
|
|
+#include <sys/un.h>
|
|
+#include <net/if.h>
|
|
+#include <syslog.h>
|
|
+
|
|
+#include <curl/curl.h>
|
|
+#include <libubox/uloop.h>
|
|
+#include <libubox/blobmsg_json.h>
|
|
+#include <libubox/list.h>
|
|
+#include <libubus.h>
|
|
+#include <mxml.h>
|
|
+
|
|
+#include "codelength.h"
|
|
+
|
|
+struct UPNPDev {
|
|
+ struct list_head list;
|
|
+ char *descURL;
|
|
+ char *st;
|
|
+ char *usn;
|
|
+};
|
|
+
|
|
+struct desc_list_elt {
|
|
+ struct list_head list;
|
|
+ char *url;
|
|
+ char *desc_path;
|
|
+ bool is_device_desc;
|
|
+};
|
|
+
|
|
+#define UPNP_DESC_PATH "/etc/upnp/description"
|
|
+#define UPNP_DISCOVER_TIMEOUT (30 * 1000)
|
|
+
|
|
+#ifndef MIN
|
|
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
+#endif /* MIN */
|
|
+
|
|
+/* macros used to read from unix socket */
|
|
+#define READ_BYTE_BUFFER(c) \
|
|
+ if ((int)bufferindex >= n) { \
|
|
+ n = read(s, buffer, sizeof(buffer)); \
|
|
+ if (n <= 0) break; \
|
|
+ bufferindex = 0; \
|
|
+ } \
|
|
+ c = buffer[bufferindex++];
|
|
+
|
|
+#define READ_COPY_BUFFER(dst, len) \
|
|
+ for (l = len, p = (unsigned char *)dst; l > 0; ) { \
|
|
+ unsigned int lcopy; \
|
|
+ if ((int)bufferindex >= n) { \
|
|
+ n = read(s, buffer, sizeof(buffer)); \
|
|
+ if ( n<= 0) break; \
|
|
+ bufferindex = 0; \
|
|
+ } \
|
|
+ lcopy = MIN(l, (n - bufferindex)); \
|
|
+ memcpy(p, buffer + bufferindex, lcopy); \
|
|
+ l -= lcopy; \
|
|
+ p += lcopy; \
|
|
+ bufferindex += lcopy; \
|
|
+ }
|
|
+
|
|
+LIST_HEAD(dev_list);
|
|
+LIST_HEAD(desc_list);
|
|
+
|
|
+char *ssdp_sockpath = NULL;
|
|
+
|
|
+static void upnp_discover_devices(struct uloop_timeout *timeout);
|
|
+static struct uloop_timeout upnpdiscover_timer = { .cb = upnp_discover_devices };
|
|
+
|
|
+static void add_dev_to_dev_list(char *descURL, char *st, char *usn)
|
|
+{
|
|
+ struct UPNPDev *dev = NULL;
|
|
+
|
|
+ dev = calloc(1, sizeof(struct UPNPDev));
|
|
+ list_add_tail(&dev->list, &dev_list);
|
|
+
|
|
+ dev->descURL = descURL;
|
|
+ dev->st = st;
|
|
+ dev->usn = usn;
|
|
+}
|
|
+
|
|
+void free_all_dev_list(void)
|
|
+{
|
|
+ struct UPNPDev *dev = NULL;
|
|
+
|
|
+ while (dev_list.next != &dev_list) {
|
|
+ dev = list_entry(dev_list.next, struct UPNPDev, list);
|
|
+ free(dev->descURL);
|
|
+ free(dev->st);
|
|
+ free(dev->usn);
|
|
+ free(dev);
|
|
+ list_del(&dev->list);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int connectToMiniSSDPD(void)
|
|
+{
|
|
+ int s = 0;
|
|
+ struct sockaddr_un addr;
|
|
+
|
|
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
+ if(s < 0)
|
|
+ return -1;
|
|
+
|
|
+ char *ssdp_s = ssdp_sockpath ? ssdp_sockpath : "/var/run/minissdpd.sock";
|
|
+
|
|
+ memset(&addr, 0, sizeof(addr));
|
|
+ addr.sun_family = AF_UNIX;
|
|
+
|
|
+ strncpy(addr.sun_path, ssdp_s, sizeof(addr.sun_path));
|
|
+
|
|
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
|
|
+ close(s);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return s;
|
|
+}
|
|
+
|
|
+static int disconnectFromMiniSSDPD(int s)
|
|
+{
|
|
+ if (close(s) < 0)
|
|
+ return -1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int requestDevicesFromMiniSSDPD(int s)
|
|
+{
|
|
+ unsigned char buffer[256];
|
|
+ unsigned char *p = NULL;
|
|
+ unsigned int stsize = 0, l = 0;
|
|
+ char *devtype = "ssdp:all";
|
|
+
|
|
+ buffer[0] = 3; /* request type 3 : everything */
|
|
+ stsize = strlen(devtype);
|
|
+
|
|
+ p = buffer + 1;
|
|
+ l = stsize; CODELENGTH(l, p);
|
|
+ if (p + stsize > buffer + sizeof(buffer))
|
|
+ return -1;
|
|
+
|
|
+ memcpy(p, devtype, stsize);
|
|
+ p += stsize;
|
|
+ if (write(s, buffer, p - buffer) < 0)
|
|
+ return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int receiveDevicesFromMiniSSDPD(int s)
|
|
+{
|
|
+ unsigned char buffer[256];
|
|
+ ssize_t n;
|
|
+ unsigned char *p;
|
|
+ unsigned int bufferindex;
|
|
+ unsigned int i, ndev;
|
|
+ unsigned int urlsize, stsize, usnsize, l;
|
|
+ char *url, *st, *usn;
|
|
+
|
|
+ n = read(s, buffer, sizeof(buffer));
|
|
+ if (n <= 0)
|
|
+ return -1;
|
|
+
|
|
+ ndev = buffer[0];
|
|
+ bufferindex = 1;
|
|
+ for (i = 0; i < ndev; i++) {
|
|
+ DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
|
|
+ if (n <= 0)
|
|
+ return -1;
|
|
+
|
|
+ url = (char *)malloc(urlsize);
|
|
+ if (url == NULL)
|
|
+ return -1;
|
|
+
|
|
+ READ_COPY_BUFFER(url, urlsize);
|
|
+ if (n <= 0)
|
|
+ return -1;
|
|
+
|
|
+ DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
|
|
+ if (n <= 0)
|
|
+ goto free_url_and_return;
|
|
+
|
|
+ st = (char *)malloc(stsize);
|
|
+ if (st == NULL)
|
|
+ goto free_url_and_return;
|
|
+
|
|
+ READ_COPY_BUFFER(st, stsize);
|
|
+ if (n <= 0)
|
|
+ goto free_url_and_st_and_return;
|
|
+
|
|
+ DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
|
|
+ if (n <= 0)
|
|
+ goto free_url_and_st_and_return;
|
|
+
|
|
+ usn = (char *)malloc(usnsize);
|
|
+ if (usn == NULL)
|
|
+ goto free_url_and_st_and_return;
|
|
+
|
|
+ READ_COPY_BUFFER(usn, usnsize);
|
|
+ if (n <= 0)
|
|
+ goto free_url_and_st_and_usn_and_return;
|
|
+
|
|
+ add_dev_to_dev_list(url, st, usn);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free_url_and_st_and_usn_and_return:
|
|
+ free(usn);
|
|
+free_url_and_st_and_return:
|
|
+ free(st);
|
|
+free_url_and_return:
|
|
+ free(url);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int getDevicesFromMiniSSDPD(void)
|
|
+{
|
|
+ int s = 0;
|
|
+ int res = 0;
|
|
+
|
|
+ s = connectToMiniSSDPD();
|
|
+ if (s < 0)
|
|
+ return -1;
|
|
+
|
|
+ res = requestDevicesFromMiniSSDPD(s);
|
|
+ if (res < 0)
|
|
+ goto close_socket_and_return;
|
|
+
|
|
+ res = receiveDevicesFromMiniSSDPD(s);
|
|
+
|
|
+close_socket_and_return:
|
|
+ disconnectFromMiniSSDPD(s);
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static void download_file(char *file_path, const char *url)
|
|
+{
|
|
+ CURL *curl = curl_easy_init();
|
|
+ if (curl) {
|
|
+ curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 500);
|
|
+
|
|
+ FILE *fp = fopen(file_path, "wb");
|
|
+ if (fp) {
|
|
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
|
+ curl_easy_perform(curl);
|
|
+ fclose(fp);
|
|
+ }
|
|
+
|
|
+ curl_easy_cleanup(curl);
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool is_desc_exist(const char *desc_url)
|
|
+{
|
|
+ struct desc_list_elt *desc_elt = NULL;
|
|
+
|
|
+ if (!desc_url)
|
|
+ return false;
|
|
+
|
|
+ list_for_each_entry(desc_elt, &desc_list, list) {
|
|
+ if (strcmp(desc_elt->url, desc_url) == 0)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void get_desc_name(const char *desc_url, char *str, size_t len)
|
|
+{
|
|
+ if (!desc_url || !str || len == 0)
|
|
+ return;
|
|
+
|
|
+ char *p = strstr(desc_url, "://");
|
|
+
|
|
+ snprintf(str, len, "%s", p ? p + 3 : desc_url);
|
|
+
|
|
+ for (int i = 0; str[i]; i++) {
|
|
+ if (str[i] == '/')
|
|
+ str[i] = '_';
|
|
+ }
|
|
+}
|
|
+
|
|
+static void add_desc_to_desc_list(const char *desc_path, const char *url, int is_device_desc)
|
|
+{
|
|
+ struct desc_list_elt *desc_elt;
|
|
+
|
|
+ desc_elt = calloc(1, sizeof(struct desc_list_elt));
|
|
+ list_add_tail(&desc_elt->list, &desc_list);
|
|
+
|
|
+ desc_elt->desc_path = strdup(desc_path);
|
|
+ desc_elt->url = strdup(url);
|
|
+ desc_elt->is_device_desc = is_device_desc;
|
|
+}
|
|
+
|
|
+static void free_all_desc_list(void)
|
|
+{
|
|
+ struct desc_list_elt *desc_elt = NULL;
|
|
+
|
|
+ while (desc_list.next != &desc_list) {
|
|
+ desc_elt = list_entry(desc_list.next, struct desc_list_elt, list);
|
|
+ free(desc_elt->desc_path);
|
|
+ free(desc_elt->url);
|
|
+ free(desc_elt);
|
|
+ list_del(&desc_elt->list);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void __upnp_discover_devices(void)
|
|
+{
|
|
+ struct UPNPDev *dev = NULL;
|
|
+ char desc_name[128] = {0};
|
|
+ char file_path[256] = {0};
|
|
+ int res = 0, is_device_desc = 0;
|
|
+
|
|
+ /*
|
|
+ * Discover devices
|
|
+ */
|
|
+ if (!list_empty(&dev_list))
|
|
+ free_all_dev_list();
|
|
+
|
|
+ res = getDevicesFromMiniSSDPD();
|
|
+ if (res)
|
|
+ goto end;
|
|
+
|
|
+ /*
|
|
+ * Download description files
|
|
+ */
|
|
+ list_for_each_entry_reverse(dev, &dev_list, list) {
|
|
+
|
|
+ if (is_desc_exist(dev->descURL))
|
|
+ continue;
|
|
+
|
|
+ get_desc_name(dev->descURL, desc_name, sizeof(desc_name));
|
|
+ snprintf(file_path, sizeof(file_path), "%s/%s", UPNP_DESC_PATH, desc_name);
|
|
+ is_device_desc = (dev->usn && strstr(dev->usn, ":service:")) ? 0 : 1;
|
|
+
|
|
+ // Download Description
|
|
+ download_file(file_path, dev->descURL);
|
|
+
|
|
+ // Add description to descriptions list
|
|
+ add_desc_to_desc_list(file_path, dev->descURL, is_device_desc);
|
|
+ }
|
|
+
|
|
+end:
|
|
+ uloop_timeout_set(&upnpdiscover_timer, UPNP_DISCOVER_TIMEOUT);
|
|
+}
|
|
+
|
|
+static int upnp_discovery_res(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
+ struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg __attribute__((unused)))
|
|
+{
|
|
+ struct blob_buf bb = {0};
|
|
+ struct UPNPDev *dev = NULL;
|
|
+
|
|
+ memset(&bb,0,sizeof(struct blob_buf));
|
|
+ blob_buf_init(&bb, 0);
|
|
+
|
|
+ void *devices_array = blobmsg_open_array(&bb, "devices");
|
|
+ list_for_each_entry_reverse(dev, &dev_list, list) {
|
|
+ // Parse Root device and devices
|
|
+ if ((dev->st && strstr(dev->st, ":rootdevice") != NULL) || (dev->usn && strstr(dev->usn, ":device:") != NULL)) {
|
|
+ void *device_obj = blobmsg_open_table(&bb, NULL);
|
|
+ blobmsg_add_string(&bb, "descurl", dev->descURL);
|
|
+ blobmsg_add_string(&bb, "st", dev->st);
|
|
+ blobmsg_add_string(&bb, "usn", dev->usn);
|
|
+ blobmsg_add_string(&bb, "is_root_device", dev->st && strstr(dev->st, ":rootdevice") ? "1" : "0");
|
|
+ blobmsg_close_table(&bb, device_obj);
|
|
+ }
|
|
+ }
|
|
+ blobmsg_close_array(&bb, devices_array);
|
|
+
|
|
+ void *services_array = blobmsg_open_array(&bb, "services");
|
|
+ list_for_each_entry_reverse(dev, &dev_list, list) {
|
|
+ // Parse Services
|
|
+ if (dev->usn && strstr(dev->usn, ":service:") != NULL) {
|
|
+ void *service_obj = blobmsg_open_table(&bb, NULL);
|
|
+ blobmsg_add_string(&bb, "descurl", dev->descURL);
|
|
+ blobmsg_add_string(&bb, "st", dev->st);
|
|
+ blobmsg_add_string(&bb, "usn", dev->usn);
|
|
+ blobmsg_close_table(&bb, service_obj);
|
|
+ }
|
|
+ }
|
|
+ blobmsg_close_array(&bb, services_array);
|
|
+
|
|
+ ubus_send_reply(ctx, req, bb.head);
|
|
+ blob_buf_free(&bb);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void fill_device_instances(struct blob_buf *bb, mxml_node_t *device)
|
|
+{
|
|
+ void *device_obj = NULL;
|
|
+ mxml_node_t *b = device;
|
|
+ char buf[64] = {0};
|
|
+ bool new_device_discovery = false;
|
|
+
|
|
+ while (b) {
|
|
+
|
|
+ if (mxmlGetType(b) != MXML_ELEMENT) {
|
|
+ b = mxmlWalkNext(b, device, MXML_DESCEND);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ const char *elm_name = mxmlGetElement(b);
|
|
+ const char *elm_val = mxmlGetOpaque(b);
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "device") == 0) {
|
|
+
|
|
+ if (new_device_discovery && device_obj)
|
|
+ blobmsg_close_table(bb, device_obj);
|
|
+
|
|
+ device_obj = blobmsg_open_table(bb, NULL);
|
|
+ blobmsg_add_string(bb, "parent_dev", buf);
|
|
+ new_device_discovery = true;
|
|
+ }
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "deviceType") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "deviceType", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "friendlyName") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "friendlyName", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "manufacturer") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "manufacturer", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "manufacturerURL") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "manufacturerURL", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "modelDescription") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "modelDescription", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "modelName") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "modelName", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "modelNumber") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "modelNumber", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "modelURL") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "modelURL", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "serialNumber") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "serialNumber", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "UDN") == 0 && new_device_discovery) {
|
|
+ snprintf(buf, sizeof(buf), "%s", elm_val ? elm_val : "");
|
|
+ blobmsg_add_string(bb, "UDN", buf);
|
|
+ }
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "UPC") == 0 && new_device_discovery)
|
|
+ blobmsg_add_string(bb, "UPC", elm_val ? elm_val : "");
|
|
+
|
|
+ b = mxmlWalkNext(b, device, MXML_DESCEND);
|
|
+ }
|
|
+
|
|
+ if (new_device_discovery && device_obj)
|
|
+ blobmsg_close_table(bb, device_obj);
|
|
+}
|
|
+
|
|
+static void fill_service_element(struct blob_buf *bb, mxml_node_t *service)
|
|
+{
|
|
+ mxml_node_t *b = service;
|
|
+ void *service_obj = NULL;
|
|
+ char buf[64] = {0};
|
|
+ bool new_srv_discovery = false;
|
|
+
|
|
+ while (b) {
|
|
+
|
|
+ if (mxmlGetType(b) != MXML_ELEMENT) {
|
|
+ b = mxmlWalkNext(b, service, MXML_DESCEND);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ const char *elm_name = mxmlGetElement(b);
|
|
+ const char *elm_val = mxmlGetOpaque(b);
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "UDN") == 0)
|
|
+ snprintf(buf, sizeof(buf), "%s", elm_val ? elm_val : "");
|
|
+
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "service") == 0) {
|
|
+
|
|
+ if (new_srv_discovery && service_obj)
|
|
+ blobmsg_close_table(bb, service_obj);
|
|
+
|
|
+ service_obj = blobmsg_open_table(bb, NULL);
|
|
+ blobmsg_add_string(bb, "parent_dev", buf);
|
|
+ new_srv_discovery = true;
|
|
+ }
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "serviceType") == 0 && new_srv_discovery)
|
|
+ blobmsg_add_string(bb, "serviceType", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "serviceId") == 0 && new_srv_discovery)
|
|
+ blobmsg_add_string(bb, "serviceId", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "SCPDURL") == 0 && new_srv_discovery)
|
|
+ blobmsg_add_string(bb, "SCPDURL", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "controlURL") == 0 && new_srv_discovery)
|
|
+ blobmsg_add_string(bb, "controlURL", elm_val ? elm_val : "");
|
|
+
|
|
+ if (elm_name && strcmp(elm_name, "eventSubURL") == 0 && new_srv_discovery)
|
|
+ blobmsg_add_string(bb, "eventSubURL", elm_val ? elm_val : "");
|
|
+
|
|
+ b = mxmlWalkNext(b, service, MXML_DESCEND);
|
|
+ }
|
|
+
|
|
+ if (new_srv_discovery && service_obj)
|
|
+ blobmsg_close_table(bb, service_obj);
|
|
+}
|
|
+
|
|
+static int upnp_description_res(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
+ struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg __attribute__((unused)))
|
|
+{
|
|
+ struct desc_list_elt *desc_elt = NULL;
|
|
+ struct blob_buf bb = {0};
|
|
+
|
|
+ memset(&bb,0,sizeof(struct blob_buf));
|
|
+ blob_buf_init(&bb, 0);
|
|
+
|
|
+ void *desc_array = blobmsg_open_array(&bb, "descriptions");
|
|
+ list_for_each_entry(desc_elt, &desc_list, list) {
|
|
+ void *device_obj = blobmsg_open_table(&bb, NULL);
|
|
+ blobmsg_add_string(&bb, "desc_url", desc_elt->url);
|
|
+ blobmsg_add_u32(&bb, "is_device_desc", desc_elt->is_device_desc);
|
|
+ blobmsg_close_table(&bb, device_obj);
|
|
+
|
|
+ }
|
|
+ blobmsg_close_array(&bb, desc_array);
|
|
+
|
|
+ list_for_each_entry(desc_elt, &desc_list, list) {
|
|
+
|
|
+ FILE *fp = fopen(desc_elt->desc_path, "r");
|
|
+ if (!fp)
|
|
+ continue;
|
|
+
|
|
+ mxml_node_t *tree = mxmlLoadFile(NULL, fp, MXML_OPAQUE_CALLBACK);
|
|
+ fclose(fp);
|
|
+
|
|
+ if (tree) {
|
|
+ void *devices_array = blobmsg_open_array(&bb, "devices");
|
|
+ mxml_node_t *device = mxmlFindElement(tree, tree, "device", NULL, NULL, MXML_DESCEND);
|
|
+ fill_device_instances(&bb, device);
|
|
+ blobmsg_close_array(&bb, devices_array);
|
|
+
|
|
+ void *services_array = blobmsg_open_array(&bb, "services");
|
|
+ mxml_node_t *service = mxmlFindElement(tree, tree, "device", NULL, NULL, MXML_DESCEND);
|
|
+ fill_service_element(&bb, service);
|
|
+ blobmsg_close_array(&bb, services_array);
|
|
+
|
|
+ mxmlDelete(tree);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ubus_send_reply(ctx, req, bb.head);
|
|
+ blob_buf_free(&bb);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct ubus_method upnp_methods[] = {
|
|
+ UBUS_METHOD_NOARG("discovery", upnp_discovery_res),
|
|
+ UBUS_METHOD_NOARG("description", upnp_description_res),
|
|
+};
|
|
+
|
|
+static struct ubus_object_type upnp_type = UBUS_OBJECT_TYPE("upnp", upnp_methods);
|
|
+
|
|
+static void upnp_discover_devices(struct uloop_timeout *timeout __attribute__((unused)))
|
|
+{
|
|
+ __upnp_discover_devices();
|
|
+}
|
|
+
|
|
+static struct ubus_object upnp_object = {
|
|
+ .name = "upnp",
|
|
+ .type = &upnp_type,
|
|
+ .methods = upnp_methods,
|
|
+ .n_methods = ARRAY_SIZE(upnp_methods),
|
|
+};
|
|
+
|
|
+void upnp_thread_discover_devices(void)
|
|
+{
|
|
+ struct ubus_context *ctx = NULL;
|
|
+ const char *ubus_socket = NULL;
|
|
+ int ret = 0;
|
|
+
|
|
+ uloop_init();
|
|
+
|
|
+ ctx = ubus_connect(ubus_socket);
|
|
+ if (!ctx) {
|
|
+ syslog(LOG_ERR, "Failed to connect to ubus\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ubus_add_uloop(ctx);
|
|
+
|
|
+ __upnp_discover_devices();
|
|
+
|
|
+ ret = ubus_add_object(ctx, &upnp_object);
|
|
+ if (ret) {
|
|
+ syslog(LOG_ERR, "Failed to add 'upnp' ubus object: %s\n", ubus_strerror(ret));
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+ uloop_run();
|
|
+
|
|
+end:
|
|
+ free_all_desc_list();
|
|
+ free_all_dev_list();
|
|
+ uloop_done();
|
|
+ ubus_free(ctx);
|
|
+}
|