Skip to content
Snippets Groups Projects
utils.c 7.65 KiB
/*
 * Copyright (C) 2024 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 "utils.h"

#include <openssl/sha.h>
#include <openssl/evp.h>

static bool validate_hash_value(const char *algo, const char *file_path, const char *checksum)
{
	unsigned char buffer[1024 * 16] = {0};
	char hash[BUFSIZ] = {0};
	bool res = false;
	unsigned int bytes = 0;
	FILE *file;

	EVP_MD_CTX *mdctx;
	const EVP_MD *md;
	unsigned char md_value[EVP_MAX_MD_SIZE];

	// cppcheck-suppress cert-MSC24-C
	file = fopen(file_path, "rb");
	if (!file)
		return false;

	md = EVP_get_digestbyname(algo);
	mdctx = EVP_MD_CTX_create();
	EVP_DigestInit_ex(mdctx, md, NULL);

	if (md == NULL)
		goto end;

	while ((bytes = fread (buffer, 1, sizeof(buffer), file))) {
		EVP_DigestUpdate(mdctx, buffer, bytes);
	}

	bytes = 0;
	EVP_DigestFinal_ex(mdctx, md_value, &bytes);

	for (int i = 0; i < bytes; i++)
		snprintf(&hash[i * 2], sizeof(hash) - (i * 2), "%02x", md_value[i]);

	if (DM_STRCMP(hash, checksum) == 0)
		res = true;

end:
	EVP_MD_CTX_destroy(mdctx);
	EVP_cleanup();

	fclose(file);
	return res;
}

bool validate_checksum_value(const char *file_path, const char *checksum_algorithm, const char *checksum)
{
	if (checksum && *checksum) {

		if (strcmp(checksum_algorithm, "SHA-1") == 0)
			return validate_hash_value("SHA1", file_path, checksum);
		else if (strcmp(checksum_algorithm, "SHA-224") == 0)
			return validate_hash_value("SHA224", file_path, checksum);
		else if (strcmp(checksum_algorithm, "SHA-256") == 0)
			return validate_hash_value("SHA256", file_path, checksum);
		else if (strcmp(checksum_algorithm, "SHA-384") == 0)
			return validate_hash_value("SHA384", file_path, checksum);
		else if (strcmp(checksum_algorithm, "SHA-512") == 0)
			return validate_hash_value("SHA512", file_path, checksum);
		else
			return false;
	}

	return true;
}

bool validate_server_response_code(const char *url, int response_code)
{
	if ((strncmp(url, HTTP_URI, strlen(HTTP_URI)) == 0 && response_code != 200) ||
		(strncmp(url, FTP_URI, strlen(FTP_URI)) == 0 && response_code != 226) ||
		(strncmp(url, FILE_URI, strlen(FILE_URI)) == 0 && response_code != 0) ||
		(strncmp(url, HTTP_URI, strlen(HTTP_URI)) && strncmp(url, FTP_URI, strlen(FTP_URI)) && strncmp(url, FILE_URI, strlen(FILE_URI)))) {
		return false;
	}

	return true;
}

bool validate_file_system_size(const char *file_size)
{
	if (file_size && *file_size) {
		unsigned long f_size = strtoul(file_size, NULL, 10);
		unsigned long fs_available_size = file_system_size("/tmp", FS_SIZE_AVAILABLE);

		if (fs_available_size < f_size)
			return false;
	}

	return true;
}

void send_transfer_complete_event(const char *command, const char *obj_path, const char *transfer_url,
	char *fault_string, time_t start_t, time_t complete_t, const char *commandKey, const char *transfer_type)
{
	char start_time[32] = {0};
	char complete_time[32] = {0};
	struct blob_buf bb;

	strftime(start_time, sizeof(start_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&start_t));
	strftime(complete_time, sizeof(complete_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&complete_t));

	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	blobmsg_add_string(&bb, "name", "Device.LocalAgent.TransferComplete!");
	void *arr = blobmsg_open_array(&bb, "input");

	fill_blob_param(&bb, "Command", command, DMT_TYPE[DMT_STRING], 0);
	if(commandKey)
		fill_blob_param(&bb, "CommandKey", commandKey, DMT_TYPE[DMT_STRING], 0);
	else
		fill_blob_param(&bb, "CommandKey", "", DMT_TYPE[DMT_STRING], 0);

	fill_blob_param(&bb, "Requestor", "", DMT_TYPE[DMT_STRING], 0);
	fill_blob_param(&bb, "TransferType", transfer_type, DMT_TYPE[DMT_STRING], 0);
	fill_blob_param(&bb, "Affected", obj_path, DMT_TYPE[DMT_STRING], 0);
	fill_blob_param(&bb, "TransferURL", transfer_url, DMT_TYPE[DMT_STRING], 0);
	fill_blob_param(&bb, "StartTime", start_time, DMT_TYPE[DMT_STRING], 0);
	fill_blob_param(&bb, "CompleteTime", complete_time, DMT_TYPE[DMT_STRING], 0);

	if (DM_STRLEN(fault_string) == 0) {
		fill_blob_param(&bb, "FaultCode", "0", DMT_TYPE[DMT_STRING], 0);
	} else {
		fill_blob_param(&bb, "FaultCode", "7000", DMT_TYPE[DMT_STRING], 0);
	}

	fill_blob_param(&bb, "FaultString", fault_string, DMT_TYPE[DMT_STRING], 0);
	blobmsg_close_array(&bb, arr);

	dmubus_call_blob_msg_set("bbfdm", "notify_event", &bb);

	blob_buf_free(&bb);
}

int sysmngr_uci_get(const char *package, const char *section, const char *option, const char *default_value, char *buffer, size_t buffer_size)
{
	struct uci_ptr ptr = {0};
	char uci_str[128] = {0};

	if (!package || !section || !option || !default_value || !buffer || !buffer_size)
		return -1;

	struct uci_context *ctx = uci_alloc_context();
	if (!ctx) {
		BBF_ERR("UCI context allocation failed");
		return -1;
	}

	snprintf(uci_str, sizeof(uci_str), "%s.%s.%s", package, section, option);

	if (uci_lookup_ptr(ctx, &ptr, uci_str, true) != UCI_OK || ptr.o == NULL) {
		snprintf(buffer, buffer_size, "%s", default_value);
		uci_free_context(ctx);
		return -1;
	}

	snprintf(buffer, buffer_size, "%s", ptr.o->v.string);
	uci_free_context(ctx);
	return 0;
}

int sysmngr_uci_set(const char *package, const char *section, const char *option, const char *value)
{
	struct uci_ptr ptr = {0};
	char uci_str[128] = {0};

	if (!package || !section || !value)
		return -1;

	struct uci_context *ctx = uci_alloc_context();
	if (!ctx) {
		BBF_ERR("UCI context allocation failed");
		return -1;
	}

	snprintf(uci_str, sizeof(uci_str), "%s.%s%s%s=%s",
			package,
			section,
			option ? "." : "",
			option ? option : "",
			value);

	if (uci_lookup_ptr(ctx, &ptr, uci_str, true) != UCI_OK ||
		uci_set(ctx, &ptr) != UCI_OK ||
		uci_save(ctx, ptr.p) != UCI_OK ||
		uci_commit(ctx, &ptr.p, false) != UCI_OK) {

		uci_free_context(ctx);
		return -1;
	}

	uci_free_context(ctx);
	return 0;
}

int sysmngr_uci_delete(struct uci_context *uci_ctx, const char *package, const char *section)
{
	struct uci_ptr ptr = {0};
	char uci_str[64] = {0};

	if (!package || !section)
		return -1;

	snprintf(uci_str, sizeof(uci_str), "%s.%s", package, section);

	if (uci_lookup_ptr(uci_ctx, &ptr, uci_str, true) != UCI_OK ||
		uci_delete(uci_ctx, &ptr) != UCI_OK ||
		uci_save(uci_ctx, ptr.p) != UCI_OK) {

		return -1;
	}

	return 0;
}

int sysmngr_ubus_invoke_async(struct ubus_context *ubus_ctx, const char *obj, const char *method, struct blob_attr *msg,
			    sysmngr_ubus_cb data_callback, sysmngr_ubus_async_cb complete_callback)
{
	struct ubus_request *req = NULL;
	uint32_t id;

	if (ubus_ctx == NULL) {
		BBF_ERR("Failed to connect with ubus, error: '%d'", errno);
		return -1;
	}

	if (ubus_lookup_id(ubus_ctx, obj, &id)) {
		BBF_ERR("Failed to lookup ubus object: '%s'", obj);
		return -1;
	}

	req = (struct ubus_request *)calloc(1, sizeof(struct ubus_request));
	if (req == NULL) {
		BBF_ERR("failed to allocate memory for ubus request");
		return -1;
	}

	if (ubus_invoke_async(ubus_ctx, id, method, msg, req)) {
		BBF_ERR("ubus async call failed for object: '%s', method: '%s'", obj, method);
		FREE(req);
		return -1;
	}

	if (data_callback)
		req->data_cb = data_callback;

	if (complete_callback)
		req->complete_cb = complete_callback;

	ubus_complete_request_async(ubus_ctx, req);
	return 0;
}

int sysmngr_get_uptime(void)
{
	// cppcheck-suppress cert-MSC24-C
	FILE *fp = fopen("/proc/uptime", "r");
	int uptime = 0;

	if (fp != NULL) {
		char *pch = NULL, *spch = NULL, buf[64] = {0};

		if (fgets(buf, sizeof(buf), fp) != NULL) {
			pch = strtok_r(buf, ".", &spch);
			uptime = (pch) ? (int)strtol(pch, NULL, 10) : 0;
		}
		fclose(fp);
	}

	return uptime;
}