]>
Commit | Line | Data |
---|---|---|
d76ed9a9 AS |
1 | Index: src/Makefile.am |
2 | =================================================================== | |
3 | RCS file: /cvsroot/srvx/services/src/Makefile.am,v | |
4 | retrieving revision 1.59 | |
5 | diff -u -r1.59 Makefile.am | |
6 | --- src/Makefile.am 9 Sep 2003 01:56:55 -0000 1.59 | |
7 | +++ src/Makefile.am 5 Nov 2003 14:15:46 -0000 | |
8 | @@ -9,7 +9,7 @@ | |
9 | ./expnhelp < $(srcdir)/nickserv.help.m4 > $@ | |
10 | ||
11 | EXTRA_srvx_SOURCES = proto-bahamut.c proto-common.c proto-p10.c mod-snoop.c mod-memoserv.c | |
12 | -srvx_LDADD = @MODULE_OBJS@ | |
13 | +srvx_LDADD = @MODULE_OBJS@ -lpq | |
14 | srvx_DEPENDENCIES = @MODULE_OBJS@ | |
15 | srvx_SOURCES = \ | |
16 | chanserv.c chanserv.h \ | |
17 | Index: src/helpserv.c | |
18 | =================================================================== | |
19 | RCS file: /cvsroot/srvx/services/src/helpserv.c,v | |
20 | retrieving revision 1.84 | |
21 | diff -u -r1.84 helpserv.c | |
22 | --- src/helpserv.c 5 Nov 2003 13:52:23 -0000 1.84 | |
23 | +++ src/helpserv.c 5 Nov 2003 14:15:48 -0000 | |
24 | @@ -46,6 +46,8 @@ | |
25 | #include "opserv.h" | |
26 | #include "saxdb.h" | |
27 | #include "timeq.h" | |
28 | +#include "ioset.h" | |
29 | +#include <postgresql/libpq-fe.h> | |
30 | ||
31 | #define HELPSERV_CONF_NAME "services/helpserv" | |
32 | #define HELPSERV_HELPFILE_NAME "helpserv.help" | |
33 | @@ -94,6 +96,7 @@ | |
34 | #define KEY_REQ_ON_JOIN "req_on_join" | |
35 | #define KEY_AUTO_VOICE "auto_voice" | |
36 | #define KEY_AUTO_DEVOICE "auto_devoice" | |
37 | +#define KEY_LOG_SQL "log_sql" | |
38 | ||
39 | /* General */ | |
40 | #define HSMSG_WRITE_SUCCESS "HelpServ db write completed (in "FMT_TIME_T".%03lu seconds)." | |
41 | @@ -401,6 +404,7 @@ | |
42 | const char *description; | |
43 | unsigned long db_backup_frequency; | |
44 | const char *reqlogfile; | |
45 | + PGconn *sql_log; | |
46 | } helpserv_conf; | |
47 | ||
48 | static int helpserv_enabled; | |
49 | @@ -443,6 +447,7 @@ | |
50 | unsigned int req_on_join : 1; | |
51 | unsigned int auto_voice : 1; | |
52 | unsigned int auto_devoice : 1; | |
53 | + unsigned int log_sql : 1; | |
54 | ||
55 | unsigned int helpchan_empty : 1; | |
56 | ||
57 | @@ -608,36 +613,153 @@ | |
58 | return dict_find(hs->users, hi->handle, NULL); | |
59 | } | |
60 | ||
61 | -static void helpserv_log_request(struct helpserv_request *req, const char *reason) { | |
62 | - char key[27+NICKLEN]; | |
63 | - char userhost[USERLEN+HOSTLEN+2]; | |
64 | +static struct string_list sql_queue; | |
65 | +static struct io_fd *sql_fd; | |
66 | ||
67 | - if (!reqlog_ctx || !req) | |
68 | +static void pgsql_send_next_query() { | |
69 | + int res; | |
70 | + | |
71 | + res = PQsendQuery(helpserv_conf.sql_log, sql_queue.list[0]); | |
72 | + if (!res) { | |
73 | + log_module(MAIN_LOG, LOG_ERROR, "Error sending query \"%s\": %s", sql_queue.list[0], PQerrorMessage(helpserv_conf.sql_log)); | |
74 | return; | |
75 | - if (!reason) | |
76 | - reason = ""; | |
77 | + } | |
78 | + res = PQflush(helpserv_conf.sql_log); | |
79 | + if (res == EOF) | |
80 | + log_module(MAIN_LOG, LOG_ERROR, "Error flushing PgSql output: %s", PQerrorMessage(helpserv_conf.sql_log)); | |
81 | +} | |
82 | + | |
83 | +static void pgsql_readable(UNUSED_ARG(struct io_fd *fd)) { | |
84 | + PGconn *conn; | |
85 | + PGresult *pgres; | |
86 | + unsigned int ii; | |
87 | + int res; | |
88 | + ExecStatusType st; | |
89 | + | |
90 | + conn = helpserv_conf.sql_log; | |
91 | + res = PQconsumeInput(conn); | |
92 | + if (!res) | |
93 | + log_module(MAIN_LOG, LOG_ERROR, "Error consuming PgSql input: %s", PQerrorMessage(conn)); | |
94 | + if (PQisBusy(conn)) | |
95 | + return; | |
96 | + while ((pgres = PQgetResult(conn))) { | |
97 | + st = PQresultStatus(pgres); | |
98 | + if (st != PGRES_COMMAND_OK) | |
99 | + log_module(MAIN_LOG, LOG_ERROR, "PgSql error in \"%s\": %s", sql_queue.list[0], PQresultErrorMessage(pgres)); | |
100 | + PQclear(pgres); | |
101 | + } | |
102 | + if (sql_queue.used == 1) | |
103 | + sql_queue.list[1] = NULL; | |
104 | + free(sql_queue.list[0]); | |
105 | + sql_queue.used--; | |
106 | + for (ii = 0; ii < sql_queue.used; ++ii) | |
107 | + sql_queue.list[ii] = sql_queue.list[ii+1]; | |
108 | + if (sql_queue.used) | |
109 | + pgsql_send_next_query(); | |
110 | +} | |
111 | ||
112 | - sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id); | |
113 | - saxdb_start_record(reqlog_ctx, key, 1); | |
114 | - if (req->helper) { | |
115 | - saxdb_write_string(reqlog_ctx, KEY_REQUEST_HELPER, req->helper->handle->handle); | |
116 | - saxdb_write_int(reqlog_ctx, KEY_REQUEST_ASSIGNED, req->assigned); | |
117 | +static void | |
118 | +string_buffer_append_quoted(struct string_buffer *dest, const char *src) { | |
119 | + if (src) { | |
120 | + size_t len = strlen(src); | |
121 | + string_buffer_append(dest, '\''); | |
122 | + if (dest->size < (dest->used + len * 2)) { | |
123 | + if (dest->size < len * 2) | |
124 | + dest->size = len * 4; | |
125 | + else | |
126 | + dest->size = dest->size * 2; | |
127 | + dest->list = realloc(dest->list, dest->size); | |
128 | + } | |
129 | + dest->used += PQescapeString(dest->list + dest->used, src, len); | |
130 | + string_buffer_append_string(dest, "', "); | |
131 | + } else { | |
132 | + string_buffer_append_string(dest, "NULL, "); | |
133 | } | |
134 | - if (req->handle) { | |
135 | - saxdb_write_string(reqlog_ctx, KEY_REQUEST_HANDLE, req->handle->handle); | |
136 | +} | |
137 | + | |
138 | +static void | |
139 | +string_buffer_append_time(struct string_buffer *dest, time_t when) { | |
140 | + struct tm broken_out; | |
141 | + if (!when) { | |
142 | + string_buffer_append_string(dest, "NULL, "); | |
143 | + return; | |
144 | } | |
145 | - if (req->user) { | |
146 | - saxdb_write_string(reqlog_ctx, KEY_REQUEST_NICK, req->user->nick); | |
147 | - sprintf(userhost, "%s@%s", req->user->ident, req->user->hostname); | |
148 | - saxdb_write_string(reqlog_ctx, KEY_REQUEST_USERHOST, userhost); | |
149 | + if (dest->size < dest->used + 20) { | |
150 | + if (dest->size < 20) { | |
151 | + dest->size = 40; | |
152 | + } else { | |
153 | + dest->size = dest->size * 2; | |
154 | + } | |
155 | + dest->list = realloc(dest->list, dest->size); | |
156 | } | |
157 | - saxdb_write_int(reqlog_ctx, KEY_REQUEST_OPENED, req->opened); | |
158 | - saxdb_write_int(reqlog_ctx, KEY_REQUEST_CLOSED, now); | |
159 | - saxdb_write_string(reqlog_ctx, KEY_REQUEST_CLOSEREASON, reason); | |
160 | - saxdb_write_string_list(reqlog_ctx, KEY_REQUEST_TEXT, req->text); | |
161 | - saxdb_end_record(reqlog_ctx); | |
162 | + dest->used += strftime(dest->list + dest->used, dest->size - dest->used, "'%Y-%m-%d %H:%M:%S', ", localtime_r(&when, &broken_out)); | |
163 | +} | |
164 | ||
165 | - fflush(reqlog_f); | |
166 | +static void | |
167 | +pgsql_insert(char *query) { | |
168 | + string_list_append(&sql_queue, query); | |
169 | + if (sql_queue.used == 1) | |
170 | + pgsql_send_next_query(); | |
171 | +} | |
172 | + | |
173 | +static void helpserv_log_request(struct helpserv_request *req, const char *reason) { | |
174 | + char userhost[USERLEN+HOSTLEN+2]; | |
175 | + | |
176 | + if (req->user) | |
177 | + sprintf(userhost, "%s@%s", req->user->ident, req->user->hostname); | |
178 | + else | |
179 | + userhost[0] = 0; | |
180 | + if (reqlog_ctx) { | |
181 | + char key[27+NICKLEN]; | |
182 | + sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id); | |
183 | + saxdb_start_record(reqlog_ctx, key, 1); | |
184 | + if (req->helper) { | |
185 | + saxdb_write_string(reqlog_ctx, KEY_REQUEST_HELPER, req->helper->handle->handle); | |
186 | + saxdb_write_int(reqlog_ctx, KEY_REQUEST_ASSIGNED, req->assigned); | |
187 | + } | |
188 | + if (req->handle) | |
189 | + saxdb_write_string(reqlog_ctx, KEY_REQUEST_HANDLE, req->handle->handle); | |
190 | + if (req->user) { | |
191 | + saxdb_write_string(reqlog_ctx, KEY_REQUEST_NICK, req->user->nick); | |
192 | + saxdb_write_string(reqlog_ctx, KEY_REQUEST_USERHOST, userhost); | |
193 | + } | |
194 | + saxdb_write_int(reqlog_ctx, KEY_REQUEST_OPENED, req->opened); | |
195 | + saxdb_write_int(reqlog_ctx, KEY_REQUEST_CLOSED, now); | |
196 | + saxdb_write_string(reqlog_ctx, KEY_REQUEST_CLOSEREASON, reason); | |
197 | + saxdb_write_string_list(reqlog_ctx, KEY_REQUEST_TEXT, req->text); | |
198 | + saxdb_end_record(reqlog_ctx); | |
199 | + fflush(reqlog_f); | |
200 | + } | |
201 | + if (helpserv_conf.sql_log && req->hs->log_sql) { | |
202 | + struct string_buffer query, sb; | |
203 | + unsigned int ii; | |
204 | + | |
205 | + sb.used = query.used = 0; | |
206 | + sb.size = query.size = 512; | |
207 | + query.list = malloc(query.size); | |
208 | + sb.list = malloc(sb.size); | |
209 | + string_buffer_append_string(&query, "INSERT INTO srvx_helpserv_reqs (c_bot, t_opened, t_assigned, t_closed, i_id, c_helper, c_user_account, c_user_nick, c_user_host, c_close_reason, c_text) VALUES ("); | |
210 | + string_buffer_append_quoted(&query, req->hs->helpserv->nick); | |
211 | + string_buffer_append_time(&query, req->opened); | |
212 | + string_buffer_append_time(&query, req->assigned); | |
213 | + string_buffer_append_time(&query, now); | |
214 | + string_buffer_append_printf(&query, "%lu, ", req->id); | |
215 | + string_buffer_append_quoted(&query, req->helper ? req->helper->handle->handle : NULL); | |
216 | + string_buffer_append_quoted(&query, req->handle ? req->handle->handle : NULL); | |
217 | + string_buffer_append_quoted(&query, req->user ? req->user->nick : NULL); | |
218 | + string_buffer_append_quoted(&query, req->user ? userhost : NULL); | |
219 | + string_buffer_append_quoted(&query, reason); | |
220 | + for (ii = 0; ii < req->text->used; ++ii) { | |
221 | + string_buffer_append_string(&sb, req->text->list[ii]); | |
222 | + string_buffer_append(&sb, '\n'); | |
223 | + } | |
224 | + string_buffer_append(&sb, 0); | |
225 | + string_buffer_append_quoted(&query, sb.list); | |
226 | + query.used -= 2; /* chop off ", " from append_quoted */ | |
227 | + string_buffer_append_string(&query, ");"); | |
228 | + pgsql_insert(query.list); | |
229 | + free(sb.list); | |
230 | + } | |
231 | } | |
232 | ||
233 | /* Searches for a request by number, nick, or account (num|nick|*account). | |
234 | @@ -2931,6 +3053,28 @@ | |
235 | OPTION_BINARY(hs->auto_devoice, "AutoDeVoice"); | |
236 | } | |
237 | ||
238 | +static HELPSERV_OPTION(opt_log_sql) { | |
239 | + int changed = 0; | |
240 | + if (argc > 0) { | |
241 | + if (!from_opserv) { | |
242 | + helpserv_notice(user, HSMSG_SET_NEED_OPER); | |
243 | + return 0; | |
244 | + } | |
245 | + if (enabled_string(argv[0])) { | |
246 | + hs->log_sql = 1; | |
247 | + changed = 1; | |
248 | + } else if (disabled_string(argv[0])) { | |
249 | + hs->log_sql = 0; | |
250 | + changed = 1; | |
251 | + } else { | |
252 | + helpserv_notice(user, MSG_INVALID_BINARY, argv[0]); | |
253 | + return 0; | |
254 | + } | |
255 | + } | |
256 | + helpserv_notice(user, HSMSG_STRING_VALUE, "LogSql", hs->log_sql ? "Enabled" : "Disabled"); | |
257 | + return changed; | |
258 | +} | |
259 | + | |
260 | static HELPSERV_FUNC(cmd_set) { | |
261 | helpserv_option_func_t *opt; | |
262 | ||
263 | @@ -2944,7 +3088,7 @@ | |
264 | opt_empty_interval, opt_stale_delay, opt_request_persistence, | |
265 | opt_helper_persistence, opt_notification, opt_id_wrap, | |
266 | opt_req_maxlen, opt_privmsg_only, opt_req_on_join, opt_auto_voice, | |
267 | - opt_auto_devoice | |
268 | + opt_auto_devoice, opt_log_sql | |
269 | }; | |
270 | ||
271 | helpserv_notice(user, HSMSG_QUEUE_OPTIONS); | |
272 | @@ -3267,6 +3411,7 @@ | |
273 | saxdb_write_int(ctx, KEY_REQ_ON_JOIN, hs->req_on_join); | |
274 | saxdb_write_int(ctx, KEY_AUTO_VOICE, hs->auto_voice); | |
275 | saxdb_write_int(ctx, KEY_AUTO_DEVOICE, hs->auto_devoice); | |
276 | + saxdb_write_int(ctx, KEY_LOG_SQL, hs->log_sql); | |
277 | ||
278 | /* End bot record */ | |
279 | saxdb_end_record(ctx); | |
280 | @@ -3376,6 +3521,8 @@ | |
281 | hs->auto_voice = str ? enabled_string(str) : 0; | |
282 | str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_DEVOICE, RECDB_QSTRING); | |
283 | hs->auto_devoice = str ? enabled_string(str) : 0; | |
284 | + str = database_get_data(GET_RECORD_OBJECT(br), KEY_LOG_SQL, RECDB_QSTRING); | |
285 | + hs->log_sql = str ? enabled_string(str) : 0; | |
286 | ||
287 | dict_foreach(users, user_read_helper, hs); | |
288 | ||
289 | @@ -3422,6 +3569,31 @@ | |
290 | helpserv_conf.reqlogfile = NULL; | |
291 | } | |
292 | ||
293 | + if (helpserv_conf.sql_log) { | |
294 | + PQfinish(helpserv_conf.sql_log); | |
295 | + helpserv_conf.sql_log = NULL; | |
296 | + } | |
297 | + str = database_get_data(conf_node, "sql_log", RECDB_QSTRING); | |
298 | + if (str) { | |
299 | + PGconn *conn = PQconnectdb(str); | |
300 | + if (!conn) { | |
301 | + log_module(HS_LOG, LOG_ERROR, "Unable to allocate pgsql connection"); | |
302 | + } else if (PQstatus(conn) == CONNECTION_BAD) { | |
303 | + log_module(HS_LOG, LOG_ERROR, "Pgsql connection failed: %s", PQerrorMessage(conn)); | |
304 | + PQfinish(conn); | |
305 | + } else if (PQsetnonblocking(conn, 1) == -1) { | |
306 | + log_module(HS_LOG, LOG_ERROR, "Unable to make pgsql non-blocking"); | |
307 | + PQfinish(conn); | |
308 | + } else { | |
309 | + helpserv_conf.sql_log = conn; | |
310 | + sql_fd = ioset_add(PQsocket(conn)); | |
311 | + sql_fd->connected = 1; | |
312 | + sql_fd->wants_reads = 1; | |
313 | + sql_fd->readable_cb = pgsql_readable; | |
314 | + while (PQflush(conn)) ; | |
315 | + } | |
316 | + } | |
317 | + | |
318 | if (reqlog_ctx) { | |
319 | saxdb_close_context(reqlog_ctx); | |
320 | reqlog_ctx = NULL; | |
321 | @@ -4159,16 +4331,20 @@ | |
322 | return mktime(timeinfo); | |
323 | } | |
324 | ||
325 | -/* If data != NULL, then don't add to the timeq */ | |
326 | static void helpserv_run_stats(time_t when) { | |
327 | struct tm when_s; | |
328 | + struct string_buffer query; | |
329 | struct helpserv_bot *hs; | |
330 | struct helpserv_user *hs_user; | |
331 | int i; | |
332 | dict_iterator_t it, it2; | |
333 | + char timestamp[64]; | |
334 | ||
335 | last_stats_update = when; | |
336 | localtime_r(&when, &when_s); | |
337 | + strftime(timestamp, sizeof(timestamp), "'%Y-%m-%d %H:%M:%S', ", &when_s); | |
338 | + query.size = 512; | |
339 | + | |
340 | for (it=dict_first(helpserv_bots_dict); it; it=iter_next(it)) { | |
341 | hs = iter_data(it); | |
342 | ||
343 | @@ -4195,6 +4371,18 @@ | |
344 | hs_user->reassigned_to[i] = hs_user->reassigned_to[i-1]; | |
345 | } | |
346 | ||
347 | + /* Log to SQL */ | |
348 | + if (helpserv_conf.sql_log && hs->log_sql) { | |
349 | + query.list = malloc(query.size); | |
350 | + query.used = 0; | |
351 | + string_buffer_append_string(&query, "INSERT INTO srvx_helpserv_stats (c_bot, t_weekstart, c_helper, i_time, i_picked_up, i_closed, i_reassigned_from, i_reassigned_to) VALUES("); | |
352 | + string_buffer_append_quoted(&query, hs->helpserv->nick); | |
353 | + string_buffer_append_quoted(&query, timestamp); | |
354 | + string_buffer_append_quoted(&query, hs_user->handle->handle); | |
355 | + string_buffer_append_printf(&query, "%d, %d, %d, %d, %d);", hs_user->time_per_week[0], hs_user->picked_up[0], hs_user->closed[0], hs_user->reassigned_from[0], hs_user->reassigned_to[0]); | |
356 | + pgsql_insert(query.list); | |
357 | + } | |
358 | + | |
359 | /* Reset it for this week */ | |
360 | hs_user->time_per_week[0] = hs_user->picked_up[0] = hs_user->closed[0] = hs_user->reassigned_from[0] = hs_user->reassigned_to[0] = 0; | |
361 | } | |
362 | @@ -4228,6 +4416,10 @@ | |
363 | saxdb_close_context(reqlog_ctx); | |
364 | if (reqlog_f) | |
365 | fclose(reqlog_f); | |
366 | + if (helpserv_conf.sql_log) { | |
367 | + PQfinish(helpserv_conf.sql_log); | |
368 | + helpserv_conf.sql_log = NULL; | |
369 | + } | |
370 | } | |
371 | ||
372 | void init_helpserv() { | |
373 | @@ -4293,6 +4485,7 @@ | |
374 | helpserv_define_option("REQONJOIN", opt_req_on_join); | |
375 | helpserv_define_option("AUTOVOICE", opt_auto_voice); | |
376 | helpserv_define_option("AUTODEVOICE", opt_auto_devoice); | |
377 | + helpserv_define_option("LOGSQL", opt_log_sql); | |
378 | ||
379 | helpserv_bots_dict = dict_new(); | |
380 | dict_set_free_data(helpserv_bots_dict, helpserv_free_bot); |