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>