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>