]> jfr.im git - irc/quakenet/newserv.git/blob - sqlite/sqlite.c
GLINES: fix null pointer deref in trustgline / trustungline
[irc/quakenet/newserv.git] / sqlite / sqlite.c
1 /*
2 * SQLite module
3 */
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <string.h>
9
10 #ifndef __USE_POSIX199309
11 #define __USE_POSIX199309
12 #endif
13
14 #include <time.h>
15
16 #include "../core/config.h"
17 #include "../core/error.h"
18 #include "../core/events.h"
19 #include "../core/hooks.h"
20 #include "../lib/version.h"
21 #include "../lib/strlfunc.h"
22 #include "../core/nsmalloc.h"
23 #include "../core/schedule.h"
24
25 #define BUILDING_DBAPI
26 #include "../dbapi/dbapi.h"
27
28 #include "sqlite.h"
29
30 MODULE_VERSION("");
31
32 static int dbconnected = 0;
33 static struct sqlite3 *conn;
34 static SQLiteModuleIdentifier modid;
35
36 struct sqlitequeue {
37 sqlite3_stmt *statement;
38 SQLiteQueryHandler handler;
39 void *tag;
40 int identifier;
41 struct sqlitequeue *next;
42 };
43
44 static struct sqlitequeue *head, *tail;
45 static int queuesize;
46 static void *processsched;
47 static int inited;
48
49 #define SYNC_MODE "OFF"
50
51 static void sqlitequeueprocessor(void *arg);
52 static void dbstatus(int hooknum, void *arg);
53
54 void _init(void) {
55 sstring *dbfile;
56 int rc;
57
58 dbfile = getcopyconfigitem("sqlite", "file", "newserv.db", 100);
59
60 if(!dbfile) {
61 Error("sqlite", ERR_ERROR, "Unable to get config settings.");
62 return;
63 }
64
65 processsched = schedulerecurring(time(NULL), 0, 1, &sqlitequeueprocessor, NULL);
66 if(!processsched) {
67 Error("sqlite", ERR_ERROR, "Unable to schedule query processor.");
68 freesstring(dbfile);
69 return;
70 }
71
72
73 if(sqlite3_initialize() != SQLITE_OK) {
74 Error("sqlite", ERR_ERROR, "Unable to initialise sqlite");
75 return;
76 }
77 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
78
79 inited = 1;
80
81 rc = sqlite3_open(dbfile->content, &conn);
82 freesstring(dbfile);
83
84 if(rc) {
85 Error("sqlite", ERR_ERROR, "Unable to connect to database: %s", sqlite3_errmsg(conn));
86 deleteschedule(processsched, &sqlitequeueprocessor, NULL);
87 return;
88 }
89
90 dbconnected = 1;
91
92 sqliteasyncqueryf(0, NULL, NULL, 0, "PRAGMA synchronous=" SYNC_MODE ";");
93 registerhook(HOOK_CORE_STATSREQUEST, dbstatus);
94 }
95
96 void _fini(void) {
97 struct sqlitequeue *q, *nq;
98
99 if(sqliteconnected()) {
100 deregisterhook(HOOK_CORE_STATSREQUEST, dbstatus);
101 deleteschedule(processsched, &sqlitequeueprocessor, NULL);
102
103 /* we assume every module that's being unloaded
104 * has us as a dependency and will have cleaned up
105 * their queries by using freeid..
106 */
107 for(q=head;q;q=nq) {
108 nq = q->next;
109 sqlite3_finalize(q->statement);
110 nsfree(POOL_SQLITE, q);
111 }
112
113 sqlite3_close(conn);
114
115 dbconnected = 0;
116 }
117
118 if(inited) {
119 sqlite3_shutdown();
120 inited = 0;
121 }
122
123 nscheckfreeall(POOL_SQLITE);
124 }
125
126 /* busy processing is done externally as that varies depending on what you are... */
127 static void processstatement(int rc, sqlite3_stmt *s, SQLiteQueryHandler handler, void *tag, char *querybuf) {
128 if(handler) { /* the handler deals with the cleanup */
129 SQLiteResult *r;
130
131 if((rc != SQLITE_ROW) && (rc != SQLITE_DONE)) {
132 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
133 handler(NULL, tag);
134 return;
135 }
136
137 r = (SQLiteResult *)nsmalloc(POOL_SQLITE, sizeof(SQLiteResult));
138 r->r = s;
139 r->first = 1;
140 if(rc == SQLITE_ROW) {
141 r->final = 0;
142 } else if(rc == SQLITE_DONE) {
143 r->final = 1;
144 }
145 handler(r, tag);
146 } else {
147 if(rc == SQLITE_ROW) {
148 Error("sqlite", ERR_WARNING, "Unhandled data from query: %s", querybuf);
149 } else if(rc != SQLITE_DONE) {
150 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
151 }
152 sqlite3_finalize(s);
153 }
154 }
155
156 static void pushqueue(sqlite3_stmt *s, int identifier, SQLiteQueryHandler handler, void *tag) {
157 struct sqlitequeue *q = (struct sqlitequeue *)nsmalloc(POOL_SQLITE, sizeof(struct sqlitequeue));
158
159 q->identifier = identifier;
160 q->handler = handler;
161 q->tag = tag;
162 q->next = NULL;
163 q->statement = s;
164
165 if(!tail) {
166 head = q;
167 } else {
168 tail->next = q;
169 }
170 tail = q;
171 queuesize++;
172 }
173
174 static struct sqlitequeue *peekqueue(void) {
175 return head;
176 }
177
178 /* a weird pop that doesn't return the value */
179 static void popqueue(void) {
180 struct sqlitequeue *q;
181 if(!head)
182 return;
183
184 q = head;
185 if(head == tail) {
186 head = NULL;
187 tail = NULL;
188 } else {
189 head = head->next;
190 }
191
192 nsfree(POOL_SQLITE, q);
193 queuesize--;
194 return;
195 }
196
197 void sqliteasyncqueryf(int identifier, SQLiteQueryHandler handler, void *tag, int flags, char *format, ...) {
198 char querybuf[8192];
199 int len;
200 int rc;
201 sqlite3_stmt *s;
202 va_list va;
203
204 if(!sqliteconnected())
205 return;
206
207 va_start(va, format);
208 len = vsnprintf(querybuf, sizeof(querybuf), format, va);
209 va_end(va);
210
211 rc = sqlite3_prepare(conn, querybuf, -1, &s, NULL);
212 if(rc != SQLITE_OK) {
213 if(flags != DB_CREATE)
214 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
215 if(handler)
216 handler(NULL, tag);
217 return;
218 }
219
220 if(head) { /* stuff already queued */
221 pushqueue(s, identifier, handler, tag);
222 return;
223 }
224
225 rc = sqlite3_step(s);
226 if(rc == SQLITE_BUSY) {
227 pushqueue(s, identifier, handler, tag);
228 return;
229 }
230
231 processstatement(rc, s, handler, tag, querybuf);
232 }
233
234 int sqliteconnected(void) {
235 return dbconnected;
236 }
237
238 size_t sqliteescapestring(char *buf, char *src, unsigned int len) {
239 unsigned int i;
240 char *p, *d;
241
242 for(p=src,d=buf,i=0;i<len;i++,p++) {
243 if(*p == '\'')
244 *d++ = *p;
245 *d++ = *p;
246 }
247 *d = '\0';
248
249 return d - buf;
250 }
251
252 SQLiteResult *sqlitegetresult(SQLiteConn *r) {
253 return r;
254 }
255
256 int sqlitefetchrow(SQLiteResult *r) {
257 int rc, v;
258 struct timespec t;
259
260 if(!r || !r->r || r->final)
261 return 0;
262
263 if(r->first) { /* we've extracted the first row already */
264 r->first = 0;
265 return 1;
266 }
267
268 t.tv_sec = 0;
269 for(;;) {
270 rc = sqlite3_step(r->r);
271 if(rc != SQLITE_BUSY)
272 break;
273
274 v = rand() % 50 + 50;
275 t.tv_nsec = v * 1000000;
276 Error("sqlite", ERR_WARNING, "SQLite is busy, retrying in %fs...", (double)v / 1000);
277 nanosleep(&t, NULL);
278 }
279
280 if(rc == SQLITE_DONE) {
281 r->final = 1;
282 return 0;
283 }
284
285 if(rc != SQLITE_ROW) {
286 Error("sqlite", ERR_WARNING, "SQL error %d: %s", rc, sqlite3_errmsg(conn));
287 r->final = 1;
288 return 0;
289 }
290
291 return 1;
292 }
293
294 void sqliteclear(SQLiteResult *r) {
295 if(!r)
296 return;
297
298 if(r->r)
299 sqlite3_finalize(r->r);
300
301 nsfree(POOL_SQLITE, r);
302 }
303
304 int sqlitequerysuccessful(SQLiteResult *r) {
305 if(r && r->r)
306 return 1;
307
308 return 0;
309 }
310
311 struct sqlitetableloader {
312 SQLiteQueryHandler init, data, fini;
313 void *tag;
314 char tablename[];
315 };
316
317 static void loadtablerows(SQLiteConn *c, void *tag) {
318 struct sqlitetableloader *t = (struct sqlitetableloader *)tag;
319
320 if(!c) { /* pqsql doesnt call the handlers so we don't either */
321 nsfree(POOL_SQLITE, t);
322 return;
323 }
324
325 /* the handlers do all the checking and cleanup */
326 if(t->init)
327 (t->init)(NULL, t->tag);
328
329 (t->data)(c, t->tag);
330
331 if(t->fini)
332 (t->fini)(NULL, t->tag);
333
334 nsfree(POOL_SQLITE, t);
335 }
336
337 static void loadtablecount(SQLiteConn *c, void *tag) {
338 struct sqlitetableloader *t = (struct sqlitetableloader *)tag;
339 SQLiteResult *r = NULL;
340
341 if(!c) { /* unloaded */
342 nsfree(POOL_SQLITE, t);
343 return;
344 }
345
346 if(!(r = sqlitegetresult(c)) || !sqlitefetchrow(r)) {
347 Error("sqlite", ERR_ERROR, "Error getting row count for %s.", t->tablename);
348 nsfree(POOL_SQLITE, t);
349
350 if(r)
351 sqliteclear(r);
352 return;
353 }
354
355 Error("sqlite", ERR_INFO, "Found %s entries in table %s, loading...", (char *)sqlite3_column_text(r->r, 0), t->tablename);
356 sqliteclear(r);
357
358 sqliteasyncqueryf(0, loadtablerows, t, 0, "SELECT * FROM %s", t->tablename);
359 }
360
361 void sqliteloadtable(char *tablename, SQLiteQueryHandler init, SQLiteQueryHandler data, SQLiteQueryHandler fini, void *tag) {
362 struct sqlitetableloader *t;
363 int len;
364
365 if(!sqliteconnected())
366 return;
367
368 len = strlen(tablename);
369
370 t = (struct sqlitetableloader *)nsmalloc(POOL_SQLITE, sizeof(struct sqlitetableloader) + len + 1);
371 memcpy(t->tablename, tablename, len + 1);
372 t->init = init;
373 t->data = data;
374 t->fini = fini;
375 t->tag = tag;
376
377 sqliteasyncqueryf(0, loadtablecount, t, 0, "SELECT COUNT(*) FROM %s", tablename);
378 }
379
380 void sqliteattach(char *schema) {
381 sqliteasyncqueryf(0, NULL, NULL, 0, "ATTACH DATABASE '%s.db' AS %s", schema, schema);
382 sqliteasyncqueryf(0, NULL, NULL, 0, "PRAGMA %s.synchronous=" SYNC_MODE, schema);
383 }
384
385 void sqlitedetach(char *schema) {
386 sqliteasyncqueryf(0, NULL, NULL, 0, "DETACH DATABASE %s", schema);
387 }
388
389 int sqlitegetid(void) {
390 modid++;
391 if(modid == 0)
392 modid = 1;
393
394 return modid;
395 }
396
397 void sqlitefreeid(int id) {
398 struct sqlitequeue *q, *pq;
399 if(id == 0)
400 return;
401
402 for(pq=NULL,q=head;q;) {
403 if(q->identifier == id) {
404 if(pq) {
405 pq->next = q->next;
406 if(q == tail)
407 tail = pq;
408 } else { /* head */
409 head = q->next;
410 if(q == tail)
411 tail = NULL;
412 }
413 sqlite3_finalize(q->statement);
414
415 q->handler(NULL, q->tag);
416 nsfree(POOL_SQLITE, q);
417
418 queuesize--;
419 if(pq) {
420 q = pq->next;
421 } else {
422 q = head;
423 }
424 } else {
425 pq = q;
426 q = q->next;
427 }
428 }
429 }
430
431 static void sqlitequeueprocessor(void *arg) {
432 struct sqlitequeue *q = peekqueue();
433
434 while(q) {
435 int rc = sqlite3_step(q->statement);
436 if(rc == SQLITE_BUSY)
437 return;
438
439 processstatement(rc, q->statement, q->handler, q->tag, "??");
440 popqueue();
441
442 q = peekqueue();
443 }
444 }
445
446 static void dbstatus(int hooknum, void *arg) {
447 if((long)arg > 10) {
448 char message[100];
449
450 snprintf(message, sizeof(message), "SQLite : %6d queries queued.", queuesize);
451 triggerhook(HOOK_CORE_STATSREPLY, message);
452 }
453 }
454
455 sqlite3 *sqlitegetconn(void) {
456 return conn;
457 }