#include "jbr.h"
#include "ejdb2cfg.h"
#include <iowow/iwpool.h>
#include <iowow/iwconv.h>
#include <iwnet/iwn_utils.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
struct env {
const char *program;
EJDB db;
EJDB_OPTS opts;
IWPOOL *pool;
} env;
static int _usage(const char *err) {
if (err) {
fprintf(stderr, "\n%s\n", err);
}
fprintf(stderr, "\n\tEJDB " EJDB2_VERSION " HTTP REST/Websocket server. http://ejdb.org\n");
fprintf(stderr, "\nUsage:\n\n\t %s [options]\n\n", env.program);
fprintf(stderr, "\t-v, --version\t\tPrint program version.\n");
fprintf(stderr, "\t-f, --file=<>\t\tDatabase file path. Default: ejdb2.db\n");
fprintf(stderr, "\t-p, --port=NUM\t\tHTTP server port numer. Default: 9191\n");
fprintf(stderr, "\t-l, --listen=<>\t\tNetwork address server will listen. Default: localhost\n");
fprintf(stderr, "\t-k, --key=<>\t\tPEM private key file for TLS 1.2 HTTP server.\n");
fprintf(stderr, "\t-c, --certs=<>\t\tPEM certificates file for TLS 1.2 HTTP server.\n");
fprintf(stderr, "\t-a, --access=TOKEN|@FILE\t\tAccess token to match 'X-Access-Token' HTTP header value.\n");
fprintf(stderr, "\t-r, --access-read\t\tAllows unrestricted read-only data access.\n");
fprintf(stderr, "\t-C, --cors\t\tEnable COSR response headers for HTTP server\n");
fprintf(stderr, "\t-t, --trunc\t\tCleanup/reset database file on open.\n");
fprintf(stderr, "\t-w, --wal\t\tuse the write ahead log (WAL). Used to provide data durability.\n");
fprintf(stderr, "\nAdvanced options:\n");
fprintf(stderr,
"\t-S, --sbz=NUM\t\tMax sorting buffer size. If exceeded, an overflow temp file for data will be created."
"Default: 16777216, min: 1048576\n");
fprintf(stderr, "\t-D, --dsz=NUM\t\tInitial size of buffer to process/store document on queries."
" Preferable average size of document. Default: 65536, min: 16384\n");
fprintf(stderr, "\t-T, --trylock Exit with error if database is locked by another process."
" If not set, current process will wait for lock release.");
fprintf(stderr, "\n\n");
return 1;
}
static void _on_signal(int signo) {
if (env.db) {
jbr_shutdown_request(env.db);
}
}
static void _version(void) {
fprintf(stdout, EJDB2_VERSION);
}
int main(int argc, char *argv[]) {
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGALRM, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
if (signal(SIGTERM, _on_signal) == SIG_ERR) {
return EXIT_FAILURE;
}
if (signal(SIGINT, _on_signal) == SIG_ERR) {
return EXIT_FAILURE;
}
IWPOOL *pool;
int ec = 0, ch;
env.program = argc ? argv[0] : "";
env.opts.http.enabled = true;
env.opts.http.blocking = true;
env.opts.no_wal = true;
iwrc rc = ejdb_init();
if (rc) {
iwlog_ecode_error3(rc);
return EXIT_FAILURE;
}
RCA(pool = env.pool = iwpool_create_empty(), finish);
static const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "file", 1, 0, 'f' },
{ "port", 1, 0, 'p' },
{ "bind", 1, 0, 'b' }, { "listen", 1, 0, 'l' },
{ "key", 1, 0, 'k' },
{ "certs", 1, 0, 'c' },
{ "access", 1, 0, 'a' },
{ "access-read", 0, 0, 'r' },
{ "cors", 0, 0, 'C' },
{ "trunc", 0, 0, 't' },
{ "wal", 0, 0, 'w' },
{ "sbz", 1, 0, 'S' },
{ "dsz", 1, 0, 'D' },
{ "trylock", 0, 0, 'T' }
};
while ((ch = getopt_long(argc, argv, "f:p:b:l:k:c:a:S:D:rCtwThv", long_options, 0)) != -1) {
switch (ch) {
case 'h':
ec = _usage(0);
goto finish;
case 'v':
_version();
goto finish;
case 'f':
env.opts.kv.path = iwpool_strdup2(pool, optarg);
break;
case 'p':
env.opts.http.port = iwatoi(optarg);
break;
case 'b':
case 'l':
env.opts.http.bind = iwpool_strdup2(pool, optarg);
break;
case 'k':
env.opts.http.ssl_private_key = iwpool_strdup2(pool, optarg);
break;
case 'c':
env.opts.http.ssl_certs = iwpool_strdup2(pool, optarg);
break;
case 'a':
env.opts.http.access_token = iwpool_strdup2(pool, optarg);
env.opts.http.access_token_len = env.opts.http.access_token ? strlen(env.opts.http.access_token) : 0;
break;
case 'C':
env.opts.http.cors = true;
break;
case 't':
env.opts.kv.oflags |= IWKV_TRUNC;
break;
case 'w':
env.opts.no_wal = false;
break;
case 'S':
env.opts.sort_buffer_sz = iwatoi(optarg);
break;
case 'D':
env.opts.document_buffer_sz = iwatoi(optarg);
break;
case 'T':
env.opts.kv.file_lock_fail_fast = true;
break;
case 'r':
env.opts.http.read_anon = true;
break;
default:
ec = _usage(0);
goto finish;
}
}
if (!env.opts.kv.path) {
env.opts.kv.path = "ejdb2.db";
}
if (env.opts.http.port < 1) {
env.opts.http.port = 9191;
}
if (env.opts.sort_buffer_sz < 1) {
env.opts.sort_buffer_sz = 16777216;
} else if (env.opts.sort_buffer_sz < 1048576) {
env.opts.sort_buffer_sz = 1048576;
}
if (env.opts.document_buffer_sz < 1) {
env.opts.document_buffer_sz = 65536;
} else if (env.opts.document_buffer_sz < 16384) {
env.opts.document_buffer_sz = 16384;
}
RCC(rc, finish, ejdb_open(&env.opts, &env.db));
RCC(rc, finish, ejdb_close(&env.db));
finish:
if (rc) {
iwlog_ecode_error3(rc);
}
iwpool_destroy(env.pool);
fflush(0);
return rc == 0 ? ec : 1;
}