The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_firebird.c
Go to the documentation of this file.
1 /*
2  * sql_firebird.c Part of Firebird rlm_sql driver
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * @copyright 2006 The FreeRADIUS server project
19  * @copyright 2006 Vitaly Bodzhgua (vitaly@eastera.net)
20  */
21 RCSID("$Id: e2fa5d0137bfc8d8f571a64a7fdf58eb6f0f4847 $")
22 
23 #define LOG_PREFIX "sql - firebird"
24 
25 #include "sql_fbapi.h"
26 #include <freeradius-devel/util/debug.h>
27 #include "rlm_sql_trunk.h"
28 
29 static char tpb[] = {isc_tpb_version3, isc_tpb_wait, isc_tpb_write,
30  isc_tpb_read_committed, isc_tpb_no_rec_version};
31 
32 /** Establish connection to the db
33  *
34  */
35 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
36 static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
37 {
40 
41  MEM(c = talloc_zero(conn, rlm_sql_firebird_conn_t));
42 
43  /*
44  * Firebird uses a client assigned structure to write info about output data.
45  * Based on standard authorize queries, we pre-allocate a structure
46  * for 5 columns in SELECT queries.
47  */
48  MEM(c->sqlda_out = (XSQLDA *)_talloc_array(conn, 1, XSQLDA_LENGTH(5), "XSQLDA"));
49  c->sqlda_out->sqln = 5;
50  c->sqlda_out->version = SQLDA_VERSION1;
51  c->sql_dialect = 3;
52 
53  /*
54  * Set tpb to read_committed/wait/no_rec_version
55  */
56  c->tpb = tpb;
57  c->tpb_len = NUM_ELEMENTS(tpb);
58 
59  if (fb_connect(c, &sql->config)) {
60  ERROR("Connection failed: %s", c->error);
62  }
63 
64  *h = c;
66 }
67 
69 {
70  rlm_sql_firebird_conn_t *c = talloc_get_type_abort(h, rlm_sql_firebird_conn_t);
71 
72  DEBUG2("Socket destructor called, closing socket");
73 
74  fb_commit(c);
75  if (c->dbh) {
77  isc_detach_database(c->status, &(c->dbh));
78 
79  if (fb_error(c)) WARN("Got error when closing socket: %s", c->error);
80  }
81 
82  talloc_free_children(c);
83 }
84 
86 
87 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
88 static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
89  connection_t *conn, UNUSED void *uctx)
90 {
91  rlm_sql_firebird_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_firebird_conn_t);
92  trunk_request_t *treq;
93  request_t *request;
94  fr_sql_query_t *query_ctx;
95  bool deadlock = false;
96 
97  if (trunk_connection_pop_request(&treq, tconn) != 0) return;
98  if (!treq) return;
99 
100  query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
101  request = query_ctx->request;
102  query_ctx->tconn = tconn;
103 
104  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
105 
106  try_again:
107  /*
108  * Try again query when deadlock, because in any case it
109  * will be retried.
110  */
111  if (fb_sql_query(sql_conn, query_ctx->query_str)) {
112  /* but may be lost for short sessions */
113  if ((sql_conn->sql_code == DEADLOCK_SQL_CODE) && !deadlock) {
114  ROPTIONAL(RWARN, WARN, "SQL deadlock. Retry query %s", query_ctx->query_str);
115 
116  /*
117  * @todo For non READ_COMMITED transactions put
118  * rollback here
119  * fb_rollback(conn);
120  */
121  deadlock = true;
122  goto try_again;
123  }
124 
125  if (sql_conn->sql_code == DUPLICATE_KEY_SQL_CODE) {
126  query_ctx->rcode = RLM_SQL_ALT_QUERY;
127  goto finish;
128  }
129 
130  ROPTIONAL(RERROR, ERROR, "conn_id rlm_sql_firebird,sql_query error: sql_code=%li, error='%s', query=%s",
131  (long int) sql_conn->sql_code, sql_conn->error, query_ctx->query_str);
132 
133  query_ctx->status = SQL_QUERY_FAILED;
135 
136  if (sql_conn->sql_code == DOWN_SQL_CODE) {
137  reconnect:
138  query_ctx->rcode = RLM_SQL_RECONNECT;
140  return;
141  }
142 
143  /* Free problem query */
144  if (fb_rollback(sql_conn)) {
145  //assume the network is down if rollback had failed
146  ROPTIONAL(RERROR, ERROR, "Fail to rollback transaction after previous error: %s", sql_conn->error);
147 
148  goto reconnect;
149  }
150 
151  query_ctx->rcode = RLM_SQL_ERROR;
152  return;
153  }
154 
155  query_ctx->rcode = RLM_SQL_OK;
156 finish:
157  query_ctx->status = SQL_QUERY_RETURNED;
159  if (request) unlang_interpret_mark_runnable(request);
160 }
161 
163 
164 static void sql_request_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED void *uctx)
165 {
166  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
167  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
168 
169  fb_commit(conn);
170 }
171 
172 static void sql_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx,
174 {
175  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
176 
177  query_ctx->treq = NULL;
178  if (query_ctx->rcode == RLM_SQL_OK) query_ctx->rcode = RLM_SQL_ERROR;
179 }
180 
181 /** Returns name of fields.
182  *
183  */
184 static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
185 {
186  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
187 
188  int fields, i;
189  char const **names;
190 
191  fields = conn->sqlda_out->sqld;
192  if (fields <= 0) return RLM_SQL_ERROR;
193 
194  MEM(names = talloc_array(query_ctx, char const *, fields));
195 
196  for (i = 0; i < fields; i++) names[i] = conn->sqlda_out->sqlvar[i].sqlname;
197  *out = names;
198 
199  return RLM_SQL_OK;
200 }
201 
202 /** Returns an individual row.
203  *
204  */
205 static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
206 {
207  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
208  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
209  int res;
210 
211  query_ctx->row = NULL;
212 
213  if (conn->statement_type != isc_info_sql_stmt_exec_procedure) {
214  res = fb_fetch(conn);
215  if (res == 100) {
216  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
218  }
219 
220  if (res) {
221  ERROR("Fetch problem: %s", conn->error);
222 
223  query_ctx->rcode = RLM_SQL_ERROR;
225  }
226  } else {
227  conn->statement_type = 0;
228  }
229 
230  TALLOC_FREE(conn->row);
231  query_ctx->rcode = fb_store_row(conn);
232  if (query_ctx->rcode == RLM_SQL_OK) query_ctx->row = conn->row;
233 
235 }
236 
237 /** End the query, such as freeing memory or result.
238  *
239  */
241 {
242  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
243 
244  fb_free_statement(conn);
245  talloc_free_children(conn->sqlda_out);
246  TALLOC_FREE(conn->row);
247  query_ctx->status = SQL_QUERY_PREPARED;
248 
249  return 0;
250 }
251 
252 /** Frees memory allocated for a result set.
253  *
254  */
256 {
257  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
258 
259  TALLOC_FREE(conn->row);
260  return 0;
261 }
262 
263 /** Retrieves any errors associated with the query context
264  *
265  * @note Caller will free any memory allocated in ctx.
266  *
267  * @param ctx to allocate temporary error buffers in.
268  * @param out Array of sql_log_entrys to fill.
269  * @param outlen Length of out array.
270  * @param query_ctx Query context to retrieve error for.
271  * @param config rlm_sql config.
272  * @return number of errors written to the #sql_log_entry_t array.
273  */
274 static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
275  fr_sql_query_t *query_ctx)
276 {
277  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
278 
279  fr_assert(conn);
280  fr_assert(outlen > 0);
281 
282  if (!conn->error) return 0;
283 
284  out[0].type = L_ERR;
285  out[0].msg = conn->error;
286 
287  return 1;
288 }
289 
290 /** Return the number of rows affected by the query (update, or insert)
291  *
292  */
294 {
295  rlm_sql_firebird_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_firebird_conn_t);
296 
297  return fb_affected_rows(conn);
298 }
299 
300 /* Exported to rlm_sql */
303  .common = {
304  .name = "sql_firebird",
305  .magic = MODULE_MAGIC_INIT
306  },
308  .sql_query_resume = sql_query_resume,
309  .sql_select_query_resume = sql_query_resume,
310  .sql_affected_rows = sql_affected_rows,
311  .sql_fetch_row = sql_fetch_row,
312  .sql_fields = sql_fields,
313  .sql_free_result = sql_free_result,
314  .sql_error = sql_error,
315  .sql_finish_query = sql_finish_query,
316  .sql_finish_select_query = sql_finish_query,
317  .uses_trunks = true,
318  .trunk_io_funcs = {
319  .connection_alloc = sql_trunk_connection_alloc,
320  .request_mux = sql_trunk_request_mux,
321  .request_complete = sql_request_complete,
322  .request_fail = sql_request_fail
323  }
324 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RWARN(fmt,...)
Definition: log.h:297
#define RERROR(fmt,...)
Definition: log.h:298
Stores all information relating to an event list.
Definition: event.c:411
@ L_ERR
Error message.
Definition: log.h:56
static const conf_parser_t config[]
Definition: base.c:183
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define WARN(fmt,...)
Definition: radclient.h:47
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
fr_sql_query_status_t status
Status of the query.
Definition: rlm_sql.h:146
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition: rlm_sql.h:142
char const * query_str
Query string to run.
Definition: rlm_sql.h:144
request_t * request
Request this query relates to.
Definition: rlm_sql.h:139
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:44
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition: rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:50
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:172
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:148
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:147
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:143
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
@ SQL_QUERY_FAILED
Failed to submit.
Definition: rlm_sql.h:128
@ SQL_QUERY_PREPARED
Ready to submit.
Definition: rlm_sql.h:129
Definition: rlm_sql.h:61
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)
Returns name of fields.
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
End the query, such as freeing memory or result.
rlm_sql_driver_t rlm_sql_firebird
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
Returns an individual row.
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static char tpb[]
static SQL_QUERY_RESUME void sql_request_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED void *uctx)
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Frees memory allocated for a result set.
static void sql_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
CC_NO_UBSAN(function)
Establish connection to the db.
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Return the number of rows affected by the query (update, or insert)
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_QUERY_RESUME
Definition: rlm_sql_trunk.h:56
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
Definition: rlm_sql_trunk.h:35
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1167
int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:396
int fb_connect(rlm_sql_firebird_conn_t *conn, rlm_sql_config_t const *config)
Definition: sql_fbapi.c:271
int fb_rollback(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:447
int fb_error(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:63
int fb_fetch(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:331
sql_rcode_t fb_store_row(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:133
int fb_commit(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:456
int fb_affected_rows(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:413
void fb_free_statement(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:440
#define DUPLICATE_KEY_SQL_CODE
Definition: sql_fbapi.h:35
#define DEADLOCK_SQL_CODE
Definition: sql_fbapi.h:33
#define DOWN_SQL_CODE
Definition: sql_fbapi.h:34
isc_db_handle dbh
Definition: sql_fbapi.h:46
ISC_STATUS status[20]
Magic interbase status code array (holds multiple error codes used to construct more detailed error m...
Definition: sql_fbapi.h:50
rlm_sql_row_t row
Definition: sql_fbapi.h:63
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:204
rlm_sql_config_t config
Definition: rlm_sql.h:238
#define talloc_get_type_abort_const
Definition: talloc.h:282
static const char * names[8]
Definition: time.c:617
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3861
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition: trunk.c:2065
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
static fr_event_list_t * el
static size_t char ** out
Definition: value.h:997