fix possible crash on user deletion
[srvx.git] / patches / log-pgsql.diff
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     28 Sep 2003 14:16:39 -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/log.c
18 ===================================================================
19 RCS file: /cvsroot/srvx/services/src/log.c,v
20 retrieving revision 1.65
21 diff -u -r1.65 log.c
22 --- src/log.c   22 Aug 2003 00:26:21 -0000      1.65
23 +++ src/log.c   28 Sep 2003 14:16:40 -0000
24 @@ -22,6 +22,8 @@
25  #include "log.h"
26  #include "helpfile.h"
27  #include "nickserv.h"
28 +#include "ioset.h"
29 +#include <postgresql/libpq-fe.h>
30  
31  struct logDestination;
32  
33 @@ -992,6 +994,234 @@
34      ldIrc_module
35  };
36  
37 +/* pgsql: log type */
38 +
39 +struct logDest_pgsql {
40 +    struct logDestination base;
41 +    struct string_list queue;
42 +    PGconn *conn;
43 +    struct io_fd *fd;
44 +    int recurse_level;
45 +};
46 +static struct logDest_vtable ldPgsql_vtbl;
47 +
48 +static void ldPgsql_send_next_query(struct logDest_pgsql *ld) {
49 +    int res;
50 +
51 +    res = PQsendQuery(ld->conn, ld->queue.list[0]);
52 +    if (!res) {
53 +        ld->recurse_level++;
54 +        log_module(MAIN_LOG, LOG_ERROR, "Error sending query \"%s\": %s", ld->queue.list[0], PQerrorMessage(ld->conn));
55 +        ld->recurse_level--;
56 +        return;
57 +    }
58 +    res = PQflush(ld->conn);
59 +    if (res == EOF) {
60 +        ld->recurse_level++;
61 +        log_module(MAIN_LOG, LOG_ERROR, "Error flushing PgSql output: %s", PQerrorMessage(ld->conn));
62 +        ld->recurse_level--;
63 +    }
64 +}
65 +
66 +static void ldPgsql_readable(struct io_fd *fd) {
67 +    struct logDest_pgsql *ld = fd->data;
68 +    PGresult *pgres;
69 +    unsigned int ii;
70 +    int res;
71 +    ExecStatusType st;
72 +
73 +    res = PQconsumeInput(ld->conn);
74 +    if (!res) {
75 +        ld->recurse_level++;
76 +        log_module(MAIN_LOG, LOG_ERROR, "Error consuming PgSql input: %s", PQerrorMessage(ld->conn));
77 +        ld->recurse_level--;
78 +    }
79 +    if (PQisBusy(ld->conn))
80 +        return;
81 +    while ((pgres = PQgetResult(ld->conn))) {
82 +        st = PQresultStatus(pgres);
83 +        if (st != PGRES_COMMAND_OK) {
84 +            ld->recurse_level++;
85 +            log_module(MAIN_LOG, LOG_ERROR, "PgSql error in \"%s\": %s", ld->queue.list[0], PQresultErrorMessage(pgres));
86 +            ld->recurse_level--;
87 +        }
88 +        PQclear(pgres);
89 +    }
90 +    if (ld->queue.used == 1)
91 +        ld->queue.list[1] = NULL;
92 +    free(ld->queue.list[0]);
93 +    ld->queue.used--;
94 +    for (ii = 0; ii < ld->queue.used; ++ii)
95 +        ld->queue.list[ii] = ld->queue.list[ii+1];
96 +    if (ld->queue.used)
97 +        ldPgsql_send_next_query(ld);
98 +}
99 +
100 +static struct logDestination *
101 +ldPgsql_open(const char *args) {
102 +    struct logDest_pgsql *ld;
103 +    ld = calloc(1, sizeof(*ld));
104 +    ld->base.vtbl = &ldPgsql_vtbl;
105 +    ld->conn = PQconnectdb(args);
106 +    ld->recurse_level++;
107 +    if (!ld->conn) {
108 +        log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate pgsql connection");
109 +    } else if (PQstatus(ld->conn) == CONNECTION_BAD) {
110 +        log_module(MAIN_LOG, LOG_ERROR, "Pgsql connection failed: %s", PQerrorMessage(ld->conn));
111 +    } else {
112 +        int res;
113 +        res = PQsetnonblocking(ld->conn, 1);
114 +        if (res == -1)
115 +            log_module(MAIN_LOG, LOG_ERROR, "Unable to make pgsql non-blocking");
116 +        ld->fd = ioset_add(PQsocket(ld->conn));
117 +        ld->fd->data = ld;
118 +        ld->fd->connected = 1;
119 +        ld->fd->wants_reads = 1;
120 +        ld->fd->readable_cb = ldPgsql_readable;
121 +        while (PQflush(ld->conn)) ;
122 +    }
123 +    ld->recurse_level--;
124 +    return &ld->base;
125 +}
126 +
127 +static void
128 +ldPgsql_reopen(struct logDestination *self_) {
129 +    struct logDest_pgsql *self = (struct logDest_pgsql*)self_;
130 +    PQreset(self->conn);
131 +}
132 +
133 +static void
134 +ldPgsql_close(struct logDestination *self_) {
135 +    struct logDest_pgsql *self = (struct logDest_pgsql*)self_;
136 +    unsigned int ii;
137 +
138 +    PQfinish(self->conn);
139 +    ioset_close(self->fd->fd, 0);
140 +    for (ii = 0; ii < self->queue.used; ++ii)
141 +        free(self->queue.list[ii]);
142 +    self->recurse_level--;
143 +    free(self->queue.list);
144 +    free(self);
145 +}
146 +
147 +static void
148 +string_buffer_append_quoted(struct string_buffer *dest, const char *src) {
149 +    size_t len = strlen(src);
150 +    if (dest->size < dest->used + len * 2) {
151 +        if (dest->size < len * 2) {
152 +            dest->size = len * 4;
153 +        } else {
154 +            dest->size = dest->size * 2;
155 +        }
156 +        dest->list = realloc(dest->list, dest->size);
157 +    }
158 +    dest->used += PQescapeString(dest->list+dest->used, src, len);
159 +}
160 +
161 +static void
162 +string_buffer_append_time(struct string_buffer *dest, time_t when) {
163 +    struct tm gmt;
164 +    if (dest->size < dest->used + 20) {
165 +        if (dest->size < 20) {
166 +            dest->size = 40;
167 +        } else {
168 +            dest->size = dest->size * 2;
169 +        }
170 +        dest->list = realloc(dest->list, dest->size);
171 +    }
172 +    dest->used += strftime(dest->list + dest->used, dest->size - dest->used, "%Y-%m-%d %H:%M:%S", gmtime_r(&when, &gmt));
173 +}
174 +
175 +static void
176 +string_buffer_append_quoted2(struct string_buffer *dest, const char *src) {
177 +    if (src) {
178 +        string_buffer_append_string(dest, ", '");
179 +        string_buffer_append_quoted(dest, src);
180 +        string_buffer_append(dest, '\'');
181 +    } else {
182 +        string_buffer_append_string(dest, ", NULL");
183 +    }
184 +}
185 +
186 +static void
187 +pgsql_insert(struct logDest_pgsql *self, char *query) {
188 +    string_list_append(&self->queue, query);
189 +    if (self->queue.used == 1)
190 +        ldPgsql_send_next_query(self);
191 +}
192 +
193 +static void
194 +ldPgsql_audit(struct logDestination *self_, struct log_type *type, struct logEntry *entry) {
195 +    struct logDest_pgsql *self = (struct logDest_pgsql*)self_;
196 +    struct string_buffer query;
197 +
198 +    if (self->recurse_level)
199 +        return;
200 +    query.size = 512;
201 +    query.list = malloc(query.size);
202 +    query.used = 0;
203 +    string_buffer_append_printf(&query, "INSERT INTO srvx_audit (i_module, i_severity, i_bot, t_when, c_channel_name, c_user_nick, c_user_account, c_user_hostmask, c_command) VALUES (srvx_module_id('");
204 +    string_buffer_append_quoted(&query, type->name);
205 +    string_buffer_append_printf(&query, "'), %d, srvx_bot_id('", entry->slvl);
206 +    string_buffer_append_quoted(&query, entry->bot->nick);
207 +    string_buffer_append_string(&query, "'), '");
208 +    string_buffer_append_time(&query, entry->time);
209 +    string_buffer_append_string(&query, "'");
210 +    string_buffer_append_quoted2(&query, entry->channel_name);
211 +    string_buffer_append_quoted2(&query, entry->user_nick);
212 +    string_buffer_append_quoted2(&query, entry->user_account);
213 +    string_buffer_append_quoted2(&query, entry->user_hostmask);
214 +    string_buffer_append_quoted2(&query, entry->command);
215 +    string_buffer_append_string(&query, ");");
216 +    pgsql_insert(self, query.list);
217 +}
218 +
219 +static void
220 +ldPgsql_replay(struct logDestination *self_, struct log_type *type, int is_write, const char *line) {
221 +    struct logDest_pgsql *self = (struct logDest_pgsql*)self_;
222 +    struct string_buffer query;
223 +
224 +    if (self->recurse_level)
225 +        return;
226 +    query.size = 512;
227 +    query.list = malloc(query.size);
228 +    query.used = 0;
229 +    string_buffer_append_printf(&query, "INSERT INTO srvx_replay (i_module, b_is_write, c_text) VALUES (srvx_module_id('");
230 +    string_buffer_append_quoted(&query, type->name);
231 +    string_buffer_append_printf(&query, "'), %s, '", (is_write ? "true" : "false"));
232 +    string_buffer_append_quoted(&query, line);
233 +    string_buffer_append_string(&query, "');");
234 +    pgsql_insert(self, query.list);
235 +}
236 +
237 +static void
238 +ldPgsql_module(struct logDestination *self_, struct log_type *type, enum log_severity sev, const char *message) {
239 +    struct logDest_pgsql *self = (struct logDest_pgsql*)self_;
240 +    struct string_buffer query;
241 +
242 +    if (self->recurse_level)
243 +        return;
244 +    query.size = 512;
245 +    query.list = malloc(query.size);
246 +    query.used = 0;
247 +    string_buffer_append_printf(&query, "INSERT INTO srvx_log (i_module, i_severity, c_message) VALUES (srvx_module_id('");
248 +    string_buffer_append_quoted(&query, type->name);
249 +    string_buffer_append_printf(&query, "'), %d, '", sev);
250 +    string_buffer_append_quoted(&query, message);
251 +    string_buffer_append_string(&query, "');");
252 +    pgsql_insert(self, query.list);
253 +}
254 +
255 +static struct logDest_vtable ldPgsql_vtbl = {
256 +    "pgsql",
257 +    ldPgsql_open,
258 +    ldPgsql_reopen,
259 +    ldPgsql_close,
260 +    ldPgsql_audit,
261 +    ldPgsql_replay,
262 +    ldPgsql_module
263 +};
264 +
265  void
266  log_init(void)
267  {
268 @@ -1003,6 +1233,7 @@
269      dict_insert(log_dest_types, ldFile_vtbl.type_name, &ldFile_vtbl);
270      dict_insert(log_dest_types, ldStd_vtbl.type_name, &ldStd_vtbl);
271      dict_insert(log_dest_types, ldIrc_vtbl.type_name, &ldIrc_vtbl);
272 +    dict_insert(log_dest_types, ldPgsql_vtbl.type_name, &ldPgsql_vtbl);
273      conf_register_reload(log_conf_read);
274      log_default = log_register_type("*", NULL);
275      reg_exit_func(cleanup_logs);