cli.c (56397B)
1 /*
2 * Copyright (c) 2014-2022 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/queue.h>
21 #include <sys/wait.h>
22 #include <sys/mman.h>
23 #include <sys/time.h>
24
25 #if !defined(KODEV_MINIMAL)
26 #include <openssl/err.h>
27 #include <openssl/pem.h>
28 #include <openssl/x509v3.h>
29 #endif
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <inttypes.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <stdarg.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <utime.h>
45
46 #define errno_s strerror(errno)
47 #define ssl_errno_s ERR_error_string(ERR_get_error(), NULL)
48
49 #if defined(OpenBSD) || defined(__FreeBSD_version) || \
50 defined(NetBSD) || defined(__DragonFly_version)
51 #define PRI_TIME_T "d"
52 #endif
53
54 #if defined(__linux__)
55 #if defined(__x86_64__)
56 #define PRI_TIME_T PRIu64
57 #else
58 #define PRI_TIME_T "ld"
59 #endif
60 #endif
61
62 #if defined(__MACH__)
63 #define PRI_TIME_T "ld"
64 #endif
65
66 #define LD_FLAGS_MAX 300
67 #define CFLAGS_MAX 300
68 #define CXXFLAGS_MAX CFLAGS_MAX
69
70 #define BUILD_NOBUILD 0
71 #define BUILD_C 1
72 #define BUILD_CXX 2
73
74 struct cli_buf {
75 u_int8_t *data;
76 size_t length;
77 size_t offset;
78 };
79
80 struct mime_type {
81 char *ext;
82 char *type;
83 TAILQ_ENTRY(mime_type) list;
84 };
85
86 TAILQ_HEAD(mime_list, mime_type);
87
88 struct buildopt {
89 char *name;
90 char *kore_source;
91 char *kore_flavor;
92 int flavor_nohttp;
93 int single_binary;
94 struct cli_buf *cflags;
95 struct cli_buf *cxxflags;
96 struct cli_buf *ldflags;
97 TAILQ_ENTRY(buildopt) list;
98 };
99
100 TAILQ_HEAD(buildopt_list, buildopt);
101
102 struct cmd {
103 const char *name;
104 const char *descr;
105 void (*cb)(int, char **);
106 };
107
108 struct filegen {
109 void (*cb)(void);
110 };
111
112 struct cfile {
113 struct stat st;
114 int build;
115 char *name;
116 char *fpath;
117 char *opath;
118 TAILQ_ENTRY(cfile) list;
119 };
120
121 TAILQ_HEAD(cfile_list, cfile);
122
123 static struct cli_buf *cli_buf_alloc(size_t);
124 static void cli_buf_free(struct cli_buf *);
125 static char *cli_buf_stringify(struct cli_buf *, size_t *);
126 static void cli_buf_append(struct cli_buf *, const void *, size_t);
127 static void cli_buf_appendf(struct cli_buf *, const char *, ...);
128 static void cli_buf_appendv(struct cli_buf *, const char *,
129 va_list);
130
131 static void *cli_malloc(size_t);
132 static char *cli_strdup(const char *);
133 static void *cli_realloc(void *, size_t);
134
135 static char *cli_text_trim(char *, size_t);
136 static char *cli_read_line(FILE *, char *, size_t);
137 static long long cli_strtonum(const char *, long long, long long);
138 static int cli_split_string(char *, const char *, char **, size_t);
139
140 static void usage(void) __attribute__((noreturn));
141 static void fatal(const char *, ...) __attribute__((noreturn));
142
143 static void cli_file_close(int);
144 static void cli_run_kore(void);
145 static void cli_run_kore_python(void);
146 static void cli_compile_kore(void *);
147 static void cli_link_application(void *);
148 static void cli_compile_source_file(void *);
149 static void cli_mkdir(const char *, int);
150 static int cli_dir_exists(const char *);
151 static int cli_file_exists(const char *);
152 static void cli_cleanup_files(const char *);
153 static void cli_build_cflags(struct buildopt *);
154 static void cli_build_cxxflags(struct buildopt *);
155 static void cli_build_ldflags(struct buildopt *);
156 static void cli_file_read(int, char **, size_t *);
157 static void cli_file_writef(int, const char *, ...);
158 static void cli_file_open(const char *, int, int *);
159 static void cli_file_remove(char *, struct dirent *);
160 static void cli_build_asset(char *, struct dirent *);
161 static void cli_file_write(int, const void *, size_t);
162 static int cli_vasprintf(char **, const char *, ...);
163 static void cli_spawn_proc(void (*cb)(void *), void *);
164 static void cli_write_asset(const char *, const char *,
165 struct buildopt *);
166 static void cli_register_kore_file(char *, struct dirent *);
167 static void cli_register_source_file(char *, struct dirent *);
168 static int cli_file_requires_build(struct stat *, const char *);
169 static void cli_find_files(const char *,
170 void (*cb)(char *, struct dirent *));
171 static void cli_add_source_file(char *, char *, char *,
172 struct stat *, int);
173
174 static struct buildopt *cli_buildopt_default(void);
175 static struct buildopt *cli_buildopt_new(const char *);
176 static struct buildopt *cli_buildopt_find(const char *);
177 static void cli_buildopt_cleanup(void);
178 static void cli_buildopt_parse(const char *);
179 static void cli_buildopt_cflags(struct buildopt *, const char *);
180 static void cli_buildopt_cxxflags(struct buildopt *, const char *);
181 static void cli_buildopt_ldflags(struct buildopt *, const char *);
182 static void cli_buildopt_single_binary(struct buildopt *,
183 const char *);
184 static void cli_buildopt_kore_source(struct buildopt *,
185 const char *);
186 static void cli_buildopt_kore_flavor(struct buildopt *,
187 const char *);
188 static void cli_buildopt_mime(struct buildopt *, const char *);
189
190 static void cli_build_flags_common(struct buildopt *,
191 struct cli_buf *);
192
193 static void cli_flavor_load(void);
194 static void cli_flavor_change(const char *);
195 static void cli_kore_load_file(const char *, struct buildopt *,
196 char **, size_t *);
197
198 static void cli_run(int, char **);
199 static void cli_help(int, char **);
200 static void cli_info(int, char **);
201 static void cli_build(int, char **);
202 static void cli_clean(int, char **);
203 static void cli_source(int, char **);
204 static void cli_reload(int, char **);
205 static void cli_flavor(int, char **);
206 static void cli_cflags(int, char **);
207 static void cli_ldflags(int, char **);
208 static void cli_genasset(int, char **);
209 static void cli_genasset_help(void);
210
211 #if !defined(KODEV_MINIMAL)
212 static void cli_create(int, char **);
213 static void cli_create_help(void);
214
215 static void file_create_src(void);
216 static void file_create_config(void);
217 static void file_create_gitignore(void);
218 static void file_create_python_src(void);
219
220 static void cli_generate_certs(void);
221 static void cli_file_create(const char *, const char *, size_t);
222 #endif
223
224 static struct cmd cmds[] = {
225 { "help", "this help text", cli_help },
226 { "run", "run an application (-fnr implied)", cli_run },
227 { "gen", "generate asset file for compilation", cli_genasset },
228 { "reload", "reload the application (SIGHUP)", cli_reload },
229 { "info", "show info on kore on this system", cli_info },
230 { "build", "build an application", cli_build },
231 { "clean", "cleanup the build files", cli_clean },
232 { "source", "print the path to kore sources", cli_source },
233 #if !defined(KODEV_MINIMAL)
234 { "create", "create a new application skeleton", cli_create },
235 #endif
236 { "flavor", "switch between build flavors", cli_flavor },
237 { "cflags", "show kore CFLAGS", cli_cflags },
238 { "ldflags", "show kore LDFLAGS", cli_ldflags },
239 { NULL, NULL, NULL }
240 };
241
242 #if !defined(KODEV_MINIMAL)
243 static struct filegen gen_files[] = {
244 { file_create_src },
245 { file_create_config },
246 { file_create_gitignore },
247 { NULL }
248 };
249
250 static const char *gen_dirs[] = {
251 "src",
252 "cert",
253 "conf",
254 "assets",
255 NULL
256 };
257
258 static const char *python_gen_dirs[] = {
259 "cert",
260 NULL
261 };
262
263 static struct filegen python_gen_files[] = {
264 { file_create_python_src },
265 { file_create_gitignore },
266 { NULL }
267 };
268
269 static const char *http_serveable_function =
270 "int\n"
271 "asset_serve_%s_%s(struct http_request *req)\n"
272 "{\n"
273 " http_serveable(req, asset_%s_%s, asset_len_%s_%s,\n"
274 " asset_sha256_%s_%s, \"%s\");\n"
275 " return (KORE_RESULT_OK);\n"
276 "}\n";
277
278 static const char *src_data =
279 "#include <kore/kore.h>\n"
280 "#include <kore/http.h>\n"
281 "\n"
282 "int\t\tpage(struct http_request *);\n"
283 "\n"
284 "int\n"
285 "page(struct http_request *req)\n"
286 "{\n"
287 "\thttp_response(req, 200, NULL, 0);\n"
288 "\treturn (KORE_RESULT_OK);\n"
289 "}\n";
290
291 static const char *config_data =
292 "# %s configuration\n"
293 "\n"
294 "server tls {\n"
295 "\tbind 127.0.0.1 8888\n"
296 "}\n"
297 "\n"
298 "load\t\t./%s.so\n"
299 "\n"
300 "domain * {\n"
301 "\tattach\t\ttls\n"
302 "\n"
303 "\tcertfile\tcert/server.pem\n"
304 "\tcertkey\t\tcert/key.pem\n"
305 "\n"
306 "\troute / {\n"
307 "\t\thandler page\n"
308 "\t}\n"
309 "\n"
310 "}\n";
311
312 static const char *build_data =
313 "# %s build config\n"
314 "# You can switch flavors using: kodev flavor [newflavor]\n"
315 "\n"
316 "# Set to yes if you wish to produce a single binary instead\n"
317 "# of a dynamic library. If you set this to yes you must also\n"
318 "# set kore_source together with kore_flavor.\n"
319 "#single_binary=no\n"
320 "#kore_source=/home/joris/src/kore\n"
321 "#kore_flavor=\n"
322 "\n"
323 "# The flags below are shared between flavors\n"
324 "cflags=-Wall -Wmissing-declarations -Wshadow\n"
325 "cflags=-Wstrict-prototypes -Wmissing-prototypes\n"
326 "cflags=-Wpointer-arith -Wcast-qual -Wsign-compare\n"
327 "\n"
328 "cxxflags=-Wall -Wmissing-declarations -Wshadow\n"
329 "cxxflags=-Wpointer-arith -Wcast-qual -Wsign-compare\n"
330 "\n"
331 "# Mime types for assets served via the builtin asset_serve_*\n"
332 "#mime_add=txt:text/plain; charset=utf-8\n"
333 "#mime_add=png:image/png\n"
334 "#mime_add=html:text/html; charset=utf-8\n"
335 "\n"
336 "dev {\n"
337 " # These flags are added to the shared ones when\n"
338 " # you build the \"dev\" flavor.\n"
339 " cflags=-g\n"
340 " cxxflags=-g\n"
341 "}\n"
342 "\n"
343 "#prod {\n"
344 "# You can specify additional flags here which are only\n"
345 "# included if you build with the \"prod\" flavor.\n"
346 "#}\n";
347
348 static const char *python_init_data =
349 "from .app import koreapp\n";
350
351 static const char *python_app_data =
352 "import kore\n"
353 "\n"
354 "class KoreApp:\n"
355 " def configure(self, args):\n"
356 " kore.config.deployment = \"development\"\n"
357 " kore.server(\"default\", ip=\"127.0.0.1\", port=\"8888\")\n"
358 "\n"
359 " d = kore.domain(\"*\",\n"
360 " attach=\"default\",\n"
361 " key=\"cert/key.pem\",\n"
362 " cert=\"cert/server.pem\",\n"
363 " )\n"
364 "\n"
365 " d.route(\"/\", self.index, methods=[\"get\"])\n"
366 "\n"
367 " async def index(self, req):\n"
368 " req.response(200, b'')\n"
369 "\n"
370 "koreapp = KoreApp()";
371
372 static const char *gitignore = "*.o\n.flavor\n.objs\n%s.so\nassets.h\ncert\n";
373
374 #endif /* !KODEV_MINIMAL */
375
376 static int s_fd = -1;
377 static char *appl = NULL;
378 static int run_after = 0;
379 static char *compiler_c = "cc";
380 static char *compiler_cpp = "c++";
381 static char *compiler_ld = "cc";
382 static const char *prefix = PREFIX;
383 static struct mime_list mime_types;
384 static struct cfile_list source_files;
385 static struct buildopt_list build_options;
386 static int source_files_count;
387 static int cxx_files_count;
388 static struct cmd *command = NULL;
389 static int cflags_count = 0;
390 static int genasset_cmd = 0;
391 static int cxxflags_count = 0;
392 static int ldflags_count = 0;
393 static char *flavor = NULL;
394 static char *out_dir = ".";
395 static char *object_dir = ".objs";
396 static char *cflags[CFLAGS_MAX];
397 static char *cxxflags[CXXFLAGS_MAX];
398 static char *ldflags[LD_FLAGS_MAX];
399
400 static void
401 usage(void)
402 {
403 int i;
404
405 fprintf(stderr, "Usage: kodev [command]\n");
406 #if defined(KODEV_MINIMAL)
407 fprintf(stderr, "minimal (only build commands supported)\n");
408 #endif
409 fprintf(stderr, "\nAvailable commands:\n");
410
411 for (i = 0; cmds[i].name != NULL; i++)
412 printf("\t%s\t%s\n", cmds[i].name, cmds[i].descr);
413
414 fprintf(stderr, "\nFind more information on https://kore.io\n");
415 exit(1);
416 }
417
418 int
419 main(int argc, char **argv)
420 {
421 int i;
422 char *env;
423
424 if (argc < 2)
425 usage();
426
427 argc--;
428 argv++;
429
430 if ((env = getenv("KORE_PREFIX")) != NULL)
431 prefix = env;
432
433 if ((env = getenv("KORE_OBJDIR")) != NULL)
434 object_dir = env;
435
436 if ((env = getenv("KODEV_OUTPUT")) != NULL)
437 out_dir = env;
438
439 (void)umask(S_IWGRP | S_IWOTH);
440
441 for (i = 0; cmds[i].name != NULL; i++) {
442 if (!strcmp(argv[0], cmds[i].name)) {
443 if (strcmp(argv[0], "create")) {
444 argc--;
445 argv++;
446 }
447 command = &cmds[i];
448 cmds[i].cb(argc, argv);
449 break;
450 }
451 }
452
453 if (cmds[i].name == NULL) {
454 fprintf(stderr, "unknown command: %s\n", argv[0]);
455 usage();
456 }
457
458 return (0);
459 }
460
461 static void
462 cli_help(int argc, char **argv)
463 {
464 usage();
465 }
466
467 #if !defined(KODEV_MINIMAL)
468 static void
469 cli_create_help(void)
470 {
471 printf("Usage: kodev create [-p] [name]\n");
472 printf("Synopsis:\n");
473 printf(" Create a new application skeleton directory structure.\n");
474 printf("\n");
475 printf(" Optional flags:\n");
476 printf("\t-p = generate a python application skeleton\n");
477
478 exit(1);
479 }
480
481 static void
482 cli_create(int argc, char **argv)
483 {
484 char *fpath;
485 const char **dirs;
486 struct filegen *files;
487 int i, ch, python;
488
489 python = 0;
490
491 while ((ch = getopt(argc, argv, "hp")) != -1) {
492 switch (ch) {
493 case 'h':
494 cli_create_help();
495 break;
496 case 'p':
497 python = 1;
498 break;
499 default:
500 cli_create_help();
501 break;
502 }
503 }
504
505 argc -= optind;
506 argv += optind;
507
508 if (argc != 1)
509 cli_create_help();
510
511 appl = argv[0];
512 cli_mkdir(appl, 0755);
513
514 if (python) {
515 dirs = python_gen_dirs;
516 files = python_gen_files;
517 } else {
518 dirs = gen_dirs;
519 files = gen_files;
520 }
521
522 for (i = 0; dirs[i] != NULL; i++) {
523 (void)cli_vasprintf(&fpath, "%s/%s", appl, dirs[i]);
524 cli_mkdir(fpath, 0755);
525 free(fpath);
526 }
527
528 for (i = 0; files[i].cb != NULL; i++)
529 files[i].cb();
530
531 if (chdir(appl) == -1)
532 fatal("chdir(%s): %s", appl, errno_s);
533
534 cli_generate_certs();
535
536 printf("%s created successfully!\n", appl);
537 printf("WARNING: DO NOT USE THE GENERATED CERTIFICATE IN PRODUCTION\n");
538 }
539 #endif
540
541 static void
542 cli_flavor(int argc, char **argv)
543 {
544 struct buildopt *bopt;
545 char pwd[MAXPATHLEN], *conf;
546
547 if (getcwd(pwd, sizeof(pwd)) == NULL)
548 fatal("could not get cwd: %s", errno_s);
549
550 appl = basename(pwd);
551 (void)cli_vasprintf(&conf, "conf/%s.conf", appl);
552 if (!cli_dir_exists("conf") || !cli_file_exists(conf))
553 fatal("%s doesn't appear to be a kore app", appl);
554 free(conf);
555
556 TAILQ_INIT(&build_options);
557 TAILQ_INIT(&mime_types);
558 (void)cli_buildopt_new("_default");
559 cli_buildopt_parse("conf/build.conf");
560
561 if (argc == 0) {
562 cli_flavor_load();
563 TAILQ_FOREACH(bopt, &build_options, list) {
564 if (!strcmp(bopt->name, "_default"))
565 continue;
566 if (!strcmp(bopt->name, flavor)) {
567 printf("* %s\n", bopt->name);
568 } else {
569 printf(" %s\n", bopt->name);
570 }
571 }
572 } else {
573 cli_flavor_change(argv[0]);
574 printf("changed build flavor to: %s\n", argv[0]);
575 }
576
577 cli_buildopt_cleanup();
578 }
579
580 static void
581 cli_build(int argc, char **argv)
582 {
583 #if !defined(KODEV_MINIMAL)
584 int l;
585 char *data;
586 #endif
587 struct dirent dp;
588 struct cfile *cf;
589 struct buildopt *bopt;
590 struct timeval times[2];
591 char *build_path;
592 char *vsrc, *vobj;
593 int requires_relink;
594 char *sofile, *config;
595 char *assets_path, *p, *src_path;
596 char pwd[PATH_MAX], *assets_header;
597
598 if (getcwd(pwd, sizeof(pwd)) == NULL)
599 fatal("could not get cwd: %s", errno_s);
600
601 appl = cli_strdup(basename(pwd));
602
603 if ((p = getenv("CC")) != NULL) {
604 compiler_c = p;
605 compiler_ld = p;
606 }
607
608 if ((p = getenv("CXX")) != NULL) {
609 compiler_cpp = p;
610 compiler_ld = p;
611 }
612
613 source_files_count = 0;
614 cxx_files_count = 0;
615 TAILQ_INIT(&source_files);
616 TAILQ_INIT(&build_options);
617 TAILQ_INIT(&mime_types);
618
619 (void)cli_vasprintf(&src_path, "src");
620 (void)cli_vasprintf(&assets_path, "assets");
621 (void)cli_vasprintf(&config, "conf/%s.conf", appl);
622 (void)cli_vasprintf(&build_path, "conf/build.conf");
623 (void)cli_vasprintf(&assets_header, "%s/assets.h", object_dir);
624
625 if (!cli_dir_exists(src_path) || !cli_file_exists(config))
626 fatal("%s doesn't appear to be a kore app", appl);
627
628 cli_flavor_load();
629 bopt = cli_buildopt_new("_default");
630
631 #if !defined(KODEV_MINIMAL)
632 if (!cli_file_exists(build_path)) {
633 l = cli_vasprintf(&data, build_data, appl);
634 cli_file_create("conf/build.conf", data, l);
635 free(data);
636 }
637 #endif
638
639 cli_find_files(src_path, cli_register_source_file);
640 free(src_path);
641
642 cli_buildopt_parse(build_path);
643 free(build_path);
644
645 if (!cli_dir_exists(object_dir))
646 cli_mkdir(object_dir, 0755);
647
648 if (bopt->single_binary) {
649 if (bopt->kore_source == NULL)
650 fatal("single_binary set but not kore_source");
651
652 printf("building kore (%s)\n", bopt->kore_source);
653 cli_spawn_proc(cli_compile_kore, bopt);
654
655 (void)cli_vasprintf(&src_path, "%s/src", bopt->kore_source);
656 cli_find_files(src_path, cli_register_kore_file);
657 free(src_path);
658
659 (void)cli_vasprintf(&vsrc, "%s/version.c", object_dir);
660 (void)cli_vasprintf(&vobj, "%s/version.o", object_dir);
661
662 cli_add_source_file("version.c",
663 vsrc, vobj, NULL, BUILD_NOBUILD);
664 }
665
666 printf("building %s (%s)\n", appl, flavor);
667
668 cli_build_cflags(bopt);
669 cli_build_cxxflags(bopt);
670 cli_build_ldflags(bopt);
671
672 (void)unlink(assets_header);
673
674 /* Generate the assets. */
675 cli_file_open(assets_header, O_CREAT | O_TRUNC | O_WRONLY, &s_fd);
676 cli_file_writef(s_fd, "#ifndef __H_KORE_ASSETS_H\n");
677 cli_file_writef(s_fd, "#define __H_KORE_ASSETS_H\n");
678
679 if (cli_dir_exists(assets_path))
680 cli_find_files(assets_path, cli_build_asset);
681
682 if (bopt->single_binary) {
683 memset(&dp, 0, sizeof(dp));
684 dp.d_type = DT_REG;
685 printf("adding config %s\n", config);
686 (void)snprintf(dp.d_name,
687 sizeof(dp.d_name), "builtin_kore.conf");
688 cli_build_asset(config, &dp);
689 }
690
691 cli_file_writef(s_fd, "\n#endif\n");
692 cli_file_close(s_fd);
693
694 free(assets_path);
695 free(config);
696
697 if (cxx_files_count > 0)
698 compiler_ld = compiler_cpp;
699
700 requires_relink = 0;
701 TAILQ_FOREACH(cf, &source_files, list) {
702 if (cf->build == BUILD_NOBUILD)
703 continue;
704
705 printf("compiling %s\n", cf->name);
706 cli_spawn_proc(cli_compile_source_file, cf);
707
708 times[0].tv_usec = 0;
709 times[0].tv_sec = cf->st.st_mtime;
710 times[1] = times[0];
711
712 if (utimes(cf->opath, times) == -1)
713 printf("utime(%s): %s\n", cf->opath, errno_s);
714
715 requires_relink++;
716 }
717
718 free(assets_header);
719
720 #if !defined(KODEV_MINIMAL)
721 if (bopt->kore_flavor == NULL ||
722 !strstr(bopt->kore_flavor, "NOTLS=1")) {
723 if (!cli_dir_exists("cert")) {
724 cli_mkdir("cert", 0700);
725 cli_generate_certs();
726 }
727 }
728 #endif
729
730 if (bopt->single_binary) {
731 requires_relink++;
732 (void)cli_vasprintf(&sofile, "%s/%s", out_dir, appl);
733 } else {
734 (void)cli_vasprintf(&sofile, "%s/%s.so", out_dir, appl);
735 }
736
737 if (!cli_file_exists(sofile) && source_files_count > 0)
738 requires_relink++;
739
740 free(sofile);
741
742 if (requires_relink) {
743 cli_spawn_proc(cli_link_application, bopt);
744 printf("%s built successfully!\n", appl);
745 } else {
746 printf("nothing to be done!\n");
747 }
748
749 if (run_after == 0)
750 cli_buildopt_cleanup();
751 }
752
753 static void
754 cli_source(int argc, char **argv)
755 {
756 printf("%s/share/kore/\n", prefix);
757 }
758
759 static void
760 cli_clean(int argc, char **argv)
761 {
762 struct buildopt *bopt;
763 char pwd[PATH_MAX], *bin;
764
765 if (cli_dir_exists(object_dir))
766 cli_cleanup_files(object_dir);
767
768 if (getcwd(pwd, sizeof(pwd)) == NULL)
769 fatal("could not get cwd: %s", errno_s);
770
771 appl = basename(pwd);
772
773 TAILQ_INIT(&mime_types);
774 TAILQ_INIT(&build_options);
775
776 cli_flavor_load();
777 bopt = cli_buildopt_new("_default");
778 cli_buildopt_parse("conf/build.conf");
779
780 if (bopt->single_binary)
781 (void)cli_vasprintf(&bin, "%s/%s", out_dir, appl);
782 else
783 (void)cli_vasprintf(&bin, "%s/%s.so", out_dir, appl);
784
785 if (unlink(bin) == -1 && errno != ENOENT)
786 printf("couldn't unlink %s: %s", bin, errno_s);
787
788 free(bin);
789 }
790
791 static void
792 cli_run(int argc, char **argv)
793 {
794 if (cli_file_exists("__init__.py")) {
795 cli_run_kore_python();
796 return;
797 }
798
799 run_after = 1;
800 cli_build(argc, argv);
801
802 /*
803 * We are exec()'ing kore again, while we could technically set
804 * the right cli options manually and just continue running.
805 */
806 cli_run_kore();
807 }
808
809 static void
810 cli_reload(int argc, char **argv)
811 {
812 int fd;
813 size_t len;
814 pid_t pid;
815 char *buf;
816
817 cli_file_open("kore.pid", O_RDONLY, &fd);
818 cli_file_read(fd, &buf, &len);
819 cli_file_close(fd);
820
821 if (len == 0)
822 fatal("reload: pid file is empty");
823
824 buf[len - 1] = '\0';
825
826 pid = cli_strtonum(buf, 0, UINT_MAX);
827
828 if (kill(pid, SIGHUP) == -1)
829 fatal("failed to reload: %s", errno_s);
830
831 printf("reloaded application\n");
832 }
833
834 static void
835 cli_info(int argc, char **argv)
836 {
837 size_t len;
838 struct buildopt *bopt;
839 char *features;
840
841 TAILQ_INIT(&mime_types);
842 TAILQ_INIT(&build_options);
843
844 cli_flavor_load();
845 bopt = cli_buildopt_new("_default");
846 cli_buildopt_parse("conf/build.conf");
847
848 printf("active flavor\t %s\n", flavor);
849 printf("output type \t %s\n",
850 (bopt->single_binary) ? "binary" : "dso");
851
852 if (bopt->single_binary) {
853 printf("kore features\t %s\n", bopt->kore_flavor);
854 printf("kore source \t %s\n", bopt->kore_source);
855 } else {
856 cli_kore_load_file("features", bopt, &features, &len);
857 printf("kore binary \t %s/bin/kore\n", prefix);
858 printf("kore features\t %.*s\n", (int)len, features);
859 free(features);
860 }
861 }
862
863 static void
864 cli_cflags(int argc, char **argv)
865 {
866 struct cli_buf *buf;
867
868 buf = cli_buf_alloc(128);
869 cli_build_flags_common(NULL, buf);
870 printf("%.*s\n", (int)buf->offset, buf->data);
871 cli_buf_free(buf);
872 }
873
874 static void
875 cli_ldflags(int argc, char **argv)
876 {
877 char *p;
878 size_t len;
879
880 cli_kore_load_file("linker", NULL, &p, &len);
881 printf("%.*s ", (int)len, p);
882
883 #if defined(__MACH__)
884 printf("-dynamiclib -undefined suppress -flat_namespace ");
885 #else
886 printf("-shared ");
887 #endif
888 printf("\n");
889
890 free(p);
891 }
892
893 static void
894 cli_genasset(int argc, char **argv)
895 {
896 struct stat st;
897 struct dirent dp;
898 char *hdr;
899
900 genasset_cmd = 1;
901 TAILQ_INIT(&build_options);
902 (void)cli_buildopt_new("_default");
903
904 if (getenv("KORE_OBJDIR") == NULL)
905 object_dir = out_dir;
906
907 if (argv[0] == NULL)
908 cli_genasset_help();
909
910 (void)cli_vasprintf(&hdr, "%s/assets.h", out_dir);
911 (void)unlink(hdr);
912
913 cli_file_open(hdr, O_CREAT | O_TRUNC | O_WRONLY, &s_fd);
914 cli_file_writef(s_fd, "#ifndef __H_KORE_ASSETS_H\n");
915 cli_file_writef(s_fd, "#define __H_KORE_ASSETS_H\n");
916
917 if (stat(argv[0], &st) == -1)
918 fatal("%s: %s", argv[0], errno_s);
919
920 if (S_ISDIR(st.st_mode)) {
921 if (cli_dir_exists(argv[0]))
922 cli_find_files(argv[0], cli_build_asset);
923 } else if (S_ISREG(st.st_mode)) {
924 memset(&dp, 0, sizeof(dp));
925 dp.d_type = DT_REG;
926 (void)snprintf(dp.d_name, sizeof(dp.d_name), "%s",
927 basename(argv[0]));
928 cli_build_asset(argv[0], &dp);
929 } else {
930 fatal("%s is not a directory or regular file", argv[0]);
931 }
932
933 cli_file_writef(s_fd, "\n#endif\n");
934 cli_file_close(s_fd);
935 }
936
937 static void
938 cli_genasset_help(void)
939 {
940 printf("Usage: kodev genasset [source]\n");
941 printf("Synopsis:\n");
942 printf(" Generates asset file(s) to be used for compilation.\n");
943 printf(" The source can be a single file or directory.\n");
944 printf("\n");
945 printf("This command honors the KODEV_OUTPUT environment variable.\n");
946 printf("This command honors the KORE_OBJDIR environment variable.\n");
947
948 exit(1);
949 }
950
951 #if !defined(KODEV_MINIMAL)
952 static void
953 file_create_python_src(void)
954 {
955 char *name;
956
957 (void)cli_vasprintf(&name, "%s/__init__.py", appl);
958 cli_file_create(name, python_init_data, strlen(python_init_data));
959 free(name);
960
961 (void)cli_vasprintf(&name, "%s/app.py", appl);
962 cli_file_create(name, python_app_data, strlen(python_app_data));
963 free(name);
964 }
965
966 static void
967 file_create_src(void)
968 {
969 char *name;
970
971 (void)cli_vasprintf(&name, "%s/src/%s.c", appl, appl);
972 cli_file_create(name, src_data, strlen(src_data));
973 free(name);
974 }
975
976 static void
977 file_create_config(void)
978 {
979 int l;
980 char *name, *data;
981
982 (void)cli_vasprintf(&name, "%s/conf/%s.conf", appl, appl);
983 l = cli_vasprintf(&data, config_data, appl, appl);
984 cli_file_create(name, data, l);
985 free(name);
986 free(data);
987
988 (void)cli_vasprintf(&name, "%s/conf/build.conf", appl);
989 l = cli_vasprintf(&data, build_data, appl);
990 cli_file_create(name, data, l);
991 free(name);
992 free(data);
993 }
994
995 static void
996 file_create_gitignore(void)
997 {
998 int l;
999 char *name, *data;
1000
1001 (void)cli_vasprintf(&name, "%s/.gitignore", appl);
1002 l = cli_vasprintf(&data, gitignore, appl);
1003 cli_file_create(name, data, l);
1004 free(name);
1005 free(data);
1006 }
1007 #endif
1008
1009 static void
1010 cli_mkdir(const char *fpath, int mode)
1011 {
1012 if (mkdir(fpath, mode) == -1)
1013 fatal("cli_mkdir(%s): %s", fpath, errno_s);
1014 }
1015
1016 static int
1017 cli_file_exists(const char *fpath)
1018 {
1019 struct stat st;
1020
1021 if (stat(fpath, &st) == -1)
1022 return (0);
1023
1024 if (!S_ISREG(st.st_mode))
1025 return (0);
1026
1027 return (1);
1028 }
1029
1030 static int
1031 cli_file_requires_build(struct stat *fst, const char *opath)
1032 {
1033 struct stat ost;
1034
1035 if (stat(opath, &ost) == -1) {
1036 if (errno == ENOENT)
1037 return (1);
1038 fatal("stat(%s): %s", opath, errno_s);
1039 }
1040
1041 return (fst->st_mtime != ost.st_mtime);
1042 }
1043
1044 static int
1045 cli_dir_exists(const char *fpath)
1046 {
1047 struct stat st;
1048
1049 if (stat(fpath, &st) == -1)
1050 return (0);
1051
1052 if (!S_ISDIR(st.st_mode))
1053 return (0);
1054
1055 return (1);
1056 }
1057
1058 static void
1059 cli_file_open(const char *fpath, int flags, int *fd)
1060 {
1061 if ((*fd = open(fpath, flags, 0644)) == -1)
1062 fatal("cli_file_open(%s): %s", fpath, errno_s);
1063 }
1064
1065 static void
1066 cli_file_read(int fd, char **buf, size_t *len)
1067 {
1068 struct stat st;
1069 char *p;
1070 ssize_t ret;
1071 size_t offset, bytes;
1072
1073 if (fstat(fd, &st) == -1)
1074 fatal("fstat(): %s", errno_s);
1075
1076 if (st.st_size > USHRT_MAX)
1077 fatal("cli_file_read: way too big");
1078
1079 offset = 0;
1080 bytes = st.st_size;
1081 p = cli_malloc(bytes);
1082
1083 while (offset != bytes) {
1084 ret = read(fd, p + offset, bytes - offset);
1085 if (ret == -1) {
1086 if (errno == EINTR)
1087 continue;
1088 fatal("read(): %s", errno_s);
1089 }
1090
1091 if (ret == 0)
1092 fatal("unexpected EOF");
1093
1094 offset += (size_t)ret;
1095 }
1096
1097 *buf = p;
1098 *len = bytes;
1099 }
1100
1101 static void
1102 cli_file_close(int fd)
1103 {
1104 if (close(fd) == -1)
1105 printf("warning: close() %s\n", errno_s);
1106 }
1107
1108 static void
1109 cli_file_writef(int fd, const char *fmt, ...)
1110 {
1111 int l;
1112 char *buf;
1113 va_list args;
1114
1115 va_start(args, fmt);
1116 l = vasprintf(&buf, fmt, args);
1117 va_end(args);
1118
1119 if (l == -1)
1120 fatal("cli_file_writef");
1121
1122 cli_file_write(fd, buf, l);
1123 free(buf);
1124 }
1125
1126 static void
1127 cli_file_write(int fd, const void *buf, size_t len)
1128 {
1129 ssize_t r;
1130 const u_int8_t *d;
1131 size_t written;
1132
1133 d = buf;
1134 written = 0;
1135 while (written != len) {
1136 r = write(fd, d + written, len - written);
1137 if (r == -1) {
1138 if (errno == EINTR)
1139 continue;
1140 fatal("cli_file_write: %s", errno_s);
1141 }
1142
1143 written += r;
1144 }
1145 }
1146
1147 #if !defined(KODEV_MINIMAL)
1148 static void
1149 cli_file_create(const char *name, const char *data, size_t len)
1150 {
1151 int fd;
1152
1153 cli_file_open(name, O_CREAT | O_TRUNC | O_WRONLY, &fd);
1154 cli_file_write(fd, data, len);
1155 cli_file_close(fd);
1156
1157 printf("created %s\n", name);
1158 }
1159 #endif
1160
1161 static void
1162 cli_write_asset(const char *n, const char *e, struct buildopt *bopt)
1163 {
1164 cli_file_writef(s_fd, "extern const u_int8_t asset_%s_%s[];\n", n, e);
1165 cli_file_writef(s_fd, "extern const u_int32_t asset_len_%s_%s;\n", n, e);
1166 cli_file_writef(s_fd, "extern const time_t asset_mtime_%s_%s;\n", n, e);
1167
1168 #if !defined(KODEV_MINIMAL)
1169 cli_file_writef(s_fd, "extern const char *asset_sha256_%s_%s;\n", n, e);
1170 #endif
1171
1172 if (bopt->flavor_nohttp == 0) {
1173 cli_file_writef(s_fd,
1174 "int asset_serve_%s_%s(struct http_request *);\n", n, e);
1175 }
1176 }
1177
1178 static void
1179 cli_build_asset(char *fpath, struct dirent *dp)
1180 {
1181 u_int8_t *d;
1182 struct stat st;
1183 #if !defined(KODEV_MINIMAL)
1184 SHA256_CTX sctx;
1185 int i, len;
1186 struct mime_type *mime;
1187 const char *mime_type;
1188 u_int8_t digest[SHA256_DIGEST_LENGTH];
1189 char hash[(SHA256_DIGEST_LENGTH * 2) + 1];
1190 #endif
1191 off_t off;
1192 void *base;
1193 struct buildopt *bopt;
1194 int in, out;
1195 char *cpath, *ext, *opath, *p, *name;
1196
1197 bopt = cli_buildopt_default();
1198
1199 /* Ignore hidden files and some editor files */
1200 if (dp->d_name[0] == '.' ||
1201 strrchr(dp->d_name, '~') || strrchr(dp->d_name, '#')) {
1202 return;
1203 }
1204
1205 name = cli_strdup(dp->d_name);
1206
1207 /* Grab the extension as we're using it in the symbol name. */
1208 if ((ext = strrchr(name, '.')) == NULL)
1209 fatal("couldn't find ext in %s", name);
1210
1211 /* Replace dots, spaces, etc etc with underscores. */
1212 for (p = name; *p != '\0'; p++) {
1213 if (*p == '.' || isspace((unsigned char)*p) || *p == '-')
1214 *p = '_';
1215 }
1216
1217 /* Grab inode information. */
1218 if (stat(fpath, &st) == -1)
1219 fatal("stat: %s %s", fpath, errno_s);
1220
1221 /* If this file was empty, skip it. */
1222 if (st.st_size == 0) {
1223 printf("skipping empty asset %s\n", name);
1224 free(name);
1225 return;
1226 }
1227
1228 (void)cli_vasprintf(&opath, "%s/%s.o", object_dir, name);
1229 (void)cli_vasprintf(&cpath, "%s/%s.c", object_dir, name);
1230
1231 /* Check if the file needs to be built. */
1232 if (!cli_file_requires_build(&st, opath)) {
1233 *(ext)++ = '\0';
1234 cli_write_asset(name, ext, bopt);
1235 *ext = '_';
1236
1237 cli_add_source_file(name, cpath, opath, &st, BUILD_NOBUILD);
1238 free(name);
1239 return;
1240 }
1241
1242 /* Open the file we're converting. */
1243 cli_file_open(fpath, O_RDONLY, &in);
1244
1245 /* mmap our in file. */
1246 if ((base = mmap(NULL, st.st_size,
1247 PROT_READ, MAP_PRIVATE, in, 0)) == MAP_FAILED)
1248 fatal("mmap: %s %s", fpath, errno_s);
1249
1250 /* Create the c file where we will write too. */
1251 cli_file_open(cpath, O_CREAT | O_TRUNC | O_WRONLY, &out);
1252
1253 /* No longer need name so cut off the extension. */
1254 printf("building asset %s\n", dp->d_name);
1255 *(ext)++ = '\0';
1256
1257 /* Start generating the file. */
1258 cli_file_writef(out, "/* Auto generated */\n");
1259 cli_file_writef(out, "#include <sys/types.h>\n\n");
1260 cli_file_writef(out, "#include <kore/kore.h>\n");
1261 cli_file_writef(out, "#include <kore/http.h>\n\n");
1262 cli_file_writef(out, "#include \"assets.h\"\n\n");
1263
1264 /* Write the file data as a byte array. */
1265 cli_file_writef(out, "const u_int8_t asset_%s_%s[] = {\n", name, ext);
1266 d = base;
1267 for (off = 0; off < st.st_size; off++)
1268 cli_file_writef(out, "0x%02x,", *d++);
1269
1270 /*
1271 * Always NUL-terminate the asset, even if this NUL is not included in
1272 * the actual length. This way assets can be cast to char * without
1273 * any additional thinking for the developer.
1274 */
1275 cli_file_writef(out, "0x00");
1276
1277 #if !defined(KODEV_MINIMAL)
1278 /* Calculate the SHA256 digest of the contents. */
1279 (void)SHA256_Init(&sctx);
1280 (void)SHA256_Update(&sctx, base, st.st_size);
1281 (void)SHA256_Final(digest, &sctx);
1282
1283 for (i = 0; i < (int)sizeof(digest); i++) {
1284 len = snprintf(hash + (i * 2), sizeof(hash) - (i * 2),
1285 "%02x", digest[i]);
1286 if (len == -1 || (size_t)len >= sizeof(hash))
1287 fatal("failed to convert SHA256 digest to hex");
1288 }
1289
1290 mime = NULL;
1291 TAILQ_FOREACH(mime, &mime_types, list) {
1292 if (!strcasecmp(mime->ext, ext))
1293 break;
1294 }
1295
1296 if (mime != NULL)
1297 mime_type = mime->type;
1298 else
1299 mime_type = "text/plain";
1300 #endif
1301
1302 /* Add the meta data. */
1303 cli_file_writef(out, "};\n\n");
1304 cli_file_writef(out, "const u_int32_t asset_len_%s_%s = %" PRIu32 ";\n",
1305 name, ext, (u_int32_t)st.st_size);
1306 cli_file_writef(out,
1307 "const time_t asset_mtime_%s_%s = %" PRI_TIME_T ";\n",
1308 name, ext, st.st_mtime);
1309
1310 #if !defined(KODEV_MINIMAL)
1311 if (bopt->flavor_nohttp == 0) {
1312 cli_file_writef(out,
1313 "const char *asset_sha256_%s_%s = \"\\\"%s\\\"\";\n",
1314 name, ext, hash);
1315 cli_file_writef(out, http_serveable_function,
1316 name, ext, name, ext, name, ext, name, ext, mime_type);
1317 }
1318 #endif
1319
1320 /* Write the file symbols into assets.h so they can be used. */
1321 cli_write_asset(name, ext, bopt);
1322
1323 /* Cleanup static file source. */
1324 if (munmap(base, st.st_size) == -1)
1325 fatal("munmap: %s %s", fpath, errno_s);
1326
1327 /* Cleanup fds */
1328 cli_file_close(in);
1329 cli_file_close(out);
1330
1331 /* Restore the original name */
1332 *--ext = '.';
1333
1334 /* Register the .c file now (cpath is free'd later). */
1335 if (genasset_cmd == 0)
1336 cli_add_source_file(name, cpath, opath, &st, BUILD_C);
1337
1338 free(name);
1339 }
1340
1341 static void
1342 cli_add_source_file(char *name, char *fpath, char *opath, struct stat *st,
1343 int build)
1344 {
1345 struct cfile *cf;
1346
1347 source_files_count++;
1348 cf = cli_malloc(sizeof(*cf));
1349
1350 if (st != NULL)
1351 cf->st = *st;
1352 else
1353 memset(&cf->st, 0, sizeof(cf->st));
1354
1355 cf->build = build;
1356 cf->fpath = fpath;
1357 cf->opath = opath;
1358 cf->name = cli_strdup(name);
1359
1360 TAILQ_INSERT_TAIL(&source_files, cf, list);
1361 }
1362
1363 static void
1364 cli_register_source_file(char *fpath, struct dirent *dp)
1365 {
1366 struct stat st;
1367 char *ext, *opath;
1368 int build;
1369
1370 if ((ext = strrchr(fpath, '.')) == NULL ||
1371 (strcmp(ext, ".c") && strcmp(ext, ".cpp")))
1372 return;
1373
1374 if (stat(fpath, &st) == -1)
1375 fatal("stat(%s): %s", fpath, errno_s);
1376
1377 if (!strcmp(ext, ".cpp"))
1378 cxx_files_count++;
1379
1380 (void)cli_vasprintf(&opath, "%s/%s.o", object_dir, dp->d_name);
1381 if (!cli_file_requires_build(&st, opath)) {
1382 build = BUILD_NOBUILD;
1383 } else if (!strcmp(ext, ".cpp")) {
1384 build = BUILD_CXX;
1385 } else {
1386 build = BUILD_C;
1387 }
1388
1389 cli_add_source_file(dp->d_name, fpath, opath, &st, build);
1390 }
1391
1392 static void
1393 cli_register_kore_file(char *fpath, struct dirent *dp)
1394 {
1395 struct stat st, ost;
1396 char *opath, *ext, *fname;
1397
1398 if ((ext = strrchr(fpath, '.')) == NULL || strcmp(ext, ".c"))
1399 return;
1400
1401 if (stat(fpath, &st) == -1)
1402 fatal("stat(%s): %s", fpath, errno_s);
1403
1404 *ext = '\0';
1405 if ((fname = basename(fpath)) == NULL)
1406 fatal("basename failed");
1407
1408 (void)cli_vasprintf(&opath, "%s/%s.o", object_dir, fname);
1409
1410 /* Silently ignore non existing object files for kore source files. */
1411 if (stat(opath, &ost) == -1) {
1412 free(opath);
1413 return;
1414 }
1415
1416 cli_add_source_file(dp->d_name, fpath, opath, &st, BUILD_NOBUILD);
1417 }
1418
1419 static void
1420 cli_file_remove(char *fpath, struct dirent *dp)
1421 {
1422 if (unlink(fpath) == -1)
1423 fprintf(stderr, "couldn't unlink %s: %s", fpath, errno_s);
1424 }
1425
1426 static void
1427 cli_find_files(const char *path, void (*cb)(char *, struct dirent *))
1428 {
1429 DIR *d;
1430 struct stat st;
1431 struct dirent *dp;
1432 char *fpath;
1433
1434 if ((d = opendir(path)) == NULL)
1435 fatal("cli_find_files: opendir(%s): %s", path, errno_s);
1436
1437 while ((dp = readdir(d)) != NULL) {
1438 if (!strcmp(dp->d_name, ".") ||
1439 !strcmp(dp->d_name, ".."))
1440 continue;
1441
1442 (void)cli_vasprintf(&fpath, "%s/%s", path, dp->d_name);
1443 if (stat(fpath, &st) == -1) {
1444 fprintf(stderr, "stat(%s): %s\n", fpath, errno_s);
1445 free(fpath);
1446 continue;
1447 }
1448
1449 if (S_ISDIR(st.st_mode)) {
1450 cli_find_files(fpath, cb);
1451 free(fpath);
1452 } else if (S_ISREG(st.st_mode)) {
1453 cb(fpath, dp);
1454 } else {
1455 fprintf(stderr, "ignoring %s\n", fpath);
1456 free(fpath);
1457 }
1458 }
1459
1460 closedir(d);
1461 }
1462
1463 #if !defined(KODEV_MINIMAL)
1464 static void
1465 cli_generate_certs(void)
1466 {
1467 BIGNUM *e;
1468 FILE *fp;
1469 time_t now;
1470 X509_NAME *name;
1471 EVP_PKEY *pkey;
1472 X509 *x509;
1473 RSA *kpair;
1474 char issuer[64];
1475
1476 /* Create new certificate. */
1477 if ((x509 = X509_new()) == NULL)
1478 fatal("X509_new(): %s", ssl_errno_s);
1479
1480 /* Generate version 3. */
1481 if (!X509_set_version(x509, 2))
1482 fatal("X509_set_version(): %s", ssl_errno_s);
1483
1484 /* Generate RSA keys. */
1485 if ((pkey = EVP_PKEY_new()) == NULL)
1486 fatal("EVP_PKEY_new(): %s", ssl_errno_s);
1487 if ((kpair = RSA_new()) == NULL)
1488 fatal("RSA_new(): %s", ssl_errno_s);
1489 if ((e = BN_new()) == NULL)
1490 fatal("BN_new(): %s", ssl_errno_s);
1491
1492 if (!BN_set_word(e, 65537))
1493 fatal("BN_set_word(): %s", ssl_errno_s);
1494 if (!RSA_generate_key_ex(kpair, 2048, e, NULL))
1495 fatal("RSA_generate_key_ex(): %s", ssl_errno_s);
1496
1497 BN_free(e);
1498
1499 if (!EVP_PKEY_assign_RSA(pkey, kpair))
1500 fatal("EVP_PKEY_assign_RSA(): %s", ssl_errno_s);
1501
1502 /* Set serial number to current timestamp. */
1503 time(&now);
1504 if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), now))
1505 fatal("ASN1_INTEGER_set(): %s", ssl_errno_s);
1506
1507 /* Not before and not after dates. */
1508 if (!X509_gmtime_adj(X509_get_notBefore(x509), 0))
1509 fatal("X509_gmtime_adj(): %s", ssl_errno_s);
1510 if (!X509_gmtime_adj(X509_get_notAfter(x509),
1511 (long)60 * 60 * 24 * 3000))
1512 fatal("X509_gmtime_adj(): %s", ssl_errno_s);
1513
1514 /* Attach the pkey to the certificate. */
1515 if (!X509_set_pubkey(x509, pkey))
1516 fatal("X509_set_pubkey(): %s", ssl_errno_s);
1517
1518 /* Set certificate information. */
1519 if ((name = X509_get_subject_name(x509)) == NULL)
1520 fatal("X509_get_subject_name(): %s", ssl_errno_s);
1521
1522 (void)snprintf(issuer, sizeof(issuer), "kore autogen: %s", appl);
1523 if (!X509_NAME_add_entry_by_txt(name, "C",
1524 MBSTRING_ASC, (const unsigned char *)"SE", -1, -1, 0))
1525 fatal("X509_NAME_add_entry_by_txt(): C %s", ssl_errno_s);
1526 if (!X509_NAME_add_entry_by_txt(name, "O",
1527 MBSTRING_ASC, (const unsigned char *)issuer, -1, -1, 0))
1528 fatal("X509_NAME_add_entry_by_txt(): O %s", ssl_errno_s);
1529 if (!X509_NAME_add_entry_by_txt(name, "CN",
1530 MBSTRING_ASC, (const unsigned char *)"localhost", -1, -1, 0))
1531 fatal("X509_NAME_add_entry_by_txt(): CN %s", ssl_errno_s);
1532
1533 if (!X509_set_issuer_name(x509, name))
1534 fatal("X509_set_issuer_name(): %s", ssl_errno_s);
1535
1536 if (!X509_sign(x509, pkey, EVP_sha256()))
1537 fatal("X509_sign(): %s", ssl_errno_s);
1538
1539 if ((fp = fopen("cert/key.pem", "w")) == NULL)
1540 fatal("fopen(cert/key.pem): %s", errno_s);
1541 if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL))
1542 fatal("PEM_write_PrivateKey(): %s", ssl_errno_s);
1543 fclose(fp);
1544
1545 if ((fp = fopen("cert/server.pem", "w")) == NULL)
1546 fatal("fopen(cert/server.pem): %s", errno_s);
1547 if (!PEM_write_X509(fp, x509))
1548 fatal("PEM_write_X509(%s)", errno_s);
1549 fclose(fp);
1550
1551 EVP_PKEY_free(pkey);
1552 X509_free(x509);
1553 }
1554 #endif
1555
1556 static void
1557 cli_compile_source_file(void *arg)
1558 {
1559 struct cfile *cf;
1560 int idx, i;
1561 char **flags;
1562 char *compiler;
1563 int flags_count;
1564 char *args[34 + CFLAGS_MAX];
1565
1566 cf = arg;
1567
1568 switch (cf->build) {
1569 case BUILD_C:
1570 compiler = compiler_c;
1571 flags = cflags;
1572 flags_count = cflags_count;
1573 break;
1574 case BUILD_CXX:
1575 compiler = compiler_cpp;
1576 flags = cxxflags;
1577 flags_count = cxxflags_count;
1578 break;
1579 default:
1580 fatal("cli_compile_file: unexpected file type: %d",
1581 cf->build);
1582 break;
1583 }
1584
1585 idx = 0;
1586 args[idx++] = compiler;
1587
1588 for (i = 0; i < flags_count; i++)
1589 args[idx++] = flags[i];
1590
1591 args[idx++] = "-I";
1592 args[idx++] = object_dir;
1593 args[idx++] = "-c";
1594 args[idx++] = cf->fpath;
1595 args[idx++] = "-o";
1596 args[idx++] = cf->opath;
1597 args[idx] = NULL;
1598
1599 execvp(compiler, args);
1600 fatal("failed to start '%s': %s", compiler, errno_s);
1601 }
1602
1603 static void
1604 cli_link_application(void *arg)
1605 {
1606 struct cfile *cf;
1607 struct buildopt *bopt;
1608 int idx, i;
1609 char *output;
1610 char *args[source_files_count + 11 + LD_FLAGS_MAX];
1611
1612 bopt = arg;
1613
1614 if (bopt->single_binary)
1615 (void)cli_vasprintf(&output, "%s/%s", out_dir, appl);
1616 else
1617 (void)cli_vasprintf(&output, "%s/%s.so", out_dir, appl);
1618
1619 idx = 0;
1620 args[idx++] = compiler_ld;
1621
1622 TAILQ_FOREACH(cf, &source_files, list)
1623 args[idx++] = cf->opath;
1624
1625 for (i = 0; i < ldflags_count; i++)
1626 args[idx++] = ldflags[i];
1627
1628 args[idx++] = "-o";
1629 args[idx++] = output;
1630 args[idx] = NULL;
1631
1632 execvp(compiler_ld, args);
1633 fatal("failed to start '%s': %s", compiler_ld, errno_s);
1634 }
1635
1636 static void
1637 cli_compile_kore(void *arg)
1638 {
1639 struct buildopt *bopt = arg;
1640 int idx, i, fcnt;
1641 char pwd[MAXPATHLEN], *obj, *args[20], *flavors[7];
1642
1643 if (object_dir[0] != '/') {
1644 if (getcwd(pwd, sizeof(pwd)) == NULL)
1645 fatal("could not get cwd: %s", errno_s);
1646 (void)cli_vasprintf(&obj, "OBJDIR=%s/%s", pwd, object_dir);
1647 } else {
1648 (void)cli_vasprintf(&obj, "OBJDIR=%s", object_dir);
1649 }
1650
1651 if (putenv(obj) != 0)
1652 fatal("cannot set OBJDIR for building kore");
1653
1654 fcnt = cli_split_string(bopt->kore_flavor, " ", flavors, 7);
1655
1656 #if defined(OpenBSD) || defined(__FreeBSD_version) || \
1657 defined(NetBSD) || defined(__DragonFly_version)
1658 args[0] = "gmake";
1659 #else
1660 args[0] = "make";
1661 #endif
1662
1663 args[1] = "-s";
1664 args[2] = "-C";
1665 args[3] = bopt->kore_source;
1666 args[4] = "objects";
1667
1668 idx = 5;
1669 for (i = 0; i < fcnt; i++) {
1670 printf("using flavor %s\n", flavors[i]);
1671 args[idx++] = flavors[i];
1672 }
1673
1674 args[idx++] = "KORE_SINGLE_BINARY=1";
1675 args[idx] = NULL;
1676
1677 execvp(args[0], args);
1678 fatal("failed to start '%s': %s", args[0], errno_s);
1679 }
1680
1681 static void
1682 cli_run_kore_python(void)
1683 {
1684 char *args[5], *cmd;
1685 char pwd[MAXPATHLEN];
1686
1687 (void)cli_vasprintf(&cmd, "%s/bin/kore", prefix);
1688
1689 if (getcwd(pwd, sizeof(pwd)) == NULL)
1690 fatal("could not get cwd: %s", errno_s);
1691
1692 args[0] = cmd;
1693 args[1] = pwd;
1694 args[2] = NULL;
1695
1696 execvp(args[0], args);
1697 fatal("failed to start '%s': %s", args[0], errno_s);
1698
1699 }
1700
1701 static void
1702 cli_run_kore(void)
1703 {
1704 struct buildopt *bopt;
1705 char *args[4], *cpath, *cmd, *flags;
1706
1707 bopt = cli_buildopt_default();
1708
1709 if (bopt->single_binary) {
1710 cpath = NULL;
1711 flags = "-fnr";
1712 (void)cli_vasprintf(&cmd, "./%s", appl);
1713 } else {
1714 flags = "-fnrc";
1715 (void)cli_vasprintf(&cmd, "%s/bin/kore", prefix);
1716 (void)cli_vasprintf(&cpath, "conf/%s.conf", appl);
1717 }
1718
1719 args[0] = cmd;
1720 args[1] = flags;
1721
1722 if (cpath != NULL) {
1723 args[2] = cpath;
1724 args[3] = NULL;
1725 } else {
1726 args[2] = NULL;
1727 }
1728
1729 execvp(args[0], args);
1730 fatal("failed to start '%s': %s", args[0], errno_s);
1731 }
1732
1733 static void
1734 cli_buildopt_parse(const char *path)
1735 {
1736 FILE *fp;
1737 const char *env;
1738 struct buildopt *bopt;
1739 char buf[BUFSIZ], *p, *t;
1740
1741 if ((fp = fopen(path, "r")) == NULL)
1742 fatal("cli_buildopt_parse: fopen(%s): %s", path, errno_s);
1743
1744 bopt = NULL;
1745
1746 while ((p = cli_read_line(fp, buf, sizeof(buf))) != NULL) {
1747 if (strlen(p) == 0)
1748 continue;
1749
1750 if (bopt != NULL && !strcmp(p, "}")) {
1751 bopt = NULL;
1752 continue;
1753 }
1754
1755 if (bopt == NULL) {
1756 if ((t = strchr(p, '=')) != NULL)
1757 goto parse_option;
1758 if ((t = strchr(p, ' ')) == NULL)
1759 fatal("unexpected '%s'", p);
1760 *(t)++ = '\0';
1761 if (strcmp(t, "{"))
1762 fatal("expected '{', got '%s'", t);
1763 bopt = cli_buildopt_new(p);
1764 continue;
1765 }
1766
1767 if ((t = strchr(p, '=')) == NULL) {
1768 printf("bad buildopt line: '%s'\n", p);
1769 continue;
1770 }
1771
1772 parse_option:
1773 *(t)++ = '\0';
1774
1775 p = cli_text_trim(p, strlen(p));
1776 t = cli_text_trim(t, strlen(t));
1777
1778 if (!strcasecmp(p, "cflags")) {
1779 cli_buildopt_cflags(bopt, t);
1780 } else if (!strcasecmp(p, "cxxflags")) {
1781 cli_buildopt_cxxflags(bopt, t);
1782 } else if (!strcasecmp(p, "ldflags")) {
1783 cli_buildopt_ldflags(bopt, t);
1784 } else if (!strcasecmp(p, "single_binary")) {
1785 cli_buildopt_single_binary(bopt, t);
1786 } else if (!strcasecmp(p, "kore_source")) {
1787 cli_buildopt_kore_source(bopt, t);
1788 } else if (!strcasecmp(p, "kore_flavor")) {
1789 cli_buildopt_kore_flavor(bopt, t);
1790 } else if (!strcasecmp(p, "mime_add")) {
1791 cli_buildopt_mime(bopt, t);
1792 } else {
1793 printf("ignoring unknown option '%s'\n", p);
1794 }
1795 }
1796
1797 fclose(fp);
1798
1799 if ((env = getenv("KORE_SOURCE")) != NULL)
1800 cli_buildopt_kore_source(NULL, env);
1801
1802 if ((env = getenv("KORE_FLAVOR")) != NULL)
1803 cli_buildopt_kore_flavor(NULL, env);
1804 }
1805
1806 static struct buildopt *
1807 cli_buildopt_new(const char *name)
1808 {
1809 struct buildopt *bopt;
1810
1811 bopt = cli_malloc(sizeof(*bopt));
1812 bopt->cflags = NULL;
1813 bopt->cxxflags = NULL;
1814 bopt->ldflags = NULL;
1815 bopt->flavor_nohttp = 0;
1816 bopt->single_binary = 0;
1817 bopt->kore_flavor = NULL;
1818 bopt->name = cli_strdup(name);
1819
1820 (void)cli_vasprintf(&bopt->kore_source, "%s/share/kore/", prefix);
1821
1822 TAILQ_INSERT_TAIL(&build_options, bopt, list);
1823 return (bopt);
1824 }
1825
1826 static struct buildopt *
1827 cli_buildopt_find(const char *name)
1828 {
1829 struct buildopt *bopt;
1830
1831 TAILQ_FOREACH(bopt, &build_options, list) {
1832 if (!strcmp(bopt->name, name))
1833 return (bopt);
1834 }
1835
1836 return (NULL);
1837 }
1838
1839 static struct buildopt *
1840 cli_buildopt_default(void)
1841 {
1842 struct buildopt *bopt;
1843
1844 if ((bopt = cli_buildopt_find("_default")) == NULL)
1845 fatal("no _default buildopt options");
1846
1847 return (bopt);
1848 }
1849
1850 static void
1851 cli_buildopt_cleanup(void)
1852 {
1853 struct buildopt *bopt, *next;
1854 struct mime_type *mime, *mnext;
1855
1856 for (bopt = TAILQ_FIRST(&build_options); bopt != NULL; bopt = next) {
1857 next = TAILQ_NEXT(bopt, list);
1858 TAILQ_REMOVE(&build_options, bopt, list);
1859
1860 if (bopt->cflags != NULL)
1861 cli_buf_free(bopt->cflags);
1862 if (bopt->cxxflags != NULL)
1863 cli_buf_free(bopt->cxxflags);
1864 if (bopt->ldflags != NULL)
1865 cli_buf_free(bopt->ldflags);
1866 if (bopt->kore_source != NULL)
1867 free(bopt->kore_source);
1868 if (bopt->kore_flavor != NULL)
1869 free(bopt->kore_flavor);
1870 free(bopt);
1871 }
1872
1873 for (mime = TAILQ_FIRST(&mime_types); mime != NULL; mime = mnext) {
1874 mnext = TAILQ_NEXT(mime, list);
1875 TAILQ_REMOVE(&mime_types, mime, list);
1876 free(mime->type);
1877 free(mime->ext);
1878 free(mime);
1879 }
1880 }
1881
1882 static void
1883 cli_buildopt_cflags(struct buildopt *bopt, const char *string)
1884 {
1885 if (bopt == NULL)
1886 bopt = cli_buildopt_default();
1887
1888 if (bopt->cflags == NULL)
1889 bopt->cflags = cli_buf_alloc(128);
1890
1891 cli_buf_appendf(bopt->cflags, "%s ", string);
1892 }
1893
1894 static void
1895 cli_buildopt_cxxflags(struct buildopt *bopt, const char *string)
1896 {
1897 if (bopt == NULL)
1898 bopt = cli_buildopt_default();
1899
1900 if (bopt->cxxflags == NULL)
1901 bopt->cxxflags = cli_buf_alloc(128);
1902
1903 cli_buf_appendf(bopt->cxxflags, "%s ", string);
1904 }
1905
1906 static void
1907 cli_buildopt_ldflags(struct buildopt *bopt, const char *string)
1908 {
1909 if (bopt == NULL)
1910 bopt = cli_buildopt_default();
1911
1912 if (bopt->ldflags == NULL)
1913 bopt->ldflags = cli_buf_alloc(128);
1914
1915 cli_buf_appendf(bopt->ldflags, "%s ", string);
1916 }
1917
1918 static void
1919 cli_buildopt_single_binary(struct buildopt *bopt, const char *string)
1920 {
1921 if (bopt == NULL)
1922 bopt = cli_buildopt_default();
1923 else
1924 fatal("single_binary only supported in global context");
1925
1926 if (!strcmp(string, "yes"))
1927 bopt->single_binary = 1;
1928 else
1929 bopt->single_binary = 0;
1930 }
1931
1932 static void
1933 cli_buildopt_kore_source(struct buildopt *bopt, const char *string)
1934 {
1935 if (bopt == NULL)
1936 bopt = cli_buildopt_default();
1937 else
1938 fatal("kore_source only supported in global context");
1939
1940 if (bopt->kore_source != NULL)
1941 free(bopt->kore_source);
1942
1943 bopt->kore_source = cli_strdup(string);
1944 }
1945
1946 static void
1947 cli_buildopt_kore_flavor(struct buildopt *bopt, const char *string)
1948 {
1949 int cnt, i;
1950 char *p, *copy, *flavors[10];
1951
1952 if (bopt == NULL)
1953 bopt = cli_buildopt_default();
1954 else
1955 fatal("kore_flavor only supported in global context");
1956
1957 if (bopt->kore_flavor != NULL)
1958 free(bopt->kore_flavor);
1959
1960 copy = cli_strdup(string);
1961 cnt = cli_split_string(copy, " ", flavors, 10);
1962
1963 for (i = 0; i < cnt; i++) {
1964 if ((p = strchr(flavors[i], '=')) == NULL)
1965 fatal("invalid flavor %s", string);
1966
1967 *p = '\0';
1968
1969 if (!strcmp(flavors[i], "NOHTTP"))
1970 bopt->flavor_nohttp = 1;
1971 }
1972
1973 bopt->kore_flavor = cli_strdup(string);
1974 free(copy);
1975 }
1976
1977 static void
1978 cli_buildopt_mime(struct buildopt *bopt, const char *ext)
1979 {
1980 struct mime_type *mime;
1981 char *type;
1982
1983 if (bopt == NULL)
1984 bopt = cli_buildopt_default();
1985 else
1986 fatal("mime_add only supported in global context");
1987
1988 if ((type = strchr(ext, ':')) == NULL)
1989 fatal("no type given in %s", ext);
1990
1991 *(type)++ = '\0';
1992 TAILQ_FOREACH(mime, &mime_types, list) {
1993 if (!strcmp(mime->ext, ext))
1994 fatal("duplicate extension %s found", ext);
1995 }
1996
1997 mime = cli_malloc(sizeof(*mime));
1998 mime->ext = cli_strdup(ext);
1999 mime->type = cli_strdup(type);
2000
2001 TAILQ_INSERT_TAIL(&mime_types, mime, list);
2002 }
2003
2004 static void
2005 cli_build_flags_common(struct buildopt *bopt, struct cli_buf *buf)
2006 {
2007 size_t len;
2008 char *data;
2009
2010 cli_buf_appendf(buf, "-fPIC ");
2011
2012 if (bopt != NULL)
2013 cli_buf_appendf(buf, "-Isrc -Isrc/includes ");
2014
2015 if (bopt == NULL || bopt->single_binary == 0)
2016 cli_buf_appendf(buf, "-I%s/include ", prefix);
2017 else
2018 cli_buf_appendf(buf, "-I%s/include ", bopt->kore_source);
2019
2020 if (bopt == NULL || bopt->single_binary == 0) {
2021 cli_kore_load_file("features", bopt, &data, &len);
2022 cli_buf_append(buf, data, len);
2023 cli_buf_appendf(buf, " ");
2024 free(data);
2025 }
2026 }
2027
2028 static void
2029 cli_build_cflags(struct buildopt *bopt)
2030 {
2031 size_t len;
2032 struct buildopt *obopt;
2033 char *string, *buf, *env;
2034
2035 if ((obopt = cli_buildopt_find(flavor)) == NULL)
2036 fatal("no such build flavor: %s", flavor);
2037
2038 if (bopt->cflags == NULL)
2039 bopt->cflags = cli_buf_alloc(128);
2040
2041 cli_build_flags_common(bopt, bopt->cflags);
2042
2043 if (obopt != NULL && obopt->cflags != NULL) {
2044 cli_buf_append(bopt->cflags, obopt->cflags->data,
2045 obopt->cflags->offset);
2046 }
2047
2048 if (bopt->single_binary) {
2049 cli_kore_load_file("features", bopt, &buf, &len);
2050 cli_buf_append(bopt->cflags, buf, len);
2051 cli_buf_appendf(bopt->cflags, " ");
2052 free(buf);
2053 }
2054
2055 if ((env = getenv("CFLAGS")) != NULL)
2056 cli_buf_appendf(bopt->cflags, "%s", env);
2057
2058 string = cli_buf_stringify(bopt->cflags, NULL);
2059 printf("CFLAGS=%s\n", string);
2060 cflags_count = cli_split_string(string, " ", cflags, CFLAGS_MAX);
2061 }
2062
2063 static void
2064 cli_build_cxxflags(struct buildopt *bopt)
2065 {
2066 struct buildopt *obopt;
2067 char *string, *env;
2068
2069 if ((obopt = cli_buildopt_find(flavor)) == NULL)
2070 fatal("no such build flavor: %s", flavor);
2071
2072 if (bopt->cxxflags == NULL)
2073 bopt->cxxflags = cli_buf_alloc(128);
2074
2075 cli_build_flags_common(bopt, bopt->cxxflags);
2076
2077 if (obopt != NULL && obopt->cxxflags != NULL) {
2078 cli_buf_append(bopt->cxxflags, obopt->cxxflags->data,
2079 obopt->cxxflags->offset);
2080 }
2081
2082 if ((env = getenv("CXXFLAGS")) != NULL)
2083 cli_buf_appendf(bopt->cxxflags, "%s", env);
2084
2085 string = cli_buf_stringify(bopt->cxxflags, NULL);
2086 if (cxx_files_count > 0)
2087 printf("CXXFLAGS=%s\n", string);
2088 cxxflags_count = cli_split_string(string, " ", cxxflags, CXXFLAGS_MAX);
2089 }
2090
2091 static void
2092 cli_build_ldflags(struct buildopt *bopt)
2093 {
2094 int fd;
2095 size_t len;
2096 struct buildopt *obopt;
2097 char *string, *buf, *env, *path;
2098
2099 if ((obopt = cli_buildopt_find(flavor)) == NULL)
2100 fatal("no such build flavor: %s", flavor);
2101
2102 if (bopt->ldflags == NULL)
2103 bopt->ldflags = cli_buf_alloc(128);
2104
2105 if (bopt->single_binary == 0) {
2106 #if defined(__MACH__)
2107 cli_buf_appendf(bopt->ldflags,
2108 "-dynamiclib -undefined suppress -flat_namespace ");
2109 #else
2110 cli_buf_appendf(bopt->ldflags, "-shared ");
2111 #endif
2112 } else {
2113 (void)cli_vasprintf(&path, "%s/ldflags", object_dir);
2114 cli_file_open(path, O_RDONLY, &fd);
2115 cli_file_read(fd, &buf, &len);
2116 cli_file_close(fd);
2117 if (len == 0)
2118 fatal("ldflags is empty");
2119 len--;
2120
2121 cli_buf_append(bopt->ldflags, buf, len);
2122 cli_buf_appendf(bopt->ldflags, " ");
2123 free(buf);
2124 }
2125
2126 if (obopt != NULL && obopt->ldflags != NULL) {
2127 cli_buf_append(bopt->ldflags, obopt->ldflags->data,
2128 obopt->ldflags->offset);
2129 }
2130
2131 if ((env = getenv("LDFLAGS")) != NULL)
2132 cli_buf_appendf(bopt->ldflags, "%s", env);
2133
2134 string = cli_buf_stringify(bopt->ldflags, NULL);
2135 printf("LDFLAGS=%s\n", string);
2136 ldflags_count = cli_split_string(string, " ", ldflags, LD_FLAGS_MAX);
2137 }
2138
2139 static void
2140 cli_flavor_load(void)
2141 {
2142 FILE *fp;
2143 char buf[BUFSIZ], pwd[MAXPATHLEN], *p, *conf, *env;
2144
2145 if ((env = getenv("KORE_BUILD_FLAVOR")) != NULL) {
2146 flavor = cli_strdup(env);
2147 return;
2148 }
2149
2150 if (getcwd(pwd, sizeof(pwd)) == NULL)
2151 fatal("could not get cwd: %s", errno_s);
2152
2153 appl = basename(pwd);
2154 if (appl == NULL)
2155 fatal("basename: %s", errno_s);
2156 appl = cli_strdup(appl);
2157 (void)cli_vasprintf(&conf, "conf/%s.conf", appl);
2158
2159 if (!cli_dir_exists("conf") || !cli_file_exists(conf))
2160 fatal("%s doesn't appear to be a kore app", appl);
2161 free(conf);
2162
2163 if ((fp = fopen(".flavor", "r")) == NULL) {
2164 flavor = cli_strdup("dev");
2165 return;
2166 }
2167
2168 if (fgets(buf, sizeof(buf), fp) == NULL)
2169 fatal("failed to read flavor from file");
2170
2171 if ((p = strchr(buf, '\n')) != NULL)
2172 *p = '\0';
2173
2174 flavor = cli_strdup(buf);
2175 (void)fclose(fp);
2176 }
2177
2178 static void
2179 cli_kore_load_file(const char *name, struct buildopt *bopt,
2180 char **out, size_t *outlen)
2181 {
2182 int fd;
2183 size_t len;
2184 char *path, *data;
2185
2186 if (bopt != NULL && bopt->single_binary) {
2187 (void)cli_vasprintf(&path, "%s/%s", object_dir, name);
2188 } else {
2189 (void)cli_vasprintf(&path, "%s/share/kore/%s", prefix, name);
2190 }
2191
2192 cli_file_open(path, O_RDONLY, &fd);
2193 cli_file_read(fd, &data, &len);
2194 cli_file_close(fd);
2195 free(path);
2196
2197 if (len == 0)
2198 fatal("%s is empty", name);
2199
2200 len--;
2201
2202 *out = data;
2203 *outlen = len;
2204 }
2205
2206 static void
2207 cli_flavor_change(const char *name)
2208 {
2209 FILE *fp;
2210 int ret;
2211 struct buildopt *bopt;
2212
2213 if ((bopt = cli_buildopt_find(name)) == NULL)
2214 fatal("no such flavor: %s", name);
2215
2216 if ((fp = fopen(".flavor.tmp", "w")) == NULL)
2217 fatal("failed to open temporary file to save flavor");
2218
2219 ret = fprintf(fp, "%s\n", name);
2220 if (ret == -1 || (size_t)ret != (strlen(name) + 1))
2221 fatal("failed to write new build flavor");
2222
2223 (void)fclose(fp);
2224
2225 if (rename(".flavor.tmp", ".flavor") == -1)
2226 fatal("failed to replace build flavor");
2227
2228 cli_clean(0, NULL);
2229 }
2230
2231 static void
2232 cli_spawn_proc(void (*cb)(void *), void *arg)
2233 {
2234 pid_t pid;
2235 int status;
2236
2237 pid = fork();
2238 switch (pid) {
2239 case -1:
2240 fatal("cli_compile_cfile: fork() %s", errno_s);
2241 /* NOTREACHED */
2242 case 0:
2243 cb(arg);
2244 fatal("cli_spawn_proc: %s", errno_s);
2245 /* NOTREACHED */
2246 default:
2247 break;
2248 }
2249
2250 if (waitpid(pid, &status, 0) == -1)
2251 fatal("couldn't wait for child %d", pid);
2252
2253 if (WEXITSTATUS(status) || WTERMSIG(status) || WCOREDUMP(status))
2254 fatal("subprocess trouble, check output");
2255 }
2256
2257 static int
2258 cli_vasprintf(char **out, const char *fmt, ...)
2259 {
2260 int l;
2261 va_list args;
2262
2263 va_start(args, fmt);
2264 l = vasprintf(out, fmt, args);
2265 va_end(args);
2266
2267 if (l == -1)
2268 fatal("cli_vasprintf");
2269
2270 return (l);
2271 }
2272
2273 static void
2274 cli_cleanup_files(const char *spath)
2275 {
2276 cli_find_files(spath, cli_file_remove);
2277
2278 if (rmdir(spath) == -1 && errno != ENOENT)
2279 printf("couldn't rmdir %s\n", spath);
2280 }
2281
2282 static void *
2283 cli_malloc(size_t len)
2284 {
2285 void *ptr;
2286
2287 if ((ptr = calloc(1, len)) == NULL)
2288 fatal("calloc: %s", errno_s);
2289
2290 return (ptr);
2291 }
2292
2293 static void *
2294 cli_realloc(void *ptr, size_t len)
2295 {
2296 void *nptr;
2297
2298 if ((nptr = realloc(ptr, len)) == NULL)
2299 fatal("realloc: %s", errno_s);
2300
2301 return (nptr);
2302 }
2303
2304 static char *
2305 cli_strdup(const char *string)
2306 {
2307 char *copy;
2308
2309 if ((copy = strdup(string)) == NULL)
2310 fatal("strdup: %s", errno_s);
2311
2312 return (copy);
2313 }
2314
2315 struct cli_buf *
2316 cli_buf_alloc(size_t initial)
2317 {
2318 struct cli_buf *buf;
2319
2320 buf = cli_malloc(sizeof(*buf));
2321
2322 if (initial > 0)
2323 buf->data = cli_malloc(initial);
2324 else
2325 buf->data = NULL;
2326
2327 buf->length = initial;
2328 buf->offset = 0;
2329
2330 return (buf);
2331 }
2332
2333 void
2334 cli_buf_free(struct cli_buf *buf)
2335 {
2336 free(buf->data);
2337 buf->data = NULL;
2338 buf->offset = 0;
2339 buf->length = 0;
2340 free(buf);
2341 }
2342
2343 void
2344 cli_buf_append(struct cli_buf *buf, const void *d, size_t len)
2345 {
2346 if ((buf->offset + len) < len)
2347 fatal("overflow in cli_buf_append");
2348
2349 if ((buf->offset + len) > buf->length) {
2350 buf->length += len;
2351 buf->data = cli_realloc(buf->data, buf->length);
2352 }
2353
2354 memcpy((buf->data + buf->offset), d, len);
2355 buf->offset += len;
2356 }
2357
2358 void
2359 cli_buf_appendv(struct cli_buf *buf, const char *fmt, va_list args)
2360 {
2361 int l;
2362 va_list copy;
2363 char *b, sb[BUFSIZ];
2364
2365 va_copy(copy, args);
2366
2367 l = vsnprintf(sb, sizeof(sb), fmt, args);
2368 if (l == -1)
2369 fatal("cli_buf_appendv(): vsnprintf error");
2370
2371 if ((size_t)l >= sizeof(sb)) {
2372 l = vasprintf(&b, fmt, copy);
2373 if (l == -1)
2374 fatal("cli_buf_appendv(): error or truncation");
2375 } else {
2376 b = sb;
2377 }
2378
2379 cli_buf_append(buf, b, l);
2380 if (b != sb)
2381 free(b);
2382
2383 va_end(copy);
2384 }
2385
2386 void
2387 cli_buf_appendf(struct cli_buf *buf, const char *fmt, ...)
2388 {
2389 va_list args;
2390
2391 va_start(args, fmt);
2392 cli_buf_appendv(buf, fmt, args);
2393 va_end(args);
2394 }
2395
2396 char *
2397 cli_buf_stringify(struct cli_buf *buf, size_t *len)
2398 {
2399 char c;
2400
2401 if (len != NULL)
2402 *len = buf->offset;
2403
2404 c = '\0';
2405 cli_buf_append(buf, &c, sizeof(c));
2406
2407 return ((char *)buf->data);
2408 }
2409
2410 static int
2411 cli_split_string(char *input, const char *delim, char **out, size_t ele)
2412 {
2413 int count;
2414 char **ap;
2415
2416 if (ele == 0)
2417 return (0);
2418
2419 count = 0;
2420 for (ap = out; ap < &out[ele - 1] &&
2421 (*ap = strsep(&input, delim)) != NULL;) {
2422 if (**ap != '\0') {
2423 ap++;
2424 count++;
2425 }
2426 }
2427
2428 *ap = NULL;
2429 return (count);
2430 }
2431
2432 static char *
2433 cli_read_line(FILE *fp, char *in, size_t len)
2434 {
2435 char *p, *t;
2436
2437 if (fgets(in, len, fp) == NULL)
2438 return (NULL);
2439
2440 p = in;
2441 in[strcspn(in, "\n")] = '\0';
2442
2443 while (isspace(*(unsigned char *)p))
2444 p++;
2445
2446 if (p[0] == '#' || p[0] == '\0') {
2447 p[0] = '\0';
2448 return (p);
2449 }
2450
2451 for (t = p; *t != '\0'; t++) {
2452 if (*t == '\t')
2453 *t = ' ';
2454 }
2455
2456 return (p);
2457 }
2458
2459 static char *
2460 cli_text_trim(char *string, size_t len)
2461 {
2462 char *end;
2463
2464 if (len == 0)
2465 return (string);
2466
2467 end = (string + len) - 1;
2468 while (isspace(*(unsigned char *)string) && string < end)
2469 string++;
2470
2471 while (isspace(*(unsigned char *)end) && end > string)
2472 *(end)-- = '\0';
2473
2474 return (string);
2475 }
2476
2477 static long long
2478 cli_strtonum(const char *str, long long min, long long max)
2479 {
2480 long long l;
2481 char *ep;
2482
2483 if (min > max)
2484 fatal("cli_strtonum: min > max");
2485
2486 errno = 0;
2487 l = strtoll(str, &ep, 10);
2488 if (errno != 0 || str == ep || *ep != '\0')
2489 fatal("strtoll(): %s", errno_s);
2490
2491 if (l < min)
2492 fatal("cli_strtonum: value < min");
2493
2494 if (l > max)
2495 fatal("cli_strtonum: value > max");
2496
2497 return (l);
2498 }
2499
2500 static void
2501 fatal(const char *fmt, ...)
2502 {
2503 va_list args;
2504 char buf[2048];
2505
2506 va_start(args, fmt);
2507 (void)vsnprintf(buf, sizeof(buf), fmt, args);
2508 va_end(args);
2509
2510 if (command != NULL)
2511 printf("kore %s: %s\n", command->name, buf);
2512 else
2513 printf("kore: %s\n", buf);
2514
2515 exit(1);
2516 }