mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 03:29:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			326 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2018 Rafael Zalamena <rzalamena@gmail.com>
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the Free
 | 
						|
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
						|
 * any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | 
						|
 * more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along
 | 
						|
 * with this program; see the file COPYING; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) 2016 Rafael Zalamena <rzalamena@gmail.com>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "db.h"
 | 
						|
#include "log.h"
 | 
						|
 | 
						|
static struct sqlite3 *dbp;
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize the database in path.
 | 
						|
 *
 | 
						|
 * It's possible to use in memory database with ':memory:' path.
 | 
						|
 */
 | 
						|
int db_init(const char *path_fmt, ...)
 | 
						|
{
 | 
						|
	char path[BUFSIZ];
 | 
						|
	va_list ap;
 | 
						|
 | 
						|
	if (dbp)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	va_start(ap, path_fmt);
 | 
						|
	vsnprintf(path, sizeof(path), path_fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	if (sqlite3_open_v2(path, &dbp,
 | 
						|
			    (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE), NULL)
 | 
						|
	    != SQLITE_OK) {
 | 
						|
		if (dbp == NULL) {
 | 
						|
			zlog_warn("%s: failed to open dabatase '%s'", __func__,
 | 
						|
				  path);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		zlog_warn("%s: failed to open database '%s': %s", __func__,
 | 
						|
			  path, sqlite3_errmsg(dbp));
 | 
						|
		if (sqlite3_close_v2(dbp) != SQLITE_OK)
 | 
						|
			zlog_warn("%s: failed to terminate database", __func__);
 | 
						|
		dbp = NULL;
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Closes the database if open. */
 | 
						|
int db_close(void)
 | 
						|
{
 | 
						|
	if (dbp == NULL)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (sqlite3_close_v2(dbp) != SQLITE_OK) {
 | 
						|
		zlog_warn("%s: failed to terminate database", __func__);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Helper function to handle formating. */
 | 
						|
static int db_vbindf(struct sqlite3_stmt *ss, const char *fmt, va_list vl)
 | 
						|
{
 | 
						|
	const char *sptr = fmt;
 | 
						|
	int column = 1;
 | 
						|
	const char *str;
 | 
						|
	void *blob;
 | 
						|
	uint64_t uinteger64;
 | 
						|
	uint32_t uinteger;
 | 
						|
	int vlen;
 | 
						|
 | 
						|
	while (*sptr) {
 | 
						|
		if (*sptr != '%') {
 | 
						|
			sptr++;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (sptr++ && *sptr == 0)
 | 
						|
			break;
 | 
						|
 | 
						|
		switch (*sptr) {
 | 
						|
		case 'i':
 | 
						|
			uinteger = va_arg(vl, uint32_t);
 | 
						|
			if (sqlite3_bind_int(ss, column++, uinteger)
 | 
						|
			    != SQLITE_OK)
 | 
						|
				return -1;
 | 
						|
			break;
 | 
						|
		case 'd':
 | 
						|
			uinteger64 = va_arg(vl, uint64_t);
 | 
						|
			if (sqlite3_bind_int64(ss, column++, uinteger64)
 | 
						|
			    != SQLITE_OK)
 | 
						|
				return -1;
 | 
						|
			break;
 | 
						|
		case 's':
 | 
						|
			str = va_arg(vl, const char *);
 | 
						|
			vlen = va_arg(vl, int);
 | 
						|
			if (sqlite3_bind_text(ss, column++, str, vlen,
 | 
						|
					      SQLITE_STATIC)
 | 
						|
			    != SQLITE_OK)
 | 
						|
				return -1;
 | 
						|
			break;
 | 
						|
		case 'b':
 | 
						|
			blob = va_arg(vl, void *);
 | 
						|
			vlen = va_arg(vl, int);
 | 
						|
			if (sqlite3_bind_blob(ss, column++, blob, vlen,
 | 
						|
					      SQLITE_STATIC)
 | 
						|
			    != SQLITE_OK)
 | 
						|
				return -1;
 | 
						|
			break;
 | 
						|
		case 'n':
 | 
						|
			if (sqlite3_bind_null(ss, column++) != SQLITE_OK)
 | 
						|
				return -1;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			zlog_warn("%s: invalid format '%c'", __func__, *sptr);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Binds values using format to the database query.
 | 
						|
 *
 | 
						|
 * Might be used to bind variables to a query, insert or update.
 | 
						|
 */
 | 
						|
int db_bindf(struct sqlite3_stmt *ss, const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list vl;
 | 
						|
	int result;
 | 
						|
 | 
						|
	va_start(vl, fmt);
 | 
						|
	result = db_vbindf(ss, fmt, vl);
 | 
						|
	va_end(vl);
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/* Prepares an statement to the database with the statement length. */
 | 
						|
struct sqlite3_stmt *db_prepare_len(const char *stmt, int stmtlen)
 | 
						|
{
 | 
						|
	struct sqlite3_stmt *ss;
 | 
						|
	int c;
 | 
						|
 | 
						|
	if (dbp == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	c = sqlite3_prepare_v2(dbp, stmt, stmtlen, &ss, NULL);
 | 
						|
	if (ss == NULL) {
 | 
						|
		zlog_warn("%s: failed to prepare (%d:%s)", __func__, c,
 | 
						|
			  sqlite3_errmsg(dbp));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return ss;
 | 
						|
}
 | 
						|
 | 
						|
/* Prepares an statement to the database. */
 | 
						|
struct sqlite3_stmt *db_prepare(const char *stmt)
 | 
						|
{
 | 
						|
	return db_prepare_len(stmt, strlen(stmt));
 | 
						|
}
 | 
						|
 | 
						|
/* Run a prepared statement. */
 | 
						|
int db_run(struct sqlite3_stmt *ss)
 | 
						|
{
 | 
						|
	int result;
 | 
						|
 | 
						|
	result = sqlite3_step(ss);
 | 
						|
	switch (result) {
 | 
						|
	case SQLITE_BUSY:
 | 
						|
		/* TODO handle busy database. */
 | 
						|
		break;
 | 
						|
 | 
						|
	case SQLITE_OK:
 | 
						|
	/*
 | 
						|
	 * SQLITE_DONE just causes confusion since it means the query went OK,
 | 
						|
	 * but it has a different value.
 | 
						|
	 */
 | 
						|
	case SQLITE_DONE:
 | 
						|
		result = SQLITE_OK;
 | 
						|
		break;
 | 
						|
 | 
						|
	case SQLITE_ROW:
 | 
						|
		/* NOTHING */
 | 
						|
		/* It is expected to receive SQLITE_ROW on search queries. */
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		zlog_warn("%s: step failed (%d:%s)", __func__, result,
 | 
						|
			  sqlite3_errstr(result));
 | 
						|
	}
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/* Helper function to load format to variables. */
 | 
						|
static int db_vloadf(struct sqlite3_stmt *ss, const char *fmt, va_list vl)
 | 
						|
{
 | 
						|
	const char *sptr = fmt;
 | 
						|
	int column = 0;
 | 
						|
	const char **str;
 | 
						|
	void *blob;
 | 
						|
	const void *blobsrc;
 | 
						|
	uint64_t *uinteger64;
 | 
						|
	uint32_t *uinteger;
 | 
						|
	int vlen;
 | 
						|
	int dlen;
 | 
						|
	int columncount;
 | 
						|
 | 
						|
	columncount = sqlite3_column_count(ss);
 | 
						|
	if (columncount == 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	while (*sptr) {
 | 
						|
		if (*sptr != '%') {
 | 
						|
			sptr++;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (sptr++ && *sptr == 0)
 | 
						|
			break;
 | 
						|
 | 
						|
		switch (*sptr) {
 | 
						|
		case 'i':
 | 
						|
			uinteger = va_arg(vl, uint32_t *);
 | 
						|
			*uinteger = sqlite3_column_int(ss, column);
 | 
						|
			break;
 | 
						|
		case 'd':
 | 
						|
			uinteger64 = va_arg(vl, uint64_t *);
 | 
						|
			*uinteger64 = sqlite3_column_int64(ss, column);
 | 
						|
			break;
 | 
						|
		case 's':
 | 
						|
			str = va_arg(vl, const char **);
 | 
						|
			*str = (const char *)sqlite3_column_text(ss, column);
 | 
						|
			break;
 | 
						|
		case 'b':
 | 
						|
			blob = va_arg(vl, void *);
 | 
						|
			vlen = va_arg(vl, int);
 | 
						|
			dlen = sqlite3_column_bytes(ss, column);
 | 
						|
			blobsrc = sqlite3_column_blob(ss, column);
 | 
						|
			memcpy(blob, blobsrc, MIN(vlen, dlen));
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			zlog_warn("%s: invalid format '%c'", __func__, *sptr);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		column++;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Function to load format from database row. */
 | 
						|
int db_loadf(struct sqlite3_stmt *ss, const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list vl;
 | 
						|
	int result;
 | 
						|
 | 
						|
	va_start(vl, fmt);
 | 
						|
	result = db_vloadf(ss, fmt, vl);
 | 
						|
	va_end(vl);
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/* Finalize query and return memory. */
 | 
						|
void db_finalize(struct sqlite3_stmt **ss)
 | 
						|
{
 | 
						|
	sqlite3_finalize(*ss);
 | 
						|
	*ss = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Execute one or more statements. */
 | 
						|
int db_execute(const char *stmt_fmt, ...)
 | 
						|
{
 | 
						|
	char stmt[BUFSIZ];
 | 
						|
	va_list ap;
 | 
						|
 | 
						|
	if (dbp == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	va_start(ap, stmt_fmt);
 | 
						|
	vsnprintf(stmt, sizeof(stmt), stmt_fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	if (sqlite3_exec(dbp, stmt, NULL, 0, NULL) != SQLITE_OK) {
 | 
						|
		zlog_warn("%s: failed to execute statement(s): %s", __func__,
 | 
						|
			  sqlite3_errmsg(dbp));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |