/* SPDX-License-Identifier: BSD-3-Clause */

#include "ucentral.h"

enum {
	CMD_COMMAND,
	CMD_TIMEOUT,
	CMD_DELAY,
	__CMD_MAX,
};

static const struct blobmsg_policy cmd_policy[__CMD_MAX] = {
	[CMD_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
	[CMD_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
	[CMD_DELAY] = { .name = "delay", .type = BLOBMSG_TYPE_INT32 },
};

static void
cmd_run_cb(time_t uuid, uint32_t _id)
{
	char str[128];
	char id[32];

	ULOG_INFO("running command task\n");

	sprintf(str, "/tmp/ucentral.cmd.%010" PRIu64, uuid);
	snprintf(id, sizeof(id), "%d", _id);
	execlp("/usr/share/ucentral/cmd.uc", "/usr/share/ucentral/cmd.uc", str, id, NULL);
	exit(1);
}

static void
cmd_complete_cb(struct task *t, time_t uuid, uint32_t id, int ret)
{
	char str[128];
	sprintf(str, "/tmp/ucentral.cmd.%010" PRIu64, uuid);
	unlink(str);
	if (t->cancelled)
		result_send_error(1, "command timed out and was aborted", -1, id);
	else if (ret)
		result_send_error(1, "command returned an error code", ret, id);
}

int
cmd_run(struct blob_attr *attr, uint32_t id, int admin)
{
	static struct blob_attr *tb[__CMD_MAX];
	char *json = NULL;
	FILE *fp = NULL;
	char path[128];
	int ret = -1;
	time_t t = id;

	blobmsg_parse(cmd_policy, __CMD_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
	if (!tb[CMD_COMMAND]) {
		ULOG_ERR("cmd has invalid parameters\n");
		goto out;
	}

	json = blobmsg_format_json(attr, true);
	if (!json) {
		ULOG_ERR("failed to format cmd json\n");
		goto out;
	}

	snprintf(path, sizeof(path), "/tmp/ucentral.cmd.%010" PRIu64, t);
	fp = fopen(path, "w+");
	if (!fp) {
		ULOG_ERR("failed to open %s\n", path);
		goto out;
	}
	if (fwrite(json, strlen(json), 1, fp) == 1) {
		struct task *task = calloc(1, sizeof(*task));

		if (tb[CMD_TIMEOUT])
			task->run_time = blobmsg_get_u32(tb[CMD_TIMEOUT]);
		else
			task->run_time = 60;
		if (tb[CMD_DELAY])
			task->delay = blobmsg_get_u32(tb[CMD_DELAY]);
		task->run = cmd_run_cb;
		task->complete = cmd_complete_cb;
		fclose(fp);
		fp = NULL;
		task_run(task, t, id, admin);
		ret = 0;
	}

out:
	if (json)
		free(json);
	if (fp)
		fclose(fp);

	return ret;
}