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 * This module is used to load Apache modules at runtime. This means that the 19 * server functionality can be extended without recompiling and even without 20 * taking the server down at all. Only a HUP or AP_SIG_GRACEFUL signal 21 * needs to be sent to the server to reload the dynamically loaded modules. 22 * 23 * To use, you'll first need to build your module as a shared library, then 24 * update your configuration (httpd.conf) to get the Apache core to load the 25 * module at start-up. 26 * 27 * The easiest way to build a module as a shared library is to use the 28 * `SharedModule' command in the Configuration file, instead of `AddModule'. 29 * You should also change the file extension from `.o' to `.so'. So, for 30 * example, to build the status module as a shared library edit Configuration 31 * and change 32 * AddModule modules/standard/mod_status.o 33 * to 34 * SharedModule modules/standard/mod_status.so 35 * 36 * Run Configure and make. Now Apache's httpd binary will _not_ include 37 * mod_status. Instead a shared object called mod_status.so will be build, in 38 * the modules/standard directory. You can build most of the modules as shared 39 * libraries like this. 40 * 41 * To use the shared module, move the .so file(s) into an appropriate 42 * directory. You might like to create a directory called "modules" under you 43 * server root for this (e.g. /usr/local/httpd/modules). 44 * 45 * Then edit your conf/httpd.conf file, and add LoadModule lines. For 46 * example 47 * LoadModule status_module modules/mod_status.so 48 * 49 * The first argument is the module's structure name (look at the end of the 50 * module source to find this). The second option is the path to the module 51 * file, relative to the server root. Put these directives right at the top 52 * of your httpd.conf file. 53 * 54 * Now you can start Apache. A message will be logged at "debug" level to your 55 * error_log to confirm that the module(s) are loaded (use "LogLevel debug" 56 * directive to get these log messages). 57 * 58 * If you edit the LoadModule directives while the server is live you can get 59 * Apache to re-load the modules by sending it a HUP or AP_SIG_GRACEFUL 60 * signal as normal. You can use this to dynamically change the capability 61 * of your server without bringing it down. 62 * 63 * Because currently there is only limited builtin support in the Configure 64 * script for creating the shared library files (`.so'), please consult your 65 * vendors cc(1), ld(1) and dlopen(3) manpages to find out the appropriate 66 * compiler and linker flags and insert them manually into the Configuration 67 * file under CFLAGS_SHLIB, LDFLAGS_SHLIB and LDFLAGS_SHLIB_EXPORT. 68 * 69 * If you still have problems figuring out the flags both try the paper 70 * http://developer.netscape.com/library/documentation/enterprise 71 * /unix/svrplug.htm#1013807 72 * or install a Perl 5 interpreter on your platform and then run the command 73 * 74 * $ perl -V:usedl -V:ccdlflags -V:cccdlflags -V:lddlflags 75 * 76 * This gives you what type of dynamic loading Perl 5 uses on your platform 77 * and which compiler and linker flags Perl 5 uses to create the shared object 78 * files. 79 * 80 * Another location where you can find useful hints is the `ltconfig' script 81 * of the GNU libtool 1.2 package. Search for your platform name inside the 82 * various "case" constructs. 83 * 84 */ 85 86#include "apr.h" 87#include "apr_dso.h" 88#include "apr_strings.h" 89#include "apr_errno.h" 90 91#include "ap_config.h" 92#include "httpd.h" 93#include "http_config.h" 94#include "http_log.h" 95#include "http_core.h" 96 97#include "mod_so.h" 98 99module AP_MODULE_DECLARE_DATA so_module; 100 101 102/* 103 * Server configuration to keep track of actually 104 * loaded modules and the corresponding module name. 105 */ 106 107typedef struct so_server_conf { 108 apr_array_header_t *loaded_modules; 109} so_server_conf; 110 111static void *so_sconf_create(apr_pool_t *p, server_rec *s) 112{ 113 so_server_conf *soc; 114 115 soc = (so_server_conf *)apr_pcalloc(p, sizeof(so_server_conf)); 116 soc->loaded_modules = apr_array_make(p, DYNAMIC_MODULE_LIMIT, 117 sizeof(ap_module_symbol_t)); 118 119 return (void *)soc; 120} 121 122#ifndef NO_DLOPEN 123 124/* 125 * This is the cleanup for a loaded shared object. It unloads the module. 126 * This is called as a cleanup function from the core. 127 */ 128 129static apr_status_t unload_module(void *data) 130{ 131 ap_module_symbol_t *modi = (ap_module_symbol_t*)data; 132 133 /* only unload if module information is still existing */ 134 if (modi->modp == NULL) 135 return APR_SUCCESS; 136 137 /* remove the module pointer from the core structure */ 138 ap_remove_loaded_module(modi->modp); 139 140 /* destroy the module information */ 141 modi->modp = NULL; 142 modi->name = NULL; 143 return APR_SUCCESS; 144} 145 146static const char *dso_load(cmd_parms *cmd, apr_dso_handle_t **modhandlep, 147 const char *filename, const char **used_filename) 148{ 149 int retry = 0; 150 const char *fullname = ap_server_root_relative(cmd->temp_pool, filename); 151 char my_error[256]; 152 if (filename != NULL && ap_strchr_c(filename, '/') == NULL) { 153 /* retry on error without path to use dlopen()'s search path */ 154 retry = 1; 155 } 156 157 if (fullname == NULL && !retry) { 158 return apr_psprintf(cmd->temp_pool, "Invalid %s path %s", 159 cmd->cmd->name, filename); 160 } 161 *used_filename = fullname; 162 if (apr_dso_load(modhandlep, fullname, cmd->pool) == APR_SUCCESS) { 163 return NULL; 164 } 165 if (retry) { 166 *used_filename = filename; 167 if (apr_dso_load(modhandlep, filename, cmd->pool) == APR_SUCCESS) 168 return NULL; 169 } 170 171 return apr_pstrcat(cmd->temp_pool, "Cannot load ", filename, 172 " into server: ", 173 apr_dso_error(*modhandlep, my_error, sizeof(my_error)), 174 NULL); 175} 176 177/* 178 * This is called for the directive LoadModule and actually loads 179 * a shared object file into the address space of the server process. 180 */ 181 182static const char *load_module(cmd_parms *cmd, void *dummy, 183 const char *modname, const char *filename) 184{ 185 apr_dso_handle_t *modhandle; 186 apr_dso_handle_sym_t modsym; 187 module *modp; 188 const char *module_file; 189 so_server_conf *sconf; 190 ap_module_symbol_t *modi; 191 ap_module_symbol_t *modie; 192 int i; 193 const char *error; 194 195 /* we need to setup this value for dummy to make sure that we don't try 196 * to add a non-existant tree into the build when we return to 197 * execute_now. 198 */ 199 *(ap_directive_t **)dummy = NULL; 200 201 /* 202 * check for already existing module 203 * If it already exists, we have nothing to do 204 * Check both dynamically-loaded modules and statically-linked modules. 205 */ 206 sconf = (so_server_conf *)ap_get_module_config(cmd->server->module_config, 207 &so_module); 208 modie = (ap_module_symbol_t *)sconf->loaded_modules->elts; 209 for (i = 0; i < sconf->loaded_modules->nelts; i++) { 210 modi = &modie[i]; 211 if (modi->name != NULL && strcmp(modi->name, modname) == 0) { 212 ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, 213 cmd->pool, APLOGNO(01574) "module %s is already loaded, skipping", 214 modname); 215 return NULL; 216 } 217 } 218 219 for (i = 0; ap_preloaded_modules[i]; i++) { 220 const char *preload_name; 221 apr_size_t preload_len; 222 apr_size_t thismod_len; 223 224 modp = ap_preloaded_modules[i]; 225 226 /* make sure we're comparing apples with apples 227 * make sure name of preloaded module is mod_FOO.c 228 * make sure name of structure being loaded is FOO_module 229 */ 230 231 if (memcmp(modp->name, "mod_", 4)) { 232 continue; 233 } 234 235 preload_name = modp->name + strlen("mod_"); 236 preload_len = strlen(preload_name) - 2; 237 238 if (strlen(modname) <= strlen("_module")) { 239 continue; 240 } 241 thismod_len = strlen(modname) - strlen("_module"); 242 if (strcmp(modname + thismod_len, "_module")) { 243 continue; 244 } 245 246 if (thismod_len != preload_len) { 247 continue; 248 } 249 250 if (!memcmp(modname, preload_name, preload_len)) { 251 return apr_pstrcat(cmd->pool, "module ", modname, 252 " is built-in and can't be loaded", 253 NULL); 254 } 255 } 256 257 modi = apr_array_push(sconf->loaded_modules); 258 modi->name = modname; 259 260 /* 261 * Load the file into the Apache address space 262 */ 263 error = dso_load(cmd, &modhandle, filename, &module_file); 264 if (error) 265 return error; 266 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, cmd->pool, APLOGNO(01575) 267 "loaded module %s from %s", modname, module_file); 268 269 /* 270 * Retrieve the pointer to the module structure through the module name: 271 * First with the hidden variant (prefix `AP_') and then with the plain 272 * symbol name. 273 */ 274 if (apr_dso_sym(&modsym, modhandle, modname) != APR_SUCCESS) { 275 char my_error[256]; 276 277 return apr_pstrcat(cmd->pool, "Can't locate API module structure `", 278 modname, "' in file ", module_file, ": ", 279 apr_dso_error(modhandle, my_error, sizeof(my_error)), 280 NULL); 281 } 282 modp = (module*) modsym; 283 modp->dynamic_load_handle = (apr_dso_handle_t *)modhandle; 284 modi->modp = modp; 285 286 /* 287 * Make sure the found module structure is really a module structure 288 * 289 */ 290 if (modp->magic != MODULE_MAGIC_COOKIE) { 291 return apr_psprintf(cmd->pool, "API module structure '%s' in file %s " 292 "is garbled - expected signature %08lx but saw " 293 "%08lx - perhaps this is not an Apache module DSO, " 294 "or was compiled for a different Apache version?", 295 modname, module_file, 296 MODULE_MAGIC_COOKIE, modp->magic); 297 } 298 299 /* 300 * Add this module to the Apache core structures 301 */ 302 error = ap_add_loaded_module(modp, cmd->pool, modname); 303 if (error) { 304 return error; 305 } 306 307 /* 308 * Register a cleanup in the config apr_pool_t (normally pconf). When 309 * we do a restart (or shutdown) this cleanup will cause the 310 * shared object to be unloaded. 311 */ 312 apr_pool_cleanup_register(cmd->pool, modi, unload_module, apr_pool_cleanup_null); 313 314 /* 315 * Finally we need to run the configuration process for the module 316 */ 317 ap_single_module_configure(cmd->pool, cmd->server, modp); 318 319 return NULL; 320} 321 322/* 323 * This implements the LoadFile directive and loads an arbitrary 324 * shared object file into the adress space of the server process. 325 */ 326 327static const char *load_file(cmd_parms *cmd, void *dummy, const char *filename) 328{ 329 apr_dso_handle_t *handle; 330 const char *used_file, *error; 331 332 error = dso_load(cmd, &handle, filename, &used_file); 333 if (error) 334 return error; 335 336 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(01576) 337 "loaded file %s", used_file); 338 339 return NULL; 340} 341 342static module *ap_find_loaded_module_symbol(server_rec *s, const char *modname) 343{ 344 so_server_conf *sconf; 345 ap_module_symbol_t *modi; 346 ap_module_symbol_t *modie; 347 int i; 348 349 sconf = (so_server_conf *)ap_get_module_config(s->module_config, 350 &so_module); 351 modie = (ap_module_symbol_t *)sconf->loaded_modules->elts; 352 353 for (i = 0; i < sconf->loaded_modules->nelts; i++) { 354 modi = &modie[i]; 355 if (modi->name != NULL && strcmp(modi->name, modname) == 0) { 356 return modi->modp; 357 } 358 } 359 return NULL; 360} 361 362static void dump_loaded_modules(apr_pool_t *p, server_rec *s) 363{ 364 ap_module_symbol_t *modie; 365 ap_module_symbol_t *modi; 366 so_server_conf *sconf; 367 int i; 368 apr_file_t *out = NULL; 369 370 if (!ap_exists_config_define("DUMP_MODULES")) { 371 return; 372 } 373 374 apr_file_open_stdout(&out, p); 375 376 apr_file_printf(out, "Loaded Modules:\n"); 377 378 sconf = (so_server_conf *)ap_get_module_config(s->module_config, 379 &so_module); 380 for (i = 0; ; i++) { 381 modi = &ap_prelinked_module_symbols[i]; 382 if (modi->name != NULL) { 383 apr_file_printf(out, " %s (static)\n", modi->name); 384 } 385 else { 386 break; 387 } 388 } 389 390 modie = (ap_module_symbol_t *)sconf->loaded_modules->elts; 391 for (i = 0; i < sconf->loaded_modules->nelts; i++) { 392 modi = &modie[i]; 393 if (modi->name != NULL) { 394 apr_file_printf(out, " %s (shared)\n", modi->name); 395 } 396 } 397} 398 399#else /* not NO_DLOPEN */ 400 401static const char *load_file(cmd_parms *cmd, void *dummy, const char *filename) 402{ 403 ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, cmd->pool, APLOGNO(01577) 404 "WARNING: LoadFile not supported on this platform"); 405 return NULL; 406} 407 408static const char *load_module(cmd_parms *cmd, void *dummy, 409 const char *modname, const char *filename) 410{ 411 ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, cmd->pool, APLOGNO(01578) 412 "WARNING: LoadModule not supported on this platform"); 413 return NULL; 414} 415 416#endif /* NO_DLOPEN */ 417 418static void register_hooks(apr_pool_t *p) 419{ 420#ifndef NO_DLOPEN 421 APR_REGISTER_OPTIONAL_FN(ap_find_loaded_module_symbol); 422 ap_hook_test_config(dump_loaded_modules, NULL, NULL, APR_HOOK_MIDDLE); 423#endif 424} 425 426static const command_rec so_cmds[] = { 427 AP_INIT_TAKE2("LoadModule", load_module, NULL, RSRC_CONF | EXEC_ON_READ, 428 "a module name and the name of a shared object file to load it from"), 429 AP_INIT_ITERATE("LoadFile", load_file, NULL, RSRC_CONF | EXEC_ON_READ, 430 "shared object file or library to load into the server at runtime"), 431 { NULL } 432}; 433 434AP_DECLARE_MODULE(so) = { 435 STANDARD20_MODULE_STUFF, 436 NULL, /* create per-dir config */ 437 NULL, /* merge per-dir config */ 438 so_sconf_create, /* server config */ 439 NULL, /* merge server config */ 440 so_cmds, /* command apr_table_t */ 441 register_hooks /* register hooks */ 442}; 443