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 |