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

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
1.1.1.2 ! misho     130:        log_error_write(srv, __FILE__, __LINE__, "ss",
1.1       misho     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;
1.1.1.2 ! misho     176:        buffer *sel;
1.1       misho     177: 
                    178:        config_values_t cv[] = {
1.1.1.2 ! misho     179:                { "mysql-vhost.db",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           180:                { "mysql-vhost.user",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           181:                { "mysql-vhost.pass",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           182:                { "mysql-vhost.sock",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           183:                { "mysql-vhost.sql",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           184:                { "mysql-vhost.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER },
        !           185:                { "mysql-vhost.port",     NULL, T_CONFIG_SHORT,  T_CONFIG_SCOPE_SERVER },
        !           186:                { NULL,                   NULL, T_CONFIG_UNSET,  T_CONFIG_SCOPE_UNSET }
        !           187:        };
1.1       misho     188: 
1.1.1.2 ! misho     189:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
        !           190:        sel = buffer_init();
1.1       misho     191: 
                    192:        for (i = 0; i < srv->config_context->used; i++) {
                    193:                plugin_config *s;
                    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();
1.1.1.2 ! misho     201:                s->port = 0;               /* default port for mysql */
1.1       misho     202:                s->mysql = NULL;
                    203: 
                    204:                s->mysql_pre = buffer_init();
                    205:                s->mysql_post = buffer_init();
                    206: 
                    207:                cv[0].destination = s->mydb;
                    208:                cv[1].destination = s->myuser;
                    209:                cv[2].destination = s->mypass;
                    210:                cv[3].destination = s->mysock;
1.1.1.2 ! misho     211:                buffer_reset(sel);
1.1       misho     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: 
1.1.1.2 ! misho     218:                if (config_insert_values_global(srv,
1.1       misho     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: 
1.1.1.2 ! misho     252:                                buffer_free(sel);
1.1       misho     253:                                return HANDLER_ERROR;
                    254:                        }
                    255: 
                    256: #if MYSQL_VERSION_ID >= 50013
                    257:                        /* in mysql versions above 5.0.3 the reconnect flag is off by default */
                    258:                        mysql_options(s->mysql, MYSQL_OPT_RECONNECT, &reconnect);
                    259: #endif
                    260: 
                    261: #define FOO(x) (s->x->used ? s->x->ptr : NULL)
                    262: 
                    263: #if MYSQL_VERSION_ID >= 40100
1.1.1.2 ! misho     264:                        /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */ 
1.1       misho     265:                        if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass),
                    266:                                                FOO(mydb), s->port, FOO(mysock), CLIENT_MULTI_STATEMENTS)) {
                    267: #else
                    268:                        if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass),
                    269:                                                FOO(mydb), s->port, FOO(mysock), 0)) {
                    270: #endif
                    271:                                log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(s->mysql));
                    272: 
1.1.1.2 ! misho     273:                                buffer_free(sel);
1.1       misho     274:                                return HANDLER_ERROR;
                    275:                        }
                    276: #undef FOO
                    277: 
1.1.1.2 ! misho     278:                        fd_close_on_exec(s->mysql->net.fd);
1.1       misho     279:                }
                    280:        }
                    281: 
1.1.1.2 ! misho     282:        buffer_free(sel);
1.1       misho     283:        return HANDLER_GO_ON;
                    284: }
                    285: 
                    286: #define PATCH(x) \
                    287:        p->conf.x = s->x;
                    288: static int mod_mysql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) {
                    289:        size_t i, j;
                    290:        plugin_config *s = p->config_storage[0];
                    291: 
                    292:        PATCH(mysql_pre);
                    293:        PATCH(mysql_post);
                    294: #ifdef HAVE_MYSQL
                    295:        PATCH(mysql);
                    296: #endif
                    297: 
                    298:        /* skip the first, the global context */
                    299:        for (i = 1; i < srv->config_context->used; i++) {
                    300:                data_config *dc = (data_config *)srv->config_context->data[i];
                    301:                s = p->config_storage[i];
                    302: 
                    303:                /* condition didn't match */
                    304:                if (!config_check_cond(srv, con, dc)) continue;
                    305: 
                    306:                /* merge config */
                    307:                for (j = 0; j < dc->value->used; j++) {
                    308:                        data_unset *du = dc->value->data[j];
                    309: 
                    310:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("mysql-vhost.sql"))) {
                    311:                                PATCH(mysql_pre);
                    312:                                PATCH(mysql_post);
                    313:                        }
                    314:                }
                    315: 
                    316:                if (s->mysql) {
                    317:                        PATCH(mysql);
                    318:                }
                    319:        }
                    320: 
                    321:        return 0;
                    322: }
                    323: #undef PATCH
                    324: 
                    325: 
                    326: /* handle document root request */
                    327: CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) {
                    328:        plugin_data *p = p_d;
                    329:        plugin_connection_data *c;
                    330:        stat_cache_entry *sce;
                    331: 
                    332:        unsigned  cols;
                    333:        MYSQL_ROW row;
                    334:        MYSQL_RES *result = NULL;
                    335: 
                    336:        /* no host specified? */
                    337:        if (!con->uri.authority->used) return HANDLER_GO_ON;
                    338: 
                    339:        mod_mysql_vhost_patch_connection(srv, con, p);
                    340: 
                    341:        if (!p->conf.mysql) return HANDLER_GO_ON;
1.1.1.2 ! misho     342:        if (0 == p->conf.mysql_pre->used) return HANDLER_GO_ON;
1.1       misho     343: 
                    344:        /* sets up connection data if not done yet */
                    345:        c = mod_mysql_vhost_connection_data(srv, con, p_d);
                    346: 
                    347:        /* check if cached this connection */
                    348:        if (c->server_name->used && /* con->uri.authority->used && */
1.1.1.2 ! misho     349:            buffer_is_equal(c->server_name, con->uri.authority)) goto GO_ON;
1.1       misho     350: 
                    351:        /* build and run SQL query */
                    352:        buffer_copy_string_buffer(p->tmp_buf, p->conf.mysql_pre);
                    353:        if (p->conf.mysql_post->used) {
1.1.1.2 ! misho     354:                /* escape the uri.authority */
        !           355:                unsigned long to_len;
        !           356: 
        !           357:                /* 'to' has to be 'from_len * 2 + 1' */
        !           358:                buffer_prepare_append(p->tmp_buf, (con->uri.authority->used - 1) * 2 + 1);
        !           359: 
        !           360:                to_len = mysql_real_escape_string(p->conf.mysql,
        !           361:                                p->tmp_buf->ptr + p->tmp_buf->used - 1,
        !           362:                                con->uri.authority->ptr, con->uri.authority->used - 1);
        !           363:                p->tmp_buf->used += to_len;
        !           364: 
1.1       misho     365:                buffer_append_string_buffer(p->tmp_buf, p->conf.mysql_post);
                    366:        }
1.1.1.2 ! misho     367:        if (mysql_real_query(p->conf.mysql, p->tmp_buf->ptr, p->tmp_buf->used - 1)) {
1.1       misho     368:                log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(p->conf.mysql));
                    369:                goto ERR500;
                    370:        }
                    371:        result = mysql_store_result(p->conf.mysql);
                    372:        cols = mysql_num_fields(result);
                    373:        row = mysql_fetch_row(result);
                    374:        if (!row || cols < 1) {
                    375:                /* no such virtual host */
                    376:                mysql_free_result(result);
                    377: #if MYSQL_VERSION_ID >= 40100
                    378:                while (mysql_next_result(p->conf.mysql) == 0);
                    379: #endif
                    380:                return HANDLER_GO_ON;
                    381:        }
                    382: 
                    383:        /* sanity check that really is a directory */
                    384:        buffer_copy_string(p->tmp_buf, row[0]);
                    385:        BUFFER_APPEND_SLASH(p->tmp_buf);
                    386: 
                    387:        if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) {
                    388:                log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf);
                    389:                goto ERR500;
                    390:        }
