/************************************************************************* * * Function: rlm_sql_fetch_row * * Purpose: call the module's sql_fetch_row and implement re-connect * *************************************************************************/ int rlm_sql_fetch_row(SQLSOCK *sqlsocket, SQL_INST *inst) { int ret; if (sqlsocket->conn) { ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config); } else { ret = SQL_DOWN; } if (ret == SQL_DOWN) { /* close the socket that failed, but only if it was open */ if (sqlsocket->conn) { (inst->module->sql_close)(sqlsocket, inst->config); } /* reconnect the socket */ if (connect_single_socket(sqlsocket, inst) < 0) { radlog(L_ERR, "rlm_sql (%s): reconnect failed, database down?", inst->config->xlat_name); return -1; } /* retry the query on the newly connected socket */ ret = (inst->module->sql_fetch_row)(sqlsocket, inst->config); if (ret) { radlog(L_ERR, "rlm_sql (%s): failed after re-connect", inst->config->xlat_name); return -1; } } return ret; }
static int redis_init_socketpool(REDIS_INST *inst) { int i, rcode; int success = 0; REDISSOCK *dissocket; inst->connect_after = 0; inst->redispool = NULL; for (i = 0; i < inst->numconnections; i++) { radlog(L_DBG, "rlm_redis (%s): starting %d", inst->xlat_name, i); dissocket = rad_malloc(sizeof (*dissocket)); if (dissocket == NULL) { return -1; } memset(dissocket, 0, sizeof (*dissocket)); dissocket->conn = NULL; dissocket->id = i; dissocket->state = sockunconnected; #ifdef HAVE_PTHREAD_H rcode = pthread_mutex_init(&dissocket->mutex, NULL); if (rcode != 0) { free(dissocket); radlog(L_ERR, "rlm_redis: Failed to init lock: %s", strerror(errno)); return 0; } #endif if (time(NULL) > inst->connect_after) { /* * This sets the dissocket->state, and * possibly also inst->connect_after */ if (connect_single_socket(inst, dissocket) == 0) { success = 1; } } /* Add "dis" socket to the list of sockets * pun intended */ dissocket->next = inst->redispool; inst->redispool = dissocket; } inst->last_used = NULL; if (!success) { radlog(L_DBG, "rlm_redis (%s): Failed to connect to any redis server.", inst->xlat_name); } return 1; }
/************************************************************************* * * Function: sql_init_socketpool * * Purpose: Connect to the sql server, if possible * *************************************************************************/ int sql_init_socketpool(SQL_INST * inst) { int i, rcode; int success = 0; SQLSOCK *sqlsocket; inst->connect_after = 0; inst->sqlpool = NULL; for (i = 0; i < inst->config->num_sql_socks; i++) { radlog(L_DBG, "rlm_sql (%s): starting %d", inst->config->xlat_name, i); sqlsocket = rad_malloc(sizeof(*sqlsocket)); if (sqlsocket == NULL) { return -1; } memset(sqlsocket, 0, sizeof(*sqlsocket)); sqlsocket->conn = NULL; sqlsocket->id = i; sqlsocket->state = sockunconnected; #ifdef HAVE_PTHREAD_H rcode = pthread_mutex_init(&sqlsocket->mutex,NULL); if (rcode != 0) { free(sqlsocket); radlog(L_ERR, "rlm_sql: Failed to init lock: %s", strerror(errno)); return 0; } #endif if (time(NULL) > inst->connect_after) { /* * This sets the sqlsocket->state, and * possibly also inst->connect_after */ if (connect_single_socket(sqlsocket, inst) == 0) { success = 1; } } /* Add this socket to the list of sockets */ sqlsocket->next = inst->sqlpool; inst->sqlpool = sqlsocket; } inst->last_used = NULL; if (!success) { radlog(L_DBG, "rlm_sql (%s): Failed to connect to any SQL server.", inst->config->xlat_name); } return 1; }
/* * Free the redis database */ int rlm_redis_query(REDISSOCK *dissocket, REDIS_INST *inst, char *query) { if (!query || !*query) { return -1; } DEBUG2("executing query %s", query); dissocket->reply = redisCommand(dissocket->conn, query); if (dissocket->reply == NULL) { radlog(L_ERR, "rlm_redis: (%s) REDIS error: %s", inst->xlat_name, dissocket->conn->errstr); /* close the socket that failed */ if (dissocket->state == sockconnected) { redisFree(dissocket->conn); dissocket->state = sockunconnected; } /* reconnect the socket */ if (connect_single_socket(inst, dissocket) < 0) { radlog(L_ERR, "rlm_redis (%s): reconnect failed, database down?", inst->xlat_name); return -1; } DEBUG2("executing query %s", query); /* retry the query on the newly connected socket */ dissocket->reply = redisCommand(dissocket->conn, query); if (dissocket->reply == NULL) { radlog(L_ERR, "rlm_redis (%s): failed after re-connect", inst->xlat_name); return -1; } } if (dissocket->reply->type == REDIS_REPLY_ERROR) { radlog(L_ERR, "rlm_redis (%s): query failed, %s", inst->xlat_name, query); /* Free the reply just in case */ rlm_redis_finish_query(dissocket); return -1; } return 0; }
/************************************************************************* * * Function: rlm_sql_select_query * * Purpose: call the module's sql_select_query and implement re-connect * *************************************************************************/ int rlm_sql_select_query(SQLSOCK *sqlsocket, SQL_INST *inst, char *query) { int ret; /* * If there's no query, return an error. */ if (!query || !*query) { return -1; } if (sqlsocket->conn) { ret = (inst->module->sql_select_query)(sqlsocket, inst->config, query); } else { ret = SQL_DOWN; } if (ret == SQL_DOWN) { /* close the socket that failed */ if (sqlsocket->state == sockconnected) { (inst->module->sql_close)(sqlsocket, inst->config); } /* reconnect the socket */ if (connect_single_socket(sqlsocket, inst) < 0) { radlog(L_ERR, "rlm_sql (%s): reconnect failed, database down?", inst->config->xlat_name); return -1; } /* retry the query on the newly connected socket */ ret = (inst->module->sql_select_query)(sqlsocket, inst->config, query); if (ret) { radlog(L_ERR, "rlm_sql (%s): failed after re-connect", inst->config->xlat_name); return -1; } } return ret; }
/************************************************************************* * * Function: redis_get_socket * * Purpose: Return a REDIS socket from the connection pool * *************************************************************************/ REDISSOCK *redis_get_socket(REDIS_INST *inst) { REDISSOCK *cur, *start; int tried_to_connect = 0; int unconnected = 0; time_t now = time(NULL); /* * Start at the last place we left off. */ start = inst->last_used; if (!start) start = inst->redispool; cur = start; while (cur) { #ifdef HAVE_PTHREAD_H /* * If this socket is in use by another thread, * skip it, and try another socket. * * If it isn't used, then grab it ourselves. */ if (pthread_mutex_trylock(&cur->mutex) != 0) { goto next; } /* else we now have the lock */ #endif /* * If the socket has outlived its lifetime, and * is connected, close it, and mark it as open for * reconnections. */ if (inst->lifetime && (cur->state == sockconnected) && ((cur->connected + inst->lifetime) < now)) { DEBUG2("Closing socket %d as its lifetime has been exceeded", cur->id); redis_close_socket(inst, cur); cur->state = sockunconnected; goto reconnect; } /* * If we have performed too many queries over this * socket, then close it. */ if (inst->max_queries && (cur->state == sockconnected) && (cur->queries >= inst->max_queries)) { DEBUG2("Closing socket %d as its max_queries has been exceeded", cur->id); redis_close_socket(inst, cur); cur->state = sockunconnected; goto reconnect; } /* * If we happen upon an unconnected socket, and * this instance's grace period on * (re)connecting has expired, then try to * connect it. This should be really rare. */ if ((cur->state == sockunconnected) && (now > inst->connect_after)) { reconnect: radlog(L_INFO, "rlm_redis (%s): Trying to (re)connect unconnected handle %d..", inst->xlat_name, cur->id); tried_to_connect++; connect_single_socket(inst, cur); } /* if we still aren't connected, ignore this handle */ if (cur->state == sockunconnected) { DEBUG("rlm_redis (%s): Ignoring unconnected handle %d..", inst->xlat_name, cur->id); unconnected++; #ifdef HAVE_PTHREAD_H pthread_mutex_unlock(&cur->mutex); #endif goto next; } /* should be connected, grab it */ DEBUG("rlm_redis (%s): Reserving redis socket id: %d", inst->xlat_name, cur->id); if (unconnected != 0 || tried_to_connect != 0) { DEBUG("rlm_redis (%s): got socket %d after skipping %d unconnected handles, tried to reconnect %d though", inst->xlat_name, cur->id, unconnected, tried_to_connect); } /* * The socket is returned in the locked * state. * * We also remember where we left off, * so that the next search can start from * here. * * Note that multiple threads MAY over-write * the 'inst->last_used' variable. This is OK, * as it's a pointer only used for reading. */ inst->last_used = cur->next; cur->queries++; return cur; /* move along the list */ next: cur = cur->next; /* * Because we didnt start at the start, once we * hit the end of the linklist, we should go * back to the beginning and work toward the * middle! */ if (!cur) { cur = inst->redispool; } /* * If we're at the socket we started */ if (cur == start) { break; } } /* * Suppress most of the log messages. We don't want to * flood the log with this message for EVERY packet. * Instead, write to the log only once a second or so. * * This code has race conditions when threaded, but the * only result is that a few more messages are logged. */ if (now <= last_logged_failure) return NULL; last_logged_failure = now; /* We get here if every DB handle is unconnected and unconnectABLE */ radlog(L_INFO, "rlm_redis (%s): There are no DB handles to use! skipped %d, tried to connect %d", inst->xlat_name, unconnected, tried_to_connect); return NULL; }