Skip to content
Snippets Groups Projects
utils.c 20 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vivek Dutta's avatar
    Vivek Dutta committed
     * utils.c - common utility functions
     *
     * Copyright (C) 2022, IOPSYS Software Solutions AB.
     *
     * Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
     *
     * See LICENSE file for license related information.
     *
     */
    
    #include <zlib.h>
    
    #include <libubus.h>
    #include <libubox/blob.h>
    #include <libubox/blobmsg.h>
    #include <libubox/blobmsg_json.h>
    #include "utils.h"
    
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    #define DM_OBJECT_NAME "bbfdm"
    
    
    static unsigned char LogLevel = DEFAULT_LOG_LEVEL;
    static bool g_usp_object_available = false;
    
    
    enum compress_type {
    	COMPRESS_TYPE_GZIP,
    	COMPRESS_TYPE_COMPRESS,
    	COMPRESS_TYPE_DEFLATE
    };
    
    
    void set_log_level(unsigned char level)
    {
    	LogLevel = level;
    }
    
    void print_error(const char *format, ...)
    {
    	va_list arglist;
    
    	if (LogLevel < 1)
    		return;
    
    	va_start(arglist, format);
    	vsyslog(LOG_ERR, format, arglist);
    	va_end(arglist);
    }
    
    void print_warning(const char *format, ...)
    {
    	va_list arglist;
    
    	if (LogLevel < 2)
    		return;
    
    	va_start(arglist, format);
    	vsyslog(LOG_WARNING, format, arglist);
    	va_end(arglist);
    }
    
    void print_info(const char *format, ...)
    {
    	va_list arglist;
    
    	if (LogLevel < 3)
    		return;
    
    	va_start(arglist, format);
    	vsyslog(LOG_INFO, format, arglist);
    	va_end(arglist);
    }
    
    void print_debug(const char *format, ...)
    {
    	va_list arglist;
    
    	if (LogLevel < 4)
    		return;
    
    	va_start(arglist, format);
    	vsyslog(LOG_DEBUG, format, arglist);
    	va_end(arglist);
    }
    
    const char *bulkdata_get_time(void)
    {
    	static char local_time[64];
    
    	time_t t_time = time(NULL);
    	struct tm *t_tm = localtime(&t_time);
    	if (t_tm == NULL)
    		return NULL;
    
    	if (strftime(local_time, sizeof(local_time),"Date: %a, %d %b %Y %X%z GMT", t_tm) == 0)
    		return NULL;
    
    	return local_time;
    }
    
    void get_time_stamp(const char *format, char **timestamp)
    {
    	time_t now = time(NULL);
    
    
    	if (strcasecmp(format, "Unix-Epoch") == 0) {
    
    		asprintf(timestamp, "%lld", (long long int)now);
    
    	} else if (strcasecmp(format, "ISO-8601") == 0) {
    
    		char buf[32] = {0};
    		struct tm *ts = localtime(&now);
    		strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%Z", ts);
    		asprintf(timestamp, "%s", buf);
    	} else
    		*timestamp = NULL;
    }
    
    unsigned int get_next_period(time_t time_reference, int reporting_interval)
    {
    	unsigned int next_period;
    	time_t now = time(NULL);
    
    	if (now > time_reference)
    		next_period = reporting_interval - ((now - time_reference) % reporting_interval);
    	else
    		next_period = (time_reference - now) % reporting_interval;
    
    	if (next_period == 0)
    		next_period = reporting_interval;
    
    	return next_period;
    }
    
    static void bulkdata_add_data_to_list(struct list_head *dup_list, char *name, char *value, char *type)
    {
    	param_data_t *link = calloc(1, sizeof(param_data_t));
    
    	list_add_tail(&link->list, dup_list);
    	link->name = strdup(name);
    	link->data = strdup(value);
    	link->type = strdup(type);
    }
    
    void bulkdata_free_data_from_list(struct list_head *dup_list)
    {
    	param_data_t *link = NULL;
    
    	while (dup_list->next != dup_list) {
    		link = list_entry(dup_list->next, param_data_t, list);
    		list_del(&link->list);
    		FREE(link->name);
    		FREE(link->data);
    		FREE(link->type);
    		FREE(link);
    	}
    }
    
    static int detect_fault(struct blob_attr *msg)
    {
    	int fault = 0;
    	enum {
    		FAULT_ID,
    		__FAULT_MAX
    	};
    	struct blob_attr *f_attr[__FAULT_MAX] = {NULL};
    	const struct blobmsg_policy fp[__FAULT_MAX] = {
    		{ "fault", BLOBMSG_TYPE_INT32 }
    	};
    
    	blobmsg_parse(fp, __FAULT_MAX, f_attr, blobmsg_data(msg), blobmsg_len(msg));
    
    	if (f_attr[FAULT_ID])
    		fault = blobmsg_get_u32(f_attr[FAULT_ID]);
    
    	return fault;
    }
    
    static struct blob_attr *get_parameters(struct blob_attr *msg)
    {
    	struct blob_attr *params = NULL;
    	struct blob_attr *cur;
    	int rem;
    
    	blobmsg_for_each_attr(cur, msg, rem) {
    		if (blobmsg_type(cur) == BLOBMSG_TYPE_ARRAY) {
    			params = cur;
    			break;
    		}
    	}
    
    	return params;
    }
    
    static void get_value_single_cb(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
    {
    	 vendor_data_t *arg = req->priv;
    	struct blob_attr *params;
    	struct blob_attr *cur;
    	size_t rem;
    	enum {
    		P_PARAM,
    		P_VALUE,
    		__P_MAX
    	};
    	const struct blobmsg_policy p[__P_MAX] = {
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    		{ "path", BLOBMSG_TYPE_STRING },
    		{ "data", BLOBMSG_TYPE_STRING }
    
    	};
    
    	if (!msg)
    		return;
    
    	if (detect_fault(msg))
    		return;
    
    	params = get_parameters(msg);
    	if (params == NULL)
    		return;
    
    	blobmsg_for_each_attr(cur, params, rem) {
    		struct blob_attr *tb[__P_MAX] = {NULL, NULL};
    		char *path, *val;
    
    		blobmsg_parse(p, __P_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
    
    		if (!tb[P_PARAM] || !tb[P_VALUE])
    			continue;
    
    		path = blobmsg_get_string(tb[P_PARAM]);
    		val = blobmsg_get_string(tb[P_VALUE]);
    
    		if (strcmp(path, arg->path) == 0) {
    			arg->p_value = strdup(val);
    			break;
    		}
    	}
    }
    
    static void get_value_group_cb(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
    {
    	vendor_data_t *arg = req->priv;
    	struct blob_attr *params;
    	struct blob_attr *cur;
    	size_t rem;
    	enum {
    		P_PARAM,
    		P_VALUE,
    		P_TYPE,
    		__P_MAX
    	};
    	const struct blobmsg_policy p[__P_MAX] = {
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    		{ "path", BLOBMSG_TYPE_STRING },
    		{ "data", BLOBMSG_TYPE_STRING },
    
    		{ "type", BLOBMSG_TYPE_STRING }
    	};
    
    	if (!msg)
    		return;
    
    	if (detect_fault(msg))
    		return;
    
    	params = get_parameters(msg);
    	if (params == NULL)
    		return;
    
    	blobmsg_for_each_attr(cur, params, rem) {
    		struct blob_attr *tb[__P_MAX] = {NULL, NULL};
    		char *path, *val, *ptype;
    
    		blobmsg_parse(p, __P_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
    
    		if (!tb[P_PARAM] || !tb[P_VALUE] || !tb[P_TYPE])
    			continue;
    
    		path = blobmsg_get_string(tb[P_PARAM]);
    		val = blobmsg_get_string(tb[P_VALUE]);
    		ptype = blobmsg_get_string(tb[P_TYPE]);
    
    		bulkdata_add_data_to_list(arg->list, path, val, ptype);
    	}
    }
    
    static int uspd_call(struct ubus_context *ctx, char *method,
    		     struct blob_buf *data, ubus_data_handler_t callback,
    		     vendor_data_t *arg)
    {
    #define UBUS_TIMEOUT 5
    
    	uint32_t id;
    
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	if (ubus_lookup_id(ctx, DM_OBJECT_NAME, &id))
    
    		return -1;
    
    	// Invoke Ubus to get data from uspd
    	if (ubus_invoke(ctx, id, method, data->head, callback, arg, UBUS_TIMEOUT * 1000))
    		return -1;
    
    	return 0;
    }
    
    int get_value_single(vendor_data_t *arg)
    {
    	struct ubus_context *ubus_ctx = NULL;
    	struct blob_buf bb = {0};
    	int res = 0;
    
    	ubus_ctx = ubus_connect(NULL);
    	if (ubus_ctx == NULL)
    		return -1;
    
    	memset(&bb, 0, sizeof(struct blob_buf));
    	blob_buf_init(&bb, 0);
    
    	blobmsg_add_string(&bb, "path", arg->path);
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	void *table = blobmsg_open_table(&bb, "optional");
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	blobmsg_add_string(&bb, "format", "raw");
    	blobmsg_close_table(&bb, table);
    
    
    	// Invoke Ubus to get data from uspd
    	res = uspd_call(ubus_ctx, "get", &bb, get_value_single_cb, arg);
    
    	blob_buf_free(&bb);
    	ubus_free(ubus_ctx);
    	return res;
    }
    
    int get_value_group(vendor_data_t *arg)
    {
    	struct ubus_context *ubus_ctx = NULL;
    	struct blob_buf bb = {0};
    	int res = 0;
    
    	ubus_ctx = ubus_connect(NULL);
    	if (ubus_ctx == NULL)
    		return -1;
    
    	memset(&bb, 0, sizeof(struct blob_buf));
    	blob_buf_init(&bb, 0);
    
    	blobmsg_add_string(&bb, "path", arg->path);
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	void *table = blobmsg_open_table(&bb, "optional");
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	blobmsg_add_string(&bb, "format", "raw");
    	blobmsg_close_table(&bb, table);
    
    
    	// Invoke Ubus to get data from uspd
    	res = uspd_call(ubus_ctx, "get", &bb, get_value_group_cb, arg);
    
    	blob_buf_free(&bb);
    	ubus_free(ubus_ctx);
    	return res;
    }
    
    static void create_request_url(struct profile *profile, char *url, size_t len)
    {
    	int http_uri_number = profile->profile_http_request_uri_parameter_number;
    	char *value = NULL;
    	int res = 0;
    	unsigned pos = 0;
    
    	pos += snprintf(&url[pos], len - pos, "%s", profile->http_url);
    
    	for (int i = 0; i < http_uri_number; i++) {
    
    		struct profile_http_request_uri_parameter *p = &profile->profile_http_uri_parameter[i];
    
    		if (!p->reference ||
    			*(p->reference) == 0 ||
    			!p->name  ||
    			*(p->name) == 0)
    			continue;
    
    
    		vendor_data_t uri_data = { .path = p->reference };
    		res = get_value_single(&uri_data);
    		value = (res == 0) ? uri_data.p_value : strdup("");
    
    		pos += snprintf(&url[pos], len - pos, "&%s=%s", p->name, value);
    		FREE(value);
    	}
    }
    
    
    static unsigned char *compress_report(char *input_buf, long input_len, long *content_len, int type)
    {
    	z_stream zlib_ctx;
    	int err;
    	int output_len;
    	unsigned char *output_buf;
    
    	if (content_len == NULL) {
    		return NULL;
    	}
    
    	// Exit if not GZIP compression - in this case we just don't compress the report
    	if (type != COMPRESS_TYPE_GZIP || input_buf == NULL) {
    		*content_len = input_len;
    		return (unsigned char *)input_buf;
    	}
    
    	// Initialise the zlib context
    	memset(&zlib_ctx, 0, sizeof(zlib_ctx));
    	zlib_ctx.zalloc = Z_NULL;
    	zlib_ctx.zfree = Z_NULL;
    	zlib_ctx.opaque = NULL;
    
    	// Exit if unable to start deflate
    	#define WINDOW_BITS  (15+16)  // Plus 16 to get a gzip wrapper, as suggested by the zlib documentation
    	#define MEM_LEVEL 8           // This is the default value, as suggested by the zlib documentation
    	err = deflateInit2(&zlib_ctx, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS, MEM_LEVEL, Z_DEFAULT_STRATEGY);
    	if (err != Z_OK) {
    		WARNING("deflateInit2 returned %d. Falling back to sending uncompressed data", err);
    		*content_len = input_len;
    		return (unsigned char *)input_buf;
    	}
    
    	// Allocate a worst case buffer to hold the compressed data
    	output_len = (int)deflateBound(&zlib_ctx, input_len);
    	output_buf = malloc(output_len);  // Use malloc because the uncompressed report was generated with malloc() and this needs to be consistent
    	if (output_buf == NULL) {
    		WARNING("malloc failed. Falling back to sending uncompressed data", err);
    		deflateEnd(&zlib_ctx);
    		*content_len = input_len;
    		return (unsigned char *)input_buf;
    	}
    
    	// Initialise the zlib context for this compression
    	zlib_ctx.next_in  = (unsigned char *)input_buf;
    	zlib_ctx.avail_in = input_len;
    	zlib_ctx.next_out = output_buf;
    	zlib_ctx.avail_out= output_len;
    
    	// Exit if compression failed
    	err = deflate(&zlib_ctx, Z_FINISH);
    	if (err != Z_STREAM_END) {
    		WARNING("deflate failed (err=%d). Falling back to sending uncompressed data", err);
    		deflateEnd(&zlib_ctx);
    		free(output_buf);
    		*content_len = input_len;
    		return (unsigned char *)input_buf;
    	}
    
    	// Deallocate all compression state stored in the zlib context
    	// NOTE: We ignore errors from this and just log them
    	err = deflateEnd(&zlib_ctx);
    	if (err != Z_OK) {
    		WARNING("deflateEnd failed (err=%d, %s). Ignoring error", err, zlib_ctx.msg);
    	}
    
    	INFO("BulkDataReport(uncompressed size=%d, compressed size=%lu)", input_len, zlib_ctx.total_out);
    	*content_len = zlib_ctx.total_out;
    	return output_buf;
    }
    
    
    int http_send_message(struct profile *profile, char *msg_out)
    {
    #define HTTP_TIMEOUT 20
    
    	char url[1024] = {0};
    	struct curl_slist *header_list = NULL;
    	CURLcode res;
    	long http_code = 0;
    	char errbuf[CURL_ERROR_SIZE];
    
    	CURL *curl = curl_easy_init();
    	if (!curl)
    		return -1;
    
    	create_request_url(profile, url, sizeof(url));
    	INFO("ACS url: %s", url);
    
    	header_list = curl_slist_append(header_list, "User-Agent: iopsys-bulkdata");
    	if (!header_list)
    		return -1;
    
    	if (profile->http_use_date_header) {
    		if (bulkdata_get_time() != NULL) {
    			header_list = curl_slist_append(header_list, bulkdata_get_time());
    			if (!header_list) return -1;
    		}
    	}
    
    
    	if (strcasecmp(profile->encoding_type, "JSON") == 0) {
    
    		header_list = curl_slist_append(header_list, "Content-Type: application/json; charset=\"utf-8\"");
    		if (!header_list) return -1;
    
    
    		if (strcasecmp(profile->json_encoding_report_format, "ObjectHierarchy") == 0) {
    
    			header_list = curl_slist_append(header_list, "BBF-Report-Format: ObjectHierarchy");
    
    			if (!header_list) return -1;
    
    		} else if (strcasecmp(profile->json_encoding_report_format, "NameValuePair") == 0) {
    
    			header_list = curl_slist_append(header_list, "BBF-Report-Format: NameValuePair");
    
    	} else if (strcasecmp(profile->encoding_type, "CSV") == 0) {
    
    		header_list = curl_slist_append(header_list, "Content-Type: text/csv; charset=\"utf-8\"");
    		if (!header_list) return -1;
    
    
    		if (strcasecmp(profile->csv_encoding_report_format, "ParameterPerRow") == 0) {
    
    			header_list = curl_slist_append(header_list, "BBF-Report-Format: ParameterPerRow");
    
    			if (!header_list) return -1;
    
    		} else if (strcasecmp(profile->csv_encoding_report_format, "ParameterPerColumn") == 0) {
    
    			header_list = curl_slist_append(header_list, "BBF-Report-Format: ParameterPerColumn");
    
    			if (!header_list) return -1;
    		}
    	}
    
    	curl_easy_setopt(curl, CURLOPT_URL, url);
    	curl_easy_setopt(curl, CURLOPT_USERNAME, profile->http_username);
    	curl_easy_setopt(curl, CURLOPT_PASSWORD, profile->http_password);
    	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);
    	curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_TIMEOUT);
    	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, HTTP_TIMEOUT);
    	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    	curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
    	curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
    
    
    	unsigned char *content = NULL;
    	long content_len = 0;
    	long msg_out_len = msg_out ? strlen(msg_out) : 0;
    
    	if (strcasecmp(profile->http_compression, "GZIP") == 0) {
    
    		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
    		header_list = curl_slist_append(header_list, "Content-Encoding: gzip");
    
    		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_GZIP);
    
    	} else if (strcasecmp(profile->http_compression, "Compress") == 0) {
    
    		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "compress");
    		header_list = curl_slist_append(header_list, "Content-Encoding: compress");
    
    		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_COMPRESS);
    
    	} else if (strcasecmp(profile->http_compression, "Deflate") == 0) {
    
    		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
    
    		header_list = curl_slist_append(header_list, "Content-Encoding: deflate");
    		content = compress_report(msg_out, msg_out_len, &content_len, COMPRESS_TYPE_DEFLATE);
    	} else {
    		/* no compression */
    		content = (unsigned char *)msg_out;
    		content_len = msg_out ? msg_out_len : 0;
    
    	}
    
    	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
    
    	if (strcasecmp(profile->http_method, "PUT") == 0)
    
    		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    
    
    	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content);
    	curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, content_len);
    
    
    	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
    
    	INFO("Send the report of profile name '%s' to Bulkdata Collector", profile->profile_name);
    	res = curl_easy_perform(curl);
    
    	if (res != CURLE_OK) {
    		size_t len = strlen(errbuf);
    		if (len) {
    			if (errbuf[len - 1] == '\n') errbuf[len - 1] = '\0';
    			WARNING("libcurl: (%d) %s", res, errbuf);
    		} else {
    			ERR("libcurl: (%d) %s", res, curl_easy_strerror(res));
    		}
    	}
    
    
    	if (content != (unsigned char *)msg_out)
    		FREE(content);
    
    
    	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
    	if (http_code == 200)
    		INFO("Receive HTTP 200 OK from Bulkdata Collector");
    	else if (http_code == 401)
    		INFO("Receive HTTP 401 Unauthorized from Bulkdata Collector");
    	else if (http_code == 204)
    		INFO("Receive HTTP 204 No Content from Bulkdata Collector");
    	else
    		INFO("Receive HTTP %ld from Bulkdata Collector", http_code);
    
    	if (http_code != 200 && http_code != 204)
    		goto error;
    
    	curl_easy_cleanup(curl);
    	if (header_list) {
    		curl_slist_free_all(header_list);
    		header_list = NULL;
    	}
    
    	if (res)
    		goto error;
    
    	return http_code;
    
    error:
    	curl_easy_cleanup(curl);
    	if (header_list) {
    		curl_slist_free_all(header_list);
    		header_list = NULL;
    	}
    	return -1;
    }
    
    char *get_bulkdata_profile_parameter_name(char *paramref, char *paramname, char *param)
    {
    	char buf[512] = {0};
    	unsigned pos = 0;
    
    	if (paramname == NULL || strlen(paramname) == 0)
    		return strdup(param);
    	
    	pos = snprintf(buf, sizeof(buf), "%s", paramname);
    	
    	if (!strchr(paramref, '*')) {
    		bool is_same = (paramref[strlen(paramref) - 1] == param[strlen(param) - 1]);
    		snprintf(&buf[pos], sizeof(buf) - pos, "%s%s", !is_same ? "." : "", !is_same ? strstr(param, paramref) + strlen(paramref) : "");
    	} else {
    
    		char *pch = NULL, *spch = NULL;
    
    		char paramref_buf[256] = {0};
    		char tmp_buf[64] = {0};
    		bool first_occur = true;
    		
    		snprintf(paramref_buf, sizeof(paramref_buf), "%s", paramref);
    	
    		for (pch = strtok_r(paramref_buf, "*", &spch); pch != NULL; pch = strtok_r(NULL, "*", &spch)) {
    		
    			if (first_occur) {
    				first_occur = false;
    				STRNCPY(tmp_buf, pch, sizeof(tmp_buf));
    				continue;
    			}
    
    			idx1 = strstr(param, tmp_buf);
    
    			sscanf(idx1 + strlen(tmp_buf), "%u.", &index);
    
    			pos += snprintf(&buf[pos], sizeof(buf) - pos, ".%u", index);
    
    			STRNCPY(tmp_buf, pch, sizeof(tmp_buf));
    		}
    
    		bool is_same = (tmp_buf[strlen(tmp_buf) - 1] == param[strlen(param) - 1]);
    		snprintf(&buf[pos], sizeof(buf) - pos, "%s%s", !is_same ? "." : "", !is_same ? strstr(param, tmp_buf) + strlen(tmp_buf) : "");
    	}
    		
    	return strdup(buf);
    }
    
    void append_string_to_string(char *strappend, char **target)
    {
    	char *tmp = NULL;
    
    	if (strappend == NULL || strlen(strappend) == 0)
    		return;
    
    	if (*target == NULL || strlen(*target) == 0) {
    		*target = strdup(strappend);
    		return;
    	} else {
    		tmp = strdup(*target);
    		FREE(*target);
    	}
    	asprintf(target, "%s%s", tmp, strappend);
    	FREE(tmp);
    }
    
    void bulkdata_add_failed_report(struct profile *profile, char *freport)
    {
    	if (profile->nbre_failed_reports < profile->nbre_of_retained_failed_reports || profile->nbre_of_retained_failed_reports < 0) {
    		profile->nbre_failed_reports++;
    	} else {
    		struct failed_reports *retreport = NULL, *rtmp = NULL;
    
    		list_for_each_entry_safe(retreport, rtmp, profile->failed_reports, list) {
    			//bulkdata_delete_failed_report(retreport);
    			list_del(&retreport->list);
    			FREE(retreport->freport);
    			FREE(retreport);
    			break;
    		}
    	}
    
    	struct failed_reports *report = calloc(1, sizeof(struct failed_reports));
    	list_add_tail(&report->list, profile->failed_reports);
    	report->freport = strdup(freport);
    }
    
    struct failed_reports* empty_failed_reports_list(struct profile *profile)
    {
    	struct failed_reports *report = NULL, *rtmp = NULL;
    
    	if (list_empty(profile->failed_reports))
    		return NULL;
    
    	list_for_each_entry_safe(report, rtmp, profile->failed_reports, list) {
    		list_del(&report->list);
    		FREE(report->freport);
    		FREE(report);
    	}
    	return NULL;
    }
    
    void add_failed_reports_to_report_csv(struct profile *profile, char **report, int isnext)
    {
    	struct failed_reports *retreport = NULL;
    	bool j = false;
    
    	if (list_empty(profile->failed_reports))
    		return;
    	list_for_each_entry(retreport, profile->failed_reports, list) {
    		if (!j && isnext) {
    			j = true;
    			continue;
    		}
    		append_string_to_string(retreport->freport, report);
    	}
    }
    
    static void lookup_event_cb(struct ubus_context *ctx __attribute__((unused)),
    		struct ubus_event_handler *ev __attribute__((unused)),
    		const char *type, struct blob_attr *msg)
    {
    	const struct blobmsg_policy policy = {
    		"path", BLOBMSG_TYPE_STRING
    	};
    	struct blob_attr *attr;
    	const char *path;
    
    	if (strcmp(type, "ubus.object.add") != 0)
    		return;
    
    	blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
    	if (!attr)
    		return;
    
    	path = blobmsg_data(attr);
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	if (strcmp(path, DM_OBJECT_NAME) == 0) {
    
    		g_usp_object_available = true;
    		uloop_end();
    	}
    }
    
    static void lookup_timeout_cb(struct uloop_timeout *timeout __attribute__((unused)))
    {
    	uloop_end();
    }
    
    int wait_for_usp_raw_object(void)
    {
    #define USP_RAW_WAIT_TIMEOUT 30
    
    	struct ubus_context *uctx;
    	int ret;
    	uint32_t ubus_id;
    	struct ubus_event_handler add_event;
    	struct uloop_timeout u_timeout;
    
    	g_usp_object_available = false;
    	uctx = ubus_connect(NULL);
    	if (uctx == NULL) {
    		ERR("Can't create ubus context");
    		return -1;
    	}
    
    	uloop_init();
    	ubus_add_uloop(uctx);
    
    	// register for add event
    	memset(&add_event, 0, sizeof(struct ubus_event_handler));
    	add_event.cb = lookup_event_cb;
    	ubus_register_event_handler(uctx, &add_event, "ubus.object.add");
    
    	// check if object already present
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    	ret = ubus_lookup_id(uctx, DM_OBJECT_NAME, &ubus_id);
    
    	if (ret == 0) {
    		g_usp_object_available = true;
    		goto end;
    	}
    
    	// Set timeout to expire lookup
    	memset(&u_timeout, 0, sizeof(struct uloop_timeout));
    	u_timeout.cb = lookup_timeout_cb;
    	uloop_timeout_set(&u_timeout, USP_RAW_WAIT_TIMEOUT * 1000);
    
    	uloop_run();
    	uloop_done();
    
    end:
    	ubus_free(uctx);
    
    	if (g_usp_object_available == false) {
    
    Vivek Dutta's avatar
    Vivek Dutta committed
    		ERR("%s object not found", DM_OBJECT_NAME);