threebit.NET Tutorial One
[threebit.net] [tutorial home]

Note: Someone has linked to this tutorial from the Apache 2.0 Developer Documentation. This is but one in an upcoming series of tutorials - check the main tutorial page for others. For that matter, check out the main threebit site. -- Cheers, Kevin.

This tutorial guides the reader through the minimal tasks involved in writing a module for Apache 2. The module developed here has almost no functionality - it's only impact is the generation of a static message to logs/errorlog for each HTTP request.

This tutorial is not intended to showcase Apache's module API. Instead, it guides the reader through the other tasks that must be done to properly develop, compile and install a custom module - namely autoconf and automake.

Further tutorials will build from this one and explore the advanced module API. Drop a message to kevino at threebit.net if you feel the need.

# Throughout the tutorial, look for links to Apache's
# LXR website http://lxr.webperf.org/
# For example, clink on AP_MODULE_DECLARE_DATA below.
module AP_MODULE_DECLARE_DATA tut2_module;

Preparation

If you don't actually want to run or test the code in this tutorial, then feel free to skip this step. Otherwise, you'll want to perform the following actions so your work area prepared for compiling and running the tutorial.

I have assumed in this tutorial that you have an account on a Linux (or Unix) machine and you have installed the GNU build tools (autoconf, automake, etc). If you haven't then you're not going to get very far - consult your OS documentation.

# Prepare the temporary directory
cd $HOME
mkdir threebit-tutorials
cd threebit-tutorials

# Remember the tutorial home directory for later.
export TUTORIAL_HOME=`pwd`
Download via HTTP
cd $TUTORIAL_HOME
wget "http://threebit.net/tutorials/tutorials.tar.gz"
tar zxvf tutorials.tar.gz
Download via Anonymous CVS
cd $TUTORIAL_HOME
CVSROOT=:pserver:anonymous@threebit.net:/usr/local/cvs

# use "anonymous" as the password.
cvs login

cvs co tutorials/apache2_modules
mv tutorials/* .
rm -rf tutorials
Apache Note: You will get a "404 - Not Found" error if 2.0.43 is no longer the newest version of Apache. Just substitute the current version tag if that is the case.
cd $TUTORIAL_HOME

wget http://www.apache.org/dist/httpd/httpd-2.0.43.tar.gz
tar zxf httpd-2.0.43.tar.gz

cd httpd-2.0.43
./configure --prefix=$TUTORIAL_HOME/apache2 --enable-so
make
make install
Now we will fix the ServerName and Listen configuration directives so that we can run this installation as an unpriviledged user.
# store the location of the apache configuration file.
HTTPCONF=$TUTORIAL_HOME/apache2/conf/httpd.conf

# replace the ServerName directive
cat $HTTPCONF | \
  sed 's/#ServerName new.host.name:80/ServerName localhost/' \
  > $HTTPCONF.new
mv $HTTPCONF.new $HTTPCONF

# replace the Listen directive.
cat $HTTPCONF | sed 's/^Listen 80/Listen 21000/' > $HTTPCONF.new
mv $HTTPCONF.new $HTTPCONF
And test the configuration:
$TUTORIAL_HOME/apache2/bin/apachectl configtest
Syntax OK

mod_tut1.c

As stated above, the purpose of this module is to write data to the error log for each HTTP request. We are obviously building a useless module - but by limiting what the module does it becomes easier to explain what everything is doing.

The source code to the module is pretty much self documenting but let us examine each block independently.

/*
 * Include the core server components.
 */
#include "httpd.h"
#include "http_config.h"
Obviously an Apache module will require information about structures, macros and functions from Apache's core. These two header files are all that is required for this module, but real modules will need to include other header files relating to request handling, logging, protocols, etc.
/*
 * Declare and populate the module's data structure.  The
 * name of this structure ('tut1_module') is important - it
 * must match the name of the module.  This structure is the
 * only "glue" between the httpd core and the module.
 */
