The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
sql_fbapi.c
Go to the documentation of this file.
1 /*
2  * sql_fbapi.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 
22 RCSID("$Id: 27828e85c9afc01060131c650faa836c3a01ab40 $")
23 
24 #include "sql_fbapi.h"
25 
26 #include <stdarg.h>
27 
28 static void fb_set_tpb(rlm_sql_firebird_conn_t *conn, int count, ...)
29 {
30  int i;
31  va_list arg;
32 
33  va_start(arg, count);
34  MEM(conn->tpb = malloc(count));
35 
36  for (i = 0; i < count; i++) conn->tpb[i] = (char) va_arg(arg, int);
37 
38  conn->tpb_len = count;
39 
40  va_end(arg);
41 }
42 
43 
44 static void fb_dpb_add_str(char **dpb, char name, char const *value)
45 {
46  int l;
47 
48  if (!value) {
49  return;
50  }
51 
52  l = strlen(value);
53 
54  *(*dpb)++= name;
55  *(*dpb)++= (char) l;
56 
57  memmove(*dpb, value, l);
58 
59  *dpb += l;
60 }
61 
62 static void fb_set_sqlda(XSQLDA *sqlda) {
63  int i;
64 
65  for (i = 0; i < sqlda->sqld; i++) {
66  if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) {
67  MEM(sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short)));
68  } else {
69  MEM(sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen));
70  }
71 
72  if (sqlda->sqlvar[i].sqltype & 1) {
73  MEM(sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short), 1));
74  } else {
75  sqlda->sqlvar[i].sqlind = 0;
76  }
77  }
78 }
79 
80 DIAG_OFF(deprecated-declarations)
82 {
83  ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */
84  ISC_STATUS *pstatus;
85 
86  conn->sql_code = 0;
87 
88  /*
89  * Free any previous errors.
90  */
91  TALLOC_FREE(conn->error);
92 
93  /*
94  * Check if the status array contains an error
95  */
96  if (IS_ISC_ERROR(conn->status)) {
97  conn->sql_code = isc_sqlcode(conn->status);
98 
99  /*
100  * pstatus is a pointer into the status array which is
101  * advanced by isc_interprete. It's initialised to the
102  * first element of the status array.
103  */
104  pstatus = &conn->status[0];
105 
106  /*
107  * It's deprecated because the size of the buffer isn't
108  * passed and this isn't safe. But as were passing a very
109  * large buffer it's unlikely this will be an issue, and
110  * allows us to maintain compatibility with the interbase
111  * API.
112  */
113  isc_interprete(&error[0], &pstatus);
114  conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]);
115 
116  while (isc_interprete(&error[0], &pstatus)) {
117  conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]);
118  }
119 
120  memset(&conn->status, 0, sizeof(conn->status));
121  }
122 
123  return conn->sql_code;
124 }
125 DIAG_ON(deprecated-declarations)
126 
127 void fb_free_sqlda(XSQLDA *sqlda)
128 {
129  int i;
130  for (i = 0; i < sqlda->sqld; i++) {
131  free(sqlda->sqlvar[i].sqldata);
132  free(sqlda->sqlvar[i].sqlind);
133  }
134  sqlda->sqld = 0;
135 }
136 
137 
138 
139 //Macro for NULLs check
140 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
141 
142 //Structure to manage a SQL_VARYING Firebird's data types
143 typedef struct {
144  short vary_length;
145  char vary_string[1];
146 } VARY;
147 
148 //function fb_store_row based on fiebird's apifull example
150 {
151  int dtype;
152  struct tm times;
153  ISC_QUAD bid;
154  int i;
155  XSQLVAR *var;
156  VARY * vary;
157 
158  /* assumed: id, username, attribute, value, op */
159  if (conn->row_fcount<conn->sqlda_out->sqld) {
160  i = conn->row_fcount;
161  conn->row_fcount = conn->sqlda_out->sqld;
162  conn->row = (char **) realloc(conn->row, conn->row_fcount * sizeof(char *));
163  conn->row_sizes = (int *) realloc(conn->row_sizes, conn->row_fcount * sizeof(int));
164 
165  while( i <conn->row_fcount) {
166  conn->row[i] = 0;
167  conn->row_sizes[i++] = 0;
168  }
169  }
170 
171  for (i = 0, var = conn->sqlda_out->sqlvar; i < conn->sqlda_out->sqld; var++, i++) {
172  /*
173  * Initial buffer size to store field's data is 256 bytes
174  */
175  if (conn->row_sizes[i]<256) {
176  conn->row[i] = (char *) realloc(conn->row[i], 256);
177  conn->row_sizes[i] = 256;
178  }
179 
180  if (IS_NULL(var)) {
181  strcpy(conn->row[i], "NULL");
182  continue;
183  }
184 
185  dtype = var->sqltype & ~1;
186 
187  switch (dtype) {
188  case SQL_TEXT:
189  if (conn->row_sizes[i]<= var->sqllen) {
190  conn->row_sizes[i] = var->sqllen + 1;
191  conn->row[i] = realloc(conn->row[i],
192  conn->row_sizes[i]);
193  }
194 
195  memmove(conn->row[i], var->sqldata, var->sqllen);
196  conn->row[i][var->sqllen] = 0;
197  break;
198 
199  case SQL_VARYING:
200  vary = (VARY*) var->sqldata;
201  if (conn->row_sizes[i] <= vary->vary_length) {
202  conn->row_sizes[i] = vary->vary_length + 1;
203  conn->row[i] = realloc(conn->row[i],
204  conn->row_sizes[i]);
205  }
206  memmove(conn->row[i], vary->vary_string, vary->vary_length);
207  conn->row[i][vary->vary_length] = 0;
208  break;
209 
210  case SQL_FLOAT:
211  snprintf(conn->row[i], conn->row_sizes[i], "%15g",
212  *(double ISC_FAR *) (var->sqldata));
213  break;
214 
215  case SQL_SHORT:
216  case SQL_LONG:
217  case SQL_INT64:
218  {
219  ISC_INT64 value = 0;
220  short field_width = 0;
221  short dscale = 0;
222 
223  switch (dtype) {
224  case SQL_SHORT:
225  value = (ISC_INT64) *(short *)var->sqldata;
226  field_width = 6;
227  break;
228 
229  case SQL_LONG:
230  value = (ISC_INT64) *(int *)var->sqldata;
231  field_width = 11;
232  break;
233 
234  case SQL_INT64:
235  value = (ISC_INT64) *(ISC_INT64 *)var->sqldata;
236  field_width = 21;
237  break;
238  }
239  dscale = var->sqlscale;
240 
241  if (dscale < 0) {
242  ISC_INT64 tens;
243  short j;
244 
245  tens = 1;
246  for (j = 0; j > dscale; j--) {
247  tens *= 10;
248  }
249 
250  if (value >= 0) {
251  snprintf(conn->row[i], conn->row_sizes[i], "%*lld.%0*lld",
252  field_width - 1 + dscale,
253  (ISC_INT64) value / tens,
254  -dscale,
255  (ISC_INT64) value % tens);
256  } else if ((value / tens) != 0) {
257  snprintf(conn->row[i], conn->row_sizes[i], "%*lld.%0*lld",
258  field_width - 1 + dscale,
259  (ISC_INT64) (value / tens),
260  -dscale,
261  (ISC_INT64) -(value % tens));
262  } else {
263  snprintf(conn->row[i], conn->row_sizes[i], "%*s.%0*lld", field_width - 1 + dscale,
264  "-0", -dscale, (ISC_INT64) - (value % tens));
265  }
266  } else if (dscale) {
267  snprintf(conn->row[i], conn->row_sizes[i], "%*lld%0*d", field_width,
268  (ISC_INT64) value, dscale, 0);
269  } else {
270  snprintf(conn->row[i], conn->row_sizes[i], "%*lld", field_width,
271  (ISC_INT64) value);
272  }
273  }
274  break;
275 
276  case SQL_D_FLOAT:
277  case SQL_DOUBLE:
278  snprintf(conn->row[i], conn->row_sizes[i], "%24f",
279  *(double ISC_FAR *) (var->sqldata));
280  break;
281 
282  case SQL_TIMESTAMP:
283  isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, &times);
284  snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d %02d:%02d:%02d.%04d",
285  times.tm_year + 1900,
286  times.tm_mon + 1,
287  times.tm_mday,
288  times.tm_hour,
289  times.tm_min,
290  times.tm_sec,
291  ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
292  break;
293 
294  case SQL_TYPE_DATE:
295  isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, &times);
296  snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d",
297  times.tm_year + 1900,
298  times.tm_mon + 1,
299  times.tm_mday);
300  break;
301 
302  case SQL_TYPE_TIME:
303  isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, &times);
304  snprintf(conn->row[i], conn->row_sizes[i], "%02d:%02d:%02d.%04d",
305  times.tm_hour,
306  times.tm_min,
307  times.tm_sec,
308  (*((ISC_TIME *)var->sqldata)) % 10000);
309  break;
310 
311  case SQL_BLOB:
312  case SQL_ARRAY:
313  /* Print the blob id on blobs or arrays */
314  bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
315  snprintf(conn->row[i], conn->row_sizes[i], "%08" ISC_LONG_FMT "x:%08" ISC_LONG_FMT "x",
316  bid.gds_quad_high, bid.gds_quad_low);
317  break;
318  }
319  }
320 }
321 
323 {
324  memset(conn, 0, sizeof(*conn));
325  MEM(conn->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5), 1));
326  conn->sqlda_out->sqln = 5;
327  conn->sqlda_out->version = SQLDA_VERSION1;
328  conn->sql_dialect = 3;
329 
330  pthread_mutex_init (&conn->mut, NULL);
331  DEBUG("Init mutex %p\n", &conn->mut);
332 
333  /*
334  * Set tpb to read_committed/wait/no_rec_version
335  */
336  fb_set_tpb(conn, 5, isc_tpb_version3, isc_tpb_wait, isc_tpb_write,
337  isc_tpb_read_committed, isc_tpb_no_rec_version);
338  if (!conn->tpb) {
339  return -1;
340  }
341 
342  return 0;
343 }
344 
346 {
347  char *p;
348  char *database;
349 
350  conn->dpb_len = 4;
351  if (config->sql_login) {
352  conn->dpb_len+= strlen(config->sql_login) + 2;
353  }
354 
355  if (config->sql_password) {
356  conn->dpb_len += strlen(config->sql_password) + 2;
357  }
358 
359  MEM(conn->dpb = (char *) malloc(conn->dpb_len));
360  p = conn->dpb;
361 
362  *conn->dpb++= isc_dpb_version1;
363  *conn->dpb++= isc_dpb_num_buffers;
364  *conn->dpb++= 1;
365  *conn->dpb++= 90;
366 
367  fb_dpb_add_str(&conn->dpb, isc_dpb_user_name, config->sql_login);
368  fb_dpb_add_str(&conn->dpb, isc_dpb_password, config->sql_password);
369 
370  conn->dpb = p;
371 
372  /*
373  * Check if database and server in the form of server:database.
374  * If config->sql_server contains ':', then config->sql_db
375  * parameter ignored.
376  */
377  if (strchr(config->sql_server, ':')) {
378  database = strdup(config->sql_server);
379  } else {
380  /*
381  * Make database and server to be in the form
382  * of server:database
383  */
384  int ls = strlen(config->sql_server);
385  int ld = strlen(config->sql_db);
386  MEM(database = (char *) calloc(ls + ld + 2, 1));
387  strcpy(database, config->sql_server);
388  database[ls] = ':';
389  memmove(database + ls + 1, config->sql_db, ld);
390  }
391  isc_attach_database(conn->status, 0, database, &conn->dbh,
392  conn->dpb_len, conn->dpb);
393  free(database);
394 
395  return fb_error(conn);
396 }
397 
398 
400 {
401  long fetch_stat;
402  if (conn->statement_type!= isc_info_sql_stmt_select) {
403  return 100;
404  }
405 
406  fetch_stat = isc_dsql_fetch(conn->status, &conn->stmt,
407  SQL_DIALECT_V6, conn->sqlda_out);
408  if (fetch_stat) {
409  if (fetch_stat!= 100L) {
410  fb_error(conn);
411  } else {
412  conn->sql_code = 0;
413  }
414  }
415 
416  return fetch_stat;
417 }
418 
419 static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
420 {
421  static char stmt_info[] = { isc_info_sql_stmt_type };
422  char info_buffer[128];
423  short l;
424 
425  if (!conn->trh) {
426  isc_start_transaction(conn->status, &conn->trh, 1, &conn->dbh,
427  conn->tpb_len, conn->tpb);
428  if (!conn->trh) {
429  return -4;
430  }
431  }
432 
433  fb_free_statement(conn);
434  if (!conn->stmt) {
435  isc_dsql_allocate_statement(conn->status, &conn->dbh,
436  &conn->stmt);
437  if (!conn->stmt) {
438  return -1;
439  }
440  }
441 
442  fb_free_sqlda(conn->sqlda_out);
443  isc_dsql_prepare(conn->status, &conn->trh, &conn->stmt, 0, query,
444  conn->sql_dialect, conn->sqlda_out);
445  if (IS_ISC_ERROR(conn->status)) {
446  return -2;
447  }
448 
449  if (conn->sqlda_out->sqln<conn->sqlda_out->sqld) {
450  conn->sqlda_out->sqln = conn->sqlda_out->sqld;
451  conn->sqlda_out = (XSQLDA ISC_FAR *) realloc(conn->sqlda_out,
452  XSQLDA_LENGTH(conn->sqlda_out->sqld));
453  isc_dsql_describe(conn->status, &conn->stmt, SQL_DIALECT_V6,
454  conn->sqlda_out);
455 
456  if (IS_ISC_ERROR(conn->status)) {
457  return -3;
458  }
459  }
460  /*
461  * Get statement type
462  */
463  isc_dsql_sql_info(conn->status, &conn->stmt, sizeof(stmt_info),
464  stmt_info, sizeof(info_buffer), info_buffer);
465  if (IS_ISC_ERROR(conn->status)) return -4;
466 
467  l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
468  conn->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3,
469  l);
470 
471  if (conn->sqlda_out->sqld) {
472  fb_set_sqlda(conn->sqlda_out); //set out sqlda
473  }
474 
475  return 0;
476 }
477 
478 
479 int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query) {
480  if (fb_prepare(conn, query)) {
481  return fb_error(conn);
482  }
483 
484  switch (conn->statement_type) {
485  case isc_info_sql_stmt_exec_procedure:
486  isc_dsql_execute2(conn->status, &conn->trh, &conn->stmt,
487  SQL_DIALECT_V6, 0, conn->sqlda_out);
488  break;
489 
490  default:
491  isc_dsql_execute(conn->status, &conn->trh, &conn->stmt,
492  SQL_DIALECT_V6, 0);
493  break;
494  }
495  return fb_error(conn);
496 }
497 
499  static char count_info[] = {isc_info_sql_records};
500  char info_buffer[128];
501  char *p ;
502  int affected_rows = -1;
503 
504  if (!conn->stmt) return -1;
505 
506  isc_dsql_sql_info(conn->status, &conn->stmt,
507  sizeof (count_info), count_info,
508  sizeof (info_buffer), info_buffer);
509 
510  if (IS_ISC_ERROR(conn->status)) {
511  return fb_error(conn);
512  }
513 
514  p = info_buffer + 3;
515  while (*p != isc_info_end) {
516  p++;
517  {
518  short len = (short)isc_vax_integer(p, 2);
519  p += 2;
520 
521  affected_rows = isc_vax_integer(p, len);
522  if (affected_rows > 0) {
523  break;
524  }
525  p += len;
526  }
527  }
528  return affected_rows;
529 }
530 
532  isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_close);
533 
534  return fb_error(conn);
535 }
536 
538  if (conn->stmt) {
539  isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_drop);
540  conn->stmt = 0;
541  }
542 }
543 
545  conn->sql_code = 0;
546  if (conn->trh) {
547  isc_rollback_transaction(conn->status, &conn->trh);
548 // conn->in_use = 0;
549  pthread_mutex_unlock(&conn->mut);
550 
551  if (IS_ISC_ERROR(conn->status)) {
552  return fb_error(conn);
553  }
554  }
555  return conn->sql_code;
556 }
557 
559  conn->sql_code = 0;
560  if (conn->trh) {
561  isc_commit_transaction(conn->status, &conn->trh);
562  if (IS_ISC_ERROR(conn->status)) {
563  fb_error(conn);
564  ERROR("Fail to commit. Error: %s. Try to rollback.", conn->error);
565  return fb_rollback(conn);
566  }
567  }
568 // conn->in_use = 0;
569  pthread_mutex_unlock(&conn->mut);
570 
571  return conn->sql_code;
572 }
va_end(args)
strcpy(log_entry->msg, buffer)
va_start(args, fmt)
#define RCSID(id)
Definition: build.h:444
#define DIAG_ON(_x)
Definition: build.h:419
#define DIAG_OFF(_x)
Definition: build.h:418
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
Test enumeration values.
Definition: dict_test.h:92
free(array)
static const conf_parser_t config[]
Definition: base.c:188
static char const * name
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
int fb_init_socket(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:322
short vary_length
Definition: sql_fbapi.c:144
static void fb_dpb_add_str(char **dpb, char name, char const *value)
Definition: sql_fbapi.c:44
int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:479
void fb_store_row(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:149
int fb_connect(rlm_sql_firebird_conn_t *conn, rlm_sql_config_t const *config)
Definition: sql_fbapi.c:345
static void fb_set_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:62
char vary_string[1]
Definition: sql_fbapi.c:145
int fb_rollback(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:544
int fb_error(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:81
int fb_fetch(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:399
int fb_close_cursor(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:531
void fb_free_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:127
static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:419
#define IS_NULL(x)
Definition: sql_fbapi.c:140
static void fb_set_tpb(rlm_sql_firebird_conn_t *conn, int count,...)
Definition: sql_fbapi.c:28
int fb_commit(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:558
int fb_affected_rows(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:498
void fb_free_statement(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:537
isc_tr_handle trh
Definition: sql_fbapi.h:47
#define IS_ISC_ERROR(status)
Definition: sql_fbapi.h:31
isc_stmt_handle stmt
Definition: sql_fbapi.h:46
isc_db_handle dbh
Definition: sql_fbapi.h:45
ISC_STATUS status[20]
Magic interbase status code array (holds multiple error codes used to construct more detailed error m...
Definition: sql_fbapi.h:49
rlm_sql_row_t row
Definition: sql_fbapi.h:62
#define ISC_LONG_FMT
Definition: sql_fbapi.h:40
pthread_mutex_t mut
Definition: sql_fbapi.h:66
return count
Definition: module.c:175
if(!subtype_vp) goto fail
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380