kore

Kore is a web application platform for writing scalable, concurrent web based processes in C or Python.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

cli.c (59494B)



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