module AP_MODULE_DECLARE_DATA tut1_module =
{
  // Only one callback function is provided.  Real
  // modules will need to declare callback functions for
  // server/directory configuration, configuration merging
  // and other tasks.
  STANDARD20_MODULE_STUFF,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  mod_tut1_register_hooks,      /* callback for registering hooks */
};
Every module must declare it's data structure as shown above. Since this module does not require any configuration most of the callback locations have been left blank, except for the last one - that one is invoked by the HTTPD core so that the module can declare other functions that should be invoked to handle various events (like an HTTP request).
/*
 * This function is a callback and it declares what
 * other functions should be called for request
 * processing and configuration requests. This
 * callback function declares the Handlers for
 * other events.
 */
static void mod_tut1_register_hooks (apr_pool_t *p)
{
  // I think this is the call to make to register a
  // handler for method calls (GET PUT et. al.).
  // We will ask to be last so that the comment
  // has a higher tendency to go at the end.
  ap_hook_handler(mod_tut1_method_handler, NULL, NULL, APR_HOOK_LAST);
}
When this function is called by the HTTPD core, it registers a handler that should be invoked for all HTTP requests.
/*
 * This function is registered as a handler for HTTP methods and will
 * therefore be invoked for all GET requests (and others).  Regardless
 * of the request type, this function simply sends a message to
 * STDERR (which httpd redirects to logs/error_log).  A real module
 * would do *alot* more at this point.
 */
static int mod_tut1_method_handler (request_rec *r)
{
  // Send a message to stderr (apache redirects this to the error log)
  fprintf(stderr,"apache2_mod_tut1: A request was made.\n");

  // We need to flush the stream for messages to appear right away.
  // Performing an fflush() in a production system is not good for
  // performance - don't do this for real.
  fflush(stderr);

  // Return DECLINED so that the Apache core will keep looking for
  // other modules to handle this request.  This effectively makes
  // this module completely transparent.
  return DECLINED;
}
This is the function that will be invoked for every HTTP request. This is where the meat of an Apache module should go.

GNU Build Tools

Looking in TODO $TUTORIAL_HOME/tut1, you will find some familiar files that are included with most GNU applications.

Makefile.am An input file for automake
configure.in An inputfile to autoconf.
mod_tut1.c The source code to the tutorial module.
tutorial1.html This file.
The remaining files can safely be ignored.
AUTHORS automake will produce warnings if this file is not present.
COPYING The GPL license. automake will complain if this file is not present.
CVS/ CVS state directory. Ignore it. If you downloaded the tutorial using the tar ball then it won't even exist.
ChangeLog Another automake file.
INSTALL Standard install instructions. In this case, it points the reader to this file.
NEWS Another automake file.
README Another automake file.

This tutorial does not aim to be a complete reference for the GNU build tools. See the following references for information.

Aside from the module source code itself, the only files of interest to the reader are configure.in and Makefile.am. To briefly discuss these files without duplicating documetation contained in the above references:

configure.in is an input file to autoconf and is used to configure the module source code and dependencies for each target platform. Running autoconf creates the configure script we are all so familiar with.

Makefile.am is an input file to automake and is used to create a Makefile.in file. The Makefile.in file is then used by configure to create real Makefile's.

If you're confused - have no fear because I still am! You probably don't need to understand everything - just plug away through the tutorial. If you want to understand what's going on, I suggest you read the references cited above.

configure.in

I would be lying to you if I told you that I understand everything in this file. However, it seems to work so I'll tell you what I know. :) See configure.in for raw file.
AC_INIT
The mandatory autoconf initialization macro.
# Automake initialization
AM_INIT_AUTOMAKE(mod_tut1, 1.0)
This macro is provided by automake and is required when automake is used. The arguments are the package name and version number. I have provided reasonable values for the parameters but still haven't figured out what their impact is.
AC_PROG_CC
AM_PROG_LIBTOOL
These two macros add checks for suitable cc and libtool programs.
AC_DEFUN([APACHE_DIR],[

  AC_ARG_WITH(
    apache,
    [  --with-apache[=DIR]     Apache server directory],
    ,
    [with_apache="no"]
  )

  AC_MSG_CHECKING(for Apache directory)

  if test "$with_apache" = "no"; then
    AC_MSG_ERROR( Specify the apache using --with-apache)
  else
    # make sure that a well known include file exists
    if test -e $with_apache/include/httpd.h; then
      apache_dir=$with_apache
      AC_MSG_RESULT(APACHE found!)
    else
      AC_MSG_ERROR( $with_apache not found. )
    fi
  fi

])
This declares a new autoconf macro named APACHE_DIR. It is used to handle the --with-apache=/usr/local/apache2 argument to configure.
APACHE_DIR
This runs the APACHE_DIR macro that was just defined. When successfull, the directory location is stored in apache_dir.
AC_SUBST(apache_dir) 
Not all variables that are set in shell snippets are persisted to the configuration status file (config.status). This call to AC_SUBST persists the value of apache_dir.
AC_OUTPUT(Makefile)
Finally, AC_OUTPUT() saves the results of the configuration and causes a real Makefile to be generated.

