From: aland Date: Tue, 11 Jun 2002 17:41:54 +0000 (+0000) Subject: Preliminary driver for sybase database, from mattias@nogui.se X-Git-Tag: release_0_6_0~47 X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;h=f91c69f11ae35f7cca9338c77621af5b0ce8366a;p=freeradius.git Preliminary driver for sybase database, from mattias@nogui.se --- diff --git a/src/modules/rlm_sql/drivers/rlm_sql_sybase/Makefile b/src/modules/rlm_sql/drivers/rlm_sql_sybase/Makefile new file mode 100644 index 0000000..38b8af4 --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_sybase/Makefile @@ -0,0 +1,17 @@ +# +# This module is in the source, but not yet fully integrated with +# the configure scripts. For now, manual editing of this file is +# required to get it to build. +# +include ../../../../../Make.inc + +TARGET = +#TARGET = rlm_sql_sybase +SRCS = sql_sybase.c +HEADERS = sql_sybase.h +RLM_SQL_CFLAGS = $(INCLTDL) -I/opt/sybase/OCS-12_0/include +RLM_SQL_LIBS = -L/opt/sybase/OCS-12_0/lib -ltcl -lcomn -lintl -lnsl -ldl -lm -lcs -lct + +include ../rules.mak + +$(DYNAMIC_OBJS): $(HEADERS) diff --git a/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.c b/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.c new file mode 100644 index 0000000..a3ff8ba --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.c @@ -0,0 +1,918 @@ +/*************************************************************************** +* sql_sybase.c rlm_sql - FreeRADIUS SQL Module * +* * +* Sybase (ctlibrary) routines for rlm_sql * +* * +* Error handling stolen from Sybase example code "firstapp.c" * +* * +* Mattias Sjostrom * +***************************************************************************/ + +#include +#include +#include +#include + +#include "radiusd.h" +#include "sql_sybase.h" + +#define MAX_DATASTR_LEN 256 + +/************************************************************************ +* Handler for server messages. Client-Library will call this +* routine when it receives a message from the server. +************************************************************************/ + +CS_RETCODE CS_PUBLIC +servermsg_callback(cp, chp, msgp) +CS_CONTEXT *cp; +CS_CONNECTION *chp; +CS_SERVERMSG *msgp; +{ + + /* + ** Print the message info. + */ + radlog(L_ERR, + "Sybase Server message:\n"); + radlog(L_ERR, + "number(%ld) severity(%ld) state(%ld) line(%ld)\n", + (long)msgp->msgnumber, (long)msgp->severity, + (long)msgp->state, (long)msgp->line); + + /* + ** Print the server and procedure names if supplied. + */ + if (msgp->svrnlen > 0 && msgp->proclen > 0) + radlog(L_ERR, "Server name: %s Procedure name: %s", msgp->svrname, msgp->proc); + + /* + ** Print the null terminated message. + */ + radlog(L_ERR, "%s\n", msgp->text); + + /* + ** Server message callbacks must return CS_SUCCEED. + */ + return (CS_SUCCEED); +} + +/************************************************************************ +* Client-Library error handler. +************************************************************************/ + +CS_RETCODE CS_PUBLIC +clientmsg_callback(context, conn, emsgp) +CS_CONTEXT *context; +CS_CONNECTION *conn; +CS_CLIENTMSG *emsgp; +{ + + /* + ** Error number: Print the error's severity, number, origin, and + ** layer. These four numbers uniquely identify the error. + */ + radlog(L_ERR, + "Client Library error:\n"); + radlog(L_ERR, + "severity(%ld) number(%ld) origin(%ld) layer(%ld)\n", + (long)CS_SEVERITY(emsgp->severity), + (long)CS_NUMBER(emsgp->msgnumber), + (long)CS_ORIGIN(emsgp->msgnumber), + (long)CS_LAYER(emsgp->msgnumber)); + + /* + ** Error text: Print the error text. + */ + radlog(L_ERR, "%s\n", emsgp->msgstring); + + if (emsgp->osstringlen > 0) + { + radlog(L_ERR, + "Operating system error number(%ld):\n", + (long)emsgp->osnumber); + radlog(L_ERR, "%s\n", emsgp->osstring); + } + + return (CS_SUCCEED); +} + +/************************************************************************ +* CS-Library error handler. This function will be invoked +* when CS-Library has detected an error. +************************************************************************/ + +CS_RETCODE CS_PUBLIC +csmsg_callback(context, emsgp) +CS_CONTEXT *context; +CS_CLIENTMSG *emsgp; +{ + + /* + ** Print the error number and message. + */ + radlog(L_ERR, + "CS-Library error:\n"); + radlog(L_ERR, + "\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)", + (long)CS_SEVERITY(emsgp->msgnumber), + (long)CS_LAYER(emsgp->msgnumber), + (long)CS_ORIGIN(emsgp->msgnumber), + (long)CS_NUMBER(emsgp->msgnumber)); + + radlog(L_ERR, "%s\n", emsgp->msgstring); + + /* + ** Print any operating system error information. + */ + if (emsgp->osstringlen > 0) + { + radlog(L_ERR, "Operating System Error: %s\n", + emsgp->osstring); + } + + return (CS_SUCCEED); +} + +/************************************************************************* + * + * Function: sql_init_socket + * + * Purpose: Establish connection to the db + * + *************************************************************************/ +int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + rlm_sql_sybase_sock *sybase_sock; + + + sqlsocket->conn = (rlm_sql_sybase_sock *)rad_malloc(sizeof(rlm_sql_sybase_sock)); + + sybase_sock = sqlsocket->conn; + + sybase_sock->results=NULL; + + /* Allocate a CS context structure. This should really only be done once, but because of + the connection pooling design of rlm_sql, we'll have to go with one context per connection */ + + if (cs_ctx_alloc(CS_VERSION_100, &sybase_sock->context) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate CS context structure (cs_ctx_alloc())"); + return -1; + } + + /* Initialize ctlib */ + + if (ct_init(sybase_sock->context, CS_VERSION_100) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize Client-Library (ct_init())"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + /* Install callback functions for error-handling */ + + if (cs_config(sybase_sock->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install CS Library error callback"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + /* Allocate a ctlib connection structure */ + + if (ct_con_alloc(sybase_sock->context, &sybase_sock->connection) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate connection structure (ct_con_alloc())"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + /* Initialize inline error handling for the connection */ + +/* if (ct_diag(sybase_sock->connection, CS_INIT, CS_UNUSED, CS_UNUSED, NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize error handling (ct_diag())"); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } */ + + + + /* Set User and Password properties for the connection */ + + if (ct_con_props(sybase_sock->connection, CS_SET, CS_USERNAME, config->sql_login, + strlen(config->sql_login), NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set username for connection (ct_con_props())\n%s", + sql_error(sqlsocket, config)); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + if (ct_con_props(sybase_sock->connection, CS_SET, CS_PASSWORD, config->sql_password, + strlen(config->sql_password), NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set password for connection (ct_con_props())\n%s", + sql_error(sqlsocket, config)); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + + /* Establish the connection */ + + if (ct_connect(sybase_sock->connection, config->sql_server, strlen(config->sql_server)) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to establish connection to symbolic servername %s\n%s", + config->sql_server, sql_error(sqlsocket, config)); + if (sybase_sock->context != (CS_CONTEXT *)NULL) { + ct_exit(sybase_sock->context, CS_FORCE_EXIT); + cs_ctx_drop(sybase_sock->context); + } + return -1; + } + return 0; +} + + +/************************************************************************* + * + * Function: sql_destroy_socket + * + * Purpose: Free socket and private connection data + * + *************************************************************************/ +int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + /* Why bother, rlm_sql never calls sql_destroy_socket anyway */ + + return 0; +} + +/************************************************************************* + * + * Function: sql_query + * + * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to + * the database. + * + *************************************************************************/ +int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + + CS_RETCODE ret, results_ret; + CS_INT result_type; + + if (config->sqltrace) + DEBUG(querystr); + if (sybase_sock->connection == NULL) { + radlog(L_ERR, "Socket not connected"); + return -1; + } + + if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to allocate command structure (ct_cmd_alloc())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to initiate command structure (ct_command())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + if (ct_send(sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to send command (ct_send())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + /* + ** We'll make three calls to ct_results, first to get a success indicator, secondly to get a done indicator, and + ** thirdly to get a "nothing left to handle" status. + */ + + /* + ** First call to ct_results, + ** we need returncode CS_SUCCEED + ** and result_type CS_CMD_SUCCEED. + */ + + if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) { + if (result_type != CS_CMD_SUCCEED) { + if (result_type == CS_ROW_RESULT) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): sql_query processed a query returning rows. + Use sql_select_query instead!"); + } + radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s", + sql_error(sqlsocket, config)); + return -1; + } + } + else { + switch ((int) results_ret) + { + + case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */ + radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s" + , sql_error(sqlsocket, config)); + if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up."); + ct_close(sybase_sock->connection, CS_FORCE_CLOSE); + sql_close(sqlsocket, config); + } + return -1; + break; + + default: + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s", + sql_error(sqlsocket, config)); + return -1; + } + } + + + /* + ** Second call to ct_results, + ** we need returncode CS_SUCCEED + ** and result_type CS_CMD_DONE. + */ + + if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) { + if (result_type != CS_CMD_DONE) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s", + sql_error(sqlsocket, config)); + return -1; + } + } + else { + switch ((int) results_ret) + { + + case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */ + radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s" + , sql_error(sqlsocket, config)); + if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up."); + ct_close(sybase_sock->connection, CS_FORCE_CLOSE); + sql_close(sqlsocket, config); + } + return -1; + break; + + default: + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s", + sql_error(sqlsocket, config)); + return -1; + } + } + + + /* + ** Third call to ct_results, + ** we need returncode CS_END_RESULTS + ** result_type will be ignored. + */ + + results_ret = ct_results(sybase_sock->command, &result_type); + + switch ((int) results_ret) + { + + case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */ + radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s" + , sql_error(sqlsocket, config)); + if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) { + radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up."); + ct_close(sybase_sock->connection, CS_FORCE_CLOSE); + sql_close(sqlsocket, config); + } + return -1; + break; + + case CS_END_RESULTS: /* This is where we want to end up */ + break; + + default: + radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s", + sql_error(sqlsocket, config)); + return -1; + break; + } + return 0; +} + + + + +/************************************************************************* + * + * Function: sql_select_query + * + * Purpose: Issue a select query to the database + * + * Note: Only the first row from queries returning several rows + * will be returned by this function, consequitive rows will + * be discarded. + * + *************************************************************************/ +int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + + CS_RETCODE ret, results_ret; + CS_INT result_type; + CS_DATAFMT descriptor; + + int colcount,i; + char **rowdata; + + if (config->sqltrace) + DEBUG(querystr); + if (sybase_sock->connection == NULL) { + radlog(L_ERR, "Socket not connected"); + return -1; + } + + + if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to allocate command structure (ct_cmd_alloc())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to initiate command structure (ct_command())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + if (ct_send(sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to send command (ct_send())\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + results_ret = ct_results(sybase_sock->command, &result_type); + + switch (results_ret) { + + case CS_SUCCEED: + + switch (result_type) { + + case CS_ROW_RESULT: + + /* + ** Houston, we have a row. + ** + ** We set up a target buffer for the results data, and + ** associate the buffer with the results, but the actual + ** fetching takes place in sql_fetch_row. The layer above + ** MUST call sql_fetch_row and/or sql_finish_select_query + ** or this socket will be unusable and may cause segfaults + ** if reused later on. + */ + + /* + ** Set up the DATAFMT structure that describes our target array + ** and tells sybase what we want future ct_fetch calls to do. + */ + descriptor.datatype = CS_CHAR_TYPE; /* The target buffer is a string */ + descriptor.format = CS_FMT_NULLTERM; /* Null termination please */ + descriptor.maxlength = MAX_DATASTR_LEN; /* The string arrays are this large */ + descriptor.count = 1; /* Fetch one row of data */ + descriptor.locale = NULL; /* Don't do NLS stuff */ + + + colcount = sql_num_fields(sqlsocket, config); /* Get number of elements in row result */ + + + rowdata=(char **)rad_malloc(sizeof(char *) * (colcount+1)); /* Space for pointers */ + memset(rowdata, 0, (sizeof(char *) * colcount+1)); /* NULL-pad the pointers */ + + for (i=0; i < colcount; i++) { + + rowdata[i]=rad_malloc((MAX_DATASTR_LEN * sizeof(char))+1); /* Space to hold the result data */ + + /* Associate the target buffer with the data */ + if (ct_bind(sybase_sock->command, i+1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): ct_bind() failed)\n%s", + sql_error(sqlsocket, config)); + return -1; + } + + } + rowdata[i]=NULL; /* Terminate the array */ + sybase_sock->results=rowdata; + break; + + case CS_CMD_SUCCEED: + case CS_CMD_DONE: + + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Query returned no data"); + break; + + default: + + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected result type from query\n%s", + sql_error(sqlsocket, config)); + sql_finish_select_query(sqlsocket, config); + return -1; + break; + } + break; + + case CS_FAIL: + + /* + ** Serious failure, sybase requires us to cancel + ** the results and maybe even close the connection. + */ + + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Failure retrieving query results\n%s" + , sql_error(sqlsocket, config)); + if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) { + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): cleaning up."); + ct_close(sybase_sock->connection, CS_FORCE_CLOSE); + sql_close(sqlsocket, config); + } + return -1; + break; + + default: + + radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected return value from ct_results()\n%s", + sql_error(sqlsocket, config)); + return -1; + break; + } + return 0; +} + + +/************************************************************************* + * + * Function: sql_store_result + * + * Purpose: database specific store_result function. Returns a result + * set for the query. + * + *************************************************************************/ +int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + /* + ** Not needed for Sybase, code that may have gone here is + ** in sql_select_query and sql_fetch_row + */ + return 0; +} + + +/************************************************************************* + * + * Function: sql_num_fields + * + * Purpose: database specific num_fields function. Returns number + * of columns from query + * + *************************************************************************/ +int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + int num; + + if (ct_res_info(sybase_sock->command, CS_NUMDATA, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_num_fields): error retrieving column count: %s", + sql_error(sqlsocket, config)); + return -1; + } + return num; +} + +/************************************************************************* + * + * Function: sql_num_rows + * + * Purpose: database specific num_rows. Returns number of rows in + * query + * + *************************************************************************/ +int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + int num; + + if (ct_res_info(sybase_sock->command, CS_ROW_COUNT, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_num_rows): error retrieving row count: %s", + sql_error(sqlsocket, config)); + return -1; + } + return num; +} + + +/************************************************************************* + * + * Function: sql_fetch_row + * + * Purpose: database specific fetch_row. Returns a SQL_ROW struct + * with all the data for the query + * + *************************************************************************/ +SQL_ROW sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + CS_INT ret, count; + + + ret = ct_fetch(sybase_sock->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count); + + switch (ret) { + + case CS_FAIL: + + /* + ** Serious failure, sybase requires us to cancel + ** the results and maybe even close the connection. + */ + + radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Failure fething row data\n%s" + , sql_error(sqlsocket, config)); + if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) { + radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): cleaning up."); + ct_close(sybase_sock->connection, CS_FORCE_CLOSE); + sql_close(sqlsocket, config); + } + return NULL; + break; + + case CS_END_DATA: + + return NULL; + break; + + case CS_SUCCEED: + + return sybase_sock->results; + break; + + case CS_ROW_FAIL: + + radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Recoverable failure fething row data, try again perhaps?"); + return NULL; + + default: + + radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Unexpected returncode from ct_fetch"); + return NULL; + break; + } + +} + + + +/************************************************************************* + * + * Function: sql_free_result + * + * Purpose: database specific free_result. Frees memory allocated + * for a result set + * + *************************************************************************/ +int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + /* + ** Not implemented, never called from rlm_sql anyway + ** result buffer is freed in the finish_query functions. + */ + + return 0; + +} + + + +/************************************************************************* + * + * Function: sql_error + * + * Purpose: database specific error. Returns error associated with + * connection + * + *************************************************************************/ +char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + static char msg='\0'; +/* + static char msgbuf[2048]; + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + CS_INT msgcount; + CS_CLIENTMSG cmsg; + CS_SERVERMSG smsg; + + int i; + char ctempbuf[2][512]; + char stempbuf[2][512]; + + msgbuf[0]=(char)NULL; + ctempbuf[0][0]=(char)NULL; + ctempbuf[1][0]=(char)NULL; + stempbuf[0][0]=(char)NULL; + stempbuf[1][0]=(char)NULL; + + if (ct_diag(sybase_sock->connection, CS_STATUS, CS_CLIENTMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Client messages"); + return msgbuf; + } + radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Client messages: %d", (int)msgcount); + + for (i=1; i<=msgcount; i++) { + if (ct_diag(sybase_sock->connection, CS_GET, CS_CLIENTMSG_TYPE, (CS_INT)i, &cmsg) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Client message"); + return msgbuf; + } + sprintf(ctempbuf[i-1],"rlm_sql_sybase: Client Library Error: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s", + (long)CS_SEVERITY(cmsg.severity), + (long)CS_NUMBER(cmsg.msgnumber), + (long)CS_ORIGIN(cmsg.msgnumber), + (long)CS_LAYER(cmsg.msgnumber), + cmsg.msgstring); + } + + + if (ct_diag(sybase_sock->connection, CS_STATUS, CS_SERVERMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Server messages"); + return msgbuf; + } + radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Server messages: %d", (int)msgcount); + + for (i=1; i<=msgcount; i++) { + if (ct_diag(sybase_sock->connection, CS_GET, CS_SERVERMSG_TYPE, (CS_INT)i, &smsg) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Server message"); + return msgbuf; + } + sprintf(stempbuf[i-1],"rlm_sql_sybase: Server message: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s", + (long)CS_SEVERITY(cmsg.severity), + (long)CS_NUMBER(cmsg.msgnumber), + (long)CS_ORIGIN(cmsg.msgnumber), + (long)CS_LAYER(cmsg.msgnumber), + cmsg.msgstring); + } + sprintf(msgbuf,"%s || %s || %s || %s", ctempbuf[1], ctempbuf[2], stempbuf[1], stempbuf[2]); + + return msgbuf; +*/ + return &msg; +} + + +/************************************************************************* + * + * Function: sql_close + * + * Purpose: database specific close. Closes an open database + * connection and cleans up any open handles. + * + *************************************************************************/ +int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) { +/* + rlm_sql_oracle_sock *oracle_sock = sqlsocket->conn; + + if (oracle_sock->conn) { + OCILogoff (oracle_sock->conn, oracle_sock->errHandle); + } + + if (oracle_sock->queryHandle) { + OCIHandleFree((dvoid *)oracle_sock->queryHandle, (ub4) OCI_HTYPE_STMT); + } + if (oracle_sock->errHandle) { + OCIHandleFree((dvoid *)oracle_sock->errHandle, (ub4) OCI_HTYPE_ERROR); + } + if (oracle_sock->env) { + OCIHandleFree((dvoid *)oracle_sock->env, (ub4) OCI_HTYPE_ENV); + } + + oracle_sock->conn = NULL; +*/ + return 0; +} + + +/************************************************************************* + * + * Function: sql_finish_query + * + * Purpose: End the query, such as freeing memory + * + *************************************************************************/ +int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) +{ + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + + ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL); + + if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_finish_query): Freeing command structure failed."); + return -1; + } + + return 0; +} + + + +/************************************************************************* + * + * Function: sql_finish_select_query + * + * Purpose: End the select query, such as freeing memory or result + * + *************************************************************************/ +int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn; + int i=0; + + ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL); + + if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) { + radlog(L_ERR,"rlm_sql_sybase(sql_finish_select_query): Freeing command structure failed."); + return -1; + } + + if (sybase_sock->results) { + while(sybase_sock->results[i]) free(sybase_sock->results[i++]); + free(sybase_sock->results); + sybase_sock->results=NULL; + } + + return 0; + +} + + +/************************************************************************* + * + * Function: sql_affected_rows + * + * Purpose: Return the number of rows affected by the query (update, + * or insert) + * + *************************************************************************/ +int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) { + + return sql_num_rows(sqlsocket, config); + +} + + + + +/* Exported to rlm_sql */ +rlm_sql_module_t rlm_sql_sybase = { + "rlm_sql_sybase", + sql_init_socket, + sql_destroy_socket, + sql_query, + sql_select_query, + sql_store_result, + sql_num_fields, + sql_num_rows, + sql_fetch_row, + sql_free_result, + sql_error, + sql_close, + sql_finish_query, + sql_finish_select_query, + sql_affected_rows +}; diff --git a/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.h b/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.h new file mode 100644 index 0000000..f3f572b --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.h @@ -0,0 +1,33 @@ +/* + * Sybase ctlib definitions and local datastructure(s) + */ + +#include +#include "rlm_sql.h" + + +typedef struct rlm_sql_sybase_sock { + CS_CONTEXT *context; + CS_CONNECTION *connection; + CS_COMMAND *command; + char **results; + int id; + int in_use; + struct timeval tv; +} rlm_sql_sybase_sock; + +int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config); +int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config); +int sql_checksocket(const char *facility); +int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr); +int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr); +int sql_store_result(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_num_fields(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_num_rows(SQLSOCK * sqlsocket, SQL_CONFIG *config); +SQL_ROW sql_fetch_row(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_free_result(SQLSOCK * sqlsocket, SQL_CONFIG *config); +char *sql_error(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_close(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_finish_query(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_finish_select_query(SQLSOCK * sqlsocket, SQL_CONFIG *config); +int sql_affected_rows(SQLSOCK * sqlsocket, SQL_CONFIG *config);