Preliminary driver for sybase database, from mattias@nogui.se
authoraland <aland>
Tue, 11 Jun 2002 17:41:54 +0000 (17:41 +0000)
committeraland <aland>
Tue, 11 Jun 2002 17:41:54 +0000 (17:41 +0000)
src/modules/rlm_sql/drivers/rlm_sql_sybase/Makefile [new file with mode: 0644]
src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.c [new file with mode: 0644]
src/modules/rlm_sql/drivers/rlm_sql_sybase/sql_sybase.h [new file with mode: 0644]

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 (file)
index 0000000..38b8af4
--- /dev/null
@@ -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 (file)
index 0000000..a3ff8ba
--- /dev/null
@@ -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 <mattias@nogui.se>  *
+***************************************************************************/
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 (file)
index 0000000..f3f572b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Sybase ctlib definitions and local datastructure(s)
+ */
+
+#include <ctpublic.h>
+#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);