Annotation of embedaddon/strongswan/src/libstrongswan/plugins/mysql/mysql_database.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2013 Tobias Brunner
3: * Copyright (C) 2007 Martin Willi
4: * HSR Hochschule fuer Technik Rapperswil
5: *
6: * This program is free software; you can redistribute it and/or modify it
7: * under the terms of the GNU General Public License as published by the
8: * Free Software Foundation; either version 2 of the License, or (at your
9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10: *
11: * This program is distributed in the hope that it will be useful, but
12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14: * for more details.
15: */
16:
17: #include "mysql_database.h"
18:
19: #define _GNU_SOURCE
20: #include <string.h>
21: #include <mysql.h>
22:
23: #include <utils/debug.h>
24: #include <utils/chunk.h>
25: #include <threading/thread_value.h>
26: #include <threading/mutex.h>
27: #include <collections/linked_list.h>
28:
29: /* Older mysql.h headers do not define it, but we need it. It is not returned
30: * in in MySQL 4 by default, but by MySQL 5. To avoid this problem, we catch
31: * it in all cases. */
32: #ifndef MYSQL_DATA_TRUNCATED
33: #define MYSQL_DATA_TRUNCATED 101
34: #endif
35:
36: typedef struct private_mysql_database_t private_mysql_database_t;
37:
38: /**
39: * private data of mysql_database
40: */
41: struct private_mysql_database_t {
42:
43: /**
44: * public functions
45: */
46: mysql_database_t public;
47:
48: /**
49: * connection pool, contains conn_t
50: */
51: linked_list_t *pool;
52:
53: /**
54: * thread-specific transaction, as transaction_t
55: */
56: thread_value_t *transaction;
57:
58: /**
59: * mutex to lock pool
60: */
61: mutex_t *mutex;
62:
63: /**
64: * hostname to connect to
65: */
66: char *host;
67:
68: /**
69: * username to use
70: */
71: char *username;
72:
73: /**
74: * password
75: */
76: char *password;
77:
78: /**
79: * database name
80: */
81: char *database;
82:
83: /**
84: * tcp port
85: */
86: int port;
87: };
88:
89: typedef struct conn_t conn_t;
90:
91: /**
92: * connection pool entry
93: */
94: struct conn_t {
95:
96: /**
97: * MySQL database connection
98: */
99: MYSQL *mysql;
100:
101: /**
102: * connection in use?
103: */
104: bool in_use;
105: };
106:
107: /**
108: * database transaction
109: */
110: typedef struct {
111:
112: /**
113: * Reference to the specific connection we started the transaction on
114: */
115: conn_t *conn;
116:
117: /**
118: * Refcounter if transaction() is called multiple times
119: */
120: refcount_t refs;
121:
122: /**
123: * TRUE if transaction was rolled back
124: */
125: bool rollback;
126:
127: } transaction_t;
128:
129: /**
130: * Release a mysql connection
131: */
132: static void conn_release(private_mysql_database_t *this, conn_t *conn)
133: {
134: /* do not release the connection while transactions are using it */
135: if (!this->transaction->get(this->transaction))
136: {
137: this->mutex->lock(this->mutex);
138: conn->in_use = FALSE;
139: this->mutex->unlock(this->mutex);
140: }
141: }
142:
143: /**
144: * Destroy a transaction and release the connection
145: */
146: static void transaction_destroy(private_mysql_database_t *this,
147: transaction_t *trans)
148: {
149: conn_release(this, trans->conn);
150: free(trans);
151: }
152:
153: /**
154: * thread specific initialization flag
155: */
156: thread_value_t *initialized;
157:
158: /**
159: * Initialize a thread for mysql usage
160: */
161: static void thread_initialize()
162: {
163: if (initialized->get(initialized) == NULL)
164: {
165: initialized->set(initialized, (void*)TRUE);
166: mysql_thread_init();
167: }
168: }
169:
170: /**
171: * mysql library initialization function
172: */
173: bool mysql_database_init()
174: {
175: if (mysql_library_init(0, NULL, NULL))
176: {
177: return FALSE;
178: }
179: initialized = thread_value_create((thread_cleanup_t)mysql_thread_end);
180: return TRUE;
181: }
182:
183: /**
184: * mysql library cleanup function
185: */
186: void mysql_database_deinit()
187: {
188: initialized->destroy(initialized);
189: mysql_thread_end();
190: mysql_library_end();
191: }
192:
193: /**
194: * Destroy a mysql connection
195: */
196: static void conn_destroy(conn_t *this)
197: {
198: mysql_close(this->mysql);
199: free(this);
200: }
201:
202: /**
203: * Acquire/Reuse a mysql connection
204: */
205: static conn_t *conn_get(private_mysql_database_t *this, transaction_t **trans)
206: {
207: conn_t *current, *found = NULL;
208: enumerator_t *enumerator;
209: transaction_t *transaction;
210:
211: thread_initialize();
212:
213: transaction = this->transaction->get(this->transaction);
214: if (transaction)
215: {
216: if (trans)
217: {
218: *trans = transaction;
219: }
220: return transaction->conn;
221: }
222:
223: while (TRUE)
224: {
225: this->mutex->lock(this->mutex);
226: enumerator = this->pool->create_enumerator(this->pool);
227: while (enumerator->enumerate(enumerator, ¤t))
228: {
229: if (!current->in_use)
230: {
231: found = current;
232: found->in_use = TRUE;
233: break;
234: }
235: }
236: enumerator->destroy(enumerator);
237: this->mutex->unlock(this->mutex);
238: if (found)
239: { /* check connection if found, release if ping fails */
240: if (mysql_ping(found->mysql) == 0)
241: {
242: break;
243: }
244: this->mutex->lock(this->mutex);
245: this->pool->remove(this->pool, found, NULL);
246: this->mutex->unlock(this->mutex);
247: conn_destroy(found);
248: found = NULL;
249: continue;
250: }
251: break;
252: }
253: if (found == NULL)
254: {
255: INIT(found,
256: .in_use = TRUE,
257: .mysql = mysql_init(NULL),
258: );
259: if (!mysql_real_connect(found->mysql, this->host, this->username,
260: this->password, this->database, this->port,
261: NULL, 0))
262: {
263: DBG1(DBG_LIB, "connecting to mysql://%s:***@%s:%d/%s failed: %s",
264: this->username, this->host, this->port, this->database,
265: mysql_error(found->mysql));
266: conn_destroy(found);
267: found = NULL;
268: }
269: else
270: {
271: this->mutex->lock(this->mutex);
272: this->pool->insert_last(this->pool, found);
273: DBG2(DBG_LIB, "increased MySQL connection pool size to %d",
274: this->pool->get_count(this->pool));
275: this->mutex->unlock(this->mutex);
276: }
277: }
278: return found;
279: }
280:
281: /**
282: * Create and run a MySQL stmt using a sql string and args
283: */
284: static MYSQL_STMT* run(MYSQL *mysql, char *sql, va_list *args)
285: {
286: MYSQL_STMT *stmt;
287: int params;
288:
289: stmt = mysql_stmt_init(mysql);
290: if (stmt == NULL)
291: {
292: DBG1(DBG_LIB, "creating MySQL statement failed: %s",
293: mysql_error(mysql));
294: return NULL;
295: }
296: if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
297: {
298: DBG1(DBG_LIB, "preparing MySQL statement failed: %s",
299: mysql_stmt_error(stmt));
300: mysql_stmt_close(stmt);
301: return NULL;
302: }
303: params = mysql_stmt_param_count(stmt);
304: if (params > 0)
305: {
306: int i;
307: MYSQL_BIND *bind;
308:
309: bind = alloca(sizeof(MYSQL_BIND) * params);
310: memset(bind, 0, sizeof(MYSQL_BIND) * params);
311:
312: for (i = 0; i < params; i++)
313: {
314: switch (va_arg(*args, db_type_t))
315: {
316: case DB_INT:
317: {
318: bind[i].buffer_type = MYSQL_TYPE_LONG;
319: bind[i].buffer = (char*)alloca(sizeof(int));
320: *(int*)bind[i].buffer = va_arg(*args, int);
321: bind[i].buffer_length = sizeof(int);
322: break;
323: }
324: case DB_UINT:
325: {
326: bind[i].buffer_type = MYSQL_TYPE_LONG;
327: bind[i].buffer = (char*)alloca(sizeof(u_int));
328: *(u_int*)bind[i].buffer = va_arg(*args, u_int);
329: bind[i].buffer_length = sizeof(u_int);
330: bind[i].is_unsigned = TRUE;
331: break;
332: }
333: case DB_TEXT:
334: {
335: bind[i].buffer_type = MYSQL_TYPE_STRING;;
336: bind[i].buffer = va_arg(*args, char*);
337: if (bind[i].buffer)
338: {
339: bind[i].buffer_length = strlen(bind[i].buffer);
340: }
341: break;
342: }
343: case DB_BLOB:
344: {
345: chunk_t chunk = va_arg(*args, chunk_t);
346: bind[i].buffer_type = MYSQL_TYPE_BLOB;
347: bind[i].buffer = chunk.ptr;
348: bind[i].buffer_length = chunk.len;
349: break;
350: }
351: case DB_DOUBLE:
352: {
353: bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
354: bind[i].buffer = (char*)alloca(sizeof(double));
355: *(double*)bind[i].buffer = va_arg(*args, double);
356: bind[i].buffer_length = sizeof(double);
357: break;
358: }
359: case DB_NULL:
360: {
361: bind[i].buffer_type = MYSQL_TYPE_NULL;
362: break;
363: }
364: default:
365: DBG1(DBG_LIB, "invalid data type supplied");
366: mysql_stmt_close(stmt);
367: return NULL;
368: }
369: }
370: if (mysql_stmt_bind_param(stmt, bind))
371: {
372: DBG1(DBG_LIB, "binding MySQL param failed: %s",
373: mysql_stmt_error(stmt));
374: mysql_stmt_close(stmt);
375: return NULL;
376: }
377: }
378: if (mysql_stmt_execute(stmt))
379: {
380: DBG1(DBG_LIB, "executing MySQL statement failed: %s",
381: mysql_stmt_error(stmt));
382: mysql_stmt_close(stmt);
383: return NULL;
384: }
385: return stmt;
386: }
387:
388: typedef struct {
389: /** implements enumerator_t */
390: enumerator_t public;
391: /** mysql database */
392: private_mysql_database_t *db;
393: /** associated MySQL statement */
394: MYSQL_STMT *stmt;
395: /** result bindings */
396: MYSQL_BIND *bind;
397: /** pooled connection handle */
398: conn_t *conn;
399: /** value for INT, UINT, double */
400: union {
401: void *p_void;;
402: int *p_int;
403: u_int *p_uint;
404: double *p_double;
405: } val;
406: /* length for TEXT and BLOB */
407: unsigned long *length;
408: } mysql_enumerator_t;
409:
410: METHOD(enumerator_t, mysql_enumerator_destroy, void,
411: mysql_enumerator_t *this)
412: {
413: int columns, i;
414:
415: columns = mysql_stmt_field_count(this->stmt);
416:
417: for (i = 0; i < columns; i++)
418: {
419: switch (this->bind[i].buffer_type)
420: {
421: case MYSQL_TYPE_STRING:
422: case MYSQL_TYPE_BLOB:
423: {
424: free(this->bind[i].buffer);
425: break;
426: }
427: default:
428: break;
429: }
430: }
431: mysql_stmt_close(this->stmt);
432: conn_release(this->db, this->conn);
433: free(this->bind);
434: free(this->val.p_void);
435: free(this->length);
436: free(this);
437: }
438:
439: METHOD(enumerator_t, mysql_enumerator_enumerate, bool,
440: mysql_enumerator_t *this, va_list args)
441: {
442: int i, columns;
443:
444: columns = mysql_stmt_field_count(this->stmt);
445:
446: /* free/reset data set of previous call */
447: for (i = 0; i < columns; i++)
448: {
449: switch (this->bind[i].buffer_type)
450: {
451: case MYSQL_TYPE_STRING:
452: case MYSQL_TYPE_BLOB:
453: {
454: free(this->bind[i].buffer);
455: this->bind[i].buffer = NULL;
456: this->bind[i].buffer_length = 0;
457: this->bind[i].length = &this->length[i];
458: this->length[i] = 0;
459: break;
460: }
461: default:
462: break;
463: }
464: }
465:
466: switch (mysql_stmt_fetch(this->stmt))
467: {
468: case 0:
469: case MYSQL_DATA_TRUNCATED:
470: break;
471: case MYSQL_NO_DATA:
472: return FALSE;
473: default:
474: DBG1(DBG_LIB, "fetching MySQL row failed: %s",
475: mysql_stmt_error(this->stmt));
476: return FALSE;
477: }
478:
479: for (i = 0; i < columns; i++)
480: {
481: switch (this->bind[i].buffer_type)
482: {
483: case MYSQL_TYPE_LONG:
484: {
485: if (this->bind[i].is_unsigned)
486: {
487: u_int *value = va_arg(args, u_int*);
488: *value = this->val.p_uint[i];
489: }
490: else
491: {
492: int *value = va_arg(args, int*);
493: *value = this->val.p_int[i];
494: }
495: break;
496: }
497: case MYSQL_TYPE_STRING:
498: {
499: char **value = va_arg(args, char**);
500: this->bind[i].buffer = malloc(this->length[i]+1);
501: this->bind[i].buffer_length = this->length[i];
502: *value = this->bind[i].buffer;
503: mysql_stmt_fetch_column(this->stmt, &this->bind[i], i, 0);
504: ((char*)this->bind[i].buffer)[this->length[i]] = '\0';
505: break;
506: }
507: case MYSQL_TYPE_BLOB:
508: {
509: chunk_t *value = va_arg(args, chunk_t*);
510: this->bind[i].buffer = malloc(this->length[i]);
511: this->bind[i].buffer_length = this->length[i];
512: value->ptr = this->bind[i].buffer;
513: value->len = this->length[i];
514: mysql_stmt_fetch_column(this->stmt, &this->bind[i], i, 0);
515: break;
516: }
517: case MYSQL_TYPE_DOUBLE:
518: {
519: double *value = va_arg(args, double*);
520: *value = this->val.p_double[i];
521: break;
522: }
523: default:
524: break;
525: }
526: }
527: return TRUE;
528: }
529:
530: METHOD(database_t, query, enumerator_t*,
531: private_mysql_database_t *this, char *sql, ...)
532: {
533: MYSQL_STMT *stmt;
534: va_list args;
535: mysql_enumerator_t *enumerator = NULL;
536: conn_t *conn;
537:
538: conn = conn_get(this, NULL);
539: if (!conn)
540: {
541: return NULL;
542: }
543:
544: va_start(args, sql);
545: stmt = run(conn->mysql, sql, &args);
546: if (stmt)
547: {
548: int columns, i;
549:
550: INIT(enumerator,
551: .public = {
552: .enumerate = enumerator_enumerate_default,
553: .venumerate = _mysql_enumerator_enumerate,
554: .destroy = _mysql_enumerator_destroy,
555: },
556: .db = this,
557: .stmt = stmt,
558: .conn = conn,
559: );
560: columns = mysql_stmt_field_count(stmt);
561: enumerator->bind = calloc(columns, sizeof(MYSQL_BIND));
562: enumerator->length = calloc(columns, sizeof(unsigned long));
563: enumerator->val.p_void = calloc(columns, sizeof(enumerator->val));
564: for (i = 0; i < columns; i++)
565: {
566: switch (va_arg(args, db_type_t))
567: {
568: case DB_INT:
569: {
570: enumerator->bind[i].buffer_type = MYSQL_TYPE_LONG;
571: enumerator->bind[i].buffer = (char*)&enumerator->val.p_int[i];
572: break;
573: }
574: case DB_UINT:
575: {
576: enumerator->bind[i].buffer_type = MYSQL_TYPE_LONG;
577: enumerator->bind[i].buffer = (char*)&enumerator->val.p_uint[i];
578: enumerator->bind[i].is_unsigned = TRUE;
579: break;
580: }
581: case DB_TEXT:
582: {
583: enumerator->bind[i].buffer_type = MYSQL_TYPE_STRING;
584: enumerator->bind[i].length = &enumerator->length[i];
585: break;
586: }
587: case DB_BLOB:
588: {
589: enumerator->bind[i].buffer_type = MYSQL_TYPE_BLOB;
590: enumerator->bind[i].length = &enumerator->length[i];
591: break;
592: }
593: case DB_DOUBLE:
594: {
595: enumerator->bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
596: enumerator->bind[i].buffer = (char*)&enumerator->val.p_double[i];
597: break;
598: }
599: default:
600: DBG1(DBG_LIB, "invalid result data type supplied");
601: mysql_enumerator_destroy(enumerator);
602: va_end(args);
603: return NULL;
604: }
605: }
606: if (mysql_stmt_bind_result(stmt, enumerator->bind))
607: {
608: DBG1(DBG_LIB, "binding MySQL result failed: %s",
609: mysql_stmt_error(stmt));
610: mysql_enumerator_destroy(enumerator);
611: enumerator = NULL;
612: }
613: }
614: else
615: {
616: conn_release(this, conn);
617: }
618: va_end(args);
619: return (enumerator_t*)enumerator;
620: }
621:
622: METHOD(database_t, execute, int,
623: private_mysql_database_t *this, int *rowid, char *sql, ...)
624: {
625: MYSQL_STMT *stmt;
626: va_list args;
627: conn_t *conn;
628: int affected = -1;
629:
630: conn = conn_get(this, NULL);
631: if (!conn)
632: {
633: return -1;
634: }
635: va_start(args, sql);
636: stmt = run(conn->mysql, sql, &args);
637: if (stmt)
638: {
639: if (rowid)
640: {
641: *rowid = mysql_stmt_insert_id(stmt);
642: }
643: affected = mysql_stmt_affected_rows(stmt);
644: mysql_stmt_close(stmt);
645: }
646: va_end(args);
647: conn_release(this, conn);
648: return affected;
649: }
650:
651: METHOD(database_t, transaction, bool,
652: private_mysql_database_t *this, bool serializable)
653: {
654: transaction_t *trans = NULL;
655: conn_t *conn;
656:
657: conn = conn_get(this, &trans);
658: if (!conn)
659: {
660: return FALSE;
661: }
662: else if (trans)
663: {
664: ref_get(&trans->refs);
665: return TRUE;
666: }
667: /* these statements are not supported in prepared statements that are used
668: * by the execute() method */
669: if (serializable)
670: {
671: if (mysql_query(conn->mysql,
672: "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE") != 0)
673: {
674: DBG1(DBG_LIB, "starting transaction failed: %s",
675: mysql_error(conn->mysql));
676: conn_release(this, conn);
677: return FALSE;
678: }
679: }
680: if (mysql_query(conn->mysql, "START TRANSACTION") != 0)
681: {
682: DBG1(DBG_LIB, "starting transaction failed: %s",
683: mysql_error(conn->mysql));
684: conn_release(this, conn);
685: return FALSE;
686: }
687: INIT(trans,
688: .conn = conn,
689: .refs = 1,
690: );
691: this->transaction->set(this->transaction, trans);
692: return TRUE;
693: }
694:
695: /**
696: * Finalize a transaction depending on the reference count and if it should be
697: * rolled back.
698: */
699: static bool finalize_transaction(private_mysql_database_t *this,
700: bool rollback)
701: {
702: transaction_t *trans;
703: char *command = "COMMIT";
704: bool success;
705:
706: trans = this->transaction->get(this->transaction);
707: if (!trans)
708: {
709: DBG1(DBG_LIB, "no database transaction found");
710: return FALSE;
711: }
712: /* set flag, can't be unset */
713: trans->rollback |= rollback;
714:
715: if (ref_put(&trans->refs))
716: {
717: if (trans->rollback)
718: {
719: command = "ROLLBACK";
720: }
721: success = mysql_query(trans->conn->mysql, command) == 0;
722:
723: this->transaction->set(this->transaction, NULL);
724: transaction_destroy(this, trans);
725: return success;
726: }
727: return TRUE;
728: }
729:
730: METHOD(database_t, commit_, bool,
731: private_mysql_database_t *this)
732: {
733: return finalize_transaction(this, FALSE);
734: }
735:
736: METHOD(database_t, rollback, bool,
737: private_mysql_database_t *this)
738: {
739: return finalize_transaction(this, TRUE);
740: }
741:
742: METHOD(database_t, get_driver,db_driver_t,
743: private_mysql_database_t *this)
744: {
745: return DB_MYSQL;
746: }
747:
748: METHOD(database_t, destroy, void,
749: private_mysql_database_t *this)
750: {
751: this->transaction->destroy(this->transaction);
752: this->pool->destroy_function(this->pool, (void*)conn_destroy);
753: this->mutex->destroy(this->mutex);
754: free(this->host);
755: free(this->username);
756: free(this->password);
757: free(this->database);
758: free(this);
759: }
760:
761: static bool parse_uri(private_mysql_database_t *this, char *uri)
762: {
763: char *username, *password, *host, *port = "0", *database, *pos;
764:
765: /**
766: * parse mysql://username:pass@host:port/database uri
767: */
768: username = strdup(uri + 8);
769: pos = strchr(username, ':');
770: if (pos)
771: {
772: *pos = '\0';
773: password = pos + 1;
774: pos = strrchr(password, '@');
775: if (pos)
776: {
777: *pos = '\0';
778: host = pos + 1;
779: pos = strrchr(host, ':');
780: if (pos)
781: {
782: *pos = '\0';
783: port = pos + 1;
784: pos = strchr(port, '/');
785: }
786: else
787: {
788: pos = strchr(host, '/');
789: }
790: if (pos)
791: {
792: *pos = '\0';
793: database = pos + 1;
794:
795: this->host = strdup(host);
796: this->username = strdup(username);
797: this->password = strdup(password);
798: this->database = strdup(database);
799: this->port = atoi(port);
800: free(username);
801: return TRUE;
802: }
803: }
804: }
805: free(username);
806: DBG1(DBG_LIB, "parsing MySQL database uri '%s' failed", uri);
807: return FALSE;
808: }
809:
810:
811: /*
812: * see header file
813: */
814: mysql_database_t *mysql_database_create(char *uri)
815: {
816: conn_t *conn;
817: private_mysql_database_t *this;
818:
819: if (!strpfx(uri, "mysql://"))
820: {
821: return NULL;
822: }
823:
824: INIT(this,
825: .public = {
826: .db = {
827: .query = _query,
828: .execute = _execute,
829: .transaction = _transaction,
830: .commit = _commit_,
831: .rollback = _rollback,
832: .get_driver = _get_driver,
833: .destroy = _destroy,
834: },
835: },
836: );
837:
838: if (!parse_uri(this, uri))
839: {
840: free(this);
841: return NULL;
842: }
843: this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
844: this->pool = linked_list_create();
845: this->transaction = thread_value_create(NULL);
846:
847: /* check connectivity */
848: conn = conn_get(this, NULL);
849: if (!conn)
850: {
851: destroy(this);
852: return NULL;
853: }
854: conn_release(this, conn);
855: return &this->public;
856: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>