Annotation of embedaddon/lighttpd/src/mod_mysql_vhost.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>