kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

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 }