You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Paul Sutton <pa...@c2.net> on 1998/02/04 14:42:42 UTC

[PATCH] unload/reload dynamic modules (was Re: mod_so committed)

On Tue, 3 Feb 1998, Alexei Kosut wrote:
> On Tue, 3 Feb 1998, Dean Gaudet wrote:
> > >   - support for dropping modules & reloading during a restart
> > What happens now?
> 
> Both mod_dll, mod_so and mod_dld (on which the other two are based) only
> recognize LoadModule directives on startup. They get ignored on restart.
> It probably wouldn't be too hard to check each LoadModule to see if it was
> "new", and add it, and then see if any were "missing", and drop them.

Ok, here is a patch to unload dynamically loaded modules during a restart. 
It basically adds the module's handle as a clean-up for the cmd->pool
pool. The cleanup unloads the file, and drops the module's structure from
the linked-list of modules (remove_module() in http_config.h). The new
item dynamic_load_handle is added to the module structure, and set to NULL
by STANDARD_MODULE_STUFF (but this means that _all_ modules will need to
at least be recompiled). 

This patch is for Unix only, I also have a patch for Win32 which adds the
unload stuff for DLLs (it is basically identical, in fact I developed this
on NT then ported it back to mod_so). It only currently works for
LoadModule modules, not LoadFile (is there any real use for LoadFile??).
It isn't difficult to add LoadFile reloading though. 

With this patch you can now change your LoadModule lines, do a HUP or
USR1, and get an Apache with your new set of modules. So you can extend
your server without ever bringing it down! I think that's pretty neat.

//pcs

Index: include/http_config.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/include/http_config.h,v
retrieving revision 1.63
diff -u -r1.63 http_config.h
--- http_config.h	1998/02/01 22:05:33	1.63
+++ http_config.h	1998/02/04 13:28:47
@@ -176,6 +176,8 @@
 
     const char *name;
 
+    void *dynamic_load_handle;
+
     struct module_struct *next;
 
     /* init() occurs after config parsing, but before any children are
@@ -254,7 +256,7 @@
  */
 
 #define MODULE_MAGIC_NUMBER 19980201
-#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL
+#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL, NULL
 
 /* Generic accessors for other modules to get at their own module-specific
  * data
Index: main/http_config.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_config.c,v
retrieving revision 1.95
diff -u -r1.95 http_config.c
--- http_config.c	1998/02/02 22:33:31	1.95
+++ http_config.c	1998/02/04 13:28:52
@@ -530,6 +530,48 @@
     build_method_shortcuts();
 }
 
+/* 
+ * remove_module undoes what add_module did. There are some caveats:
+ * when the module is removed, its slot is lost so all the current
+ * per-dir and per-server configurations are invalid. So we should
+ * only ever call this function when you are invalidating almost
+ * all our current data. I.e. when doing a restart.
+ */
+
+API_EXPORT(void) remove_module(module *m)
+{
+    module *modp;
+
+    modp = top_module;
+    if (modp == m) {
+	/* We are the top module, special case */
+	top_module = modp->next;
+    }
+    else {
+	/* Not the top module, find use. When found modp will
+	 * point to the module _before_ us in the list
+	 */
+
+	while (modp && modp->next != m) {
+	    modp = modp->next;
+	}
+	if (!modp) {
+	    /* Uh-oh, this module doesn't exist */
+	    aplog_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
+		"Cannot remove module %s: not found in module list",
+		m->name);
+	    return;
+	}
+	/* Eliminate us from the module list */
+	modp->next = modp->next->next;
+    }
+
+    m->module_index = -1;	/* simulate being unloaded, should
+				 * be unnecessary */
+    dynamic_modules--;
+}
+
+
 void setup_prelinked_modules()
 {
     module **m;
Index: modules/standard/mod_so.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_so.c,v
retrieving revision 1.2
diff -u -r1.2 mod_so.c
--- mod_so.c	1998/02/04 10:22:18	1.2
+++ mod_so.c	1998/02/04 13:28:55
@@ -1,5 +1,5 @@
 /* ====================================================================
- * Copyright (c) 1995-1997 The Apache Group.  All rights reserved.
+ * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -120,24 +120,37 @@
 #define RTLD_LAZY 1
 #endif
 
-/*
- * The hard part of implementing LoadModule is deciding what to do about
- * rereading the config files.  This proof-of-concept implementation takes the 
- * cheap way out:  we only actually load the modules the first time through.
- */
-
-static int been_there_done_that = 0; /* Loaded the modules yet? */
 static int have_symbol_table = 0;
 
 #ifndef NO_DLOPEN
+
+/* This is the cleanup for a loaded DLL. It unloads the module */
+void unload_module(module *modp)
+{
+    char mod_name[50];		/* Um, no pool, so make this auto */
+
+    /* Take a copy of the module name so we can report that it has been
+     * unloaded (since modp itself will have been unloaded). */
+    strncpy(mod_name, modp->name, 49);
+    mod_name[49] = '\0';
+
+    remove_module(modp);
+
+    /* The Linux manpage doesn't give any way to check the success of
+     * dlclose(). */
+    dlclose(modp->dynamic_load_handle);
+
+    aplog_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
+		"unloaded module %s", mod_name);
+}
+
+/* load_module is called for the directive LoadModule */
 static const char *load_module (cmd_parms *cmd, void *dummy, char *modname, char *filename)
 {
     void *modhandle;
     module *modp;
     const char *szModuleFile=server_root_relative(cmd->pool, filename);
 
-    if (been_there_done_that) return NULL;
-    
     if (!(modhandle = dlopen(szModuleFile, RTLD_NOW)))
       {
 	const char *my_error = dlerror();
@@ -146,10 +159,6 @@
 			NULL);
       }
  
