Annotation of embedaddon/lighttpd/src/mod_mysql_vhost.c, revision 1.1
1.1 ! misho 1: #include <unistd.h>
! 2: #include <stdio.h>
! 3: #include <errno.h>
! 4: #include <fcntl.h>
! 5: #include <strings.h>
! 6:
! 7: #ifdef HAVE_CONFIG_H
! 8: #include "config.h"
! 9: #endif
! 10:
! 11: #ifdef HAVE_MYSQL
! 12: #include <mysql.h>
! 13: #endif
! 14:
! 15: #include "plugin.h"
! 16: #include "log.h"
! 17:
! 18: #include "stat_cache.h"
! 19: #ifdef DEBUG_MOD_MYSQL_VHOST
! 20: #define DEBUG
! 21: #endif
! 22:
! 23: /*
! 24: * Plugin for lighttpd to use MySQL
! 25: * for domain to directory lookups,
! 26: * i.e virtual hosts (vhosts).
! 27: *
! 28: * Optionally sets fcgi_offset and fcgi_arg
! 29: * in preparation for fcgi.c to handle
! 30: * per-user fcgi chroot jails.
! 31: *
! 32: * /ada@riksnet.se 2004-12-06
! 33: */
! 34:
! 35: #ifdef HAVE_MYSQL
! 36: typedef struct {
! 37: MYSQL *mysql;
! 38:
! 39: buffer *mydb;
! 40: buffer *myuser;
! 41: buffer *mypass;
! 42: buffer *mysock;
! 43:
! 44: buffer *hostname;
! 45: unsigned short port;
! 46:
! 47: buffer *mysql_pre;
! 48: buffer *mysql_post;
! 49: } plugin_config;
! 50:
! 51: /* global plugin data */
! 52: typedef struct {
! 53: PLUGIN_DATA;
! 54:
! 55: buffer *tmp_buf;
! 56:
! 57: plugin_config **config_storage;
! 58:
! 59: plugin_config conf;
! 60: } plugin_data;
! 61:
! 62: /* per connection plugin data */
! 63: typedef struct {
! 64: buffer *server_name;
! 65: buffer *document_root;
! 66: buffer *fcgi_arg;
! 67: unsigned fcgi_offset;
! 68: } plugin_connection_data;
! 69:
! 70: /* init the plugin data */
! 71: INIT_FUNC(mod_mysql_vhost_init) {
! 72: plugin_data *p;
! 73:
! 74: p = calloc(1, sizeof(*p));
! 75:
! 76: p->tmp_buf = buffer_init();
! 77:
! 78: return p;
! 79: }
! 80:
! 81: /* cleanup the plugin data */
! 82: SERVER_FUNC(mod_mysql_vhost_cleanup) {
! 83: plugin_data *p = p_d;
! 84:
! 85: UNUSED(srv);
! 86:
! 87: #ifdef DEBUG
! 88: log_error_write(srv, __FILE__, __LINE__, "ss",
! 89: "mod_mysql_vhost_cleanup", p ? "yes" : "NO");
! 90: #endif
! 91: if (!p) return HANDLER_GO_ON;
! 92:
! 93: if (p->config_storage) {
! 94: size_t i;
! 95: for (i = 0; i < srv->config_context->used; i++) {
! 96: plugin_config *s = p->config_storage[i];
! 97:
! 98: if (!s) continue;
! 99:
! 100: mysql_close(s->mysql);
! 101:
! 102: buffer_free(s->mydb);
! 103: buffer_free(s->myuser);
! 104: buffer_free(s->mypass);
! 105: buffer_free(s->mysock);
! 106: buffer_free(s->mysql_pre);
! 107: buffer_free(s->mysql_post);
! 108: buffer_free(s->hostname);
! 109:
! 110: free(s);
! 111: }
! 112: free(p->config_storage);
! 113: }
! 114: buffer_free(p->tmp_buf);
! 115:
! 116: free(p);
! 117:
! 118: return HANDLER_GO_ON;
! 119: }
! 120:
! 121: /* handle the plugin per connection data */
! 122: static void* mod_mysql_vhost_connection_data(server *srv, connection *con, void *p_d)
! 123: {
! 124: plugin_data *p = p_d;
! 125: plugin_connection_data *c = con->plugin_ctx[p->id];
! 126:
! 127: UNUSED(srv);
! 128:
! 129: #ifdef DEBUG
! 130: log_error_write(srv, __FILE__, __LINE__, "ss",
! 131: "mod_mysql_connection_data", c ? "old" : "NEW");
! 132: #endif
! 133:
! 134: if (c) return c;
! 135: c = calloc(1, sizeof(*c));
! 136:
! 137: c->server_name = buffer_init();
! 138: c->document_root = buffer_init();
! 139: c->fcgi_arg = buffer_init();
! 140: c->fcgi_offset = 0;
! 141:
! 142: return con->plugin_ctx[p->id] = c;
! 143: }
! 144:
! 145: /* destroy the plugin per connection data */
! 146: CONNECTION_FUNC(mod_mysql_vhost_handle_connection_close) {
! 147: plugin_data *p = p_d;
! 148: plugin_connection_data *c = con->plugin_ctx[p->id];
! 149:
! 150: UNUSED(srv);
! 151:
! 152: #ifdef DEBUG
! 153: log_error_write(srv, __FILE__, __LINE__, "ss",
! 154: "mod_mysql_vhost_handle_connection_close", c ? "yes" : "NO");
! 155: #endif
! 156:
! 157: if (!c) return HANDLER_GO_ON;
! 158:
! 159: buffer_free(c->server_name);
! 160: buffer_free(c->document_root);
! 161: buffer_free(c->fcgi_arg);
! 162: c->fcgi_offset = 0;
! 163:
! 164: free(c);
! 165:
! 166: con->plugin_ctx[p->id] = NULL;
! 167: return HANDLER_GO_ON;
! 168: }
! 169:
! 170: /* set configuration values */
! 171: SERVER_FUNC(mod_mysql_vhost_set_defaults) {
! 172: plugin_data *p = p_d;
! 173:
! 174: char *qmark;
! 175: size_t i = 0;
! 176:
! 177: config_values_t cv[] = {
! 178: { "mysql-vhost.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
! 179: { "mysql-vhost.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
! 180: { "mysql-vhost.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
! 181: { "mysql-vhost.sock", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
! 182: { "mysql-vhost.sql", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
! 183: { "mysql-vhost.hostname", NULL, T_CONFIG_STRING,T_CONFIG_SCOPE_SERVER },
! 184: { "mysql-vhost.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER },
! 185: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
! 186: };
! 187:
! 188: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
! 189:
! 190: for (i = 0; i < srv->config_context->used; i++) {
! 191: plugin_config *s;
! 192: buffer *sel;
! 193:
! 194:
! 195: s = calloc(1, sizeof(plugin_config));
! 196: s->mydb = buffer_init();
! 197: s->myuser = buffer_init();
! 198: s->mypass = buffer_init();
! 199: s->mysock = buffer_init();
! 200: s->hostname = buffer_init();
! 201: s->port = 0; /* default port for mysql */
! 202: sel = buffer_init();
! 203: s->mysql = NULL;
! 204:
! 205: s->mysql_pre = buffer_init();
! 206: s->mysql_post = buffer_init();
! 207:
! 208: cv[0].destination = s->mydb;
! 209: cv[1].destination = s->myuser;
! 210: cv[2].destination = s->mypass;
! 211: cv[3].destination = s->mysock;
! 212: cv[4].destination = sel;
! 213: cv[5].destination = s->hostname;
! 214: cv[6].destination = &(s->port);
! 215:
! 216: p->config_storage[i] = s;
! 217:
! 218: if (config_insert_values_global(srv,
! 219: ((data_config *)srv->config_context->data[i])->value,
! 220: cv)) return HANDLER_ERROR;
! 221:
! 222: s->mysql_pre = buffer_init();
! 223: s->mysql_post = buffer_init();
! 224:
! 225: if (sel->used && (qmark = strchr(sel->ptr, '?'))) {
! 226: *qmark = '\0';
! 227: buffer_copy_string(s->mysql_pre, sel->ptr);
! 228: buffer_copy_string(s->mysql_post, qmark+1);
! 229: } else {
! 230: buffer_copy_string_buffer(s->mysql_pre, sel);
! 231: }
! 232:
! 233: /* required:
! 234: * - username
! 235: * - database
! 236: *
! 237: * optional:
! 238: * - password, default: empty
! 239: * - socket, default: mysql default
! 240: * - hostname, if set overrides socket
! 241: * - port, default: 3306
! 242: */
! 243:
! 244: /* all have to be set */
! 245: if (!(buffer_is_empty(s->myuser) ||
! 246: buffer_is_empty(s->mydb))) {
! 247: my_bool reconnect = 1;
! 248:
! 249: if (NULL == (s->mysql = mysql_init(NULL))) {
! 250: log_error_write(srv, __FILE__, __LINE__, "s", "mysql_init() failed, exiting...");
! 251:
! 252: return HANDLER_ERROR;
! 253: }
! 254:
! 255: #if MYSQL_VERSION_ID >= 50013
! 256: /* in mysql versions above 5.0.3 the reconnect flag is off by default */
! 257: mysql_options(s->mysql, MYSQL_OPT_RECONNECT, &reconnect);
! 258: #endif
! 259:
! 260: #define FOO(x) (s->x->used ? s->x->ptr : NULL)
! 261:
! 262: #if MYSQL_VERSION_ID >= 40100
! 263: /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */
! 264: if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass),
! 265: FOO(mydb), s->port, FOO(mysock), CLIENT_MULTI_STATEMENTS)) {
! 266: #else
! 267: if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass),
! 268: FOO(mydb), s->port, FOO(mysock), 0)) {
! 269: #endif
! 270: log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(s->mysql));
! 271:
! 272: return HANDLER_ERROR;
! 273: }
! 274: #undef FOO
! 275:
! 276: #if 0
! 277: /* set close_on_exec for mysql the hard way */
! 278: /* Note: this only works as it is done during startup, */
! 279: /* otherwise we cannot be sure that mysql is fd i-1 */
! 280: { int fd;
! 281: if (-1 != (fd = open("/dev/null", 0))) {
! 282: close(fd);
! 283: #ifdef FD_CLOEXEC
! 284: fcntl(fd-1, F_SETFD, FD_CLOEXEC);
! 285: #endif
! 286: } }
! 287: #else
! 288: #ifdef FD_CLOEXEC
! 289: fcntl(s->mysql->net.fd, F_SETFD, FD_CLOEXEC);
! 290: #endif
! 291: #endif
! 292: }
! 293: }
! 294:
! 295: return HANDLER_GO_ON;
! 296: }
! 297:
! 298: #define PATCH(x) \
! 299: p->conf.x = s->x;
! 300: static int mod_mysql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) {
! 301: size_t i, j;
! 302: plugin_config *s = p->config_storage[0];
! 303:
! 304: PATCH(mysql_pre);
! 305: PATCH(mysql_post);
! 306: #ifdef HAVE_MYSQL
! 307: PATCH(mysql);
! 308: #endif
! 309:
! 310: /* skip the first, the global context */
! 311: for (i = 1; i < srv->config_context->used; i++) {
! 312: data_config *dc = (data_config *)srv->config_context->data[i];
! 313: s = p->config_storage[i];
! 314:
! 315: /* condition didn't match */
! 316: if (!config_check_cond(srv, con, dc)) continue;
! 317:
! 318: /* merge config */
! 319: for (j = 0; j < dc->value->used; j++) {
! 320: data_unset *du = dc->value->data[j];
! 321:
! 322: if (buffer_is_equal_string(du->key, CONST_STR_LEN("mysql-vhost.sql"))) {
! 323: PATCH(mysql_pre);
! 324: PATCH(mysql_post);
! 325: }
! 326: }
! 327:
! 328: if (s->mysql) {
! 329: PATCH(mysql);
! 330: }
! 331: }
! 332:
! 333: return 0;
! 334: }
! 335: #undef PATCH
! 336:
! 337:
! 338: /* handle document root request */
! 339: CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) {
! 340: plugin_data *p = p_d;
! 341: plugin_connection_data *c;
! 342: stat_cache_entry *sce;
! 343:
! 344: unsigned cols;
! 345: MYSQL_ROW row;
! 346: MYSQL_RES *result = NULL;
! 347:
! 348: /* no host specified? */
! 349: if (!con->uri.authority->used) return HANDLER_GO_ON;
! 350:
! 351: mod_mysql_vhost_patch_connection(srv, con, p);
! 352:
! 353: if (!p->conf.mysql) return HANDLER_GO_ON;
! 354:
! 355: /* sets up connection data if not done yet */
! 356: c = mod_mysql_vhost_connection_data(srv, con, p_d);
! 357:
! 358: /* check if cached this connection */
! 359: if (c->server_name->used && /* con->uri.authority->used && */
! 360: buffer_is_equal(c->server_name, con->uri.authority)) goto GO_ON;
! 361:
! 362: /* build and run SQL query */
! 363: buffer_copy_string_buffer(p->tmp_buf, p->conf.mysql_pre);
! 364: if (p->conf.mysql_post->used) {
! 365: buffer_append_string_buffer(p->tmp_buf, con->uri.authority);
! 366: buffer_append_string_buffer(p->tmp_buf, p->conf.mysql_post);
! 367: }
! 368: if (mysql_query(p->conf.mysql, p->tmp_buf->ptr)) {
! 369: log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(p->conf.mysql));
! 370: goto ERR500;
! 371: }
! 372: result = mysql_store_result(p->conf.mysql);
! 373: cols = mysql_num_fields(result);
! 374: row = mysql_fetch_row(result);
! 375: if (!row || cols < 1) {
! 376: /* no such virtual host */
! 377: mysql_free_result(result);
! 378: #if MYSQL_VERSION_ID >= 40100
! 379: while (mysql_next_result(p->conf.mysql) == 0);
! 380: #endif
! 381: return HANDLER_GO_ON;
! 382: }
! 383:
! 384: /* sanity check that really is a directory */
! 385: buffer_copy_string(p->tmp_buf, row[0]);
! 386: BUFFER_APPEND_SLASH(p->tmp_buf);
! 387:
! 388: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) {
! 389: log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf);
! 390: goto ERR500;
! 391: }
! 392: if (!S_ISDIR(sce->st.st_mode)) {
! 393: log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", p->tmp_buf);
! 394: goto ERR500;
! 395: }
! 396:
! 397: /* cache the data */
! 398: buffer_copy_string_buffer(c->server_name, con->uri.authority);
! 399: buffer_copy_string_buffer(c->document_root, p->tmp_buf);
! 400:
! 401: /* fcgi_offset and fcgi_arg are optional */
! 402: if (cols > 1 && row[1]) {
! 403: c->fcgi_offset = atoi(row[1]);
! 404:
! 405: if (cols > 2 && row[2]) {
! 406: buffer_copy_string(c->fcgi_arg, row[2]);
! 407: } else {
! 408: c->fcgi_arg->used = 0;
! 409: }
! 410: } else {
! 411: c->fcgi_offset = c->fcgi_arg->used = 0;
! 412: }
! 413: mysql_free_result(result);
! 414: #if MYSQL_VERSION_ID >= 40100
! 415: while (mysql_next_result(p->conf.mysql) == 0);
! 416: #endif
! 417:
! 418: /* fix virtual server and docroot */
! 419: GO_ON: buffer_copy_string_buffer(con->server_name, c->server_name);
! 420: buffer_copy_string_buffer(con->physical.doc_root, c->document_root);
! 421:
! 422: #ifdef DEBUG
! 423: log_error_write(srv, __FILE__, __LINE__, "sbbdb",
! 424: result ? "NOT CACHED" : "cached",
! 425: con->server_name, con->physical.doc_root,
! 426: c->fcgi_offset, c->fcgi_arg);
! 427: #endif
! 428: return HANDLER_GO_ON;
! 429:
! 430: ERR500: if (result) mysql_free_result(result);
! 431: #if MYSQL_VERSION_ID >= 40100
! 432: while (mysql_next_result(p->conf.mysql) == 0);
! 433: #endif
! 434: con->http_status = 500; /* Internal Error */
! 435: con->mode = DIRECT;
! 436: return HANDLER_FINISHED;
! 437: }
! 438:
! 439: /* this function is called at dlopen() time and inits the callbacks */
! 440: int mod_mysql_vhost_plugin_init(plugin *p);
! 441: int mod_mysql_vhost_plugin_init(plugin *p) {
! 442: p->version = LIGHTTPD_VERSION_ID;
! 443: p->name = buffer_init_string("mysql_vhost");
! 444:
! 445: p->init = mod_mysql_vhost_init;
! 446: p->cleanup = mod_mysql_vhost_cleanup;
! 447: p->connection_reset = mod_mysql_vhost_handle_connection_close;
! 448:
! 449: p->set_defaults = mod_mysql_vhost_set_defaults;
! 450: p->handle_docroot = mod_mysql_vhost_handle_docroot;
! 451:
! 452: return 0;
! 453: }
! 454: #else
! 455: /* we don't have mysql support, this plugin does nothing */
! 456: int mod_mysql_vhost_plugin_init(plugin *p);
! 457: int mod_mysql_vhost_plugin_init(plugin *p) {
! 458: p->version = LIGHTTPD_VERSION_ID;
! 459: p->name = buffer_init_string("mysql_vhost");
! 460:
! 461: return 0;
! 462: }
! 463: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>