1 /*
2  * Copyright 2014-2017, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: http://arcan-fe.com
5  */
6 
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <sqlite3.h>
12 #include <assert.h>
13 #include <string.h>
14 #include <inttypes.h>
15 
16 #include <sys/types.h>
17 #include <unistd.h>
18 
19 #include "arcan_math.h"
20 #include "arcan_general.h"
21 #include "arcan_db.h"
22 
23 #ifdef ARCAN_DB_STANDALONE
24 static const char* ARCAN_TBL = "arcan";
25 #else
26 static const char* ARCAN_TBL =
27 #ifdef ARCAN_LWA
28 "arcan_lwa"
29 #else
30  "arcan"
31 #endif
32 ;
33 #endif
34 
35 static bool db_init = false;
36 
37 #define DB_VERSION_NUM "4"
38 
39 #define DDL_TARGET "CREATE TABLE target ("\
40 	"tgtid INTEGER PRIMARY KEY,"\
41 	"tag STRING NOT NULL,"\
42 	"name STRING UNIQUE NOT NULL,"\
43 	"executable TEXT NOT NULL,"\
44 	"user_id STRING DEFAULT NULL,"\
45 	"user_group STRING DEFAULT NULL,"\
46 	"bfmt INTEGER NOT NULL"\
47 	")"
48 
49 #define DDL_TGT_ARGV "CREATE TABLE target_argv ("\
50 	"argnum INTEGER PRIMARY KEY,"\
51 	"arg STRING NOT NULL,"\
52 	"target INTEGER NOT NULL,"\
53 	"FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
54 
55 #define DDL_CONFIG "CREATE TABLE config ("\
56 	"cfgid INTEGER PRIMARY KEY,"\
57 	"name STRING NOT NULL,"\
58 	"passed_counter INTEGER,"\
59 	"failed_counter INTEGER,"\
60 	"target INTEGER NOT NULL,"\
61 	"UNIQUE (name, target),"\
62 	"FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
63 
64 #define DDL_CFG_ARGV "CREATE TABLE config_argv ("\
65 	"argnum INTEGER PRIMARY KEY,"\
66 	"arg STRING NOT NULL,"\
67 	"config INTEGER NOT NULL,"\
68 	"FOREIGN KEY (config) REFERENCES config(cfgid) ON DELETE CASCADE )"
69 
70 #define DDL_TGT_KV "CREATE TABLE target_kv ("\
71 	"key STRING NOT NULL,"\
72 	"val STRING NOT NULL,"\
73 	"target INT NOT NULL,"\
74 	"UNIQUE (key, target), "\
75 	"FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
76 
77 #define DDL_CFG_KV "CREATE TABLE config_kv ("\
78 	"key STRING NOT NULL,"\
79 	"val STRING NOT NULL,"\
80 	"config INT NOT NULL,"\
81 	"UNIQUE (key, config), "\
82 	"FOREIGN KEY (config) REFERENCES config(cfgid) ON DELETE CASCADE )"
83 
84 #define DDL_TGT_ENV "CREATE TABLE target_env ("\
85 	"key STRING NOT NULL,"\
86 	"val STRING NOT NULL,"\
87 	"target INT NOT NULL,"\
88 	"UNIQUE (key, target), "\
89 	"FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
90 
91 #define DDL_CFG_ENV "CREATE TABLE config_env ("\
92 	"key STRING NOT NULL,"\
93 	"val STRING NOT NULL,"\
94 	"config INT NOT NULL,"\
95 	"UNIQUE (key, config),"\
96 	"FOREIGN KEY (config) REFERENCES target(tgtid) ON DELETE CASCADE )"
97 
98 #define DDL_TGT_LIBS "CREATE TABLE target_libs ("\
99 	"libnum INTEGER PRIMARY KEY,"\
100 	"libname STRING NOT NULL,"\
101 	"libnote STRING NOT NULL,"\
102 	"target INT NOT NULL,"\
103 	"UNIQUE (libname, target),"\
104 	"FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
105 
106 static const char* ddls[] = {
107 	DDL_TARGET,   DDL_CONFIG,
108 	DDL_TGT_ARGV, DDL_CFG_ARGV,
109 	DDL_TGT_KV,   DDL_CFG_KV,
110 	DDL_TGT_ENV,  DDL_CFG_ENV,
111 	DDL_TGT_LIBS
112 };
113 
114 #define DI_INSARG_TARGET "INSERT INTO target_argv(target, arg) VALUES(?, ?);"
115 #define DI_INSARG_CONFIG "INSERT INTO config_argv(config, arg) VALUES(?, ?);"
116 
117 #define DI_DROPKV_TARGET "DELETE FROM target_kv WHERE val=\"\";"
118 
119 #define DI_INSKV_TARGET "INSERT OR REPLACE INTO "\
120 	"target_kv(key, val, target) VALUES(?, ?, ?);"
121 
122 #define DI_INSKV_CONFIG "INSERT OR REPLACE INTO "\
123 	"config_kv(key, val, config) VALUES(?, ?, ?);"
124 
125 #define DI_DROPKV_CONFIG "DELETE FROM config_kv WHERE val=\"\";"
126 
127 #define DI_INSKV_CONFIG_ENV "INSERT OR REPLACE INTO "\
128 	"config_env(key, val, config) VALUES(?, ?, ?);"
129 
130 #define DI_INSKV_TARGET_ENV "INSERT OR REPLACE INTO "\
131 	"target_env(key, val, target) VALUES(?, ?, ?);"
132 
133 #define DI_INSKV_TARGET_LIBV "INSERT OR REPLACE INTO "\
134 	"target_libs(libname, libnote, target) VALUES(?, ?, ?);"
135 
136 struct arcan_dbh {
137 	sqlite3* dbh;
138 
139 /* cached appl name used for the DBHandle, although
140  * some special functions may use a different one, none outside _db.c should */
141 	char* applname;
142 	char* akv_update;
143 	char* akv_get;
144 	char* akv_clean;
145 
146 	size_t akv_upd_sz;
147 	size_t akv_get_sz;
148 	size_t akv_clean_sz;
149 
150 	enum DB_KVTARGET ttype;
151 	union arcan_dbtrans_id trid;
152 	bool trclean;
153 	sqlite3_stmt* transaction;
154 };
155 
156 static void setup_ddl(struct arcan_dbh* dbh);
157 
158 static struct arcan_dbh* shared_handle;
arcan_db_get_shared(const char ** dappl)159 struct arcan_dbh* arcan_db_get_shared(const char** dappl)
160 {
161 	if (dappl)
162 		*dappl = ARCAN_TBL;
163 	return shared_handle;
164 }
165 
arcan_db_set_shared(struct arcan_dbh * new)166 void arcan_db_set_shared(struct arcan_dbh* new)
167 {
168 	shared_handle = new;
169 }
170 
171 /*
172  * any query that just returns a list of strings,
173  * pack into a dbres (or append to an existing one)
174  */
db_string_query(struct arcan_dbh * dbh,sqlite3_stmt * stmt,struct arcan_strarr * opt,off_t ofs)175 static struct arcan_strarr db_string_query(struct arcan_dbh* dbh,
176 	sqlite3_stmt* stmt, struct arcan_strarr* opt, off_t ofs)
177 {
178 	struct arcan_strarr res = {.data = NULL};
179 
180 	if (!opt) {
181 		res.data = arcan_alloc_mem(sizeof(char**) * 8,
182 			ARCAN_MEM_STRINGBUF, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
183 		res.limit = 8;
184 		res.count = ofs;
185 	}
186 	else
187 		res = *opt;
188 
189 /* we stop one step short of full capacity before
190  * resizing to have both a valid count and a NULL terminated array */
191 	while (sqlite3_step(stmt) == SQLITE_ROW){
192 		if (res.count+1 >= res.limit)
193 			arcan_mem_growarr(&res);
194 
195 		const char* arg = (const char*) sqlite3_column_text(stmt, 0);
196 		res.data[res.count++] = (arg ? strdup(arg) : NULL);
197 	}
198 
199 	sqlite3_finalize(stmt);
200 	return res;
201 }
202 
203 /*
204  * any query where we're just interested in an integer value
205  */
db_num_query(struct arcan_dbh * dbh,const char * qry,bool * status)206 static int db_num_query(struct arcan_dbh* dbh, const char* qry, bool* status)
207 {
208 	sqlite3_stmt* stmt = NULL;
209 	if (status) *status = false;
210 	int count = -1;
211 
212 	int code = sqlite3_prepare_v2(dbh->dbh, qry, strlen(qry), &stmt, NULL);
213 	if (SQLITE_OK == code){
214 		while (sqlite3_step(stmt) == SQLITE_ROW){
215 			count = sqlite3_column_int(stmt, 0);
216 			if (status) *status = true;
217 		}
218 
219 		sqlite3_finalize(stmt);
220 	}
221 	else
222 		count = -1;
223 
224 	return count;
225 }
226 
227 /*
228  * query that is just silently assumed to work
229  */
db_void_query(struct arcan_dbh * dbh,const char * qry,bool suppr_err)230 static inline void db_void_query(struct arcan_dbh* dbh,
231 	const char* qry, bool suppr_err)
232 {
233 	sqlite3_stmt* stmt = NULL;
234 	if (!qry || !dbh)
235 		return;
236 
237 	int rc = sqlite3_prepare_v2(dbh->dbh, qry, strlen(qry), &stmt, NULL);
238 	if (rc == SQLITE_OK){
239 		rc = sqlite3_step(stmt);
240 	}
241 
242 	if (rc != SQLITE_OK && rc != SQLITE_DONE && !suppr_err){
243 		arcan_warning("db_void_query(failed) on %s -- reason: %d(%s)\n",
244 			qry, rc, sqlite3_errmsg(dbh->dbh));
245 	}
246 
247 	sqlite3_finalize(stmt);
248 }
249 
arcan_db_dropappl(struct arcan_dbh * dbh,const char * appl)250 void arcan_db_dropappl(struct arcan_dbh* dbh, const char* appl)
251 {
252 	if (!appl || !dbh)
253 		return;
254 
255 	size_t len = strlen(appl);
256 	if (0 == len)
257 		return;
258 
259 	const char dropqry[] = "DELETE FROM appl_";
260 	char dropbuf[sizeof(dropqry) + len + 1];
261 	snprintf(dropbuf, sizeof(dropbuf), "%s%s;", dropqry, appl);
262 
263 	db_void_query(dbh, dropbuf, true);
264 
265 /* special case, reset version fields etc. */
266 	if (strcmp(appl, ARCAN_TBL) == 0){
267 		arcan_db_appl_kv(dbh, ARCAN_TBL, "dbversion", DB_VERSION_NUM);
268 	}
269 }
270 
sqliteexit()271 static void sqliteexit()
272 {
273 	sqlite3_shutdown();
274 }
275 
276 /*
277  * will be called every time the database is opened,
278  * so we expect this to fail silently when the group already exists
279  */
create_appl_group(struct arcan_dbh * dbh,const char * applname)280 static void create_appl_group(struct arcan_dbh* dbh, const char* applname)
281 {
282 	const char ddl[] = "CREATE TABLE appl_%s "
283 		"(key TEXT UNIQUE, val TEXT NOT NULL);";
284 	const char kv_ddl[] = "INSERT OR REPLACE INTO "
285 		" appl_%s(key, val) VALUES(?, ?);";
286 	const char kv_get[] = "SELECT val FROM appl_%s WHERE key = ?;";
287 	const char kv_drop[] = "DELETE FROM appl_%s WHERE val = \"\";";
288 
289 	if (dbh->akv_update)
290 		arcan_mem_free(dbh->akv_update);
291 
292 	size_t len = applname ? strlen(applname) : 0;
293 	if (0 == len){
294 		arcan_warning("create_appl_group(), missing or broken applname\n");
295 		return;
296 	}
297 
298 /* cache key insert into app specific table */
299 	assert(sizeof(kv_get) < sizeof(kv_ddl));
300 	size_t ddl_sz = len + sizeof(kv_ddl);
301 	dbh->akv_update = arcan_alloc_mem(
302 		ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
303 	dbh->akv_upd_sz = snprintf(dbh->akv_update, ddl_sz, kv_ddl, applname);
304 
305 /* cache drop empty values from table */
306 	ddl_sz = len + sizeof(kv_drop);
307 	dbh->akv_clean = arcan_alloc_mem(
308 		ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
309 	dbh->akv_clean_sz = snprintf(dbh->akv_clean, ddl_sz, kv_drop, applname);
310 
311 /* cache key retrieve into app specific table */
312 	ddl_sz = strlen(applname) + sizeof(kv_get);
313 	dbh->akv_get = arcan_alloc_mem(
314 		ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
315 	dbh->akv_get_sz = snprintf(dbh->akv_get, ddl_sz, kv_get, applname);
316 
317 /* create the actual table */
318 	char wbuf[ sizeof(ddl) + strlen(applname) ];
319 	snprintf(wbuf, sizeof(wbuf)/sizeof(wbuf[0]), ddl, applname);
320 
321 	db_void_query(dbh, wbuf, true);
322 }
323 
324 #ifdef ARCAN_DB_STANDALONE
arcan_db_droptarget(struct arcan_dbh * dbh,arcan_targetid id)325 bool arcan_db_droptarget(struct arcan_dbh* dbh, arcan_targetid id)
326 {
327 /* should suffice from ON DELETE CASCADE relationship */
328 	static const char qry[]  = "DELETE FROM target WHERE tgtid = ?;";
329 
330 	sqlite3_stmt* stmt;
331 	sqlite3_prepare_v2(dbh->dbh, qry, sizeof(qry)-1, &stmt, NULL);
332 	sqlite3_bind_int(stmt, 1, id);
333 	sqlite3_step(stmt);
334 	sqlite3_finalize(stmt);
335 
336 	return true;
337 }
338 
arcan_db_dropconfig(struct arcan_dbh * dbh,arcan_configid id)339 bool arcan_db_dropconfig(struct arcan_dbh* dbh, arcan_configid id)
340 {
341 	static const char qry[] = "DELETE FROM config WHERE cfgid = ?;";
342 
343 	sqlite3_stmt* stmt;
344 	sqlite3_prepare_v2(dbh->dbh, qry, sizeof(qry)-1, &stmt, NULL);
345 	sqlite3_bind_int(stmt, 1, id);
346 	sqlite3_step(stmt);
347 	sqlite3_finalize(stmt);
348 
349 	return true;
350 }
351 
arcan_db_addtarget(struct arcan_dbh * dbh,const char * identifier,const char * group,const char * exec,const char * argv[],size_t sz,enum DB_BFORMAT bfmt)352 arcan_targetid arcan_db_addtarget(struct arcan_dbh* dbh,
353 	const char* identifier, const char* group, const char* exec,
354 	const char* argv[], size_t sz, enum DB_BFORMAT bfmt)
355 {
356 	static const char ddl[] = "INSERT OR REPLACE INTO "
357 		"	target(tgtid, name, tag, executable, bfmt) VALUES "
358 		"((select tgtid FROM target where name = ?), ?, ?, ?, ?)";
359 
360 	sqlite3_stmt* stmt;
361 	sqlite3_prepare_v2(dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL);
362 
363 	sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
364 	sqlite3_bind_text(stmt, 2, identifier, -1, SQLITE_STATIC);
365 	sqlite3_bind_text(stmt, 3, group, -1, SQLITE_STATIC);
366 	sqlite3_bind_text(stmt, 4, exec, -1, SQLITE_STATIC);
367 	sqlite3_bind_int(stmt, 5, bfmt);
368 
369 	sqlite3_step(stmt);
370 	sqlite3_finalize(stmt);
371 
372 	arcan_targetid newid = sqlite3_last_insert_rowid(dbh->dbh);
373 
374 /* delete previous arguments */
375 	static const char drop_argv[] = "DELETE FROM target_argv WHERE target = ?;";
376 	sqlite3_prepare_v2(dbh->dbh, drop_argv, sizeof(drop_argv) - 1, &stmt, NULL);
377 	sqlite3_bind_int(stmt, 1, newid);
378 	sqlite3_step(stmt);
379 	sqlite3_finalize(stmt);
380 
381 /* add new ones */
382 	if (0 == sz)
383 		return newid;
384 
385 	static const char add_argv[] = DI_INSARG_TARGET;
386 	for (size_t i = 0; i < sz; i++){
387 		sqlite3_prepare_v2(dbh->dbh, add_argv, sizeof(add_argv) - 1, &stmt, NULL);
388 		sqlite3_bind_int(stmt, 1, newid);
389 		sqlite3_bind_text(stmt, 2, argv[i], -1, SQLITE_STATIC);
390 		sqlite3_step(stmt);
391 		sqlite3_finalize(stmt);
392 	}
393 
394 	return newid;
395 }
396 
arcan_db_addconfig(struct arcan_dbh * dbh,arcan_targetid id,const char * identifier,const char * argv[],size_t sz)397 arcan_configid arcan_db_addconfig(struct arcan_dbh* dbh,
398 	arcan_targetid id,	const char* identifier, const char* argv[], size_t sz)
399 {
400 	if (!arcan_db_verifytarget(dbh, id))
401 		return BAD_CONFIG;
402 
403 	static const char ddl[] = "INSERT OR REPLACE INTO config(cfgid, name, "
404 		"passed_counter, failed_counter, target) VALUES "
405 		"(NULL, ?, ?, ?, ?)";
406 
407 	sqlite3_stmt* stmt;
408 	sqlite3_prepare_v2(dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL);
409 
410 	sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
411 	sqlite3_bind_int(stmt, 2, 0);
412 	sqlite3_bind_int(stmt, 3, 0);
413 	sqlite3_bind_int(stmt, 4, id);
414 
415 	sqlite3_step(stmt);
416 	sqlite3_finalize(stmt);
417 
418 	arcan_configid newid = sqlite3_last_insert_rowid(dbh->dbh);
419 
420 /* delete previous arguments */
421 	static const char drop_argv[] = "DELETE FROM config_argv WHERE config = ?;";
422 	sqlite3_prepare_v2(dbh->dbh, drop_argv, sizeof(drop_argv) - 1, &stmt, NULL);
423 	sqlite3_bind_int(stmt, 1, newid);
424 	sqlite3_step(stmt);
425 	sqlite3_finalize(stmt);
426 
427 /* add new ones */
428 	if (0 == sz)
429 		return newid;
430 
431 	static const char add_argv[] = DI_INSARG_CONFIG;
432 	for (size_t i = 0; i < sz; i++){
433 		sqlite3_prepare_v2(dbh->dbh, add_argv, sizeof(add_argv) - 1, &stmt, NULL);
434 		sqlite3_bind_int(stmt, 1, newid);
435 		sqlite3_bind_text(stmt, 2, argv[i], -1, SQLITE_STATIC);
436 		sqlite3_step(stmt);
437 		sqlite3_finalize(stmt);
438 	}
439 
440 	return newid;
441 }
442 
443 #endif
444 
arcan_db_verifytarget(struct arcan_dbh * dbh,arcan_targetid id)445 bool arcan_db_verifytarget(struct arcan_dbh* dbh, arcan_targetid id)
446 {
447 	static const char ddl[] = "SELECT COUNT(*) FROM target WHERE tgtid = ?;";
448 
449 	sqlite3_stmt* stmt = NULL;
450 	if (SQLITE_OK == sqlite3_prepare_v2(
451 		dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL)){
452 		sqlite3_bind_int(stmt, 1, id);
453 		sqlite3_step(stmt);
454 		return 1 == sqlite3_column_int(stmt, 0);
455 	}
456 
457 	return false;
458 }
459 
arcan_db_targetid(struct arcan_dbh * dbh,const char * identifier,arcan_configid * defid)460 arcan_targetid arcan_db_targetid(struct arcan_dbh* dbh,
461 	const char* identifier, arcan_configid* defid)
462 {
463 	arcan_targetid rid = BAD_TARGET;
464 	static const char dql[] = "SELECT tgtid FROM target WHERE name = ?;";
465 	sqlite3_stmt* stmt;
466 
467 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
468 	sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
469 
470 	if (SQLITE_ROW == sqlite3_step(stmt))
471 		rid = sqlite3_column_int64(stmt, 0);
472 
473 	sqlite3_finalize(stmt);
474 	return rid;
475 }
476 
arcan_db_list_appl(struct arcan_dbh * dbh)477 struct arcan_strarr arcan_db_list_appl(struct arcan_dbh* dbh)
478 {
479 	static const char dql[] = "SELECT name FROM sqlite_master WHERE "
480 		"type='table' and NAME like \"appl_%\"";
481 	sqlite3_stmt* stmt;
482 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
483 
484 	return db_string_query(dbh, stmt, NULL, 0);
485 }
486 
arcan_db_config_argv(struct arcan_dbh * dbh,arcan_configid id)487 struct arcan_strarr arcan_db_config_argv(struct arcan_dbh* dbh,arcan_configid id)
488 {
489 	static const char dql[] = "SELECT arg FROM config_argv WHERE "
490 		"config = ? ORDER BY argnum ASC;";
491 	sqlite3_stmt* stmt;
492 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
493 	sqlite3_bind_int(stmt, 1, id);
494 
495 	return db_string_query(dbh, stmt, NULL, 0);
496 }
497 
arcan_db_target_argv(struct arcan_dbh * dbh,arcan_targetid id)498 struct arcan_strarr arcan_db_target_argv(struct arcan_dbh* dbh,arcan_targetid id)
499 {
500 	static const char dql[] = "SELECT arg FROM target_argv WHERE "
501 		"target = ? ORDER BY argnum ASC;";
502 	sqlite3_stmt* stmt;
503 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
504 	sqlite3_bind_int(stmt, 1, id);
505 
506 	return db_string_query(dbh, stmt, NULL, 0);
507 }
508 
arcan_db_cfgtarget(struct arcan_dbh * dbh,arcan_configid cfg)509 arcan_targetid arcan_db_cfgtarget(struct arcan_dbh* dbh, arcan_configid cfg)
510 {
511 	static const char dql[] = "SELECT target FROM config WHERE cfgid = ?;";
512 	sqlite3_stmt* stmt;
513 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
514 	sqlite3_bind_int(stmt, 1, cfg);
515 	arcan_targetid tid = BAD_TARGET;
516 
517 	if (SQLITE_ROW == sqlite3_step(stmt))
518 		tid = sqlite3_column_int64(stmt, 0);
519 
520 	sqlite3_finalize(stmt);
521 	return tid;
522 }
523 
arcan_db_configid(struct arcan_dbh * dbh,arcan_targetid target,const char * config)524 arcan_configid arcan_db_configid(struct arcan_dbh* dbh,
525 	arcan_targetid target, const char* config)
526 {
527 	static const char dql[] = "SELECT cfgid FROM config"
528 		"	WHERE name = ? AND target = ?;";
529 	sqlite3_stmt* stmt;
530 	arcan_configid cid = BAD_CONFIG;
531 
532 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
533 	sqlite3_bind_text(stmt, 1, config, strlen(config), SQLITE_STATIC);
534 	sqlite3_bind_int(stmt, 2, target);
535 
536 	if (SQLITE_ROW == sqlite3_step(stmt))
537 		cid = sqlite3_column_int64(stmt, 0);
538 
539 	sqlite3_finalize(stmt);
540 	return cid;
541 }
542 
arcan_db_target_tags(struct arcan_dbh * dbh)543 struct arcan_strarr arcan_db_target_tags(struct arcan_dbh* dbh)
544 {
545 	sqlite3_stmt* stmt;
546 	static const char dql[] = "SELECT DISTINCT tag FROM target;";
547 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
548 	return db_string_query(dbh, stmt, NULL, 0);
549 }
550 
arcan_db_targets(struct arcan_dbh * dbh,const char * tag)551 struct arcan_strarr arcan_db_targets(struct arcan_dbh* dbh, const char* tag)
552 {
553 	sqlite3_stmt* stmt;
554 	if (!tag){
555 		static const char dql[] = "SELECT name FROM target;";
556 		sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
557 	}
558 	else {
559 		static const char dql[] = "SELECT name FROM target WHERE tag=?;";
560 		sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
561 		sqlite3_bind_text(stmt, 1, tag, strlen(tag), SQLITE_STATIC);
562 	}
563 	return db_string_query(dbh, stmt, NULL, 0);
564 }
565 
arcan_db_targettag(struct arcan_dbh * dbh,arcan_targetid tid)566 char* arcan_db_targettag(struct arcan_dbh* dbh, arcan_targetid tid)
567 {
568 	static const char dql[] = "SELECT tag FROM target WHERE tgtid = ?;";
569 	char* resstr = NULL;
570 	sqlite3_stmt* stmt;
571 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
572 
573 	sqlite3_bind_int(stmt, 1, tid);
574 	if (sqlite3_step(stmt) == SQLITE_ROW){
575 		resstr = (char*) sqlite3_column_text(stmt, 0);
576 	}
577 
578 	if (resstr)
579 		resstr = strdup(resstr);
580 
581 	sqlite3_finalize(stmt);
582 	return resstr;
583 }
584 
arcan_db_targetexec(struct arcan_dbh * dbh,arcan_configid configid,enum DB_BFORMAT * bfmt,struct arcan_strarr * argv,struct arcan_strarr * env,struct arcan_strarr * libs)585 char* arcan_db_targetexec(struct arcan_dbh* dbh,
586 	arcan_configid configid, enum DB_BFORMAT* bfmt,
587 	struct arcan_strarr* argv, struct arcan_strarr* env,
588 	struct arcan_strarr* libs)
589 {
590 	arcan_targetid tid = arcan_db_cfgtarget(dbh, configid);
591 	if (tid == BAD_TARGET)
592 		return NULL;
593 
594 	sqlite3_stmt* stmt;
595 	static const char dql[] = "SELECT executable, bfmt "
596 		"FROM target WHERE tgtid = ?;";
597 
598 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql) - 1, &stmt, NULL);
599 	sqlite3_bind_int(stmt, 1, tid);
600 
601 	char* execstr = NULL;
602 	if (sqlite3_step(stmt) == SQLITE_ROW){
603 		execstr = (char*) sqlite3_column_text(stmt, 0);
604 		*bfmt = (int) sqlite3_column_int64(stmt, 1);
605 	}
606 
607 	if (execstr)
608 		execstr = strdup(execstr);
609 
610 	sqlite3_finalize(stmt);
611 
612 	static const char dql_tgt_argv[] = "SELECT arg FROM target_argv WHERE "
613 		"target = ? ORDER BY argnum ASC;";
614 	sqlite3_prepare_v2(dbh->dbh, dql_tgt_argv,
615 		sizeof(dql_tgt_argv)-1, &stmt, NULL);
616 	sqlite3_bind_int(stmt, 1, tid);
617 
618 	*argv = db_string_query(dbh, stmt, NULL, 1);
619 	argv->data[0] = strdup(execstr ? execstr : "");
620 
621 	static const char dql_cfg_argv[] = "SELECT arg FROM config_argv WHERE "
622 		"config = ? ORDER BY argnum ASC;";
623 	sqlite3_prepare_v2(dbh->dbh, dql_cfg_argv,
624 		sizeof(dql_cfg_argv)-1, &stmt, NULL);
625 	sqlite3_bind_int(stmt, 1, configid);
626 	*argv = db_string_query(dbh, stmt, argv, 0);
627 
628 	static const char dql_tgt_env[] = "SELECT key || '=' || val "
629 		"FROM target_env WHERE target = ?";
630 	sqlite3_prepare_v2(dbh->dbh, dql_tgt_env,
631 		sizeof(dql_tgt_env)-1, &stmt, NULL);
632 	sqlite3_bind_int(stmt, 1, tid);
633 	*env = db_string_query(dbh, stmt, NULL, 0);
634 
635 	static const char dql_cfg_env[] = "SELECT key || '=' || val "
636 		"FROM config_env WHERE config = ?";
637 	sqlite3_prepare_v2(dbh->dbh, dql_cfg_env,
638 		sizeof(dql_cfg_env)-1, &stmt, NULL);
639 	sqlite3_bind_int(stmt, 1, tid);
640 	db_string_query(dbh, stmt, env, 0);
641 
642 	static const char dql_tgt_lib[] = "SELECT libname FROM target_libs WHERE "
643 		"target = ?;";
644 	sqlite3_prepare_v2(dbh->dbh, dql_tgt_lib,
645 		sizeof(dql_tgt_lib)-1, &stmt, NULL);
646 	sqlite3_bind_int(stmt, 1, tid);
647 	*libs = db_string_query(dbh, stmt, NULL, 0);
648 
649 	return execstr;
650 }
651 
arcan_db_launch_status(struct arcan_dbh * dbh,arcan_configid cid,bool s)652 void arcan_db_launch_status(struct arcan_dbh* dbh, arcan_configid cid, bool s)
653 {
654 	static const char dql_ok[] = "UPDATE passed_counter SET "
655 		"passed_counter = passed_counter + 1 WHERE config = ?;";
656 
657 	static const char dql_fail[] = "UPDATE failed_counter SET "
658 		"failed_counter = failed_counter + 1 WHERE config = ?;";
659 
660 	sqlite3_stmt* stmt;
661 	sqlite3_prepare_v2(dbh->dbh,
662 		(s ? dql_ok : dql_fail), sizeof(dql_ok)-1, &stmt, NULL);
663 	sqlite3_bind_int(stmt, 1, cid);
664 	sqlite3_step(stmt);
665 	sqlite3_finalize(stmt);
666 }
667 
arcan_db_configs(struct arcan_dbh * dbh,arcan_targetid tid)668 struct arcan_strarr arcan_db_configs(struct arcan_dbh* dbh, arcan_targetid tid)
669 {
670 	static const char dql[] = "SELECT name FROM config WHERE target = ?;";
671 	sqlite3_stmt* stmt;
672 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
673 	sqlite3_bind_int(stmt, 1, tid);
674 
675 	return db_string_query(dbh, stmt, NULL, 0);
676 }
677 
arcan_db_execname(struct arcan_dbh * dbh,arcan_targetid tid)678 char* arcan_db_execname(struct arcan_dbh* dbh, arcan_targetid tid)
679 {
680 	static const char dql[] = "SELECT executable FROM target WHERE tgtid = ?;";
681 	sqlite3_stmt* stmt;
682 	sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
683 	sqlite3_bind_int(stmt, 1, tid);
684 
685 	char* res = NULL;
686 	if (sqlite3_step(stmt) == SQLITE_ROW) {
687 		const unsigned char* arg = sqlite3_column_text(stmt, 0);
688 		res = arg ? strdup((char*)arg) : NULL;
689 	}
690 
691 	sqlite3_finalize(stmt);
692 	return res;
693 }
694 
arcan_db_begin_transaction(struct arcan_dbh * dbh,enum DB_KVTARGET kvt,union arcan_dbtrans_id id)695 void arcan_db_begin_transaction(struct arcan_dbh* dbh,
696 	enum DB_KVTARGET kvt, union arcan_dbtrans_id id)
697 {
698 	if (dbh->transaction)
699 		arcan_fatal("arcan_db_begin_transaction()"
700 			"	called during a pending transaction\n");
701 
702 	sqlite3_exec(dbh->dbh, "BEGIN;", NULL, NULL, NULL);
703 	int code = SQLITE_OK;
704 
705 	switch (kvt){
706 	case DVT_APPL:
707 		code = sqlite3_prepare_v2(dbh->dbh, dbh->akv_update,
708 			dbh->akv_upd_sz, &dbh->transaction, NULL);
709 	break;
710 
711 	case DVT_TARGET:
712 		code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET,
713 			strlen(DI_INSKV_TARGET)+1, &dbh->transaction, NULL);
714 	break;
715 
716 	case DVT_CONFIG:
717 		code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_CONFIG,
718 			strlen(DI_INSKV_CONFIG)+1, &dbh->transaction, NULL);
719 	break;
720 
721 	case DVT_CONFIG_ENV:
722 		code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_CONFIG_ENV,
723 			strlen(DI_INSKV_TARGET_ENV)+1, &dbh->transaction, NULL);
724 	break;
725 
726 	case DVT_TARGET_ENV:
727 		code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET_ENV,
728 			strlen(DI_INSKV_TARGET_ENV)+1, &dbh->transaction, NULL);
729 	break;
730 
731 	case DVT_TARGET_LIBV:
732 		code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET_LIBV,
733 			strlen(DI_INSKV_TARGET_LIBV)+1, &dbh->transaction, NULL);
734 	break;
735 	case DVT_ENDM:
736 	break;
737 	}
738 
739 	if (code != SQLITE_OK){
740 		arcan_warning("arcan_db_begin_transaction(), failed: %s\n",
741 			sqlite3_errmsg(dbh->dbh));
742 	}
743 
744 	dbh->trid = id;
745 	dbh->ttype = kvt;
746 }
747 
arcan_db_getkeys(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,union arcan_dbtrans_id id)748 struct arcan_strarr arcan_db_getkeys(struct arcan_dbh* dbh,
749 	enum DB_KVTARGET tgt, union arcan_dbtrans_id id)
750 {
751 #define GET_KV_TGT "SELECT key || '=' || val FROM target_kv WHERE target = ?;"
752 	static const char* const queries[] = {
753 		GET_KV_TGT,
754 		"SELECT key || '=' || val FROM config_kv WHERE config = ?"
755 	};
756 
757 	const char* qry = NULL;
758 	if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
759 		qry = queries[0];
760 	else
761 		qry = queries[1];
762 
763 	sqlite3_stmt * stmt;
764 	sqlite3_prepare_v2(dbh->dbh, qry, sizeof(GET_KV_TGT)-1, &stmt, NULL);
765 	sqlite3_bind_int(stmt, 1, tgt>=DVT_TARGET && tgt<DVT_CONFIG ? id.tid:id.cid);
766 
767 #undef GET_KV_TGT
768 	return db_string_query(dbh, stmt, NULL, 0);
769 }
770 
arcan_db_applkeys(struct arcan_dbh * dbh,const char * applname,const char * pattern)771 struct arcan_strarr arcan_db_applkeys(struct arcan_dbh* dbh,
772 	const char* applname, const char* pattern)
773 {
774 #define MATCH_APPL "SELECT key || '=' || val FROM appl_%s WHERE key LIKE ?;"
775 
776 	size_t mk_sz = sizeof(MATCH_APPL) + strlen(applname);
777 	char mk_buf[ mk_sz ];
778 	ssize_t nw = snprintf(mk_buf, mk_sz, MATCH_APPL, applname);
779 
780 	sqlite3_stmt* stmt;
781 	sqlite3_prepare_v2(dbh->dbh, mk_buf, mk_sz-1, &stmt, NULL);
782 	sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
783 
784 	return db_string_query(dbh, stmt, NULL, 0);
785 #undef MATCH_KEY_APPL
786 }
787 
arcan_db_matchkey(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,const char * pattern)788 struct arcan_strarr arcan_db_matchkey(struct arcan_dbh* dbh,
789 	enum DB_KVTARGET tgt, const char* pattern)
790 {
791 #define MATCH_KEY_TGT "SELECT tgtid ||':'|| value"\
792 	" FROM target_kv WHERE key LIKE ?;"
793 
794 	static const char* const queries[] = {
795 		MATCH_KEY_TGT,
796 		"SELECT cfgid || ':' || value FROM config_kv WHERE key LIKE ?;"
797 	};
798 
799 	const char* qry = NULL;
800 	if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
801 		qry = queries[0];
802 	else
803 		qry = queries[1];
804 
805 	sqlite3_stmt* stmt;
806 	sqlite3_prepare_v2(dbh->dbh, qry, sizeof(MATCH_KEY_TGT)-1, &stmt, NULL);
807 	sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
808 
809 	return db_string_query(dbh, stmt, NULL, 0);
810 }
811 
arcan_db_getvalue(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,int64_t id,const char * key)812 char* arcan_db_getvalue(struct arcan_dbh* dbh,
813 	enum DB_KVTARGET tgt, int64_t id, const char* key)
814 {
815 	char* res = NULL;
816 /* must match enum */
817 	assert(DVT_ENDM == 5);
818 
819 	static const char* queries[] = {
820 		"SELECT val FROM target_kv WHERE key = ? AND tgtid = ? LIMIT 1;",
821 		"SELECT val FROM config_kv WHERE key = ? AND cfgid = ? LIMIT 1;"
822 	};
823 
824 	const char* qry = NULL;
825 	if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
826 		qry = queries[0];
827 	else
828 		qry = queries[1];
829 
830 /* assume they all are the same length */
831 	static size_t qry_sz;
832 	if (qry_sz == 0)
833 		qry_sz = strlen(queries[1]);
834 
835 	sqlite3_stmt* stmt = NULL;
836 
837 	if (tgt == DVT_APPL){
838 		sqlite3_prepare_v2(dbh->dbh, dbh->akv_get, dbh->akv_get_sz, &stmt, NULL);
839 	}
840 	else {
841 		sqlite3_prepare_v2(dbh->dbh, qry, qry_sz, &stmt, NULL);
842 		sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
843 		sqlite3_bind_int(stmt, 2, id);
844 	}
845 
846 	if (SQLITE_ROW == sqlite3_step(stmt)){
847 		const char* row = (const char*) sqlite3_column_text(stmt, 0);
848 		if (row)
849 			res = strdup(row);
850 	}
851 
852 	sqlite3_finalize(stmt);
853 	return res;
854 }
855 
arcan_db_add_kvpair(struct arcan_dbh * dbh,const char * key,const char * val)856 void arcan_db_add_kvpair(
857 	struct arcan_dbh* dbh, const char* key, const char* val)
858 {
859 	if (!dbh->transaction)
860 		arcan_fatal("arcan_db_add_kvpair() "
861 			"called without any open transaction.");
862 
863 	if (!val){
864 		dbh->trclean = true;
865 		return;
866 	}
867 
868 	if (val[0] == 0)
869 		dbh->trclean = true;
870 
871 	sqlite3_bind_text(dbh->transaction, 1, key, -1, SQLITE_TRANSIENT);
872 	sqlite3_bind_text(dbh->transaction, 2, val, -1, SQLITE_TRANSIENT);
873 
874 	switch (dbh->ttype){
875 	case DVT_APPL:
876 	case DVT_ENDM:
877 	break;
878 
879 	case DVT_TARGET:
880 	case DVT_TARGET_ENV:
881 	case DVT_TARGET_LIBV:
882 		sqlite3_bind_int(dbh->transaction, 3, dbh->trid.tid);
883 	break;
884 
885 	case DVT_CONFIG:
886 	case DVT_CONFIG_ENV:
887 		sqlite3_bind_int(dbh->transaction, 3, dbh->trid.cid);
888 	break;
889 	}
890 
891 	int rc = sqlite3_step(dbh->transaction);
892 	if (SQLITE_DONE != rc)
893 		arcan_warning("arcan_db_addkvpair(%s=%s), %d failed: %s\n",
894 			key, val, rc, sqlite3_errmsg(dbh->dbh));
895 	else {
896 		sqlite3_clear_bindings(dbh->transaction);
897 		sqlite3_reset(dbh->transaction);
898 	}
899 }
900 
arcan_db_end_transaction(struct arcan_dbh * dbh)901 void arcan_db_end_transaction(struct arcan_dbh* dbh)
902 {
903 	if (!dbh->transaction)
904 		arcan_fatal("arcan_db_end_transaction() "
905 			"called without any open transaction.");
906 
907 	sqlite3_finalize(dbh->transaction);
908 
909 	if (dbh->trclean){
910 		switch (dbh->ttype){
911 		case DVT_APPL:
912 			sqlite3_exec(dbh->dbh, dbh->akv_clean, NULL, NULL, NULL);
913 		break;
914 		case DVT_TARGET:
915 			sqlite3_exec(dbh->dbh, DI_DROPKV_TARGET, NULL, NULL, NULL);
916 		break;
917 		case DVT_CONFIG:
918 			sqlite3_exec(dbh->dbh, DI_DROPKV_CONFIG, NULL, NULL, NULL);
919 		break;
920 		default:
921 		break;
922 		}
923 		dbh->trclean = false;
924 	}
925 
926 	if (SQLITE_OK != sqlite3_exec(dbh->dbh, "COMMIT;", NULL, NULL, NULL)){
927 		arcan_warning("arcan_db_end_transaction(), failed: %s\n",
928 			sqlite3_errmsg(dbh->dbh));
929 	}
930 
931 	dbh->transaction = NULL;
932 }
933 
arcan_db_appl_kv(struct arcan_dbh * dbh,const char * applname,const char * key,const char * value)934 bool arcan_db_appl_kv(struct arcan_dbh* dbh,
935 	const char* applname, const char* key, const char* value)
936 {
937 	bool rv = false;
938 
939 	if (dbh->transaction)
940 		arcan_fatal("arcan_db_appl_kv() called during a pending transaction\n");
941 
942 	if (!applname || !dbh || !key)
943 		return rv;
944 
945 	const char ddl_insert[] = "INSERT OR REPLACE "
946 		"INTO appl_%s(key, val) VALUES(?, ?);";
947 	const char k_drop[] = "DELETE FROM appl_%s WHERE key=?;";
948 
949 	size_t upd_sz = 0;
950 	const char* dqry = ddl_insert;
951 
952 	if (!value){
953 		upd_sz = sizeof(k_drop) + strlen(applname);
954 		dqry = k_drop;
955 	}
956 	else
957 		upd_sz = sizeof(ddl_insert) + strlen(applname);
958 
959 	char upd_buf[ upd_sz ];
960 	ssize_t nw = snprintf(upd_buf, upd_sz, dqry, applname);
961 
962 	sqlite3_stmt* stmt = NULL;
963 	sqlite3_prepare_v2(dbh->dbh, upd_buf, nw, &stmt, NULL);
964 	sqlite3_bind_text(stmt, 1, key,   -1, SQLITE_TRANSIENT);
965 	sqlite3_bind_text(stmt, 2, value, -1, SQLITE_TRANSIENT);
966 
967 	rv = sqlite3_step(stmt) == SQLITE_DONE;
968 	sqlite3_finalize(stmt);
969 
970 	return rv;
971 }
972 
arcan_db_appl_val(struct arcan_dbh * dbh,const char * const applname,const char * const key)973 char* arcan_db_appl_val(struct arcan_dbh* dbh,
974 	const char* const applname, const char* const key)
975 {
976 	if (!dbh || !key)
977 		return NULL;
978 
979 	const char qry[] = "SELECT val FROM appl_%s WHERE key = ?;";
980 
981 	size_t wbuf_sz = strlen(applname) + sizeof(qry);
982 	char wbuf[ wbuf_sz ];
983 	memset(wbuf, '\0', wbuf_sz);
984 	snprintf(wbuf, wbuf_sz, qry, applname);
985 
986 	sqlite3_stmt* stmt = NULL;
987 	sqlite3_prepare_v2(dbh->dbh, wbuf, wbuf_sz, &stmt, NULL);
988 	sqlite3_bind_text(stmt, 1, (char*) key, -1, SQLITE_TRANSIENT);
989 
990 	char* rv = NULL;
991 	int rc = sqlite3_step(stmt);
992 
993 	if (rc == SQLITE_ROW){
994 		const unsigned char* rowt;
995 
996 		if ( (rowt = sqlite3_column_text(stmt, 0)) != NULL)
997 			rv = strdup((const char*) rowt);
998 	}
999 
1000 	sqlite3_finalize(stmt);
1001 
1002 	return rv;
1003 }
1004 
setup_ddl(struct arcan_dbh * dbh)1005 static void setup_ddl(struct arcan_dbh* dbh)
1006 {
1007 	create_appl_group(dbh, "arcan");
1008 	create_appl_group(dbh, "arcan_lwa");
1009 	arcan_db_appl_kv(dbh, "arcan", "dbversion", DB_VERSION_NUM);
1010 	arcan_db_appl_kv(dbh, "arcan_lwa", "dbversion", DB_VERSION_NUM);
1011 
1012 	for (size_t i = 0; i < sizeof(ddls)/sizeof(ddls[0]); i++)
1013 		db_void_query(dbh, ddls[i], false);
1014 }
1015 
1016 /* to detect old databases and upgrade if possible */
dbh_integrity_check(struct arcan_dbh * dbh)1017 static bool dbh_integrity_check(struct arcan_dbh* dbh){
1018 	int tablecount = db_num_query(dbh, "SELECT Count(*) "
1019 		"FROM sqlite_master WHERE type='table';", NULL);
1020 
1021 /* empty database */
1022 	if (tablecount <= 0){
1023 		arcan_warning("Empty database encountered, running default DDL queries.\n");
1024 		setup_ddl(dbh);
1025 
1026 		return true;
1027 	}
1028 
1029 /* check for descriptor table, missing?
1030  * that means we have a first- version database, push upgrade */
1031 	char* valstr = arcan_db_appl_val(dbh, ARCAN_TBL, "dbversion");
1032 	unsigned vnum = valstr ? strtoul(valstr, NULL, 10) : 0;
1033 
1034 	switch (vnum){
1035 		case 0:
1036 		case 1:
1037 		case 2:
1038 			arcan_warning("DB version (< 3) unsupported, rebuild necessary\n");
1039 			return false;
1040 		case 3:
1041 			arcan_warning("DB version (< 4) found, rebuild suggested\n");
1042 	}
1043 
1044 	arcan_mem_free(valstr);
1045 	return true;
1046 }
1047 
arcan_db_close(struct arcan_dbh ** ctx)1048 void arcan_db_close(struct arcan_dbh** ctx)
1049 {
1050 	if (!ctx)
1051 		return;
1052 
1053 	sqlite3_close((*ctx)->dbh);
1054 	arcan_mem_free((*ctx)->applname);
1055 	arcan_mem_free((*ctx)->akv_update);
1056 	arcan_mem_free((*ctx)->akv_get);
1057 	arcan_mem_free(*ctx);
1058 	*ctx = NULL;
1059 }
1060 
arcan_db_open(const char * fname,const char * applname)1061 struct arcan_dbh* arcan_db_open(const char* fname, const char* applname)
1062 {
1063 	sqlite3* dbh;
1064 
1065 	if (!fname)
1066 		return NULL;
1067 
1068 	if (!db_init) {
1069 		int rv = sqlite3_initialize();
1070 		db_init = true;
1071 
1072 		if (rv != SQLITE_OK)
1073 			return NULL;
1074 
1075 		atexit(sqliteexit);
1076 	}
1077 
1078 	if (!applname)
1079 		applname = "_default";
1080 
1081 	if (sqlite3_open_v2(fname, &dbh,
1082 		SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK){
1083 		struct arcan_dbh* res = arcan_alloc_mem(
1084 			sizeof(struct arcan_dbh), ARCAN_MEM_EXTSTRUCT,
1085 			ARCAN_MEM_SENSITIVE | ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE
1086 		);
1087 
1088 		res->dbh = dbh;
1089 		assert(dbh);
1090 
1091 		if ( !dbh_integrity_check(res) ){
1092 			sqlite3_close(dbh);
1093 			arcan_mem_free(res);
1094 			return NULL;
1095 		}
1096 
1097 		res->applname = strdup(applname);
1098 		create_appl_group(res, res->applname);
1099 
1100 		db_void_query(res, "PRAGMA foreign_keys=ON;", false);
1101 		db_void_query(res, "PRAGMA synchronous=OFF;", false);
1102 
1103 		return res;
1104 	}
1105 	else
1106 		;
1107 
1108 	return NULL;
1109 }
1110