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 (56310B)



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