comparison 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
comparison
equal deleted inserted replaced
18:600204c31bf0 19:de4bf839b565
1 /*
2 * scictl.c -- main scictl(8) utility file
3 *
4 * Copyright (c) 2021 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <err.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdnoreturn.h>
24 #include <unistd.h>
25
26 #include "config.h"
27 #include "req.h"
28 #include "types.h"
29 #include "util.h"
30
31 noreturn static void
32 usage(void)
33 {
34 fprintf(stderr, "usage: %s [-s sock] command [args...]\n", getprogname());
35 exit(1);
36 }
37
38 noreturn static void
39 help(void)
40 {
41 fprintf(stderr, "usage: %s job-add project tag\n", getprogname());
42 fprintf(stderr, " %s job-todo worker\n", getprogname());
43 fprintf(stderr, " %s jobresult-add id worker exitcode console\n", getprogname());
44 fprintf(stderr, " %s project-add name desc url script\n", getprogname());
45 fprintf(stderr, " %s project-info name\n", getprogname());
46 fprintf(stderr, " %s project-list\n", getprogname());
47 fprintf(stderr, " %s project-update name key value\n", getprogname());
48 fprintf(stderr, " %s worker-add name desc\n", getprogname());
49 fprintf(stderr, " %s worker-list\n", getprogname());
50 exit(0);
51 }
52
53 static char *
54 readfile(const char *path)
55 {
56 FILE *fp, *str;
57 char buf[BUFSIZ], *console;
58 size_t nr;
59
60 if (strcmp(path, "-") == 0)
61 fp = stdin;
62 else if (!(fp = fopen(path, "r")))
63 err(1, "%s", path);
64
65 console = util_calloc(1, SCI_MSG_MAX);
66
67 if (!(str = fmemopen(console, SCI_MSG_MAX, "w")))
68 err(1, "fmemopen");
69
70 while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0)
71 fwrite(buf, 1, nr, str);
72
73 if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str))) {
74 free(console);
75 console = NULL;
76 }
77
78 fclose(str);
79 fclose(fp);
80
81 return console;
82 }
83
84 static size_t
85 extract(char *s, size_t w, size_t n, void *data)
86 {
87 return fwrite(s, w, n, data);
88 }
89
90 static json_t *
91 parse(const char *data)
92 {
93 json_t *doc;
94 json_error_t err;
95
96 if (!(json_loads(doc, 0, &err)))
97 die("abort: unable to parse JSON: %s\n", err.text);
98
99 return doc;
100 }
101
102 static json_t *
103 get(const char *url)
104 {
105 CURL *curl;
106 CURLcode code;
107 FILE *fp;
108 char buf[HTTP_BUF_MAX];
109 long ret;
110
111 if (!(fp = fmemopen(buf, sizeof (buf), "w")))
112 die("abort: %s", strerror(errno));
113
114 curl = curl_easy_init();
115 curl_easy_setopt(curl, CURLOPT_URL, url);
116 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, extract);
117 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
118 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3L);
119 code = curl_easy_perform(curl);
120
121 if (code != CURLE_OK)
122 die("abort: %s", curl_easy_strerror(code));
123
124 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
125
126 if (ret != 200)
127 die("abort: HTTP %ld\n", ret);
128
129 curl_easy_cleanup(curl);
130 fclose(fp);
131
132 return parse(buf);
133 }
134
135 static struct req
136 cmd_job_add(int argc, char **argv)
137 {
138 struct job job = {0};
139 struct project project = {0};
140
141 if (argc < 2)
142 usage();
143
144 project.name = argv[0];
145
146 if (_project_find(&project, argv[0])).status)
147 return rp;
148
149 job.project_id = project.id;
150 job.tag = argv[1];
151
152 rj = req_job_add(&job);
153 req_finish(&rp);
154
155 return rj;
156 }
157
158 static struct req
159 cmd_job_todo(int argc, char **argv)
160 {
161 struct project projects[SCI_PROJECT_MAX] = {0};
162 struct job jobs[SCI_JOB_LIST_MAX] = {0};
163 struct req rp, rj;
164 size_t projectsz = UTIL_SIZE(projects), jobsz = UTIL_SIZE(jobs);
165
166 if (argc < 1)
167 usage();
168
169 /* First retrieve projects for a better listing. */
170 if ((rp = req_project_list(projects, &projectsz)).status)
171 return rp;
172
173 if ((rj = req_job_todo(jobs, &jobsz, argv[0])).status) {
174 req_finish(&rp);
175 return rj;
176 }
177
178 for (size_t i = 0; i < jobsz; ++i) {
179 const char *project = "unknown";
180
181 /* Find project if exists (it should). */
182 for (size_t p = 0; p < projectsz; ++p) {
183 if (projects[p].id == jobs[i].project_id) {
184 project = projects[p].name;
185 break;
186 }
187 }
188
189 printf("%-16s%d\n", "id:", jobs[i].id);
190 printf("%-16s%s\n", "tag:", jobs[i].tag);
191 printf("%-16s%s\n", "project:", project);
192
193 if (i + 1 < jobsz)
194 printf("\n");
195 }
196
197 req_finish(&rp);
198
199 return rj;
200 }
201
202 static struct req
203 cmd_jobresult_add(int argc, char **argv)
204 {
205 struct jobresult res = {0};
206 struct worker wk = {0};
207 struct req rw, rj;
208 char *log;
209
210 if (argc < 5)
211 usage();
212
213 /* Find worker id. */
214 if ((rw = req_worker_find(&wk, argv[1])).status)
215 return rw;
216
217 res.job_id = strtoll(argv[0], NULL, 10);
218 res.exitcode = strtoll(argv[2], NULL, 10);
219 res.worker_id = wk.id;
220 res.log = log = readfile(argv[3]);
221 rj = req_jobresult_add(&res);
222
223 free(log);
224 req_finish(&rw);
225
226 return rj;
227 }
228
229 static struct req
230 cmd_project_add(int argc, char **argv)
231 {
232 struct project pc = {0};
233 struct req res;
234 char *script;
235
236 if (argc < 4)
237 usage();
238
239 pc.name = argv[0];
240 pc.desc = argv[1];
241 pc.url = argv[2];
242 pc.script = script = readfile(argv[3]);
243 res = req_project_add(&pc);
244
245 free(script);
246
247 return res;
248 }
249
250 static struct req
251 cmd_project_update(int argc, char **argv)
252 {
253 struct project pc;
254 struct req rget, rsend;
255 char *script = NULL;
256
257 if (argc < 3)
258 help();
259
260 if ((rget = req_project_find(&pc, argv[0])).status)
261 return rget;
262
263 if (strcmp(argv[1], "name") == 0)
264 pc.name = argv[2];
265 else if (strcmp(argv[1], "desc") == 0)
266 pc.desc = argv[2];
267 else if (strcmp(argv[1], "url") == 0)
268 pc.url = argv[2];
269 else if (strcmp(argv[1], "script") == 0)
270 pc.script = script = readfile(argv[2]);
271
272 rsend = req_project_update(&pc);
273
274 req_finish(&rget);
275 free(script);
276
277 return rsend;
278 }
279
280 static struct req
281 cmd_project_info(int argc, char **argv)
282 {
283 struct project project = {0};
284 struct req req;
285
286 if (argc < 1)
287 usage();
288 if ((req = req_project_find(&project, argv[0])).status)
289 return req;
290
291 printf("%-16s%d\n", "id:", project.id);
292 printf("%-16s%s\n", "name:", project.name);
293 printf("%-16s%s\n", "desc:", project.desc);
294 printf("%-16s%s\n", "url:", project.url);
295 printf("\n");
296 printf("%s", project.script);
297
298 return req;
299 }
300
301 static struct req
302 cmd_project_list(int argc, char **argv)
303 {
304 (void)argc;
305 (void)argv;
306
307 struct project projects[SCI_PROJECT_MAX] = {0};
308 struct req req;
309 size_t projectsz = UTIL_SIZE(projects);
310
311 if ((req = req_project_list(projects, &projectsz)).status)
312 return req;
313
314 for (size_t i = 0; i < projectsz; ++i) {
315 printf("%-16s%d\n", "id:", projects[i].id);
316 printf("%-16s%s\n", "name:", projects[i].name);
317 printf("%-16s%s\n", "desc:", projects[i].desc);
318 printf("%-16s%s\n", "url:", projects[i].url);
319
320 if (i + 1 < projectsz)
321 printf("\n");
322 }
323
324 return req;
325 }
326
327 static struct req
328 cmd_worker_add(int argc, char **argv)
329 {
330 struct worker wk = {0};
331
332 if (argc < 2)
333 usage();
334
335 wk.name = argv[0];
336 wk.desc = argv[1];
337
338 return req_worker_add(&wk);
339 }
340
341 static struct req
342 cmd_worker_list(int argc, char **argv)
343 {
344 (void)argc;
345 (void)argv;
346
347 struct worker wk[SCI_WORKER_MAX];
348 struct req req;
349 size_t wksz = UTIL_SIZE(wk);
350
351 if ((req = req_worker_list(wk, &wksz)).status)
352 return req;
353
354 for (size_t i = 0; i < wksz; ++i) {
355 printf("%-16s%d\n", "id:", wk[i].id);
356 printf("%-16s%s\n", "name:", wk[i].name);
357 printf("%-16s%s\n", "desc:", wk[i].desc);
358
359 if (i + 1 < wksz)
360 printf("\n");
361 }
362
363 return req;
364 }
365
366 static struct {
367 const char *name;
368 struct req (*exec)(int, char **);
369 } commands[] = {
370 { "job-add", cmd_job_add },
371 { "job-todo", cmd_job_todo },
372 { "jobresult-add", cmd_jobresult_add },
373 { "project-add", cmd_project_add },
374 { "project-info", cmd_project_info },
375 { "project-list", cmd_project_list },
376 { "project-update", cmd_project_update },
377 { "worker-add", cmd_worker_add },
378 { "worker-list", cmd_worker_list },
379 { NULL, NULL }
380 };
381
382 int
383 main(int argc, char **argv)
384 {
385 int ch, cmdfound = 0;
386
387 setprogname("scictl");
388
389 while ((ch = getopt(argc, argv, "s:")) != -1) {
390 switch (ch) {
391 case 's':
392 req_set_path(optarg);
393 break;
394 default:
395 break;
396 }
397 }
398
399 argc -= optind;
400 argv += optind;
401
402 if (argc <= 0)
403 usage();
404 if (strcmp(argv[0], "help") == 0)
405 help();
406
407 for (size_t i = 0; commands[i].name; ++i) {
408 struct req res;
409
410 if (strcmp(commands[i].name, argv[0]) == 0) {
411 res = commands[i].exec(--argc, ++argv);
412 cmdfound = 1;
413
414 if (res.status)
415 warnx("%s", json_string_value(json_object_get(res.msg, "error")));
416
417 req_finish(&res);
418 break;
419 }
420 }
421
422 if (!cmdfound)
423 errx(1, "abort: command %s not found", argv[0]);
424 }