Makefile.am

This file is used by automake to generate a Makefile.in file. As stated earlier, Makefile.in is then parsed using an invocation of configure to create an actual Makefile.

Since writing an Apache module is the same as writing, compiling and linking any standard shared library, automake is well suited to the task.

Again, consult the full automake documentation for all the info. See the raw Makefile.am.

lib_LTLIBRARIES = libmodtut1.la
This tells automake that we are creating a shared library named libmottut1.la.
libmodtut1_la_SOURCES = mod_tut1.c
This tells automake what source files should be compiled as part of the library. In this case there is only one, but there could be serveral.
INCLUDES = -I@apache_dir@/include
Header files from the apache distribution are required when compiling the module. This directive provides a list of include directories to pass on to gcc. Does apache_dir look familiar? If you said yes, then step to the front of the class - configure will subsitute the value that was passed in with --with-apache when the Makefile is written.

aclocal, autoconf, automake

Now that you have some idea of what those files mean we can run the utilities that use them.

aclocal is used to import macros defined by automake so that autoconf can understand what's going on.

cd $TUTORIAL_HOME/apache2_modules/tut1

# import automake m4 macros.
aclocal

# create configure based on configure.in
autoconf

# create Makefile.in based on Makefile.am and configure.in
automake -a

configure

Now we can run configure to prepare the module's Makefile.
# The ubiquitous configure script
./configure --with-apache=$TUTORIAL_HOME/apache2

make

And now we can run make to compile the module. Note: don't run make install. We'll handle the module installation later.
make

apxs

** DO NOT RUN make install ** Ordinarially you would, but the install step for an Apache module is different. Instead, apxs is used to register the module in httpd.conf and move the shared object into the apache lib directory.
$TUTORIAL_HOME/apache2/bin/apxs -i -a -n tut1 libmodtut1.la
apxs also addes the following line to httpd.conf:
LoadModule tut1_module        modules/libmodtut1.so

Run Apache

Now we are ready to run Apache and test the module.
# Change to the apache directory
cd $TUTORIAL_HOME/apache2

# Start Apache
bin/apachectl start

# Use Lynx to hit the web server.
lynx --source http://localhost:21000 | grep success

# Look for the module's message in the error log
cat logs/error_log | grep tut1
apache2_mod_tut1: A request was made.

Success!

The tutorial one module has been successfully compiled and installed into the Apache 2 runtime.

Updates

2003.06.03: Dmitry Muntean was kind enough to send in a question and resolution for a problem he was having.

Dmitry: ... on the aclocal step it said that "macro AM_PROG_LIBTOOL not found in library", After some looking around I've discovered that AM_PROG_LIBTOOL is changed to AC_PROG_LIBTOOL, so I did and aclocal went fine. Then when I launched autoconf it said to me:

configure.in:9: error: possibly undefined macro: AC_PROG_LIBTOOL
    If this token and others are legitimate, please use m4_pattern_allow.
    See the Autoconf documentation.
As far as I see possibly you used another version of these utilites. I use automake 1.7.2 and autoconf 2.57. What version have you used? Or if this isn't the problem can you point me what it is?

Kevin: automake (GNU automake) 1.4-p4

Dmitry:The problem was that libtool.m4 must be included at the end of aclocal.m4, and ltmain.sh must be in that directory. Both files were taken from latest libtool package(for now it is 1.2.5).

$Id: tutorial1.html,v 1.18 2002/10/28 07:24:08 kevino Exp $