db.c
changeset 14 51eb85ae4de4
parent 13 23a242d7b7fa
child 15 2706fc514dea
equal deleted inserted replaced
13:23a242d7b7fa 14:51eb85ae4de4
    29 */
    29 */
    30 
    30 
    31 #include "volta.h"
    31 #include "volta.h"
    32 #include "db.h"
    32 #include "db.h"
    33 
    33 
    34 const unsigned short int DB_VERSION = 1;
       
    35 
       
    36 
    34 
    37 /*
    35 /*
    38  * Connect to the database specified in the 'v' global struct,
    36  * Open the database specified in the 'v' global struct,
    39  * and populate v.db with a handle to it.
    37  * setting the file descriptor.  Returns 0 on success.
    40  *
    38  *
    41  * If required, automatically handle initializing/upgrading a DB.
    39  */
    42  *
    40 short int
    43  */
       
    44 int
       
    45 db_attach( void )
    41 db_attach( void )
    46 {
    42 {
    47 	if ( v.db != NULL )          return( SQLITE_OK );    /* already attached */
    43 	/* only re-open the db at most every 10 seconds */
    48 	if ( strlen(v.dbname) == 0 ) return( SQLITE_ERROR ); /* db filename not set? */
    44 	time_t now = time( NULL );
    49 
    45 	if ( v.timer.db_lastcheck > 0 ) {
    50 	debug( 2, LOC, "Attaching to database '%s'\n", v.dbname );
    46 		if ( now - v.timer.db_lastcheck >= 10 ) {
    51 	if ( sqlite3_open( v.dbname, &v.db ) != SQLITE_OK ) {
    47 			close( v.db_fd );
    52 		debug( 1, LOC, "Error when attaching to database: %s\n", sqlite3_errmsg(v.db) );
    48 		}
    53 		return( sqlite3_errcode(v.db) );
       
    54 	}
       
    55 
       
    56 	/* check DB version */
       
    57 	unsigned short int version = db_version();
       
    58 	debug( 2, LOC, "Database version: %d\n", version );
       
    59 	if ( version != DB_VERSION ) {
       
    60 		debug( 2, LOC, "Database version mismatch: expected %hu\n", DB_VERSION );
       
    61 
       
    62 		/* We're in need of a DB initialization, or just behind.
       
    63 		 * Attempt to "stair step" upgrade to the current version. */
       
    64 		if ( version < DB_VERSION ) {
       
    65 			return( db_upgrade(version) );
       
    66 		}
       
    67 
       
    68 		/* Something else is wack. */
       
    69 		else {
    49 		else {
    70 			return( SQLITE_ERROR );
    50 			return( 0 );
    71 		}
    51 		}
    72 	}
    52 	}
    73 
    53 
    74 	/* initialize prepared statements */
    54 	debug( 2, LOC, "(Re)attaching to database '%s'\n", v.dbname );
    75 	if ( prepare_statements() != 0 ) return SQLITE_ERROR;
    55 
    76 
    56 	/* db filename not set? */
    77 	return( SQLITE_OK );
    57 	if ( strlen(v.dbname) == 0 ) {
       
    58 		debug( 1, LOC, "Error when attaching to database: DB filename unset?\n" );
       
    59 		return( -1 );
       
    60 	}
       
    61 
       
    62 	if ( (v.db_fd = open( v.dbname, O_RDONLY )) == -1 ) {
       
    63 		debug( 1, LOC, "Error when attaching to database: %s\n", strerror(errno) );
       
    64 		return( -1 );
       
    65 	}
       
    66 
       
    67 	v.timer.db_lastcheck = now;
       
    68 	return( 0 );
    78 }
    69 }
    79 
    70 
    80 
    71 
    81 /*
    72 /*
    82  * Perform automatic stair-step upgrades of DB versions
    73  * Given a rule file in ascii specified by the -c command line arg,
    83  * to get up to the current version expected from code.
    74  * convert it to a cdb after checking rules for validity.
    84  *
       
    85  */
       
    86 int
       
    87 db_upgrade( unsigned short int current_version )
       
    88 {
       
    89 	unsigned short int i = 0;
       
    90 	char user_pragma[30];
       
    91 	char sql_file[30];
       
    92 	char *upgrade_sql = NULL;
       
    93 
       
    94 	for ( i = current_version + 1; i <= DB_VERSION; i++ ) {
       
    95 		if ( i == 1 ) {
       
    96 			debug( 2, LOC, "Initializing new database.\n" );
       
    97 		}
       
    98 		else {
       
    99 			debug( 2, LOC, "Upgrading database version from %hu to %hu\n", current_version, i );
       
   100 		}
       
   101 
       
   102 		sprintf( sql_file, "sql/%d.sql", i );
       
   103 		upgrade_sql = slurp_file( sql_file );
       
   104 		if ( upgrade_sql == NULL ) return( SQLITE_ERROR );
       
   105 
       
   106 		/* If there is SQL to execute, do so and then reset for more */
       
   107 		if ( sqlite3_exec( v.db, upgrade_sql, NULL, NULL, NULL ) != SQLITE_OK ) {
       
   108 			debug( 2, LOC, "Error %s database: %s\n",
       
   109 					(i == 1 ? "initalizing" : "upgrading"), sqlite3_errmsg(v.db) );
       
   110 			return( sqlite3_errcode(v.db) );
       
   111 		}
       
   112 		free( upgrade_sql ), upgrade_sql = NULL;
       
   113 
       
   114 		/* update version metadata in DB if update was successful */
       
   115 		current_version = i;
       
   116 		sprintf( user_pragma, "PRAGMA user_version = %hu;", current_version );
       
   117 		if ( sqlite3_exec( v.db, user_pragma, NULL, NULL, NULL ) != SQLITE_OK ) {
       
   118 			debug( 2, LOC, "Error setting version: %s\n", sqlite3_errmsg(v.db) );
       
   119 			return( sqlite3_errcode(v.db) );
       
   120 		}
       
   121 	}
       
   122 
       
   123 	return( SQLITE_OK );
       
   124 }
       
   125 
       
   126 
       
   127 /*
       
   128  * Fetch and return the database's current version. or -1 on error.
       
   129  * The database should already be attached before calling this function.
       
   130  *
       
   131  */
       
   132 short int
       
   133 db_version( void )
       
   134 {
       
   135 	struct sqlite3_stmt *stmt;
       
   136 	int version = -1;
       
   137 
       
   138 	if ( sqlite3_prepare_v2( v.db, "PRAGMA user_version", -1, &stmt, NULL ) != SQLITE_OK ) {
       
   139 		debug( 2, LOC, "Error finding DB version: %s\n", sqlite3_errmsg(v.db) );
       
   140 		return( -1 );
       
   141 	}
       
   142 
       
   143 	if ( sqlite3_step( stmt ) == SQLITE_ROW )
       
   144 		version = sqlite3_column_int( stmt, 0 );
       
   145 
       
   146 	sqlite3_finalize( stmt );
       
   147 	return( version );
       
   148 }
       
   149 
       
   150 
       
   151 /*
       
   152  * Initialize the DB statements, returning 0 on success.
       
   153  *
    75  *
   154  */
    76  */
   155 unsigned short int
    77 unsigned short int
   156 prepare_statements( void )
    78 db_create_new( char *txt )
   157 {
    79 {
   158 	unsigned short int rv = 0;
    80 	struct cdb_make cdbm;
   159 
    81 
   160 	rv = rv + sqlite3_prepare_v2( v.db, DBSQL_GET_REWRITE_RULE, -1, &v.db_stmt.get_rewrite_rule, NULL );
    82 	char buf[ LINE_BUFSIZE*10 ];
   161 	if ( rv != 0 )
    83 	char tmpfile[25];
   162 		debug( 2, LOC, "Error preparing DB statement \"%s\": %s\n",
    84 	int  tmp_fd;
   163 				DBSQL_GET_REWRITE_RULE, sqlite3_errmsg(v.db) );
    85 	FILE *txt_f = NULL;
   164 
    86 	int  linenum = 0, parsed = 0;
   165 	rv = rv + sqlite3_prepare_v2( v.db, DBSQL_MATCH_REQUEST, -1, &v.db_stmt.match_request, NULL );
    87 	struct db_input *dbline;
   166 	if ( rv != 0 )
    88 
   167 		debug( 2, LOC, "Error preparing DB statement \"%s\": %s\n",
    89 	/* open temporary file */
   168 				DBSQL_MATCH_REQUEST, sqlite3_errmsg(v.db) );
    90 	debug( 0, LOC, "Creating/updating database (%s) using rules in \"%s\"\n", v.dbname, txt );
   169 
    91 	sprintf( tmpfile, "/tmp/volta-db-%d.tmp", getpid() );
   170 	return( rv );
    92 	if ( (tmp_fd = open( tmpfile,
   171 }
    93 						 O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH )) == -1 ) {
   172 
    94 		debug( 0, LOC, "Error writing temporary file: %s\n", strerror(errno) );
   173 
    95 		return( 1 );
   174 /*
    96 	}
   175  * Initialize and return a pointer to a new rewrite object.
    97 
   176  *
    98 	/* open rules file */
   177  */
    99 	if ( (txt_f = fopen( txt, "r" )) == NULL ) {
   178 rewrite *
   100 		debug( 0, LOC, "Error reading rules file: %s\n", strerror(errno) );
   179 init_rewrite( void )
   101 		return( 1 );
   180 {
   102 	}
   181 	rewrite *p_rewrite = NULL;
   103 
   182 	if ( (p_rewrite = malloc( sizeof(rewrite) )) == NULL ) {
   104 	/* init struct and start parsing lines */
   183 		debug( 5, LOC, "Unable to allocate memory for rewrite struct: %s\n", strerror(errno) );
   105 	cdb_make_start( &cdbm, tmp_fd );
   184 		return( NULL );
   106 	while ( fgets( buf, LINE_BUFSIZE*10, txt_f ) != NULL ) {
   185 	}
   107 		linenum++;
   186 
   108 
   187 	p_rewrite->scheme  = NULL;
   109 		/* skip blank lines and comments */
   188 	p_rewrite->host    = NULL;
   110 		if ( strlen(buf) == 1 || buf[0] == '#' ) continue;
   189 	p_rewrite->path    = NULL;
   111 
   190 	p_rewrite->port    = 0;
   112 		/* validate and add! */
   191 	p_rewrite->redir   = 0;
   113 		dbline = parse_dbinput( buf );
   192 
   114 		if ( dbline == NULL ) {
   193 	return( p_rewrite );
   115 			debug( 0, LOC, "Invalid rule (line %d), skipping: %s", linenum, buf );
   194 }
   116 			continue;
   195 
   117 		}
   196 
   118 
   197 #define COPY_REWRITE_ROW( INDEX ) copy_string_token( \
   119 		cdb_make_add( &cdbm, dbline->key, dbline->klen, dbline->val, dbline->vlen );
   198 			(char *)sqlite3_column_text( v.db_stmt.get_rewrite_rule, INDEX ),\
   120 		parsed++;
   199 			sqlite3_column_bytes( v.db_stmt.get_rewrite_rule, INDEX ))
   121 
   200 /*
   122 		free( dbline->key );
   201  * Given a request struct pointer, try and find the best matching
   123 		free( dbline->val );
   202  * rewrite rule, returning a pointer to a rewrite struct.
   124 		free( dbline );
   203  *
   125 	}
   204  */
   126 
   205 rewrite *
   127 	/* write indexes */
   206 prepare_rewrite( request *p_request )
   128 	fclose( txt_f );
   207 {
   129 	cdb_make_finish( &cdbm );
   208 	if ( p_request == NULL ) return( NULL );
   130 	close( tmp_fd );
   209 
   131 
   210 	unsigned short int rewrite_id = 0;
   132 	/* move cdb into place */
   211 	rewrite *p_rewrite = init_rewrite();
   133 	if ( (rename( tmpfile, v.dbname )) == -1 ) {
   212 
   134 		debug( 1, LOC, "Unable to move temp cdb into place: %s", strerror(errno) );
   213 	sqlite3_bind_text( v.db_stmt.match_request, 3, p_request->tld,       -1, SQLITE_STATIC );
   135 		return( 1 );
   214 	sqlite3_bind_text( v.db_stmt.match_request, 1, p_request->scheme,    -1, SQLITE_STATIC );
   136 	}
   215 	sqlite3_bind_text( v.db_stmt.match_request, 2, p_request->host,      -1, SQLITE_STATIC );
   137 
   216 	sqlite3_bind_text( v.db_stmt.match_request, 3, p_request->tld,       -1, SQLITE_STATIC );
   138 	debug( 0, LOC, "Added %d rules to %s.\n", parsed, v.dbname );
   217 	sqlite3_bind_text( v.db_stmt.match_request, 4, p_request->path,      -1, SQLITE_STATIC );
   139 	return( 0 );
   218 	sqlite3_bind_int(  v.db_stmt.match_request, 5, p_request->port );
   140 }
   219 	/*
   141 
   220 	sqlite3_bind_text( v.db_stmt.match_request, 6, NULL, -1, SQLITE_STATIC );
   142 
   221 	sqlite3_bind_text( v.db_stmt.match_request, 6, p_request->client_ip, -1, SQLITE_STATIC );
   143 /* Fast single record lookup.
   222 	*/
   144  * Returns a pointer to the found value or NULL if there is no match.
   223 	sqlite3_bind_text( v.db_stmt.match_request, 7, p_request->user,      -1, SQLITE_STATIC );
   145  *
   224 	sqlite3_bind_text( v.db_stmt.match_request, 8, p_request->method,    -1, SQLITE_STATIC );
   146  * The returned pointer should be freed after use.
   225 
   147  *
   226 	switch ( sqlite3_step( v.db_stmt.match_request )) {
   148  */
   227 		case SQLITE_ROW:
   149 char *
   228 			rewrite_id = sqlite3_column_int( v.db_stmt.match_request, 0 );
   150 find_record( char *key )
   229 			break;
   151 {
   230 
   152 	if ( key == NULL ) return( NULL );
   231 		case SQLITE_DONE:
   153 
   232 			break;
   154 	char *val = NULL;
   233 
   155 	cdbi_t vlen;
   234 		default:
   156 
       
   157 	if ( cdb_seek( v.db_fd, key, (int)strlen(key), &vlen) > 0 ) {
       
   158 
       
   159 		if ( (val = malloc( vlen + 1 )) == NULL ) {
       
   160 			debug( 5, LOC, "Unable to allocate memory for value storage: %s\n", strerror(errno) );
   235 			return( NULL );
   161 			return( NULL );
   236 	}
   162 		}
   237 
   163 
   238 	/* FIXME: CHECK for rewrite_rule being NULL on successful match, emit warning, continue */
   164 		cdb_bread( v.db_fd, val, vlen );
   239 
   165 		val[vlen] = '\0';
   240 	/* return early if we didn't get a matching request */
   166 		debug( 4, LOC, "Match for key '%s': %s\n", key, val );
   241 	if ( rewrite_id == 0 ) return( NULL );
   167 	}
   242 
   168 
   243 	/* pull the rewrite data, populate the struct.  only one
   169 	return val;
   244 	 * row should ever be returned for this. */
   170 }
   245 	sqlite3_bind_int( v.db_stmt.get_rewrite_rule, 1, rewrite_id );
   171 
   246 	switch ( sqlite3_step( v.db_stmt.get_rewrite_rule )) {
   172 
   247 		case SQLITE_ROW:
   173 /* 
   248 			p_rewrite->scheme = COPY_REWRITE_ROW( 1 );
   174  * Search the CDB for all occurences of the given +key+,
   249 			p_rewrite->host   = COPY_REWRITE_ROW( 2 );
   175  * populating the +results+ array with pointers to parsed rule structs.
   250 			p_rewrite->path   = COPY_REWRITE_ROW( 3 );
   176  *
   251 			p_rewrite->port   = sqlite3_column_int( v.db_stmt.get_rewrite_rule, 4 );
   177  * Returns the number of successful matches.  reset_results()
   252 			p_rewrite->redir  = sqlite3_column_int( v.db_stmt.get_rewrite_rule, 5 );
   178  * should be called after the result set is examined.
   253 			break;
   179  *
   254 
   180  */
   255 		case SQLITE_DONE:
   181 unsigned int
   256 			break;
   182 find_records( char *key, parsed **results )
   257 
   183 {
   258 		default:
   184 	if ( key == NULL ) return( 0 );
   259 			return( NULL );
   185 
   260 	}
   186 	struct cdb cdb;
   261 
   187 	struct cdb_find cdbf; /* structure to hold current find position */
   262 	return( p_rewrite );
   188 
   263 }
   189 	unsigned int match = 0;
   264 
   190 	parsed *result     = NULL;
   265 
   191 	char *val          = NULL;
   266 /*
   192 	unsigned int vlen, vpos;
   267  * Release memory used by the rewrite struct and
   193 
   268  * reset prepared statements.
   194 	/* initialize search structs */
   269  *
   195 	if ( db_attach() == -1 ) return( 0 );
   270  */
   196 	cdb_init( &cdb, v.db_fd );
   271 void
   197 	cdb_findinit( &cdbf, &cdb, key, (int)strlen(key) );
   272 finish_rewrite( rewrite *p_rewrite )
   198 
   273 {
   199 	while ( cdb_findnext( &cdbf ) > 0 && match < DB_RESULTS_MAX ) {
   274 	sqlite3_reset( v.db_stmt.get_rewrite_rule );
   200 		vpos = cdb_datapos( &cdb );
   275 	sqlite3_reset( v.db_stmt.match_request );
   201 		vlen = cdb_datalen( &cdb );
   276 	sqlite3_clear_bindings( v.db_stmt.get_rewrite_rule );
   202 
   277 	sqlite3_clear_bindings( v.db_stmt.match_request );
   203 		/* pull the value from the db */
   278 
   204 		if ( (val = calloc( vlen, sizeof(char) )) == NULL ) {
   279 	if ( p_rewrite == NULL ) return;
   205 			debug( 5, LOC, "Unable to allocate memory for DB value storage: %s\n",
   280 
   206 					strerror(errno) );
   281 	free( p_rewrite->scheme );
   207 			return( 0 );
   282 	free( p_rewrite->host );
   208 		}
   283 	free( p_rewrite->path );
   209 		cdb_read( &cdb, val, vlen, vpos );
   284 
   210 
   285 	free( p_rewrite ), p_rewrite = NULL;
   211 		/* if it parses properly, add it to the result set. */
   286 
   212 		result = parse_rule( val );
   287 	return;
   213 		if ( result != NULL ) {
   288 }
   214 			results[match] = result;
   289 
   215 			debug( 4, LOC, "DB match %d for key '%s': %s\n", match+1, key, val );
       
   216 		}
       
   217 
       
   218 		match++;
       
   219 		free( val );
       
   220 	}
       
   221 
       
   222 	cdb_free( &cdb );
       
   223 	return match;
       
   224 }
       
   225