9 #include "http_vhostdb.h"
15 * virtual host plugin using DBI for domain to directory lookups
18 * vhostdb.dbi = ( "sql" => "SELECT docroot FROM vhosts WHERE host='?'"
19 * "dbtype" => "sqlite3",
20 * "dbname" => "mydb.sqlite",
21 * "sqlite_dbdir" => "/path/to/sqlite/dbs/" )
29 short reconnect_count
;
39 plugin_config
**config_storage
;
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
));
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
;
66 dbi_conn_close(dbconf
->dbconn
);
67 dbi_shutdown_r(dbconf
->dbinst
);
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"))) {
81 } else if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("dbname"))) {
83 } else if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("dbtype"))) {
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
;
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?");
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
);
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
;
146 dbconf
->reconnect_count
= 0;
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;
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
;
167 unsigned long long nrows
;
170 /*(reuse buffer for sql query before generating docroot result)*/
171 buffer
*sqlquery
= docroot
;
172 buffer_clear(sqlquery
); /*(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 */
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
);
186 if (0 == len
) return -1;
188 d
= dbconf
->sqlquery
->ptr
+ buffer_string_length(dbconf
->sqlquery
);
189 buffer_append_string_len(sqlquery
, b
, (size_t)(d
- b
));
194 /* reset our reconnect-attempt counter, this is a new query. */
195 dbconf
->reconnect_count
= 0;
198 result
= dbi_conn_query(dbconf
->dbconn
, sqlquery
->ptr
);
199 } while (!result
&& ++retry_count
< 2);
201 buffer_clear(docroot
); /*(reset buffer to store result)*/
205 dbi_conn_error(dbconf
->dbconn
, &errmsg
);
206 log_error_write(srv
, __FILE__
, __LINE__
, "s", errmsg
);
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
);
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
);
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
];
242 mod_vhostdb_dbconf_free(s
->vdata
);
243 array_free(s
->options
);
246 free(p
->config_storage
);
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(srv
->config_context
->used
, sizeof(plugin_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
;
284 && 0 != mod_vhostdb_dbconf_setup(srv
, s
->options
, &s
->vdata
)) {
285 return HANDLER_ERROR
;
289 return HANDLER_GO_ON
;
294 static void mod_vhostdb_patch_connection (server
*srv
, connection
*con
, plugin_data
*p
)
296 plugin_config
*s
= p
->config_storage
[0];
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;
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"))) {
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
;