Fix typos/weird function names
[freeradius.git] / src / modules / rlm_sql / sql.c
1 /*
2  *  sql.c               rlm_sql - FreeRADIUS SQL Module
3  *              Main code directly taken from ICRADIUS
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Copyright 2001,2006  The FreeRADIUS server project
22  * Copyright 2000  Mike Machado <mike@innercite.com>
23  * Copyright 2000  Alan DeKok <aland@ox.org>
24  * Copyright 2001  Chad Miller <cmiller@surfsouth.com>
25  */
26
27 #include <freeradius-devel/ident.h>
28 RCSID("$Id$")
29
30 #include        <freeradius-devel/radiusd.h>
31 #include        <freeradius-devel/rad_assert.h>
32
33 #include        <sys/file.h>
34 #include        <sys/stat.h>
35
36 #include        <ctype.h>
37
38 #include        "rlm_sql.h"
39
40 #ifdef HAVE_PTHREAD_H
41 #endif
42
43 static int sql_conn_destructor(void *conn)
44 {
45         rlm_sql_handle_t *handle = conn;
46         rlm_sql_t *inst = handle->inst;
47         
48         rad_assert(inst);
49         
50         exec_trigger(NULL, inst->cs, "modules.sql.close", FALSE);
51         
52         return 0;
53 }
54
55 static void *sql_conn_create(void *ctx)
56 {
57         int rcode;
58         rlm_sql_t *inst = ctx;
59         rlm_sql_handle_t *handle;
60
61         handle = talloc_zero(ctx, rlm_sql_handle_t);
62         
63         /*
64          *      Handle requires a pointer to the SQL inst so the
65          *      destructor has access to the module configuration.
66          */
67         handle->inst = inst;
68         
69         /*
70          *      When something frees this handle the destructor set by
71          *      the driver will be called first, closing any open sockets.
72          *      Then we call our destructor to trigger an modules.sql.close
73          *      event, then all the memory is freed.
74          */
75         talloc_set_destructor((void *) handle, sql_conn_destructor);
76
77         rcode = (inst->module->sql_socket_init)(handle, inst->config);
78         if (rcode == 0) {
79                 exec_trigger(NULL, inst->cs, "modules.sql.open", FALSE);
80                 
81                 return handle;
82         }
83
84         exec_trigger(NULL, inst->cs, "modules.sql.fail", TRUE);
85
86         /*
87          *      Destroy any half opened connections.
88          */
89         talloc_free(handle);
90         return NULL;
91 }
92
93 /*
94  *      @todo Calls to this should eventually go away.
95  */
96 static int sql_conn_delete(UNUSED void *ctx, void *conn)
97 {       
98         return talloc_free(conn);
99 }
100
101 /*************************************************************************
102  *
103  *      Function: sql_socket_pool_init
104  *
105  *      Purpose: Connect to the sql server, if possible
106  *
107  *************************************************************************/
108 int sql_socket_pool_init(rlm_sql_t * inst)
109 {
110         inst->pool = fr_connection_pool_init(inst->cs, inst,
111                                              sql_conn_create,
112                                              NULL,
113                                              sql_conn_delete);
114         if (!inst->pool) return -1;
115
116         return 1;
117 }
118
119 /*************************************************************************
120  *
121  *     Function: sql_poolfree
122  *
123  *     Purpose: Clean up and free sql pool
124  *
125  *************************************************************************/
126 void sql_poolfree(rlm_sql_t * inst)
127 {
128         fr_connection_pool_delete(inst->pool);
129 }
130
131
132 /*************************************************************************
133  *
134  *      Function: sql_get_socket
135  *
136  *      Purpose: Return a SQL handle from the connection pool
137  *
138  *************************************************************************/
139 rlm_sql_handle_t * sql_get_socket(rlm_sql_t * inst)
140 {
141         return fr_connection_get(inst->pool);
142 }
143
144 /*************************************************************************
145  *
146  *      Function: sql_release_socket
147  *
148  *      Purpose: Frees a SQL handle back to the connection pool
149  *
150  *************************************************************************/
151 int sql_release_socket(rlm_sql_t * inst, rlm_sql_handle_t * handle)
152 {
153         fr_connection_release(inst->pool, handle);
154         return 0;
155 }
156
157
158 /*************************************************************************
159  *
160  *      Function: sql_userparse
161  *
162  *      Purpose: Read entries from the database and fill VALUE_PAIR structures
163  *
164  *************************************************************************/
165 int sql_userparse(VALUE_PAIR **head, rlm_sql_row_t row)
166 {
167         VALUE_PAIR *vp;
168         const char *ptr, *value;
169         char buf[MAX_STRING_LEN];
170         char do_xlat = 0;
171         FR_TOKEN token, operator = T_EOL;
172
173         /*
174          *      Verify the 'Attribute' field
175          */
176         if (row[2] == NULL || row[2][0] == '\0') {
177                 radlog(L_ERR, "rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row.");
178                 return -1;
179         }
180
181         /*
182          *      Verify the 'op' field
183          */
184         if (row[4] != NULL && row[4][0] != '\0') {
185                 ptr = row[4];
186                 operator = gettoken(&ptr, buf, sizeof(buf));
187                 if ((operator < T_OP_ADD) ||
188                     (operator > T_OP_CMP_EQ)) {
189                         radlog(L_ERR, "rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]);
190                         return -1;
191                 }
192
193         } else {
194                 /*
195                  *  Complain about empty or invalid 'op' field
196                  */
197                 operator = T_OP_CMP_EQ;
198                 radlog(L_ERR, "rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]);
199                 radlog(L_ERR, "rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect.");
200         }
201
202         /*
203          *      The 'Value' field may be empty or NULL
204          */
205         value = row[3];
206         /*
207          *      If we have a new-style quoted string, where the
208          *      *entire* string is quoted, do xlat's.
209          */
210         if (row[3] != NULL &&
211            ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) &&
212            (row[3][0] == row[3][strlen(row[3])-1])) {
213
214                 token = gettoken(&value, buf, sizeof(buf));
215                 switch (token) {
216                 /*
217                  *      Take the unquoted string.
218                  */
219                 case T_SINGLE_QUOTED_STRING:
220                 case T_DOUBLE_QUOTED_STRING:
221                         value = buf;
222                         break;
223
224                 /*
225                  *      Mark the pair to be allocated later.
226                  */
227                 case T_BACK_QUOTED_STRING:
228                         value = NULL;
229                         do_xlat = 1;
230                         break;
231
232                 /*
233                  *      Keep the original string.
234                  */
235                 default:
236                         value = row[3];
237                         break;
238                 }
239         }
240
241         /*
242          *      Create the pair
243          */
244         vp = pairmake(row[2], NULL, operator);
245         if (!vp) {
246                 radlog(L_ERR, "rlm_sql: Failed to create the pair: %s",
247                        fr_strerror());
248                 return -1;
249         }
250         
251         if (do_xlat) {
252                 if (pairmark_xlat(vp, value) < 0) {
253                         radlog(L_ERR, "rlm_sql: Error marking pair for xlat");
254                         
255                         pairbasicfree(vp);
256                         return -1;
257                 }
258         } else {
259                 if (pairparsevalue(vp, value) < 0) {
260                         radlog(L_ERR, "rlm_sql: Error parsing value");
261                         
262                         pairbasicfree(vp);
263                         return -1;
264                 }
265         }
266
267         /*
268          *      Add the pair into the packet
269          */
270         pairadd(head, vp);
271         return 0;
272 }
273
274
275 /*************************************************************************
276  *
277  *      Function: rlm_sql_fetch_row
278  *
279  *      Purpose: call the module's sql_fetch_row and implement re-connect
280  *
281  *************************************************************************/
282 int rlm_sql_fetch_row(rlm_sql_handle_t **handle, rlm_sql_t *inst)
283 {
284         int ret;
285
286         if (!*handle || !(*handle)->conn) {
287                 return -1;
288         }
289         
290         /* 
291          * We can't implement reconnect logic here, because the caller may require
292          * the original connection to free up queries or result sets associated with
293          * that connection.
294          */
295         ret = (inst->module->sql_fetch_row)(*handle, inst->config);
296         
297         if (ret < 0) {
298                 radlog(L_ERR, "rlm_sql (%s): Error fetching row: %s", inst->config->xlat_name,
299                            (inst->module->sql_error)(*handle, inst->config));
300         }
301
302         return ret;
303 }
304
305 /*************************************************************************
306  *
307  *      Function: rlm_sql_query
308  *
309  *      Purpose: call the module's sql_query and implement re-connect
310  *
311  *************************************************************************/
312 int rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char *query)
313 {
314         int ret;
315
316         /*
317          *      If there's no query, return an error.
318          */
319         if (!query || !*query) {
320                 return -1;
321         }
322
323         if (!*handle || !(*handle)->conn) {
324                 ret = -1;
325                 goto sql_down;
326         }
327         
328         while (1) {
329                 DEBUG("rlm_sql (%s): Executing query: '%s'",
330                       inst->config->xlat_name, query);
331
332                 ret = (inst->module->sql_query)(*handle, inst->config, query);
333                 /*
334                  * Run through all available sockets until we exhaust all existing
335                  * sockets in the pool and fail to establish a *new* connection.
336                  */
337                 if (ret == SQL_DOWN) {
338                         sql_down:
339                         *handle = fr_connection_reconnect(inst->pool, *handle);
340                         if (!*handle) return SQL_DOWN;
341                         
342                         continue;
343                 }
344                 
345                 if (ret < 0) {
346                         radlog(L_ERR,
347                                    "rlm_sql (%s): Database query error: '%s'",
348                                    inst->config->xlat_name,
349                                    (inst->module->sql_error)(*handle, inst->config));
350                 }
351                 
352                 return ret;
353         }
354 }
355
356 /*************************************************************************
357  *
358  *      Function: rlm_sql_select_query
359  *
360  *      Purpose: call the module's sql_select_query and implement re-connect
361  *
362  *************************************************************************/
363 int rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char *query)
364 {
365         int ret;
366
367         /*
368          *      If there's no query, return an error.
369          */
370         if (!query || !*query) {
371                 return -1;
372         }
373
374         if (!*handle || !(*handle)->conn) {
375                 ret = -1;
376                 goto sql_down;
377         }
378         
379         while (1) {
380                 DEBUG("rlm_sql (%s): Executing query: '%s'",
381                       inst->config->xlat_name, query);
382
383                 ret = (inst->module->sql_select_query)(*handle, inst->config, query);
384                 /*
385                  * Run through all available sockets until we exhaust all existing
386                  * sockets in the pool and fail to establish a *new* connection.
387                  */
388                 if (ret == SQL_DOWN) {
389                         sql_down:
390                         *handle = fr_connection_reconnect(inst->pool, *handle);
391                         if (!*handle) return SQL_DOWN;
392                         
393                         continue;
394                 }
395                 
396                 if (ret < 0) {
397                         radlog(L_ERR,
398                                    "rlm_sql (%s): Database query error '%s'",
399                                    inst->config->xlat_name,
400                                    (inst->module->sql_error)(*handle, inst->config));
401                 }
402                 
403                 return ret;
404         }
405 }
406
407
408 /*************************************************************************
409  *
410  *      Function: sql_getvpdata
411  *
412  *      Purpose: Get any group check or reply pairs
413  *
414  *************************************************************************/
415 int sql_getvpdata(rlm_sql_t * inst, rlm_sql_handle_t **handle, VALUE_PAIR **pair, char *query)
416 {
417         rlm_sql_row_t row;
418         int     rows = 0;
419
420         if (rlm_sql_select_query(handle, inst, query)) {
421                 return -1;
422         }
423
424         while (rlm_sql_fetch_row(handle, inst) == 0) {
425                 row = (*handle)->row;
426                 if (!row)
427                         break;
428                 if (sql_userparse(pair, row) != 0) {
429                         radlog(L_ERR, "rlm_sql (%s): Error getting data from database", inst->config->xlat_name);
430                         
431                         (inst->module->sql_finish_select_query)(*handle, inst->config);
432                         
433                         return -1;
434                 }
435                 rows++;
436         }
437         (inst->module->sql_finish_select_query)(*handle, inst->config);
438
439         return rows;
440 }
441
442 /*
443  *      Log the query to a file.
444  */
445 void rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request,
446                        sql_acct_section_t *section, char *query)
447 {
448         int fd;
449         const char *filename = NULL;
450         char buffer[8192];
451
452         if (section) filename = section->logfile;
453
454         if (!filename) filename = inst->config->logfile;
455
456         if (!filename) return;
457
458         if (!radius_xlat(buffer, sizeof(buffer), filename, request, NULL, NULL)) {
459                 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
460                        inst->config->xlat_name);
461                 return;
462         }
463
464         fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0666);
465         if (fd < 0) {
466                 radlog(L_ERR, "rlm_sql (%s): Couldn't open logfile '%s': %s",
467                        inst->config->xlat_name, buffer, strerror(errno));
468                 return;
469         }
470
471         if ((rad_lockfd(fd, MAX_QUERY_LEN) < 0) ||
472             (write(fd, query, strlen(query)) < 0) ||
473             (write(fd, ";\n", 2) < 0)) {
474                 radlog(L_ERR, "rlm_sql (%s): Failed writing to logfile '%s': %s",
475                        inst->config->xlat_name, buffer, strerror(errno));
476         }
477         close(fd);              /* and release the lock */
478 }