The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_oracle.c
Go to the documentation of this file.
1 /*
2  * sql_oracle.c Oracle (OCI) routines for rlm_sql
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * @copyright 2019 Robert Biktimirov (pobept@gmail.com)
19  * @copyright 2000,2006 The FreeRADIUS server project
20  * @copyright 2000 David Kerry (davidk@snti.com)
21  */
22 
23 RCSID("$Id: 228652d3d84af6a1dcf5f897a7f1228ed7d38b8a $")
24 
25 #define LOG_PREFIX "sql - oracle"
26 
27 #include <freeradius-devel/server/base.h>
28 #include <freeradius-devel/util/debug.h>
29 
30 #include <sys/stat.h>
31 
32 /*
33  * There are typos in the Oracle Instaclient where the definition controlling prototype
34  * format is _STDC_ (not __STDC__).
35  *
36  * There are still cases where the oracle headers do not declare ANSI C function types
37  * but this at least cuts down the errors.
38  *
39  * -Wno-strict-prototypes does the rest.
40  */
41 DIAG_OFF(unused-macros)
42 #if defined(__STDC__) && __STDC__
43 # define _STDC_
44 #endif
45 
46 #include <oci.h>
47 DIAG_ON(unused-macros)
48 
49 #include "rlm_sql.h"
50 #include "rlm_sql_trunk.h"
51 
52 typedef struct {
53  OCIEnv *env; //!< Environment handle
54  uint32_t stmt_cache_size; //!< Statement cache size
56 
57 typedef struct {
58  OCIStmt *query; //!< Query handle
59  OCIError *error; //!< Error handle
60  OCIServer *srv; //!< Server handle
61  OCISvcCtx *ctx; //!< Service handle
62  OCISession *sess; //!< Session handle
63  sb2 *ind; //!< Indicators regarding contents of the results row.
64  rlm_sql_row_t row; //!< Results row
65  int col_count; //!< Number of columns associated with the result set
66  connection_t *conn; //!< Generic connection structure for this connection.
67  rlm_sql_config_t const *config; //!< SQL instance configuration.
68  fr_sql_query_t *query_ctx; //!< Current request running on the connection.
69  fr_event_timer_t const *read_ev; //!< Timer event for polling reading this connection
70  fr_event_timer_t const *write_ev; //!< Timer event for polling writing this connection
71  uint select_interval; //!< How frequently this connection gets polled for select queries.
72  uint query_interval; //!< How frequently this connection gets polled for other queries.
73  uint poll_count; //!< How many polls have been done for the current query.
75 
76 static const conf_parser_t driver_config[] = {
77  { FR_CONF_OFFSET("stmt_cache_size", rlm_sql_oracle_t, stmt_cache_size), .dflt = "32" },
79 };
80 
81 #define MAX_DATASTR_LEN 64
82 
83 /** Write the last Oracle error out to a buffer
84  *
85  * @param out Where to write the error (should be at least 512 bytes).
86  * @param outlen The length of the error buffer.
87  * @param conn Oracle connection.
88  * @return
89  * - Oracle error code on success.
90  * - -1 if there was no error.
91  */
92 static int sql_snprint_error(char *out, size_t outlen, rlm_sql_oracle_conn_t *conn)
93 {
94  sb4 errcode = 0;
95 
96  fr_assert(conn);
97 
98  out[0] = '\0';
99 
100  OCIErrorGet((dvoid *) conn->error, 1, (OraText *) NULL, &errcode, (OraText *) out,
101  outlen, OCI_HTYPE_ERROR);
102  if (!errcode) return -1;
103 
104  return errcode;
105 }
106 
107 /** Retrieves any errors associated with the query context
108  *
109  * @note Caller will free any memory allocated in ctx.
110  *
111  * @param ctx to allocate temporary error buffers in.
112  * @param out Array of sql_log_entrys to fill.
113  * @param outlen Length of out array.
114  * @param query_ctx Query context to retrieve error for.
115  * @param config rlm_sql config.
116  * @return number of errors written to the #sql_log_entry_t array.
117  */
118 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
119  fr_sql_query_t *query_ctx)
120 {
121  char errbuff[512];
122  int ret;
123  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
124 
125  fr_assert(outlen > 0);
126 
127  ret = sql_snprint_error(errbuff, sizeof(errbuff), conn);
128  if (ret < 0) return 0;
129 
130  out[0].type = L_ERR;
131  out[0].msg = talloc_strdup(ctx, errbuff);
132 
133  return 1;
134 }
135 
136 static int mod_detach(module_detach_ctx_t const *mctx)
137 {
138  rlm_sql_oracle_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_oracle_t);
139 
140  if (inst->env) OCIHandleFree((dvoid *)inst->env, OCI_HTYPE_ENV);
141 
142  return 0;
143 }
144 
145 static int mod_instantiate(module_inst_ctx_t const *mctx)
146 {
147  rlm_sql_oracle_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_oracle_t);
148 
149  /*
150  * Initialises the oracle environment
151  */
152  if (OCIEnvCreate(&inst->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
153  ERROR("Couldn't init Oracle OCI environment (OCIEnvCreate())");
154  return -1;
155  }
156 
157  return 0;
158 }
159 
161 {
162  char errbuff[512];
163 
164  if (sql_snprint_error(errbuff, sizeof(errbuff), conn) < 0) return -1;
165 
166  if (strstr(errbuff, "ORA-03113") || strstr(errbuff, "ORA-03114")) {
167  ERROR("OCI_SERVER_NOT_CONNECTED");
168  return RLM_SQL_RECONNECT;
169  }
170 
171  return RLM_SQL_ERROR;
172 }
173 
175 {
176  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(h, rlm_sql_oracle_conn_t);
177 
178  if (conn->sess) {
179  OCISessionEnd(conn->ctx, conn->error, conn->sess, OCI_DEFAULT);
180  OCIHandleFree((dvoid *)conn->sess, OCI_HTYPE_SESSION);
181  }
182  if (conn->ctx) OCIHandleFree((dvoid *)conn->ctx, OCI_HTYPE_SVCCTX);
183  if (conn->srv) {
184  OCIServerDetach(conn->srv, conn->error, OCI_DEFAULT);
185  OCIHandleFree((dvoid *)conn->srv, OCI_HTYPE_SERVER);
186  }
187  if (conn->error) OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
188 
189  talloc_free(h);
190 }
191 
192 #define ORACLE_ERROR(_message) \
193  sql_snprint_error(errbuff, sizeof(errbuff), c); \
194  ERROR(_message ": %s", errbuff); \
195  return CONNECTION_STATE_FAILED
196 
197 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
198 static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
199 {
201  rlm_sql_oracle_t *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_oracle_t);
203  char errbuff[512];
204  OraText *sql_password = NULL;
205  OraText *sql_login = NULL;
206 
207  MEM(c = talloc_zero(conn, rlm_sql_oracle_conn_t));
208  *c = (rlm_sql_oracle_conn_t) {
209  .conn = conn,
210  .config = &sql->config,
211  .select_interval = 1000, /* Default starting poll interval - 1ms */
212  .query_interval = 1000,
213  };
214 
215  /*
216  * Although there are simpler methods to start a connection using a connection
217  * pool, since we need to set an option on the server handle, to enable
218  * non-blocking mode, we have to follow this overly complicated sequence of
219  * handle creation.
220  */
221 
222  /*
223  * Allocate an error handle
224  */
225  if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->error, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS) {
226  ERROR("Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
228  }
229 
230  /*
231  * Allocate a server handle and attache to a connection from the pool
232  */
233  if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->srv, (ub4)OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS) {
234  ERROR("Couldn't allocate Oracle SERVER handle");
236  }
237  if (OCIServerAttach(c->srv, c->error, (CONST OraText *)sql->config.sql_db, strlen(sql->config.sql_db), (ub4)OCI_DEFAULT) != OCI_SUCCESS) {
238  ORACLE_ERROR("Failed to attach");
239  }
240 
241  /*
242  * Allocate the service handle (which queries are run on) and associate it with the server
243  */
244  if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->ctx, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS) {
245  ERROR("Couldn't allocate Oracle SERVICE handle");
247  }
248  if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->srv, 0, OCI_ATTR_SERVER, c->error) != OCI_SUCCESS) {
249  ORACLE_ERROR("Failed to link service and server handles");
250  }
251 
252 
253  if (OCIHandleAlloc((dvoid *)inst->env, (dvoid **)&c->sess, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS) {
254  ERROR("Couldn't allocate Oracle SESSION handle");
256  }
257 
258  /*
259  * We need to fix const issues between 'const char *' vs 'unsigned char *'
260  */
261  memcpy(&sql_login, &c->config->sql_login, sizeof(sql_login));
262  memcpy(&sql_password, &c->config->sql_password, sizeof(sql_password));
263  if (OCIAttrSet((dvoid *)c->sess, OCI_HTYPE_SESSION, sql_login, strlen(c->config->sql_login),
264  OCI_ATTR_USERNAME, c->error) != OCI_SUCCESS) {
265  ORACLE_ERROR("Failed to set username");
266  }
267  if (OCIAttrSet((dvoid *)c->sess, OCI_HTYPE_SESSION, sql_password, strlen(c->config->sql_password),
268  OCI_ATTR_PASSWORD, c->error) != OCI_SUCCESS) {
269  ORACLE_ERROR("Failed to set password");
270  }
271 
272  if (OCISessionBegin((dvoid *)c->ctx, c->error, c->sess, OCI_CRED_RDBMS, OCI_STMT_CACHE) != OCI_SUCCESS) {
273  ORACLE_ERROR("Failed to start session");
274  }
275 
276  if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->sess, 0, OCI_ATTR_SESSION, c->error) != OCI_SUCCESS) {
277  ORACLE_ERROR("Failed to link service and session handles");
278  }
279 
280  if (OCIAttrSet((dvoid *)c->ctx, OCI_HTYPE_SVCCTX, (dvoid *)&inst->stmt_cache_size, 0,
281  OCI_ATTR_STMTCACHESIZE, c->error) != OCI_SUCCESS) {
282  ORACLE_ERROR("Failed to set statement cache size");
283  }
284 
285  /*
286  * Set the server to be non-blocking if we can.
287  */
288  if (OCIAttrSet((dvoid *)c->srv, OCI_HTYPE_SERVER, (dvoid *)0, 0, OCI_ATTR_NONBLOCKING_MODE, c->error) != OCI_SUCCESS) {
289  sql_snprint_error(errbuff, sizeof(errbuff), c);
290  WARN("Cound not set non-blocking mode: %s", errbuff);
291  }
292 
293  *h = c;
294 
296 }
297 
299 
300 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
301 static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
302 {
303  rlm_sql_oracle_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
304  request_t *request;
305  trunk_request_t *treq;
306  fr_sql_query_t *query_ctx;
307  sword ret;
308  char errbuff[512];
309 
310  if (trunk_connection_pop_request(&treq, tconn) != 0) return;
311  if (!treq) return;
312 
313  query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
314  request = query_ctx->request;
315 
316  switch(query_ctx->status) {
317  case SQL_QUERY_PREPARED:
318  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
319  if (OCIStmtPrepare2(sql_conn->ctx, &sql_conn->query, sql_conn->error,
320  (const OraText *)query_ctx->query_str, strlen(query_ctx->query_str),
321  NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT)) {
322  sql_snprint_error(errbuff, sizeof(errbuff), sql_conn);
323  ERROR("Failed to prepare query: %s", errbuff);
325  return;
326  }
327 
328  switch (query_ctx->type) {
329  case SQL_QUERY_SELECT:
330  ret = OCIStmtExecute(sql_conn->ctx, sql_conn->query, sql_conn->error, 0, 0, NULL, NULL, OCI_DEFAULT);
331  break;
332 
333  default:
334  ret = OCIStmtExecute(sql_conn->ctx, sql_conn->query, sql_conn->error, 1, 0, NULL, NULL,
335  OCI_COMMIT_ON_SUCCESS);
336  break;
337  }
338  query_ctx->tconn = tconn;
339 
340  switch (ret) {
341  case OCI_STILL_EXECUTING:
342  ROPTIONAL(RDEBUG3, DEBUG3, "Awaiting response");
343  query_ctx->status = SQL_QUERY_SUBMITTED;
344  sql_conn->query_ctx = query_ctx;
345  sql_conn->poll_count = 0;
347  return;
348 
349  case OCI_SUCCESS:
350  query_ctx->rcode = RLM_SQL_OK;
351  break;
352 
353  case OCI_NO_DATA:
354  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
355  break;
356 
357  default:
358  /*
359  * Error code 1 is unique contraint violated
360  */
361  if (sql_snprint_error(errbuff, sizeof(errbuff), sql_conn) == 1) {
362  query_ctx->rcode = RLM_SQL_ALT_QUERY;
363  break;
364  }
365  ERROR("SQL query failed: %s", errbuff);
367  if (sql_check_reconnect(sql_conn) == RLM_SQL_RECONNECT) {
369  }
370  return;
371  }
372  query_ctx->status = SQL_QUERY_RETURNED;
373  break;
374 
375  default:
376  return;
377  }
378 
379  ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
381  if (request) unlang_interpret_mark_runnable(request);
382 }
383 
384 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
385 static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
386  UNUSED void *uctx)
387 {
388  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
389  rlm_sql_oracle_conn_t *sql_conn= talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
390 
391  if (!query_ctx->treq) return;
392  if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
393  if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
394 }
395 
396 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
397 static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
398  connection_t *conn, UNUSED void *uctx)
399 {
400  trunk_request_t *treq;
401  rlm_sql_oracle_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
402  sword status;
403 
404  if ((trunk_connection_pop_cancellation(&treq, tconn)) != 0) return;
405  if (!treq) return;
406 
407  /*
408  * Oracle cancels non-blocking operations with this pair of calls
409  * They operate on the service context handle, and since only one
410  * query will be on a connection at a time, that is what will be cancelled.
411  *
412  * It's not clear from the documentation as to whether this can return
413  * OCI_STILL_EXECUTING - so we allow for that.
414  */
415  status = OCIBreak(sql_conn->ctx, sql_conn->error);
416  switch (status) {
417  case OCI_STILL_EXECUTING:
419  return;
420 
421  case OCI_SUCCESS:
422  break;
423 
424  default:
425  {
426  char errbuff[512];
427  sql_snprint_error(errbuff, sizeof(errbuff), sql_conn);
428  ERROR("Failed cancelling query: %s", errbuff);
429  }
430  }
431  OCIReset(sql_conn->ctx, sql_conn->error) ;
432 
434 }
435 
437 {
438  rlm_sql_oracle_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_oracle_conn_t);
439  fr_sql_query_t *query_ctx = c->query_ctx;
440  trunk_request_t *treq = query_ctx->treq;
441  request_t *request = query_ctx->request;
442  sword ret = OCI_SUCCESS;
443 
444  switch (query_ctx->status) {
445  case SQL_QUERY_SUBMITTED:
446  switch (query_ctx->type) {
447  case SQL_QUERY_SELECT:
448  ret = OCIStmtExecute(c->ctx, c->query, c->error, 0, 0, NULL, NULL, OCI_DEFAULT);
449  break;
450 
451  default:
452  ret = OCIStmtExecute(c->ctx, c->query, c->error, 1, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS);
453  break;
454  }
455  c->poll_count++;
456  /* Back off the poll interval, up to half the query timeout */
457  if (c->poll_count > 2) {
458  if (query_ctx->type == SQL_QUERY_SELECT) {
460  } else {
462  }
463  }
464 
465  switch (ret) {
466  case OCI_STILL_EXECUTING:
467  ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
468  if (fr_event_timer_in(c, el, &c->read_ev,
471  ERROR("Unable to insert polling event");
472  }
473  return;
474 
475  case OCI_SUCCESS:
476  case OCI_NO_DATA:
477  query_ctx->rcode = ret == OCI_NO_DATA ? RLM_SQL_NO_MORE_ROWS : RLM_SQL_OK;
478  /* If we only polled once, reduce the interval*/
479  if (c->poll_count == 1) {
480  if (query_ctx->type == SQL_QUERY_SELECT) {
481  c->select_interval /= 2;
482  } else {
483  c->query_interval /= 2;
484  }
485  }
486  break;
487 
488  default:
489  {
490  char errbuff[512];
491  if (sql_snprint_error(errbuff, sizeof(errbuff), c) == 1) {
492  query_ctx->rcode = RLM_SQL_ALT_QUERY;
493  break;
494  }
495  ROPTIONAL(RERROR, ERROR, "Query failed: %s", errbuff);
496  query_ctx->status = SQL_QUERY_FAILED;
499  return;
500  }
501  }
502  break;
503 
504  case SQL_QUERY_CANCELLED:
505  ret = OCIBreak(c->ctx, c->error);
506  if (ret == OCI_STILL_EXECUTING) {
507  ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
510  ERROR("Unable to insert polling event");
511  }
512  return;
513  }
514  OCIReset(c->ctx, c->error);
516  return;
517 
518  default:
519  return;
520  }
521 
522  if (request) unlang_interpret_mark_runnable(request);
523 }
524 
526 {
527  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
528 
530 }
531 
532 /*
533  * Oracle doesn't support event driven async, so in this case
534  * we have to resort to polling.
535  *
536  * This "notify" callback sets up the appropriate polling events.
537  */
538 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
539 static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el,
540  trunk_connection_event_t notify_on, UNUSED void *uctx)
541 {
542  rlm_sql_oracle_conn_t *c = talloc_get_type_abort(conn->h, rlm_sql_oracle_conn_t);
543  fr_sql_query_t *query_ctx = c->query_ctx;
544  uint poll_interval = (query_ctx && query_ctx->type != SQL_QUERY_SELECT) ? c->query_interval : c->select_interval;
545  switch (notify_on) {
549  return;
550 
553  if (c->query_ctx) {
554  if (fr_event_timer_in(c, el, &c->read_ev, fr_time_delta_from_usec(poll_interval),
556  ERROR("Unable to insert polling event");
557  }
558  }
559  if (notify_on == TRUNK_CONN_EVENT_READ) return;
560 
561  FALL_THROUGH;
562 
565  sql_trunk_connection_write_poll, tconn) < 0) {
566  ERROR("Unable to insert polling event");
567  }
568  return;
569  }
570 }
571 
574 
575 static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
576 {
577  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
578  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
579  rlm_sql_row_t row = NULL;
580  sb2 *ind;
581  int i;
582  OCIParam *param;
583  OCIDefine *define;
584  ub2 dtype, dsize;
585 
586  if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
587 
588  /*
589  * We only need to do this once per result set, because
590  * the number of columns won't change.
591  */
592  if (conn->col_count == 0) {
593  if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&conn->col_count, NULL,
594  OCI_ATTR_PARAM_COUNT, conn->error)) goto error;
595 
596  if (conn->col_count == 0) goto error;
597  }
598 
599  MEM(row = talloc_zero_array(conn, char*, conn->col_count + 1));
600  MEM(ind = talloc_zero_array(row, sb2, conn->col_count + 1));
601 
602  for (i = 0; i < conn->col_count; i++) {
603  if (OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)&param, i + 1) != OCI_SUCCESS) {
604  ERROR("OCIParamGet() failed in sql_select_query");
605  goto error;
606  }
607 
608  if (OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
609  conn->error) != OCI_SUCCESS) {
610  ERROR("OCIAttrGet() failed in sql_select_query");
611  goto error;
612  }
613 
614  dsize = MAX_DATASTR_LEN;
615 
616  /*
617  * Use the retrieved length of dname to allocate an output buffer, and then define the output
618  * variable (but only for char/string type columns).
619  */
620  switch (dtype) {
621 #ifdef SQLT_AFC
622  case SQLT_AFC: /* ansii fixed char */
623 #endif
624 #ifdef SQLT_AFV
625  case SQLT_AFV: /* ansii var char */
626 #endif
627  case SQLT_VCS: /* var char */
628  case SQLT_CHR: /* char */
629  case SQLT_STR: /* string */
630  if (OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
631  OCI_ATTR_DATA_SIZE, conn->error) != OCI_SUCCESS) {
632  ERROR("OCIAttrGet() failed in sql_select_query");
633  goto error;
634  }
635 
636  FALL_THROUGH;
637  case SQLT_DAT:
638  case SQLT_INT:
639  case SQLT_UIN:
640  case SQLT_FLT:
641  case SQLT_PDN:
642  case SQLT_BIN:
643  case SQLT_NUM:
644  MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
645 
646  break;
647  default:
648  dsize = 0;
649  row[i] = NULL;
650  break;
651  }
652 
653  ind[i] = 0;
654 
655  /*
656  * Grab the actual row value and write it to the buffer we allocated.
657  */
658  if (OCIDefineByPos(conn->query, &define, conn->error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
659  (dvoid *)&ind[i], NULL, NULL, OCI_DEFAULT) != OCI_SUCCESS) {
660  ERROR("OCIDefineByPos() failed in sql_select_query");
661  goto error;
662  }
663  }
664 
665  conn->row = row;
666  conn->ind = ind;
667 
668  query_ctx->rcode = RLM_SQL_OK;
670 
671  error:
672  talloc_free(row);
673 
674  query_ctx->rcode = RLM_SQL_ERROR;
676 }
677 
678 static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
679 {
680  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
681  int fields, i, status;
682  char const **names;
683  OCIParam *param;
684 
685  if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&fields, NULL, OCI_ATTR_PARAM_COUNT,
686  conn->error)) return RLM_SQL_ERROR;
687  if (fields == 0) return RLM_SQL_ERROR;
688 
689  MEM(names = talloc_array(query_ctx, char const *, fields));
690 
691  for (i = 0; i < fields; i++) {
692  OraText *pcol_name = NULL;
693  ub4 pcol_size = 0;
694 
695  status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)&param, i + 1);
696  if (status != OCI_SUCCESS) {
697  ERROR("OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
698  error:
700 
701  return RLM_SQL_ERROR;
702  }
703 
704  status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
705  OCI_ATTR_NAME, conn->error);
706  if (status != OCI_SUCCESS) {
707  ERROR("OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
708 
709  goto error;
710  }
711 
712  names[i] = (char const *)pcol_name;
713  }
714 
715  *out = names;
716 
717  return RLM_SQL_OK;
718 }
719 
721 {
722  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
723  ub4 rows = 0;
724  ub4 size = sizeof(ub4);
725 
726  OCIAttrGet((CONST dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->error);
727 
728  return rows;
729 }
730 
731 static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
732 {
733  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
734  int status = OCI_STILL_EXECUTING;
735  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
736 
737  if (!conn->ctx) {
738  ERROR("Socket not connected");
739 
740  query_ctx->rcode = RLM_SQL_RECONNECT;
742  }
743 
744  query_ctx->row = NULL;
745 
746  while (status == OCI_STILL_EXECUTING) {
747  status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
748  }
749  if (status == OCI_SUCCESS) {
750  query_ctx->row = conn->row;
751 
752  query_ctx->rcode = RLM_SQL_OK;
754  }
755 
756  if (status == OCI_NO_DATA) {
757  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
759  }
760 
761  if (status == OCI_ERROR) {
762  ERROR("fetch failed in sql_fetch_row");
763  query_ctx->rcode = sql_check_reconnect(conn);
765  }
766 
767  query_ctx->rcode = RLM_SQL_ERROR;
769 }
770 
772 {
773  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
774  int status = OCI_STILL_EXECUTING;
775 
776  /* Cancel the cursor first */
777  while (status == OCI_STILL_EXECUTING) {
778  status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
779  }
780 
781  TALLOC_FREE(conn->row);
782  conn->ind = NULL; /* ind is a child of row */
783  conn->col_count = 0;
784  conn->query_ctx = NULL;
785 
786  if (OCIStmtRelease(conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
787  ERROR("OCI release failed in sql_finish_query");
788  return RLM_SQL_ERROR;
789  }
790 
791  return RLM_SQL_OK;
792 }
793 
795 {
796  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
797 
798  conn->query_ctx = NULL;
799 
800  if (OCIStmtRelease(conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
801  ERROR("OCI release failed in sql_finish_query");
802  return RLM_SQL_ERROR;
803  }
804 
805  return 0;
806 }
807 
809 {
810  rlm_sql_oracle_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_oracle_conn_t);
811 
812  TALLOC_FREE(conn->row);
813  conn->ind = NULL; /* ind is a child of row */
814  conn->col_count = 0;
815  conn->query_ctx = NULL;
816 
817  if (OCIStmtRelease (conn->query, conn->error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
818  ERROR("OCI release failed in sql_finish_query");
819  return RLM_SQL_ERROR;
820  }
821 
822  return 0;
823 }
824 
825 /* Exported to rlm_sql */
828  .common = {
829  .name = "sql_oracle",
830  .magic = MODULE_MAGIC_INIT,
831  .inst_size = sizeof(rlm_sql_oracle_t),
834  .detach = mod_detach
835  },
837  .sql_query_resume = sql_query_resume,
838  .sql_select_query_resume = sql_select_query_resume,
839  .sql_num_rows = sql_num_rows,
840  .sql_affected_rows = sql_num_rows,
841  .sql_fetch_row = sql_fetch_row,
842  .sql_fields = sql_fields,
843  .sql_free_result = sql_free_result,
844  .sql_error = sql_error,
845  .sql_finish_query = sql_finish_query,
846  .sql_finish_select_query = sql_finish_select_query,
847  .uses_trunks = true,
848  .trunk_io_funcs = {
849  .connection_alloc = sql_trunk_connection_alloc,
850  .connection_notify = sql_trunk_connection_notify,
851  .request_mux = sql_trunk_request_mux,
852  .request_cancel_mux = sql_request_cancel_mux,
853  .request_cancel = sql_request_cancel,
854  .request_fail = sql_request_fail,
855  }
856 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define define
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define DIAG_ON(_x)
Definition: build.h:456
#define UNUSED
Definition: build.h:313
#define DIAG_OFF(_x)
Definition: build.h:455
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
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
#define fr_event_timer_in(...)
Definition: event.h:255
switch(errno)
Definition: fd_errno.h:4
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
#define ind(mm, x)
Definition: isaac.c:18
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition: event.c:1611
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
@ L_ERR
Error message.
Definition: log.h:56
unsigned int uint32_t
Definition: merged_model.c:33
module_instance_t * mi
Module instance to detach.
Definition: module_ctx.h:57
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for detach calls.
Definition: module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
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
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
char const * sql_db
Database to run queries against.
Definition: rlm_sql.h:78
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
fr_sql_query_type_t type
Type of query.
Definition: rlm_sql.h:145
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
@ SQL_QUERY_SELECT
Definition: rlm_sql.h:121
fr_time_delta_t query_timeout
How long to allow queries to run for.
Definition: rlm_sql.h:94
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:172
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
char const * sql_login
Login credentials to use.
Definition: rlm_sql.h:76
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
char const * sql_password
Login password to use.
Definition: rlm_sql.h:77
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:143
@ SQL_QUERY_CANCELLED
A cancellation has been sent to the server.
Definition: rlm_sql.h:134
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
@ SQL_QUERY_FAILED
Failed to submit.
Definition: rlm_sql.h:128
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition: rlm_sql.h:130
@ SQL_QUERY_PREPARED
Ready to submit.
Definition: rlm_sql.h:129
Definition: rlm_sql.h:61
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Definition: rlm_sql_db2.c:301
rlm_sql_config_t const * config
SQL instance configuration.
int col_count
Number of columns associated with the result set.
OCISession * sess
Session handle.
rlm_sql_driver_t rlm_sql_oracle
static int mod_detach(module_detach_ctx_t const *mctx)
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCISvcCtx * ctx
Service handle.
rlm_sql_row_t row
Results row.
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIServer * srv
Server handle.
fr_sql_query_t * query_ctx
Current request running on the connection.
static size_t sql_error(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 void sql_trunk_connection_write_poll(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
uint32_t stmt_cache_size
Statement cache size.
static sql_rcode_t sql_finish_select_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static int sql_snprint_error(char *out, size_t outlen, rlm_sql_oracle_conn_t *conn)
Write the last Oracle error out to a buffer.
#define MAX_DATASTR_LEN
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
uint poll_count
How many polls have been done for the current query.
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static sql_rcode_t sql_check_reconnect(rlm_sql_oracle_conn_t *conn)
OCIEnv * env
Environment handle.
OCIError * error
Error handle.
uint select_interval
How frequently this connection gets polled for select queries.
uint query_interval
How frequently this connection gets polled for other queries.
static void sql_trunk_connection_read_poll(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static const conf_parser_t driver_config[]
fr_event_timer_t const * read_ev
Timer event for polling reading this connection.
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIStmt * query
Query handle.
CC_NO_UBSAN(function)
#define ORACLE_ERROR(_message)
fr_event_timer_t const * write_ev
Timer event for polling writing this connection.
connection_t * conn
Generic connection structure for this connection.
static int mod_instantiate(module_inst_ctx_t const *mctx)
sb2 * ind
Indicators regarding contents of the results row.
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
#define SQL_QUERY_FAIL
Definition: rlm_sql_trunk.h:64
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1167
void * data
Module's instance data.
Definition: module.h:271
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:204
module_instance_t * driver_submodule
Driver's submodule.
Definition: rlm_sql.h:245
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
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
Definition: time.h:632
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
Definition: time.h:568
"server local" time.
Definition: time.h:69
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition: trunk.c:3813
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition: trunk.c:2248
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2272
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_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition: trunk.c:3881
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_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition: trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition: trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition: trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition: trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition: trunk.h:75
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition: trunk.h:57
static fr_event_list_t * el
static size_t char ** out
Definition: value.h:997