/* Copyright (c) 2003, WebThing Ltd Author: Nick Kew This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* mod_mysql_pool: manage a pool of MySQL connections. EXPORTS: MYSQL* mysqlpool_open(server_rec*) - retrieve a connection from the pool and check it's valid - May return null and log a message on error. void mysqlpool_close(server_rec*, MYSQL*) - return a connection to the pool after use CONFIG: See the code round about line 106 CAVEAT: this is a quick-hack drop-in conversion from the near-identical mod_pg_pool WARNING: this module is a hack that I spent less than an hour on, and is untested. YMMV. Actually that's only half-true. Yes it's a hack, but it's distilled from stable code running at Valet, so most of it is at least somewhat tested. Just not in this form. I'm releasing it as a demo-of-concept for modules exporting a generic reusable shared resource for other modules, where (as in my case with Valet) more than one module wants to share a connection pool. */ #include #include #include #include #include #include #include #include #include extern module AP_MODULE_DECLARE_DATA mysql_pool_module ; /************ svr cfg: manage db connection pool ****************/ typedef struct svr_cfg { apr_reslist_t* dbpool ; int nmin ; int nkeep ; int nmax ; int exptime ; const char* host ; int port ; const char* db ; const char* user ; const char* pass ; const char* sock ; } svr_cfg ; typedef enum { cmd_host , cmd_port , cmd_db, cmd_user, cmd_pass, cmd_min, cmd_keep, cmd_max, cmd_exp, cmd_sock } cmd_parts ; #define ISINT(val) \ for ( p = val; *p; ++p) \ if ( ! isdigit(*p) ) \ return "Argument must be numeric!" static const char* set_param(cmd_parms* cmd, void* cfg, const char* val) { const char* p ; svr_cfg* svr = (svr_cfg*) ap_get_module_config (cmd->server->module_config, &mysql_pool_module ) ; switch ( (int) cmd->info ) { case cmd_host: svr->host = val ; break ; case cmd_port: ISINT(val) ; svr->port = atoi(val) ; break ; case cmd_db: svr->db = val ; break ; case cmd_user: svr->user = val ; break ; case cmd_pass: svr->pass = val ; break ; case cmd_sock: svr->sock = val ; break ; case cmd_min: ISINT(val) ; svr->nmin = atoi(val) ; break ; case cmd_keep: ISINT(val) ; svr->nkeep = atoi(val) ; break ; case cmd_max: ISINT(val) ; svr->nmax = atoi(val) ; break ; case cmd_exp: ISINT(val) ; svr->exptime = atoi(val) ; break ; } return NULL ; } static const command_rec mysql_pool_cmds[] = { AP_INIT_TAKE1("MySQLPoolHost", set_param, (void*)cmd_host, RSRC_CONF, "MySQL Host") , AP_INIT_TAKE1("MySQLPoolPort", set_param, (void*)cmd_port, RSRC_CONF, "MySQL Port") , AP_INIT_TAKE1("MySQLPoolDB", set_param, (void*)cmd_db, RSRC_CONF, "MySQL Database") , AP_INIT_TAKE1("MySQLPoolUser", set_param, (void*)cmd_user, RSRC_CONF, "MySQL Username") , AP_INIT_TAKE1("MySQLPoolPass", set_param, (void*)cmd_pass, RSRC_CONF, "MySQL Password") , AP_INIT_TAKE1("MySQLPoolSock", set_param, (void*)cmd_sock, RSRC_CONF, "MySQL Unix Socket") , AP_INIT_TAKE1("MySQLPoolMin", set_param, (void*)cmd_min, RSRC_CONF, "Minimum number of connections") , AP_INIT_TAKE1("MySQLPoolKeep", set_param, (void*)cmd_keep, RSRC_CONF, "Maximum number of sustained connections") , AP_INIT_TAKE1("MySQLPoolMax", set_param, (void*)cmd_max, RSRC_CONF, "Maximum number of connections") , AP_INIT_TAKE1("MySQLPoolExptime", set_param, (void*)cmd_exp, RSRC_CONF, "Keepalive time for idle connections") , {NULL} } ; static void* mysql_pool_cfg(apr_pool_t* p, server_rec* x) { svr_cfg* svr = (svr_cfg*) apr_pcalloc(p, sizeof(svr_cfg)) ; return svr ; } /************ svr cfg: manage db connection pool ****************/ /* an apr_reslist_constructor for MySQL connections */ static apr_status_t mysqlpool_construct(void** db, void* params, apr_pool_t* pool) { svr_cfg* svr = (svr_cfg*) params ; MYSQL* sql = NULL ; sql = mysql_init(sql) ; if ( sql == NULL ) { ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, pool, "mysql_init failed") ; return APR_EGENERAL ; } *db = mysql_real_connect(sql, svr->host, svr->user, svr->pass, svr->db, svr->port, svr->sock, 0) ; if ( ! *db ) { ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, pool, "MySQL Error: %s", mysql_error(sql) ) ; return APR_EGENERAL ; } return APR_SUCCESS ; } static apr_status_t mysqlpool_destruct(void* sql, void* params, apr_pool_t* pool) { mysql_close((MYSQL*)sql) ; return APR_SUCCESS ; } static int setup_db_pool(apr_pool_t* p, apr_pool_t* plog, apr_pool_t* ptemp, server_rec* s) { svr_cfg* svr = (svr_cfg*) ap_get_module_config(s->module_config, &mysql_pool_module) ; if ( apr_reslist_create(&svr->dbpool, svr->nmin, svr->nkeep, svr->nmax, svr->exptime, mysqlpool_construct, mysqlpool_destruct, svr, p) != APR_SUCCESS ) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "MySQLPool: failed to initialise") ; return 500 ; } apr_pool_cleanup_register(p, svr->dbpool, (void*)apr_reslist_destroy, apr_pool_cleanup_null) ; return OK ; } static void mysql_pool_hooks(apr_pool_t* p) { ap_hook_post_config(setup_db_pool, NULL, NULL, APR_HOOK_MIDDLE) ; } module AP_MODULE_DECLARE_DATA mysql_pool_module = { STANDARD20_MODULE_STUFF, NULL , NULL , mysql_pool_cfg , NULL , mysql_pool_cmds , mysql_pool_hooks } ; /* Functions we export for modules to use: - open acquires a connection from the pool (opens one if necessary) - close releases it back in to the pool */ MYSQL* mysqlpool_open(server_rec* s) { MYSQL* ret = NULL ; svr_cfg* svr = (svr_cfg*) ap_get_module_config(s->module_config, &mysql_pool_module) ; if ( apr_reslist_acquire(svr->dbpool, (void**)&ret) != APR_SUCCESS ) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Failed to acquire MySQL connection from pool!") ; return NULL ; } if (mysql_ping(ret) != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "MySQL Error: %s", mysql_error(ret) ) ; apr_reslist_release(svr->dbpool, ret) ; return NULL ; } return ret ; } void mysqlpool_close(server_rec* s, MYSQL* sql) { svr_cfg* svr = (svr_cfg*) ap_get_module_config(s->module_config, &mysql_pool_module) ; apr_reslist_release(svr->dbpool, sql) ; }