document rlm_otp fd leak fix
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_iodbc / sql_iodbc.c
1 /*
2  * sql_iodbc.c  iODBC support for FreeRadius
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Jeff Carneal <jeff@apex.net>
22  */
23
24 #include <freeradius-devel/autoconf.h>
25
26 #include <stdio.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <isql.h>
32 #include <isqlext.h>
33 #include <sqltypes.h>
34
35 #include <freeradius-devel/radiusd.h>
36 #include "rlm_sql.h"
37
38 typedef struct rlm_sql_iodbc_sock {
39         HENV    env_handle;
40         HDBC    dbc_handle;
41         HSTMT   stmt_handle;
42         int             id;
43         SQL_ROW row;
44
45         struct sql_socket *next;
46
47         void    *conn;
48 } rlm_sql_iodbc_sock;;
49
50 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config);
51 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config);
52
53 /*************************************************************************
54  *
55  *      Function: sql_init_socket
56  *
57  *      Purpose: Establish connection to the db
58  *
59  *************************************************************************/
60 static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
61
62         rlm_sql_iodbc_sock *iodbc_sock;
63         SQLRETURN rcode;
64
65         if (!sqlsocket->conn) {
66                 sqlsocket->conn = (rlm_sql_iodbc_sock *)rad_malloc(sizeof(rlm_sql_iodbc_sock));
67                 if (!sqlsocket->conn) {
68                         return -1;
69                 }
70         }
71         iodbc_sock = sqlsocket->conn;
72         memset(iodbc_sock, 0, sizeof(*iodbc_sock));
73
74         rcode = SQLAllocEnv(&iodbc_sock->env_handle);
75         if (!SQL_SUCCEEDED(rcode)) {
76                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocEnv failed:  %s",
77                                 sql_error(sqlsocket, config));
78                 return -1;
79         }
80
81         rcode = SQLAllocConnect(iodbc_sock->env_handle,
82                                 &iodbc_sock->dbc_handle);
83         if (!SQL_SUCCEEDED(rcode)) {
84                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocConnect failed:  %s",
85                                 sql_error(sqlsocket, config));
86                 return -1;
87         }
88
89         rcode = SQLConnect(iodbc_sock->dbc_handle, config->sql_db,
90                            SQL_NTS, config->sql_login, SQL_NTS,
91                            config->sql_password, SQL_NTS);
92         if (!SQL_SUCCEEDED(rcode)) {
93                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLConnectfailed:  %s",
94                                 sql_error(sqlsocket, config));
95                 return -1;
96         }
97
98         return 0;
99 }
100
101 /*************************************************************************
102  *
103  *      Function: sql_destroy_socket
104  *
105  *      Purpose: Free socket and private connection data
106  *
107  *************************************************************************/
108 static int sql_destroy_socket(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config)
109 {
110         free(sqlsocket->conn);
111         sqlsocket->conn = NULL;
112         return 0;
113 }
114
115 /*************************************************************************
116  *
117  *      Function: sql_query
118  *
119  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
120  *               the database.
121  *
122  *************************************************************************/
123 static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
124
125         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
126         SQLRETURN rcode;
127
128         rcode = SQLAllocStmt(iodbc_sock->dbc_handle,
129                              &iodbc_sock->stmt_handle);
130         if (!SQL_SUCCEEDED(rcode)) {
131                 radlog(L_CONS|L_ERR, "sql_create_socket: SQLAllocStmt failed:  %s",
132                                 sql_error(sqlsocket, config));
133                 return -1;
134         }
135
136         if (config->sqltrace)
137                 radlog(L_DBG, "rlm_sql:  %s", querystr);
138         if (iodbc_sock->dbc_handle == NULL) {
139                 radlog(L_ERR, "sql_query:  Socket not connected");
140                 return -1;
141         }
142
143         rcode = SQLExecDirect(iodbc_sock->stmt_handle, querystr, SQL_NTS);
144         if (!SQL_SUCCEEDED(rcode)) {
145                 radlog(L_CONS|L_ERR, "sql_query: failed:  %s",
146                                 sql_error(sqlsocket, config));
147                 return -1;
148         }
149
150         return 0;
151 }
152
153
154 /*************************************************************************
155  *
156  *      Function: sql_select_query
157  *
158  *      Purpose: Issue a select query to the database
159  *
160  *************************************************************************/
161 static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
162
163         int numfields = 0;
164         int i=0;
165         char **row=NULL;
166         SQLINTEGER len=0;
167         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
168
169         if(sql_query(sqlsocket, config, querystr) < 0) {
170                 return -1;
171         }
172
173         numfields = sql_num_fields(sqlsocket, config);
174
175         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
176         memset(row, 0, (sizeof(char *) * (numfields)));
177         row[numfields] = NULL;
178
179         for(i=1; i<=numfields; i++) {
180                 SQLColAttributes(iodbc_sock->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH,
181                                                                                 NULL, 0, NULL, &len);
182                 len++;
183
184                 /*
185                  * Allocate space for each column
186                  */
187                 row[i-1] = (SQLCHAR*)rad_malloc((int)len);
188
189                 /*
190                  * This makes me feel dirty, but, according to Microsoft, it works.
191                  * Any ODBC datatype can be converted to a 'char *' according to
192                  * the following:
193                  *
194                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
195                  */
196                 SQLBindCol(iodbc_sock->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
197         }
198
199         iodbc_sock->row = row;
200
201         return 0;
202 }
203
204
205 /*************************************************************************
206  *
207  *      Function: sql_store_result
208  *
209  *      Purpose: database specific store_result function. Returns a result
210  *               set for the query.
211  *
212  *************************************************************************/
213 static int sql_store_result(UNUSED SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
214
215         return 0;
216 }
217
218
219 /*************************************************************************
220  *
221  *      Function: sql_num_fields
222  *
223  *      Purpose: database specific num_fields function. Returns number
224  *               of columns from query
225  *
226  *************************************************************************/
227 static int sql_num_fields(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
228
229         SQLSMALLINT count=0;
230         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
231
232         SQLNumResultCols(iodbc_sock->stmt_handle, &count);
233
234         return (int)count;
235 }
236
237 /*************************************************************************
238  *
239  *      Function: sql_num_rows
240  *
241  *      Purpose: database specific num_rows. Returns number of rows in
242  *               query
243  *
244  *************************************************************************/
245 static int sql_num_rows(UNUSED SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
246         /*
247          * I presume this function is used to determine the number of
248          * rows in a result set *before* fetching them.  I don't think
249          * this is possible in ODBC 2.x, but I'd be happy to be proven
250          * wrong.  If you know how to do this, email me at jeff@apex.net
251          */
252         return 0;
253 }
254
255
256 /*************************************************************************
257  *
258  *      Function: sql_fetch_row
259  *
260  *      Purpose: database specific fetch_row. Returns a SQL_ROW struct
261  *               with all the data for the query in 'sqlsocket->row'. Returns
262  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'
263  *
264  *************************************************************************/
265 static int sql_fetch_row(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
266
267         SQLRETURN rc;
268         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
269
270         sqlsocket->row = NULL;
271
272         if((rc = SQLFetch(iodbc_sock->stmt_handle)) == SQL_NO_DATA_FOUND) {
273                 return 0;
274         }
275         /* XXX Check rc for database down, if so, return SQL_DOWN */
276
277         sqlsocket->row = iodbc_sock->row;
278         return 0;
279 }
280
281
282
283 /*************************************************************************
284  *
285  *      Function: sql_free_result
286  *
287  *      Purpose: database specific free_result. Frees memory allocated
288  *               for a result set
289  *
290  *************************************************************************/
291 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
292
293         int i=0;
294         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
295
296         for(i=0; i<sql_num_fields(sqlsocket, config); i++) {
297                 free(iodbc_sock->row[i]);
298         }
299         free(iodbc_sock->row);
300         iodbc_sock->row=NULL;
301
302         SQLFreeStmt( iodbc_sock->stmt_handle, SQL_DROP );
303
304         return 0;
305 }
306
307
308 /*************************************************************************
309  *
310  *      Function: sql_error
311  *
312  *      Purpose: database specific error. Returns error associated with
313  *               connection
314  *
315  *************************************************************************/
316 static char *sql_error(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
317
318         SQLINTEGER errornum = 0;
319         SQLSMALLINT length = 0;
320         SQLCHAR state[256] = "";
321         static SQLCHAR error[256] = "";
322         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
323
324         SQLError(iodbc_sock->env_handle, iodbc_sock->dbc_handle, iodbc_sock->stmt_handle,
325                 state, &errornum, error, 256, &length);
326         return error;
327 }
328
329
330 /*************************************************************************
331  *
332  *      Function: sql_close
333  *
334  *      Purpose: database specific close. Closes an open database
335  *               connection and cleans up any open handles.
336  *
337  *************************************************************************/
338 static int sql_close(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
339
340         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
341
342         SQLFreeStmt(iodbc_sock->stmt_handle, SQL_DROP);
343         SQLDisconnect(iodbc_sock->dbc_handle);
344         SQLFreeConnect(iodbc_sock->dbc_handle);
345         SQLFreeEnv(iodbc_sock->env_handle);
346
347         iodbc_sock->stmt_handle = NULL;
348         iodbc_sock->dbc_handle = NULL;
349         iodbc_sock->env_handle = NULL;
350
351         return 0;
352 }
353
354
355 /*************************************************************************
356  *
357  *      Function: sql_finish_query
358  *
359  *      Purpose: End the query, such as freeing memory
360  *
361  *************************************************************************/
362 static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
363
364         return sql_free_result(sqlsocket, config);
365 }
366
367
368
369 /*************************************************************************
370  *
371  *      Function: sql_finish_select_query
372  *
373  *      Purpose: End the select query, such as freeing memory or result
374  *
375  *************************************************************************/
376 static int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
377         return sql_free_result(sqlsocket, config);
378 }
379
380
381 /*************************************************************************
382  *
383  *      Function: sql_affected_rows
384  *
385  *      Purpose: Return the number of rows affected by the query (update,
386  *               or insert)
387  *
388  *************************************************************************/
389 static int sql_affected_rows(SQLSOCK *sqlsocket, UNUSED SQL_CONFIG *config) {
390
391         SQLINTEGER count;
392         rlm_sql_iodbc_sock *iodbc_sock = sqlsocket->conn;
393
394         SQLRowCount(iodbc_sock->stmt_handle, &count);
395         return (int)count;
396 }
397
398 /* Exported to rlm_sql */
399 rlm_sql_module_t rlm_sql_iodbc = {
400         "rlm_sql_iodbc",
401         sql_init_socket,
402         sql_destroy_socket,
403         sql_query,
404         sql_select_query,
405         sql_store_result,
406         sql_num_fields,
407         sql_num_rows,
408         sql_fetch_row,
409         sql_free_result,
410         sql_error,
411         sql_close,
412         sql_finish_query,
413         sql_finish_select_query,
414         sql_affected_rows
415 };