2 * sql.c rlm_sql - FreeRADIUS SQL Module
3 * Main code directly taken from ICRADIUS
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * Copyright 2001 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>
28 #include <sys/types.h>
29 #include <sys/socket.h>
34 #include <netinet/in.h>
57 * Connect to a server. If error, set this socket's state to be "sockunconnected"
58 * and set a grace period, during which we won't try connecting again (to prevent unduly
59 * lagging the server and being impolite to a DB server that may be having other
60 * issues). If successful in connecting, set state to sockconnected. - chad
62 static int connect_single_socket(SQLSOCK *sqlsocket, SQL_INST *inst) {
63 radlog(L_DBG, "rlm_sql: Attempting to connect #%d", sqlsocket->id);
64 if ((inst->module->sql_init_socket)(sqlsocket, inst->config) < 0) {
65 radlog(L_CONS | L_ERR, "rlm_sql: Failed to connect DB handle #%d", sqlsocket->id);
66 inst->connect_after = time(NULL) + inst->config->connect_failure_retry_delay;
67 sqlsocket->state = sockunconnected;
70 radlog(L_DBG, "rlm_sql: Connected new DB handle, #%d", sqlsocket->id);
71 sqlsocket->state = sockconnected;
77 /*************************************************************************
79 * Function: sql_init_socketpool
81 * Purpose: Connect to the sql server, if possible
83 *************************************************************************/
84 int sql_init_socketpool(SQL_INST * inst) {
89 inst->connect_after = 0;
94 for (i = 0; i < inst->config->num_sql_socks; i++) {
95 radlog(L_DBG, "rlm_sql: starting %d", i);
97 sqlsocket = rad_malloc(sizeof(SQLSOCK));
98 if (sqlsocket == NULL) {
101 sqlsocket->conn = NULL;
103 sqlsocket->state = sockunconnected;
107 * FIXME! Check return codes!
109 sqlsocket->semaphore = (sem_t *) rad_malloc(sizeof(sem_t));
110 sem_init(sqlsocket->semaphore, 0, SQLSOCK_UNLOCKED);
112 sqlsocket->in_use = SQLSOCK_UNLOCKED;
115 if (time(NULL) > inst->connect_after) {
116 /* this sets the sqlsocket->state, and possibly sets inst->connect_after */
117 /* FIXME! check return code */
118 connect_single_socket(sqlsocket, inst);
121 /* Add this socket to the list of sockets */
122 sqlsocket->next = inst->sqlpool;
123 inst->sqlpool = sqlsocket;
127 pthread_mutex_init(&inst->mutex, NULL);
133 /*************************************************************************
135 * Function: sql_poolfree
137 * Purpose: Clean up and free sql pool
139 *************************************************************************/
140 void sql_poolfree(SQL_INST * inst) {
144 for (cur = inst->sqlpool; cur; cur = cur->next) {
145 sql_close_socket(inst, cur);
148 pthread_mutex_destroy(&inst->mutex);
153 /*************************************************************************
155 * Function: sql_close_socket
157 * Purpose: Close and free a sql sqlsocket
159 *************************************************************************/
160 int sql_close_socket(SQL_INST *inst, SQLSOCK * sqlsocket) {
162 radlog(L_DBG, "rlm_sql: Closing sqlsocket %d", sqlsocket->id);
163 (inst->module->sql_close)(sqlsocket, inst->config);
165 sem_destroy(sqlsocket->semaphore);
172 /*************************************************************************
174 * Function: sql_get_socket
176 * Purpose: Return a SQL sqlsocket from the connection pool
178 *************************************************************************/
179 SQLSOCK * sql_get_socket(SQL_INST * inst) {
181 int tried_to_connect = 0;
183 while (inst->used == inst->config->num_sql_socks) {
184 radlog(L_ERR, "rlm_sql: All sockets are being used! Please increase the maximum number of sockets!");
189 * Rotating the socket so that all get used and none get closed due to
190 * inactivity from the SQL server ( such as mySQL ).
193 pthread_mutex_lock(&inst->mutex);
196 if(inst->socknr == 0) {
197 inst->socknr = inst->config->num_sql_socks;
200 cur2 = inst->sqlpool;
201 while (inst->socknr != cur2->id) {
205 pthread_mutex_unlock(&inst->mutex);
208 for (cur = cur2; cur; cur = cur->next) {
210 /* if we happen upon an unconnected socket, and this instance's grace
211 * period on (re)connecting has expired, then try to connect it. This
212 * should be really rare. - chad
214 if ((cur->state == sockunconnected) && (time(NULL) > inst->connect_after)) {
215 tried_to_connect = 1;
216 radlog(L_INFO, "rlm_sql: Trying to (re)connect an unconnected handle...");
217 connect_single_socket(cur, inst);
220 /* if we still aren't connected, ignore this handle */
221 if (cur->state == sockunconnected) {
222 radlog(L_DBG, "rlm_sql: Ignoring unconnected handle");
227 if (sem_trywait(cur->semaphore) == 0) {
229 if (cur->in_use == SQLSOCK_UNLOCKED) {
232 #ifndef HAVE_SEMAPHORE_H
233 cur->in_use = SQLSOCK_LOCKED;
235 radlog(L_DBG, "rlm_sql: Reserving sql socket id: %d", cur->id);
240 /* We get here if every DB handle is unconnected and unconnectABLE */
241 radlog((tried_to_connect == 0) ? (L_DBG) : (L_CONS | L_ERR), "rlm_sql: There are no DB handles to use!");
245 /*************************************************************************
247 * Function: sql_release_socket
249 * Purpose: Frees a SQL sqlsocket back to the connection pool
251 *************************************************************************/
252 int sql_release_socket(SQL_INST * inst, SQLSOCK * sqlsocket) {
256 sem_post(sqlsocket->semaphore);
258 sqlsocket->in_use = SQLSOCK_UNLOCKED;
261 radlog(L_DBG, "rlm_sql: Released sql socket id: %d", sqlsocket->id);
267 /*************************************************************************
269 * Function: sql_userparse
271 * Purpose: Read entries from the database and fill VALUE_PAIR structures
273 *************************************************************************/
274 int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int querymode) {
277 VALUE_PAIR *pair, *check;
280 int pairmode = T_EOL;
282 if ((attr = dict_attrbyname(row[2])) == (DICT_ATTR *) NULL) {
283 radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[2]);
287 if (row[4] != NULL && strlen(row[4]) > 0) {
289 pairmode = gettoken(&ptr, buf, sizeof(buf));
291 if (pairmode <= T_EOL) pairmode = T_OP_CMP_EQ;
294 * If attribute is already there, skip it because we checked usercheck first
295 * and we want user settings to over ride group settings
297 if (pairmode != T_OP_ADD && (check = pairfind(*first_pair, attr->attr)) != NULL &&
299 attr->type != PW_TYPE_ABINARY &&
301 querymode == PW_VP_GROUPDATA)
304 pair = pairmake(row[2], row[3], pairmode);
305 pairadd(first_pair, pair);
311 /*************************************************************************
313 * Function: rlm_sql_fetch_row
315 * Purpose: call the module's sql_fetch_row and implement re-connect
317 *************************************************************************/
318 int rlm_sql_fetch_row(SQLSOCK *sqlsocket, SQL_INST *inst) {
321 ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config);
323 if (ret == SQL_DOWN) {
324 if (connect_single_socket(sqlsocket, inst) < 0) {
325 radlog(L_ERR, "rlm_sql: reconnect failed, database down?");
329 ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config);
332 radlog(L_ERR, "rlm_sql: failed after re-connect");
340 /*************************************************************************
342 * Function: rlm_sql_query
344 * Purpose: call the module's sql_query and implement re-connect
346 *************************************************************************/
347 int rlm_sql_query(SQLSOCK *sqlsocket, SQL_INST *inst, char *query) {
350 ret = (inst->module->sql_query)(sqlsocket, inst->config, query);
352 if (ret == SQL_DOWN) {
353 if (connect_single_socket(sqlsocket, inst) < 0) {
354 radlog(L_ERR, "rlm_sql: reconnect failed, database down?");
358 ret = (inst->module->sql_query)(sqlsocket, inst->config, query);
361 radlog(L_ERR, "rlm_sql: failed after re-connect");
369 /*************************************************************************
371 * Function: rlm_sql_select_query
373 * Purpose: call the module's sql_select_query and implement re-connect
375 *************************************************************************/
376 int rlm_sql_select_query(SQLSOCK *sqlsocket, SQL_INST *inst, char *query) {
379 ret = (inst->module->sql_select_query)(sqlsocket, inst->config, query);
381 if (ret == SQL_DOWN) {
382 if (connect_single_socket(sqlsocket, inst) < 0) {
383 radlog(L_ERR, "rlm_sql: reconnect failed, database down?");
387 ret = (inst->module->sql_select_query)(sqlsocket, inst->config, query);
390 radlog(L_ERR, "rlm_sql: failed after re-connect");
399 /*************************************************************************
401 * Function: sql_getvpdata
403 * Purpose: Get any group check or reply pairs
405 *************************************************************************/
406 int sql_getvpdata(SQL_INST * inst, SQLSOCK * sqlsocket, VALUE_PAIR **pair, char *query, int mode) {
411 if (rlm_sql_select_query(sqlsocket, inst, query)) {
412 radlog(L_ERR, "rlm_sql_getvpdata: database query error");
415 while (rlm_sql_fetch_row(sqlsocket, inst)==0) {
416 row = sqlsocket->row;
419 if (sql_userparse(pair, row, mode) != 0) {
420 radlog(L_ERR | L_CONS, "rlm_sql: Error getting data from database");
421 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
426 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
434 alrm_handler(int i) {
438 void query_log(SQL_INST * inst, char *querystr) {
439 FILE *sqlfile = NULL;
441 if (inst->config->sqltrace) {
442 if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
443 radlog(L_ERR, "rlm_sql: Couldn't open file %s",
444 inst->config->tracefile);
446 int fd = fileno(sqlfile);
448 rad_lockfd(fd, MAX_QUERY_LEN);
449 fputs(querystr, sqlfile);
450 fputs(";\n", sqlfile);
451 fclose(sqlfile); /* and release the lock */