Mercurial > sci
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 } |