-    /* If I knew what the correct cast is here, I'd be happy. But 
-     * I don't. So I'll use (void *). It works.
-     */
-
     aplog_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
 		"loaded module %s", modname);
 
@@ -162,8 +171,18 @@
 			" in file ", filename, ":", dlerror(), NULL);
     }
 	
+    modp->dynamic_load_handle = modhandle;
+    printf("load_module: m->dlh=%p\n", modp->dynamic_load_handle);
+
     add_module (modp);
 
+    /* Register a cleanup in the config pool (normally pconf). When
+     * we do a restart (or shutdown) this cleanup will cause the
+     * DLL to be unloaded.
+     */
+    register_cleanup(cmd->pool, modp, 
+		     (void (*)(void*))unload_module, null_cleanup);
+
     /* Alethea Patch (rws,djw2) - need to run configuration functions
        in new modules */
 
@@ -181,8 +200,6 @@
 
 static const char *load_file (cmd_parms *cmd, void *dummy, char *filename)
 {
-   if (been_there_done_that) return NULL;
-    
 	if (!dlopen(server_root_relative(cmd->pool, filename), 1))
 		return pstrcat (cmd->pool, "Cannot load ", filename, " into server", NULL);
  
@@ -191,26 +208,17 @@
 #else
 static const char *load_file(cmd_parms *cmd, void *dummy, char *filename)
 {
-  if(!been_there_done_that)
-    fprintf(stderr, "WARNING: LoadFile not supported\n");
+  fprintf(stderr, "WARNING: LoadFile not supported\n");
   return NULL;
 }
 
 static const char *load_module(cmd_parms *cmd, void *dummy, char *modname, char *filename)
 {
-  if(!been_there_done_that)
-    fprintf(stderr, "WARNING: LoadModule not supported\n");
+  fprintf(stderr, "WARNING: LoadModule not supported\n");
   return NULL;
 }
 #endif
 
-static void check_loaded_modules (server_rec *dummy, pool *p)
-{
-    if (been_there_done_that) return;
-
-    been_there_done_that = 1;
-}
-
 command_rec so_cmds[] = {
 { "LoadModule", load_module, NULL, RSRC_CONF, TAKE2,
   "a module name, and the name of a file to load it from"},
@@ -221,7 +229,7 @@
 
 module so_module = {
    STANDARD_MODULE_STUFF,
-   check_loaded_modules,	/* initializer */
+   NULL,			/* initializer */
    NULL,			/* create per-dir config */
    NULL,			/* merge per-dir config */
    NULL,			/* server config */