]> jfr.im git - irc/quakenet/newserv.git/blob - noperserv/noperserv_psql.c
Began manual merge.
[irc/quakenet/newserv.git] / noperserv / noperserv_psql.c
1 /*
2 * NOperserv v0.01
3 *
4 * A replacement for Germania's ageing Operservice2
5 * PSQL functions
6 *
7 * Copyright (C) 2005 Chris Porter.
8 * 99% of the PGSQL handling is stolen from Q9, copyright (C) David Mansell.
9 */
10
11 #include "../core/config.h"
12 #include "../core/error.h"
13 #include "../irc/irc_config.h"
14 #include "../core/events.h"
15 #include "../core/hooks.h"
16 #include "noperserv_psql.h"
17
18 #include <stdlib.h>
19 #include <sys/poll.h>
20 #include <stdarg.h>
21
22 typedef struct no_asyncquery {
23 sstring *query;
24 void *tag;
25 NoQueryHandler handler;
26 struct no_asyncquery *next;
27 } no_asyncquery;
28
29 no_asyncquery *queryhead = NULL, *querytail = NULL;
30
31 int db_connected = 0;
32 PGconn *no_dbconn;
33
34 void noperserv_db_handler(int fd, short revents);
35 void noperserv_db_status(int hooknum, void *arg);
36
37 int noperserv_connect_db(NoCreateHandler createtables) {
38 sstring *dbhost, *dbusername, *dbpassword, *dbdatabase, *dbport;
39 char connectstr[1024];
40
41 if(db_connected)
42 return 1;
43
44 /* stolen from chanserv as I'm lazy */
45 dbhost = getcopyconfigitem("noperserv", "dbhost", "localhost", HOSTLEN);
46 dbusername = getcopyconfigitem("noperserv", "dbusername", "noperserv", 20);
47 dbpassword = getcopyconfigitem("noperserv", "dbpassword", "moo", 20);
48 dbdatabase = getcopyconfigitem("noperserv", "dbdatabase", "noperserv", 20);
49 dbport = getcopyconfigitem("noperserv", "dbport", "3306", 8);
50
51 if(!dbhost || !dbusername || !dbpassword || !dbdatabase || !dbport) {
52 /* freesstring allows NULL */
53 freesstring(dbhost);
54 freesstring(dbusername);
55 freesstring(dbpassword);
56 freesstring(dbdatabase);
57 freesstring(dbport);
58 return 2;
59 }
60
61 snprintf(connectstr, sizeof(connectstr), "dbname=%s user=%s password=%s", dbdatabase->content, dbusername->content, dbpassword->content);
62
63 freesstring(dbhost);
64 freesstring(dbusername);
65 freesstring(dbpassword);
66 freesstring(dbdatabase);
67 freesstring(dbport);
68
69 Error("noperserv", ERR_INFO, "Attempting database connection: %s", connectstr);
70
71 /* Blocking connect for now.. */
72 no_dbconn = PQconnectdb(connectstr);
73
74 if (!no_dbconn || (PQstatus(no_dbconn) != CONNECTION_OK))
75 return 1;
76
77 db_connected = 1;
78
79 if(createtables)
80 createtables();
81
82 PQsetnonblocking(no_dbconn, 1);
83
84 /* this kicks ass, thanks splidge! */
85 registerhandler(PQsocket(no_dbconn), POLLIN, noperserv_db_handler);
86 registerhook(HOOK_CORE_STATSREQUEST, noperserv_db_status);
87
88 return 0;
89 }
90
91 void noperserv_db_handler(int fd, short revents) {
92 PGresult *res;
93 no_asyncquery *qqp;
94
95 if(revents & POLLIN) {
96 PQconsumeInput(no_dbconn);
97
98 if(!PQisBusy(no_dbconn)) { /* Query is complete */
99 if(queryhead->handler)
100 (queryhead->handler)(no_dbconn, queryhead->tag);
101
102 while((res = PQgetResult(no_dbconn))) {
103 switch(PQresultStatus(res)) {
104 case PGRES_TUPLES_OK:
105 Error("noperserv", ERR_WARNING, "Unhandled tuples output (query: %s)", queryhead->query->content);
106 break;
107
108 case PGRES_NONFATAL_ERROR:
109 case PGRES_FATAL_ERROR:
110 Error("noperserv", ERR_WARNING, "Unhandled error response (query: %s)", queryhead->query->content);
111 break;
112
113 default:
114 break;
115 }
116
117 PQclear(res);
118 }
119
120 /* Free the query and advance */
121 qqp = queryhead;
122 if(queryhead == querytail)
123 querytail = NULL;
124
125 queryhead = queryhead->next;
126
127 freesstring(qqp->query);
128 free(qqp);
129
130 if(queryhead) { /* Submit the next query */
131 PQsendQuery(no_dbconn, queryhead->query->content);
132 PQflush(no_dbconn);
133 }
134 }
135 }
136 }
137
138 /* sorry Q9 */
139 void noperserv_async_query(NoQueryHandler handler, void *tag, char *format, ...) {
140 char querybuf[8192];
141 va_list va;
142 int len;
143 no_asyncquery *qp;
144
145 if(!db_connected)
146 return;
147
148 va_start(va, format);
149 len = vsnprintf(querybuf, sizeof(querybuf), format, va);
150 va_end(va);
151
152 qp = (no_asyncquery *)malloc(sizeof(no_asyncquery));
153 qp->query = getsstring(querybuf, len);
154 qp->tag = tag;
155 qp->handler = handler;
156 qp->next = NULL; /* shove them at the end */
157
158 if(querytail) {
159 querytail->next = qp;
160 querytail = qp;
161 } else {
162 querytail = queryhead = qp;
163 PQsendQuery(no_dbconn, qp->query->content);
164 PQflush(no_dbconn);
165 }
166 }
167
168 void noperserv_sync_query(char *format, ...){
169 char querybuf[8192];
170 va_list va;
171 int len;
172
173 if(!db_connected)
174 return;
175
176 va_start(va, format);
177 len = vsnprintf(querybuf, sizeof(querybuf), format, va);
178 va_end(va);
179
180 PQclear(PQexec(no_dbconn, querybuf));
181 }
182
183 void noperserv_disconnect_db(void) {
184 no_asyncquery *qqp = queryhead, *nqqp;
185
186 if(!db_connected)
187 return;
188
189 /* do this first else we may get conflicts */
190 deregisterhandler(PQsocket(no_dbconn), 0);
191
192 /* Throw all the queued queries away, beware of data malloc()ed inside the query item.. */
193 while(qqp) {
194 nqqp = qqp->next;
195 freesstring(qqp->query);
196 free(qqp);
197 qqp = nqqp;
198 }
199
200 deregisterhook(HOOK_CORE_STATSREQUEST, noperserv_db_status);
201 PQfinish(no_dbconn);
202 no_dbconn = NULL; /* hmm? */
203
204 db_connected = 0;
205 }
206
207 /* more stolen code from Q9 */
208 void noperserv_db_status(int hooknum, void *arg) {
209 if ((int)arg > 10) {
210 int i = 0;
211 no_asyncquery *qqp;
212 char message[100];
213
214 if(queryhead)
215 for(qqp=queryhead;qqp;qqp=qqp->next)
216 i++;
217
218 snprintf(message, sizeof(message), "NOServ : %6d database queries queued.",i);
219
220 triggerhook(HOOK_CORE_STATSREPLY, message);
221 }
222 }