]> granicus.if.org Git - apache/blob - modules/database/mod_dbd.c
mod_dbd: The dbd_prepared_init() function reported the result of the last
[apache] / modules / database / mod_dbd.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Overview of what this is and does:
18  * http://www.apache.org/~niq/dbd.html
19  * or
20  * http://apache.webthing.com/database/
21  */
22
23 #include "apr_reslist.h"
24 #include "apr_strings.h"
25 #include "apr_hash.h"
26 #include "apr_tables.h"
27 #include "apr_lib.h"
28 #include "apr_dbd.h"
29
30 #define APR_WANT_MEMFUNC
31 #define APR_WANT_STRFUNC
32 #include "apr_want.h"
33
34 #include "http_protocol.h"
35 #include "http_config.h"
36 #include "http_log.h"
37 #include "http_request.h"
38 #include "mod_dbd.h"
39
40 extern module AP_MODULE_DECLARE_DATA dbd_module;
41
42 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(dbd, DBD, apr_status_t, post_connect,
43                                     (apr_pool_t *pool, dbd_cfg_t *cfg,
44                                     ap_dbd_t *dbd),
45                                     (pool, cfg, dbd), OK, DECLINED)
46
47 /************ svr cfg: manage db connection pool ****************/
48
49 #define NMIN_SET     0x1
50 #define NKEEP_SET    0x2
51 #define NMAX_SET     0x4
52 #define EXPTIME_SET  0x8
53
54 typedef struct dbd_group_t dbd_group_t;
55
56 struct dbd_group_t {
57     dbd_cfg_t *cfg;
58     dbd_group_t *next;
59     apr_pool_t *pool;
60 #if APR_HAS_THREADS
61     apr_thread_mutex_t *mutex;
62     apr_reslist_t *reslist;
63     int destroyed;
64 #else
65     ap_dbd_t *rec;
66 #endif
67 };
68
69 typedef struct {
70     dbd_cfg_t *cfg;
71     dbd_group_t *group;
72 } svr_cfg;
73
74 typedef enum { cmd_name, cmd_params, cmd_persist,
75                cmd_min, cmd_keep, cmd_max, cmd_exp
76 } cmd_parts;
77
78 static apr_pool_t *config_pool;
79 static dbd_group_t *group_list;
80
81 /* a default DBDriver value that'll generate meaningful error messages */
82 static const char *const no_dbdriver = "[DBDriver unset]";
83
84 /* A default nmin of >0 will help with generating meaningful
85  * startup error messages if the database is down.
86  */
87 #define DEFAULT_NMIN 1
88 #define DEFAULT_NKEEP 2
89 #define DEFAULT_NMAX 10
90 #define DEFAULT_EXPTIME 300
91
92 #define DEFAULT_SQL_INIT_ARRAY_SIZE 5
93
94 static void *create_dbd_config(apr_pool_t *pool, server_rec *s)
95 {
96     svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg));
97     dbd_cfg_t *cfg = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t));
98
99     cfg->server = s;
100     cfg->name = no_dbdriver; /* to generate meaningful error messages */
101     cfg->params = ""; /* don't risk segfault on misconfiguration */
102     cfg->persist = -1;
103 #if APR_HAS_THREADS
104     cfg->nmin = DEFAULT_NMIN;
105     cfg->nkeep = DEFAULT_NKEEP;
106     cfg->nmax = DEFAULT_NMAX;
107     cfg->exptime = DEFAULT_EXPTIME;
108 #endif
109     cfg->queries = apr_hash_make(pool);
110     cfg->init_queries = apr_array_make(pool, DEFAULT_SQL_INIT_ARRAY_SIZE,
111                                        sizeof(const char *));
112
113     return svr;
114 }
115
116 static void *merge_dbd_config(apr_pool_t *pool, void *basev, void *addv)
117 {
118     dbd_cfg_t *base = ((svr_cfg*) basev)->cfg;
119     dbd_cfg_t *add = ((svr_cfg*) addv)->cfg;
120     svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg));
121     dbd_cfg_t *new = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t));
122
123     new->server = add->server;
124     new->name = (add->name != no_dbdriver) ? add->name : base->name;
125     new->params = strcmp(add->params, "") ? add->params : base->params;
126     new->persist = (add->persist != -1) ? add->persist : base->persist;
127 #if APR_HAS_THREADS
128     new->nmin = (add->set&NMIN_SET) ? add->nmin : base->nmin;
129     new->nkeep = (add->set&NKEEP_SET) ? add->nkeep : base->nkeep;
130     new->nmax = (add->set&NMAX_SET) ? add->nmax : base->nmax;
131     new->exptime = (add->set&EXPTIME_SET) ? add->exptime : base->exptime;
132 #endif
133     new->queries = apr_hash_overlay(pool, add->queries, base->queries);
134     new->init_queries = apr_array_append(pool, add->init_queries,
135                                          base->init_queries);
136
137     return svr;
138 }
139
140 static void ap_dbd_sql_init(server_rec *s, const char *query)
141 {
142     svr_cfg *svr;
143     const char **arr_item;
144
145     svr = ap_get_module_config(s->module_config, &dbd_module);
146     if (!svr) {
147          /* some modules may call from within config directive handlers, and
148           * if these are called in a server context that contains no mod_dbd
149           * config directives, then we have to create our own server config
150           */
151          svr = create_dbd_config(config_pool, s);
152          ap_set_module_config(s->module_config, &dbd_module, svr);
153     }
154
155     if (query) {
156         arr_item = apr_array_push(svr->cfg->init_queries);
157         *arr_item = query;
158     }
159 }
160
161 static const char *dbd_param(cmd_parms *cmd, void *dconf, const char *val)
162 {
163     apr_status_t rv;
164     const apr_dbd_driver_t *driver = NULL;
165     svr_cfg *svr = ap_get_module_config(cmd->server->module_config,
166                                         &dbd_module);
167     dbd_cfg_t *cfg = svr->cfg;
168
169     switch ((long) cmd->info) {
170     case cmd_name:
171         cfg->name = val;
172         /* loading the driver involves once-only dlloading that is
173          * best done at server startup.  This also guarantees that
174          * we won't return an error later.
175          */
176         rv = apr_dbd_get_driver(cmd->pool, cfg->name, &driver);
177         if (APR_STATUS_IS_ENOTIMPL(rv)) {
178             return apr_psprintf(cmd->pool, "DBD: No driver for %s", cfg->name);
179         }
180         else if (APR_STATUS_IS_EDSOOPEN(rv)) {
181             return apr_psprintf(cmd->pool,
182 #ifdef NETWARE
183                                 "DBD: Can't load driver file dbd%s.nlm",
184 #else
185                                 "DBD: Can't load driver file apr_dbd_%s.so",
186 #endif
187                                 cfg->name);
188         }
189         else if (APR_STATUS_IS_ESYMNOTFOUND(rv)) {
190             return apr_psprintf(cmd->pool,
191                                 "DBD: Failed to load driver apr_dbd_%s_driver",
192                                 cfg->name);
193         }
194         break;
195     case cmd_params:
196         cfg->params = val;
197         break;
198     }
199
200     return NULL;
201 }
202
203 #if APR_HAS_THREADS
204 static const char *dbd_param_int(cmd_parms *cmd, void *dconf, const char *val)
205 {
206     svr_cfg *svr = ap_get_module_config(cmd->server->module_config,
207                                         &dbd_module);
208     dbd_cfg_t *cfg = svr->cfg;
209     const char *p;
210
211     for (p = val; *p; ++p) {
212         if (!apr_isdigit(*p)) {
213             return "Argument must be numeric!";
214         }
215     }
216
217     switch ((long) cmd->info) {
218     case cmd_min:
219         cfg->nmin = atoi(val);
220         cfg->set |= NMIN_SET;
221         break;
222     case cmd_keep:
223         cfg->nkeep = atoi(val);
224         cfg->set |= NKEEP_SET;
225         break;
226     case cmd_max:
227         cfg->nmax = atoi(val);
228         cfg->set |= NMAX_SET;
229         break;
230     case cmd_exp:
231         cfg->exptime = atoi(val);
232         cfg->set |= EXPTIME_SET;
233         break;
234     }
235
236     return NULL;
237 }
238 #endif
239
240 static const char *dbd_param_flag(cmd_parms *cmd, void *dconf, int flag)
241 {
242     svr_cfg *svr = ap_get_module_config(cmd->server->module_config,
243                                         &dbd_module);
244
245     switch ((long) cmd->info) {
246     case cmd_persist:
247         svr->cfg->persist = flag;
248         break;
249     }
250
251     return NULL;
252 }
253
254 static const char *dbd_prepare(cmd_parms *cmd, void *dconf, const char *query,
255                                const char *label)
256 {
257     if (!label) {
258         label = query;
259         query = "";
260     }
261
262     ap_dbd_prepare(cmd->server, query, label);
263
264     return NULL;
265 }
266
267 static const char *dbd_init_sql(cmd_parms *cmd, void *dconf, const char *query)
268 {
269     if (!query || *query == '\n') {
270         return "You should specify SQL statement";
271     }
272
273     ap_dbd_sql_init(cmd->server, query);
274
275     return NULL;
276 }
277
278 static const command_rec dbd_cmds[] = {
279     AP_INIT_TAKE1("DBDriver", dbd_param, (void*)cmd_name, RSRC_CONF,
280                   "SQL Driver"),
281     AP_INIT_TAKE1("DBDParams", dbd_param, (void*)cmd_params, RSRC_CONF,
282                   "SQL Driver Params"),
283     AP_INIT_FLAG("DBDPersist", dbd_param_flag, (void*)cmd_persist, RSRC_CONF,
284                  "Use persistent connection/pool"),
285     AP_INIT_TAKE12("DBDPrepareSQL", dbd_prepare, NULL, RSRC_CONF,
286                    "SQL statement to prepare (or nothing, to override "
287                    "statement inherited from main server) and label"),
288     AP_INIT_TAKE1("DBDInitSQL", dbd_init_sql, NULL, RSRC_CONF,
289                    "SQL statement to be executed after connection is created"),
290 #if APR_HAS_THREADS
291     AP_INIT_TAKE1("DBDMin", dbd_param_int, (void*)cmd_min, RSRC_CONF,
292                   "Minimum number of connections"),
293     /* XXX: note that mod_proxy calls this "smax" */
294     AP_INIT_TAKE1("DBDKeep", dbd_param_int, (void*)cmd_keep, RSRC_CONF,
295                   "Maximum number of sustained connections"),
296     AP_INIT_TAKE1("DBDMax", dbd_param_int, (void*)cmd_max, RSRC_CONF,
297                   "Maximum number of connections"),
298     /* XXX: note that mod_proxy calls this "ttl" (time to live) */
299     AP_INIT_TAKE1("DBDExptime", dbd_param_int, (void*)cmd_exp, RSRC_CONF,
300                   "Keepalive time for idle connections"),
301 #endif
302     {NULL}
303 };
304
305 static int dbd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
306                           apr_pool_t *ptemp)
307 {
308    config_pool = pconf;
309    group_list = NULL;
310    return OK;
311 }
312
313 DBD_DECLARE_NONSTD(void) ap_dbd_prepare(server_rec *s, const char *query,
314                                         const char *label)
315 {
316     svr_cfg *svr;
317
318     svr = ap_get_module_config(s->module_config, &dbd_module);
319     if (!svr) {
320          /* some modules may call from within config directive handlers, and
321           * if these are called in a server context that contains no mod_dbd
322           * config directives, then we have to create our own server config
323           */
324          svr = create_dbd_config(config_pool, s);
325          ap_set_module_config(s->module_config, &dbd_module, svr);
326     }
327
328     if (apr_hash_get(svr->cfg->queries, label, APR_HASH_KEY_STRING)
329         && strcmp(query, "")) {
330         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
331                      "conflicting SQL statements with label %s", label);
332     }
333
334     apr_hash_set(svr->cfg->queries, label, APR_HASH_KEY_STRING, query);
335 }
336
337 typedef struct {
338     const char *label, *query;
339 } dbd_query_t;
340
341 static int dbd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
342                            apr_pool_t *ptemp, server_rec *s)
343 {
344     server_rec *sp;
345     apr_array_header_t *add_queries = apr_array_make(ptemp, 10,
346                                                      sizeof(dbd_query_t));
347
348     for (sp = s; sp; sp = sp->next) {
349         svr_cfg *svr = ap_get_module_config(sp->module_config, &dbd_module);
350         dbd_cfg_t *cfg = svr->cfg;
351         apr_hash_index_t *hi_first = apr_hash_first(ptemp, cfg->queries);
352         dbd_group_t *group;
353
354         /* dbd_setup in 2.2.3 and under was causing spurious error messages
355          * when dbd isn't configured.  We can stop that with a quick check here
356          * together with a similar check in ap_dbd_open (where being
357          * unconfigured is a genuine error that must be reported).
358          */
359         if (cfg->name == no_dbdriver || !cfg->persist) {
360             continue;
361         }
362
363         for (group = group_list; group; group = group->next) {
364             dbd_cfg_t *group_cfg = group->cfg;
365             apr_hash_index_t *hi;
366             int group_ok = 1;
367
368             if (strcmp(cfg->name, group_cfg->name)
369                 || strcmp(cfg->params, group_cfg->params)) {
370                 continue;
371             }
372
373 #if APR_HAS_THREADS
374             if (cfg->nmin != group_cfg->nmin
375                 || cfg->nkeep != group_cfg->nkeep
376                 || cfg->nmax != group_cfg->nmax
377                 || cfg->exptime != group_cfg->exptime) {
378                 continue;
379             }
380 #endif
381
382             add_queries->nelts = 0;
383
384             for (hi = hi_first; hi; hi = apr_hash_next(hi)) {
385                 const char *label, *query;
386                 const char *group_query;
387
388                 apr_hash_this(hi, (void*) &label, NULL, (void*) &query);
389
390                 group_query = apr_hash_get(group_cfg->queries, label,
391                                            APR_HASH_KEY_STRING);
392
393                 if (!group_query) {
394                     dbd_query_t *add_query = apr_array_push(add_queries);
395
396                     add_query->label = label;
397                     add_query->query = query;
398                 }
399                 else if (strcmp(query, group_query)) {
400                     group_ok = 0;
401                     break;
402                 }
403             }
404
405             if (group_ok) {
406                 int i;
407
408                 for (i = 0; i < add_queries->nelts; ++i) {
409                     dbd_query_t *add_query = ((dbd_query_t*) add_queries->elts)
410                                              + i;
411
412                     apr_hash_set(group_cfg->queries, add_query->label,
413                                  APR_HASH_KEY_STRING, add_query->query);
414                 }
415
416                 svr->group = group;
417                 break;
418             }
419         }
420
421         if (!svr->group) {
422             svr->group = group = apr_pcalloc(pconf, sizeof(dbd_group_t));
423
424             group->cfg = cfg;
425
426             group->next = group_list;
427             group_list = group;
428         }
429     }
430
431     return OK;
432 }
433
434 static apr_status_t dbd_prepared_init(apr_pool_t *pool, dbd_cfg_t *cfg,
435                                       ap_dbd_t *rec)
436 {
437     apr_hash_index_t *hi;
438
439     rec->prepared = apr_hash_make(pool);
440
441     for (hi = apr_hash_first(pool, cfg->queries); hi;
442          hi = apr_hash_next(hi)) {
443         const char *label, *query;
444         apr_dbd_prepared_t *stmt;
445
446         apr_hash_this(hi, (void*) &label, NULL, (void*) &query);
447
448         if (!strcmp(query, "")) {
449             continue;
450         }
451
452         stmt = NULL;
453         if (apr_dbd_prepare(rec->driver, pool, rec->handle, query,
454                             label, &stmt)) {
455             return APR_EGENERAL;
456         }
457         else {
458             apr_hash_set(rec->prepared, label, APR_HASH_KEY_STRING, stmt);
459         }
460     }
461
462     return APR_SUCCESS;
463 }
464
465 static apr_status_t dbd_init_sql_init(apr_pool_t *pool, dbd_cfg_t *cfg,
466                                       ap_dbd_t *rec)
467 {
468     int i;
469     apr_status_t rv = APR_SUCCESS;
470
471     for (i = 0; i < cfg->init_queries->nelts; i++) {
472         int nrows;
473         char **query_p;
474
475         query_p = (char **)cfg->init_queries->elts + i;
476
477         if (apr_dbd_query(rec->driver, rec->handle, &nrows, *query_p)) {
478             rv = APR_EGENERAL;
479             break;
480         }
481     }
482
483     return rv;
484 }
485
486 static apr_status_t dbd_close(void *data)
487 {
488     ap_dbd_t *rec = data;
489
490     return apr_dbd_close(rec->driver, rec->handle);
491 }
492
493 #if APR_HAS_THREADS
494 static apr_status_t dbd_destruct(void *data, void *params, apr_pool_t *pool)
495 {
496     dbd_group_t *group = params;
497
498     if (!group->destroyed) {
499         ap_dbd_t *rec = data;
500
501         apr_pool_destroy(rec->pool);
502     }
503
504     return APR_SUCCESS;
505 }
506 #endif
507
508 /* an apr_reslist_constructor for SQL connections
509  * Also use this for opening in non-reslist modes, since it gives
510  * us all the error-handling in one place.
511  */
512 static apr_status_t dbd_construct(void **data_ptr,
513                                   void *params, apr_pool_t *pool)
514 {
515     dbd_group_t *group = params;
516     dbd_cfg_t *cfg = group->cfg;
517     apr_pool_t *rec_pool, *prepared_pool;
518     ap_dbd_t *rec;
519     apr_status_t rv;
520     const char *err = "";
521
522     rv = apr_pool_create(&rec_pool, pool);
523     if (rv != APR_SUCCESS) {
524         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server,
525                      "DBD: Failed to create memory pool");
526         return rv;
527     }
528
529     rec = apr_pcalloc(rec_pool, sizeof(ap_dbd_t));
530
531     rec->pool = rec_pool;
532
533     /* The driver is loaded at config time now, so this just checks a hash.
534      * If that changes, the driver DSO could be registered to unload against
535      * our pool, which is probably not what we want.  Error checking isn't
536      * necessary now, but in case that changes in the future ...
537      */
538     rv = apr_dbd_get_driver(rec->pool, cfg->name, &rec->driver);
539     if (rv != APR_SUCCESS) {
540         if (APR_STATUS_IS_ENOTIMPL(rv)) {
541             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
542                          "DBD: driver for %s not available", cfg->name);
543         }
544         else if (APR_STATUS_IS_EDSOOPEN(rv)) {
545             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
546                          "DBD: can't find driver for %s", cfg->name);
547         }
548         else if (APR_STATUS_IS_ESYMNOTFOUND(rv)) {
549             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
550                          "DBD: driver for %s is invalid or corrupted",
551                          cfg->name);
552         }
553         else {
554             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
555                          "DBD: mod_dbd not compatible with APR in get_driver");
556         }
557         apr_pool_destroy(rec->pool);
558         return rv;
559     }
560
561     rv = apr_dbd_open_ex(rec->driver, rec->pool, cfg->params, &rec->handle, &err);
562     if (rv != APR_SUCCESS) {
563         switch (rv) {
564         case APR_EGENERAL:
565             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
566                          "DBD: Can't connect to %s: %s", cfg->name, err);
567             break;
568         default:
569             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
570                          "DBD: mod_dbd not compatible with APR in open");
571             break;
572         }
573
574         apr_pool_destroy(rec->pool);
575         return rv;
576     }
577
578     apr_pool_cleanup_register(rec->pool, rec, dbd_close,
579                               apr_pool_cleanup_null);
580
581     /* we use a sub-pool for the prepared statements for each connection so
582      * that they will be cleaned up first, before the connection is closed
583      */
584     rv = apr_pool_create(&prepared_pool, rec->pool);
585     if (rv != APR_SUCCESS) {
586         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server,
587                      "DBD: Failed to create memory pool");
588
589         apr_pool_destroy(rec->pool);
590         return rv;
591     }
592
593     rv = dbd_prepared_init(prepared_pool, cfg, rec);
594     if (rv != APR_SUCCESS) {
595         const char *errmsg = apr_dbd_error(rec->driver, rec->handle, rv);
596         ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
597                      "DBD: failed to prepare SQL statements: %s",
598                      (errmsg ? errmsg : "[???]"));
599
600         apr_pool_destroy(rec->pool);
601         return rv;
602     }
603
604     dbd_run_post_connect(prepared_pool, cfg, rec);
605
606     *data_ptr = rec;
607
608     return APR_SUCCESS;
609 }
610
611 #if APR_HAS_THREADS
612 static apr_status_t dbd_destroy(void *data)
613 {
614     dbd_group_t *group = data;
615
616     group->destroyed = 1;
617
618     return APR_SUCCESS;
619 }
620
621 static apr_status_t dbd_setup(server_rec *s, dbd_group_t *group)
622 {
623     dbd_cfg_t *cfg = group->cfg;
624     apr_status_t rv;
625
626     /* We create the reslist using a sub-pool of the pool passed to our
627      * child_init hook.  No other threads can be here because we're
628      * either in the child_init phase or dbd_setup_lock() acquired our mutex.
629      * No other threads will use this sub-pool after this, except via
630      * reslist calls, which have an internal mutex.
631      *
632      * We need to short-circuit the cleanup registered internally by
633      * apr_reslist_create().  We do this by registering dbd_destroy()
634      * as a cleanup afterwards, so that it will run before the reslist's
635      * internal cleanup.
636      *
637      * If we didn't do this, then we could free memory twice when the pool
638      * was destroyed.  When apr_pool_destroy() runs, it first destroys all
639      * all the per-connection sub-pools created in dbd_construct(), and
640      * then it runs the reslist's cleanup.  The cleanup calls dbd_destruct()
641      * on each resource, which would then attempt to destroy the sub-pools
642      * a second time.
643      */
644     rv = apr_reslist_create(&group->reslist,
645                             cfg->nmin, cfg->nkeep, cfg->nmax,
646                             apr_time_from_sec(cfg->exptime),
647                             dbd_construct, dbd_destruct, group,
648                             group->pool);
649     if (rv != APR_SUCCESS) {
650         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
651                      "DBD: failed to initialise");
652         return rv;
653     }
654
655     apr_pool_cleanup_register(group->pool, group, dbd_destroy,
656                               apr_pool_cleanup_null);
657
658     return APR_SUCCESS;
659 }
660 #endif
661
662 static apr_status_t dbd_setup_init(apr_pool_t *pool, server_rec *s)
663 {
664     dbd_group_t *group;
665     apr_status_t rv = APR_SUCCESS;
666
667     for (group = group_list; group; group = group->next) {
668         apr_status_t rv2;
669
670         rv2 = apr_pool_create(&group->pool, pool);
671         if (rv2 != APR_SUCCESS) {
672             ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s,
673                          "DBD: Failed to create reslist cleanup memory pool");
674             return rv2;
675         }
676
677 #if APR_HAS_THREADS
678         rv2 = dbd_setup(s, group);
679         if (rv2 == APR_SUCCESS) {
680             continue;
681         }
682         else if (rv == APR_SUCCESS) {
683             rv = rv2;
684         }
685
686         /* we failed, so create a mutex so that subsequent competing callers
687          * to ap_dbd_open can serialize themselves while they retry
688          */
689         rv2 = apr_thread_mutex_create(&group->mutex,
690                                       APR_THREAD_MUTEX_DEFAULT, pool);
691         if (rv2 != APR_SUCCESS) {
692              ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s,
693                           "DBD: Failed to create thread mutex");
694              return rv2;
695         }
696 #endif
697     }
698
699     return rv;
700 }
701
702 static void dbd_child_init(apr_pool_t *p, server_rec *s)
703 {
704   apr_status_t rv = dbd_setup_init(p, s);
705   if (rv) {
706     ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
707                  "DBD: child init failed!");
708   }
709 }
710
711 #if APR_HAS_THREADS
712 static apr_status_t dbd_setup_lock(server_rec *s, dbd_group_t *group)
713 {
714     apr_status_t rv = APR_SUCCESS, rv2;
715
716     /* several threads could be here at the same time, all trying to
717      * initialize the reslist because dbd_setup_init failed to do so
718      */
719     if (!group->mutex) {
720         /* we already logged an error when the mutex couldn't be created */
721         return APR_EGENERAL;
722     }
723
724     rv2 = apr_thread_mutex_lock(group->mutex);
725     if (rv2 != APR_SUCCESS) {
726         ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s,
727                      "DBD: Failed to acquire thread mutex");
728         return rv2;
729     }
730
731     if (!group->reslist) {
732         rv = dbd_setup(s, group);
733     }
734
735     rv2 = apr_thread_mutex_unlock(group->mutex);
736     if (rv2 != APR_SUCCESS) {
737         ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s,
738                      "DBD: Failed to release thread mutex");
739         if (rv == APR_SUCCESS) {
740             rv = rv2;
741         }
742     }
743
744     return rv;
745 }
746 #endif
747
748 /* Functions we export for modules to use:
749         - open acquires a connection from the pool (opens one if necessary)
750         - close releases it back in to the pool
751 */
752 DBD_DECLARE_NONSTD(void) ap_dbd_close(server_rec *s, ap_dbd_t *rec)
753 {
754     svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module);
755
756     if (!svr->cfg->persist) {
757         apr_pool_destroy(rec->pool);
758     }
759 #if APR_HAS_THREADS
760     else {
761         apr_reslist_release(svr->group->reslist, rec);
762     }
763 #endif
764 }
765
766 static apr_status_t dbd_check(apr_pool_t *pool, server_rec *s, ap_dbd_t *rec)
767 {
768     svr_cfg *svr;
769     apr_status_t rv = apr_dbd_check_conn(rec->driver, pool, rec->handle);
770     const char *errmsg;
771
772     if ((rv == APR_SUCCESS) || (rv == APR_ENOTIMPL)) {
773         return APR_SUCCESS;
774     }
775
776     /* we don't have a driver-specific error code, so we'll just pass
777      * a "success" value and rely on the driver to ignore it
778      */
779     errmsg = apr_dbd_error(rec->driver, rec->handle, 0);
780     if (!errmsg) {
781         errmsg = "(unknown)";
782     }
783
784     svr = ap_get_module_config(s->module_config, &dbd_module);
785     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
786                  "DBD [%s] Error: %s", svr->cfg->name, errmsg);
787     return rv;
788 }
789
790 DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s)
791 {
792     svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module);
793     dbd_group_t *group = svr->group;
794     dbd_cfg_t *cfg = svr->cfg;
795     ap_dbd_t *rec = NULL;
796 #if APR_HAS_THREADS
797     apr_status_t rv;
798 #endif
799
800     /* If nothing is configured, we shouldn't be here */
801     if (cfg->name == no_dbdriver) {
802         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "DBD: not configured");
803         return NULL;
804     }
805
806     if (!cfg->persist) {
807         /* Return a once-only connection */
808         group = apr_pcalloc(pool, sizeof(dbd_group_t));
809
810         group->cfg = cfg;
811
812         dbd_construct((void*) &rec, group, pool);
813         return rec;
814     }
815
816 #if APR_HAS_THREADS
817     if (!group->reslist) {
818         if (dbd_setup_lock(s, group) != APR_SUCCESS) {
819             return NULL;
820         }
821     }
822
823     rv = apr_reslist_acquire(group->reslist, (void*) &rec);
824     if (rv != APR_SUCCESS) {
825         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
826                      "Failed to acquire DBD connection from pool!");
827         return NULL;
828     }
829
830     if (dbd_check(pool, s, rec) != APR_SUCCESS) {
831         apr_reslist_invalidate(group->reslist, rec);
832         return NULL;
833     }
834 #else
835     /* If we have a persistent connection and it's good, we'll use it;
836      * since this is non-threaded, we can update without a mutex
837      */
838     rec = group->rec;
839     if (rec) {
840         if (dbd_check(pool, s, rec) != APR_SUCCESS) {
841             apr_pool_destroy(rec->pool);
842             rec = NULL;
843         }
844     }
845
846     /* We don't have a connection right now, so we'll open one */
847     if (!rec) {
848         dbd_construct((void*) &rec, group, group->pool);
849         group->rec = rec;
850     }
851 #endif
852
853     return rec;
854 }
855
856 #if APR_HAS_THREADS
857 typedef struct {
858     ap_dbd_t *rec;
859     apr_reslist_t *reslist;
860 } dbd_acquire_t;
861
862 static apr_status_t dbd_release(void *data)
863 {
864     dbd_acquire_t *acq = data;
865     apr_reslist_release(acq->reslist, acq->rec);
866     return APR_SUCCESS;
867 }
868
869 DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r)
870 {
871     dbd_acquire_t *acq;
872
873     while (!ap_is_initial_req(r)) {
874         if (r->prev) {
875             r = r->prev;
876         }
877         else if (r->main) {
878             r = r->main;
879         }
880     }
881
882     acq = ap_get_module_config(r->request_config, &dbd_module);
883     if (!acq) {
884         acq = apr_palloc(r->pool, sizeof(dbd_acquire_t));
885         acq->rec = ap_dbd_open(r->pool, r->server);
886         if (acq->rec) {
887             svr_cfg *svr = ap_get_module_config(r->server->module_config,
888                                                 &dbd_module);
889
890             ap_set_module_config(r->request_config, &dbd_module, acq);
891             if (svr->cfg->persist) {
892                 acq->reslist = svr->group->reslist;
893                 apr_pool_cleanup_register(r->pool, acq, dbd_release,
894                                           apr_pool_cleanup_null);
895             }
896         }
897     }
898
899     return acq->rec;
900 }
901
902 DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_cacquire(conn_rec *c)
903 {
904     dbd_acquire_t *acq = ap_get_module_config(c->conn_config, &dbd_module);
905
906     if (!acq) {
907         acq = apr_palloc(c->pool, sizeof(dbd_acquire_t));
908         acq->rec = ap_dbd_open(c->pool, c->base_server);
909         if (acq->rec) {
910             svr_cfg *svr = ap_get_module_config(c->base_server->module_config,
911                                                 &dbd_module);
912
913             ap_set_module_config(c->conn_config, &dbd_module, acq);
914             if (svr->cfg->persist) {
915                 acq->reslist = svr->group->reslist;
916                 apr_pool_cleanup_register(c->pool, acq, dbd_release,
917                                           apr_pool_cleanup_null);
918             }
919         }
920     }
921
922     return acq->rec;
923 }
924 #else
925 DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r)
926 {
927     ap_dbd_t *rec;
928
929     while (!ap_is_initial_req(r)) {
930         if (r->prev) {
931             r = r->prev;
932         }
933         else if (r->main) {
934             r = r->main;
935         }
936     }
937
938     rec = ap_get_module_config(r->request_config, &dbd_module);
939     if (!rec) {
940         rec = ap_dbd_open(r->pool, r->server);
941         if (rec) {
942             ap_set_module_config(r->request_config, &dbd_module, rec);
943         }
944     }
945
946     return rec;
947 }
948
949 DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_cacquire(conn_rec *c)
950 {
951     ap_dbd_t *rec = ap_get_module_config(c->conn_config, &dbd_module);
952
953     if (!rec) {
954         rec = ap_dbd_open(c->pool, c->base_server);
955         if (rec) {
956             ap_set_module_config(c->conn_config, &dbd_module, rec);
957         }
958     }
959
960     return rec;
961 }
962 #endif
963
964 static void dbd_hooks(apr_pool_t *pool)
965 {
966     ap_hook_pre_config(dbd_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
967     ap_hook_post_config(dbd_post_config, NULL, NULL, APR_HOOK_MIDDLE);
968     ap_hook_child_init(dbd_child_init, NULL, NULL, APR_HOOK_MIDDLE);
969
970     APR_REGISTER_OPTIONAL_FN(ap_dbd_prepare);
971     APR_REGISTER_OPTIONAL_FN(ap_dbd_open);
972     APR_REGISTER_OPTIONAL_FN(ap_dbd_close);
973     APR_REGISTER_OPTIONAL_FN(ap_dbd_acquire);
974     APR_REGISTER_OPTIONAL_FN(ap_dbd_cacquire);
975
976     APR_OPTIONAL_HOOK(dbd, post_connect, dbd_init_sql_init,
977                       NULL, NULL, APR_HOOK_MIDDLE);
978
979     apr_dbd_init(pool);
980 }
981
982 AP_DECLARE_MODULE(dbd) = {
983     STANDARD20_MODULE_STUFF,
984     NULL,
985     NULL,
986     create_dbd_config,
987     merge_dbd_config,
988     dbd_cmds,
989     dbd_hooks
990 };
991