1 #include "common.h"
2 #include "misc.h"
3 #include "iflist.h"
4 #include "dbsql.h"
5 
6 /* global db */
7 sqlite3 *db;
8 int db_errcode;
9 int db_intransaction;
10 
db_open_ro(void)11 int db_open_ro(void)
12 {
13 	return db_open(0, 1);
14 }
15 
db_open_rw(const int createifnotfound)16 int db_open_rw(const int createifnotfound)
17 {
18 	return db_open(createifnotfound, 0);
19 }
20 
db_open(const int createifnotfound,const int readonly)21 int db_open(const int createifnotfound, const int readonly)
22 {
23 	int rc, createdb = 0;
24 	char dbfilename[530];
25 
26 #ifdef CHECK_VNSTAT
27 	/* use ram based database when testing for shorter test execution times by reducing disk i/o */
28 	snprintf(dbfilename, 530, ":memory:");
29 	createdb = 1;
30 #else
31 	struct stat filestat;
32 
33 	if (db != NULL) {
34 		return 1;
35 	}
36 
37 	snprintf(dbfilename, 530, "%s/%s", cfg.dbdir, DATABASEFILE);
38 
39 	/* create database if file doesn't exist */
40 	if (stat(dbfilename, &filestat) != 0) {
41 		if (errno == ENOENT && createifnotfound && !readonly) {
42 			createdb = 1;
43 		} else {
44 			if (debug)
45 				printf("Error (debug): Handling database \"%s\" failed: %s\n", dbfilename, strerror(errno));
46 			return 0;
47 		}
48 	} else {
49 		if (filestat.st_size == 0) {
50 			if (createifnotfound) {
51 				createdb = 1;
52 			} else {
53 				printf("Error: Database \"%s\" contains 0 bytes and isn't a valid database, exiting.\n", dbfilename);
54 				exit(EXIT_FAILURE);
55 			}
56 		}
57 	}
58 #endif
59 	db_errcode = 0;
60 	db_intransaction = 0;
61 	if (readonly) {
62 		rc = sqlite3_open_v2(dbfilename, &db, SQLITE_OPEN_READONLY, NULL);
63 	} else {
64 		rc = sqlite3_open_v2(dbfilename, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
65 	}
66 
67 	if (rc) {
68 		db_errcode = rc;
69 		if (debug)
70 			printf("Error (debug): Can't open database \"%s\": %s\n", dbfilename, sqlite3_errmsg(db));
71 		return 0;
72 	} else {
73 		if (debug)
74 			printf("Database \"%s\" open (ro: %d)\n", dbfilename, readonly);
75 	}
76 
77 	if (createdb) {
78 #ifndef CHECK_VNSTAT
79 		if (!spacecheck(cfg.dbdir)) {
80 			printf("Error: Not enough free diskspace available in \"%s\", exiting.\n", cfg.dbdir);
81 			db_close();
82 			exit(EXIT_FAILURE);
83 		}
84 #endif
85 		if (!db_create()) {
86 			if (debug)
87 				printf("Error (debug): Creating database \"%s\" structure failed\n", dbfilename);
88 			db_close();
89 			return 0;
90 		} else {
91 			if (debug)
92 				printf("Database \"%s\" structure created\n", dbfilename);
93 			if (!db_setinfo("dbversion", SQLDBVERSION, 1)) {
94 				if (debug)
95 					printf("Error (debug): Writing version info to database \"%s\" failed\n", dbfilename);
96 				db_close();
97 				return 0;
98 			}
99 		}
100 	}
101 
102 	/* set pragmas */
103 	if (!readonly) {
104 		sqlite3_busy_timeout(db, cfg.updateinterval * 1000);
105 		if (!db_setpragmas()) {
106 			db_close();
107 			return 0;
108 		}
109 	} else {
110 		/* set busy timeout when database is open in read-only mode */
111 		sqlite3_busy_timeout(db, DBREADTIMEOUTSECS * 1000);
112 	}
113 
114 	if (!createdb) {
115 		if (!db_validate(readonly)) {
116 			db_close();
117 			return 0;
118 		}
119 	}
120 
121 	if (createifnotfound && !readonly) {
122 		if (!db_setinfo("vnstatversion", getversion(), 1)) {
123 			db_close();
124 			return 0;
125 		}
126 	}
127 
128 	return 1;
129 }
130 
db_validate(const int readonly)131 int db_validate(const int readonly)
132 {
133 	int dbversion, currentversion;
134 
135 	db_errcode = 0;
136 	dbversion = atoi(db_getinfo("dbversion"));
137 	if (db_errcode) {
138 		return 0;
139 	}
140 
141 	currentversion = atoi(SQLDBVERSION);
142 
143 	if (debug) {
144 		printf("Database version \"%d\", current version \"%d\"\n", dbversion, currentversion);
145 	}
146 
147 	if (dbversion == currentversion) {
148 		return 1;
149 
150 	} else if (dbversion == 0) {
151 		printf("Error: Database version \"%d\" suggests error situation in database, exiting.\n", dbversion);
152 		return 0;
153 
154 	} else if (dbversion > currentversion) {
155 		printf("Error: Database version \"%d\" is not supported. Support is available up to version \"%d\", exiting.\n", dbversion, currentversion);
156 		return 0;
157 
158 	} else if (dbversion < currentversion) {
159 		if (readonly) {
160 			/* database upgrade actions should be performed here once needed */
161 			printf("Error: Unable to upgrade read-only database from version \"%d\" to \"%d\", exiting.\n", dbversion, currentversion);
162 			return 0;
163 		}
164 		/* database upgrade actions should be performed here once needed, then return 1 */
165 		/* however, since this is the first database version, always return 0 */
166 	}
167 
168 	return 0;
169 }
170 
db_setpragmas(void)171 int db_setpragmas(void)
172 {
173 	int rc;
174 	char sql[25];
175 	sqlite3_stmt *sqlstmt;
176 
177 	/* enable use of foreign keys */
178 	if (!db_exec("PRAGMA foreign_keys = ON")) {
179 		return 0;
180 	}
181 
182 	rc = sqlite3_prepare_v2(db, "PRAGMA foreign_keys", -1, &sqlstmt, NULL);
183 	if (rc != SQLITE_OK) {
184 		db_errcode = rc;
185 		snprintf(errorstring, 1024, "Exec prepare \"PRAGMA foreign_keys;\" failed (%d): %s", rc, sqlite3_errmsg(db));
186 		printe(PT_Error);
187 		return 0;
188 	}
189 
190 	/* PRAGMA foreign_keys; is expected to return one row if the feature is supported */
191 	rc = sqlite3_step(sqlstmt);
192 	if (rc != SQLITE_ROW) {
193 		db_errcode = rc;
194 		snprintf(errorstring, 1024, "PRAGMA foreign_keys returned no row (%d): %s", rc, sqlite3_errmsg(db));
195 		printe(PT_Error);
196 		sqlite3_finalize(sqlstmt);
197 		return 0;
198 	}
199 
200 	rc = sqlite3_finalize(sqlstmt);
201 	if (rc) {
202 		db_errcode = rc;
203 		snprintf(errorstring, 1024, "Exec finalize \"PRAGMA foreign_keys;\" failed (%d): %s", rc, sqlite3_errmsg(db));
204 		printe(PT_Error);
205 		return 0;
206 	}
207 
208 #if HAVE_DECL_SQLITE_CHECKPOINT_RESTART
209 	/* set journal_mode */
210 	if (cfg.waldb) {
211 		if (!db_exec("PRAGMA journal_mode = WAL")) {
212 			return 0;
213 		}
214 	} else {
215 		if (!db_exec("PRAGMA journal_mode = DELETE")) {
216 			return 0;
217 		}
218 	}
219 #endif
220 
221 	/* set synchronous */
222 	if (cfg.dbsynchronous == -1) {
223 #if HAVE_DECL_SQLITE_CHECKPOINT_RESTART
224 		if (cfg.waldb) {
225 			if (!db_exec("PRAGMA synchronous = 1")) {
226 				return 0;
227 			}
228 		} else {
229 			if (!db_exec("PRAGMA synchronous = 2")) {
230 				return 0;
231 			}
232 		}
233 #else
234 		if (!db_exec("PRAGMA synchronous = 2")) {
235 			return 0;
236 		}
237 #endif
238 	} else {
239 		snprintf(sql, 25, "PRAGMA synchronous = %d", cfg.dbsynchronous);
240 		if (!db_exec(sql)) {
241 			return 0;
242 		}
243 	}
244 
245 	return 1;
246 }
247 
db_close(void)248 int db_close(void)
249 {
250 	int rc;
251 
252 	if (db == NULL) {
253 		return 1;
254 	}
255 
256 	rc = sqlite3_close(db);
257 	if (rc == SQLITE_OK) {
258 		db = NULL;
259 		if (debug)
260 			printf("Database closed\n");
261 		return 1;
262 	} else {
263 		db_errcode = rc;
264 		if (debug)
265 			printf("Error (debug): Closing database failed (%d): %s\n", rc, sqlite3_errmsg(db));
266 		return 0;
267 	}
268 }
269 
db_exec(const char * sql)270 int db_exec(const char *sql)
271 {
272 	int rc;
273 	sqlite3_stmt *sqlstmt;
274 
275 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
276 	if (rc != SQLITE_OK) {
277 		db_errcode = rc;
278 		snprintf(errorstring, 1024, "Exec prepare failed (%d: %s): \"%s\"", rc, sqlite3_errmsg(db), sql);
279 		printe(PT_Error);
280 		return 0;
281 	}
282 
283 	rc = sqlite3_step(sqlstmt);
284 	if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
285 		db_errcode = rc;
286 		snprintf(errorstring, 1024, "Exec step failed (%d: %s): \"%s\"", rc, sqlite3_errmsg(db), sql);
287 		printe(PT_Error);
288 		sqlite3_finalize(sqlstmt);
289 		return 0;
290 	}
291 
292 	rc = sqlite3_finalize(sqlstmt);
293 	if (rc) {
294 		db_errcode = rc;
295 		snprintf(errorstring, 1024, "Exec finalize failed (%d: %s): \"%s\"", rc, sqlite3_errmsg(db), sql);
296 		printe(PT_Error);
297 		return 0;
298 	}
299 
300 	return 1;
301 }
302 
db_create(void)303 int db_create(void)
304 {
305 	int i;
306 	const char *constsql;
307 	char *sql;
308 	char buffer[32];
309 	const char *datatables[] = {"fiveminute", "hour", "day", "month", "year", "top"};
310 
311 	if (!db_begintransaction()) {
312 		return 0;
313 	}
314 
315 	constsql = "CREATE TABLE info(\n"
316 		  "  id       INTEGER PRIMARY KEY,\n"
317 		  "  name     TEXT UNIQUE NOT NULL,\n"
318 		  "  value    TEXT NOT NULL)";
319 
320 	if (!db_exec(constsql)) {
321 		db_rollbacktransaction();
322 		return 0;
323 	}
324 
325 	constsql = "CREATE TABLE interface(\n"
326 		  "  id           INTEGER PRIMARY KEY,\n"
327 		  "  name         TEXT UNIQUE NOT NULL,\n"
328 		  "  alias        TEXT,\n"
329 		  "  active       INTEGER NOT NULL,\n"
330 		  "  created      DATE NOT NULL,\n"
331 		  "  updated      DATE NOT NULL,\n"
332 		  "  rxcounter    INTEGER NOT NULL,\n"
333 		  "  txcounter    INTEGER NOT NULL,\n"
334 		  "  rxtotal      INTEGER NOT NULL,\n"
335 		  "  txtotal      INTEGER NOT NULL)";
336 
337 	if (!db_exec(constsql)) {
338 		db_rollbacktransaction();
339 		return 0;
340 	}
341 
342 	sql = malloc(sizeof(char) * 512);
343 	for (i = 0; i < 6; i++) {
344 		sqlite3_snprintf(512, sql, "CREATE TABLE %s(\n"
345 								   "  id           INTEGER PRIMARY KEY,\n"
346 								   "  interface    INTEGER REFERENCES interface(id) ON DELETE CASCADE,\n"
347 								   "  date         DATE NOT NULL,\n"
348 								   "  rx           INTEGER NOT NULL,\n"
349 								   "  tx           INTEGER NOT NULL,\n"
350 								   "  CONSTRAINT u UNIQUE (interface, date))",
351 						 datatables[i]);
352 
353 		if (!db_exec(sql)) {
354 			free(sql);
355 			db_rollbacktransaction();
356 			return 0;
357 		}
358 	}
359 	free(sql);
360 
361 	snprintf(buffer, 32, "%" PRIu64 "", (uint64_t)MAX32);
362 	if (!db_setinfo("btime", buffer, 1)) {
363 		db_rollbacktransaction();
364 		return 0;
365 	}
366 
367 	return db_committransaction();
368 }
369 
db_addinterface(const char * iface)370 int db_addinterface(const char *iface)
371 {
372 	char sql[256];
373 
374 	if (!strlen(iface)) {
375 		return 0;
376 	}
377 
378 	sqlite3_snprintf(256, sql, "insert into interface (name, active, created, updated, rxcounter, txcounter, rxtotal, txtotal) values ('%q', 1, datetime('now'%s), datetime('now'%s), 0, 0, 0, 0)", iface, cfg.dbtzmodifier, cfg.dbtzmodifier);
379 	return db_exec(sql);
380 }
381 
db_removeinterface(const char * iface)382 int db_removeinterface(const char *iface)
383 {
384 	char sql[64];
385 	sqlite3_int64 ifaceid = 0;
386 
387 	ifaceid = db_getinterfaceid(iface, 0);
388 	if (ifaceid == 0) {
389 		return 0;
390 	}
391 
392 	sqlite3_snprintf(64, sql, "delete from interface where id=%" PRId64 "", (int64_t)ifaceid);
393 	return db_exec(sql);
394 }
395 
db_renameinterface(const char * iface,const char * newifname)396 int db_renameinterface(const char *iface, const char *newifname)
397 {
398 	char sql[128];
399 	sqlite3_int64 ifaceid = 0;
400 
401 	if (!strlen(newifname)) {
402 		return 0;
403 	}
404 
405 	ifaceid = db_getinterfaceid(iface, 0);
406 	if (ifaceid == 0) {
407 		return 0;
408 	}
409 
410 	sqlite3_snprintf(128, sql, "update interface set name='%q' where id=%" PRId64 "", newifname, (int64_t)ifaceid);
411 	return db_exec(sql);
412 }
413 
db_getinterfacecount(void)414 uint64_t db_getinterfacecount(void)
415 {
416 	return db_getinterfacecountbyname("");
417 }
418 
db_getinterfacecountbyname(const char * iface)419 uint64_t db_getinterfacecountbyname(const char *iface)
420 {
421 	int rc;
422 	uint64_t result = 0;
423 	char sql[128], *inquery = NULL;
424 	sqlite3_stmt *sqlstmt;
425 
426 	if (strchr(iface, '+') == NULL) {
427 		if (strlen(iface) > 0) {
428 			sqlite3_snprintf(128, sql, "select count(*) from interface where name='%q'", iface);
429 		} else {
430 			sqlite3_snprintf(128, sql, "select count(*) from interface");
431 		}
432 	} else {
433 		inquery = getifaceinquery(iface);
434 		if (inquery == NULL) {
435 			return 0;
436 		}
437 		sqlite3_snprintf(128, sql, "select count(*) from interface where name in (%q)", inquery);
438 		free(inquery);
439 	}
440 
441 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
442 	if (rc != SQLITE_OK) {
443 		db_errcode = rc;
444 		snprintf(errorstring, 1024, "Failed to get interface count from database (%d): %s", rc, sqlite3_errmsg(db));
445 		printe(PT_Error);
446 		return 0;
447 	}
448 	if (sqlite3_column_count(sqlstmt) != 1) {
449 		return 0;
450 	}
451 	if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
452 		result = (uint64_t)sqlite3_column_int64(sqlstmt, 0);
453 	}
454 	sqlite3_finalize(sqlstmt);
455 
456 	/* consider merge query as invalid if not all requested interfaces are found or are not unique */
457 	if (strchr(iface, '+') != NULL) {
458 		if (result != getqueryinterfacecount(iface)) {
459 			result = 0;
460 		}
461 	}
462 
463 	return result;
464 }
465 
db_getinterfaceid(const char * iface,const int createifnotfound)466 sqlite3_int64 db_getinterfaceid(const char *iface, const int createifnotfound)
467 {
468 	int rc;
469 	char sql[128];
470 	sqlite3_int64 ifaceid = 0;
471 	sqlite3_stmt *sqlstmt;
472 
473 	sqlite3_snprintf(128, sql, "select id from interface where name='%q'", iface);
474 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
475 	if (rc == SQLITE_OK) {
476 		if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
477 			ifaceid = sqlite3_column_int64(sqlstmt, 0);
478 		}
479 		sqlite3_finalize(sqlstmt);
480 	} else {
481 		db_errcode = rc;
482 		snprintf(errorstring, 1024, "Failed to get interface id from database (%d): %s", rc, sqlite3_errmsg(db));
483 		printe(PT_Error);
484 	}
485 
486 	if (ifaceid == 0 && createifnotfound) {
487 		if (!db_addinterface(iface)) {
488 			return 0;
489 		}
490 		ifaceid = sqlite3_last_insert_rowid(db);
491 	}
492 
493 	return ifaceid;
494 }
495 
db_getinterfaceidin(const char * iface)496 char *db_getinterfaceidin(const char *iface)
497 {
498 	int rc;
499 	char sql[256], *result, *inquery;
500 	sqlite3_stmt *sqlstmt;
501 
502 	result = NULL;
503 	inquery = getifaceinquery(iface);
504 	if (inquery == NULL) {
505 		return NULL;
506 	}
507 
508 	sqlite3_snprintf(256, sql, "select group_concat(id) from interface where name in (%q)", inquery);
509 	free(inquery);
510 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
511 	if (rc == SQLITE_OK) {
512 		if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
513 			if (sqlite3_column_text(sqlstmt, 0) != NULL) {
514 				result = strdup((const char *)sqlite3_column_text(sqlstmt, 0));
515 			}
516 		}
517 		sqlite3_finalize(sqlstmt);
518 	} else {
519 		db_errcode = rc;
520 		snprintf(errorstring, 1024, "Failed to get interface id from database (%d): %s", rc, sqlite3_errmsg(db));
521 		printe(PT_Error);
522 	}
523 
524 	return result;
525 }
526 
db_setactive(const char * iface,const int active)527 int db_setactive(const char *iface, const int active)
528 {
529 	char sql[64];
530 	sqlite3_int64 ifaceid = 0;
531 
532 	ifaceid = db_getinterfaceid(iface, 0);
533 	if (ifaceid == 0) {
534 		return 0;
535 	}
536 
537 	sqlite3_snprintf(64, sql, "update interface set active=%d where id=%" PRId64 "", active, (int64_t)ifaceid);
538 	return db_exec(sql);
539 }
540 
db_setupdated(const char * iface,const time_t timestamp)541 int db_setupdated(const char *iface, const time_t timestamp)
542 {
543 	char sql[256];
544 	sqlite3_int64 ifaceid = 0;
545 
546 	ifaceid = db_getinterfaceid(iface, 0);
547 	if (ifaceid == 0) {
548 		return 0;
549 	}
550 
551 	sqlite3_snprintf(256, sql, "update interface set updated=datetime(%" PRIu64 ", 'unixepoch'%s) where id=%" PRId64 "", (uint64_t)timestamp, cfg.dbtzmodifier, (int64_t)ifaceid);
552 	return db_exec(sql);
553 }
554 
db_setcounters(const char * iface,const uint64_t rxcounter,const uint64_t txcounter)555 int db_setcounters(const char *iface, const uint64_t rxcounter, const uint64_t txcounter)
556 {
557 	char sql[256];
558 	sqlite3_int64 ifaceid = 0;
559 
560 	ifaceid = db_getinterfaceid(iface, 0);
561 	if (ifaceid == 0) {
562 		return 0;
563 	}
564 
565 	sqlite3_snprintf(256, sql, "update interface set rxcounter=%" PRIu64 ", txcounter=%" PRIu64 " where id=%" PRId64 "", rxcounter, txcounter, (int64_t)ifaceid);
566 	return db_exec(sql);
567 }
568 
db_getcounters(const char * iface,uint64_t * rxcounter,uint64_t * txcounter)569 int db_getcounters(const char *iface, uint64_t *rxcounter, uint64_t *txcounter)
570 {
571 	int rc;
572 	char sql[128];
573 	sqlite3_int64 ifaceid = 0;
574 	sqlite3_stmt *sqlstmt;
575 
576 	*rxcounter = *txcounter = 0;
577 
578 	ifaceid = db_getinterfaceid(iface, 0);
579 	if (ifaceid == 0) {
580 		return 0;
581 	}
582 
583 	sqlite3_snprintf(128, sql, "select rxcounter, txcounter from interface where id=%" PRId64 "", (int64_t)ifaceid);
584 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
585 	if (rc != SQLITE_OK) {
586 		db_errcode = rc;
587 		snprintf(errorstring, 1024, "Failed to get interface counters from database (%d): %s", rc, sqlite3_errmsg(db));
588 		printe(PT_Error);
589 		return 0;
590 	}
591 	if (sqlite3_column_count(sqlstmt) != 2) {
592 		sqlite3_finalize(sqlstmt);
593 		return 0;
594 	}
595 	if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
596 		*rxcounter = (uint64_t)sqlite3_column_int64(sqlstmt, 0);
597 		*txcounter = (uint64_t)sqlite3_column_int64(sqlstmt, 1);
598 	}
599 	sqlite3_finalize(sqlstmt);
600 
601 	return 1;
602 }
603 
db_getinterfaceinfo(const char * iface,interfaceinfo * info)604 int db_getinterfaceinfo(const char *iface, interfaceinfo *info)
605 {
606 	int rc;
607 	char sql[512], *ifaceidin = NULL;
608 	sqlite3_int64 ifaceid;
609 	sqlite3_stmt *sqlstmt;
610 
611 	if (strchr(iface, '+') == NULL) {
612 		ifaceid = db_getinterfaceid(iface, 0);
613 		if (ifaceid == 0) {
614 			return 0;
615 		}
616 		sqlite3_snprintf(512, sql, "select name, alias, active, strftime('%%s', created, 'utc'), strftime('%%s', updated, 'utc'), rxcounter, txcounter, rxtotal, txtotal from interface where id=%" PRId64 "", (int64_t)ifaceid);
617 	} else {
618 		ifaceidin = db_getinterfaceidin(iface);
619 		if (ifaceidin == NULL || strlen(ifaceidin) < 1) {
620 			free(ifaceidin);
621 			return 0;
622 		}
623 		sqlite3_snprintf(512, sql, "select \"%q\", NULL, max(active), max(strftime('%%s', created, 'utc')), min(strftime('%%s', updated, 'utc')), 0, 0, sum(rxtotal), sum(txtotal) from interface where id in (%q)", iface, ifaceidin);
624 		free(ifaceidin);
625 	}
626 
627 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
628 	if (rc != SQLITE_OK) {
629 		db_errcode = rc;
630 		snprintf(errorstring, 1024, "Failed to get interface information from database (%d): %s", rc, sqlite3_errmsg(db));
631 		printe(PT_Error);
632 		return 0;
633 	}
634 	if (sqlite3_column_count(sqlstmt) != 9) {
635 		return 0;
636 	}
637 	if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
638 		if (sqlite3_column_text(sqlstmt, 0) != NULL) {
639 			strncpy_nt(info->name, (const char *)sqlite3_column_text(sqlstmt, 0), 32);
640 		} else {
641 			info->name[0] = '\0';
642 		}
643 		if (sqlite3_column_text(sqlstmt, 1) != NULL) {
644 			strncpy_nt(info->alias, (const char *)sqlite3_column_text(sqlstmt, 1), 32);
645 		} else {
646 			info->alias[0] = '\0';
647 		}
648 		info->active = sqlite3_column_int(sqlstmt, 2);
649 		info->created = (time_t)sqlite3_column_int64(sqlstmt, 3);
650 		info->updated = (time_t)sqlite3_column_int64(sqlstmt, 4);
651 		info->rxcounter = (uint64_t)sqlite3_column_int64(sqlstmt, 5);
652 		info->txcounter = (uint64_t)sqlite3_column_int64(sqlstmt, 6);
653 		info->rxtotal = (uint64_t)sqlite3_column_int64(sqlstmt, 7);
654 		info->txtotal = (uint64_t)sqlite3_column_int64(sqlstmt, 8);
655 	}
656 	sqlite3_finalize(sqlstmt);
657 
658 	return 1;
659 }
660 
db_setalias(const char * iface,const char * alias)661 int db_setalias(const char *iface, const char *alias)
662 {
663 	char sql[128];
664 	sqlite3_int64 ifaceid = 0;
665 
666 	ifaceid = db_getinterfaceid(iface, 0);
667 	if (ifaceid == 0) {
668 		return 0;
669 	}
670 
671 	sqlite3_snprintf(128, sql, "update interface set alias='%q' where id=%" PRId64 "", alias, (int64_t)ifaceid);
672 	return db_exec(sql);
673 }
674 
db_setinfo(const char * name,const char * value,const int createifnotfound)675 int db_setinfo(const char *name, const char *value, const int createifnotfound)
676 {
677 	int rc;
678 	char sql[128];
679 
680 	sqlite3_snprintf(128, sql, "update info set value='%q' where name='%q'", value, name);
681 	rc = db_exec(sql);
682 	if (!rc || (!sqlite3_changes(db) && !createifnotfound)) {
683 		return 0;
684 	}
685 	if (!sqlite3_changes(db) && createifnotfound) {
686 		sqlite3_snprintf(512, sql, "insert into info (name, value) values ('%q', '%q')", name, value);
687 		rc = db_exec(sql);
688 	}
689 	return rc;
690 }
691 
db_getinfo(const char * name)692 char *db_getinfo(const char *name)
693 {
694 	int rc;
695 	char sql[128];
696 	static char buffer[64];
697 	sqlite3_stmt *sqlstmt;
698 
699 	buffer[0] = '\0';
700 
701 	sqlite3_snprintf(128, sql, "select value from info where name='%q'", name);
702 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
703 	if (rc != SQLITE_OK) {
704 		db_errcode = rc;
705 		snprintf(errorstring, 1024, "Failed to get info value for \"%s\" from database (%d): %s", name, rc, sqlite3_errmsg(db));
706 		printe(PT_Error);
707 		return buffer;
708 	}
709 	if (sqlite3_step(sqlstmt) == SQLITE_ROW) {
710 		if (sqlite3_column_text(sqlstmt, 0) != NULL) {
711 			strncpy_nt(buffer, (const char *)sqlite3_column_text(sqlstmt, 0), 64);
712 		}
713 	}
714 	sqlite3_finalize(sqlstmt);
715 
716 	return buffer;
717 }
718 
db_getiflist(iflist ** ifl)719 int db_getiflist(iflist **ifl)
720 {
721 	return db_getiflist_sorted(ifl, 0);
722 }
723 
db_getiflist_sorted(iflist ** ifl,const int orderbytraffic)724 int db_getiflist_sorted(iflist **ifl, const int orderbytraffic)
725 {
726 	int rc;
727 	const char *constsql;
728 	sqlite3_stmt *sqlstmt;
729 
730 	if (!orderbytraffic) {
731 		constsql = "select name from interface order by name asc";
732 	} else {
733 		constsql = "select name from interface order by rxtotal+txtotal desc";
734 	}
735 
736 	rc = sqlite3_prepare_v2(db, constsql, -1, &sqlstmt, NULL);
737 	if (rc != SQLITE_OK) {
738 		db_errcode = rc;
739 		snprintf(errorstring, 1024, "Failed to get interface list from database (%d): %s", rc, sqlite3_errmsg(db));
740 		printe(PT_Error);
741 		return -1;
742 	}
743 
744 	rc = 0;
745 	while (sqlite3_step(sqlstmt) == SQLITE_ROW) {
746 		if (sqlite3_column_text(sqlstmt, 0) == NULL) {
747 			continue;
748 		}
749 		if (!iflistadd(ifl, (const char *)sqlite3_column_text(sqlstmt, 0), 0)) {
750 			break;
751 		}
752 		rc++;
753 	}
754 
755 	sqlite3_finalize(sqlstmt);
756 
757 	return rc;
758 }
759 
db_get_date_generator(const int range,const short direct,const char * nowdate)760 char *db_get_date_generator(const int range, const short direct, const char *nowdate)
761 {
762 	static char dgen[512];
763 	dgen[0] = '\0';
764 
765 	switch (range) {
766 		case 0: /* 5min */
767 			snprintf(dgen, 512, "datetime(%s, ('-' || (strftime('%%M', %s)) || ' minutes'), ('-' || (strftime('%%S', %s)) || ' seconds'), ('+' || (round(strftime('%%M', %s)/5,0)*5) || ' minutes')%s)", nowdate, nowdate, nowdate, nowdate, cfg.dbtzmodifier);
768 			break;
769 		case 1: /* hour */
770 			snprintf(dgen, 512, "strftime('%%Y-%%m-%%d %%H:00:00', %s%s)", nowdate, cfg.dbtzmodifier);
771 			break;
772 		case 2: /* day */
773 		case 5: /* top */
774 			snprintf(dgen, 512, "date(%s%s)", nowdate, cfg.dbtzmodifier);
775 			break;
776 		case 3: /* month */
777 			if (direct || cfg.monthrotate == 1) {
778 				snprintf(dgen, 512, "strftime('%%Y-%%m-01', %s%s)", nowdate, cfg.dbtzmodifier);
779 			} else {
780 				snprintf(dgen, 512, "strftime('%%Y-%%m-01', datetime(%s, '-%d days')%s)", nowdate, cfg.monthrotate - 1, cfg.dbtzmodifier);
781 			}
782 			break;
783 		case 4: /* year */
784 			if (direct || cfg.monthrotate == 1 || cfg.monthrotateyears == 0) {
785 				snprintf(dgen, 512, "strftime('%%Y-01-01', %s%s)", nowdate, cfg.dbtzmodifier);
786 			} else {
787 				snprintf(dgen, 512, "strftime('%%Y-01-01', datetime(%s, '-%d days')%s)", nowdate, cfg.monthrotate - 1, cfg.dbtzmodifier);
788 			}
789 			break;
790 		default:
791 			break;
792 	}
793 	return dgen;
794 }
795 
db_addtraffic(const char * iface,const uint64_t rx,const uint64_t tx)796 int db_addtraffic(const char *iface, const uint64_t rx, const uint64_t tx)
797 {
798 	return db_addtraffic_dated(iface, rx, tx, 0);
799 }
800 
db_addtraffic_dated(const char * iface,const uint64_t rx,const uint64_t tx,const uint64_t timestamp)801 int db_addtraffic_dated(const char *iface, const uint64_t rx, const uint64_t tx, const uint64_t timestamp)
802 {
803 	int i, intransaction = db_intransaction;
804 	char sql[1024], nowdate[64];
805 	sqlite3_int64 ifaceid = 0;
806 
807 	const char *datatables[] = {"fiveminute", "hour", "day", "month", "year", "top"};
808 	int32_t *featurecfg[] = {&cfg.fiveminutehours, &cfg.hourlydays, &cfg.dailydays, &cfg.monthlymonths, &cfg.yearlyyears, &cfg.topdayentries};
809 
810 	ifaceid = db_getinterfaceid(iface, 1);
811 	if (ifaceid == 0) {
812 		return 0;
813 	}
814 
815 	if (timestamp > 0) {
816 		snprintf(nowdate, 64, "datetime(%" PRIu64 ", 'unixepoch')", timestamp);
817 	} else {
818 		snprintf(nowdate, 64, "'now'");
819 	}
820 
821 	if (debug)
822 		printf("db add %s (%" PRId64 ") %" PRIu64 ": rx %" PRIu64 " - tx %" PRIu64 "\n", iface, (int64_t)ifaceid, timestamp, rx, tx);
823 
824 	if (!intransaction) {
825 		if (!db_begintransaction()) {
826 			return 0;
827 		}
828 	}
829 
830 	/* total */
831 	if (rx > 0 || tx > 0) {
832 		sqlite3_snprintf(1024, sql, "update interface set rxtotal=rxtotal+%" PRIu64 ", txtotal=txtotal+%" PRIu64 " where id=%" PRId64 "", rx, tx, (int64_t)ifaceid);
833 		if (!db_exec(sql)) {
834 			db_rollbacktransaction();
835 			return 0;
836 		}
837 	}
838 
839 	/* time specific */
840 	for (i = 0; i < 6; i++) {
841 		if (featurecfg[i] == 0) {
842 			continue;
843 		}
844 		sqlite3_snprintf(1024, sql, "insert or ignore into %s (interface, date, rx, tx) values (%" PRId64 ", %s, 0, 0)", datatables[i], (int64_t)ifaceid, db_get_date_generator(i, 0, nowdate));
845 		if (!db_exec(sql)) {
846 			db_rollbacktransaction();
847 			return 0;
848 		}
849 		sqlite3_snprintf(1024, sql, "update %s set rx=rx+%" PRIu64 ", tx=tx+%" PRIu64 " where interface=%" PRId64 " and date=%s", datatables[i], rx, tx, (int64_t)ifaceid, db_get_date_generator(i, 0, nowdate));
850 		if (!db_exec(sql)) {
851 			db_rollbacktransaction();
852 			return 0;
853 		}
854 	}
855 
856 	if (!intransaction) {
857 		return db_committransaction();
858 	}
859 	return 1;
860 }
861 
db_setcreation(const char * iface,const time_t timestamp)862 int db_setcreation(const char *iface, const time_t timestamp)
863 {
864 	char sql[256];
865 	sqlite3_int64 ifaceid = 0;
866 
867 	ifaceid = db_getinterfaceid(iface, 0);
868 	if (ifaceid == 0) {
869 		return 0;
870 	}
871 
872 	sqlite3_snprintf(256, sql, "update interface set created=datetime(%" PRIu64 ", 'unixepoch'%s) where id=%" PRId64 "", (uint64_t)timestamp, cfg.dbtzmodifier, (int64_t)ifaceid);
873 	return db_exec(sql);
874 }
875 
db_settotal(const char * iface,const uint64_t rx,const uint64_t tx)876 int db_settotal(const char *iface, const uint64_t rx, const uint64_t tx)
877 {
878 	char sql[256];
879 	sqlite3_int64 ifaceid = 0;
880 
881 	ifaceid = db_getinterfaceid(iface, 0);
882 	if (ifaceid == 0) {
883 		return 0;
884 	}
885 
886 	sqlite3_snprintf(256, sql, "update interface set rxtotal=%" PRIu64 ", txtotal=%" PRIu64 " where id=%" PRId64 "", rx, tx, (int64_t)ifaceid);
887 	return db_exec(sql);
888 }
889 
890 /* used only for legacy data import */
db_insertdata(const char * table,const char * iface,const uint64_t rx,const uint64_t tx,const uint64_t timestamp)891 int db_insertdata(const char *table, const char *iface, const uint64_t rx, const uint64_t tx, const uint64_t timestamp)
892 {
893 	int i, index = -1;
894 	char sql[1024], nowdate[64];
895 	sqlite3_int64 ifaceid = 0;
896 
897 	const char *datatables[] = {"hour", "day", "month", "year", "top"};
898 
899 	for (i = 0; i < 5; i++) {
900 		if (strcmp(table, datatables[i]) == 0) {
901 			index = i;
902 			break;
903 		}
904 	}
905 
906 	if (index == -1) {
907 		return 0;
908 	}
909 
910 	ifaceid = db_getinterfaceid(iface, 0);
911 	if (ifaceid == 0) {
912 		return 0;
913 	}
914 
915 	/* legacy data is always local timezone and needs to be enforced here as a result */
916 	if (cfg.useutc) {
917 		snprintf(nowdate, 64, "datetime(%" PRIu64 ", 'unixepoch', 'localtime')", timestamp);
918 	} else {
919 		snprintf(nowdate, 64, "datetime(%" PRIu64 ", 'unixepoch')", timestamp);
920 	}
921 
922 	sqlite3_snprintf(1024, sql, "insert or ignore into %s (interface, date, rx, tx) values (%" PRId64 ", %s, %" PRIu64 ", %" PRIu64 ")", table, (int64_t)ifaceid, db_get_date_generator(index + 1, 1, nowdate), rx, tx);
923 	return db_exec(sql);
924 }
925 
db_removeoldentries(void)926 int db_removeoldentries(void)
927 {
928 	char sql[256];
929 
930 	if (debug) {
931 		printf("db: removing old entries\n");
932 	}
933 
934 	if (!db_begintransaction()) {
935 		return 0;
936 	}
937 
938 	if (!db_removeoldentries_top()) {
939 		db_rollbacktransaction();
940 		return 0;
941 	}
942 
943 	if (cfg.fiveminutehours > 0) {
944 		if (debug) {
945 			printf("db: fiveminute cleanup (%dh)\n", cfg.fiveminutehours);
946 		}
947 		sqlite3_snprintf(256, sql, "delete from fiveminute where date < datetime('now', '-%d hours'%s)", cfg.fiveminutehours, cfg.dbtzmodifier);
948 		if (!db_exec(sql)) {
949 			db_rollbacktransaction();
950 			return 0;
951 		}
952 	}
953 
954 	if (cfg.hourlydays > 0) {
955 		if (debug) {
956 			printf("db: hour cleanup (%dd)\n", cfg.hourlydays);
957 		}
958 		sqlite3_snprintf(256, sql, "delete from hour where date < datetime('now', '-%d days'%s)", cfg.hourlydays, cfg.dbtzmodifier);
959 		if (!db_exec(sql)) {
960 			db_rollbacktransaction();
961 			return 0;
962 		}
963 	}
964 
965 	if (cfg.dailydays > 0) {
966 		if (debug) {
967 			printf("db: day cleanup (%dd)\n", cfg.dailydays);
968 		}
969 		sqlite3_snprintf(256, sql, "delete from day where date < date('now', '-%d days'%s)", cfg.dailydays, cfg.dbtzmodifier);
970 		if (!db_exec(sql)) {
971 			db_rollbacktransaction();
972 			return 0;
973 		}
974 	}
975 
976 	if (cfg.monthlymonths > 0) {
977 		if (debug) {
978 			printf("db: month cleanup (%dm)\n", cfg.monthlymonths);
979 		}
980 		sqlite3_snprintf(256, sql, "delete from month where date < date('now', '-%d months'%s)", cfg.monthlymonths, cfg.dbtzmodifier);
981 		if (!db_exec(sql)) {
982 			db_rollbacktransaction();
983 			return 0;
984 		}
985 	}
986 
987 	if (cfg.yearlyyears > 0) {
988 		if (debug) {
989 			printf("db: year cleanup (%dy)\n", cfg.yearlyyears);
990 		}
991 		sqlite3_snprintf(256, sql, "delete from year where date < date('now', '-%d years'%s)", cfg.yearlyyears, cfg.dbtzmodifier);
992 		if (!db_exec(sql)) {
993 			db_rollbacktransaction();
994 			return 0;
995 		}
996 	}
997 
998 	return db_committransaction();
999 }
1000 
db_removeoldentries_top(void)1001 int db_removeoldentries_top(void)
1002 {
1003 	int errorcount = 0;
1004 	char sql[512];
1005 	iflist *dbifl = NULL, *dbifl_iterator = NULL;
1006 	sqlite3_int64 ifaceid;
1007 
1008 	if (cfg.topdayentries <= 0) {
1009 		return 1;
1010 	}
1011 
1012 	if (db_getiflist(&dbifl) < 0) {
1013 		return 0;
1014 	}
1015 
1016 	dbifl_iterator = dbifl;
1017 
1018 	while (dbifl_iterator != NULL) {
1019 		if (debug) {
1020 			printf("db: top cleanup: %s (%d)\n", dbifl_iterator->interface, cfg.topdayentries);
1021 		}
1022 
1023 		ifaceid = db_getinterfaceid(dbifl_iterator->interface, 0);
1024 		if (ifaceid == 0) {
1025 			errorcount++;
1026 			dbifl_iterator = dbifl_iterator->next;
1027 			continue;
1028 		}
1029 
1030 		sqlite3_snprintf(512, sql, "delete from top where id in ( select id from top where interface=%" PRId64 " and date!=date('now'%s) order by rx+tx desc, date asc limit -1 offset %d )", (int64_t)ifaceid, cfg.dbtzmodifier, cfg.topdayentries);
1031 
1032 		if (!db_exec(sql)) {
1033 			errorcount++;
1034 			dbifl_iterator = dbifl_iterator->next;
1035 			continue;
1036 		}
1037 
1038 		dbifl_iterator = dbifl_iterator->next;
1039 	}
1040 
1041 	iflistfree(&dbifl);
1042 
1043 	if (errorcount) {
1044 		return 0;
1045 	}
1046 
1047 	return 1;
1048 }
1049 
db_vacuum(void)1050 int db_vacuum(void)
1051 {
1052 	if (debug) {
1053 		printf("db: vacuum\n");
1054 	}
1055 	return db_exec("VACUUM");
1056 }
1057 
db_begintransaction(void)1058 int db_begintransaction(void)
1059 {
1060 	int rc;
1061 
1062 	if (debug) {
1063 		printf("db: begin transaction\n");
1064 	}
1065 
1066 	rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0);
1067 	if (rc) {
1068 		db_errcode = rc;
1069 		snprintf(errorstring, 1024, "Begin transaction to database failed (%d): %s", rc, sqlite3_errmsg(db));
1070 		printe(PT_Error);
1071 		return 0;
1072 	}
1073 	db_intransaction = 1;
1074 	return 1;
1075 }
1076 
db_committransaction(void)1077 int db_committransaction(void)
1078 {
1079 	int rc;
1080 
1081 	if (debug) {
1082 		printf("db: commit transaction\n");
1083 	}
1084 
1085 	db_intransaction = 0;
1086 
1087 	rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
1088 	if (rc) {
1089 		snprintf(errorstring, 1024, "Commit transaction to database failed (%d): %s", rc, sqlite3_errmsg(db));
1090 		printe(PT_Error);
1091 		/* execute rollback if commit failure left the transaction open */
1092 		if (!sqlite3_get_autocommit(db)) {
1093 			db_rollbacktransaction();
1094 		}
1095 		db_errcode = rc;
1096 		return 0;
1097 	}
1098 	return 1;
1099 }
1100 
db_rollbacktransaction(void)1101 int db_rollbacktransaction(void)
1102 {
1103 	int rc;
1104 
1105 	if (debug) {
1106 		printf("db: rollback transaction\n");
1107 	}
1108 
1109 	db_intransaction = 0;
1110 
1111 	rc = sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
1112 	if (rc) {
1113 		db_errcode = rc;
1114 		snprintf(errorstring, 1024, "Transaction rollback failed (%d): %s", rc, sqlite3_errmsg(db));
1115 		printe(PT_Error);
1116 		return 0;
1117 	}
1118 	return 1;
1119 }
1120 
db_iserrcodefatal(int errcode)1121 int db_iserrcodefatal(int errcode)
1122 {
1123 	switch (errcode) {
1124 		case SQLITE_OK:
1125 		case SQLITE_FULL:
1126 		case SQLITE_IOERR:
1127 		case SQLITE_LOCKED:
1128 		case SQLITE_BUSY:
1129 			return 0;
1130 		default:
1131 			return 1;
1132 	}
1133 }
1134 
db_isdiskfull(int errcode)1135 int db_isdiskfull(int errcode)
1136 {
1137 	if (errcode == SQLITE_FULL) {
1138 		return 1;
1139 	} else {
1140 		return 0;
1141 	}
1142 }
1143 
1144 #if HAVE_DECL_SQLITE_CHECKPOINT_RESTART
db_walcheckpoint(void)1145 void db_walcheckpoint(void)
1146 {
1147 	double used_secs = 0.0;
1148 
1149 	timeused(__func__, 1);
1150 #if HAVE_DECL_SQLITE_CHECKPOINT_TRUNCATE
1151 	sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_TRUNCATE, NULL, NULL);
1152 #else
1153 	sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_RESTART, NULL, NULL);
1154 #endif
1155 	timeused(__func__, 0);
1156 
1157 	used_secs = timeused(__func__, 0);
1158 	if (used_secs > SLOWDBWARNLIMIT) {
1159 		snprintf(errorstring, 1024, "Write-Ahead Logging checkpoint took %.1f seconds.", used_secs);
1160 		printe(PT_Warning);
1161 	}
1162 }
1163 #endif
1164 
db_getdata(dbdatalist ** dbdata,dbdatalistinfo * listinfo,const char * iface,const char * table,const uint32_t resultlimit)1165 int db_getdata(dbdatalist **dbdata, dbdatalistinfo *listinfo, const char *iface, const char *table, const uint32_t resultlimit)
1166 {
1167 	return db_getdata_range(dbdata, listinfo, iface, table, resultlimit, "", "");
1168 }
1169 
db_getdata_range(dbdatalist ** dbdata,dbdatalistinfo * listinfo,const char * iface,const char * table,const uint32_t resultlimit,const char * databegin,const char * dataend)1170 int db_getdata_range(dbdatalist **dbdata, dbdatalistinfo *listinfo, const char *iface, const char *table, const uint32_t resultlimit, const char *databegin, const char *dataend)
1171 {
1172 	int ret, i, rc;
1173 	const char *datatables[] = {"fiveminute", "hour", "day", "month", "year", "top"};
1174 	char sql[512], limit[64], dbegin[32], dend[44], *ifaceidin = NULL;
1175 	sqlite3_stmt *sqlstmt;
1176 	time_t timestamp;
1177 	int64_t rowid;
1178 	uint64_t rx, tx;
1179 
1180 	listinfo->count = 0;
1181 
1182 	ret = 0;
1183 	for (i = 0; i < 6; i++) {
1184 		if (strcmp(table, datatables[i]) == 0) {
1185 			ret = 1;
1186 			break;
1187 		}
1188 	}
1189 	if (!ret) {
1190 		return 0;
1191 	}
1192 
1193 	ifaceidin = db_getinterfaceidin(iface);
1194 	if (ifaceidin == NULL) {
1195 		return 0;
1196 	}
1197 
1198 	dbegin[0] = '\0';
1199 	if (strlen(databegin)) {
1200 		snprintf(dbegin, 32, "and date >= '%s'", databegin);
1201 	}
1202 
1203 	dend[0] = '\0';
1204 	if (strlen(dataend)) {
1205 		if (strchr(dataend, ':')) {
1206 			snprintf(dend, 44, "and date <= datetime('%s')", dataend);
1207 		} else {
1208 			snprintf(dend, 44, "and date <= datetime('%s 23:59:59')", dataend);
1209 		}
1210 	}
1211 
1212 	limit[0] = '\0';
1213 	if (resultlimit > 0 && (!strlen(dbegin) || !strlen(dend))) {
1214 		snprintf(limit, 64, "limit %" PRIu32 "", resultlimit);
1215 	}
1216 
1217 	/* note that using the linked list reverses the order */
1218 	/* most recent last in the linked list is considered the normal order */
1219 	if (strcmp(table, "top") == 0) {
1220 		/* 'top' entries, requires different query due to rx+tx ordering */
1221 		if (strlen(dbegin)) {
1222 			if (resultlimit > 0) {
1223 				snprintf(limit, 64, "limit %" PRIu32 "", resultlimit);
1224 			}
1225 			sqlite3_snprintf(512, sql, "select * from (select id, strftime('%%s', date, 'utc') as unixdate, sum(rx) as rx, sum(tx) as tx from day where interface in (%q) %s %s group by date order by rx+tx desc, unixdate asc %s) order by rx+tx asc, unixdate desc", ifaceidin, dbegin, dend, limit);
1226 		} else {
1227 			sqlite3_snprintf(512, sql, "select * from (select id, strftime('%%s', date, 'utc') as unixdate, sum(rx) as rx, sum(tx) as tx from top where interface in (%q) group by date order by rx+tx desc, unixdate asc %s) order by rx+tx asc, unixdate desc", ifaceidin, limit);
1228 		}
1229 	} else {
1230 		if (strlen(dbegin) && strlen(limit)) {
1231 			sqlite3_snprintf(512, sql, "select * from (select id, strftime('%%s', date, 'utc') as unixdate, sum(rx), sum(tx) from %s where interface in (%q) %s %s group by date order by unixdate asc %s) order by unixdate desc", table, ifaceidin, dbegin, dend, limit);
1232 		} else {
1233 			sqlite3_snprintf(512, sql, "select id, strftime('%%s', date, 'utc') as unixdate, sum(rx), sum(tx) from %s where interface in (%q) %s %s group by date order by unixdate desc %s", table, ifaceidin, dbegin, dend, limit);
1234 		}
1235 	}
1236 	free(ifaceidin);
1237 
1238 	rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
1239 	if (rc != SQLITE_OK) {
1240 		db_errcode = rc;
1241 		snprintf(errorstring, 1024, "Get data prepare failed (%d: %s): \"%s\"", rc, sqlite3_errmsg(db), sql);
1242 		printe(PT_Error);
1243 		return 0;
1244 	}
1245 
1246 	rc = sqlite3_column_count(sqlstmt);
1247 	if (rc != 4) {
1248 		snprintf(errorstring, 1024, "Get data returned unexpected column count %d instead of 4: \"%s\"", rc, sql);
1249 		printe(PT_Error);
1250 		sqlite3_finalize(sqlstmt);
1251 		return 0;
1252 	}
1253 
1254 	while (sqlite3_step(sqlstmt) == SQLITE_ROW) {
1255 		rowid = (int64_t)sqlite3_column_int64(sqlstmt, 0);
1256 		timestamp = (time_t)sqlite3_column_int64(sqlstmt, 1);
1257 		rx = (uint64_t)sqlite3_column_int64(sqlstmt, 2);
1258 		tx = (uint64_t)sqlite3_column_int64(sqlstmt, 3);
1259 		if (!dbdatalistadd(dbdata, rx, tx, timestamp, rowid)) {
1260 			snprintf(errorstring, 1024, "Storing data for processing failed: %s", strerror(errno));
1261 			printe(PT_Error);
1262 			ret = 0;
1263 			break;
1264 		}
1265 		updatelistinfo(listinfo, rx, tx, timestamp);
1266 	}
1267 	sqlite3_finalize(sqlstmt);
1268 
1269 	/* clean list on failure */
1270 	if (!ret) {
1271 		dbdatalistfree(dbdata);
1272 		listinfo->count = 0;
1273 	}
1274 
1275 	return ret;
1276 }
1277 
updatelistinfo(dbdatalistinfo * listinfo,const uint64_t rx,const uint64_t tx,const time_t timestamp)1278 void updatelistinfo(dbdatalistinfo *listinfo, const uint64_t rx, const uint64_t tx, const time_t timestamp)
1279 {
1280 	if (listinfo->count == 0) {
1281 		listinfo->maxtime = timestamp;
1282 		listinfo->mintime = timestamp;
1283 		listinfo->maxrx = rx;
1284 		listinfo->minrx = rx;
1285 		listinfo->maxtx = tx;
1286 		listinfo->mintx = tx;
1287 		listinfo->min = rx + tx;
1288 		listinfo->max = rx + tx;
1289 		listinfo->sumrx = rx;
1290 		listinfo->sumtx = tx;
1291 	} else {
1292 		if (timestamp > listinfo->maxtime) {
1293 			listinfo->maxtime = timestamp;
1294 		}
1295 		if (timestamp < listinfo->mintime) {
1296 			listinfo->mintime = timestamp;
1297 		}
1298 		if (rx < listinfo->minrx) {
1299 			listinfo->minrx = rx;
1300 		}
1301 		if (tx < listinfo->mintx) {
1302 			listinfo->mintx = tx;
1303 		}
1304 		if (rx > listinfo->maxrx) {
1305 			listinfo->maxrx = rx;
1306 		}
1307 		if (tx > listinfo->maxtx) {
1308 			listinfo->maxtx = tx;
1309 		}
1310 		if (rx + tx > listinfo->max) {
1311 			listinfo->max = rx + tx;
1312 		}
1313 		if (rx + tx < listinfo->min) {
1314 			listinfo->min = rx + tx;
1315 		}
1316 		listinfo->sumrx += rx;
1317 		listinfo->sumtx += tx;
1318 	}
1319 	listinfo->count++;
1320 }
1321 
dbdatalistadd(dbdatalist ** dbdata,const uint64_t rx,const uint64_t tx,const time_t timestamp,const int64_t rowid)1322 int dbdatalistadd(dbdatalist **dbdata, const uint64_t rx, const uint64_t tx, const time_t timestamp, const int64_t rowid)
1323 {
1324 	dbdatalist *newdata;
1325 
1326 	newdata = malloc(sizeof(dbdatalist));
1327 	if (newdata == NULL) {
1328 		return 0;
1329 	}
1330 
1331 	newdata->next = *dbdata;
1332 	*dbdata = newdata;
1333 
1334 	newdata->rowid = rowid;
1335 	newdata->timestamp = timestamp;
1336 	newdata->rx = rx;
1337 	newdata->tx = tx;
1338 
1339 	return 1;
1340 }
1341 
dbdatalistfree(dbdatalist ** dbdata)1342 void dbdatalistfree(dbdatalist **dbdata)
1343 {
1344 	dbdatalist *dbdata_prev;
1345 
1346 	while (*dbdata != NULL) {
1347 		dbdata_prev = *dbdata;
1348 		*dbdata = (*dbdata)->next;
1349 		free(dbdata_prev);
1350 	}
1351 }
1352 
getqueryinterfacecount(const char * input)1353 unsigned int getqueryinterfacecount(const char *input)
1354 {
1355 	unsigned int i, ifacecount = 1;
1356 
1357 	if (!strlen(input) || input[0] == '+' || input[strlen(input) - 1] == '+') {
1358 		return 0;
1359 	}
1360 
1361 	for (i = 0; i < (unsigned int)strlen(input); i++) {
1362 		if (input[i] == '+') {
1363 			if (i > 0 && input[i - 1] == '+') {
1364 				return 0;
1365 			} else {
1366 				ifacecount++;
1367 			}
1368 		}
1369 	}
1370 
1371 	return ifacecount;
1372 }
1373 
getifaceinquery(const char * input)1374 char *getifaceinquery(const char *input)
1375 {
1376 	unsigned int i, j, ifacecount;
1377 	char *result;
1378 
1379 	ifacecount = getqueryinterfacecount(input);
1380 
1381 	if (ifacecount == 0) {
1382 		return NULL;
1383 	}
1384 
1385 	/* each interface requires two quotes and comma or \0 so 3 extra chars */
1386 	j = (unsigned int)strlen(input) + ifacecount * 3;
1387 	result = malloc(sizeof(char) * j);
1388 	if (result == NULL) {
1389 		panicexit(__FILE__, __LINE__);
1390 	}
1391 
1392 	memset(result, '\0', j);
1393 
1394 	result[0] = '"';
1395 	j = 1;
1396 	for (i = 0; i < (unsigned int)strlen(input); i++) {
1397 		if (input[i] == '+') {
1398 			strcat(result, "\",\"");
1399 			j += 3;
1400 		} else {
1401 			result[j] = input[i];
1402 			j++;
1403 		}
1404 	}
1405 	result[j] = '"';
1406 
1407 	return result;
1408 }
1409