Annotation of embedaddon/lighttpd/src/mod_mysql_vhost.c, revision 1.1.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>