--- a/minissdpd/minissdpd.c +++ b/minissdpd/minissdpd.c @@ -32,6 +32,8 @@ #include #include #endif +/* for uloop thread */ +#include /* LOG_PERROR does not exist on Solaris */ #ifndef LOG_PERROR @@ -52,6 +54,10 @@ #define MIN(x,y) (((x)<(y))?(x):(y)) #endif +extern char *ssdp_sockpath; +void upnp_thread_discover_devices(void); +void ssdpd_ubus_stop(void); + /* current request management structure */ struct reqelem { int socket; @@ -1220,6 +1226,12 @@ static void ssdpDiscover(int s, int ipv6 } } +static void *thread_discover_devices(void *args __attribute__((unused))) +{ + upnp_thread_discover_devices(); + return NULL; +} + /* main(): program entry point */ int main(int argc, char * * argv) { @@ -1264,6 +1276,7 @@ int main(int argc, char * * argv) unsigned char ttl = 2; /* UDA says it should default to 2 */ const char * searched_device = NULL; /* if not NULL, search/filter a specific device type */ int opt; + pthread_t upnp_thread; LIST_INIT(&reqlisthead); LIST_INIT(&servicelisthead); @@ -1309,6 +1322,7 @@ int main(int argc, char * * argv) break; case 's': sockpath = optarg; + ssdp_sockpath = optarg; break; #ifndef NO_BACKGROUND_NO_PIDFILE case 'p': @@ -1496,6 +1510,11 @@ int main(int argc, char * * argv) if(s_ssdp6 >= 0) ssdpDiscover(s_ssdp6, 1, searched_device); + int err = pthread_create(&upnp_thread, NULL, &thread_discover_devices, NULL); + if (err < 0) { + syslog(LOG_ERR, "Error when creating upnp thread"); + } + /* Main loop */ while(!quitting) { /* fill readfds fd_set */ @@ -1704,6 +1723,8 @@ quit: if(unlink(pidfilename) < 0) syslog(LOG_ERR, "unlink(%s): %m", pidfilename); #endif + ssdpd_ubus_stop(); + pthread_join(upnp_thread, NULL); closelog(); return ret; } --- a/minissdpd/ssdpd.c +++ b/minissdpd/ssdpd.c @@ -39,10 +39,9 @@ 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_DESC_PATH "/tmp/ssdp/description" #define UPNP_DISCOVER_TIMEOUT (30 * 1000) #ifndef MIN @@ -93,17 +92,41 @@ static void add_dev_to_dev_list(char *de dev->usn = usn; } -void free_all_dev_list(void) +static void free_all_dev_list(void) { struct UPNPDev *dev = NULL; + struct UPNPDev *dev_tmp = NULL; - while (dev_list.next != &dev_list) { - dev = list_entry(dev_list.next, struct UPNPDev, list); + list_for_each_entry_safe(dev, dev_tmp, &dev_list, list) { + list_del(&dev->list); free(dev->descURL); free(dev->st); free(dev->usn); free(dev); - list_del(&dev->list); + } +} + +static void add_desc_to_desc_list(const char *desc_path, const char *url) +{ + 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); +} + +static void free_all_desc_list(void) +{ + struct desc_list_elt *desc_elt = NULL; + struct desc_list_elt *desc_elt_tmp = NULL; + + list_for_each_entry_safe(desc_elt, desc_elt_tmp, &desc_list, list) { + list_del(&desc_elt->list); + free(desc_elt->desc_path); + free(desc_elt->url); + free(desc_elt); } } @@ -167,8 +190,8 @@ static int receiveDevicesFromMiniSSDPD(i ssize_t n; unsigned char *p; unsigned int bufferindex; - unsigned int i, ndev; - unsigned int urlsize, stsize, usnsize, l; + unsigned int i = 0, ndev = 0; + unsigned int urlsize = 0, stsize = 0, usnsize = 0, l = 0; char *url, *st, *usn; n = read(s, buffer, sizeof(buffer)); @@ -182,11 +205,12 @@ static int receiveDevicesFromMiniSSDPD(i if (n <= 0) return -1; - url = (char *)malloc(urlsize); + url = (char *)calloc(urlsize + 1, sizeof(char)); if (url == NULL) return -1; READ_COPY_BUFFER(url, urlsize); + url[urlsize] = 0; if (n <= 0) return -1; @@ -194,11 +218,12 @@ static int receiveDevicesFromMiniSSDPD(i if (n <= 0) goto free_url_and_return; - st = (char *)malloc(stsize); + st = (char *)calloc(stsize + 1, sizeof(char)); if (st == NULL) goto free_url_and_return; READ_COPY_BUFFER(st, stsize); + st[stsize] = 0; if (n <= 0) goto free_url_and_st_and_return; @@ -206,11 +231,12 @@ static int receiveDevicesFromMiniSSDPD(i if (n <= 0) goto free_url_and_st_and_return; - usn = (char *)malloc(usnsize); + usn = (char *)calloc(usnsize + 1, sizeof(char)); if (usn == NULL) goto free_url_and_st_and_return; READ_COPY_BUFFER(usn, usnsize); + usn[usnsize] = 0; if (n <= 0) goto free_url_and_st_and_usn_and_return; @@ -282,6 +308,21 @@ static bool is_desc_exist(const char *de return false; } +static bool is_device_exist(const char *dev_url) +{ + struct UPNPDev *dev = NULL; + + if (!dev_url) + return false; + + list_for_each_entry(dev, &dev_list, list) { + if (strcmp(dev->descURL, dev_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) @@ -297,37 +338,14 @@ static void get_desc_name(const char *de } } -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 desc_list_elt *desc_elt = NULL; + struct desc_list_elt *desc_elt_tmp = NULL; struct UPNPDev *dev = NULL; char desc_name[128] = {0}; char file_path[256] = {0}; - int res = 0, is_device_desc = 0; + int res = 0; /* * Discover devices @@ -349,13 +367,26 @@ static void __upnp_discover_devices(void 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); + add_desc_to_desc_list(file_path, dev->descURL); + } + + /* + * Remove unused descriptions + */ + list_for_each_entry_safe(desc_elt, desc_elt_tmp, &desc_list, list) { + + if (is_device_exist(desc_elt->url)) + continue; + + list_del(&desc_elt->list); + free(desc_elt->desc_path); + free(desc_elt->url); + free(desc_elt); } end: @@ -371,15 +402,27 @@ static int upnp_discovery_res(struct ubu memset(&bb,0,sizeof(struct blob_buf)); blob_buf_init(&bb, 0); + void *root_devices_array = blobmsg_open_array(&bb, "root_devices"); + list_for_each_entry_reverse(dev, &dev_list, list) { + // Parse root device + if (dev->st && strstr(dev->st, ":rootdevice") != 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_close_table(&bb, device_obj); + } + } + blobmsg_close_array(&bb, root_devices_array); + 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)) { + // Parse devices + if (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); } } @@ -472,7 +515,7 @@ static void fill_device_instances(struct blobmsg_close_table(bb, device_obj); } -static void fill_service_element(struct blob_buf *bb, mxml_node_t *service) +static void fill_service_instances(struct blob_buf *bb, mxml_node_t *service) { mxml_node_t *b = service; void *service_obj = NULL; @@ -525,6 +568,32 @@ static void fill_service_element(struct blobmsg_close_table(bb, service_obj); } +static void fill_device_service_instances(struct blob_buf *bb, bool is_device) +{ + struct desc_list_elt *desc_elt = NULL; + + 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) { + mxml_node_t *node = mxmlFindElement(tree, tree, "device", NULL, NULL, MXML_DESCEND); + + if (is_device) + fill_device_instances(bb, node); + else + fill_service_instances(bb, node); + + mxmlDelete(tree); + } + } +} + 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))) { @@ -534,39 +603,25 @@ static int upnp_description_res(struct u memset(&bb,0,sizeof(struct blob_buf)); blob_buf_init(&bb, 0); + // Descriptions Array 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); + // Devices Array + void *devices_array = blobmsg_open_array(&bb, "devices"); + fill_device_service_instances(&bb, true); + blobmsg_close_array(&bb, devices_array); - mxmlDelete(tree); - } - } + // Services Array + void *services_array = blobmsg_open_array(&bb, "services"); + fill_device_service_instances(&bb, false); + blobmsg_close_array(&bb, services_array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); @@ -624,3 +679,9 @@ end: uloop_done(); ubus_free(ctx); } + +void ssdpd_ubus_stop(void) +{ + uloop_end(); +} + --- a/minissdpd/config.h +++ b/minissdpd/config.h @@ -32,7 +32,7 @@ /* When NO_BACKGROUND_NO_PIDFILE is defined, minissdpd does not go to * background and does not create any pidfile */ -/*#define NO_BACKGROUND_NO_PIDFILE*/ +#define NO_BACKGROUND_NO_PIDFILE /* define HAVE_IP_MREQN to use struct ip_mreqn instead of struct ip_mreq * for setsockopt(IP_MULTICAST_IF). Available with Linux 2.4+,