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>