27 RCSID(
"$Id: 6c52371f05fcdb3875a248e6ce463b08a4b53f64 $")
29 #define LOG_PREFIX "sql - freetds"
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/util/debug.h>
54 #define MAX_DATASTR_LEN 256
74 if (emsgp->severity == CS_SV_INFORM) {
75 INFO(
"%s", emsgp->msgstring);
80 if ((cs_config(
context, CS_GET, CS_USERDATA, &
this,
sizeof(
this), &len) != CS_SUCCEED) || !
this) {
81 ERROR(
"failed retrieving context userdata");
86 if (this->error) TALLOC_FREE(this->error);
88 this->error =
talloc_typed_asprintf(
this,
"client error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
89 (
long)CS_SEVERITY(emsgp->severity), (
long)CS_NUMBER(emsgp->msgnumber),
90 (
long)CS_ORIGIN(emsgp->msgnumber), (
long)CS_LAYER(emsgp->msgnumber),
93 if (emsgp->osstringlen > 0) {
94 this->error = talloc_asprintf_append(this->error,
". os error: number(%ld): %s",
95 (
long)emsgp->osnumber, emsgp->osstring);
118 if (emsgp->severity == CS_SV_INFORM) {
119 INFO(
"%s", emsgp->msgstring);
124 if ((cs_config(
context, CS_GET, CS_USERDATA, &
this,
sizeof(
this), &len) != CS_SUCCEED) || !
this) {
125 ERROR(
"failed retrieving context userdata");
130 if (this->error) TALLOC_FREE(this->error);
132 this->error =
talloc_typed_asprintf(
this,
"cs error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
133 (
long)CS_SEVERITY(emsgp->severity), (
long)CS_NUMBER(emsgp->msgnumber),
134 (
long)CS_ORIGIN(emsgp->msgnumber), (
long)CS_LAYER(emsgp->msgnumber),
137 if (emsgp->osstringlen > 0) {
138 this->error = talloc_asprintf_append(this->error,
". os error: number(%ld): %s",
139 (
long)emsgp->osnumber, emsgp->osstring);
162 if ((cs_config(
context, CS_GET, CS_USERDATA, &
this,
sizeof(
this), &len) != CS_SUCCEED) || !
this) {
163 ERROR(
"failed retrieving context userdata");
171 if (this->established) {
172 INFO(
"server msg from \"%s\": severity(%ld), number(%ld), origin(%ld), "
173 "layer(%ld), procedure \"%s\": %s",
174 (msgp->svrnlen > 0) ? msgp->svrname :
"unknown",
175 (
long)msgp->msgnumber, (
long)msgp->severity, (
long)msgp->state, (
long)msgp->line,
176 (msgp->proclen > 0) ? msgp->proc :
"none", msgp->text);
178 if (this->error) TALLOC_FREE(this->error);
181 "origin(%ld), layer(%ld), procedure \"%s\": %s",
182 (msgp->svrnlen > 0) ? msgp->svrname :
"unknown",
183 (
long)msgp->msgnumber, (
long)msgp->severity, (
long)msgp->state,
185 (msgp->proclen > 0) ? msgp->proc :
"none", msgp->text);
201 CS_RETCODE results_ret;
210 if (ct_cmd_alloc(conn->
db, &conn->
command) != CS_SUCCEED) {
215 if (ct_command(conn->
command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
220 if (ct_send(conn->
command) != CS_SUCCEED) {
233 switch(ct_results(conn->
command, &result_type)) {
235 switch (result_type) {
240 "Use sql_select_query instead!");
251 ROPTIONAL(
RERROR,
ERROR,
"Result failure or unexpected result type from query, (%d)", result_type);
273 if (ct_res_info(conn->
command, CS_ROW_COUNT, &conn->
rows_affected, CS_UNUSED, NULL) != CS_SUCCEED) {
282 if ((results_ret = ct_results(conn->
command, &result_type)) == CS_SUCCEED) {
283 if (result_type != CS_CMD_DONE) {
288 switch (results_ret) {
305 results_ret = ct_results(conn->
command, &result_type);
306 switch (results_ret) {
341 if (ct_res_info(conn->
command, CS_NUMDATA, (CS_INT *)&fields, CS_UNUSED, NULL) != CS_SUCCEED) {
342 ERROR(
"sql_fields() Error retrieving column count");
349 MEM(
names = talloc_array(query_ctx,
char const *, fields));
351 for (i = 0; i < fields; i++) {
359 if (ct_describe(conn->
command, col, &datafmt) != CS_SUCCEED) {
360 ERROR(
"sql_fields() Problems with ct_describe(), column %d", col);
365 if (datafmt.namelen > 0) {
366 MEM(p = talloc_array(
names,
char, (
size_t)datafmt.namelen + 1));
367 strlcpy(p, datafmt.name, (
size_t)datafmt.namelen + 1);
395 if (!conn->
error)
return 0;
407 ct_cancel(NULL, conn->
command, CS_CANCEL_ALL);
408 if (ct_cmd_drop(conn->
command) != CS_SUCCEED) {
409 ERROR(
"freeing command structure failed");
427 CS_RETCODE results_ret;
429 CS_DATAFMT descriptor;
438 if (ct_cmd_alloc(conn->
db, &conn->
command) != CS_SUCCEED) {
443 if (ct_command(conn->
command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
448 if (ct_send(conn->
command) != CS_SUCCEED) {
453 results_ret = ct_results(conn->
command, &result_type);
454 switch (results_ret) {
456 switch (result_type) {
471 descriptor.datatype = CS_CHAR_TYPE;
472 descriptor.format = CS_FMT_NULLTERM;
474 descriptor.count = 1;
475 descriptor.locale = NULL;
477 if (ct_res_info(conn->
command, CS_NUMDATA, &conn->
colcount, CS_UNUSED, NULL) != CS_SUCCEED) {
482 rowdata = talloc_zero_array(conn,
char *, conn->
colcount + 1);
483 conn->
ind = talloc_zero_array(conn, CS_SMALLINT, conn->
colcount);
485 for (i = 0; i < conn->
colcount; i++) {
490 if (ct_bind(conn->
command, i + 1, &descriptor, rowdata[i], NULL, &conn->
ind[i]) != CS_SUCCEED) {
549 query_ctx->
tconn = tconn;
553 switch (query_ctx->
type) {
562 switch (query_ctx->
rcode) {
592 if (conn->
nulls) TALLOC_FREE(query_ctx->
row);
593 query_ctx->
row = NULL;
595 ret = ct_fetch(conn->
command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &
count);
602 if (ct_cancel(NULL, conn->
command, CS_CANCEL_ALL) == CS_FAIL) {
623 for (i = 0; i < conn->
colcount; i++) {
624 if (conn->
ind[i] < 0) {
631 query_ctx->
row = talloc_zero_array(query_ctx,
char *, conn->
colcount + 1);
632 for (i = 0; i < conn->
colcount; i++) {
633 if (conn->
ind[i] < 0)
continue;
634 query_ctx->
row[i] = talloc_strdup(query_ctx->
row, conn->
results[i]);
661 ct_cancel(NULL, conn->
command, CS_CANCEL_ALL);
662 if (ct_cmd_drop(conn->
command) != CS_SUCCEED) {
663 ERROR(
"freeing command structure failed");
677 DEBUG2(
"socket destructor called, closing socket");
680 ct_cancel(NULL, c->
command, CS_CANCEL_ALL);
681 if (ct_cmd_drop(c->
command) != CS_SUCCEED) {
682 ERROR(
"freeing command structure failed");
696 if (ct_close(c->
db, CS_UNUSED) != CS_SUCCEED) ct_close(c->
db, CS_FORCE_CLOSE);
702 ct_exit(c->
context, CS_UNUSED);
724 if (cs_ctx_alloc(CS_VERSION_100, &c->
context) != CS_SUCCEED) {
725 ERROR(
"unable to allocate CS context structure (cs_ctx_alloc())");
734 if (ct_init(c->
context, CS_VERSION_100) != CS_SUCCEED) {
735 ERROR(
"unable to initialize Client-Library");
739 if (ct_config(c->
context, CS_SET, CS_LOGIN_TIMEOUT, (CS_VOID *)&timeout_ms, CS_UNUSED, NULL) != CS_SUCCEED) {
740 ERROR(
"Setting connection timeout failed");
747 if (cs_config(c->
context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)
csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) {
748 ERROR(
"unable to install CS Library error callback");
752 if (cs_config(c->
context, CS_SET, CS_USERDATA, (CS_VOID *)&c,
sizeof(c), NULL) != CS_SUCCEED) {
753 ERROR(
"unable to set userdata pointer");
757 if (ct_callback(c->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)
clientmsg_callback) != CS_SUCCEED) {
758 ERROR(
"unable to install client message callback");
762 if (ct_callback(c->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)
servermsg_callback) != CS_SUCCEED) {
763 ERROR(
"unable to install server message callback");
770 if (ct_con_alloc(c->context, &c->db) != CS_SUCCEED) {
771 ERROR(
"unable to allocate db structure");
778 if (ct_con_props(c->db, CS_SET, CS_USERNAME,
780 ERROR(
"unable to set username for db");
784 if (ct_con_props(c->db, CS_SET, CS_PASSWORD,
785 UNCONST(CS_VOID *,
config->sql_password), strlen(
config->sql_password), NULL) != CS_SUCCEED) {
786 ERROR(
"unable to set password for db");
793 if (ct_connect(c->db,
UNCONST(CS_CHAR *,
config->sql_server), strlen(
config->sql_server)) != CS_SUCCEED) {
794 ERROR(
"unable to establish db to symbolic servername %s",
819 query_ctx->
treq = NULL;
829 .name =
"sql_freetds"
832 .sql_query_resume = sql_query_resume,
833 .sql_select_query_resume = sql_query_resume,
842 .connection_alloc = sql_trunk_connection_alloc,
843 .request_mux = sql_trunk_request_mux,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
fr_dcursor_eval_t void const * uctx
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Stores all information relating to an event list.
static const conf_parser_t config[]
rlm_rcode_t
Return codes indicating the result of the module call.
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
trunk_connection_t * tconn
Trunk connection this query is being run on.
fr_sql_query_type_t type
Type of query.
char const * query_str
Query string to run.
request_t * request
Request this query relates to.
sql_rcode_t
Action to take at end of an SQL query.
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
@ RLM_SQL_ERROR
General connection/server error.
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
rlm_sql_row_t row
Row data from the last query.
sql_rcode_t rcode
Result code.
trunk_request_t * treq
Trunk request for this query.
@ SQL_QUERY_RETURNED
Query has executed.
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
SQL_TRUNK_CONNECTION_ALLOC static SQL_QUERY_RESUME void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
CS_SMALLINT * ind
Indicators of data length / NULL.
char ** results
Result strings from statement execution.
static sql_rcode_t sql_query(request_t *request, rlm_sql_freetds_conn_t *conn, char const *query)
static CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_SERVERMSG *msgp)
Server error handler.
static CS_RETCODE CS_PUBLIC csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp)
Client error handler.
static sql_rcode_t sql_finish_select_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
CS_CONNECTION * db
Handle specifying a single connection to the database.
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
CS_COMMAND * command
A prepared statement.
static CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_CLIENTMSG *emsgp)
Client-Library error handler.
CS_INT rows_affected
Rows affected by last INSERT / UPDATE / DELETE.
bool established
Set to false once the connection has been properly established.
int colcount
How many columns are in the current result set.
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static sql_rcode_t sql_select_query(request_t *request, rlm_sql_freetds_conn_t *conn, char const *query)
Execute a query when we expected a result set.
CS_CONTEXT * context
Structure FreeTDS uses to avoid creating globals.
bool nulls
Were there any NULL values in the last row.
char * error
The last error string created by one of the call backs.
rlm_sql_driver_t rlm_sql_freetds
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
size_t strlcpy(char *dst, char const *src, size_t siz)
module_t common
Common fields for all loadable modules.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
#define talloc_get_type_abort_const
static const char * names[8]
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Associates request queues with a connection.
trunk_request_state_t
Used for sanity checks and to simplify freeing.
static fr_event_list_t * el
static size_t char ** out