diff scictl/scictl.c @ 19:de4bf839b565

misc: revamp SQL
author David Demelier <markand@malikania.fr>
date Fri, 15 Jul 2022 11:11:48 +0200
parents scictl.c@600204c31bf0
children dd078aea5d02
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scictl/scictl.c	Fri Jul 15 11:11:48 2022 +0200
@@ -0,0 +1,424 @@
+/*
+ * scictl.c -- main scictl(8) utility file
+ *
+ * Copyright (c) 2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdnoreturn.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "req.h"
+#include "types.h"
+#include "util.h"
+
+noreturn static void
+usage(void)
+{
+	fprintf(stderr, "usage: %s [-s sock] command [args...]\n", getprogname());
+	exit(1);
+}
+
+noreturn static void
+help(void)
+{
+	fprintf(stderr, "usage: %s job-add project tag\n", getprogname());
+	fprintf(stderr, "       %s job-todo worker\n", getprogname());
+	fprintf(stderr, "       %s jobresult-add id worker exitcode console\n", getprogname());
+	fprintf(stderr, "       %s project-add name desc url script\n", getprogname());
+	fprintf(stderr, "       %s project-info name\n", getprogname());
+	fprintf(stderr, "       %s project-list\n", getprogname());
+	fprintf(stderr, "       %s project-update name key value\n", getprogname());
+	fprintf(stderr, "       %s worker-add name desc\n", getprogname());
+	fprintf(stderr, "       %s worker-list\n", getprogname());
+	exit(0);
+}
+
+static char *
+readfile(const char *path)
+{
+	FILE *fp, *str;
+	char buf[BUFSIZ], *console;
+	size_t nr;
+
+	if (strcmp(path, "-") == 0)
+		fp = stdin;
+	else if (!(fp = fopen(path, "r")))
+		err(1, "%s", path);
+
+	console = util_calloc(1, SCI_MSG_MAX);
+
+	if (!(str = fmemopen(console, SCI_MSG_MAX, "w")))
+		err(1, "fmemopen");
+
+	while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0)
+		fwrite(buf, 1, nr, str);
+
+	if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str))) {
+		free(console);
+		console = NULL;
+	}
+
+	fclose(str);
+	fclose(fp);
+
+	return console;
+}
+
+static size_t
+extract(char *s, size_t w, size_t n, void *data)
+{
+	return fwrite(s, w, n, data);
+}
+
+static json_t *
+parse(const char *data)
+{
+	json_t *doc;
+	json_error_t err;
+
+	if (!(json_loads(doc, 0, &err)))
+		die("abort: unable to parse JSON: %s\n", err.text);
+
+	return doc;
+}
+
+static json_t *
+get(const char *url)
+{
+	CURL *curl;
+	CURLcode code;
+	FILE *fp;
+	char buf[HTTP_BUF_MAX];
+	long ret;
+
+	if (!(fp = fmemopen(buf, sizeof (buf), "w")))
+		die("abort: %s", strerror(errno));
+
+	curl = curl_easy_init();
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, extract);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3L);
+	code = curl_easy_perform(curl);
+
+	if (code != CURLE_OK)
+		die("abort: %s", curl_easy_strerror(code));
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
+
+	if (ret != 200)
+		die("abort: HTTP %ld\n", ret);
+
+	curl_easy_cleanup(curl);
+	fclose(fp);
+
+	return parse(buf);
+}
+
+static struct req
+cmd_job_add(int argc, char **argv)
+{
+	struct job job = {0};
+	struct project project = {0};
+
+	if (argc < 2)
+		usage();
+
+	project.name = argv[0];
+
+	if (_project_find(&project, argv[0])).status)
+		return rp;
+
+	job.project_id = project.id;
+	job.tag = argv[1];
+
+	rj = req_job_add(&job);
+	req_finish(&rp);
+
+	return rj;
+}
+
+static struct req
+cmd_job_todo(int argc, char **argv)
+{
+	struct project projects[SCI_PROJECT_MAX] = {0};
+	struct job jobs[SCI_JOB_LIST_MAX] = {0};
+	struct req rp, rj;
+	size_t projectsz = UTIL_SIZE(projects), jobsz = UTIL_SIZE(jobs);
+
+	if (argc < 1)
+		usage();
+
+	/* First retrieve projects for a better listing. */
+	if ((rp = req_project_list(projects, &projectsz)).status)
+		return rp;
+
+	if ((rj = req_job_todo(jobs, &jobsz, argv[0])).status) {
+		req_finish(&rp);
+		return rj;
+	}
+
+	for (size_t i = 0; i < jobsz; ++i) {
+		const char *project = "unknown";
+
+		/* Find project if exists (it should). */
+		for (size_t p = 0; p < projectsz; ++p) {
+			if (projects[p].id == jobs[i].project_id) {
+				project = projects[p].name;
+				break;
+			}
+		}
+
+		printf("%-16s%d\n", "id:", jobs[i].id);
+		printf("%-16s%s\n", "tag:", jobs[i].tag);
+		printf("%-16s%s\n", "project:", project);
+
+		if (i + 1 < jobsz)
+			printf("\n");
+	}
+
+	req_finish(&rp);
+	
+	return rj;
+}
+
+static struct req
+cmd_jobresult_add(int argc, char **argv)
+{
+	struct jobresult res = {0};
+	struct worker wk = {0};
+	struct req rw, rj;
+	char *log;
+
+	if (argc < 5)
+		usage();
+
+	/* Find worker id. */
+	if ((rw = req_worker_find(&wk, argv[1])).status)
+		return rw;
+
+	res.job_id = strtoll(argv[0], NULL, 10);
+	res.exitcode = strtoll(argv[2], NULL, 10);
+	res.worker_id = wk.id;
+	res.log = log = readfile(argv[3]);
+	rj = req_jobresult_add(&res);
+
+	free(log);
+	req_finish(&rw);
+
+	return rj;
+}
+
+static struct req
+cmd_project_add(int argc, char **argv)
+{
+	struct project pc = {0};
+	struct req res;
+	char *script;
+
+	if (argc < 4)
+		usage();
+
+	pc.name = argv[0];
+	pc.desc = argv[1];
+	pc.url = argv[2];
+	pc.script = script = readfile(argv[3]);
+	res = req_project_add(&pc);
+
+	free(script);
+
+	return res;
+}
+
+static struct req
+cmd_project_update(int argc, char **argv)
+{
+	struct project pc;
+	struct req rget, rsend;
+	char *script = NULL;
+
+	if (argc < 3)
+		help();
+
+	if ((rget = req_project_find(&pc, argv[0])).status)
+		return rget;
+
+	if (strcmp(argv[1], "name") == 0)
+		pc.name = argv[2];
+	else if (strcmp(argv[1], "desc") == 0)
+		pc.desc = argv[2];
+	else if (strcmp(argv[1], "url") == 0)
+		pc.url = argv[2];
+	else if (strcmp(argv[1], "script") == 0)
+		pc.script = script = readfile(argv[2]);
+
+	rsend = req_project_update(&pc);
+
+	req_finish(&rget);
+	free(script);
+
+	return rsend;
+}
+
+static struct req
+cmd_project_info(int argc, char **argv)
+{
+	struct project project = {0};
+	struct req req;
+
+	if (argc < 1)
+		usage();
+	if ((req = req_project_find(&project, argv[0])).status)
+		return req;
+
+	printf("%-16s%d\n", "id:", project.id);
+	printf("%-16s%s\n", "name:", project.name);
+	printf("%-16s%s\n", "desc:", project.desc);
+	printf("%-16s%s\n", "url:", project.url);
+	printf("\n");
+	printf("%s", project.script);
+
+	return req;
+}
+
+static struct req
+cmd_project_list(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	struct project projects[SCI_PROJECT_MAX] = {0};
+	struct req req;
+	size_t projectsz = UTIL_SIZE(projects);
+
+	if ((req = req_project_list(projects, &projectsz)).status)
+		return req;
+
+	for (size_t i = 0; i < projectsz; ++i) {
+		printf("%-16s%d\n", "id:", projects[i].id);
+		printf("%-16s%s\n", "name:", projects[i].name);
+		printf("%-16s%s\n", "desc:", projects[i].desc);
+		printf("%-16s%s\n", "url:", projects[i].url);
+
+		if (i + 1 < projectsz)
+			printf("\n");
+	}
+
+	return req;
+}
+
+static struct req
+cmd_worker_add(int argc, char **argv)
+{
+	struct worker wk = {0};
+
+	if (argc < 2)
+		usage();
+
+	wk.name = argv[0];
+	wk.desc = argv[1];
+
+	return req_worker_add(&wk);
+}
+
+static struct req
+cmd_worker_list(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	struct worker wk[SCI_WORKER_MAX];
+	struct req req;
+	size_t wksz = UTIL_SIZE(wk);
+
+	if ((req = req_worker_list(wk, &wksz)).status)
+		return req;
+
+	for (size_t i = 0; i < wksz; ++i) {
+		printf("%-16s%d\n", "id:", wk[i].id);
+		printf("%-16s%s\n", "name:", wk[i].name);
+		printf("%-16s%s\n", "desc:", wk[i].desc);
+
+		if (i + 1 < wksz)
+			printf("\n");
+	}
+
+	return req;
+}
+
+static struct {
+	const char *name;
+	struct req (*exec)(int, char **);
+} commands[] = {
+	{ "job-add",            cmd_job_add             },
+	{ "job-todo",           cmd_job_todo            },
+	{ "jobresult-add",      cmd_jobresult_add       },
+	{ "project-add",        cmd_project_add         },
+	{ "project-info",       cmd_project_info        },
+	{ "project-list",       cmd_project_list        },
+	{ "project-update",     cmd_project_update      },
+	{ "worker-add",         cmd_worker_add          },
+	{ "worker-list",        cmd_worker_list         },
+	{ NULL,                 NULL                    }
+};
+
+int
+main(int argc, char **argv)
+{
+	int ch, cmdfound = 0;
+
+	setprogname("scictl");
+
+	while ((ch = getopt(argc, argv, "s:")) != -1) {
+		switch (ch) {
+		case 's':
+			req_set_path(optarg);
+			break;
+		default:
+			break;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc <= 0)
+		usage();
+	if (strcmp(argv[0], "help") == 0)
+		help();
+
+	for (size_t i = 0; commands[i].name; ++i) {
+		struct req res;
+
+		if (strcmp(commands[i].name, argv[0]) == 0) {
+			res = commands[i].exec(--argc, ++argv);
+			cmdfound = 1;
+
+			if (res.status)
+				warnx("%s", json_string_value(json_object_get(res.msg, "error")));
+
+			req_finish(&res);
+			break;
+		}
+	}
+
+	if (!cmdfound)
+		errx(1, "abort: command %s not found", argv[0]);
+}