1 #include "jbr.h"
2 #include "ejdb2cfg.h"
3
4 #include <iowow/iwpool.h>
5 #include <iowow/iwconv.h>
6 #include <iwnet/iwn_utils.h>
7
8 #include <stdio.h>
9 #include <errno.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include <getopt.h>
13 #include <signal.h>
14
15 struct env {
16 const char *program;
17 EJDB db;
18 EJDB_OPTS opts;
19 IWPOOL *pool;
20 } env;
21
_usage(const char * err)22 static int _usage(const char *err) {
23 if (err) {
24 fprintf(stderr, "\n%s\n", err);
25 }
26 fprintf(stderr, "\n\tEJDB " EJDB2_VERSION " HTTP REST/Websocket server. http://ejdb.org\n");
27 fprintf(stderr, "\nUsage:\n\n\t %s [options]\n\n", env.program);
28 fprintf(stderr, "\t-v, --version\t\tPrint program version.\n");
29 fprintf(stderr, "\t-f, --file=<>\t\tDatabase file path. Default: ejdb2.db\n");
30 fprintf(stderr, "\t-p, --port=NUM\t\tHTTP server port numer. Default: 9191\n");
31 fprintf(stderr, "\t-l, --listen=<>\t\tNetwork address server will listen. Default: localhost\n");
32 fprintf(stderr, "\t-k, --key=<>\t\tPEM private key file for TLS 1.2 HTTP server.\n");
33 fprintf(stderr, "\t-c, --certs=<>\t\tPEM certificates file for TLS 1.2 HTTP server.\n");
34 fprintf(stderr, "\t-a, --access=TOKEN|@FILE\t\tAccess token to match 'X-Access-Token' HTTP header value.\n");
35 fprintf(stderr, "\t-r, --access-read\t\tAllows unrestricted read-only data access.\n");
36 fprintf(stderr, "\t-C, --cors\t\tEnable COSR response headers for HTTP server\n");
37 fprintf(stderr, "\t-t, --trunc\t\tCleanup/reset database file on open.\n");
38 fprintf(stderr, "\t-w, --wal\t\tuse the write ahead log (WAL). Used to provide data durability.\n");
39 fprintf(stderr, "\nAdvanced options:\n");
40 fprintf(stderr,
41 "\t-S, --sbz=NUM\t\tMax sorting buffer size. If exceeded, an overflow temp file for data will be created."
42 "Default: 16777216, min: 1048576\n");
43 fprintf(stderr, "\t-D, --dsz=NUM\t\tInitial size of buffer to process/store document on queries."
44 " Preferable average size of document. Default: 65536, min: 16384\n");
45 fprintf(stderr, "\t-T, --trylock Exit with error if database is locked by another process."
46 " If not set, current process will wait for lock release.");
47 fprintf(stderr, "\n\n");
48 return 1;
49 }
50
_on_signal(int signo)51 static void _on_signal(int signo) {
52 if (env.db) {
53 jbr_shutdown_request(env.db);
54 }
55 }
56
_version(void)57 static void _version(void) {
58 fprintf(stdout, EJDB2_VERSION);
59 }
60
main(int argc,char * argv[])61 int main(int argc, char *argv[]) {
62 signal(SIGPIPE, SIG_IGN);
63 signal(SIGHUP, SIG_IGN);
64 signal(SIGALRM, SIG_IGN);
65 signal(SIGUSR1, SIG_IGN);
66 signal(SIGUSR2, SIG_IGN);
67 if (signal(SIGTERM, _on_signal) == SIG_ERR) {
68 return EXIT_FAILURE;
69 }
70 if (signal(SIGINT, _on_signal) == SIG_ERR) {
71 return EXIT_FAILURE;
72 }
73
74 IWPOOL *pool;
75 int ec = 0, ch;
76
77 env.program = argc ? argv[0] : "";
78 env.opts.http.enabled = true;
79 env.opts.http.blocking = true;
80 env.opts.no_wal = true;
81
82 iwrc rc = ejdb_init();
83 if (rc) {
84 iwlog_ecode_error3(rc);
85 return EXIT_FAILURE;
86 }
87
88 RCA(pool = env.pool = iwpool_create_empty(), finish);
89
90 static const struct option long_options[] = {
91 { "help", 0, 0, 'h' },
92 { "version", 0, 0, 'v' },
93 { "file", 1, 0, 'f' },
94 { "port", 1, 0, 'p' },
95 { "bind", 1, 0, 'b' }, // for backward compatibility
96 { "listen", 1, 0, 'l' },
97 { "key", 1, 0, 'k' },
98 { "certs", 1, 0, 'c' },
99 { "access", 1, 0, 'a' },
100 { "access-read", 0, 0, 'r' },
101 { "cors", 0, 0, 'C' },
102 { "trunc", 0, 0, 't' },
103 { "wal", 0, 0, 'w' },
104 { "sbz", 1, 0, 'S' },
105 { "dsz", 1, 0, 'D' },
106 { "trylock", 0, 0, 'T' }
107 };
108
109 while ((ch = getopt_long(argc, argv, "f:p:b:l:k:c:a:S:D:rCtwThv", long_options, 0)) != -1) {
110 switch (ch) {
111 case 'h':
112 ec = _usage(0);
113 goto finish;
114 case 'v':
115 _version();
116 goto finish;
117 case 'f':
118 env.opts.kv.path = iwpool_strdup2(pool, optarg);
119 break;
120 case 'p':
121 env.opts.http.port = iwatoi(optarg);
122 break;
123 case 'b':
124 case 'l':
125 env.opts.http.bind = iwpool_strdup2(pool, optarg);
126 break;
127 case 'k':
128 env.opts.http.ssl_private_key = iwpool_strdup2(pool, optarg);
129 break;
130 case 'c':
131 env.opts.http.ssl_certs = iwpool_strdup2(pool, optarg);
132 break;
133 case 'a':
134 env.opts.http.access_token = iwpool_strdup2(pool, optarg);
135 env.opts.http.access_token_len = env.opts.http.access_token ? strlen(env.opts.http.access_token) : 0;
136 break;
137 case 'C':
138 env.opts.http.cors = true;
139 break;
140 case 't':
141 env.opts.kv.oflags |= IWKV_TRUNC;
142 break;
143 case 'w':
144 env.opts.no_wal = false;
145 break;
146 case 'S':
147 env.opts.sort_buffer_sz = iwatoi(optarg);
148 break;
149 case 'D':
150 env.opts.document_buffer_sz = iwatoi(optarg);
151 break;
152 case 'T':
153 env.opts.kv.file_lock_fail_fast = true;
154 break;
155 case 'r':
156 env.opts.http.read_anon = true;
157 break;
158 default:
159 ec = _usage(0);
160 goto finish;
161 }
162 }
163
164 if (!env.opts.kv.path) {
165 env.opts.kv.path = "ejdb2.db";
166 }
167 if (env.opts.http.port < 1) {
168 env.opts.http.port = 9191;
169 }
170 if (env.opts.sort_buffer_sz < 1) {
171 env.opts.sort_buffer_sz = 16777216;
172 } else if (env.opts.sort_buffer_sz < 1048576) {
173 env.opts.sort_buffer_sz = 1048576;
174 }
175 if (env.opts.document_buffer_sz < 1) {
176 env.opts.document_buffer_sz = 65536;
177 } else if (env.opts.document_buffer_sz < 16384) {
178 env.opts.document_buffer_sz = 16384;
179 }
180
181 RCC(rc, finish, ejdb_open(&env.opts, &env.db));
182 RCC(rc, finish, ejdb_close(&env.db));
183
184 finish:
185 if (rc) {
186 iwlog_ecode_error3(rc);
187 }
188 iwpool_destroy(env.pool);
189 fflush(0);
190 return rc == 0 ? ec : 1;
191 }
192