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/*
18 * Info Module.  Display configuration information for the server and
19 * all included modules.
20 *
21 * <Location /server-info>
22 * SetHandler server-info
23 * </Location>
24 *
25 * GET /server-info - Returns full configuration page for server and all modules
26 * GET /server-info?server - Returns server configuration only
27 * GET /server-info?module_name - Returns configuration for a single module
28 * GET /server-info?list - Returns quick list of included modules
29 * GET /server-info?config - Returns full configuration
30 * GET /server-info?hooks - Returns a listing of the modules active for each hook
31 *
32 * Original Author:
33 *   Rasmus Lerdorf <rasmus vex.net>, May 1996
34 *
35 * Modified By:
36 *   Lou Langholtz <ldl usi.utah.edu>, July 1997
37 *
38 * Apache 2.0 Port:
39 *   Ryan Morgan <rmorgan covalent.net>, August 2000
40 *
41 */
42
43
44#include "apr.h"
45#include "apr_strings.h"
46#include "apr_lib.h"
47#include "apr_version.h"
48#if APR_MAJOR_VERSION < 2
49#include "apu_version.h"
50#endif
51#define APR_WANT_STRFUNC
52#include "apr_want.h"
53
54#include "httpd.h"
55#include "http_config.h"
56#include "http_core.h"
57#include "http_log.h"
58#include "http_main.h"
59#include "http_protocol.h"
60#include "http_connection.h"
61#include "http_request.h"
62#include "util_script.h"
63#include "ap_mpm.h"
64#include "mpm_common.h"
65#include "ap_provider.h"
66#include <stdio.h>
67#include <stdlib.h>
68
69typedef struct
70{
71    const char *name;           /* matching module name */
72    const char *info;           /* additional info */
73} info_entry;
74
75typedef struct
76{
77    apr_array_header_t *more_info;
78} info_svr_conf;
79
80module AP_MODULE_DECLARE_DATA info_module;
81
82/* current file name when doing -DDUMP_CONFIG */
83const char *dump_config_fn_info;
84/* file handle when doing -DDUMP_CONFIG */
85apr_file_t *out = NULL;
86
87static void *create_info_config(apr_pool_t * p, server_rec * s)
88{
89    info_svr_conf *conf =
90        (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf));
91
92    conf->more_info = apr_array_make(p, 20, sizeof(info_entry));
93    return conf;
94}
95
96static void *merge_info_config(apr_pool_t * p, void *basev, void *overridesv)
97{
98    info_svr_conf *new =
99        (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf));
100    info_svr_conf *base = (info_svr_conf *) basev;
101    info_svr_conf *overrides = (info_svr_conf *) overridesv;
102
103    new->more_info =
104        apr_array_append(p, overrides->more_info, base->more_info);
105    return new;
106}
107
108static void put_int_flush_right(request_rec * r, int i, int field)
109{
110    if (field > 1 || i > 9)
111        put_int_flush_right(r, i / 10, field - 1);
112    if (i) {
113        if (r)
114            ap_rputc('0' + i % 10, r);
115        else
116            apr_file_putc((char)('0' + i % 10), out);
117    }
118    else {
119        if (r)
120            ap_rputs("&nbsp;", r);
121        else
122            apr_file_printf(out, " ");
123    }
124}
125
126static void set_fn_info(request_rec *r, const char *name)
127{
128    if (r)
129        ap_set_module_config(r->request_config, &info_module, (void *)name);
130    else
131        dump_config_fn_info = name;
132}
133
134static const char *get_fn_info(request_rec *r)
135{
136    if (r)
137        return ap_get_module_config(r->request_config, &info_module);
138    else
139        return dump_config_fn_info;
140}
141
142
143static void mod_info_indent(request_rec * r, int nest,
144                            const char *thisfn, int linenum)
145{
146    int i;
147    const char *prevfn = get_fn_info(r);
148    if (thisfn == NULL)
149        thisfn = "*UNKNOWN*";
150    if (prevfn == NULL || 0 != strcmp(prevfn, thisfn)) {
151        if (r) {
152            thisfn = ap_escape_html(r->pool, thisfn);
153            ap_rprintf(r, "<dd><tt><strong>In file: %s</strong></tt></dd>\n",
154                   thisfn);
155        }
156        else {
157            apr_file_printf(out, "# In file: %s\n", thisfn);
158        }
159        set_fn_info(r, thisfn);
160    }
161
162    if (r) {
163        ap_rputs("<dd><tt>", r);
164        put_int_flush_right(r, linenum > 0 ? linenum : 0, 4);
165        ap_rputs(":&nbsp;", r);
166    }
167    else if (linenum > 0) {
168        for (i = 1; i <= nest; ++i)
169            apr_file_printf(out, "  ");
170        apr_file_putc('#', out);
171        put_int_flush_right(r, linenum, 4);
172        apr_file_printf(out, ":\n");
173    }
174
175    for (i = 1; i <= nest; ++i) {
176        if (r)
177            ap_rputs("&nbsp;&nbsp;", r);
178        else
179            apr_file_printf(out, "  ");
180    }
181}
182
183static void mod_info_show_cmd(request_rec * r, const ap_directive_t * dir,
184                              int nest)
185{
186    mod_info_indent(r, nest, dir->filename, dir->line_num);
187    if (r)
188        ap_rprintf(r, "%s <i>%s</i></tt></dd>\n",
189                   ap_escape_html(r->pool, dir->directive),
190                   ap_escape_html(r->pool, dir->args));
191    else
192        apr_file_printf(out, "%s %s\n", dir->directive, dir->args);
193}
194
195static void mod_info_show_open(request_rec * r, const ap_directive_t * dir,
196                               int nest)
197{
198    mod_info_indent(r, nest, dir->filename, dir->line_num);
199    if (r)
200        ap_rprintf(r, "%s %s</tt></dd>\n",
201                   ap_escape_html(r->pool, dir->directive),
202                   ap_escape_html(r->pool, dir->args));
203    else
204        apr_file_printf(out, "%s %s\n", dir->directive, dir->args);
205}
206
207static void mod_info_show_close(request_rec * r, const ap_directive_t * dir,
208                                int nest)
209{
210    const char *dirname = dir->directive;
211    mod_info_indent(r, nest, dir->filename, 0);
212    if (*dirname == '<') {
213        if (r)
214            ap_rprintf(r, "&lt;/%s&gt;</tt></dd>",
215                       ap_escape_html(r->pool, dirname + 1));
216        else
217            apr_file_printf(out, "</%s>\n", dirname + 1);
218    }
219    else {
220        if (r)
221            ap_rprintf(r, "/%s</tt></dd>", ap_escape_html(r->pool, dirname));
222        else
223            apr_file_printf(out, "/%s\n", dirname);
224    }
225}
226
227static int mod_info_has_cmd(const command_rec * cmds, ap_directive_t * dir)
228{
229    const command_rec *cmd;
230    if (cmds == NULL)
231        return 1;
232    for (cmd = cmds; cmd->name; ++cmd) {
233        if (strcasecmp(cmd->name, dir->directive) == 0)
234            return 1;
235    }
236    return 0;
237}
238
239static void mod_info_show_parents(request_rec * r, ap_directive_t * node,
240                                  int from, int to)
241{
242    if (from < to)
243        mod_info_show_parents(r, node->parent, from, to - 1);
244    mod_info_show_open(r, node, to);
245}
246
247static int mod_info_module_cmds(request_rec * r, const command_rec * cmds,
248                                ap_directive_t * node, int from, int level)
249{
250    int shown = from;
251    ap_directive_t *dir;
252    if (level == 0)
253        set_fn_info(r, NULL);
254    for (dir = node; dir; dir = dir->next) {
255        if (dir->first_child != NULL) {
256            if (level < mod_info_module_cmds(r, cmds, dir->first_child,
257                                             shown, level + 1)) {
258                shown = level;
259                mod_info_show_close(r, dir, level);
260            }
261        }
262        else if (mod_info_has_cmd(cmds, dir)) {
263            if (shown < level) {
264                mod_info_show_parents(r, dir->parent, shown, level - 1);
265                shown = level;
266            }
267            mod_info_show_cmd(r, dir, level);
268        }
269    }
270    return shown;
271}
272
273typedef struct
274{                               /*XXX: should get something from apr_hooks.h instead */
275    void (*pFunc) (void);       /* just to get the right size */
276    const char *szName;
277    const char *const *aszPredecessors;
278    const char *const *aszSuccessors;
279    int nOrder;
280} hook_struct_t;
281
282/*
283 * hook_get_t is a pointer to a function that takes void as an argument and
284 * returns a pointer to an apr_array_header_t.  The nasty WIN32 ifdef
285 * is required to account for the fact that the ap_hook* calls all use
286 * STDCALL calling convention.
287 */
288typedef apr_array_header_t *(
289#ifdef WIN32
290                                __stdcall
291#endif
292                                * hook_get_t)      (void);
293
294typedef struct
295{
296    const char *name;
297    hook_get_t get;
298} hook_lookup_t;
299
300static hook_lookup_t startup_hooks[] = {
301    {"Pre-Config", ap_hook_get_pre_config},
302    {"Check Configuration", ap_hook_get_check_config},
303    {"Test Configuration", ap_hook_get_test_config},
304    {"Post Configuration", ap_hook_get_post_config},
305    {"Open Logs", ap_hook_get_open_logs},
306    {"Pre-MPM", ap_hook_get_pre_mpm},
307    {"MPM", ap_hook_get_mpm},
308    {"Drop Privileges", ap_hook_get_drop_privileges},
309    {"Retrieve Optional Functions", ap_hook_get_optional_fn_retrieve},
310    {"Child Init", ap_hook_get_child_init},
311    {NULL},
312};
313
314static hook_lookup_t request_hooks[] = {
315    {"Pre-Connection", ap_hook_get_pre_connection},
316    {"Create Connection", ap_hook_get_create_connection},
317    {"Process Connection", ap_hook_get_process_connection},
318    {"Create Request", ap_hook_get_create_request},
319    {"Pre-Read Request", ap_hook_get_pre_read_request},
320    {"Post-Read Request", ap_hook_get_post_read_request},
321    {"Header Parse", ap_hook_get_header_parser},
322    {"HTTP Scheme", ap_hook_get_http_scheme},
323    {"Default Port", ap_hook_get_default_port},
324    {"Quick Handler", ap_hook_get_quick_handler},
325    {"Translate Name", ap_hook_get_translate_name},
326    {"Map to Storage", ap_hook_get_map_to_storage},
327    {"Check Access", ap_hook_get_access_checker_ex},
328    {"Check Access (legacy)", ap_hook_get_access_checker},
329    {"Verify User ID", ap_hook_get_check_user_id},
330    {"Note Authentication Failure", ap_hook_get_note_auth_failure},
331    {"Verify User Access", ap_hook_get_auth_checker},
332    {"Check Type", ap_hook_get_type_checker},
333    {"Fixups", ap_hook_get_fixups},
334    {"Insert Filters", ap_hook_get_insert_filter},
335    {"Content Handlers", ap_hook_get_handler},
336    {"Transaction Logging", ap_hook_get_log_transaction},
337    {"Insert Errors", ap_hook_get_insert_error_filter},
338    {"Generate Log ID", ap_hook_get_generate_log_id},
339    {NULL},
340};
341
342static hook_lookup_t other_hooks[] = {
343    {"Monitor", ap_hook_get_monitor},
344    {"Child Status", ap_hook_get_child_status},
345    {"End Generation", ap_hook_get_end_generation},
346    {"Error Logging", ap_hook_get_error_log},
347    {"Query MPM Attributes", ap_hook_get_mpm_query},
348    {"Query MPM Name", ap_hook_get_mpm_get_name},
349    {"Register Timed Callback", ap_hook_get_mpm_register_timed_callback},
350    {"Extend Expression Parser", ap_hook_get_expr_lookup},
351    {"Set Management Items", ap_hook_get_get_mgmt_items},
352#if AP_ENABLE_EXCEPTION_HOOK
353    {"Handle Fatal Exceptions", ap_hook_get_fatal_exception},
354#endif
355    {NULL},
356};
357
358static int module_find_hook(module * modp, hook_get_t hook_get)
359{
360    int i;
361    apr_array_header_t *hooks = hook_get();
362    hook_struct_t *elts;
363
364    if (!hooks) {
365        return 0;
366    }
367
368    elts = (hook_struct_t *) hooks->elts;
369
370    for (i = 0; i < hooks->nelts; i++) {
371        if (strcmp(elts[i].szName, modp->name) == 0) {
372            return 1;
373        }
374    }
375
376    return 0;
377}
378
379static void module_participate(request_rec * r,
380                               module * modp,
381                               hook_lookup_t * lookup, int *comma)
382{
383    if (module_find_hook(modp, lookup->get)) {
384        if (*comma) {
385            ap_rputs(", ", r);
386        }
387        ap_rvputs(r, "<tt>", lookup->name, "</tt>", NULL);
388        *comma = 1;
389    }
390}
391
392static void module_request_hook_participate(request_rec * r, module * modp)
393{
394    int i, comma = 0;
395
396    ap_rputs("<dt><strong>Request Phase Participation:</strong>\n", r);
397
398    for (i = 0; request_hooks[i].name; i++) {
399        module_participate(r, modp, &request_hooks[i], &comma);
400    }
401
402    if (!comma) {
403        ap_rputs("<tt> <em>none</em></tt>", r);
404    }
405    ap_rputs("</dt>\n", r);
406}
407
408static const char *find_more_info(server_rec * s, const char *module_name)
409{
410    int i;
411    info_svr_conf *conf =
412        (info_svr_conf *) ap_get_module_config(s->module_config,
413                                               &info_module);
414    info_entry *entry = (info_entry *) conf->more_info->elts;
415
416    if (!module_name) {
417        return 0;
418    }
419    for (i = 0; i < conf->more_info->nelts; i++) {
420        if (!strcmp(module_name, entry->name)) {
421            return entry->info;
422        }
423        entry++;
424    }
425    return 0;
426}
427
428static int show_server_settings(request_rec * r)
429{
430    server_rec *serv = r->server;
431    int max_daemons, forked, threaded;
432
433    ap_rputs("<h2><a name=\"server\">Server Settings</a></h2>", r);
434    ap_rprintf(r,
435               "<dl><dt><strong>Server Version:</strong> "
436               "<font size=\"+1\"><tt>%s</tt></font></dt>\n",
437               ap_get_server_description());
438    ap_rprintf(r,
439               "<dt><strong>Server Built:</strong> "
440               "<font size=\"+1\"><tt>%s</tt></font></dt>\n",
441               ap_get_server_built());
442    ap_rprintf(r,
443               "<dt><strong>Server loaded APR Version:</strong> "
444               "<tt>%s</tt></dt>\n", apr_version_string());
445    ap_rprintf(r,
446               "<dt><strong>Compiled with APR Version:</strong> "
447               "<tt>%s</tt></dt>\n", APR_VERSION_STRING);
448#if APR_MAJOR_VERSION < 2
449    ap_rprintf(r,
450               "<dt><strong>Server loaded APU Version:</strong> "
451               "<tt>%s</tt></dt>\n", apu_version_string());
452    ap_rprintf(r,
453               "<dt><strong>Compiled with APU Version:</strong> "
454               "<tt>%s</tt></dt>\n", APU_VERSION_STRING);
455#endif
456    ap_rprintf(r,
457               "<dt><strong>Module Magic Number:</strong> "
458               "<tt>%d:%d</tt></dt>\n", MODULE_MAGIC_NUMBER_MAJOR,
459               MODULE_MAGIC_NUMBER_MINOR);
460    ap_rprintf(r,
461               "<dt><strong>Hostname/port:</strong> "
462               "<tt>%s:%u</tt></dt>\n",
463               ap_escape_html(r->pool, ap_get_server_name(r)),
464               ap_get_server_port(r));
465    ap_rprintf(r,
466               "<dt><strong>Timeouts:</strong> "
467               "<tt>connection: %d &nbsp;&nbsp; "
468               "keep-alive: %d</tt></dt>",
469               (int) (apr_time_sec(serv->timeout)),
470               (int) (apr_time_sec(serv->keep_alive_timeout)));
471    ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
472    ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);
473    ap_mpm_query(AP_MPMQ_IS_FORKED, &forked);
474    ap_rprintf(r, "<dt><strong>MPM Name:</strong> <tt>%s</tt></dt>\n",
475               ap_show_mpm());
476    ap_rprintf(r,
477               "<dt><strong>MPM Information:</strong> "
478               "<tt>Max Daemons: %d Threaded: %s Forked: %s</tt></dt>\n",
479               max_daemons, threaded ? "yes" : "no", forked ? "yes" : "no");
480    ap_rprintf(r,
481               "<dt><strong>Server Architecture:</strong> "
482               "<tt>%ld-bit</tt></dt>\n", 8 * (long) sizeof(void *));
483    ap_rprintf(r,
484               "<dt><strong>Server Root:</strong> "
485               "<tt>%s</tt></dt>\n", ap_server_root);
486    ap_rprintf(r,
487               "<dt><strong>Config File:</strong> "
488               "<tt>%s</tt></dt>\n", ap_conftree->filename);
489
490    ap_rputs("<dt><strong>Server Built With:</strong>\n"
491             "<tt style=\"white-space: pre;\">\n", r);
492
493    /* TODO: Not all of these defines are getting set like they do in main.c.
494     *       Missing some headers?
495     */
496
497#ifdef BIG_SECURITY_HOLE
498    ap_rputs(" -D BIG_SECURITY_HOLE\n", r);
499#endif
500
501#ifdef SECURITY_HOLE_PASS_AUTHORIZATION
502    ap_rputs(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n", r);
503#endif
504
505#ifdef OS
506    ap_rputs(" -D OS=\"" OS "\"\n", r);
507#endif
508
509#ifdef HAVE_SHMGET
510    ap_rputs(" -D HAVE_SHMGET\n", r);
511#endif
512
513#if APR_FILE_BASED_SHM
514    ap_rputs(" -D APR_FILE_BASED_SHM\n", r);
515#endif
516
517#if APR_HAS_SENDFILE
518    ap_rputs(" -D APR_HAS_SENDFILE\n", r);
519#endif
520
521#if APR_HAS_MMAP
522    ap_rputs(" -D APR_HAS_MMAP\n", r);
523#endif
524
525#ifdef NO_WRITEV
526    ap_rputs(" -D NO_WRITEV\n", r);
527#endif
528
529#ifdef NO_LINGCLOSE
530    ap_rputs(" -D NO_LINGCLOSE\n", r);
531#endif
532
533#if APR_HAVE_IPV6
534    ap_rputs(" -D APR_HAVE_IPV6 (IPv4-mapped addresses ", r);
535#ifdef AP_ENABLE_V4_MAPPED
536    ap_rputs("enabled)\n", r);
537#else
538    ap_rputs("disabled)\n", r);
539#endif
540#endif
541
542#if APR_USE_FLOCK_SERIALIZE
543    ap_rputs(" -D APR_USE_FLOCK_SERIALIZE\n", r);
544#endif
545
546#if APR_USE_SYSVSEM_SERIALIZE
547    ap_rputs(" -D APR_USE_SYSVSEM_SERIALIZE\n", r);
548#endif
549
550#if APR_USE_POSIXSEM_SERIALIZE
551    ap_rputs(" -D APR_USE_POSIXSEM_SERIALIZE\n", r);
552#endif
553
554#if APR_USE_FCNTL_SERIALIZE
555    ap_rputs(" -D APR_USE_FCNTL_SERIALIZE\n", r);
556#endif
557
558#if APR_USE_PROC_PTHREAD_SERIALIZE
559    ap_rputs(" -D APR_USE_PROC_PTHREAD_SERIALIZE\n", r);
560#endif
561#if APR_PROCESS_LOCK_IS_GLOBAL
562    ap_rputs(" -D APR_PROCESS_LOCK_IS_GLOBAL\n", r);
563#endif
564
565#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
566    ap_rputs(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n", r);
567#endif
568
569#if APR_HAS_OTHER_CHILD
570    ap_rputs(" -D APR_HAS_OTHER_CHILD\n", r);
571#endif
572
573#ifdef AP_HAVE_RELIABLE_PIPED_LOGS
574    ap_rputs(" -D AP_HAVE_RELIABLE_PIPED_LOGS\n", r);
575#endif
576
577#ifdef BUFFERED_LOGS
578    ap_rputs(" -D BUFFERED_LOGS\n", r);
579#ifdef PIPE_BUF
580    ap_rputs(" -D PIPE_BUF=%ld\n", (long) PIPE_BUF, r);
581#endif
582#endif
583
584#if APR_CHARSET_EBCDIC
585    ap_rputs(" -D APR_CHARSET_EBCDIC\n", r);
586#endif
587
588#ifdef NEED_HASHBANG_EMUL
589    ap_rputs(" -D NEED_HASHBANG_EMUL\n", r);
590#endif
591
592/* This list displays the compiled in default paths: */
593#ifdef HTTPD_ROOT
594    ap_rputs(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n", r);
595#endif
596
597#ifdef SUEXEC_BIN
598    ap_rputs(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n", r);
599#endif
600
601#ifdef DEFAULT_PIDLOG
602    ap_rputs(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n", r);
603#endif
604
605#ifdef DEFAULT_SCOREBOARD
606    ap_rputs(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n", r);
607#endif
608
609#ifdef DEFAULT_ERRORLOG
610    ap_rputs(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n", r);
611#endif
612
613
614#ifdef AP_TYPES_CONFIG_FILE
615    ap_rputs(" -D AP_TYPES_CONFIG_FILE=\"" AP_TYPES_CONFIG_FILE "\"\n", r);
616#endif
617
618#ifdef SERVER_CONFIG_FILE
619    ap_rputs(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n", r);
620#endif
621    ap_rputs("</tt></dt>\n", r);
622    ap_rputs("</dl><hr />", r);
623    return 0;
624}
625
626static int dump_a_hook(request_rec * r, hook_get_t hook_get)
627{
628    int i;
629    char qs;
630    hook_struct_t *elts;
631    apr_array_header_t *hooks = hook_get();
632
633    if (!hooks) {
634        return 0;
635    }
636
637    if (r->args && strcasecmp(r->args, "hooks") == 0) {
638        qs = '?';
639    }
640    else {
641        qs = '#';
642    }
643
644    elts = (hook_struct_t *) hooks->elts;
645
646    for (i = 0; i < hooks->nelts; i++) {
647        ap_rprintf(r,
648                   "&nbsp;&nbsp; %02d <a href=\"%c%s\">%s</a> <br/>",
649                   elts[i].nOrder, qs, elts[i].szName, elts[i].szName);
650    }
651    return 0;
652}
653
654static int show_active_hooks(request_rec * r)
655{
656    int i;
657    ap_rputs("<h2><a name=\"startup_hooks\">Startup Hooks</a></h2>\n<dl>", r);
658
659    for (i = 0; startup_hooks[i].name; i++) {
660        ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n",
661                   startup_hooks[i].name);
662        dump_a_hook(r, startup_hooks[i].get);
663        ap_rputs("\n  </tt>\n</dt>\n", r);
664    }
665
666    ap_rputs
667        ("</dl>\n<hr />\n<h2><a name=\"request_hooks\">Request Hooks</a></h2>\n<dl>",
668         r);
669
670    for (i = 0; request_hooks[i].name; i++) {
671        ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n",
672                   request_hooks[i].name);
673        dump_a_hook(r, request_hooks[i].get);
674        ap_rputs("\n  </tt>\n</dt>\n", r);
675    }
676
677    ap_rputs
678        ("</dl>\n<hr />\n<h2><a name=\"other_hooks\">Other Hooks</a></h2>\n<dl>",
679         r);
680
681    for (i = 0; other_hooks[i].name; i++) {
682        ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n",
683                   other_hooks[i].name);
684        dump_a_hook(r, other_hooks[i].get);
685        ap_rputs("\n  </tt>\n</dt>\n", r);
686    }
687
688    ap_rputs("</dl>\n<hr />\n", r);
689
690    return 0;
691}
692
693static int cmp_provider_groups(const void *a_, const void *b_)
694{
695    const ap_list_provider_groups_t *a = a_, *b = b_;
696    int ret = strcmp(a->provider_group, b->provider_group);
697    if (!ret)
698        ret = strcmp(a->provider_version, b->provider_version);
699    return ret;
700}
701
702static int cmp_provider_names(const void *a_, const void *b_)
703{
704    const ap_list_provider_names_t *a = a_, *b = b_;
705    return strcmp(a->provider_name, b->provider_name);
706}
707
708static void show_providers(request_rec *r)
709{
710    apr_array_header_t *groups = ap_list_provider_groups(r->pool);
711    ap_list_provider_groups_t *group;
712    apr_array_header_t *names;
713    ap_list_provider_names_t *name;
714    int i,j;
715    const char *cur_group = NULL;
716
717    qsort(groups->elts, groups->nelts, sizeof(ap_list_provider_groups_t),
718          cmp_provider_groups);
719    ap_rputs("<h2><a name=\"providers\">Providers</a></h2>\n<dl>", r);
720
721    for (i = 0; i < groups->nelts; i++) {
722        group = &APR_ARRAY_IDX(groups, i, ap_list_provider_groups_t);
723        if (!cur_group || strcmp(cur_group, group->provider_group) != 0) {
724            if (cur_group)
725                ap_rputs("\n</dt>\n", r);
726            cur_group = group->provider_group;
727            ap_rprintf(r, "<dt><strong>%s</strong> (version <tt>%s</tt>):"
728                          "\n <br />\n", cur_group, group->provider_version);
729        }
730        names = ap_list_provider_names(r->pool, group->provider_group,
731                                       group->provider_version);
732        qsort(names->elts, names->nelts, sizeof(ap_list_provider_names_t),
733              cmp_provider_names);
734        for (j = 0; j < names->nelts; j++) {
735            name = &APR_ARRAY_IDX(names, j, ap_list_provider_names_t);
736            ap_rprintf(r, "<tt>&nbsp;&nbsp;%s</tt><br/>", name->provider_name);
737        }
738    }
739    if (cur_group)
740        ap_rputs("\n</dt>\n", r);
741    ap_rputs("</dl>\n<hr />\n", r);
742}
743
744static int cmp_module_name(const void *a_, const void *b_)
745{
746    const module * const *a = a_;
747    const module * const *b = b_;
748    return strcmp((*a)->name, (*b)->name);
749}
750
751static apr_array_header_t *get_sorted_modules(apr_pool_t *p)
752{
753    apr_array_header_t *arr = apr_array_make(p, 64, sizeof(module *));
754    module *modp, **entry;
755    for (modp = ap_top_module; modp; modp = modp->next) {
756        entry = &APR_ARRAY_PUSH(arr, module *);
757        *entry = modp;
758    }
759    qsort(arr->elts, arr->nelts, sizeof(module *), cmp_module_name);
760    return arr;
761}
762
763static int display_info(request_rec * r)
764{
765    module *modp = NULL;
766    const char *more_info;
767    const command_rec *cmd;
768    apr_array_header_t *modules = NULL;
769    int i;
770
771    if (strcmp(r->handler, "server-info")) {
772        return DECLINED;
773    }
774
775    r->allowed |= (AP_METHOD_BIT << M_GET);
776    if (r->method_number != M_GET) {
777        return DECLINED;
778    }
779
780    ap_set_content_type(r, "text/html; charset=ISO-8859-1");
781
782    ap_rputs(DOCTYPE_XHTML_1_0T
783             "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
784             "<head>\n"
785             "  <title>Server Information</title>\n" "</head>\n", r);
786    ap_rputs("<body><h1 style=\"text-align: center\">"
787             "Apache Server Information</h1>\n", r);
788    if (!r->args || strcasecmp(r->args, "list")) {
789        if (!r->args) {
790            ap_rputs("<dl><dt><tt>Subpages:<br />", r);
791            ap_rputs("<a href=\"?config\">Configuration Files</a>, "
792                     "<a href=\"?server\">Server Settings</a>, "
793                     "<a href=\"?list\">Module List</a>, "
794                     "<a href=\"?hooks\">Active Hooks</a>, "
795                     "<a href=\"?providers\">Available Providers</a>", r);
796            ap_rputs("</tt></dt></dl><hr />", r);
797
798            ap_rputs("<dl><dt><tt>Sections:<br />", r);
799            ap_rputs("<a href=\"#modules\">Loaded Modules</a>, "
800                     "<a href=\"#server\">Server Settings</a>, "
801                     "<a href=\"#startup_hooks\">Startup Hooks</a>, "
802                     "<a href=\"#request_hooks\">Request Hooks</a>, "
803                     "<a href=\"#other_hooks\">Other Hooks</a>, "
804                     "<a href=\"#providers\">Providers</a>", r);
805            ap_rputs("</tt></dt></dl><hr />", r);
806
807            ap_rputs("<h2><a name=\"modules\">Loaded Modules</a></h2>"
808                    "<dl><dt><tt>", r);
809
810            modules = get_sorted_modules(r->pool);
811            for (i = 0; i < modules->nelts; i++) {
812                modp = APR_ARRAY_IDX(modules, i, module *);
813                ap_rprintf(r, "<a href=\"#%s\">%s</a>", modp->name,
814                           modp->name);
815                if (i < modules->nelts) {
816                    ap_rputs(", ", r);
817                }
818            }
819            ap_rputs("</tt></dt></dl><hr />", r);
820        }
821
822        if (!r->args || !strcasecmp(r->args, "server")) {
823            show_server_settings(r);
824        }
825
826        if (!r->args || !strcasecmp(r->args, "hooks")) {
827            show_active_hooks(r);
828        }
829
830        if (!r->args || !strcasecmp(r->args, "providers")) {
831            show_providers(r);
832        }
833
834        if (r->args && 0 == strcasecmp(r->args, "config")) {
835            ap_rputs("<dl><dt><strong>Configuration:</strong>\n", r);
836            mod_info_module_cmds(r, NULL, ap_conftree, 0, 0);
837            ap_rputs("</dl><hr />", r);
838        }
839        else {
840            int comma = 0;
841            if (!modules)
842                 modules = get_sorted_modules(r->pool);
843            for (i = 0; i < modules->nelts; i++) {
844                modp = APR_ARRAY_IDX(modules, i, module *);
845                if (!r->args || !strcasecmp(modp->name, r->args)) {
846                    ap_rprintf(r,
847                               "<dl><dt><a name=\"%s\"><strong>Module Name:</strong></a> "
848                               "<font size=\"+1\"><tt><a href=\"?%s\">%s</a></tt></font></dt>\n",
849                               modp->name, modp->name, modp->name);
850                    ap_rputs("<dt><strong>Content handlers:</strong> ", r);
851
852                    if (module_find_hook(modp, ap_hook_get_handler)) {
853                        ap_rputs("<tt> <em>yes</em></tt>", r);
854                    }
855                    else {
856                        ap_rputs("<tt> <em>none</em></tt>", r);
857                    }
858
859                    ap_rputs("</dt>", r);
860                    ap_rputs
861                        ("<dt><strong>Configuration Phase Participation:</strong>\n",
862                         r);
863                    if (modp->create_dir_config) {
864                        if (comma) {
865                            ap_rputs(", ", r);
866                        }
867                        ap_rputs("<tt>Create Directory Config</tt>", r);
868                        comma = 1;
869                    }
870                    if (modp->merge_dir_config) {
871                        if (comma) {
872                            ap_rputs(", ", r);
873                        }
874                        ap_rputs("<tt>Merge Directory Configs</tt>", r);
875                        comma = 1;
876                    }
877                    if (modp->create_server_config) {
878                        if (comma) {
879                            ap_rputs(", ", r);
880                        }
881                        ap_rputs("<tt>Create Server Config</tt>", r);
882                        comma = 1;
883                    }
884                    if (modp->merge_server_config) {
885                        if (comma) {
886                            ap_rputs(", ", r);
887                        }
888                        ap_rputs("<tt>Merge Server Configs</tt>", r);
889                        comma = 1;
890                    }
891                    if (!comma)
892                        ap_rputs("<tt> <em>none</em></tt>", r);
893                    comma = 0;
894                    ap_rputs("</dt>", r);
895
896                    module_request_hook_participate(r, modp);
897
898                    cmd = modp->cmds;
899                    if (cmd) {
900                        ap_rputs
901                            ("<dt><strong>Module Directives:</strong></dt>",
902                             r);
903                        while (cmd) {
904                            if (cmd->name) {
905                                ap_rprintf(r, "<dd><tt>%s%s - <i>",
906                                           ap_escape_html(r->pool, cmd->name),
907                                           cmd->name[0] == '<' ? "&gt;" : "");
908                                if (cmd->errmsg) {
909                                    ap_rputs(ap_escape_html(r->pool, cmd->errmsg), r);
910                                }
911                                ap_rputs("</i></tt></dd>\n", r);
912                            }
913                            else {
914                                break;
915                            }
916                            cmd++;
917                        }
918                        ap_rputs
919                            ("<dt><strong>Current Configuration:</strong></dt>\n",
920                             r);
921                        mod_info_module_cmds(r, modp->cmds, ap_conftree, 0,
922                                             0);
923                    }
924                    else {
925                        ap_rputs
926                            ("<dt><strong>Module Directives:</strong> <tt>none</tt></dt>",
927                             r);
928                    }
929                    more_info = find_more_info(r->server, modp->name);
930                    if (more_info) {
931                        ap_rputs
932                            ("<dt><strong>Additional Information:</strong>\n</dt><dd>",
933                             r);
934                        ap_rputs(more_info, r);
935                        ap_rputs("</dd>", r);
936                    }
937                    ap_rputs("</dl><hr />\n", r);
938                    if (r->args) {
939                        break;
940                    }
941                }
942            }
943            if (!modp && r->args && strcasecmp(r->args, "server")) {
944                ap_rputs("<p><b>No such module</b></p>\n", r);
945            }
946        }
947    }
948    else {
949        ap_rputs("<dl><dt>Server Module List</dt>", r);
950        modules = get_sorted_modules(r->pool);
951        for (i = 0; i < modules->nelts; i++) {
952            modp = APR_ARRAY_IDX(modules, i, module *);
953            ap_rputs("<dd>", r);
954            ap_rputs(modp->name, r);
955            ap_rputs("</dd>", r);
956        }
957        ap_rputs("</dl><hr />", r);
958    }
959    ap_rputs(ap_psignature("", r), r);
960    ap_rputs("</body></html>\n", r);
961    /* Done, turn off timeout, close file and return */
962    return 0;
963}
964
965static const char *add_module_info(cmd_parms * cmd, void *dummy,
966                                   const char *name, const char *info)
967{
968    server_rec *s = cmd->server;
969    info_svr_conf *conf =
970        (info_svr_conf *) ap_get_module_config(s->module_config,
971                                               &info_module);
972    info_entry *new = apr_array_push(conf->more_info);
973
974    new->name = name;
975    new->info = info;
976    return NULL;
977}
978
979static const command_rec info_cmds[] = {
980    AP_INIT_TAKE2("AddModuleInfo", add_module_info, NULL, RSRC_CONF,
981                  "a module name and additional information on that module"),
982    {NULL}
983};
984
985static int check_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp,
986                        server_rec *s)
987{
988    if (ap_exists_config_define("DUMP_CONFIG")) {
989        apr_file_open_stdout(&out, ptemp);
990        mod_info_module_cmds(NULL, NULL, ap_conftree, 0, 0);
991    }
992
993    return DECLINED;
994}
995
996
997static void register_hooks(apr_pool_t * p)
998{
999    ap_hook_handler(display_info, NULL, NULL, APR_HOOK_MIDDLE);
1000    ap_hook_check_config(check_config, NULL, NULL, APR_HOOK_FIRST);
1001}
1002
1003AP_DECLARE_MODULE(info) = {
1004    STANDARD20_MODULE_STUFF,
1005    NULL,                       /* dir config creater */
1006    NULL,                       /* dir merger --- default is to override */
1007    create_info_config,         /* server config */
1008    merge_info_config,          /* merge server config */
1009    info_cmds,                  /* command apr_table_t */
1010    register_hooks
1011};
1012