Mercurial > sci
view scid.c @ 3:215c0c3b3609
misc: use JSON everywhere (scictl/sciwebd)
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 14 Jun 2021 22:08:24 +0200 |
parents | 5fa3d2f479b2 |
children | 9c4fea43803c |
line wrap: on
line source
/* * scid.c -- main scid(8) program 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 <sys/un.h> #include <sys/socket.h> #include <assert.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <stdnoreturn.h> #include <string.h> #include <unistd.h> #include "config.h" #include "db.h" #include "log.h" #include "types.h" #include "util.h" static char dbpath[PATH_MAX] = VARDIR "/db/sci/sci.db"; static char scpath[PATH_MAX] = VARDIR "/run/sci.sock"; static int sc; noreturn static inline void usage(void) { fprintf(stderr, "usage: %s [-d database]\n", getprogname()); exit(1); } static void init(void) { struct sockaddr_un sun; int reuse = 1; memset(&sun, 0, sizeof (sun)); strlcpy(sun.sun_path, scpath, sizeof (sun.sun_path)); sun.sun_family = PF_LOCAL; log_open("scid"); log_info("opening database %s", dbpath); log_info("opening incoming connection on %s", scpath); remove(scpath); if ((sc = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) err(1, "socket"); if (setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)) < 0) err(1, "setsockopt"); if (bind(sc, (const struct sockaddr *)&sun, sizeof (sun)) < 0) err(1, "bind"); if (listen(sc, 32) < 0) err(1, "listen"); if (db_open(dbpath) < 0) err(1, "unable to open database"); } static int answer(int fd, json_t *json) { char *str; int ret = 0; str = json_dumps(json, JSON_COMPACT); if (send(fd, str, strlen(str), 0) < 0 || send(fd, "\r\n", 2, 0) < 0) ret = -1; free(str); json_decref(json); return ret; } static inline int ok(int fd) { return answer(fd, json_pack("{ss}", "status", "ok")); } static inline int error(int fd, const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); return answer(fd, json_pack("{ss ss}", "status", "error", "error", buf )); } static int cmd_job_add(int fd, json_t *doc) { struct job job = {0}; if (job_from(&job, 1, doc) < 0) return EINVAL; if (db_job_add(&job) < 0) return error(fd, "unable to create job"); log_info("queued new job (%d), with tag %s", job.id, job.tag); return ok(fd); } static int cmd_job_todo(int fd, json_t *doc) { struct db_ctx ctx; struct job jobs[SCI_JOB_LIST_MAX]; ssize_t jobsz; int worker_id, ret; if (json_unpack(doc, "{si}", "worker_id", &worker_id) < 0) return EINVAL; if ((jobsz = db_job_todo(&ctx, jobs, UTIL_SIZE(jobs), worker_id)) < 0) return error(fd, "unable to retrieve list"); ret = answer(fd, job_to(jobs, jobsz)); db_ctx_finish(&ctx); return ret; } static int cmd_jobresult_add(int fd, json_t *doc) { struct jobresult res = {0}; if (jobresult_from(&res, 1, doc) < 0) return EINVAL; if (db_jobresult_add(&res) < 0) return error(fd, "unable to create job result"); log_info("insert new result (%d), with job_id %d (exitcode=%d)", res.id, res.job_id, res.exitcode); return ok(fd); } static int cmd_project_add(int fd, json_t *doc) { struct project proj = {0}; if (project_from(&proj, 1, doc) < 0) return EINVAL; if (db_project_add(&proj) < 0) return error(fd, "unable to create project"); log_info("created new project (%d) %s", proj.id, proj.name); return ok(fd); } static int cmd_project_find(int fd, json_t *doc) { struct db_ctx ctx; struct project proj = {0}; int ret; if (json_unpack(doc, "{ss}", "name", &proj.name) < 0) return EINVAL; if (db_project_find(&ctx, &proj) < 0) return error(fd, "unable to retrieve project"); ret = answer(fd, project_to(&proj, 1)); db_ctx_finish(&ctx); return ret; } static int cmd_project_find_id(int fd, json_t *doc) { struct db_ctx ctx; struct project proj = {0}; int ret; if (json_unpack(doc, "{si}", "id", &proj.id) < 0) return EINVAL; if (db_project_find_id(&ctx, &proj) < 0) return error(fd, "unable to retrieve project"); ret = answer(fd, project_to(&proj, 1)); db_ctx_finish(&ctx); return ret; } static int cmd_project_list(int fd, json_t *doc) { (void)doc; struct db_ctx ctx; struct project projects[SCI_PROJECT_MAX]; ssize_t projectsz; int ret; if ((projectsz = db_project_list(&ctx, projects, UTIL_SIZE(projects))) < 0) return EINVAL; ret = answer(fd, project_to(projects, projectsz)); db_ctx_finish(&ctx); return ret; } static int cmd_worker_add(int fd, json_t *doc) { struct worker wk = {0}; if (worker_from(&wk, 1, doc) < 0) return EINVAL; if (db_worker_add(&wk) <0) return error(fd, "unable to create worker"); log_info("created new worker (%d) %s", wk.id, wk.name); return ok(fd); } static int cmd_worker_find(int fd, json_t *doc) { struct worker wk = {0}; struct db_ctx ctx; int ret; if (json_unpack(doc, "{ss}", "name", &wk.name) < 0) return EINVAL; if (db_worker_find(&ctx, &wk) < 0) return error(fd, "unable to retrieve worker"); ret = answer(fd, worker_to(&wk, 1)); db_ctx_finish(&ctx); return ret; } static int cmd_worker_find_id(int fd, json_t *doc) { struct worker wk = {0}; struct db_ctx ctx; int ret; if (json_unpack(doc, "{si}", "id", &wk.id) < 0) return EINVAL; if (db_worker_find_id(&ctx, &wk) < 0) return error(fd, "unable to retrieve worker"); ret = answer(fd, worker_to(&wk, 1)); db_ctx_finish(&ctx); return ret; } static int cmd_worker_list(int fd, json_t *doc) { (void)doc; struct db_ctx ctx; struct worker workers[SCI_WORKER_MAX]; ssize_t workersz; int ret; if ((workersz = db_worker_list(&ctx, workers, UTIL_SIZE(workers))) < 0) return error(fd, "unable to retrieve worker list"); ret = answer(fd, worker_to(workers, workersz)); db_ctx_finish(&ctx); return ret; } static void dispatch(int fd, const char *msg) { static const struct { const char *name; int (*exec)(int, json_t *); } cmds[] = { { "job-add", cmd_job_add }, { "job-todo", cmd_job_todo }, { "jobresult-add", cmd_jobresult_add }, { "project-add", cmd_project_add }, { "project-find", cmd_project_find }, { "project-find-id", cmd_project_find_id }, { "project-list", cmd_project_list }, { "worker-add", cmd_worker_add }, { "worker-find", cmd_worker_find }, { "worker-find-id", cmd_worker_find_id }, { "worker-list", cmd_worker_list }, { NULL, NULL } }; json_t *doc, *cmd; json_error_t err; if (!(doc = json_loads(msg, 0, &err))) return log_warn("json error: %s", err.text); if (!json_is_string((cmd = json_object_get(doc, "cmd")))) { json_decref(doc); return log_warn("json invalid"); } for (size_t i = 0; cmds[i].name; ++i) { if (strcmp(cmds[i].name, json_string_value(cmd)) == 0) { int ret = 0; if ((ret = cmds[i].exec(fd, doc)) != 0) error(fd, strerror(errno)); json_decref(doc); break; } } } static void run(void) { int client; FILE *fp; char *msg, *endl, buf[BUFSIZ]; ssize_t nr; struct timeval tv = { .tv_sec = 30 }; if ((client = accept(sc, NULL, 0)) < 0) err(1, "accept"); if (setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0) err(1, "setsockopt"); if (setsockopt(client, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) < 0) err(1, "setsockopt"); fp = util_fmemopen((msg = util_calloc(1, SCI_MSG_MAX)), SCI_MSG_MAX, "w"); setbuf(fp, NULL); while (!strstr(msg, "\r\n") && (nr = recv(client, buf, sizeof (buf), 0)) > 0) if (fwrite(buf, 1, nr, fp) != (size_t)nr) warn("fwrite"); if (nr < 0) warn("recv"); if ((endl = strstr(msg, "\r\n"))) *endl = 0; fclose(fp); dispatch(client, msg); close(client); free(msg); } int main(int argc, char **argv) { int ch; setprogname("scid"); while ((ch = getopt(argc, argv, "d:s:")) != -1) { switch (ch) { case 'd': strlcpy(dbpath, optarg, sizeof (dbpath)); break; case 's': strlcpy(scpath, optarg, sizeof (scpath)); break; default: usage(); break; } } argc -= optind; argv += optind; init(); for (;;) run(); }