[mod_openssl] remove erroneous SSL_set_shutdown()
[lighttpd.git] / src / mod_vhostdb_dbi.c
blobbf58ab6932982d542b5e780e081170ffacf6eca3
1 #include "first.h"
3 #include <dbi/dbi.h>
5 #include <string.h>
6 #include <stdlib.h>
8 #include "base.h"
9 #include "http_vhostdb.h"
10 #include "fdevent.h"
11 #include "log.h"
12 #include "plugin.h"
15 * virtual host plugin using DBI for domain to directory lookups
17 * e.g.
18 * vhostdb.dbi = ( "sql" => "SELECT docroot FROM vhosts WHERE host='?'"
19 * "dbtype" => "sqlite3",
20 * "dbname" => "mydb.sqlite",
21 * "sqlite_dbdir" => "/path/to/sqlite/dbs/" )
24 typedef struct {
25 dbi_conn dbconn;
26 dbi_inst dbinst;
27 buffer *sqlquery;
28 server *srv;
29 short reconnect_count;
30 } vhostdb_config;
32 typedef struct {
33 void *vdata;
34 array *options;
35 } plugin_config;
37 typedef struct {
38 PLUGIN_DATA;
39 plugin_config **config_storage;
40 plugin_config conf;
41 } plugin_data;
43 /* used to reconnect to the database when we get disconnected */
44 static void mod_vhostdb_dbi_error_callback (dbi_conn dbconn, void *vdata)
46 vhostdb_config *dbconf = (vhostdb_config *)vdata;
47 const char *errormsg = NULL;
48 /*assert(dbconf->dbconn == dbconn);*/
50 while (++dbconf->reconnect_count <= 3) { /* retry */
51 if (0 == dbi_conn_connect(dbconn)) {
52 fdevent_setfd_cloexec(dbi_conn_get_socket(dbconn));
53 return;
57 dbi_conn_error(dbconn, &errormsg);
58 log_error_write(dbconf->srv, __FILE__, __LINE__, "ss",
59 "dbi_conn_connect():", errormsg);
62 static void mod_vhostdb_dbconf_free (void *vdata)
64 vhostdb_config *dbconf = (vhostdb_config *)vdata;
65 if (!dbconf) return;
66 dbi_conn_close(dbconf->dbconn);
67 dbi_shutdown_r(dbconf->dbinst);
68 free(dbconf);
71 static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata)
73 buffer *sqlquery = NULL;
74 const buffer *dbtype=NULL, *dbname=NULL;
76 for (size_t i = 0; i < opts->used; ++i) {
77 const data_string *ds = (data_string *)opts->data[i];
78 if (ds->type == TYPE_STRING) {
79 if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) {
80 sqlquery = ds->value;
81 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) {
82 dbname = ds->value;
83 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbtype"))) {
84 dbtype = ds->value;
89 /* required:
90 * - sql (sql query)
91 * - dbtype
92 * - dbname
94 * optional:
95 * - username, some databases don't require this (sqlite)
96 * - password, default: empty
97 * - socket, default: database type default
98 * - hostname, if set overrides socket
99 * - port, default: database default
100 * - encoding, default: database default
103 if (!buffer_string_is_empty(sqlquery)
104 && !buffer_is_empty(dbname) && !buffer_is_empty(dbtype)) {
105 /* create/initialise database */
106 vhostdb_config *dbconf;
107 dbi_inst dbinst = NULL;
108 dbi_conn dbconn;
109 if (dbi_initialize_r(NULL, &dbinst) < 1) {
110 log_error_write(srv, __FILE__, __LINE__, "s",
111 "dbi_initialize_r() failed. "
112 "Do you have the DBD for this db type installed?");
113 return -1;
115 dbconn = dbi_conn_new_r(dbtype->ptr, dbinst);
116 if (NULL == dbconn) {
117 log_error_write(srv, __FILE__, __LINE__, "s",
118 "dbi_conn_new_r() failed. "
119 "Do you have the DBD for this db type installed?");
120 dbi_shutdown_r(dbinst);
121 return -1;
124 /* set options */
125 for (size_t j = 0; j < opts->used; ++j) {
126 data_unset *du = opts->data[j];
127 const buffer *opt = du->key;
128 if (!buffer_string_is_empty(opt)) {
129 if (du->type == TYPE_INTEGER) {
130 data_integer *di = (data_integer *)du;
131 dbi_conn_set_option_numeric(dbconn, opt->ptr, di->value);
132 } else if (du->type == TYPE_STRING) {
133 data_string *ds = (data_string *)du;
134 if (ds->value != sqlquery && ds->value != dbtype) {
135 dbi_conn_set_option(dbconn, opt->ptr, ds->value->ptr);
141 dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
142 dbconf->dbinst = dbinst;
143 dbconf->dbconn = dbconn;
144 dbconf->sqlquery = sqlquery;
145 dbconf->srv = srv;
146 dbconf->reconnect_count = 0;
147 *vdata = dbconf;
149 /* used to automatically reconnect to the database */
150 dbi_conn_error_handler(dbconn, mod_vhostdb_dbi_error_callback, dbconf);
152 /* connect to database */
153 mod_vhostdb_dbi_error_callback(dbconn, dbconf);
154 if (dbconf->reconnect_count >= 3) return -1;
157 return 0;
160 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
162 static int mod_vhostdb_dbi_query(server *srv, connection *con, void *p_d, buffer *docroot)
164 plugin_data *p = (plugin_data *)p_d;
165 vhostdb_config *dbconf;
166 dbi_result result;
167 unsigned long long nrows;
168 int retry_count = 0;
170 /*(reuse buffer for sql query before generating docroot result)*/
171 buffer *sqlquery = docroot;
172 buffer_string_set_length(sqlquery, 0); /*(also resets docroot (alias))*/
174 mod_vhostdb_patch_connection(srv, con, p);
175 if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
176 dbconf = (vhostdb_config *)p->conf.vdata;
178 for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) {
179 if (NULL != (d = strchr(b, '?'))) {
180 /* escape the uri.authority */
181 char *esc = NULL;
182 size_t len = dbi_conn_escape_string_copy(dbconf->dbconn, con->uri.authority->ptr, &esc);
183 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
184 buffer_append_string_len(sqlquery, esc, len);
185 free(esc);
186 if (0 == len) return -1;
187 } else {
188 d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery);
189 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
190 break;
194 /* reset our reconnect-attempt counter, this is a new query. */
195 dbconf->reconnect_count = 0;
197 do {
198 result = dbi_conn_query(dbconf->dbconn, sqlquery->ptr);
199 } while (!result && ++retry_count < 2);
201 buffer_string_set_length(docroot, 0); /*(reset buffer to store result)*/
203 if (!result) {
204 const char *errmsg;
205 dbi_conn_error(dbconf->dbconn, &errmsg);
206 log_error_write(srv, __FILE__, __LINE__, "s", errmsg);
207 return -1;
210 nrows = dbi_result_get_numrows(result);
211 if (nrows && nrows != DBI_ROW_ERROR && dbi_result_next_row(result)) {
212 buffer_copy_string(docroot, dbi_result_get_string_idx(result, 1));
213 } /* else no such virtual host */
215 dbi_result_free(result);
216 return 0;
222 INIT_FUNC(mod_vhostdb_init) {
223 static http_vhostdb_backend_t http_vhostdb_backend_dbi =
224 { "dbi", mod_vhostdb_dbi_query, NULL };
225 plugin_data *p = calloc(1, sizeof(*p));
227 /* register http_vhostdb_backend_dbi */
228 http_vhostdb_backend_dbi.p_d = p;
229 http_vhostdb_backend_set(&http_vhostdb_backend_dbi);
231 return p;
234 FREE_FUNC(mod_vhostdb_cleanup) {
235 plugin_data *p = p_d;
236 if (!p) return HANDLER_GO_ON;
238 if (p->config_storage) {
239 for (size_t i = 0; i < srv->config_context->used; i++) {
240 plugin_config *s = p->config_storage[i];
241 if (!s) continue;
242 mod_vhostdb_dbconf_free(s->vdata);
243 array_free(s->options);
244 free(s);
246 free(p->config_storage);
248 free(p);
250 UNUSED(srv);
251 return HANDLER_GO_ON;
254 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
255 plugin_data *p = p_d;
257 config_values_t cv[] = {
258 { "vhostdb.dbi", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
259 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
262 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
264 for (size_t i = 0; i < srv->config_context->used; ++i) {
265 data_config const *config = (data_config const*)srv->config_context->data[i];
266 plugin_config *s = calloc(1, sizeof(plugin_config));
268 s->options = array_init();
269 cv[0].destination = s->options;
271 p->config_storage[i] = s;
273 if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
274 return HANDLER_ERROR;
277 if (!array_is_kvany(s->options)) {
278 log_error_write(srv, __FILE__, __LINE__, "s",
279 "unexpected value for vhostdb.dbi; expected list of \"option\" => \"value\"");
280 return HANDLER_ERROR;
283 if (s->options->used
284 && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) {
285 return HANDLER_ERROR;
289 return HANDLER_GO_ON;
292 #define PATCH(x) \
293 p->conf.x = s->x;
294 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p)
296 plugin_config *s = p->config_storage[0];
297 PATCH(vdata);
299 /* skip the first, the global context */
300 for (size_t i = 1; i < srv->config_context->used; ++i) {
301 data_config *dc = (data_config *)srv->config_context->data[i];
302 s = p->config_storage[i];
304 /* condition didn't match */
305 if (!config_check_cond(srv, con, dc)) continue;
307 /* merge config */
308 for (size_t j = 0; j < dc->value->used; ++j) {
309 data_unset *du = dc->value->data[j];
311 if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.dbi"))) {
312 PATCH(vdata);
317 #undef PATCH
319 /* this function is called at dlopen() time and inits the callbacks */
320 int mod_vhostdb_dbi_plugin_init (plugin *p);
321 int mod_vhostdb_dbi_plugin_init (plugin *p)
323 p->version = LIGHTTPD_VERSION_ID;
324 p->name = buffer_init_string("vhostdb_dbi");
326 p->init = mod_vhostdb_init;
327 p->cleanup = mod_vhostdb_cleanup;
328 p->set_defaults = mod_vhostdb_set_defaults;
330 return 0;