Return to mysql_database.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libstrongswan / plugins / mysql |
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: }