view scictl/scictl.c @ 58:7a4112eec15b

scid: add /projects page
author David Demelier <markand@malikania.fr>
date Wed, 17 Aug 2022 19:45:32 +0200
parents 319979427566
children 8be8188d61a5
line wrap: on
line source

/*
 * scictl.c -- main scictl(8) utility file
 *
 * Copyright (c) 2021-2022 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 <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <utlist.h>

#include "apic.h"
#include "util.h"

static void
usage(void)
{
	fprintf(stderr, "usage: scictl [-k key] [-u baseurl] command [args...]\n");
	exit(1);
}

static void
help(void)
{
	fprintf(stderr, "usage: scictl job-add project tag\n");
	fprintf(stderr, "       scictl job-todo worker\n");
	fprintf(stderr, "       scictl project-add name desc url script\n");
	fprintf(stderr, "       scictl project-info name\n");
	fprintf(stderr, "       scictl project-list\n");
	fprintf(stderr, "       scictl project-update name key value\n");
	fprintf(stderr, "       scictl worker-add name desc\n");
	fprintf(stderr, "       scictl worker-list\n");
	exit(0);
}

long long
toint(const char *s)
{
	long long v;
	const char *err;

	v = util_strtonum(s, 0, LLONG_MAX, &err);

	if (err)
		util_die("abort: %s\n", err);

	return v;
}

static char *
readfile(const char *path)
{
	FILE *fp, *str;
	char buf[BUFSIZ], *console = NULL;
	size_t nr, consolesz = 0;

	if (strcmp(path, "-") == 0)
		fp = stdin;
	else if (!(fp = fopen(path, "r")))
		util_die("abort: %s: %s\n", path, strerror(errno));

	if (!(str = open_memstream(&console, &consolesz)))
		util_die("abort: open_memstream: %s\n", strerror(errno));

	while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0)
		fwrite(buf, 1, nr, str);

	if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str)))
		util_die("abort: %s\n", strerror(errno));

	fclose(str);
	fclose(fp);

	return console;
}

static inline intmax_t
get_int(json_t *obj, const char *key)
{
	json_t *val;

	if ((val = json_object_get(obj, key)) && json_is_integer(val))
		return json_integer_value(val);

	return 0;
}

static inline const char *
get_string(json_t *obj, const char *key)
{
	json_t *val;

	if ((val = json_object_get(obj, key)) && json_is_string(val))
		return json_string_value(val);

	return "";
}

static void
cmd_job_add(int argc, char **argv)
{
	struct apic req;
	json_t *obj;

	if (argc < 3)
		usage();

	obj = json_pack("{ss ss}",
		"project_name", argv[1],
		"tag",          argv[2]
	);

	if (apic_job_add(&req, obj) < 0)
		util_die("abort: %s\n", req.error);

	json_decref(obj);
}

static void
cmd_job_todo(int argc, char **argv)
{
	struct apic req;
	json_t *array, *obj;
	size_t i;

	if (argc < 2)
		usage();

	if ((array = apic_job_todo(&req, argv[1])))
		util_die("abort: %s\n", req.error);

	json_array_foreach(array, i, obj) {
		printf("%-16s%jd\n", "id:", get_int(obj, "id"));
		printf("%-16s%s\n", "tag:", get_string(obj, "tag"));
		printf("%-16s%s\n", "project:", get_string(obj, "project_name"));

		if (i + 1 < json_array_size(array))
			printf("\n");
	}

	json_decref(array);
}

static void
cmd_project_add(int argc, char **argv)
{
	struct apic req;
	char *script;
	json_t *obj;

	if (argc < 5)
		usage();

	script = readfile(argv[4]);
	obj = json_pack("{ss ss ss ss}",
		"name",         argv[1],
		"desc",         argv[2],
		"url",          argv[3],
		"script",	script
	);

	if (apic_project_save(&req, obj) < 0)
		util_die("abort: unable to create project: %s\n", req.error);

	json_decref(obj);
}

static void
cmd_project_update(int argc, char **argv)
{
	(void)argc;
	(void)argv;
#if 0
	struct project pc;
	struct apic req;

	if (argc < 4)
		usage();

	if (apic_project_find(&req, &pc, argv[1]) < 0)
		util_die("abort: unable to find project: %s\n", req.error);

	if (strcmp(argv[2], "name") == 0)
		replace(&pc.name, argv[3]);
	else if (strcmp(argv[2], "desc") == 0)
		replace(&pc.desc, argv[3]);
	else if (strcmp(argv[2], "url") == 0)
		replace(&pc.url, argv[3]);
	else if (strcmp(argv[2], "script") == 0)
		replace(&pc.script, readfile(argv[3]));

	if (apic_project_save(&req, &pc) < 0)
		util_die("abort: unable to save project: %s\n", req.error);

	apic_finish(&req);
	project_finish(&pc);
#endif
}

static void
cmd_project_info(int argc, char **argv)
{
	struct apic req;
	json_t *obj;

	if (argc < 2)
		usage();
	if (!(obj = apic_project_find(&req, argv[1])))
		util_die("abort: unable to find project: %s\n", req.error);

	printf("%-16s%s\n", "name:", get_string(obj, "name"));
	printf("%-16s%s\n", "desc:", get_string(obj, "desc"));
	printf("%-16s%s\n", "url:", get_string(obj, "url"));
	printf("\n");
	printf("%s", get_string(obj, "script"));

	json_decref(obj);
}

static void
cmd_project_list(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	struct apic req;
	json_t *array, *obj;
	size_t i;

	if (!(array = apic_project_list(&req)))
		util_die("abort: unable to list projects: %s\n", req.error);

	json_array_foreach(array, i, obj) {
		printf("%-16s%s\n", "name:", get_string(obj, "name"));
		printf("%-16s%s\n", "desc:", get_string(obj, "desc"));
		printf("%-16s%s\n", "url:", get_string(obj, "url"));

		if (i + 1 < json_array_size(array))
			printf("\n");
	}

	json_decref(array);
}

static void
cmd_worker_add(int argc, char **argv)
{
	struct apic req;
	json_t *obj;

	if (argc < 3)
		usage();

	obj = json_pack("{ss ss}",
		"name", argv[1],
		"desc", argv[2]
	);

	if (apic_worker_save(&req, obj) < 0)
		util_die("abort: unable to save worker: %s\n", req.error);

	json_decref(obj);
}

static void
cmd_worker_list(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	struct apic req;
	json_t *array, *obj;
	size_t i;

	if (!(array = apic_worker_list(&req)))
		util_die("abort: unable to list worker: %s\n", req.error);

	json_array_foreach(array, i, obj) {
		printf("%-16s%s\n", "name:", get_string(obj, "name"));
		printf("%-16s%s\n", "desc:", get_string(obj, "desc"));

		if (i + 1 < json_array_size(array))
			printf("\n");
	}

	json_decref(array);
}

static struct {
	const char *name;
	void (*exec)(int, char **);
} commands[] = {
	{ "job-add",            cmd_job_add             },
	{ "job-todo",           cmd_job_todo            },
	{ "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;
	const char *env;

	opterr = 0;
	setenv("POSIXLY_CORRECT", "1", 1);

	/* Environment first, options after. */
	if ((env = getenv("SCI_API_URL")))
		util_strlcpy(apiconf.baseurl, env, sizeof (apiconf.baseurl));
	if ((env = getenv("SCI_API_KEY")))
		util_strlcpy(apiconf.key, env, sizeof (apiconf.key));

	while ((ch = util_getopt(argc, argv, "k:u:")) != -1) {
		switch (ch) {
		case 'k':
			util_strlcpy(apiconf.key, optarg, sizeof (apiconf.key));
			break;
		case 'u':
			util_strlcpy(apiconf.baseurl, optarg, sizeof (apiconf.baseurl));
			break;
		default:
			break;
		}
	}

	argc -= optind;
	argv += optind;

	optind = 1;

	if (argc < 1)
		usage();
	if (strcmp(argv[0], "help") == 0)
		help();

	/* At this step, every command requires an API key. */
	if (strlen(apiconf.key) == 0)
		util_die("abort: no API key defined\n");

	for (size_t i = 0; commands[i].name; ++i) {
		if (strcmp(commands[i].name, argv[0]) == 0) {
			commands[i].exec(argc, argv);
			return 0;
		}
	}

	util_die("abort: invalid command: %s\n", argv[0]);
}