1.1.1.2 ! misho     391:        if (!S_ISDIR(sce->st.st_mode)) {
1.1       misho     392:                log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", p->tmp_buf);
                    393:                goto ERR500;
                    394:        }
                    395: 
                    396:        /* cache the data */
                    397:        buffer_copy_string_buffer(c->server_name, con->uri.authority);
                    398:        buffer_copy_string_buffer(c->document_root, p->tmp_buf);
                    399: 
                    400:        /* fcgi_offset and fcgi_arg are optional */
                    401:        if (cols > 1 && row[1]) {
                    402:                c->fcgi_offset = atoi(row[1]);
                    403: 
                    404:                if (cols > 2 && row[2]) {
                    405:                        buffer_copy_string(c->fcgi_arg, row[2]);
                    406:                } else {
                    407:                        c->fcgi_arg->used = 0;
                    408:                }
                    409:        } else {
                    410:                c->fcgi_offset = c->fcgi_arg->used = 0;
                    411:        }
                    412:        mysql_free_result(result);
                    413: #if MYSQL_VERSION_ID >= 40100
                    414:        while (mysql_next_result(p->conf.mysql) == 0);
                    415: #endif
                    416: 
                    417:        /* fix virtual server and docroot */
1.1.1.2 ! misho     418: GO_ON:
        !           419:        buffer_copy_string_buffer(con->server_name, c->server_name);
1.1       misho     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: 
1.1.1.2 ! misho     430: ERR500:
        !           431:        if (result) mysql_free_result(result);
1.1       misho     432: #if MYSQL_VERSION_ID >= 40100
                    433:        while (mysql_next_result(p->conf.mysql) == 0);
                    434: #endif
                    435:        con->http_status = 500; /* Internal Error */
                    436:        con->mode = DIRECT;
                    437:        return HANDLER_FINISHED;
                    438: }
                    439: 
                    440: /* this function is called at dlopen() time and inits the callbacks */
                    441: int mod_mysql_vhost_plugin_init(plugin *p);
                    442: int mod_mysql_vhost_plugin_init(plugin *p) {
                    443:        p->version        = LIGHTTPD_VERSION_ID;
                    444:        p->name           = buffer_init_string("mysql_vhost");
                    445: 
                    446:        p->init           = mod_mysql_vhost_init;
                    447:        p->cleanup        = mod_mysql_vhost_cleanup;
                    448:        p->connection_reset = mod_mysql_vhost_handle_connection_close;
                    449: 
                    450:        p->set_defaults   = mod_mysql_vhost_set_defaults;
                    451:        p->handle_docroot = mod_mysql_vhost_handle_docroot;
                    452: 
                    453:        return 0;
                    454: }
                    455: #else
                    456: /* we don't have mysql support, this plugin does nothing */
                    457: int mod_mysql_vhost_plugin_init(plugin *p);
                    458: int mod_mysql_vhost_plugin_init(plugin *p) {
                    459:        p->version     = LIGHTTPD_VERSION_ID;
                    460:        p->name        = buffer_init_string("mysql_vhost");
                    461: 
                    462:        return 0;
                    463: }
                    464: #endif

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